Repository: redis/jedis Branch: master Commit: 8ba734cf950d Files: 1030 Total size: 7.5 MB Directory structure: gitextract_6u_dou_5/ ├── .github/ │ ├── CONTRIBUTING.md │ ├── ISSUE_TEMPLATE │ ├── actions/ │ │ └── run-tests/ │ │ └── action.yml │ ├── codecov.yml │ ├── dependabot.yml │ ├── release-drafter-config.yml │ └── workflows/ │ ├── codeql-analysis.yml │ ├── docs.yml │ ├── doctests.yml │ ├── format_check.yml │ ├── integration.yml │ ├── release-drafter.yml │ ├── snapshot.yml │ ├── stale-issues.yml │ ├── test-on-docker.yml │ └── version-and-release.yml ├── .gitignore ├── LICENSE ├── Makefile ├── README.md ├── docs/ │ ├── Dockerfile │ ├── README.md │ ├── advanced-usage.md │ ├── css/ │ │ └── extra.css │ ├── failover.md │ ├── faq.md │ ├── index.md │ ├── jedis-maven.md │ ├── migration-guides/ │ │ ├── v3-to-v4-primitives.md │ │ ├── v3-to-v4-zset-list.md │ │ ├── v3-to-v4.md │ │ ├── v4-to-v5.md │ │ ├── v5-to-v6.md │ │ └── v6-to-v7.md │ ├── redisearch.md │ ├── redisjson.md │ ├── requirements.txt │ ├── transactions-multi.md │ ├── tutorials_examples.md │ └── verifying-artifacts.md ├── formatter-pom.xml ├── hbase-formatter.xml ├── mkdocs.yml ├── pom.xml └── src/ ├── main/ │ ├── java/ │ │ └── redis/ │ │ └── clients/ │ │ └── jedis/ │ │ ├── AbstractPipeline.java │ │ ├── AbstractTransaction.java │ │ ├── BinaryJedisPubSub.java │ │ ├── BinaryJedisShardedPubSub.java │ │ ├── Builder.java │ │ ├── BuilderFactory.java │ │ ├── ClientSetInfoConfig.java │ │ ├── ClusterCommandObjects.java │ │ ├── ClusterPipeline.java │ │ ├── CommandArguments.java │ │ ├── CommandFlagsRegistry.java │ │ ├── CommandKeyArgumentPreProcessor.java │ │ ├── CommandObject.java │ │ ├── CommandObjects.java │ │ ├── Connection.java │ │ ├── ConnectionFactory.java │ │ ├── ConnectionPool.java │ │ ├── ConnectionPoolConfig.java │ │ ├── DefaultJedisClientConfig.java │ │ ├── DefaultJedisSocketFactory.java │ │ ├── DefaultRedisCredentials.java │ │ ├── DefaultRedisCredentialsProvider.java │ │ ├── DriverInfo.java │ │ ├── Endpoint.java │ │ ├── GeoCoordinate.java │ │ ├── HostAndPort.java │ │ ├── HostAndPortMapper.java │ │ ├── Jedis.java │ │ ├── JedisClientConfig.java │ │ ├── JedisCluster.java │ │ ├── JedisClusterInfoCache.java │ │ ├── JedisFactory.java │ │ ├── JedisMetaInfo.java │ │ ├── JedisMonitor.java │ │ ├── JedisPool.java │ │ ├── JedisPoolConfig.java │ │ ├── JedisPooled.java │ │ ├── JedisPubSub.java │ │ ├── JedisPubSubBase.java │ │ ├── JedisSafeAuthenticator.java │ │ ├── JedisSentinelPool.java │ │ ├── JedisSentineled.java │ │ ├── JedisShardedPubSub.java │ │ ├── JedisShardedPubSubBase.java │ │ ├── JedisSocketFactory.java │ │ ├── Module.java │ │ ├── MultiDbClient.java │ │ ├── MultiDbConfig.java │ │ ├── MultiNodePipelineBase.java │ │ ├── Pipeline.java │ │ ├── PipeliningBase.java │ │ ├── Protocol.java │ │ ├── RedisClient.java │ │ ├── RedisClusterClient.java │ │ ├── RedisCredentials.java │ │ ├── RedisCredentialsProvider.java │ │ ├── RedisProtocol.java │ │ ├── RedisSentinelClient.java │ │ ├── ReliableTransaction.java │ │ ├── Response.java │ │ ├── SSLSocketWrapper.java │ │ ├── ScanIteration.java │ │ ├── SslOptions.java │ │ ├── SslVerifyMode.java │ │ ├── StaticCommandFlagsRegistry.java │ │ ├── StaticCommandFlagsRegistryInitializer.java │ │ ├── StreamEntryID.java │ │ ├── Transaction.java │ │ ├── UnifiedJedis.java │ │ ├── annots/ │ │ │ ├── Experimental.java │ │ │ ├── Internal.java │ │ │ └── VisibleForTesting.java │ │ ├── args/ │ │ │ ├── BitCountOption.java │ │ │ ├── BitOP.java │ │ │ ├── ClientAttributeOption.java │ │ │ ├── ClientPauseMode.java │ │ │ ├── ClientType.java │ │ │ ├── ClusterFailoverOption.java │ │ │ ├── ClusterResetType.java │ │ │ ├── ExpiryOption.java │ │ │ ├── FlushMode.java │ │ │ ├── FunctionRestorePolicy.java │ │ │ ├── GeoUnit.java │ │ │ ├── HotkeysMetric.java │ │ │ ├── LatencyEvent.java │ │ │ ├── ListDirection.java │ │ │ ├── ListPosition.java │ │ │ ├── Rawable.java │ │ │ ├── RawableFactory.java │ │ │ ├── SaveMode.java │ │ │ ├── SortedSetOption.java │ │ │ ├── SortingOrder.java │ │ │ ├── StreamDeletionPolicy.java │ │ │ ├── UnblockType.java │ │ │ └── package-info.java │ │ ├── authentication/ │ │ │ ├── AuthXEventListener.java │ │ │ ├── AuthXManager.java │ │ │ ├── JedisAuthenticationException.java │ │ │ └── TokenCredentials.java │ │ ├── bloom/ │ │ │ ├── BFInsertParams.java │ │ │ ├── BFReserveParams.java │ │ │ ├── CFInsertParams.java │ │ │ ├── CFReserveParams.java │ │ │ ├── RedisBloomProtocol.java │ │ │ ├── TDigestMergeParams.java │ │ │ ├── commands/ │ │ │ │ ├── BloomFilterCommands.java │ │ │ │ ├── BloomFilterPipelineCommands.java │ │ │ │ ├── CountMinSketchCommands.java │ │ │ │ ├── CountMinSketchPipelineCommands.java │ │ │ │ ├── CuckooFilterCommands.java │ │ │ │ ├── CuckooFilterPipelineCommands.java │ │ │ │ ├── RedisBloomCommands.java │ │ │ │ ├── RedisBloomPipelineCommands.java │ │ │ │ ├── TDigestSketchCommands.java │ │ │ │ ├── TDigestSketchPipelineCommands.java │ │ │ │ ├── TopKFilterCommands.java │ │ │ │ ├── TopKFilterPipelineCommands.java │ │ │ │ └── package-info.java │ │ │ └── package-info.java │ │ ├── builders/ │ │ │ ├── AbstractClientBuilder.java │ │ │ ├── ClusterClientBuilder.java │ │ │ ├── MultiDbClientBuilder.java │ │ │ ├── SentinelClientBuilder.java │ │ │ └── StandaloneClientBuilder.java │ │ ├── commands/ │ │ │ ├── AccessControlLogBinaryCommands.java │ │ │ ├── AccessControlLogCommands.java │ │ │ ├── BitBinaryCommands.java │ │ │ ├── BitCommands.java │ │ │ ├── BitPipelineBinaryCommands.java │ │ │ ├── BitPipelineCommands.java │ │ │ ├── ClientBinaryCommands.java │ │ │ ├── ClientCommands.java │ │ │ ├── ClusterCommands.java │ │ │ ├── CommandCommands.java │ │ │ ├── ConfigCommands.java │ │ │ ├── ControlBinaryCommands.java │ │ │ ├── ControlCommands.java │ │ │ ├── DatabaseCommands.java │ │ │ ├── DatabasePipelineCommands.java │ │ │ ├── FunctionBinaryCommands.java │ │ │ ├── FunctionCommands.java │ │ │ ├── FunctionPipelineBinaryCommands.java │ │ │ ├── FunctionPipelineCommands.java │ │ │ ├── GenericControlCommands.java │ │ │ ├── GeoBinaryCommands.java │ │ │ ├── GeoCommands.java │ │ │ ├── GeoPipelineBinaryCommands.java │ │ │ ├── GeoPipelineCommands.java │ │ │ ├── HashBinaryCommands.java │ │ │ ├── HashCommands.java │ │ │ ├── HashPipelineBinaryCommands.java │ │ │ ├── HashPipelineCommands.java │ │ │ ├── HyperLogLogBinaryCommands.java │ │ │ ├── HyperLogLogCommands.java │ │ │ ├── HyperLogLogPipelineBinaryCommands.java │ │ │ ├── HyperLogLogPipelineCommands.java │ │ │ ├── JedisBinaryCommands.java │ │ │ ├── JedisCommands.java │ │ │ ├── KeyBinaryCommands.java │ │ │ ├── KeyCommands.java │ │ │ ├── KeyPipelineBinaryCommands.java │ │ │ ├── KeyPipelineCommands.java │ │ │ ├── ListBinaryCommands.java │ │ │ ├── ListCommands.java │ │ │ ├── ListPipelineBinaryCommands.java │ │ │ ├── ListPipelineCommands.java │ │ │ ├── ModuleCommands.java │ │ │ ├── PipelineBinaryCommands.java │ │ │ ├── PipelineCommands.java │ │ │ ├── ProtocolCommand.java │ │ │ ├── RedisModuleCommands.java │ │ │ ├── RedisModulePipelineCommands.java │ │ │ ├── SampleBinaryKeyedCommands.java │ │ │ ├── SampleBinaryKeyedPipelineCommands.java │ │ │ ├── SampleKeyedCommands.java │ │ │ ├── SampleKeyedPipelineCommands.java │ │ │ ├── ScriptingControlCommands.java │ │ │ ├── ScriptingKeyBinaryCommands.java │ │ │ ├── ScriptingKeyCommands.java │ │ │ ├── ScriptingKeyPipelineBinaryCommands.java │ │ │ ├── ScriptingKeyPipelineCommands.java │ │ │ ├── SentinelCommands.java │ │ │ ├── ServerCommands.java │ │ │ ├── SetBinaryCommands.java │ │ │ ├── SetCommands.java │ │ │ ├── SetPipelineBinaryCommands.java │ │ │ ├── SetPipelineCommands.java │ │ │ ├── SlowlogCommands.java │ │ │ ├── SortedSetBinaryCommands.java │ │ │ ├── SortedSetCommands.java │ │ │ ├── SortedSetPipelineBinaryCommands.java │ │ │ ├── SortedSetPipelineCommands.java │ │ │ ├── StreamBinaryCommands.java │ │ │ ├── StreamCommands.java │ │ │ ├── StreamPipelineBinaryCommands.java │ │ │ ├── StreamPipelineCommands.java │ │ │ ├── StringBinaryCommands.java │ │ │ ├── StringCommands.java │ │ │ ├── StringPipelineBinaryCommands.java │ │ │ ├── StringPipelineCommands.java │ │ │ ├── VectorSetBinaryCommands.java │ │ │ ├── VectorSetCommands.java │ │ │ ├── VectorSetPipelineBinaryCommands.java │ │ │ ├── VectorSetPipelineCommands.java │ │ │ └── package-info.java │ │ ├── csc/ │ │ │ ├── AbstractCache.java │ │ │ ├── Cache.java │ │ │ ├── CacheConfig.java │ │ │ ├── CacheConnection.java │ │ │ ├── CacheEntry.java │ │ │ ├── CacheFactory.java │ │ │ ├── CacheKey.java │ │ │ ├── CacheStats.java │ │ │ ├── Cacheable.java │ │ │ ├── DefaultCache.java │ │ │ ├── DefaultCacheable.java │ │ │ ├── EvictionPolicy.java │ │ │ ├── LRUEviction.java │ │ │ ├── RedisVersion.java │ │ │ ├── package-info.java │ │ │ └── util/ │ │ │ ├── AllowAndDenyListWithStringKeys.java │ │ │ └── package-info.java │ │ ├── exceptions/ │ │ │ ├── ClusterAggregationException.java │ │ │ ├── InvalidURIException.java │ │ │ ├── JedisAccessControlException.java │ │ │ ├── JedisAskDataException.java │ │ │ ├── JedisBroadcastException.java │ │ │ ├── JedisBusyException.java │ │ │ ├── JedisCacheException.java │ │ │ ├── JedisClusterException.java │ │ │ ├── JedisClusterOperationException.java │ │ │ ├── JedisConnectionException.java │ │ │ ├── JedisDataException.java │ │ │ ├── JedisException.java │ │ │ ├── JedisMovedDataException.java │ │ │ ├── JedisNoScriptException.java │ │ │ ├── JedisRedirectionException.java │ │ │ ├── JedisValidationException.java │ │ │ ├── UnsupportedAggregationException.java │ │ │ └── package-info.java │ │ ├── executors/ │ │ │ ├── ClusterCommandExecutor.java │ │ │ ├── CommandExecutor.java │ │ │ ├── ConnectionResolver.java │ │ │ ├── ConnectionResolverFactory.java │ │ │ ├── DefaultCommandExecutor.java │ │ │ ├── ReplicaOnlyConnectionResolver.java │ │ │ ├── RetryableCommandExecutor.java │ │ │ ├── RoundRobinConnectionResolver.java │ │ │ ├── SimpleCommandExecutor.java │ │ │ ├── SingleConnectionResolver.java │ │ │ ├── SlotBasedConnectionResolver.java │ │ │ ├── aggregators/ │ │ │ │ ├── Aggregator.java │ │ │ │ ├── ClusterReplyAggregator.java │ │ │ │ ├── DefaultPolicyAggregator.java │ │ │ │ ├── FirstNonNullAggregator.java │ │ │ │ ├── JedisByteHashMapAggregator.java │ │ │ │ ├── JedisByteMapAggregator.java │ │ │ │ ├── ListAggregator.java │ │ │ │ ├── LogicalAndAggregator.java │ │ │ │ ├── LogicalBinaryAggregator.java │ │ │ │ ├── LogicalOrAggregator.java │ │ │ │ ├── MapAggregator.java │ │ │ │ ├── MaxAggregator.java │ │ │ │ ├── MinAggregator.java │ │ │ │ ├── MultiNodeResultAggregator.java │ │ │ │ ├── SetAggregator.java │ │ │ │ └── SumAggregator.java │ │ │ └── package-info.java │ │ ├── json/ │ │ │ ├── DefaultGsonObjectMapper.java │ │ │ ├── JsonBuilderFactory.java │ │ │ ├── JsonObjectMapper.java │ │ │ ├── JsonProtocol.java │ │ │ ├── JsonSetParams.java │ │ │ ├── Path.java │ │ │ ├── Path2.java │ │ │ ├── commands/ │ │ │ │ ├── RedisJsonCommands.java │ │ │ │ ├── RedisJsonPipelineCommands.java │ │ │ │ ├── RedisJsonV1Commands.java │ │ │ │ ├── RedisJsonV1PipelineCommands.java │ │ │ │ ├── RedisJsonV2Commands.java │ │ │ │ └── RedisJsonV2PipelineCommands.java │ │ │ └── package-info.java │ │ ├── mcf/ │ │ │ ├── CircuitBreakerThresholdsAdapter.java │ │ │ ├── ConnectionFailoverException.java │ │ │ ├── ConnectionInitializationContext.java │ │ │ ├── DatabaseSwitchEvent.java │ │ │ ├── HealthCheck.java │ │ │ ├── HealthCheckCollection.java │ │ │ ├── HealthCheckImpl.java │ │ │ ├── HealthCheckStrategy.java │ │ │ ├── HealthStatus.java │ │ │ ├── HealthStatusChangeEvent.java │ │ │ ├── HealthStatusListener.java │ │ │ ├── HealthStatusManager.java │ │ │ ├── InitializationPolicy.java │ │ │ ├── JedisFailoverException.java │ │ │ ├── LagAwareStrategy.java │ │ │ ├── MultiDbCommandExecutor.java │ │ │ ├── MultiDbConnectionProvider.java │ │ │ ├── MultiDbConnectionSupplier.java │ │ │ ├── MultiDbFailoverBase.java │ │ │ ├── MultiDbPipeline.java │ │ │ ├── MultiDbTransaction.java │ │ │ ├── PingStrategy.java │ │ │ ├── ProbingPolicy.java │ │ │ ├── RedisRestAPI.java │ │ │ ├── StatusTracker.java │ │ │ ├── SwitchReason.java │ │ │ ├── TrackingConnectionPool.java │ │ │ └── package-info.java │ │ ├── params/ │ │ │ ├── BaseGetExParams.java │ │ │ ├── BaseSetExParams.java │ │ │ ├── BitPosParams.java │ │ │ ├── ClientKillParams.java │ │ │ ├── CommandListFilterByParams.java │ │ │ ├── FailoverParams.java │ │ │ ├── GeoAddParams.java │ │ │ ├── GeoRadiusParam.java │ │ │ ├── GeoRadiusStoreParam.java │ │ │ ├── GeoSearchParam.java │ │ │ ├── GetExParams.java │ │ │ ├── HGetExParams.java │ │ │ ├── HSetExParams.java │ │ │ ├── HotkeysParams.java │ │ │ ├── IParams.java │ │ │ ├── LCSParams.java │ │ │ ├── LPosParams.java │ │ │ ├── LolwutParams.java │ │ │ ├── MSetExParams.java │ │ │ ├── MigrateParams.java │ │ │ ├── ModuleLoadExParams.java │ │ │ ├── RestoreParams.java │ │ │ ├── ScanParams.java │ │ │ ├── SetParams.java │ │ │ ├── ShutdownParams.java │ │ │ ├── SortingParams.java │ │ │ ├── VAddParams.java │ │ │ ├── VSimParams.java │ │ │ ├── XAddParams.java │ │ │ ├── XAutoClaimParams.java │ │ │ ├── XCfgSetParams.java │ │ │ ├── XClaimParams.java │ │ │ ├── XPendingParams.java │ │ │ ├── XReadGroupParams.java │ │ │ ├── XReadParams.java │ │ │ ├── XTrimParams.java │ │ │ ├── ZAddParams.java │ │ │ ├── ZIncrByParams.java │ │ │ ├── ZParams.java │ │ │ ├── ZRangeParams.java │ │ │ └── package-info.java │ │ ├── providers/ │ │ │ ├── ClusterConnectionProvider.java │ │ │ ├── ConnectionProvider.java │ │ │ ├── ManagedConnectionProvider.java │ │ │ ├── PooledConnectionProvider.java │ │ │ ├── SentineledConnectionProvider.java │ │ │ └── package-info.java │ │ ├── resps/ │ │ │ ├── AccessControlLogEntry.java │ │ │ ├── AccessControlUser.java │ │ │ ├── ClusterShardInfo.java │ │ │ ├── ClusterShardNodeInfo.java │ │ │ ├── CommandDocument.java │ │ │ ├── CommandInfo.java │ │ │ ├── FunctionStats.java │ │ │ ├── GeoRadiusResponse.java │ │ │ ├── HotkeysInfo.java │ │ │ ├── LCSMatchResult.java │ │ │ ├── LatencyHistoryInfo.java │ │ │ ├── LatencyLatestInfo.java │ │ │ ├── LibraryInfo.java │ │ │ ├── RawVector.java │ │ │ ├── ScanResult.java │ │ │ ├── Slowlog.java │ │ │ ├── StreamConsumerFullInfo.java │ │ │ ├── StreamConsumerInfo.java │ │ │ ├── StreamConsumersInfo.java │ │ │ ├── StreamEntry.java │ │ │ ├── StreamEntryBinary.java │ │ │ ├── StreamEntryDeletionResult.java │ │ │ ├── StreamFullInfo.java │ │ │ ├── StreamGroupFullInfo.java │ │ │ ├── StreamGroupInfo.java │ │ │ ├── StreamInfo.java │ │ │ ├── StreamPendingEntry.java │ │ │ ├── StreamPendingSummary.java │ │ │ ├── TrackingInfo.java │ │ │ ├── Tuple.java │ │ │ ├── VSimScoreAttribs.java │ │ │ ├── VectorInfo.java │ │ │ └── package-info.java │ │ ├── search/ │ │ │ ├── Apply.java │ │ │ ├── Combiner.java │ │ │ ├── Combiners.java │ │ │ ├── Document.java │ │ │ ├── FTCreateParams.java │ │ │ ├── FTProfileParams.java │ │ │ ├── FTSearchParams.java │ │ │ ├── FTSpellCheckParams.java │ │ │ ├── FieldName.java │ │ │ ├── Filter.java │ │ │ ├── FtSearchIteration.java │ │ │ ├── IndexDataType.java │ │ │ ├── IndexDefinition.java │ │ │ ├── IndexOptions.java │ │ │ ├── Limit.java │ │ │ ├── ProfilingInfo.java │ │ │ ├── Query.java │ │ │ ├── RediSearchCommands.java │ │ │ ├── RediSearchPipelineCommands.java │ │ │ ├── RediSearchUtil.java │ │ │ ├── Schema.java │ │ │ ├── Scorer.java │ │ │ ├── Scorers.java │ │ │ ├── SearchBuilderFactory.java │ │ │ ├── SearchProtocol.java │ │ │ ├── SearchResult.java │ │ │ ├── aggr/ │ │ │ │ ├── AggregationBuilder.java │ │ │ │ ├── AggregationResult.java │ │ │ │ ├── FtAggregateIteration.java │ │ │ │ ├── Group.java │ │ │ │ ├── Reducer.java │ │ │ │ ├── Reducers.java │ │ │ │ ├── Row.java │ │ │ │ ├── SortedField.java │ │ │ │ └── package-info.java │ │ │ ├── hybrid/ │ │ │ │ ├── FTHybridParams.java │ │ │ │ ├── FTHybridPostProcessingParams.java │ │ │ │ ├── FTHybridSearchParams.java │ │ │ │ ├── FTHybridVectorParams.java │ │ │ │ └── HybridResult.java │ │ │ ├── package-info.java │ │ │ ├── querybuilder/ │ │ │ │ ├── DisjunctNode.java │ │ │ │ ├── DisjunctUnionNode.java │ │ │ │ ├── DoubleRangeValue.java │ │ │ │ ├── GeoValue.java │ │ │ │ ├── IntersectNode.java │ │ │ │ ├── LongRangeValue.java │ │ │ │ ├── Node.java │ │ │ │ ├── OptionalNode.java │ │ │ │ ├── QueryBuilders.java │ │ │ │ ├── QueryNode.java │ │ │ │ ├── RangeValue.java │ │ │ │ ├── UnionNode.java │ │ │ │ ├── Value.java │ │ │ │ ├── ValueNode.java │ │ │ │ └── Values.java │ │ │ └── schemafields/ │ │ │ ├── GeoField.java │ │ │ ├── GeoShapeField.java │ │ │ ├── NumericField.java │ │ │ ├── SchemaField.java │ │ │ ├── TagField.java │ │ │ ├── TextField.java │ │ │ └── VectorField.java │ │ ├── timeseries/ │ │ │ ├── AggregationType.java │ │ │ ├── DuplicatePolicy.java │ │ │ ├── EncodingFormat.java │ │ │ ├── RedisTimeSeriesCommands.java │ │ │ ├── RedisTimeSeriesPipelineCommands.java │ │ │ ├── TSAddParams.java │ │ │ ├── TSAlterParams.java │ │ │ ├── TSArithByParams.java │ │ │ ├── TSCreateParams.java │ │ │ ├── TSDecrByParams.java │ │ │ ├── TSElement.java │ │ │ ├── TSGetParams.java │ │ │ ├── TSIncrByParams.java │ │ │ ├── TSInfo.java │ │ │ ├── TSMGetElement.java │ │ │ ├── TSMGetParams.java │ │ │ ├── TSMRangeElements.java │ │ │ ├── TSMRangeParams.java │ │ │ ├── TSRangeParams.java │ │ │ ├── TimeSeriesBuilderFactory.java │ │ │ ├── TimeSeriesProtocol.java │ │ │ └── package-info.java │ │ └── util/ │ │ ├── ByteArrayComparator.java │ │ ├── CompareCondition.java │ │ ├── Delay.java │ │ ├── DoublePrecision.java │ │ ├── IOUtils.java │ │ ├── JedisAsserts.java │ │ ├── JedisByteHashMap.java │ │ ├── JedisByteMap.java │ │ ├── JedisClusterCRC16.java │ │ ├── JedisClusterHashTag.java │ │ ├── JedisCommandIterationBase.java │ │ ├── JedisURIHelper.java │ │ ├── KeyValue.java │ │ ├── LazyRawable.java │ │ ├── Pool.java │ │ ├── PrefixedKeyArgumentPreProcessor.java │ │ ├── RedisInputStream.java │ │ ├── RedisOutputStream.java │ │ ├── SafeEncoder.java │ │ └── package-info.java │ └── resources/ │ └── redis/ │ └── clients/ │ └── jedis/ │ └── pom.properties └── test/ ├── java/ │ ├── io/ │ │ └── redis/ │ │ ├── examples/ │ │ │ ├── BitMapsExample.java │ │ │ ├── BitfieldExample.java │ │ │ ├── BloomFilterExample.java │ │ │ ├── CMSExample.java │ │ │ ├── CmdsCnxmgmtExample.java │ │ │ ├── CmdsGenericExample.java │ │ │ ├── CmdsHashExample.java │ │ │ ├── CmdsListExample.java │ │ │ ├── CmdsServerMgmtExample.java │ │ │ ├── CmdsSetExample.java │ │ │ ├── CmdsSortedSetExample.java │ │ │ ├── CmdsStringExample.java │ │ │ ├── CuckooFilterExample.java │ │ │ ├── GeoExample.java │ │ │ ├── GeoIndexExample.java │ │ │ ├── HashExample.java │ │ │ ├── HomeJsonExample.java │ │ │ ├── HomeProbDtsExample.java │ │ │ ├── HyperLogLogExample.java │ │ │ ├── JsonExample.java │ │ │ ├── ListExample.java │ │ │ ├── PipeTransExample.java │ │ │ ├── QueryAggExample.java │ │ │ ├── QueryEmExample.java │ │ │ ├── QueryFtExample.java │ │ │ ├── QueryGeoExample.java │ │ │ ├── QueryRangeExample.java │ │ │ ├── SearchQuickstartExample.java │ │ │ ├── SetGetExample.java │ │ │ ├── SetsExample.java │ │ │ ├── SortedSetsExample.java │ │ │ ├── StreamsExample.java │ │ │ ├── StringExample.java │ │ │ ├── TDigestExample.java │ │ │ ├── TimeSeriesTutorialExample.java │ │ │ ├── TopKExample.java │ │ │ └── VectorSetExample.java │ │ └── test/ │ │ ├── annotations/ │ │ │ ├── ConditionalOnEnv.java │ │ │ ├── EnabledOnCommand.java │ │ │ └── SinceRedisVersion.java │ │ └── utils/ │ │ ├── RedisInfo.java │ │ └── RedisVersion.java │ └── redis/ │ └── clients/ │ └── jedis/ │ ├── ACLJedisPoolTest.java │ ├── ACLJedisSentinelPoolTest.java │ ├── ACLJedisTest.java │ ├── BuilderTest.java │ ├── ClusterCommandObjectsTest.java │ ├── ClusterPipeliningTest.java │ ├── ConnectionTest.java │ ├── DefaultJedisClientConfigTest.java │ ├── EndpointConfig.java │ ├── Endpoints.java │ ├── HostAndPortTest.java │ ├── JedisClusterCommandTest.java │ ├── JedisClusterInfoCacheTest.java │ ├── JedisClusterWithoutSetupTest.java │ ├── JedisPoolTest.java │ ├── JedisPoolUnitTest.java │ ├── JedisPubSubBaseTest.java │ ├── JedisSentinelPoolTest.java │ ├── JedisSentinelTest.java │ ├── JedisShardedPubSubBaseTest.java │ ├── JedisTest.java │ ├── ManagedConnectionProviderTest.java │ ├── MigratePipeliningTest.java │ ├── MultiDbClientTest.java │ ├── PipeliningTest.java │ ├── ProtocolTest.java │ ├── RedisClientTest.java │ ├── ReliableTransactionTest.java │ ├── SentineledConnectionProviderTest.java │ ├── StaticCommandFlagsRegistryTest.java │ ├── TransactionV2Test.java │ ├── TupleSortedSetTest.java │ ├── UdsTest.java │ ├── UnavailableConnectionTest.java │ ├── UnboundRedisClusterClientTest.java │ ├── UnboundRedisClusterClientTestBase.java │ ├── UnifiedJedisCustomCommandsTest.java │ ├── authentication/ │ │ ├── EntraIDTestContext.java │ │ ├── RedisEntraIDClusterIntegrationTests.java │ │ ├── RedisEntraIDIntegrationTests.java │ │ ├── RedisEntraIDManagedIdentityIntegrationTests.java │ │ ├── TokenBasedAuthenticationClusterIntegrationTests.java │ │ ├── TokenBasedAuthenticationIntegrationTests.java │ │ └── TokenBasedAuthenticationUnitTests.java │ ├── benchmark/ │ │ ├── CRC16Benchmark.java │ │ ├── GetSetBenchmark.java │ │ ├── PipelinedGetSetBenchmark.java │ │ ├── PoolBenchmark.java │ │ ├── ProtocolBenchmark.java │ │ ├── RedisClientBenchmark.java │ │ ├── RedisClientCSCBenchmark.java │ │ └── SafeEncoderBenchmark.java │ ├── builders/ │ │ ├── ClientBuilderTest.java │ │ ├── ClusterClientBuilderTest.java │ │ ├── JedisClusterConstructorReflectionTest.java │ │ ├── JedisPooledConstructorReflectionTest.java │ │ ├── JedisSentineledConstructorReflectionTest.java │ │ ├── RedisClusterClientMigrationIntegrationTest.java │ │ └── UnifiedJedisConstructorReflectionTest.java │ ├── codegen/ │ │ ├── CommandFlagsRegistryGenerator.java │ │ └── README.md │ ├── collections/ │ │ ├── JedisByteHashMapTest.java │ │ └── SetFromListTest.java │ ├── commands/ │ │ ├── CommandsTestsParameters.java │ │ ├── commandobjects/ │ │ │ ├── CommandObjectsBitmapCommandsTest.java │ │ │ ├── CommandObjectsBloomFilterCommandsTest.java │ │ │ ├── CommandObjectsCountMinSketchCommandsTest.java │ │ │ ├── CommandObjectsCuckooFilterCommandsTest.java │ │ │ ├── CommandObjectsGenericCommandsTest.java │ │ │ ├── CommandObjectsGeospatialCommandsTest.java │ │ │ ├── CommandObjectsHashCommandsTest.java │ │ │ ├── CommandObjectsHyperloglogCommandsTest.java │ │ │ ├── CommandObjectsJsonCommandsTest.java │ │ │ ├── CommandObjectsListCommandsTest.java │ │ │ ├── CommandObjectsModulesTestBase.java │ │ │ ├── CommandObjectsScriptingCommandsTest.java │ │ │ ├── CommandObjectsSearchAndQueryCommandsTest.java │ │ │ ├── CommandObjectsServerManagementCommandsTest.java │ │ │ ├── CommandObjectsSetCommandsTest.java │ │ │ ├── CommandObjectsSortedSetCommandsTest.java │ │ │ ├── CommandObjectsStandaloneTestBase.java │ │ │ ├── CommandObjectsStreamCommandsTest.java │ │ │ ├── CommandObjectsStringCommandsTest.java │ │ │ ├── CommandObjectsTDigestCommandsTest.java │ │ │ ├── CommandObjectsTestBase.java │ │ │ ├── CommandObjectsTimeSeriesCommandsTest.java │ │ │ ├── CommandObjectsTopkCommandsTest.java │ │ │ └── Person.java │ │ ├── jedis/ │ │ │ ├── AccessControlListCommandsTest.java │ │ │ ├── AllKindOfValuesCommandsTest.java │ │ │ ├── BinaryValuesCommandsTest.java │ │ │ ├── BitCommandsTest.java │ │ │ ├── ClientCommandsTest.java │ │ │ ├── ClusterBinaryValuesCommandsTest.java │ │ │ ├── ClusterCommandsTest.java │ │ │ ├── ClusterHotkeysCommandsTest.java │ │ │ ├── ClusterJedisCommandsTestBase.java │ │ │ ├── ClusterScriptingCommandsTest.java │ │ │ ├── ClusterShardedPublishSubscribeCommandsTest.java │ │ │ ├── ClusterValuesCommandsTest.java │ │ │ ├── ControlCommandsTest.java │ │ │ ├── FailoverCommandsTest.java │ │ │ ├── GeoCommandsTest.java │ │ │ ├── HashesCommandsTest.java │ │ │ ├── HotkeysCommandsTest.java │ │ │ ├── HyperLogLogCommandsTest.java │ │ │ ├── JedisCommandsTestBase.java │ │ │ ├── ListCommandsTest.java │ │ │ ├── MigrateTest.java │ │ │ ├── ModuleTest.java │ │ │ ├── ObjectCommandsTest.java │ │ │ ├── PublishSubscribeCommandsTest.java │ │ │ ├── ScriptingCommandsTest.java │ │ │ ├── SentinelCommandsTest.java │ │ │ ├── SetCommandsTest.java │ │ │ ├── SlowlogCommandsTest.java │ │ │ ├── SortedSetCommandsTest.java │ │ │ ├── SortingCommandsTest.java │ │ │ ├── StreamsBinaryCommandsTest.java │ │ │ ├── StreamsCommandsTest.java │ │ │ ├── StringValuesCommandsTest.java │ │ │ ├── TransactionCommandsTest.java │ │ │ ├── VariadicCommandsTest.java │ │ │ └── VectorSetCommandsTest.java │ │ └── unified/ │ │ ├── AllKindOfValuesCommandsTestBase.java │ │ ├── BinaryValuesCommandsTestBase.java │ │ ├── BitCommandsTestBase.java │ │ ├── ExtendedVectorSetCommandsTestBase.java │ │ ├── FunctionCommandsTestBase.java │ │ ├── GeoCommandsTestBase.java │ │ ├── HashesCommandsTestBase.java │ │ ├── HotkeysCommandsTestBase.java │ │ ├── HyperLogLogCommandsTestBase.java │ │ ├── ListCommandsTestBase.java │ │ ├── SetCommandsTestBase.java │ │ ├── SortedSetCommandsTestBase.java │ │ ├── StreamsBinaryCommandsTestBase.java │ │ ├── StreamsCommandsTestBase.java │ │ ├── StringValuesCommandsTestBase.java │ │ ├── UnifiedJedisCommandsTestBase.java │ │ ├── VectorSetCommandsTestBase.java │ │ ├── client/ │ │ │ ├── RedisClientAllKindOfValuesCommandsTest.java │ │ │ ├── RedisClientBinaryValuesCommandsTest.java │ │ │ ├── RedisClientBitCommandsTest.java │ │ │ ├── RedisClientCommandsTestHelper.java │ │ │ ├── RedisClientExtendedVectorSetCommandsTest.java │ │ │ ├── RedisClientGeoCommandsTest.java │ │ │ ├── RedisClientHashesCommandsTest.java │ │ │ ├── RedisClientHotkeysCommandsTest.java │ │ │ ├── RedisClientHyperLogLogCommandsTest.java │ │ │ ├── RedisClientListCommandsTest.java │ │ │ ├── RedisClientMiscellaneousTest.java │ │ │ ├── RedisClientSetCommandsTest.java │ │ │ ├── RedisClientSortedSetCommandsTest.java │ │ │ ├── RedisClientStreamsBinaryCommandsTest.java │ │ │ ├── RedisClientStreamsCommandsTest.java │ │ │ ├── RedisClientStringValuesCommandsTest.java │ │ │ ├── RedisClientTransactionIT.java │ │ │ ├── RedisClientVectorSetCommandsTest.java │ │ │ └── search/ │ │ │ └── FTHybridRedisClientCommandsTest.java │ │ ├── cluster/ │ │ │ ├── ClusterAllKindOfValuesCommandsTest.java │ │ │ ├── ClusterBinaryValuesCommandsTest.java │ │ │ ├── ClusterBitCommandsTest.java │ │ │ ├── ClusterCommandsTestHelper.java │ │ │ ├── ClusterExtendedVectorSetCommandsTest.java │ │ │ ├── ClusterFunctionCommandsTest.java │ │ │ ├── ClusterGeoCommandsTest.java │ │ │ ├── ClusterHashesCommandsTest.java │ │ │ ├── ClusterHotkeysCommandsTest.java │ │ │ ├── ClusterHyperLogLogCommandsTest.java │ │ │ ├── ClusterListCommandsTest.java │ │ │ ├── ClusterSetCommandsTest.java │ │ │ ├── ClusterSortedSetCommandsTest.java │ │ │ ├── ClusterStreamsBinaryCommandsTest.java │ │ │ ├── ClusterStreamsCommandsTest.java │ │ │ ├── ClusterStringValuesCommandsTest.java │ │ │ ├── ClusterVectorSetCommandsTest.java │ │ │ └── search/ │ │ │ └── FTHybridCommandsClusterTest.java │ │ ├── pipeline/ │ │ │ ├── BinaryStreamsPipelineCommandsTest.java │ │ │ ├── GeoPipelineCommandsTest.java │ │ │ ├── HashesPipelineCommandsTest.java │ │ │ ├── ListPipelineCommandsTest.java │ │ │ ├── PipelineCommandsTestBase.java │ │ │ ├── SetPipelineCommandsTest.java │ │ │ ├── SortedSetPipelineCommandsTest.java │ │ │ └── StreamsPipelineCommandsTest.java │ │ ├── search/ │ │ │ └── FTHybridCommandsTestBase.java │ │ └── sentinel/ │ │ └── SentinelAllKindOfValuesCommandsIT.java │ ├── csc/ │ │ ├── AllowAndDenyListCacheableTest.java │ │ ├── ClientSideCacheFunctionalityTest.java │ │ ├── ClientSideCacheTestBase.java │ │ ├── RedisClientSideCacheTest.java │ │ ├── RedisClientSideCacheTestBase.java │ │ ├── RedisClusterClientSideCacheTest.java │ │ ├── RedisSentinelClientSideCacheTest.java │ │ ├── SSLRedisClientSideCacheTest.java │ │ ├── TestCache.java │ │ ├── UnifiedJedisClientSideCacheTestBase.java │ │ └── VersionTest.java │ ├── examples/ │ │ ├── BroadcastCommandsToAllClusterNodes.java │ │ ├── GeoShapeFieldsUsageInRediSearch.java │ │ ├── RedisCredentialsProviderUsage.java │ │ └── RetryableCommandExecution.java │ ├── exceptions/ │ │ ├── ExceptionsTest.java │ │ └── FailoverAbortedException.java │ ├── executors/ │ │ ├── ClusterCommandExecutorTest.java │ │ ├── RetryableCommandExecutorTest.java │ │ └── aggregators/ │ │ ├── ClusterReplyAggregatorTest.java │ │ └── MultiNodeResultAggregatorTest.java │ ├── failover/ │ │ └── FailoverIntegrationTest.java │ ├── mcf/ │ │ ├── ActiveActiveLocalFailoverTest.java │ │ ├── ConnectionInitializationContextTest.java │ │ ├── DatabaseEvaluateThresholdsTest.java │ │ ├── DefaultValuesTest.java │ │ ├── FailbackMechanismIntegrationTest.java │ │ ├── FailbackMechanismUnitTest.java │ │ ├── HealthCheckIntegrationTest.java │ │ ├── HealthCheckTest.java │ │ ├── InitializationPolicyTest.java │ │ ├── LagAwareStrategyUnitTest.java │ │ ├── MultiDbCircuitBreakerThresholdsTest.java │ │ ├── MultiDbConnectionProviderDynamicEndpointUnitTest.java │ │ ├── MultiDbConnectionProviderFailoverAttemptsConfigTest.java │ │ ├── MultiDbConnectionProviderHelper.java │ │ ├── MultiDbConnectionProviderInitializationTest.java │ │ ├── MultiDbConnectionProviderTest.java │ │ ├── PeriodicFailbackTest.java │ │ ├── PingStrategyIntegrationTest.java │ │ ├── RedisRestAPIIT.java │ │ ├── RedisRestAPIUnitTest.java │ │ ├── StatusTrackerTest.java │ │ └── TestHealthCheckStrategy.java │ ├── misc/ │ │ ├── AutomaticFailoverTest.java │ │ ├── ClientSetInfoConfigTest.java │ │ ├── ClusterInitErrorTest.java │ │ ├── ResponsesToStringTest.java │ │ └── TupleTest.java │ ├── mocked/ │ │ ├── MockedCommandObjectsTestBase.java │ │ ├── pipeline/ │ │ │ ├── PipeliningBaseBitmapCommandsTest.java │ │ │ ├── PipeliningBaseBloomFilterCommandsTest.java │ │ │ ├── PipeliningBaseCountMinSketchCommandsTest.java │ │ │ ├── PipeliningBaseCuckooFilterCommandsTest.java │ │ │ ├── PipeliningBaseGenericCommandsTest.java │ │ │ ├── PipeliningBaseGeospatialCommandsTest.java │ │ │ ├── PipeliningBaseHashCommandsTest.java │ │ │ ├── PipeliningBaseHyperloglogCommandsTest.java │ │ │ ├── PipeliningBaseJsonCommandsTest.java │ │ │ ├── PipeliningBaseListCommandsTest.java │ │ │ ├── PipeliningBaseMiscellaneousTest.java │ │ │ ├── PipeliningBaseMockedTestBase.java │ │ │ ├── PipeliningBaseScriptingAndFunctionsCommandsTest.java │ │ │ ├── PipeliningBaseSearchAndQueryCommandsTest.java │ │ │ ├── PipeliningBaseServerManagementCommandsTest.java │ │ │ ├── PipeliningBaseSetCommandsTest.java │ │ │ ├── PipeliningBaseSortedSetCommandsTest.java │ │ │ ├── PipeliningBaseStreamCommandsTest.java │ │ │ ├── PipeliningBaseStringCommandsTest.java │ │ │ ├── PipeliningBaseTDigestCommandsTest.java │ │ │ ├── PipeliningBaseTimeSeriesCommandsTest.java │ │ │ └── PipeliningBaseTopKCommandsTest.java │ │ └── unified/ │ │ ├── UnifiedJedisBitmapCommandsTest.java │ │ ├── UnifiedJedisBloomFilterCommandsTest.java │ │ ├── UnifiedJedisConnectionManagementCommandsTest.java │ │ ├── UnifiedJedisCountMinSketchCommandsTest.java │ │ ├── UnifiedJedisCuckooFilterCommandsTest.java │ │ ├── UnifiedJedisGenericCommandsTest.java │ │ ├── UnifiedJedisGeospatialCommandsTest.java │ │ ├── UnifiedJedisHashCommandsTest.java │ │ ├── UnifiedJedisHyperloglogCommandsTest.java │ │ ├── UnifiedJedisJsonCommandsTest.java │ │ ├── UnifiedJedisListCommandsTest.java │ │ ├── UnifiedJedisMockedTestBase.java │ │ ├── UnifiedJedisPubSubCommandsTest.java │ │ ├── UnifiedJedisScriptingAndFunctionsCommandsTest.java │ │ ├── UnifiedJedisSearchAndQueryCommandsTest.java │ │ ├── UnifiedJedisServerManagementCommandsTest.java │ │ ├── UnifiedJedisSetCommandsTest.java │ │ ├── UnifiedJedisSortedSetCommandsTest.java │ │ ├── UnifiedJedisStreamCommandsTest.java │ │ ├── UnifiedJedisStringCommandsTest.java │ │ ├── UnifiedJedisTDigestCommandsTest.java │ │ ├── UnifiedJedisTimeSeriesCommandsTest.java │ │ └── UnifiedJedisTopKCommandsTest.java │ ├── modules/ │ │ ├── ConsolidatedAccessControlListCommandsTest.java │ │ ├── ConsolidatedConfigurationCommandsTest.java │ │ ├── RedisModuleCommandsTestBase.java │ │ ├── RedisModulesPipelineTest.java │ │ ├── bloom/ │ │ │ ├── BloomTest.java │ │ │ ├── CMSTest.java │ │ │ ├── CuckooTest.java │ │ │ ├── TDigestTest.java │ │ │ └── TopKTest.java │ │ ├── json/ │ │ │ ├── JsonObjects.java │ │ │ ├── Path2Test.java │ │ │ ├── PathTest.java │ │ │ ├── RedisJsonV1Test.java │ │ │ └── RedisJsonV2Test.java │ │ ├── search/ │ │ │ ├── AggregationBuilderTest.java │ │ │ ├── AggregationTest.java │ │ │ ├── CreateTest.java │ │ │ ├── DocumentTest.java │ │ │ ├── JsonSearchTest.java │ │ │ ├── JsonSearchWithGsonTest.java │ │ │ ├── QueryBuilderTest.java │ │ │ ├── QueryTest.java │ │ │ ├── SchemaTest.java │ │ │ ├── SearchDefaultDialectTest.java │ │ │ ├── SearchTest.java │ │ │ ├── SearchWithParamsTest.java │ │ │ ├── SpellCheckTest.java │ │ │ ├── SuggestionTest.java │ │ │ └── UtilTest.java │ │ └── timeseries/ │ │ └── TimeSeriesTest.java │ ├── params/ │ │ ├── BitPosParamsTest.java │ │ ├── ClientKillParamsTest.java │ │ ├── CommandListFilterByParamsTest.java │ │ ├── FailoverParamsTest.java │ │ ├── GeoAddParamsTest.java │ │ ├── GetExParamsTest.java │ │ ├── HotkeysParamsTest.java │ │ ├── LCSParamsTest.java │ │ ├── LPosParamsTest.java │ │ ├── LolwutParamsTest.java │ │ ├── MigrateParamsTest.java │ │ ├── ModuleLoadExParamsTest.java │ │ ├── RestoreParamsTest.java │ │ ├── ScanParamsTest.java │ │ ├── SetParamsTest.java │ │ ├── ShutdownParamsTest.java │ │ ├── SortingParamsTest.java │ │ ├── XAddParamsTest.java │ │ ├── XAutoClaimParamsTest.java │ │ ├── XCfgSetParamsTest.java │ │ ├── XClaimParamsTest.java │ │ ├── XPendingParamsTest.java │ │ ├── XReadGroupParamsTest.java │ │ ├── XReadParamsTest.java │ │ ├── XTrimParamsTest.java │ │ ├── ZAddParamsTest.java │ │ ├── ZIncrByParamsTest.java │ │ ├── ZParamsTest.java │ │ └── ZRangeParamsTest.java │ ├── prefix/ │ │ ├── JedisPooledPrefixedKeysTest.java │ │ ├── PrefixedKeysTest.java │ │ ├── RedisClusterPrefixedKeysTest.java │ │ └── RedisSentinelClientPrefixedKeysTest.java │ ├── providers/ │ │ ├── HealthStatusManagerTest.java │ │ ├── MultiClusterPooledConnectionProviderTest.java │ │ ├── MultiDbProviderHealthStatusChangeTest.java │ │ └── SentineledConnectionProviderReconnectionTest.java │ ├── resps/ │ │ ├── LibraryInfoTest.java │ │ └── StreamEntryDeletionResultTest.java │ ├── scenario/ │ │ ├── ActiveActiveFailoverIT.java │ │ ├── ClusterTopologyRefreshIT.java │ │ ├── ConnectionInterruptionIT.java │ │ ├── FakeApp.java │ │ ├── FaultInjectionClient.java │ │ ├── LagAwareStrategySslIT.java │ │ ├── MultiThreadedFakeApp.java │ │ ├── RecommendedSettings.java │ │ └── RestEndpointUtil.java │ ├── search/ │ │ └── hybrid/ │ │ └── FTHybridPostProcessingParamsTest.java │ ├── tls/ │ │ ├── ACLJedisIT.java │ │ ├── ACLRedisClientIT.java │ │ ├── ACLRedisSentinelClientIT.java │ │ ├── ClientAuthIT.java │ │ ├── ClientAuthJedisIT.java │ │ ├── ClientAuthRedisClientIT.java │ │ ├── ClientAuthRedisClusterClientIT.java │ │ ├── ClientAuthTestBase.java │ │ ├── JedisIT.java │ │ ├── JedisSentinelPoolIT.java │ │ ├── JedisTlsTestBase.java │ │ ├── RedisClientIT.java │ │ ├── RedisClientTlsTestBase.java │ │ ├── RedisClusterClientIT.java │ │ ├── RedisClusterTestBase.java │ │ ├── RedisSentinelClientIT.java │ │ ├── RedisSentinelTlsTestBase.java │ │ ├── SSLOptionsJedisIT.java │ │ ├── SSLOptionsJedisSentinelPoolIT.java │ │ ├── SSLOptionsRedisClientIT.java │ │ └── SSLOptionsRedisSentinelClientIT.java │ └── util/ │ ├── ACLTestUtil.java │ ├── AssertUtil.java │ ├── ByteArrayComparatorTest.java │ ├── ByteArrayMapMatcher.java │ ├── ByteArrayUtil.java │ ├── ClientKillerUtil.java │ ├── ClientTestUtil.java │ ├── CommandArgumentsMatchers.java │ ├── DelayTest.java │ ├── EnabledOnCommandCondition.java │ ├── EnvCondition.java │ ├── FragmentedByteArrayInputStream.java │ ├── GeoCoordinateMatcher.java │ ├── GeoRadiusResponseMatcher.java │ ├── JedisByteMapMatcher.java │ ├── JedisClusterCRC16Test.java │ ├── JedisClusterTestUtil.java │ ├── JedisSentinelTestUtil.java │ ├── JedisURIHelperTest.java │ ├── JsonObjectMapperTestUtil.java │ ├── ProtocolTestUtil.java │ ├── RedisConditions.java │ ├── RedisVersionCondition.java │ ├── RedisVersionUtil.java │ ├── ReflectionTestUtil.java │ ├── StreamEntryBinaryListMatcher.java │ ├── TestDataUtil.java │ ├── TestEnvUtil.java │ ├── TlsUtil.java │ └── VectorTestUtils.java └── resources/ ├── cert.pem ├── endpoints.json ├── endpoints_source.json ├── env/ │ ├── cluster-unbound/ │ │ └── config/ │ │ ├── node-7379-8379/ │ │ │ └── redis.conf │ │ ├── node-7380-8380/ │ │ │ └── redis.conf │ │ ├── node-7381-8381/ │ │ │ └── redis.conf │ │ ├── node-7382-8382/ │ │ │ └── redis.conf │ │ └── node-7383-8383/ │ │ └── redis.conf │ ├── config/ │ │ └── redis6-7/ │ │ └── node-sentinel-26381-36381/ │ │ └── redis.conf │ ├── docker-compose.yml │ ├── redis-uds/ │ │ └── config/ │ │ └── node-0/ │ │ └── redis.conf │ ├── redis1-2-5-8-sentinel/ │ │ └── config/ │ │ ├── node-6379-6390/ │ │ │ └── redis.conf │ │ ├── node-6380/ │ │ │ └── redis.conf │ │ ├── node-6383-6391/ │ │ │ └── redis.conf │ │ ├── node-6386/ │ │ │ └── redis.conf │ │ └── node-sentinel-26379-36379/ │ │ └── redis.conf │ ├── redis9-10/ │ │ └── config/ │ │ ├── node-6388/ │ │ │ └── redis.conf │ │ └── node-6389/ │ │ └── redis.conf │ ├── sentinel-standalone2-failover/ │ │ └── config/ │ │ ├── node-6384/ │ │ │ └── redis.conf │ │ ├── node-6385/ │ │ │ └── redis.conf │ │ └── node-sentinel-26381-36381/ │ │ └── redis.conf │ └── standalone2-sentinel/ │ └── config/ │ ├── node-6381-16381/ │ │ └── redis.conf │ ├── node-6382-16382/ │ │ └── redis.conf │ ├── node-sentinel-26380-36380/ │ │ └── redis.conf │ └── node-sentinel-26382-36382/ │ └── redis.conf ├── functions/ │ ├── keyspaceTriggers.js │ ├── pingpong.js │ ├── streamTriggers.js │ ├── withConfig.js │ ├── withFlags.js │ └── workingWIthHashes.js ├── junit-platform.properties ├── logback-test.xml ├── redismodule.h └── testmodule.c ================================================ FILE CONTENTS ================================================ ================================================ FILE: .github/CONTRIBUTING.md ================================================ # How to use Jedis Github Issue * Github issues SHOULD BE USED to report bugs and for DETAILED feature requests. Everything else belongs in the [Jedis Google Group](https://groups.google.com/g/jedis_redis) or [Jedis Github Discussions](https://github.com/redis/jedis/discussions). Please post general questions to Google Groups or Github discussions. These can be closed without response when posted to Github issues. # How to contribute by Pull Request 1. Fork Jedis repo on github ([how to fork a repo](https://docs.github.com/en/get-started/quickstart/fork-a-repo)) 2. Create a topic branch (`git checkout -b my_branch`) 3. Push to your remote branch (`git push origin my_branch`) 4. Create a pull request on github ([how to create a pull request](https://docs.github.com/en/pull-requests/collaborating-with-pull-requests/proposing-changes-to-your-work-with-pull-requests/creating-a-pull-request)) Create a branch with meaningful name, and do not modify the master branch directly. Please add unit tests to validate your changes work, then ensure your changes pass all unit tests. # Jedis Test Environment Jedis uses a Docker-based test environment as the primary method for running tests. A simplified local environment is also available for basic testing. Jedis integration tests use many Redis instances, so we use a `Makefile` to prepare the environment. ## Quick Start (Docker - Recommended) Start tests with `make test`. This will: 1. Start the Docker-based test environment 2. Run all tests 3. Stop and clean up the environment Set up test environments with `make start`, tear down those environments with `make stop`. # Jedis Test Environment Using Docker This guide explains how to bootstrap and manage a test environment for Jedis using Docker Compose. ## Workflow Steps 1. **Start the test environment** by running `make start` (examples below). 2. **Run tests** through your IDE, Maven, or other testing tools as needed. 3. **Stop the test environment** by running `make stop`. - This will stop and tear down the Docker containers running the Redis service. # Start the Test Environment Using Docker You can bootstrap the test environment for supported versions of Redis using the provided `make` targets. ## Option 1: Using `make` Targets To bring up the test environment for a specific Redis version use the following command: ```bash make start version=8.0 # Replace with desired version ``` To stop test environment: ```bash make stop ``` To run tests using the Docker environment: ```bash make test ``` ## Option 2: Using docker compose commands directly Docker compose file can be found in `src/test/resources/env` folder. - **Redis 8.4 (or other versions without custom env file)** ```bash rm -rf /tmp/redis-env-work export REDIS_VERSION=8.4 docker compose --env-file .env -f src/test/resources/env/docker-compose.yml up ``` - **Redis 7.4, 7.2, 6.2 (versions with custom env files)** ```bash rm -rf /tmp/redis-env-work export REDIS_VERSION=6.2 docker compose --env-file .env --env-file .env.v6.2 -f src/test/resources/env/docker-compose.yml up ``` # Local Test Environment (Simplified) For basic testing with a minimal local Redis setup (requires Redis to be installed locally): ```bash make start-local # Start local Redis instances (standalone + Unix socket) make test-local # Run tests against local environment make stop-local # Stop local Redis instances ``` **Note:** The local environment provides only the `standalone-0` endpoint and a Unix socket instance. For full test coverage, use the Docker-based environment. # Some rules of Jedis source code ## Code Convention * Jedis uses HBase Formatter introduced by [HBASE-5961](https://issues.apache.org/jira/browse/HBASE-5961) * You can import code style file (located to hbase-formatter.xml) to Eclipse, IntelliJ * line break by column count seems not working with IntelliJ * You can run ```make format``` anytime to reformat without IDEs * DO NOT format the source codes within `io.redis.examples` test package. * A test class name MUST NOT end with `Example`. ## Adding commands * Jedis uses many interfaces to structure commands * planned to write documentation about it, contribution is more welcome! * We need to add commands to all interfaces which have responsibility to expose * ex) We need to add ping() command to BasicCommands, and provide implementation to all of classes which implemented BasicCommands ## type <-> byte array conversion * string <-> byte array : use SafeEncoder.encode() * Caution: use String.toBytes() directly will break GBK support! * boolean, int, long, double -> byte array : use Protocol.toByteArray() Thanks! ================================================ FILE: .github/ISSUE_TEMPLATE ================================================ ### Expected behavior Write here what you're expecting ... ### Actual behavior Write here what happens instead ... ### Steps to reproduce: Please create a reproducible case of your problem. Make sure that case repeats consistently and it's not random 1. 2. 3. ### Redis / Jedis Configuration #### Jedis version: #### Redis version: #### Java version: ================================================ FILE: .github/actions/run-tests/action.yml ================================================ # Note: this action is used as a part of redis oss release and test automation in # redis-developer/redis-oss-release-automation repo name: 'Run Jedis Tests' description: 'Run Jedis tests in a containerized environment' inputs: redis_version: description: 'Redis version to test against' required: false client_libs_test_image_tag: description: 'Custom client libs test image tag to use instead of redis_version' required: false default: '' client_libs_test_image: description: 'Custom client libs test image name to use' required: false default: '' java_version: description: 'Java version to use' required: false default: '8' java_distribution: description: 'Java distribution to use' required: false default: 'temurin' specific_test: description: 'Run specific test(s) (optional)' required: false default: '' codecov_token: description: 'Codecov token for uploading coverage' required: false default: '' # repository and ref are reqired for correct checkout when using action # externally (e.g.: in redis-developer/redis-oss-release-automation) repository: description: 'Git repository to checkout' required: false default: '' ref: description: 'Git ref to checkout' required: false default: '' redis_env_work_dir: description: 'Redis env work directory' required: false default: '' redis_env_conf_dir: description: 'Redis env conf directory' required: false default: '' runs: using: 'composite' steps: - name: Checkout code uses: actions/checkout@v4 with: repository: ${{ inputs.repository }} ref: ${{ inputs.ref }} - name: Validate inputs and prepare environment shell: bash id: args env: REDIS_VERSION: ${{ inputs.redis_version }} CLIENT_LIBS_TEST_IMAGE: ${{ inputs.client_libs_test_image }} CLIENT_LIBS_TEST_IMAGE_TAG: ${{ inputs.client_libs_test_image_tag }} REDIS_ENV_WORK_DIR: ${{ inputs.redis_env_work_dir }} REDIS_ENV_CONF_DIR: ${{ inputs.redis_env_conf_dir }} REDIS_VERSION_LABEL: ${{ inputs.client_libs_test_image_tag || inputs.redis_version }} run: | make_args=() if [ -n "$CLIENT_LIBS_TEST_IMAGE" ]; then make_args+=(CLIENT_LIBS_TEST_IMAGE="$CLIENT_LIBS_TEST_IMAGE") fi if [ -n "$CLIENT_LIBS_TEST_IMAGE_TAG" ]; then make_args+=(CLIENT_LIBS_TEST_IMAGE_TAG="$CLIENT_LIBS_TEST_IMAGE_TAG") elif [ -n "$REDIS_VERSION" ]; then make_args+=(version="$REDIS_VERSION") else echo "Error: redis_version or client_libs_test_image_tag input is required" exit 1 fi echo "make_args=${make_args[*]}" | tee -a ${GITHUB_OUTPUT} # either custom docker tag name or actual redis version echo "redis_version_label=$REDIS_VERSION_LABEL" | tee -a ${GITHUB_OUTPUT} if [ -z "$REDIS_ENV_CONF_DIR" ]; then REDIS_ENV_CONF_DIR=$(readlink -f "${{ github.action_path }}/../../../src/test/resources/env") fi echo "redis_env_conf_dir=$REDIS_ENV_CONF_DIR" | tee -a ${GITHUB_OUTPUT} if [ -n "$REDIS_ENV_WORK_DIR" ]; then echo "redis_env_work_dir=$REDIS_ENV_WORK_DIR" | tee -a ${GITHUB_OUTPUT} else REDIS_ENV_WORK_DIR=$(mktemp -du) echo "redis_env_work_dir=$REDIS_ENV_WORK_DIR" | tee -a ${GITHUB_OUTPUT} fi - name: Set up Java uses: actions/setup-java@v4 with: java-version: ${{ inputs.java_version }} distribution: ${{ inputs.java_distribution }} - name: System setup shell: bash run: | sudo apt update sudo apt install -y make make compile-module - name: Cache dependencies uses: actions/cache@v4 with: path: | ~/.m2/repository /var/cache/apt key: jedis-${{hashFiles('**/pom.xml')}} - name: Set up Docker Compose environment shell: bash run: | mkdir -m 777 $REDIS_ENV_WORK_DIR make start ${{ steps.args.outputs.make_args }} env: REDIS_ENV_CONF_DIR: ${{ steps.args.outputs.redis_env_conf_dir }} REDIS_ENV_WORK_DIR: ${{ steps.args.outputs.redis_env_work_dir }} - name: Maven offline shell: bash run: | mvn -q dependency:go-offline - name: Build docs shell: bash run: | mvn javadoc:jar - name: Run Maven tests shell: bash run: | export TEST_ENV_PROVIDER=oss-docker export TEST_WORK_FOLDER=$REDIS_ENV_WORK_DIR echo $TEST_WORK_FOLDER if [ -z "$TESTS" ]; then mvn -B -Dwith-param-names=true clean compile verify else mvn -B -Dwith-param-names=true -Dtest=$TESTS clean verify fi env: REDIS_ENV_WORK_DIR: ${{ steps.args.outputs.redis_env_work_dir }} JVM_OPTS: "-XX:+HeapDumpOnOutOfMemoryError -XX:+ExitOnOutOfMemoryError -XX:HeapDumpPath=${{ runner.temp }}/heapdump-${{ steps.args.outputs.redis_version_label }}.hprof" TESTS: ${{ inputs.specific_test }} - name: Upload Heap Dumps if: failure() uses: actions/upload-artifact@v4 with: name: heap-dumps-${{ steps.args.outputs.redis_version_label }} path: ${{ runner.temp }}/heapdump-${{ steps.args.outputs.redis_version_label }}.hprof retention-days: 5 - name: Upload Surefire Dump File uses: actions/upload-artifact@v4 with: name: surefire-dumpstream path: target/surefire-reports/*.dumpstream - name: Publish Test Results uses: EnricoMi/publish-unit-test-result-action@v2 if: github.actor != 'dependabot[bot]' with: files: | target/surefire-reports/**/*.xml target/failsafe-reports/**/*.xml - name: Collect logs on failure if: failure() shell: bash run: | echo "Collecting logs from $REDIS_ENV_WORK_DIR..." ls -la $REDIS_ENV_WORK_DIR env: REDIS_ENV_WORK_DIR: ${{ steps.args.outputs.redis_env_work_dir }} - name: Upload logs on failure if: failure() uses: actions/upload-artifact@v4 with: name: redis-env-work-logs-${{ steps.args.outputs.redis_version_label }} path: ${{ steps.args.outputs.redis_env_work_dir }} - name: Tear down Docker Compose environment if: always() shell: bash run: | make stop continue-on-error: true - name: Upload merged coverage to Codecov if: inputs.codecov_token != '' uses: codecov/codecov-action@v5 with: files: ./target/site/jacoco/jacoco.xml flags: docker-${{ steps.args.outputs.redis_version_label }} name: merged-coverage fail_ci_if_error: false token: ${{ inputs.codecov_token }} - name: Upload test results to Codecov (unit + IT) if: inputs.codecov_token != '' && !cancelled() && (github.event_name == 'schedule' || github.event_name == 'push' || github.event_name == 'workflow_dispatch') uses: codecov/test-results-action@v1 with: fail_ci_if_error: false files: ./target/surefire-reports/TEST*,./target/failsafe-reports/TEST* flags: test-results-docker-${{ steps.args.outputs.redis_version_label }} token: ${{ inputs.codecov_token }} ================================================ FILE: .github/codecov.yml ================================================ codecov: # see https://docs.codecov.com/docs/codecovyml-reference branch: master coverage: status: # see https://docs.codecov.com/docs/commit-status project: default: target: auto # minimum coverage ratio that the commit must meet to be considered a success threshold: 5 # Allow the coverage to drop by %, and posting a success status branches: - master - '[0-9].*' comment: # see https://docs.codecov.com/docs/pull-request-comments layout: "condensed_header, condensed_files, condensed_footer" behavior: new require_changes: true ignore: - "**/*.txt" ================================================ FILE: .github/dependabot.yml ================================================ version: 2 updates: - package-ecosystem: "maven" directory: "/" schedule: interval: "weekly" ================================================ FILE: .github/release-drafter-config.yml ================================================ name-template: '$NEXT_MINOR_VERSION' tag-template: 'v$NEXT_MINOR_VERSION' filter-by-commitish: true commitish: master autolabeler: - label: 'maintenance' files: - '*.md' - '.github/*' - label: 'bug' branch: - '/bug-.+' - label: 'maintenance' branch: - '/maintenance-.+' - label: 'feature' branch: - '/feature-.+' categories: - title: '🔥 Breaking Changes' labels: - 'breakingchange' - title: '🧪 Experimental Features' labels: - 'experimental' - title: '🚀 New Features' labels: - 'feature' - 'enhancement' - title: '🐛 Bug Fixes' labels: - 'fix' - 'bugfix' - 'bug' - 'BUG' - title: '🧰 Maintenance' labels: - 'maintenance' - 'dependencies' - 'documentation' - 'docs' - 'testing' change-template: '- $TITLE (#$NUMBER)' exclude-labels: - 'skip-changelog' template: | # Changes $CHANGES ## Contributors We'd like to thank all the contributors who worked on this release! $CONTRIBUTORS ================================================ FILE: .github/workflows/codeql-analysis.yml ================================================ name: "CodeQL" on: workflow_dispatch: push: branches: [ master ] pull_request: # The branches below must be a subset of the branches above branches: [ master ] schedule: - cron: '31 4 * * 4' jobs: analyze: name: Analyze runs-on: ubuntu-latest permissions: security-events: write actions: read contents: read steps: - name: Checkout repository uses: actions/checkout@v4 # Initializes the CodeQL tools for scanning. - name: Initialize CodeQL uses: github/codeql-action/init@v3 with: languages: java dependency-caching: true build-mode: manual - name: Set up JDK 8 uses: actions/setup-java@v4 with: distribution: temurin java-version: '8' cache: maven - name: Build run: mvn -B package - name: Perform CodeQL Analysis uses: github/codeql-action/analyze@v3 ================================================ FILE: .github/workflows/docs.yml ================================================ name: Publish Docs on: push: branches: ["master"] permissions: contents: read pages: write id-token: write concurrency: group: "pages" cancel-in-progress: false jobs: build-and-deploy: concurrency: ci-${{ github.ref }} runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - uses: actions/setup-python@v5 with: python-version: 3.13 - name: Install dependencies run: | python -m pip install --upgrade pip pip install -r docs/requirements.txt - name: Build docs run: | mkdocs build -d docsbuild - name: Setup Pages uses: actions/configure-pages@v3 - name: Upload artifact uses: actions/upload-pages-artifact@v3 with: path: 'docsbuild' - name: Deploy to GitHub Pages id: deployment uses: actions/deploy-pages@v4 ================================================ FILE: .github/workflows/doctests.yml ================================================ name: Documentation Tests on: push: branches: - master - 'emb-examples' pull_request: workflow_dispatch: jobs: doctests: runs-on: ubuntu-latest services: redis: image: redis:latest options: >- --health-cmd "redis-cli ping" --health-interval 10s --health-timeout 5s --health-retries 5 ports: - 6379:6379 steps: - uses: actions/checkout@v4 - name: Cache dependencies uses: actions/cache@v4 with: path: | ~/.m2/repository /var/cache/apt key: jedis-${{hashFiles('**/pom.xml')}} - name: Set up Java uses: actions/setup-java@v4 with: java-version: '11' distribution: 'temurin' - name: Maven offline run: | mvn -q dependency:go-offline - name: Run doctests run: | mvn -Pdoctests clean compile test ================================================ FILE: .github/workflows/format_check.yml ================================================ name: Java Format Check on: workflow_dispatch: pull_request: paths: - '**/*.java' # Only trigger for Java file changes jobs: check-format: runs-on: ubuntu-latest steps: # Step 1: Checkout the PR code - name: Checkout code uses: actions/checkout@v3 # Step 2: Set up Java (if needed for format check tools) - name: Set up JDK uses: actions/setup-java@v4 with: java-version: '11' distribution: 'temurin' # Step 3: Fetch latest changes - name: Fetch latest changes run: git fetch origin - name: Get changed Java files id: changed_files run: | echo "::group::Changed Java Files" echo Base Branch: ${{ github.event.pull_request.base.ref }} CHANGED_FILES=$(git diff --name-only --diff-filter=A origin/${{ github.event.pull_request.base.ref }} | grep '\.java$' || true) echo "$CHANGED_FILES" echo "::endgroup::" # Write the multiline content to a file echo "$CHANGED_FILES" > changed_files.txt # Step 4: Get a list of changed Java files in the PR - name: Check Java file format run: | # Check if the changed_files.txt exists if [ ! -f changed_files.txt ]; then echo "No changed files found." exit 0 fi # Read the multiline content from the file CHANGED_FILES=$(cat changed_files.txt) # Ensure there are changed files if [ -z "$CHANGED_FILES" ]; then echo "No Java files changed." else echo "Processing the following changed Java files:" # Iterate over the CHANGED_FILES variable, assuming files are separated by newlines while IFS= read -r FILE; do # Skip empty lines if any if [ -n "$FILE" ]; then FILE_NAME=$(basename "$FILE") echo "Checking for $FILE_NAME" # Run your formatter validation for each file mvn formatter:validate -f formatter-pom.xml "-Dformatter.includes=**/$FILE_NAME" fi done <<< "$CHANGED_FILES" fi ================================================ FILE: .github/workflows/integration.yml ================================================ --- name: Build and Test using local environment on: push: paths-ignore: - 'docs/**' - '**/*.md' - '**/*.rst' branches: - master - '[0-9].*' - 'topic/**' pull_request: branches: - master - '[0-9].*' schedule: - cron: '0 1 * * *' # nightly build workflow_dispatch: jobs: build: name: Build and Test runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - name: Set up publishing to maven central uses: actions/setup-java@v4 with: java-version: '8' distribution: 'temurin' - name: System setup run: | sudo apt update sudo apt install -y make make system-setup - name: Cache dependencies uses: actions/cache@v4 with: path: | ~/.m2/repository /var/cache/apt key: jedis-${{hashFiles('**/pom.xml')}} - name: Maven offline run: | mvn -q dependency:go-offline - name: Build docs run: | mvn javadoc:jar - name: Run tests run: | export TEST_ENV_PROVIDER=oss-local make test-local env: JVM_OPTS: -Xmx3200m TERM: dumb ================================================ FILE: .github/workflows/release-drafter.yml ================================================ name: Release Drafter on: push: # branches to consider in the event; optional, defaults to all branches: - master workflow_dispatch: jobs: update_release_draft: runs-on: ubuntu-latest steps: # Drafts your next Release notes as Pull Requests are merged into "master" - uses: release-drafter/release-drafter@v5 with: # (Optional) specify config name to use, relative to .github/. Default: release-drafter.yml config-name: release-drafter-config.yml env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} ================================================ FILE: .github/workflows/snapshot.yml ================================================ --- name: Publish Snapshot on: push: branches: - master - '[0-9].x' workflow_dispatch: jobs: snapshot: name: Deploy Snapshot runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - name: Set up publishing to maven central uses: actions/setup-java@v4 with: java-version: '8' distribution: 'temurin' server-id: central server-username: MAVEN_USERNAME server-password: MAVEN_PASSWORD - name: Cache dependencies uses: actions/cache@v4 with: path: | ~/.m2/repository /var/cache/apt key: jedis-${{hashFiles('**/pom.xml')}} - name: mvn offline run: | mvn -q dependency:go-offline - name: deploy run: | mvn --no-transfer-progress \ -DskipTests deploy env: MAVEN_USERNAME: ${{secrets.OSSH_USERNAME}} MAVEN_PASSWORD: ${{secrets.OSSH_TOKEN}} ================================================ FILE: .github/workflows/stale-issues.yml ================================================ name: "Close stale issues" on: schedule: - cron: "0 0 * * *" permissions: {} jobs: stale: permissions: issues: write # to close stale issues (actions/stale) pull-requests: write # to close stale PRs (actions/stale) runs-on: ubuntu-latest steps: - uses: actions/stale@v3 with: repo-token: ${{ secrets.GITHUB_TOKEN }} stale-issue-message: 'This issue is marked stale. It will be closed in 30 days if it is not updated.' stale-pr-message: 'This pull request is marked stale. It will be closed in 30 days if it is not updated.' days-before-stale: 30 days-before-close: 30 stale-issue-label: "stale" stale-pr-label: "stale" operations-per-run: 10 remove-stale-when-updated: true only-labels: 'waiting-for-feedback' exempt-issue-labels: 'feedback-provided' exempt-pr-labels: 'feedback-provided' exempt-all-milestones: true ================================================ FILE: .github/workflows/test-on-docker.yml ================================================ --- name: Build and Test using a containerized environment run-name: "Build and Test using ${{ github.event.inputs.client_libs_test_image_tag != '' && format('image: {0}', github.event.inputs.client_libs_test_image_tag) || 'a containerized environment' }}" on: push: paths-ignore: - 'docs/**' - '**/*.md' - '**/*.rst' branches: - master - '[0-9].*' - 'feature/**' - 'topic/**' pull_request: branches: - master - '[0-9].*' - 'feature/**' schedule: - cron: '0 1 * * *' # nightly build workflow_dispatch: inputs: specific_test: description: 'Run specific test(s) (optional)' required: false default: '' client_libs_test_image_tag: description: 'Custom client libs test image tag to use instead of redis_version' required: false default: '' jobs: build: name: Build and Test if: github.event.inputs.client_libs_test_image_tag == '' runs-on: ubuntu-latest strategy: fail-fast: false matrix: redis_version: - "8.6" - "8.4" - "8.2" - "8.0" - "7.4" - "7.2" # - "6.2" steps: - name: Checkout code uses: actions/checkout@v4 - uses: ./.github/actions/run-tests with: redis_version: ${{ matrix.redis_version }} specific_test: ${{ github.event.inputs.specific_test || '' }} codecov_token: ${{ secrets.CODECOV_TOKEN }} redis_env_work_dir: ${{ github.workspace }}/redis-env-work redis_env_conf_dir: ${{ github.workspace }}/src/test/resources/env build_using_custom_image: name: Build and Test using custom image if: github.event.inputs.client_libs_test_image_tag != '' runs-on: ubuntu-latest steps: - name: Checkout code uses: actions/checkout@v4 - uses: ./.github/actions/run-tests with: client_libs_test_image_tag: ${{ github.event.inputs.client_libs_test_image_tag }} specific_test: ${{ github.event.inputs.specific_test || '' }} codecov_token: ${{ secrets.CODECOV_TOKEN }} redis_env_work_dir: ${{ github.workspace }}/redis-env-work redis_env_conf_dir: ${{ github.workspace }}/src/test/resources/env ================================================ FILE: .github/workflows/version-and-release.yml ================================================ name: Release on: release: types: [published] jobs: build: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - name: get version from tag id: get_version run: | realversion="${GITHUB_REF/refs\/tags\//}" realversion="${realversion//v/}" echo "VERSION=$realversion" >> $GITHUB_OUTPUT - name: Set up publishing to maven central uses: actions/setup-java@v4 with: java-version: '8' distribution: 'temurin' server-id: central server-username: MAVEN_USERNAME server-password: MAVEN_PASSWORD - name: mvn versions run: mvn versions:set -DnewVersion=${{ steps.get_version.outputs.VERSION }} - name: Install gpg key run: | cat <(echo -e "${{ secrets.OSSH_GPG_SECRET_KEY }}") | gpg --batch --import gpg --list-secret-keys --keyid-format LONG - name: Publish run: | mvn --no-transfer-progress \ --batch-mode \ -Dgpg.passphrase='${{ secrets.OSSH_GPG_SECRET_KEY_PASSWORD }}' \ -DskipTests deploy -P release env: MAVEN_USERNAME: ${{secrets.OSSH_USERNAME}} MAVEN_PASSWORD: ${{secrets.OSSH_TOKEN}} ================================================ FILE: .gitignore ================================================ .classpath *.iml *.ipr *.iws nb* .project .settings/ .gradle/ target/ build/ bin/ tags .idea .run *.aof *.rdb redis-git appendonlydir/ .DS_Store .vscode/settings.json ================================================ FILE: LICENSE ================================================ MIT License Copyright (c) 2021-2023, Redis, inc. Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ================================================ FILE: Makefile ================================================ PATH := ./redis-git/src:${PATH} # Supported test env versions SUPPORTED_TEST_ENV_VERSIONS := 8.6 8.4 8.2 8.0 7.4 7.2 6.2 DEFAULT_TEST_ENV_VERSION := 8.6 REDIS_ENV_WORK_DIR := $(or ${REDIS_ENV_WORK_DIR},/tmp/redis-env-work) TOXIPROXY_IMAGE := ghcr.io/shopify/toxiproxy:2.8.0 define REDIS1_CONF daemonize yes protected-mode no port 6379 requirepass foobared user acljedis on allcommands allkeys >fizzbuzz user deploy on allcommands allkeys >verify pidfile /tmp/redis1.pid logfile /tmp/redis1.log save "" appendonly no enable-module-command yes client-output-buffer-limit pubsub 256k 128k 5 endef # UDS REDIS NODES define REDIS_UDS daemonize yes protected-mode no port 0 pidfile /tmp/redis_uds.pid logfile /tmp/redis_uds.log unixsocket /tmp/redis_uds.sock unixsocketperm 777 save "" appendonly no endef # UNAVAILABLE REDIS NODES define REDIS_UNAVAILABLE_CONF daemonize yes protected-mode no port 6400 pidfile /tmp/redis_unavailable.pid logfile /tmp/redis_unavailable.log save "" appendonly no endef export REDIS1_CONF export REDIS_UDS export REDIS_UNAVAILABLE_CONF start-local: cleanup compile-module # Simple local test env that provides only "standalone-0" endpoint and an instance listening on Unix socket export TEST_ENV_PROVIDER=oss-source echo "$$REDIS1_CONF" | redis-server - echo "$$REDIS_UDS" | redis-server - echo "$$REDIS_UNAVAILABLE_CONF" | redis-server - cleanup: - rm -vf /tmp/redis*.log 2>/dev/null - rm dump.rdb appendonly.aof - 2>/dev/null stop-local: @for pidfile in \ /tmp/redis1.pid \ /tmp/redis_uds.pid; do \ if [ -f $$pidfile ]; then \ pid=$$(cat $$pidfile); \ if kill -0 $$pid 2>/dev/null; then \ echo "Stopping process $$pid from $$pidfile"; \ kill $$pid; \ sleep 1; \ if kill -0 $$pid 2>/dev/null; then \ echo "PID $$pid did not exit, forcing kill"; \ kill -9 $$pid; \ fi; \ fi; \ rm -f $$pidfile; \ fi; \ done [ -f /tmp/redis_unavailable.pid ] && kill `cat /tmp/redis_unavailable.pid` || true test-local: | start-local mvn-test-local stop-local mvn-test-local: @TEST_ENV_PROVIDER=oss-source mvn -Dwith-param-names=true -Dtest=${TEST} clean verify mvn-test: mvn -Dwith-param-names=true -Dtest=${TEST} clean verify format: mvn java-formatter:format system-setup: # Install gcc with Homebrew (macOS) or apt (Linux) if [ "$(shell uname)" = "Darwin" ]; then \ brew install gcc || true; \ else \ sudo apt install -y gcc g++; \ fi [ ! -e redis-git ] && git clone https://github.com/redis/redis.git --branch unstable --single-branch redis-git || true $(MAKE) -C redis-git clean $(MAKE) -C redis-git BUILD_TLS=yes compile-module: gcc -shared -o /tmp/testmodule.so -fPIC src/test/resources/testmodule.c # Start test environment with specific version using predefined docker compose setup start: @if [ -z "$(version)" ]; then \ version=$(arg); \ if [ -z "$$version" ]; then \ version="$(DEFAULT_TEST_ENV_VERSION)"; \ fi; \ fi; \ if [ -n "$$CLIENT_LIBS_TEST_IMAGE_TAG" ]; then \ echo "Using custom image tag: $$CLIENT_LIBS_TEST_IMAGE_TAG"; \ version=""; \ elif ! echo "$(SUPPORTED_TEST_ENV_VERSIONS)" | grep -qw "$$version"; then \ echo "Error: Invalid version '$$version'. Supported versions are: $(SUPPORTED_TEST_ENV_VERSIONS)."; \ exit 1; \ fi; \ default_env_file="src/test/resources/env/.env"; \ custom_env_file="src/test/resources/env/.env.v$$version"; \ env_files="--env-file $$default_env_file"; \ if [ -f "$$custom_env_file" ]; then \ env_files="$$env_files --env-file $$custom_env_file"; \ fi; \ rm -rf "$(REDIS_ENV_WORK_DIR)"; \ mkdir -p "$(REDIS_ENV_WORK_DIR)"; \ docker compose $$env_files -f src/test/resources/env/docker-compose.yml up -d --wait --quiet-pull; \ echo "Started test environment with Redis version $$version. " # Stop the test environment stop: docker compose -f src/test/resources/env/docker-compose.yml down; \ rm -rf "$(REDIS_ENV_WORK_DIR)"; \ echo "Stopped test environment and performed cleanup." test: | start mvn-test stop .PHONY: test test-local start start-local stop stop-local cleanup mvn-test-local mvn-test format system-setup compile-module ================================================ FILE: README.md ================================================ # Jedis [![Release](https://img.shields.io/github/release/redis/jedis.svg?sort=semver)](https://github.com/redis/jedis/releases/latest) [![Maven Central](https://img.shields.io/maven-central/v/redis.clients/jedis.svg)](https://central.sonatype.com/artifact/redis.clients/jedis) [![Javadocs](https://www.javadoc.io/badge/redis.clients/jedis.svg)](https://www.javadoc.io/doc/redis.clients/jedis) [![MIT licensed](https://img.shields.io/badge/license-MIT-blue.svg)](https://github.com/redis/jedis/blob/master/LICENSE) [![codecov](https://codecov.io/gh/redis/jedis/branch/master/graph/badge.svg?token=pAstxAAjYo)](https://codecov.io/gh/redis/jedis) [![Discord](https://img.shields.io/discord/697882427875393627?style=flat-square)](https://discord.gg/redis) ## What is Jedis? Jedis is a Java client for [Redis](https://github.com/redis/redis "Redis") designed for performance and ease of use. Are you looking for a high-level library to handle object mapping? See [redis-om-spring](https://github.com/redis/redis-om-spring)! ## How do I Redis? [Learn for free at Redis University](https://university.redis.io/academy/) [Try the Redis Cloud](https://redis.io/try-free/) [Dive in developer tutorials](https://redis.io/learn/) [Join the Redis community](https://redis.io/community/) [Work at Redis](https://redis.io/careers/jobs/) ## Supported Redis versions The most recent version of this library supports redis version [7.2](https://github.com/redis/redis/blob/7.2/00-RELEASENOTES), [7.4](https://github.com/redis/redis/blob/7.4/00-RELEASENOTES), [8.0](https://github.com/redis/redis/blob/8.0/00-RELEASENOTES), [8.2](https://github.com/redis/redis/blob/8.2/00-RELEASENOTES) and [8.4](https://github.com/redis/redis/blob/8.4/00-RELEASENOTES). The table below highlights version compatibility of the most-recent library versions with Redis and JDK versions. Compatibility means communication features, and Redis command capabilities. | Jedis version | Supported Redis versions | JDK Compatibility | |---------------|---------------------------------------|-------------------| | 3.9+ | 5.0 to 6.2 Family of releases | 8, 11 | | >= 4.0 | Version 5.0 to 7.2 Family of releases | 8, 11, 17 | | >= 5.0 | Version 6.0 to current | 8, 11, 17, 21 | | >= 5.2 | Version 7.2 to current | 8, 11, 17, 21 | | >= 6.0 | Version 7.2 to current | 8, 11, 17, 21 | | >= 7.0 | Version 7.2 to current | 8, 11, 17, 21 | ## Getting started To get started with Jedis, first add it as a dependency in your Java project. If you're using Maven, that looks like this: ```xml redis.clients jedis 7.1.0 ``` To use the cutting-edge Jedis, check [here](https://redis.github.io/jedis/jedis-maven/). Next, you'll need to connect to Redis. Consider installing a redis server with docker: ```bash docker run -p 6379:6379 -it redis:latest ``` You can instantiate a RedisClient like so: ```java RedisClient jedis = RedisClient.builder().hostAndPort("localhost", 6379).build(); ``` Now you can send commands: ```java jedis.sadd("planets", "Venus"); ``` ## Connecting to a Redis cluster Jedis lets you connect to Redis Clusters, supporting the [Redis Cluster Specification](https://redis.io/topics/cluster-spec). To do this, you'll need to connect using `RedisClusterClient`. See the example below: ```java Set jedisClusterNodes = new HashSet(); jedisClusterNodes.add(new HostAndPort("127.0.0.1", 7379)); jedisClusterNodes.add(new HostAndPort("127.0.0.1", 7380)); RedisClusterClient jedis = RedisClusterClient.builder().nodes(jedisClusterNodes).build(); ``` Now you can use the `RedisClusterClient` instance and send commands like you would with a standard pooled connection: ```java jedis.sadd("planets", "Mars"); ``` ## Support for Redis data types Jedis includes support for all [Redis data types](https://redis.io/docs/latest/develop/data-types/) and features such as [JSON](https://redis.io/docs/latest/develop/data-types/json/) and [VectorSets](https://redis.io/docs/latest/develop/data-types/vector-sets/). ## Failover Jedis supports retry and failover for your Redis deployments. This is useful when: 1. You have more than one Redis deployment. This might include two independent Redis servers or two or more Redis databases replicated across multiple [active-active Redis Enterprise](https://redis.io/docs/latest/operate/rs/databases/active-active/) clusters. 2. You want your application to connect to one deployment at a time and to fail over to the next available deployment if the first deployment becomes unavailable. For the complete failover configuration options and examples, see the [Jedis failover docs](https://redis.github.io/jedis/failover/). ## Token-Based Authentication Jedis supports Token-Based authentication (TBA) starting with 5.3.0 GA release. This feature is complemented by an extension library that enhances the developer experience and provides most of the components required for TBA functionality. Notably, the extension library includes built-in support for **Microsoft EntraID**, offering a seamless integration as part of the generic solution. For more details and examples, please refer to the [Advanced Usage](https://redis.github.io/jedis/advanced-usage/) documentation. ## Documentation The [Jedis documentation site](https://redis.github.io/jedis/) contains several useful articles for using Jedis. You can also check the [latest Jedis Javadocs](https://www.javadoc.io/doc/redis.clients/jedis/latest/index.html). Some specific use-case examples can be found in [`redis.clients.jedis.examples` package](https://github.com/redis/jedis/tree/master/src/test/java/redis/clients/jedis/examples/) of the test source codes. ## Troubleshooting If you run into trouble or have any questions, we're here to help! Hit us up on the [Redis Discord Server](http://discord.gg/redis) or [Jedis GitHub Discussions](https://github.com/redis/jedis/discussions). ## Contributing We'd love your contributions! Bug reports are always welcome! [You can open a bug report on GitHub](https://github.com/redis/jedis/issues/new). You can also contribute documentation -- or anything to improve Jedis. Please see [contribution guideline](https://github.com/redis/jedis/blob/master/.github/CONTRIBUTING.md) for more details. ## License Jedis is licensed under the [MIT license](https://github.com/redis/jedis/blob/master/LICENSE). ## Sponsorship [![Redis Logo](https://raw.githubusercontent.com/redis/jedis/master/redis-logo-full-color-rgb.png)](https://redis.io/) ================================================ FILE: docs/Dockerfile ================================================ FROM squidfunk/mkdocs-material COPY requirements.txt . RUN pip install -r requirements.txt ================================================ FILE: docs/README.md ================================================ # Jedis Documentation This documentation uses [MkDocs](https://www.mkdocs.org/) to generate the static site. See [mkdocs.yml](../mkdocs.yml) for the configuration. To develop the documentation locally, you can use the included [Dockerfile](Dockerfile) to build a container with all the dependencies, and run it to preview your changes: ```bash # in docs/ docker build -t squidfunk/mkdocs-material . # cd .. docker run --rm -it -p 8000:8000 -v ${PWD}:/docs squidfunk/mkdocs-material ``` ================================================ FILE: docs/advanced-usage.md ================================================ # Advanced Usage ## Transactions To do transactions in Jedis, you have to wrap operations in a transaction block, very similar to pipelining: ```java jedis.watch (key1, key2, ...); Transaction t = jedis.multi(); t.set("foo", "bar"); t.exec(); ``` Note: when you have any method that returns values, you have to do like this: ```java Transaction t = jedis.multi(); t.set("fool", "bar"); Response result1 = t.get("fool"); t.zadd("foo", 1, "barowitch"); t.zadd("foo", 0, "barinsky"); t.zadd("foo", 0, "barikoviev"); Response> sose = t.zrange("foo", 0, -1); // get the entire sortedset t.exec(); // dont forget it String foolbar = result1.get(); // use Response.get() to retrieve things from a Response int soseSize = sose.get().size(); // on sose.get() you can directly call Set methods! // List allResults = t.exec(); // you could still get all results at once, as before ``` Note that a Response Object does not contain the result before t.exec() is called (it is a kind of a Future). Forgetting exec gives you exceptions. In the last lines, you see how transactions/pipelines were dealt with before version 2. You can still do it that way, but then you need to extract objects from a list, which contains also Redis status messages. Note 2: Redis does not allow to use intermediate results of a transaction within that same transaction. This does not work: ```java // this does not work! Intra-transaction dependencies are not supported by Redis! jedis.watch(...); Transaction t = jedis.multi(); if(t.get("key1").equals("something")) t.set("key2", "value2"); else t.set("key", "value"); ``` However, there are some commands like setnx, that include such a conditional execution. Those are of course supported within transactions. You can build your own customized commands using EVAL / LUA scripting. ## Pipelining Sometimes you need to send a bunch of different commands. A very cool way to do that, and have better performance than doing it the naive way, is to use pipelining. This way you send commands without waiting for response, and you actually read the responses at the end, which is faster. Here is how to do it: ```java Pipeline p = jedis.pipelined(); p.set("fool", "bar"); p.zadd("foo", 1, "barowitch"); p.zadd("foo", 0, "barinsky"); p.zadd("foo", 0, "barikoviev"); Response pipeString = p.get("fool"); Response> sose = p.zrange("foo", 0, -1); p.sync(); int soseSize = sose.get().size(); Set setBack = sose.get(); ``` For more explanations see code comments in the transaction section. ## Publish/Subscribe To subscribe to a channel in Redis, create an instance of JedisPubSub and call subscribe on the Jedis instance: ```java class MyListener extends JedisPubSub { public void onMessage(String channel, String message) { } public void onSubscribe(String channel, int subscribedChannels) { } public void onUnsubscribe(String channel, int subscribedChannels) { } public void onPSubscribe(String pattern, int subscribedChannels) { } public void onPUnsubscribe(String pattern, int subscribedChannels) { } public void onPMessage(String pattern, String channel, String message) { } } MyListener l = new MyListener(); jedis.subscribe(l, "foo"); ``` Note that subscribe is a blocking operation because it will poll Redis for responses on the thread that calls subscribe. A single JedisPubSub instance can be used to subscribe to multiple channels. You can call subscribe or psubscribe on an existing JedisPubSub instance to change your subscriptions. ## Monitoring To use the monitor command you can do something like the following: ```java new Thread(new Runnable() { public void run() { Jedis j = new Jedis("localhost"); for (int i = 0; i < 100; i++) { j.incr("foobared"); try { Thread.sleep(200); } catch (InterruptedException e) { } } j.disconnect(); } }).start(); jedis.monitor(new JedisMonitor() { public void onCommand(String command) { System.out.println(command); } }); ``` ## Token-Based Authentication Starting with version 5.3.0 GA, Jedis supports token-based authentication. The [redis-authx-entraid](https://github.com/redis/jvm-redis-authx-entraid) repository provides the necessary components that Jedis utilizes to enable this functionality. Additionally, support for Microsoft EntraID has been fully implemented and is now available as an extension for Azure Managed Redis (AMR) and Azure Cache for Redis(ACR). ### Using With Custom Identity Provider Jedis provides a token-based authentication mechanism with a generic identity provider of your choice. For custom use of this feature, you will need to provide an implementation of `IdentityProvider` and `IdentityProviderConfig` and configure it in the way Jedis expects. You will have the required interfaces from transitive Jedis dependencies; ``` redis.clients.authentication redis-authx-core ${version} ``` **An example to get started:** ```java public class YourCustomIdentityProviderConfig implements IdentityProviderConfig { ... } public class YourCustomIdentityProvider implements IdentityProvider { ... } ``` Then configure Jedis like this: ```java IdentityProviderConfig yourCustomIdentityProviderConfig = new YourCustomIdentityProviderConfig(); TokenAuthConfig tokenAuthConfig = TokenAuthConfig.builder().identityProviderConfig(yourCustomIdentityProviderConfig); JedisClientConfig config = DefaultJedisClientConfig.builder() .authXManager(new AuthXManager(tokenAuthConfig)).build(); ... ``` ### Using With Microsoft EntraID Extension for EntraID is fully integrated and ready to use with [Azure Managed Redis](https://azure.microsoft.com/en-us/products/managed-redis)(AMR) or [Azure Cache for Redis](https://azure.microsoft.com/en-us/products/cache/)(ACR). All you need is to add the EntraID dependency and code for configuration for chosen authentication type with Microsoft EntraID service. To get started, add the `redis-authx-entraid` extension as dependency; ``` redis.clients.authentication redis-authx-entraid ${version} ``` After adding the dependency, configure it using `EntraIDTokenAuthConfigBuilder`: ```java ... TokenAuthConfig tokenAuthConfig = EntraIDTokenAuthConfigBuilder.builder() .expirationRefreshRatio(0.8F) .clientId("yourClientId") .secret("yourClientSecret") .authority("yourAuthority") .scopes("yourRedisScopes").build(); AuthXManager authXManager = new AuthXManager(tokenAuthConfig); JedisClientConfig config = DefaultJedisClientConfig.builder() .authXManager(authXManager).build(); ... ``` Here you will see the `AuthXManager` class that is built into Jedis. Essentially it integrates the extension into Jedis and handles the authentication process. For other available configurations, detailed information and usage of Jedis with Microsoft EntraID, please refer to the [official guide](https://redis.io/docs/latest/develop/clients/jedis/amr/) **Setting Up AMR or ACR with Microsoft EntraID:** To use Microsoft EntraID with AMR or ACR, for sure you will need to set up and configure your AMR/ACR services as well as Microsoft EntraID. The following resources provide useful information; [Azure Managed Redis](https://azure.microsoft.com/en-us/products/managed-redis) [Azure Cache for Redis](https://azure.microsoft.com/en-us/products/cache/) [Microsoft Entra ID for AMR authentication](https://learn.microsoft.com/en-us/azure/azure-cache-for-redis/managed-redis/managed-redis-entra-for-authentication) [Microsoft Entra ID for ACR authentication](https://learn.microsoft.com/en-us/azure/azure-cache-for-redis/cache-azure-active-directory-for-authentication) [Use Microsoft Entra](https://learn.microsoft.com/en-us/azure/app-service/configure-authentication-provider-aad?tabs=workforce-configuration) ## HostAndPortMapper When running Jedis in certain network environments, such as behind a NAT gateway, or within container orchestration systems like Docker or Kubernetes, the host and port that the client needs to connect to might be different from the host and port that the Redis Cluster nodes report. To handle this discrepancy, Jedis provides the `HostAndPortMapper` interface. This allows you to dynamically map the address reported by a Redis node to a different address that the client can actually reach. You can implement this either by creating a dedicated class or by using a concise lambda expression. ### Example use case: NAT or Docker with different advertised ports Suppose you run a Redis cluster inside Docker on a remote host. Inside the cluster config, nodes announce addresses like: ``` 172.18.0.2:6379 172.18.0.3:6379 172.18.0.4:6379 ``` But externally, you reach them through the host IP with mapped ports: ``` my-redis.example.com:7001 my-redis.example.com:7002 my-redis.example.com:7003 ``` ### Implementing with a Dedicated Class You can provide your mapping logic by creating a class that implements the `HostAndPortMapper` interface. This approach is useful for more complex mapping logic or for reusability. First, define your custom mapper class: ```java public class DockerNATMapper implements HostAndPortMapper { // Key: The address reported by Redis (internal). // Value: The address the client should connect to (external). private final Map mapping; public DockerNATMapper(Map mapping) { this.mapping = mapping; } @Override public HostAndPort getHostAndPort(HostAndPort hostAndPort) { return mapping.getOrDefault(hostAndPort, hostAndPort); } } ``` Then, instantiate this class and pass it to the RedisClusterClient builder: ```java Map nodeMapping = new HashMap<>(); nodeMapping.put(new HostAndPort("172.18.0.2", 6379), new HostAndPort("my-redis.example.com", 7001)); nodeMapping.put(new HostAndPort("172.18.0.3", 6379), new HostAndPort("my-redis.example.com", 7002)); nodeMapping.put(new HostAndPort("172.18.0.4", 6379), new HostAndPort("my-redis.example.com", 7002)); Set initialNodes = new HashSet<>(); // seed node initialNodes.add(new HostAndPort("my-redis.example.com", 7001)); HostAndPortMapper mapper = new DockerNATMapper(nodeMapping); JedisClientConfig jedisClientConfig = DefaultJedisClientConfig.builder() .user("myuser") .password("mypassword") .hostAndPortMapper(mapper) .build(); RedisClusterClient jedisCluster = RedisClusterClient.builder() .nodes(initialNodes) .clientConfig(jedisClientConfig) .build(); ``` Now, when RedisClusterClient discovers a node at "172.18.0.2:6379", the mapper will translate it to "localhost:7001" before attempting to connect. ### Implementing with a Lambda Expression Since HostAndPortMapper is a functional interface (it has only one abstract method), you can also provide the implementation more concisely using a lambda expression. This is often preferred for simpler, inline mapping logic. ```java Map nodeMapping = new HashMap<>(); nodeMapping.put(new HostAndPort("172.18.0.2", 6379), new HostAndPort("my-redis.example.com", 7001)); nodeMapping.put(new HostAndPort("172.18.0.3", 6379), new HostAndPort("my-redis.example.com", 7002)); nodeMapping.put(new HostAndPort("172.18.0.4", 6379), new HostAndPort("my-redis.example.com", 7002)); Set initialNodes = new HashSet<>(); initialNodes.add(new HostAndPort("my-redis.example.com", 7001)); HostAndPortMapper mapper = internalAddress -> nodeMapping.getOrDefault(internalAddress, internalAddress); JedisClientConfig jedisClientConfig = DefaultJedisClientConfig.builder() .user("myuser") .password("mypassword") .hostAndPortMapper(mapper) .build(); RedisClusterClient jedisCluster = RedisClusterClient.builder() .nodes(initialNodes) .clientConfig(jedisClientConfig) .build(); ``` ## Miscellaneous ### A note about String and Binary - what is native? Redis/Jedis talks a lot about Strings. And here [[http://redis.io/topics/internals]] it says Strings are the basic building block of Redis. However, this stress on strings may be misleading. Redis' "String" refer to the C char type (8 bit), which is incompatible with Java Strings (16-bit). Redis sees only 8-bit blocks of data of predefined length, so normally it doesn't interpret the data (it's "binary safe"). Therefore in Java, byte[] data is "native", whereas Strings have to be encoded before being sent, and decoded after being retrieved by the SafeEncoder. This has some minor performance impact. In short: if you have binary data, don't encode it into String, but use the binary versions. ### A note on Redis' master/slave distribution A Redis network consists of redis servers, which can be either masters or slaves. Slaves are synchronized to the master (master/slave replication). However, master and slaves look identical to a client, and slaves do accept write requests, but they will not be propagated "up-hill" and could eventually be overwritten by the master. It makes sense to route reads to slaves, and write demands to the master. Furthermore, being a slave doesn't prevent from being considered master by another slave. ================================================ FILE: docs/css/extra.css ================================================ /* extra.css */ .md-header { background-color: #FB2A2C; } ================================================ FILE: docs/failover.md ================================================ # Automatic Failover and Failback with Jedis > API was significantly changed in 7.0.0. Please follow the migration guide below. > > This feature is experimental and may change in future versions. Jedis supports failover and failback for your Redis deployments. This is useful when: 1. You have more than one Redis deployment. This might include two independent Redis servers or two or more Redis databases replicated across multiple [active-active Redis Enterprise](https://docs.redis.com/latest/rs/databases/active-active/) clusters. 2. You want your application to connect to and use one deployment at a time. 3. You want your application to fail over to the next available deployment if the current deployment becomes unavailable. 4. You want your application to fail back to the original deployment when it becomes available again. Jedis will fail over to a subsequent Redis deployment after reaching a configurable failure threshold. This failure threshold is implemented using a [circuit breaker pattern](https://en.wikipedia.org/wiki/Circuit_breaker_design_pattern). You can also configure Jedis to retry failed calls to Redis. Once a maximum number of retries have been exhausted, the circuit breaker will record a failure. When the circuit breaker reaches its failure threshold, a failover will be triggered on the subsequent operation. In the background, Jedis executes configured health checks to determine when a Redis deployment is available again. When this occurs, Jedis will fail back to the original deployment after a configurable grace period. The remainder of this guide describes: * A basic failover and health check configuration * Supported retry and circuit breaker settings * Failback and the database selection API * Dynamic database management for adding and removing databases at runtime * Dynamic weight management for runtime priority adjustments (since 7.4.0) We recommend that you read this guide carefully and understand the configuration settings before enabling Jedis failover in production. ## Migration from 6.x to 7.x In Jedis 6.x, failover was supported using special constructor for `UnifiedJedis`. In Jedis 7.x, failover is supported using `MultiDbClient` and `MultiDbConfig.builder`: ```java // Jedis 6.x JedisClientConfig config = DefaultJedisClientConfig.builder().user("cache").password("secret").build(); ClusterConfig[] clientConfigs = new ClusterConfig[2]; clientConfigs[0] = new ClusterConfig(new HostAndPort("redis-east.example.com", 14000), config); clientConfigs[1] = new ClusterConfig(new HostAndPort("redis-west.example.com", 14000), config); MultiClusterClientConfig.Builder builder = new MultiClusterClientConfig.Builder(clientConfigs); // ... MultiClusterPooledConnectionProvider provider = new MultiClusterPooledConnectionProvider(builder.build()); UnifiedJedis client = new UnifiedJedis(provider); // Jedis 7.x // MultiClusterClientConfig was renamed to MultiDbConfig and MultiDbClient with convenient builder was added MultiDbConfig multiConfig = MultiDbConfig.builder() .database(DatabaseConfig.builder(east, config).weight(1.0f).build()) .database(DatabaseConfig.builder(west, config).weight(0.5f).build()) .build(); // Use MultiDbClient instead of UnifiedJedis MultiDbClient multiDbClient = MultiDbClient.builder().multiDbConfig(multiConfig).build(); ``` For more details on configuration options see sections below. ## Installing optional dependencies Jedis failover support is provided by optional dependencies. To use failover, add the following dependencies to your project: ```xml io.github.resilience4j resilience4j-all 1.7.1 io.github.resilience4j resilience4j-circuitbreaker 1.7.1 io.github.resilience4j resilience4j-retry 1.7.1 ``` ## Basic usage To configure Jedis for failover, you specify a weighted list of Redis databases. Jedis will connect to the Redis database in the list with the highest weight. If the highest-weighted database becomes unavailable, Jedis will attempt to connect to the database with the next highest weight in the list, and so on. Database weights determine the priority for selecting which database becomes active. Weights can be configured at initialization and can also be changed dynamically at runtime (introduced in version 7.4.0), allowing you to adjust active database selection priorities without recreating the client. Suppose you run two Redis deployments. We'll call them `redis-east` and `redis-west`. You want your application to first connect to `redis-east`. If `redis-east` becomes unavailable, you want your application to connect to `redis-west`. Let's look at one way of configuring Jedis for this scenario. First, start by defining the initial configuration for each Redis database available and prioritize them using weights. ```java JedisClientConfig config = DefaultJedisClientConfig.builder() .user("cache").password("secret") .socketTimeoutMillis(5000).connectionTimeoutMillis(5000).build(); // Custom pool config per database can be provided ConnectionPoolConfig poolConfig = new ConnectionPoolConfig(); poolConfig.setMaxTotal(8); poolConfig.setMaxIdle(8); poolConfig.setMinIdle(0); poolConfig.setBlockWhenExhausted(true); poolConfig.setMaxWait(Duration.ofSeconds(1)); poolConfig.setTestWhileIdle(true); poolConfig.setTimeBetweenEvictionRuns(Duration.ofSeconds(1)); HostAndPort east = new HostAndPort("redis-east.example.com", 14000); HostAndPort west = new HostAndPort("redis-west.example.com", 14000); MultiDbConfig.Builder multiConfig = MultiDbConfig.builder() .database(DatabaseConfig.builder(east, config).connectionPoolConfig(poolConfig).weight(1.0f).build()) .database(DatabaseConfig.builder(west, config).connectionPoolConfig(poolConfig).weight(0.5f).build()); ``` The configuration above represents your two Redis deployments: `redis-east` and `redis-west`. Continue using the `MultiDbConfig.Builder` builder to set your preferred retry and failover configuration. Then build a `MultiDbClient`: ```java // Configure circuit breaker for failure detection multiConfig .failureDetector(MultiDbConfig.CircuitBreakerConfig.builder() .slidingWindowSize(1000) // Sliding window size in number of calls .failureRateThreshold(50.0f) // percentage of failures to trigger circuit breaker .minNumOfFailures(500) // Minimum number of failures before circuit breaker is tripped .build()) .failbackSupported(true) // Enable failback .failbackCheckInterval(1000) // Check every second the unhealthy database to see if it has recovered .gracePeriod(10000) // Keep database disabled for 10 seconds after it becomes unhealthy // Optional: configure retry settings .commandRetry(MultiDbConfig.RetryConfig.builder() .maxAttempts(3) // Maximum number of retry attempts (including the initial call) .waitDuration(500) // Number of milliseconds to wait between retry attempts .exponentialBackoffMultiplier(2) // Exponential backoff factor multiplied against wait duration between retries .build()) // Optional: configure fast failover .fastFailover(true) // Force closing connections to unhealthy database on failover .retryOnFailover(false); // Do not retry failed commands during failover MultiDbClient multiDbClient = MultiDbClient.builder() .multiDbConfig(multiConfig.build()) .build(); ``` In the configuration here, we've set a sliding window size of 1000 and a failure rate threshold of 50%. This means that a failover will be triggered only if both 500 out of any 1000 calls to Redis fail (i.e., the failure rate threshold is reached) and the minimum number of failures is also met. You can now use this `MultiDbClient` instance in your application to execute Redis commands. ## Configuration options Under the hood, Jedis' failover support relies on [resilience4j](https://resilience4j.readme.io/docs/getting-started), a fault-tolerance library that implements [retry](https://resilience4j.readme.io/docs/retry) and [circuit breakers](https://resilience4j.readme.io/docs/circuitbreaker). Once you configure a `MultiDbClient`, each call to Redis is decorated with a resilience4j retry and circuit breaker. By default, any call that throws a `JedisConnectionException` will be retried up to 3 times. If the call fail then the circuit breaker will record a failure. The circuit breaker maintains a record of failures in a sliding window data structure. If the failure rate reaches a configured threshold (e.g., when 50% of the last 1000 calls have failed), then the circuit breaker's state transitions from `CLOSED` to `OPEN`. When this occurs, Jedis will attempt to connect to the next Redis database with the highest weight in its client configuration list. The supported retry and circuit breaker settings, and their default values, are described below. You can configure any of these settings using the `MultiDbConfig.Builder` builder. Refer the basic usage above for an example of this. ### Retry configuration Configuration for command retry behavior is encapsulated in `MultiDbConfig.RetryConfig`. Jedis uses the following retry settings: | Setting | Default value | Description | |----------------------------------|----------------------------|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| | Max retry attempts | 3 | Maximum number of retry attempts (including the initial call) | | Retry wait duration | 500 ms | Number of milliseconds to wait between retry attempts | | Wait duration backoff multiplier | 2 | Exponential backoff factor multiplied against wait duration between retries. For example, with a wait duration of 1 second and a multiplier of 2, the retries would occur after 1s, 2s, 4s, 8s, 16s, and so on. | | Retry included exception list | [JedisConnectionException] | A list of Throwable classes that count as failures and should be retried. | | Retry ignored exception list | null | A list of Throwable classes to explicitly ignore for the purposes of retry. | To disable retry, set `maxAttempts` to 1. ### Circuit breaker configuration For failover, Jedis uses a circuit breaker to detect when a Redis database has failed. Failover configuration is encapsulated in `MultiDbConfig.CircuitBreakerConfig` and can be provided using the `MultiDbConfig.Builder.failureDetector()`. Jedis uses the following circuit breaker settings: | Setting | Default value | Description | |-----------------------------------------|----------------------------|--------------------------------------------------------------------------------------------------------------------------| | Sliding window size | 2 | The size of the sliding window. Units depend on sliding window type. The size represents seconds. | | Threshold min number of failures | 1000 | Minimum number of failures before circuit breaker is tripped. | | Failure rate threshold | `10.0f` | Percentage of calls within the sliding window that must fail before the circuit breaker transitions to the `OPEN` state. | | Circuit breaker included exception list | [JedisConnectionException] | A list of Throwable classes that count as failures and add to the failure rate. | | Circuit breaker ignored exception list | null | A list of Throwable classes to explicitly ignore for failure rate calculations. | | ### Health Check Configuration and Customization The `MultiDbClient` includes a comprehensive health check system that continuously monitors the availability of Redis databases to enable automatic failover and failback. The health check system serves several critical purposes in the failover architecture: 1. **Proactive Monitoring**: Continuously monitors passive databases that aren't currently receiving traffic 2. **Failback Detection**: Determines when a previously failed database has recovered and is ready to accept traffic 3. **Circuit Breaker Integration**: Works with the circuit breaker pattern to manage database state transitions 4. **Customizable Strategies**: Supports pluggable health check implementations for different deployment scenarios The health check system operates independently of your application traffic, running background checks at configurable intervals to assess database health without impacting performance. #### Available Health Check Types ##### 1. PingStrategy (Default) The `PingStrategy` is the default health check implementation that uses Redis's `PING` command to verify both connectivity and write capability. **Use Cases:** - General-purpose health checking for most Redis deployments - Verifying both read and write operations - Simple connectivity validation **How it works:** - Sends `PING` command to the Redis server - Expects exact response `"PONG"` to consider the server healthy - Any exception or unexpected response marks the server as unhealthy ##### 2. LagAwareStrategy [PREVIEW] (Redis Enterprise) The `LagAwareStrategy` is designed specifically for Redis Enterprise Active-Active deployments and uses the Redis Enterprise REST API to check database availability and replication lag. **Use Cases:** - Redis Enterprise Active-Active (CRDB) deployments - Scenarios where replication lag tolerance is critical - Enterprise environments with REST API access **How it works:** - Queries Redis Enterprise REST API for database availability - Optionally validates replication lag against configurable thresholds - Automatically discovers database IDs based on endpoint hostnames **Example Configuration:** ```java BiFunction, MultiDbConfig.StrategySupplier> healthCheckStrategySupplier = (HostAndPort dbHostPort, Supplier credentialsSupplier) -> { LagAwareStrategy.Config lagConfig = LagAwareStrategy.Config.builder(dbHostPort, credentialsSupplier) .interval(5000) // Check every 5 seconds .timeout(3000) // 3 second timeout .extendedCheckEnabled(true) .build(); return (hostAndPort, jedisClientConfig) -> new LagAwareStrategy(lagConfig); }; // Configure REST API endpoint and credentials HostAndPort restEndpoint = new HostAndPort("redis-enterprise-db-fqdn", 9443); Supplier credentialsSupplier = () -> new DefaultRedisCredentials("rest-api-user", "pwd"); MultiDbConfig.StrategySupplier lagawareStrategySupplier = healthCheckStrategySupplier.apply( restEndpoint, credentialsSupplier); MultiDbConfig.DatabaseConfig dbConfig = MultiDbConfig.DatabaseConfig.builder(hostAndPort, clientConfig) .healthCheckStrategySupplier(lagawareStrategySupplier) .build(); ``` ##### 3. Custom Health Check Strategies You can implement custom health check strategies by implementing the `HealthCheckStrategy` interface. **Use Cases:** - Application-specific health validation logic - Integration with external monitoring systems - Custom performance or latency-based health checks Use the `healthCheckStrategySupplier()` method to provide a custom health check implementation: ```java // Custom strategy supplier MultiDbConfig.StrategySupplier customStrategy = (hostAndPort, jedisClientConfig) -> { // Return your custom HealthCheckStrategy implementation return new MyCustomHealthCheckStrategy(hostAndPort, jedisClientConfig); }; MultiDbConfig.DatabaseConfig dbConfig = MultiDbConfig.DatabaseConfig.builder(hostAndPort, clientConfig) .healthCheckStrategySupplier(customStrategy) .weight(1.0f) .build(); ``` You can implement custom health check strategies by implementing the `HealthCheckStrategy` interface: ```java MultiDbConfig.StrategySupplier pingStrategy = (hostAndPort, jedisClientConfig) -> { return new HealthCheckStrategy() { @Override public int getInterval() { return 1000; // Check every second } @Override public int getTimeout() { return 500; // 500ms timeout } @Override public int getNumProbes() { return 1; } @Override public ProbingPolicy getPolicy() { return ProbingPolicy.BuiltIn.ANY_SUCCESS; } @Override public int getDelayInBetweenProbes() { return 100; } @Override public HealthStatus doHealthCheck(Endpoint endpoint) { try (UnifiedJedis jedis = new UnifiedJedis(hostAndPort, jedisClientConfig)) { String result = jedis.ping(); return "PONG".equals(result) ? HealthStatus.HEALTHY : HealthStatus.UNHEALTHY; } catch (Exception e) { return HealthStatus.UNHEALTHY; } } @Override public void close() { // Cleanup resources if needed } }; }; MultiDbConfig.DatabaseConfig dbConfig = MultiDbConfig.DatabaseConfig.builder(hostAndPort, clientConfig) .healthCheckStrategySupplier(pingStrategy) .build(); ``` #### Disabling Health Checks Use the `healthCheckEnabled(false)` method to completely disable health checks: ```java MultiDbConfig.DatabaseConfig dbConfig = MultiDbConfig.DatabaseConfig.builder(east, config) .healthCheckEnabled(false) // Disable health checks entirely .build(); ``` ### Fallback configuration Jedis uses the following fallback settings: | Setting | Default value | Description | |-------------------------|-------------------------------------------------------|----------------------------------------------------| | Fallback exception list | [CallNotPermittedException, JedisConnectionException] | A list of Throwable classes that trigger fallback. | ### Failover callbacks In the event that Jedis fails over, you may wish to take some action. This might include logging a warning, recording a metric, or externally persisting the database connection state, to name just a few examples. For this reason, `MultiDbClient` lets you register a custom callback that will be called whenever Jedis fails over to a new database. To use this feature, you'll need to design a class that implements `java.util.function.Consumer`. This class must implement the `accept` method, as you can see below. ```java public class FailoverReporter implements Consumer { @Override public void accept(DatabaseSwitchEvent e) { System.out.println("Jedis failover to database: " + e.getDatabaseName() + " due to " + e.getReason()); } } ``` DatabaseSwitchEvent consumer can be registered as follows: ```java FailoverReporter reporter = new FailoverReporter(); MultiDbClient client = MultiDbClient.builder() .databaseSwitchListener(reporter) .build(); ``` The provider will call your `accept` whenever a failover occurs. or directly using lambda expression: ```java MultiDbClient client = MultiDbClient.builder() .databaseSwitchListener(event -> System.out.println("Switched to: " + event.getEndpoint())) .build(); ``` ## Failing back Jedis supports automatic failback based on health checks or manual failback using the database selection API. ## Failback scenario When a failover is triggered, Jedis will attempt to connect to the next Redis server based on the weights of server configurations you provide at setup. For example, recall the `redis-east` and `redis-west` deployments from the basic usage example above. Jedis will attempt to connect to `redis-east` first. If `redis-east` becomes unavailable (and the circuit breaker transitions), then Jedis will attempt to use `redis-west`. Now suppose that `redis-east` eventually comes back online. You will likely want to fail your application back to `redis-east`. ### Automatic failback based on health checks When health checks are enabled, Jedis automatically monitors the health of all configured databases, including those that are currently inactive due to previous failures. The automatic failback process works as follows: 1. **Continuous Monitoring**: Health checks run continuously for all databases, regardless of their current active status 2. **Recovery Detection**: When a previously failed database passes the required number of consecutive health checks, it's marked as healthy 3. **Weight-Based Failback**: If automatic failback is enabled and a recovered database has a higher weight than the currently active database, Jedis will automatically switch to the recovered database 4. **Grace Period Respect**: Failback only occurs after the configured grace period has elapsed since the database was marked as unhealthy ## Manual Failback using the database selection API Once you've determined that it's safe to fail back to a previously-unavailable database, you need to decide how to trigger the failback. There are two ways to accomplish this: `MultiDbClient` exposes a method that you can use to manually select which database Jedis should use. To select a different database to use, pass the database's `HostAndPort` to `setActiveDatabase()`: ``` Endpoint endpoint = new HostAndPort("redis-east.example.com", 14000); client.setActiveDatabase(endpoint); ``` This method is thread-safe. If you decide to implement manual failback, you will need a way for external systems to trigger this method in your application. For example, if your application exposes a REST API, you might consider creating a REST endpoint to call `setActiveDatabase` and fail back the application. ## Dynamic Weight Management > Introduced in version 7.4.0 Jedis allows you to dynamically adjust database weights at runtime without recreating the `MultiDbClient`. **Important**: Weight determines the **priority for selecting which database becomes the active database**. At any given time, only ONE database is active and receives all traffic. Weight does not distribute load across databases - it determines which single database Jedis will prefer to use as the active connection. This is useful for scenarios where you need to change the active database selection priority based on operational conditions, such as: - Changing which database should be preferred during planned maintenance - Adjusting selection priority based on database performance or regional preferences - Implementing controlled switchover between databases - Responding to changing infrastructure conditions ### Getting and Setting Weights The `MultiDbClient` provides methods to query and modify database weights at runtime: ```java // Get the current weight of a database HostAndPort east = new HostAndPort("redis-east.example.com", 14000); float currentWeight = client.getWeight(east); System.out.println("Current weight: " + currentWeight); // Set a new weight for a database client.setWeight(east, 2.0f); ``` ### Weight Constraints When setting weights dynamically, the following constraints apply: - **Weight must be greater than 0**: Attempting to set a weight of 0 or negative values will throw an `IllegalArgumentException` - **Endpoint must exist**: The endpoint must be part of the configured databases, otherwise a `JedisValidationException` is thrown ### Runtime Behavior When you change a database's weight at runtime: 1. **Immediate Effect on Selection**: The weight change takes effect immediately for future active database selection decisions during failover or failback 2. **Automatic Failback Trigger**: If automatic failback is enabled and you increase a database's weight above the currently active database, Jedis will automatically switch to the higher-weight database during the next periodic failback check (if the database is healthy and the grace period has elapsed) 3. **No Disruption**: Changing weights does not interrupt ongoing operations or force an immediate switch 4. **Single Active Database**: Remember that only one database is active at any time - all traffic goes to that single database ### How Weight Affects Database Selection During failover or failback, Jedis selects the active database by: 1. Filtering for healthy databases (passing health checks, not in grace period, circuit breaker not open) 2. Sorting the healthy databases by weight in descending order (highest weight first) 3. Selecting the first database from this sorted list as the active database ### Example: Changing Active Database Priority Here's a practical example of dynamically adjusting weights to control which database should be active: ```java // Initial configuration - primary has higher weight, so it will be selected as active HostAndPort primary = new HostAndPort("redis-primary.example.com", 6379); HostAndPort secondary = new HostAndPort("redis-secondary.example.com", 6379); MultiDbConfig config = MultiDbConfig.builder() .database(DatabaseConfig.builder(primary, clientConfig).weight(2.0f).build()) .database(DatabaseConfig.builder(secondary, clientConfig).weight(1.0f).build()) .failbackSupported(true) .failbackCheckInterval(1000) .build(); MultiDbClient client = MultiDbClient.builder() .multiDbConfig(config) .build(); // At this point, 'primary' is the active database (weight 2.0 > 1.0) // Before planned maintenance on primary, make secondary the preferred database client.setWeight(secondary, 3.0f); // Now secondary has highest weight // During the next failback check, Jedis will switch to secondary as the active database // After maintenance, restore primary as the preferred database client.setWeight(primary, 4.0f); // Now primary has highest weight again // During the next failback check, Jedis will switch back to primary as the active database ``` ### Monitoring Database Switches You can combine weight changes with failover callbacks to monitor when the active database switches due to weight adjustments: ```java MultiDbClient client = MultiDbClient.builder() .multiDbConfig(config) .databaseSwitchListener(event -> { System.out.println("Active database switched to: " + event.getEndpoint() + " due to: " + event.getReason()); }) .build(); // Change weight - may trigger automatic failback to switch active database client.setWeight(secondary, 5.0f); ``` ## Dynamic Database Management Jedis allows you to dynamically add and remove database endpoints at runtime without recreating the `MultiDbClient`. This provides flexibility for scenarios such as: - Adding new database replicas or regions as they become available - Removing databases during planned maintenance or decommissioning - Scaling your Redis infrastructure dynamically - Responding to infrastructure changes without application restarts ### Adding Databases at Runtime The `MultiDbClient` provides two overloaded methods for adding databases dynamically: #### Method 1: Using DatabaseConfig This method provides maximum flexibility for advanced configurations including custom health check strategies, connection pool settings, and other database-specific options. ```java // Create a fully configured DatabaseConfig HostAndPort newEndpoint = new HostAndPort("redis-new.example.com", 6379); JedisClientConfig clientConfig = DefaultJedisClientConfig.builder() .user("cache").password("secret").build(); DatabaseConfig databaseConfig = DatabaseConfig.builder(newEndpoint, clientConfig) .weight(1.5f).connectionPoolConfig(poolConfig).healthCheckEnabled(true).build(); // Add the database to the client client.addDatabase(databaseConfig); ``` #### Method 2: Using Endpoint, Weight, and ClientConfig This is a convenience method for simpler configurations when you don't need advanced customization. ```java HostAndPort newEndpoint = new HostAndPort("redis-new.example.com", 6379); JedisClientConfig clientConfig = DefaultJedisClientConfig.builder() .user("cache").password("secret").build(); // Add the database with basic configuration client.addDatabase(newEndpoint, 1.5f, clientConfig); ``` ### Removing Databases at Runtime You can remove database endpoints dynamically using the `removeDatabase()` method: ```java HostAndPort endpointToRemove = new HostAndPort("redis-old.example.com", 6379); // Remove the database from the client client.removeDatabase(endpointToRemove); ``` ### Behavior and Constraints When adding or removing databases, the following behavior applies: #### Adding Databases - **Immediate Availability**: The new endpoint becomes available for failover operations immediately after being added - **Health Check Integration**: If health checks are configured, the new database will be monitored according to the configured health check strategy - **Duplicate Prevention**: Attempting to add an endpoint that already exists will throw a `JedisValidationException` - **Weight-Based Selection**: The new database participates in weight-based active database selection according to its configured weight #### Removing Databases - **Automatic Failover**: If the removed endpoint is currently the active database, Jedis will automatically failover to the next available healthy endpoint based on weight priority - **Last Database Protection**: You cannot remove the last remaining endpoint - attempting to do so will throw a `JedisValidationException` - **Non-Existent Endpoint**: Attempting to remove an endpoint that doesn't exist will throw a `JedisValidationException` - **Resource Cleanup**: The removed database's connections and resources are properly closed and cleaned up - **Health Check Cleanup**: Health checks for the removed database are automatically stopped and unregistered ### Querying Configured Databases You can retrieve the set of all currently configured database endpoints: ```java Set endpoints = client.getDatabaseEndpoints(); System.out.println("Configured databases: " + endpoints); ``` ### Complete Example: Dynamic Database Management Here's a practical example demonstrating dynamic database management: ```java // Initial setup with two databases, primary and secondary. HostAndPort primary = new HostAndPort("redis-primary.example.com", 6379); HostAndPort secondary = new HostAndPort("redis-secondary.example.com", 6379); JedisClientConfig clientConfig = DefaultJedisClientConfig.builder() .user("cache").password("secret").build(); MultiDbConfig config = MultiDbConfig.builder() .database(DatabaseConfig.builder(primary, clientConfig).weight(2.0f).build()) .database(DatabaseConfig.builder(secondary, clientConfig).weight(1.0f).build()) .failbackSupported(true) .build(); MultiDbClient client = MultiDbClient.builder() .multiDbConfig(config) .databaseSwitchListener(event -> { System.out.println("Switched to: " + event.getEndpoint() + " due to: " + event.getReason()); }) .build(); // Add a new database in a different region HostAndPort newRegion = new HostAndPort("redis-eu.example.com", 6379); client.addDatabase(newRegion, 1.0f, clientConfig); System.out.println("Added new database: " + newRegion); // Verify the database was added Set endpoints = client.getDatabaseEndpoints(); System.out.println("Current databases: " + endpoints); // Output: [redis-primary.example.com:6379, redis-secondary.example.com:6379, redis-eu.example.com:6379] // Later, remove the secondary database for maintenance client.removeDatabase(secondary); System.out.println("Removed database: " + secondary); // If secondary was active, automatic failover occurs to primary or newRegion // Verify the database was removed endpoints = client.getDatabaseEndpoints(); System.out.println("Current databases: " + endpoints); // Output: [redis-primary.example.com:6379, redis-eu.example.com:6379] ``` ### Thread Safety Both `addDatabase()` and `removeDatabase()` methods are thread-safe and can be called concurrently from multiple threads. The client ensures that database additions and removals are properly synchronized with ongoing operations. ## Troubleshooting Failover and Failback Issues #### Health Checks Always Report Unhealthy **Common causes:** - Timeout too aggressive for network conditions - Authentication issues with Redis server - Network connectivity problems **Solutions:** ```java // Increase timeout values HealthCheckStrategy.Config config = HealthCheckStrategy.Config.builder() .timeout(3000) // Increase from default 1000ms .build(); ``` #### Intermittent Health Check Failures **Solutions:** ```java // Require more consecutive successes for stability HealthCheckStrategy.Config config = HealthCheckStrategy.Config.builder() .interval(5000) // Less frequent checks .timeout(2000) // More generous timeout .build(); ``` #### Slow Failback After Recovery **Solutions:** ```java // Faster recovery configuration HealthCheckStrategy.Config config = HealthCheckStrategy.Config.builder() .interval(1000) // More frequent checks .build(); // Adjust failback timing MultiDbConfig multiConfig = MultiDbConfig.builder() .gracePeriod(5000) // Shorter grace period .build(); ``` ## Need help or have questions? For assistance with this automatic failover and failback feature, [start a discussion](https://github.com/redis/jedis/discussions/new?category=q-a). ================================================ FILE: docs/faq.md ================================================ # Frequently Asked Questions ## If you get `java.net.SocketTimeoutException: Read timed out` exception Try setting own `timeout` value when constructing `JedisPool` using the following constructor: ```java JedisPool(GenericObjectPoolConfig poolConfig, String host, int port, int timeout) ``` where `timeout` is given as milliseconds. Default `timeout` value is **2 seconds**. ## JedisPool blocks after getting 8 connections JedisPool defaults to 8 connections, you can change this in the PoolConfig: ```java JedisPoolConfig poolConfig = new JedisPoolConfig(); poolConfig.setMaxTotal(maxTotal); // maximum active connections poolConfig.setMaxIdle(maxIdle); // maximum idle connections ``` Take into account that `JedisPool` inherits commons-pool [BaseObjectPoolConfig](https://commons.apache.org/proper/commons-pool/api-2.3/org/apache/commons/pool2/impl/BaseObjectPoolConfig.html) which has a lot of configuration parameters. We've set some defined ones which suit most of the cases. In case, you experience [issues](https://github.com/xetorthio/jedis/issues?utf8=%E2%9C%93&q=is%3Aissue+is%3Aopen+JedisPool) tuning these parameters may help. ## How to configure the buffer size of socket(s) The buffer size of all Jedis sockets in an application can be configured through system property. Buffer size of input stream can be configured by setting `jedis.bufferSize.input` or `jedis.bufferSize` system property. Buffer size of output stream can be configured by setting `jedis.bufferSize.output` or `jedis.bufferSize` system property. If you want to set the buffer size of both input and output stream to same value, you can just set `jedis.bufferSize`. Note: This feature is available since Jedis 4.2.0. ## How to avoid cluster initialization error As of Jedis 4.0.0, a `JedisClusterOperationException` is raised with the message `Could not initialize cluster slots cache.` when the cluster initialization process fails. Should you would want to avoid this error (for example, creating `JedisConnectionFactory` to an unavailable cluster for a spring-data-redis `Bean`), set the system property `jedis.cluster.initNoError` to any value. In the console, add the option `-Djedis.cluster.initNoError`. In an application, `System.setProperty("jedis.cluster.initNoError", "");` can be set before creating any cluster object. Note: This feature is available since Jedis 4.4.2. ================================================ FILE: docs/index.md ================================================ {% include 'README.md' %} ================================================ FILE: docs/jedis-maven.md ================================================ ## Use Jedis as a maven dependency: ### Official Releases ```xml redis.clients jedis 6.2.0 ``` ### Snapshots ```xml sonatype-snapshots https://oss.sonatype.org/content/repositories/snapshots false true ``` and ```xml redis.clients jedis 7.0.0-SNAPSHOT ``` ================================================ FILE: docs/migration-guides/v3-to-v4-primitives.md ================================================ ## The following methods now return primitive values: \>> `long`/`boolean`/`double` instead of `Long`/`Boolean`/`Double`: - dbSize() - lastsave() - slowlogLen() - clientId() - clientUnblock(long clientId, UnblockType unblockType) - clientKill(ClientKillParams params) - aclDelUser(String name) - aclDelUser(byte[] name) - move(String key, int dbIndex) - move(byte[] key, int dbIndex) - waitReplicas(int replicas, long timeout) - waitReplicas(String key, int replicas, long timeout) - exists(String key) - exists(byte[] key) - exists(String... keys) - exists(byte[]... keys) - persist(String key) - expire(String key, long seconds) - expire(byte[] key, long seconds) - pexpire(String key, long milliseconds) - pexpire(byte[] key, long milliseconds) - expireAt(String key, long unixTime) - expireAt(byte[] key, long unixTime) - pexpireAt(String key, long millisecondsTimestamp) - pexpireAt(byte[] key, long millisecondsTimestamp) - ttl(String key) - ttl(byte[] key) - pttl(String key) - touch(String key) - touch(byte[] key) - touch(String... keys) - touch(byte[]... keys) - setbit(String key, long offset, boolean value) - setbit(byte[] key, long offset, boolean value) - getbit(String key, long offset) - getbit(byte[] key, long offset) - setrange(String key, long offset, String value) - setrange(byte[] key, long offset, byte[] value) - setnx(String key, String value) - setnx(byte[] key, byte[] value) - incr(String key) - incr(byte[] key) - decr(String key) - decr(byte[] key) - incrBy(String key, long increment) - incrBy(byte[] key, long increment) - decrBy(String key, long decrement) - decrBy(byte[] key, long decrement) - incrByFloat(String key, double increment) - incrByFloat(byte[] key, double increment) - append(String key, String value) - append(byte[] key, byte[] value) - hset(String key, String field, String value) - hset(byte[] key, byte[] field, byte[] value) - hset(String key, Map hash) - hset(byte[] key, Map hash) - hsetnx(String key, String field, String value) - hsetnx(byte[] key, byte[] field, byte[] value) - hincrBy(String key, String field, long value) - hincrBy(byte[] key, byte[] field, long value) - hincrByFloat(String key, String field, double value) - hincrByFloat(byte[] key, byte[] field, double value) - hexists(String key, String field) - hexists(byte[] key, byte[] field) - hdel(String key, String... field) - hdel(byte[] key, byte[]... field) - hlen(String key) - hlen(byte[] key) - rpush(String key, String... string) - rpush(byte[] key, byte[]... args) - lpush(String key, String... string) - lpush(byte[] key, byte[]... args) - llen(String key) - llen(byte[] key) - lrem(String key, long count, String value) - lrem(byte[] key, long count, byte[] value) - sadd(String key, String... member) - sadd(byte[] key, byte[]... member) - scard(String key) - scard(byte[] key) - sismember(String key, String member) - sismember(byte[] key, byte[] member) - strlen(String key) - strlen(byte[] key) - zadd(String key, double score, String member) - zadd(byte[] key, double score, byte[] member) - zadd(String key, double score, String member, ZAddParams params) - zadd(byte[] key, double score, byte[] member, ZAddParams params) - zadd(String key, Map scoreMembers) - zadd(byte[] key, Map scoreMembers) - zadd(String key, Map scoreMembers, ZAddParams params) - zadd(byte[] key, Map scoreMembers, ZAddParams params) - zrem(String key, String... members) - zrem(byte[] key, byte[]... members) - zincrby(String key, double increment, String member) - zincrby(byte[] key, double increment, byte[] member) - zcard(String key) - zcard(byte[] key) - zcount(String key, double min, double max) - zcount(byte[] key, double min, double max) - zcount(String key, String min, String max) - zcount(byte[] key, byte[] min, byte[] max) - zremrangeByRank(String key, long start, long stop) - zremrangeByRank(byte[] key, long start, long stop) - zremrangeByScore(String key, double min, double max) - zremrangeByScore(byte[] key, double min, double max) - zremrangeByScore(String key, String min, String max) - zremrangeByScore(byte[] key, byte[] min, byte[] max) - zlexcount(String key, String min, String max) - zlexcount(byte[] key, byte[] min, byte[] max) - zremrangeByLex(String key, String min, String max) - zremrangeByLex(byte[] key, byte[] min, byte[] max) - linsert(String key, ListPosition where, String pivot, String value) - linsert(byte[] key, ListPosition where, byte[] pivot, byte[] value) - lpushx(String key, String... string) - lpushx(byte[] key, byte[]... arg) - rpushx(String key, String... string) - rpushx(byte[] key, byte[]... arg) - del(String key) - del(byte[] key) - del(String... keys) - unlink(String key) - unlink(byte[] key) - unlink(String... keys) - bitcount(String key) - bitcount(byte[] key) - bitcount(String key, long start, long end) - bitcount(byte[] key, long start, long end) - bitpos(String key, boolean value) - bitpos(String key, boolean value, BitPosParams params) - pfadd(String key, String... elements) - pfadd(byte[] key, byte[]... elements) - pfcount(byte[]... keys) - geoadd(String key, double longitude, double latitude, String member) - geoadd(byte[] key, double longitude, double latitude, byte[] member) - geoadd(String key, Map memberCoordinateMap) - geoadd(byte[] key, Map memberCoordinateMap) - geoadd(String key, GeoAddParams params, Map memberCoordinateMap) - geoadd(byte[] key, GeoAddParams params, Map memberCoordinateMap) - hstrlen(String key, String field) - hstrlen(byte[] key, byte[] field) - xlen(String key) - xlen(byte[] key) - xack(String key, String group, StreamEntryID... ids) - xack(byte[] key, byte[] group, byte[]... ids) - xgroupDestroy(String key, String groupname) - xgroupDestroy(byte[] key, byte[] consumer) - xgroupDelConsumer( String key, String groupname, String consumername) - xgroupDelConsumer(byte[] key, byte[] consumer, byte[] consumerName) - xdel(String key, StreamEntryID... ids) - xdel(byte[] key, byte[]... ids) - xtrim(String key, long maxLen, boolean approximate) - xtrim(byte[] key, long maxLen, boolean approximateLength) - xtrim(String key, XTrimParams params) - xtrim(byte[] key, XTrimParams params) - clusterKeySlot(String key) - clusterCountKeysInSlot(int slot) - msetnx(String... keysvalues) - msetnx(byte[]... keysvalues) - renamenx(String oldkey, String newkey) - renamenx(byte[] oldkey, byte[] newkey) - sdiffstore(String dstkey, String... keys) - sdiffstore(byte[] dstkey, byte[]... keys) - sinterstore(String dstkey, String... keys) - sinterstore(byte[] dstkey, byte[]... keys) - smove(String srckey, String dstkey, String member) - smove(byte[] srckey, byte[] dstkey, byte[] member) - sort(String key, String dstkey) - sort(byte[] key, byte[] dstkey) - sort(String key, SortingParams sortingParameters, String dstkey) - sort(byte[] key, SortingParams sortingParameters, byte[] dstkey) - sunionstore(String dstkey, String... keys) - sunionstore(byte[] dstkey, byte[]... keys) - zdiffStore(String dstkey, String... keys) - zdiffStore(byte[] dstkey, byte[]... keys) - zinterstore(String dstkey, String... sets) - zinterstore(byte[] dstkey, byte[]... sets) - zinterstore(String dstkey, ZParams params, String... sets) - zinterstore(byte[] dstkey, ZParams params, byte[]... sets) - zunionstore(String dstkey, String... sets) - zunionstore(byte[] dstkey, byte[]... sets) - zunionstore(String dstkey, ZParams params, String... sets) - zunionstore(byte[] dstkey, ZParams params, byte[]... sets) - bitop(BitOP op, String destKey, String... srcKeys) - bitop(BitOP op, byte[] destKey, byte[]... srcKeys) - georadiusStore(String key, double longitude, double latitude, double radius, GeoUnit unit, GeoRadiusParam param, GeoRadiusStoreParam storeParam) - georadiusStore(byte[] key, double longitude, double latitude, double radius, GeoUnit unit, GeoRadiusParam param, GeoRadiusStoreParam storeParam) - georadiusByMemberStore(String key, String member, double radius, GeoUnit unit, GeoRadiusParam param, GeoRadiusStoreParam storeParam) - georadiusByMemberStore(byte[] key, byte[] member, double radius, GeoUnit unit, GeoRadiusParam param, GeoRadiusStoreParam storeParam) - copy(String srcKey, String dstKey, int db, boolean replace) - copy(byte[] srcKey, byte[] dstKey, int db, boolean replace) - copy(String srcKey, String dstKey, boolean replace) - copy(byte[] srcKey, byte[] dstKey, boolean replace) ================================================ FILE: docs/migration-guides/v3-to-v4-zset-list.md ================================================ ## Each of the following sorted set methods now return a Java `List` instead of a `Set`: - zrange(byte[] key, long start, long stop) - zrange(String key, long start, long stop) - zrevrange(byte[] key, long start, long stop) - zrevrange(String key, long start, long stop) - zrangeWithScores(byte[] key, long start, long stop) - zrangeWithScores(String key, long start, long stop) - zrevrangeWithScores(byte[] key, long start, long stop) - zrevrangeWithScores(String key, long start, long stop) - zrandmember(byte[] key, long count) - zrandmember(String key, long count) - zrandmemberWithScores(byte[] key, long count) - zrandmemberWithScores(String key, long count) - zpopmax(byte[] key, int count) - zpopmax(String key, int count) - zpopmin(byte[] key, int count) - zpopmin(String key, int count) - zrangeByScore(byte[] key, double min, double max) - zrangeByScore(String key, double min, double max) - zrangeByScore(byte[] key, byte[] min, byte[] max) - zrangeByScore(String key, String min, String max) - zrevrangeByScore(byte[] key, double max, double min) - zrevrangeByScore(String key, double max, double min) - zrangeByScore(byte[] key, double min, double max, int offset, int count) - zrangeByScore(String key, double min, double max, int offset, int count) - zrevrangeByScore(byte[] key, byte[] max, byte[] min) - zrevrangeByScore(String key, String max, String min) - zrangeByScore(byte[] key, byte[] min, byte[] max, int offset, int count) - zrangeByScore(String key, String min, String max, int offset, int count) - zrevrangeByScore(byte[] key, double max, double min, int offset, int count) - zrevrangeByScore(String key, double max, double min, int offset, int count) - zrangeByScoreWithScores(byte[] key, double min, double max) - zrangeByScoreWithScores(String key, double min, double max) - zrevrangeByScoreWithScores(byte[] key, double max, double min) - zrevrangeByScoreWithScores(String key, double max, double min) - zrangeByScoreWithScores(byte[] key, double min, double max, int offset, int count) - zrangeByScoreWithScores(String key, double min, double max, int offset, int count) - zrevrangeByScore(byte[] key, byte[] max, byte[] min, int offset, int count) - zrevrangeByScore(String key, String max, String min, int offset, int count) - zrangeByScoreWithScores(byte[] key, byte[] min, byte[] max) - zrangeByScoreWithScores(String key, String min, String max) - zrevrangeByScoreWithScores(byte[] key, byte[] max, byte[] min) - zrevrangeByScoreWithScores(String key, String max, String min) - zrangeByScoreWithScores(byte[] key, byte[] min, byte[] max, int offset, int count) - zrangeByScoreWithScores(String key, String min, String max, int offset, int count) - zrevrangeByScoreWithScores(byte[] key, double max, double min, int offset, int count) - zrevrangeByScoreWithScores(String key, double max, double min, int offset, int count) - zrevrangeByScoreWithScores(byte[] key, byte[] max, byte[] min, int offset, int count) - zrevrangeByScoreWithScores(String key, String max, String min, int offset, int count) - zrangeByLex(byte[] key, byte[] min, byte[] max) - zrangeByLex(String key, String min, String max) - zrangeByLex(byte[] key, byte[] min, byte[] max, int offset, int count) - zrangeByLex(String key, String min, String max, int offset, int count) - zrevrangeByLex(byte[] key, byte[] max, byte[] min) - zrevrangeByLex(String key, String max, String min) - zrevrangeByLex(byte[] key, byte[] max, byte[] min, int offset, int count) - zrevrangeByLex(String key, String max, String min, int offset, int count) ================================================ FILE: docs/migration-guides/v3-to-v4.md ================================================ # Jedis 4 Breaking Changes - The `BinaryJedis` and `BinaryJedisCluster` classes have been removed. The methods from these classes are available in the `Jedis` and `JedisCluster` classes respectively. - The following cases now throws an `IllegalStateException` instead of a `JedisDataException`. - `Cannot use Jedis when in Multi. Please use Transaction or reset Jedis state.` - `Cannot use Jedis when in Pipeline. Please use Pipeline or reset Jedis state.` - The Redis transaction methods `multi()`, `exec()` and `discard()` have been removed from `Pipeline`. - The `execGetResponse()` method has been removed from `Transaction`. - The `watch()` and `unwatch()` methods from the `Transaction` class are unsupported within MULTI (i.e., after the `multi()` method). However, `watch()` and `unwatch ()` can still be used before calling MULTI. - The `JedisCluster` constructors with `GenericObjectPoolConfig` now accept `GenericObjectPoolConfig`. - All `JedisCluster` constructors now throw a `JedisClusterOperationException` if unable to connect to any of the provided `HostAndPort(s)`. Previously, the connection would go into an unusable state. - `JedisCluster.getClusterNodes()` returns `Map` instead of `Map`. - `JedisCluster.getConnectionFromSlot(int)` returns `Connection` instead of `Jedis`. - `JedisNoReachableClusterNodeException` has been removed. `JedisClusterOperationException`, with a similar message, is thrown instead. - `JedisClusterMaxAttemptsException` has been removed. `JedisClusterOperationException`, with a similar message, is thrown instead. - `JedisExhaustedPoolException` has been removed. A `JedisException` with a similar message is thrown instead. - [Many sorted set methods](v3-to-v4-zset-list.md) return a Java `List` instead of a `Set`. [See the complete list](v3-to-v4-zset-list.md). - [Many methods return primitive values](v3-to-v4-primitives.md)) (`long`/`boolean`/`double` instead of `Long`/`Boolean`/ `Double`). [See the complete list](v3-to-v4-primitives.md). - `scriptExists(byte[])` method now returns `Boolean` instead of `Long`. - `scriptExists(byte[]...)` method now returns `List` instead of `List`. - In the [`xadd`](https://redis.io/commands/XADD) method with StreamEntryID parameter, sending untyped `null` raises an exception. Casting the `null` to StreamEntryID (`(StreamEntryID) null`) resolves this issue. - In the [`xrange`](https://redis.io/commands/XRANGE) and [`xrevrange`](https://redis.io/commands/xrevrange) methods with StreamEntryID parameters, sending untyped `null`s for both start and end parameters raises an exception. Casting the `null`s to StreamEntryID (`(StreamEntryID) null`) resolves this issue. - The return type of `Jedis.shutdown()` is now `void`. Previously, it would return null. - The `eval` and `evalsha` methods are now non-blocking. These methods were blocking in Jedis 3.x. - The `HostAndPort.localhost` constant has been removed. - The following methods have been removed from HostAndPort class: - `extractParts()` - `parseString()` - `convertHost()` - `setLocalhost()` - `getLocalhost()` - `getLocalHostQuietly()` - The following classes have been moved to the `redis.clients.jedis.args` package. - `BitOP` - `GeoUnit` - `ListPosition` - The following classes have been moved to the `redis.clients.jedis.params` package. - `BitPosParams` - `ScanParams` - `SortingParams` - `ZParams` - The following classes have been moved to the `redis.clients.jedis.resps` package. - `AccessControlLogEntry` - `AccessControlUser` - `GeoRadiusResponse` - `ScanResult` - `Slowlog` - `StreamConsumersInfo` - `StreamEntry` - `StreamGroupInfo` - `StreamInfo` - `StreamPendingEntry` - `StreamPendingSummary` - `Tuple` - Jedis and JedisPool constructors with a `String` parameter, and no `int` parameter, only support a URL or URI string. - Jedis(String) - JedisPool(String) - JedisPool(String, SSLSocketFactory, SSLParameters, HostnameVerifier) - JedisPool(GenericObjectPoolConfig, String) - The `Client` and `BinaryClient` classes have been removed. - `redis.clients.jedis.commands` package has been reimplemented, meaning that the `Commands` interfaces have been restructured. - The `ShardedJedisPool`, `Sharded`, `ShardedJedis`, `BinaryShardedJedis`, `ShardInfo`, `JedisShardInfo` classes have been removed. - Introduced `JedisSharding` class to replace `ShardedJedisPool`. Earlier code without the use of "name" and "weight" (in ShardInfo/JedisShardInfo) are transferable to the new class. - `ShardedJedisPipeline` class has been removed. - Introduced `ShardedPipeline` class to replace `ShardedJedisPipeline`. - The type of `Protocol.CHARSET` has been changed to `java.nio.charset.Charset`. - `Jedis.debug(DebugParams)` method has been removed. - The `DebugParams` class has been removed. - The `Jedis.sync()` method has been removed. - The `Jedis.pubsubNumSub(String...)` method now returns `Map` instead of `Map`. - `setDataSource` method in Jedis class now has `protected` access. - `JedisPoolAbstract` class has been removed. Use `Pool`. - The `Pool.initPool()` method has been removed. - The `Pool.getNumActive()` method now returns `0` (via GenericObjectPool) when the pool is closed. - The `Connection.getRawObjectMultiBulkReply()` method has been removed. Use `Connection.getUnflushedObjectMultiBulkReply()` method. - The `Queable.getResponse(Builder builder)` method has been renamed to `Queable.enqueResponse(Builder builder)`. - All methods in Queable are now `final`: - `clean()` - `generateResponse(Object data)` - `enqueResponse(Builder builder)` - `getPipelinedResponseLength()` - These BuilderFactory implementations have been removed: - `OBJECT` (use `RAW_OBJECT`) - `BYTE_ARRAY_ZSET` (use `BINARY_LIST` or `BINARY_SET`) - `BYTE_ARRAY_MAP` (use `BINARY_MAP`) - `STRING_ZSET` (use `STRING_LIST` or `STRING_SET`) - `EVAL_RESULT` (use `ENCODED_OBJECT`) - `EVAL_BINARY_RESULT` (use `RAW_OBJECT`) - All String variables representing Cluster, Sentinel and PubSub subcommands in Protocol class have been removed. - `ClientKillParams.Type` has been removed. Use `ClientType`. - `ClusterReset` has been removed. Use `ClusterResetType`. - The `JedisClusterHostAndPortMap` interface has been removed. Use the `HostAndPortMapper` interface. - `JedisClusterHashTagUtil` class has been renamed to `JedisClusterHashTag`. - The `KeyMergeUtil` class has been removed. ================================================ FILE: docs/migration-guides/v4-to-v5.md ================================================ # Jedis 5 Breaking Changes - All variants of `blmpop` and `bzmpop` methods now take `double timeout` parameter instead of `long timeout` parameter. This is breaking ONLY IF you are using `Long` for timeout. - `Reducer` abstract class is refactored: - **`Reducer(String field)` constructor is removed; `Reducer(String name, String field)` constructor is added.** - **`Reducer(String name)` constructor is added; it will cause runtime error with older `Reducer(String field)` constructor.** - `getName` method is removed. - `getAlias` method is removed. - `setAlias` method is removed; use `as` method. - `setAliasAsField` method is removed. - `getOwnArgs` method is now abstract. - `getArgs` method is removed. - `quit()` method has been removed from `Connection` and `ServerCommands` interface and implementations. - `updatePassword(String password)` method has been removed from `JedisClientConfig` and implementations. - `setPassword(String password)` method has been removed from both `JedisFactory` and `ConnectionFactory` classes. - Both `bzpopmax(double timeout, String... keys)` and `bzpopmin(double timeout, String... keys)` now return `KeyValue` (instead of `KeyedZSetElement`). - Both `bzpopmax(double timeout, byte[]... keys)` and `bzpopmin(double timeout, byte[]... keys)` now return `KeyValue` (instead of `List`). - Following methods now return `KeyValue` instead of `KeyedListElement`: - `blpop(double timeout, String key)` - `blpop(double timeout, String... keys)` - `brpop(double timeout, String key)` - `brpop(double timeout, String... keys)` - Following methods now return `KeyValue` instead of `List`: - `blpop(double timeout, byte[]... keys)` - `brpop(double timeout, byte[]... keys)` - `zdiff(String... keys)` method now returns `List` (instead of `Set`). - `zdiff(byte[]... keys)` method now returns `List` (instead of `Set`). - Both `zdiffWithScores(String... keys)` and `zdiffWithScores(byte[]... keys)` methods now return `List` (instead of `Set`). - `zinter(ZParams params, String... keys)` method now returns `List` (instead of `Set`). - `zinter(ZParams params, byte[]... keys)` method now returns `List` (instead of `Set`). - Both `zinterWithScores(ZParams params, String... keys)` and `zinterWithScores(ZParams params, byte[]... keys)` methods now return `List` (instead of `Set`). - `zunion(ZParams params, String... keys)` method now returns `List` (instead of `Set`). - `zunion(ZParams params, byte[]... keys)` method now returns `List` (instead of `Set`). - Both `zunionWithScores(ZParams params, String... keys)` and `zunionWithScores(ZParams params, byte[]... keys)` methods now return `List` (instead of `Set`). - Both `configGet(String pattern)` and `configGet(String... patterns)` methods now return `Map` instead of `List`. - Both `configGet(byte[] pattern)` and `configGet(byte[]... patterns)` methods now return `Map` instead of `List`. - New `aclDelUser(String... names)` method replaces `aclDelUser(String name)` and `aclDelUser(String name, String... names)` methods. - New `aclDelUser(byte[]... names)` method replaces `aclDelUser(byte[] name)` and `aclDelUser(byte[] name, byte[]... names)` methods. - `tsMGet(TSMGetParams multiGetParams, String... filters)` method now returns `Map` instead of `List>`. - Following methods now return `Map` instead of `List`: - `tsMRange(long fromTimestamp, long toTimestamp, String... filters)` - `tsMRange(TSMRangeParams multiRangeParams)` - `tsMRevRange(long fromTimestamp, long toTimestamp, String... filters)` - `tsMRevRange(TSMRangeParams multiRangeParams)` - `jsonNumIncrBy(String key, Path2 path, double value)` method now returns `Object` instead of `JSONArray`. - The returning object would still be JSONArray for all previous cases. So simple type casting is enough to handle this change. - The returning object will be `List` when running under RESP3 protocol. - `getAgeSeconds()` in `AccessControlLogEntry` now returns `Double` instead of `String`. - Both `ftConfigGet(String option)` and `ftConfigGet(String indexName, String option)` methods now return `Map` instead of `Map`. - `ftList()` method now returns `Set` instead of `List`. - `graphSlowlog(String graphName)` now returns `List>` (instead of `List>`). - `CommandListFilterByParams` now throws `IllegalArgumentException` (instead of `JedisDataException`) in case of unfulfilling filter. - `FailoverParams` now throws `IllegalArgumentException` (instead of `IllegalStateException`) in case of unfulfilling optional arguments. - `XPendingParams` now throws `IllegalArgumentException` (instead of `IllegalStateException`) in case of unfulfilling optional arguments. - `get()` option has been removed from `SetParams`. Following methods have been added in Jedis/UnifiedJedis for convenience: - `setGet(String key, String value)` method has been added in `StringCommands` interface. - `setGet(byte[] key, byte[] value)` method has been added in `StringBinaryCommands` interface. - `xpending(String key, String groupName, StreamEntryID start, StreamEntryID end, int count, String consumerName)` method has been removed from everywhere. - Use `xpending(java.lang.String, java.lang.String, redis.clients.jedis.params.XPendingParams)` instead. - `xpending(byte[] key, byte[] groupName, byte[] start, byte[] end, int count, byte[] consumerName)` method has been removed from everywhere. - Use `xpending(byte[], byte[], redis.clients.jedis.params.XPendingParams)` instead. - `retentionTime(long retentionTime)` method in `TSAlterParams` has been removed. Use `retention(long)` method instead. - Following classes have been removed: - `KeyedZSetElement` - `KeyedListElement` - `TSKeyValue` - `TSKeyedElements` - `Limit` - Following BuilderFactory implementations have been removed: - `BYTE_ARRAY` (use `BINARY`) - `BYTE_ARRAY_LIST` (use `BINARY_LIST`) - `BINARY_MAP_FROM_PAIRS` - `STRING_ORDERED_SET` - All _payload_ related parameters are removed from _search_ related classes; namely `Document`, `IndexDefinition`, `Query`. - `topkCount(String key, String... items)` method has been removed from everywhere. - Following methods supporting JSON.RESP command have been removed: - `jsonResp(String key)` - `jsonResp(String key, Path path)` - `jsonResp(String key, Path2 path)` - `RedisJsonCommands` and `RedisJsonPipelineCommands` interfaces have been moved into `redis.clients.jedis.json.commands` package. - `AbortedTransactionException` is removed. - `Queable` class is removed. - `Params` abstract class is removed. - `toString()` support used by its sub-classes is now unavailable. - `getParams()` method is removed from `SortingParams` class. - Both `SEARCH_AGGREGATION_RESULT` and `SEARCH_AGGREGATION_RESULT_WITH_CURSOR` implementations from `SearchBuilderFactory` class have been moved to `AggregationResult` class. - All `AggregationResult` constructors have been made `private`. - `getArgs()`, `getArgsString()` and `serializeRedisArgs(List redisArgs)` methods have been removed from `AggregationBuilder`. - `totalResults` variable in `AggregationResult` has been made private. Use `getTotalResults()` method instead. - `getArgs()` and `limit(Limit limit)` methods have been removed from `Group` class. - `addCommandEncodedArguments` and `addCommandBinaryArguments` methods have been removed from `FieldName` class. - `addObjects(int[] ints)` method has been removed from `CommandArguments`. - Following methods have been removed: - `strAlgoLCSStrings(String strA, String strB, StrAlgoLCSParams params)` - `strAlgoLCSStrings(byte[] strA, byte[] strB, StrAlgoLCSParams params)` - `strAlgoLCSKeys(String keyA, String keyB, StrAlgoLCSParams params)` - `strAlgoLCSKeys(byte[] keyA, byte[] keyB, StrAlgoLCSParams params)` - `StrAlgoLCSParams` class has been removed. - Following methods have been removed from all Pipeline classes: - `ftCursorRead(String indexName, long cursorId, int count)` - `ftCursorDel(String indexName, long cursorId)` - `ftDropIndex(String indexName)` - `ftDropIndexDD(String indexName)` - `ftAliasAdd(String aliasName, String indexName)` - `ftAliasUpdate(String aliasName, String indexName)` - `ftAliasDel(String aliasName)` - `JedisSentineled(String masterName, Set sentinels, JedisClientConfig masterClientConfig, JedisClientConfig sentinelClientConfig)` and `JedisSentineled(String masterName, Set sentinels, GenericObjectPoolConfig poolConfig, JedisClientConfig masterClientConfig, JedisClientConfig sentinelClientConfig)` constructors have been removed. - `JedisClusterInfoCache(JedisClientConfig clientConfig)` and `JedisClusterInfoCache(JedisClientConfig clientConfig, GenericObjectPoolConfig poolConfig)` constructors have been removed. ================================================ FILE: docs/migration-guides/v5-to-v6.md ================================================ # Jedis 6.0.0 Migration Guide This guide helps you migrate from Jedis 5.x to Jedis 6.0.0. Version 6.0.0 includes breaking changes focused on Redis 8.0 support, removal of deprecated modules, and improvements to the Search API. ## Table of Contents - [Overview](#overview) - [Breaking Changes](#breaking-changes) - [Removed RedisGraph Support](#removed-redisgraph-support) - [Removed Triggers and Functions (RedisGears v2)](#removed-triggers-and-functions-redisgears-v2) - [Search Dialect Default Change](#search-dialect-default-change) - [FT.PROFILE Return Type Change](#ftprofile-return-type-change) - [COMMAND INFO Response Changes](#command-info-response-changes) - [New Features](#new-features) - [Redis 8.0 Support](#redis-80-support) - [SslOptions for Advanced SSL Configuration](#ssloptions-for-advanced-ssl-configuration) - [Token-Based Authentication](#token-based-authentication) - [New Hash Commands](#new-hash-commands) - [Search Warning Messages](#search-warning-messages) - [Additional Resources](#additional-resources) ## Overview Jedis 6.0.0 is a major release that adds Redis 8.0 support and removes deprecated features. The main focus areas are: 1. **Redis 8.0 compatibility** - Full support for Redis 8.0 features including built-in JSON, Search, and TimeSeries 2. **Module deprecations** - Removal of RedisGraph and Triggers & Functions (RedisGears v2) support 3. **Search API improvements** - Default dialect change and enhanced profiling responses 4. **Security enhancements** - New SSL options and token-based authentication support ## Breaking Changes ### Removed RedisGraph Support RedisGraph module support has been completely removed from Jedis 6.0.0 as the module has been deprecated by Redis. #### Removed Classes and Interfaces All classes in the `redis.clients.jedis.graph` package have been removed: - `RedisGraphCommands` interface - `RedisGraphPipelineCommands` interface - `GraphCommandObjects` class - `GraphCache`, `GraphProtocol`, `GraphQueryParams` classes - `ResultSet`, `ResultSetBuilder`, `Record`, `Header`, `Statistics` classes - All entity classes: `Edge`, `Node`, `Path`, `Point`, `Property`, `GraphEntity` ### Removed Triggers and Functions (RedisGears v2) Support for Triggers and Functions (RedisGears v2) has been removed from Jedis 6.0.0. #### Removed Classes and Interfaces All classes in the `redis.clients.jedis.gears` package have been removed: - `RedisGearsCommands` interface - `RedisGearsProtocol` class - `TFunctionListParams`, `TFunctionLoadParams` classes - Response classes: `FunctionInfo`, `FunctionStreamInfo`, `GearsLibraryInfo`, `StreamTriggerInfo`, `TriggerInfo` ### Search Dialect Default Change **BREAKING:** The default search dialect has changed from server-side default to **DIALECT 2** (client-side override). #### Impact Starting with Jedis 6.0.0, all `FT.SEARCH` and `FT.AGGREGATE` commands automatically append `DIALECT 2` unless explicitly configured otherwise. This may affect query results if you were relying on DIALECT 1 behavior. #### Migration Path **Option 1: Accept DIALECT 2 (Recommended)** Review your search queries to ensure they work correctly with DIALECT 2. Most queries should work without changes. **Option 2: Revert to DIALECT 1** If you need to maintain DIALECT 1 behavior: ```java JedisPooled jedis = new JedisPooled("redis://localhost:6379"); // Set default dialect to 1 jedis.setDefaultSearchDialect(1); // Now all search commands will use DIALECT 1 SearchResult result = jedis.ftSearch("idx:products", "@category:electronics"); ``` ### FT.PROFILE Return Type Change The return type of `FT.PROFILE` commands has changed from `Map` to a structured `ProfilingInfo` object. #### Changed Methods **Before (v5.x):** ```java Map.Entry> ftProfileSearch( String indexName, FTProfileParams profileParams, Query query); Map.Entry> ftProfileAggregate( String indexName, FTProfileParams profileParams, AggregationBuilder aggr); ``` **After (v6.0.0):** ```java Map.Entry ftProfileSearch( String indexName, FTProfileParams profileParams, Query query); Map.Entry ftProfileAggregate( String indexName, FTProfileParams profileParams, AggregationBuilder aggr); ``` ### COMMAND INFO Response Changes The response format for `COMMAND INFO` has been updated to include subcommand details, making it compatible with Redis 7.0+ and Redis 8.0. #### Impact If you were parsing the `COMMAND INFO` response, you may need to update your code to handle the new structure that includes subcommand information. **Before (v5.x):** ```java List commandInfo = jedis.commandInfo("SET"); // Returns basic command information ``` **After (v6.0.0):** ```java List commandInfo = jedis.commandInfo("SET"); // Returns command information including subcommand details // Compatible with Redis 7.0+ format ``` ## New Features ### Redis 8.0 Support Jedis 6.0.0 adds full support for Redis 8.0, which includes built-in support for: - **JSON** - Native JSON data type (previously RedisJSON module) - **Search and Query** - Full-text search capabilities (previously RediSearch module) - **TimeSeries** - Time-series data support (previously RedisTimeSeries module) **Example:** ```java JedisPooled jedis = new JedisPooled("redis://localhost:6379"); // JSON operations (built-in in Redis 8.0) jedis.jsonSet("user:1", Path2.of("$"), "{\"name\":\"John\",\"age\":30}"); String json = jedis.jsonGet("user:1"); // Search operations (built-in in Redis 8.0) jedis.ftCreate("idx:users", FTCreateParams.createParams() .on(IndexDataType.JSON) .addPrefix("user:"), TextField.of("$.name").as("name"), NumericField.of("$.age").as("age")); SearchResult result = jedis.ftSearch("idx:users", "@name:John"); ``` ### SslOptions for Advanced SSL Configuration A new `SslOptions` class provides advanced SSL/TLS configuration options for secure connections. **Features:** - Custom keystore and truststore configuration - SSL protocol selection - SSL verification mode control - SSL parameters customization **Example:** ```java SslOptions sslOptions = SslOptions.builder() .keystore(new File("/path/to/keystore.jks")) .keystorePassword("keystorePassword".toCharArray()) .truststore(new File("/path/to/truststore.jks")) .truststorePassword("truststorePassword".toCharArray()) .sslVerifyMode(SslVerifyMode.FULL) .build(); JedisClientConfig config = DefaultJedisClientConfig.builder() .ssl(true) .sslOptions(sslOptions) .build(); JedisPooled jedis = new JedisPooled("localhost", 6379, config); ``` ### Token-Based Authentication Jedis 6.0.0 introduces support for token-based authentication, useful for cloud environments and managed Redis services. **Example:** ```java // Token-based authentication with automatic token refresh TokenCredentials tokenCredentials = new TokenCredentials("initial-token"); JedisClientConfig config = DefaultJedisClientConfig.builder() .credentials(tokenCredentials) .build(); JedisPooled jedis = new JedisPooled("localhost", 6379, config); // Token can be updated dynamically tokenCredentials.updateToken("new-token"); ``` ### New Hash Commands Support for new hash field expiration commands introduced in Redis 7.4: - `HGETDEL` - Get and delete a hash field - `HGETEX` - Get a hash field with expiration options - `HSETEX` - Set a hash field with expiration **Example:** ```java // HSETEX - Set field with expiration jedis.hsetex("user:1", 3600, "session", "abc123"); // HGETEX - Get field and update expiration String session = jedis.hgetex("user:1", "session", HGetExParams.hgetExParams().ex(7200)); // HGETDEL - Get and delete field String oldSession = jedis.hgetdel("user:1", "session"); ``` ### Search Warning Messages Search and aggregation queries now support warning messages in results, helping identify potential issues with queries. **Example:** ```java SearchResult result = jedis.ftSearch("idx:products", "@name:laptop"); // Check for warnings if (result.hasWarnings()) { List warnings = result.getWarnings(); warnings.forEach(warning -> System.out.println("Search warning: " + warning)); } ``` ## Additional Resources - [Redis 8.0 Release Notes](https://github.com/redis/redis/blob/8.0/00-RELEASENOTES) - [Redis Search Documentation](https://redis.io/docs/interact/search-and-query/) - [Redis Query Dialects](https://redis.io/docs/interact/search-and-query/advanced-concepts/dialects/) ## Getting Help If you encounter issues during migration: 1. Check the [Jedis GitHub Issues](https://github.com/redis/jedis/issues) 2. Join the [Redis Discord](https://discord.gg/redis) 3. Review the [Jedis Javadocs](https://www.javadoc.io/doc/redis.clients/jedis/latest/) 4. Start a [Discussion](https://github.com/redis/jedis/discussions) ================================================ FILE: docs/migration-guides/v6-to-v7.md ================================================ # Jedis 7.0.0 Migration Guide This guide helps you migrate from Jedis 6.2.0 to Jedis 7.0.0. Version 7.0.0 includes several breaking changes focused on removing deprecated features and improving the API design. ## Table of Contents - [Overview](#overview) - [Breaking Changes](#breaking-changes) - [Removed Deprecated Sharding/Sharded Features](#removed-deprecated-shardingsharded-features) - [Base Class Changes](#base-class-changes) - [UnifiedJedis Constructor Changes](#unifiedjedis-constructor-changes) - [Return Type Changes](#return-type-changes) - [New Features](#new-features) - [Automatic Failover and Failback](#automatic-failover-and-failback) - [Builder Pattern for Client Creation](#builder-pattern-for-client-creation) ## Overview Jedis 7.0.0 is a major release that removes previously deprecated features and modernizes the API. The main focus areas are: 1. **Removal of deprecated sharding features** - JedisSharding and related classes have been removed 2. **Base class consolidation** - Pipeline and Transaction base classes have been renamed 3. **Builder pattern introduction** - New fluent builders for JedisPooled, JedisCluster, and JedisSentineled 4. **API cleanup** - Removal of deprecated constructors and methods ## Breaking Changes ### Removed Deprecated Sharding Features The following classes and features have been **completely removed** in Jedis 7.0.0: #### Removed Classes - `JedisSharding` - Use `JedisCluster` for distributed Redis deployments instead - `ShardedPipeline` - Use `Pipeline` with `JedisCluster` instead - `ShardedConnectionProvider` - No replacement needed - `ShardedCommandArguments` - Internal class, no replacement needed - `ShardedCommandObjects` - Internal class, no replacement needed #### Migration Path If you were using `JedisSharding`: **Before (v6.2.0):** ```java List shards = Arrays.asList( new HostAndPort("localhost", 6379), new HostAndPort("localhost", 6380) ); JedisSharding jedisSharding = new JedisSharding(shards); jedisSharding.set("key", "value"); ``` **After (v7.0.0):** ```java // Option 1: Use JedisCluster for distributed deployments Set nodes = new HashSet<>(Arrays.asList( new HostAndPort("localhost", 6379), new HostAndPort("localhost", 6380) )); JedisCluster jedisCluster = new JedisCluster(nodes); jedisCluster.set("key", "value"); // Option 2: Use JedisPooled for single-node deployments JedisPooled jedis = new JedisPooled("localhost", 6379); jedis.set("key", "value"); ``` ### Base Class Changes Several base classes have been renamed to better reflect their purpose: #### Pipeline Base Class - `PipelineBase` has been **removed** - `Pipeline` now extends `AbstractPipeline` instead **Impact:** If you were using `PipelineBase` as a type reference, change it to `AbstractPipeline`. **Before (v6.2.0):** ```java PipelineBase pipeline = jedis.pipelined(); ``` **After (v7.0.0):** ```java AbstractPipeline pipeline = jedis.pipelined(); // Or use the concrete type: Pipeline pipeline = (Pipeline) jedis.pipelined(); ``` #### Transaction Base Class - `TransactionBase` has been **removed** - `Transaction` now extends `AbstractTransaction` instead **Impact:** If you were using `TransactionBase` as a type reference, change it to `AbstractTransaction`. **Before (v6.2.0):** ```java TransactionBase transaction = jedis.multi(); ``` **After (v7.0.0):** ```java AbstractTransaction transaction = jedis.multi(); // Or use the concrete type: Transaction transaction = (Transaction) jedis.multi(); ``` ### UnifiedJedis Constructor Changes Several deprecated constructors have been removed from `UnifiedJedis`: #### Removed Constructors 1. **Cluster constructors with maxAttempts:** ```java // REMOVED in v7.0.0 UnifiedJedis(Set nodes, JedisClientConfig config, int maxAttempts) UnifiedJedis(Set nodes, JedisClientConfig config, int maxAttempts, Duration maxTotalRetriesDuration) UnifiedJedis(Set nodes, JedisClientConfig config, GenericObjectPoolConfig poolConfig, int maxAttempts, Duration maxTotalRetriesDuration) ``` 2. **Sharding constructors:** ```java // REMOVED in v7.0.0 UnifiedJedis(ShardedConnectionProvider provider) UnifiedJedis(ShardedConnectionProvider provider, Pattern tagPattern) ``` #### Migration Path **Before (v6.2.0):** ```java Set nodes = new HashSet<>(); nodes.add(new HostAndPort("localhost", 6379)); JedisClientConfig config = DefaultJedisClientConfig.builder().build(); UnifiedJedis jedis = new UnifiedJedis(nodes, config, 3); ``` **After (v7.0.0):** ```java Set nodes = new HashSet<>(); nodes.add(new HostAndPort("localhost", 6379)); JedisClientConfig config = DefaultJedisClientConfig.builder().build(); // Use JedisCluster instead JedisCluster jedis = new JedisCluster(nodes, config); // Or use the new builder pattern JedisCluster jedis = JedisCluster.builder() .nodes(nodes) .clientConfig(config) .build(); ``` ### Return Type Changes #### UnifiedJedis.pipelined() The return type has been changed to be more generic: **Before (v6.2.0):** ```java PipelineBase pipelined() ``` **After (v7.0.0):** ```java AbstractPipeline pipelined() ``` **Impact:** Minimal - `AbstractPipeline` is the parent class, so existing code should continue to work. ## New Features ### Automatic Failover and Failback Jedis 7.0.0 significantly refactors the automatic failover and failback API. If you were using the failover features in v6.2.0 with `MultiClusterClientConfig` and `MultiClusterPooledConnectionProvider`, these have been renamed and improved in v7.0.0. **For detailed migration guidance on automatic failover and failback** please refer to the **[Automatic Failover and Failback Migration Guide](https://redis.github.io/jedis/failover/#migration-from-6x-to-7x)**. ### Builder Pattern for Client Creation Jedis 7.0.0 introduces a fluent builder pattern for creating client instances, making configuration more intuitive and discoverable. #### JedisPooled Builder **New in v7.0.0:** ```java JedisPooled jedis = JedisPooled.builder() .hostAndPort("localhost", 6379) .clientConfig(DefaultJedisClientConfig.builder() .user("myuser") .password("mypassword") .database(0) .build()) .poolConfig(new GenericObjectPoolConfig<>()) .build(); ``` #### JedisCluster Builder **New in v7.0.0:** ```java Set nodes = new HashSet<>(); nodes.add(new HostAndPort("localhost", 7000)); nodes.add(new HostAndPort("localhost", 7001)); JedisCluster cluster = JedisCluster.builder() .nodes(nodes) .clientConfig(DefaultJedisClientConfig.builder() .password("mypassword") .build()) .maxAttempts(5) .maxTotalRetriesDuration(Duration.ofSeconds(10)) .build(); ``` #### JedisSentineled Builder **New in v7.0.0:** ```java Set sentinels = new HashSet<>(); sentinels.add(new HostAndPort("localhost", 26379)); sentinels.add(new HostAndPort("localhost", 26380)); JedisSentineled jedis = JedisSentineled.builder() .masterName("mymaster") .sentinels(sentinels) .clientConfig(DefaultJedisClientConfig.builder() .password("mypassword") .build()) .build(); ``` ## Getting Help If you encounter issues during migration create an issue or [start a discussion](https://github.com/redis/jedis/discussions/new?category=q-a). ================================================ FILE: docs/redisearch.md ================================================ # RediSearch Jedis Quick Start To use RediSearch features with Jedis, you'll need to use an implementation of RediSearchCommands. ## Creating the RediSearch client Initializing the client with RedisClient: ```java RedisClient client = RedisClient.builder().hostAndPort("localhost", 6379).build(); ``` Initializing the client with RedisClusterClient: ```java Set nodes = new HashSet<>(); nodes.add(new HostAndPort("127.0.0.1", 7379)); nodes.add(new HostAndPort("127.0.0.1", 7380)); RedisClusterClient client = RedisClusterClient.builder().nodes(nodes).build(); ``` ## Indexing and querying ### Indexing Defining a schema for an index and creating it: ```java Schema sc = new Schema() .addTextField("title", 5.0) .addTextField("body", 1.0) .addNumericField("price"); IndexDefinition def = new IndexDefinition() .setPrefixes(new String[]{"item:", "product:"}) .setFilter("@price>100"); client.ftCreate("item-index", IndexOptions.defaultOptions().setDefinition(def), sc); ``` Alternatively, we can create the same index using FTCreateParams: ```java client.ftCreate("item-index", FTCreateParams.createParams() .prefix("item:", "product:") .filter("@price>100"), TextField.of("title").weight(5.0), TextField.of("body"), NumericField.of("price") ); ``` ### Inserting Adding documents to the index: ```java Map fields = new HashMap<>(); fields.put("title", "hello world"); fields.put("state", "NY"); fields.put("body", "lorem ipsum"); fields.put("price", 1337); client.hset("item:hw", RediSearchUtil.toStringMap(fields)); ``` Another way to insert documents: ```java client.hsetObject("item:hw", fields); ``` ### Querying Searching the index: ```java Query q = new Query("hello world") .addFilter(new Query.NumericFilter("price", 0, 1000)) .limit(0, 5); SearchResult sr = client.ftSearch("item-index", q); ``` Alternative searching using FTSearchParams: ```java SearchResult sr = client.ftSearch("item-index", "hello world", FTSearchParams.searchParams() .filter("price", 0, 1000) .limit(0, 5)); ``` Aggregation query: ```java AggregationBuilder ab = new AggregationBuilder("hello") .apply("@price/1000", "k") .groupBy("@state", Reducers.avg("@k").as("avgprice")) .filter("@avgprice>=2") .sortBy(10, SortedField.asc("@state")); AggregationResult ar = client.ftAggregate("item-index", ab); ``` ================================================ FILE: docs/redisjson.md ================================================ # RedisJSON Jedis Quick Start Jedis supports [RedisJSON](https://redis.io/docs/latest/develop/data-types/json/) and [RediSearch](https://redis.io/docs/latest/develop/interact/search-and-query/). The latest versions of RedisJSON let you store, manipulate, index, and query JSON. To use these features with Jedis, you'll need to use the `UnifiedJedis` interface or a sub-class of it. Let's see how this works. ## Creating with RedisJSON client First, let's create a `RedisClient` client instance: ```java RedisClient client = RedisClient.builder().hostAndPort("localhost", 6479).build(); ``` Or, a `RedisClusterClient` client instance: ```java Set nodes = new HashSet<>(); nodes.add(new HostAndPort("127.0.0.1", 7379)); nodes.add(new HostAndPort("127.0.0.1", 7380)); RedisClusterClient client = RedisClusterClient.builder().nodes(nodes).build(); ``` Now we can start working with JSON. For these examples, we'll be using [GSON](https://github.com/google/gson) to handle the serialization of POJOs to JSON. ## Creating JSON documents Suppose we're building an online learning platform, and we want to represent students. Let's create a POJO to represent our students: ```java private class Student { private String firstName; private String lastName; public Student(String firstName, String lastName) { this.firstName = firstName; this.lastName = lastName; } public String getFirstName() { return firstName; } public String getLastName() { return lastName; } } ``` Now we can create some students and store them in Redis as JSON: ```java final Gson gson = new Gson(); Student maya = new Student("Maya", "Jayavant"); client.jsonSet("student:111", gson.toJson(maya)); Student oliwia = new Student("Oliwia", "Jagoda"); client.jsonSet("student:112", gson.toJson(oliwia)); ``` Some of other ways to store POJOs as JSON: ``` client.jsonSetLegacy("student:111", maya); client.jsonSetWithEscape("student:112", oliwia); ``` ## Querying and indexing JSON If we want to be able to query this JSON, we'll need to create an index. Let's create an index on the "firstName" and "lastName" fields. 1. We define which fields to index ("firstName" and "lastName"). 2. We set up the index definition to recognize JSON and include only those documents whose key starts with "student:". 3. Then we actually create the index, called "student-index", by calling `ftCreate()`. ```java Schema schema = new Schema().addTextField("$.firstName", 1.0).addTextField("$.lastName", 1.0); IndexDefinition rule = new IndexDefinition(IndexDefinition.Type.JSON) .setPrefixes(new String[]{"student:"}); client.ftCreate("student-index", IndexOptions.defaultOptions().setDefinition(rule), schema); ``` Alternatively creating the same index using FTCreateParams: ```java client.ftCreate("student-index", FTCreateParams.createParams().on(IndexDataType.JSON).prefix("student:"), TextField.of("$.firstName"), TextField.of("$.lastName")); ``` With an index now defined, we can query our JSON. Let's find all students whose name begins with "maya": ```java Query q = new Query("@\\$\\.firstName:maya*"); SearchResult mayaSearch = client.ftSearch("student-index", q); ``` Same query can be done using FTSearchParams: ```java SearchResult mayaSearch = client.ftSearch("student-index", "@\\$\\.firstName:maya*", FTSearchParams.searchParams()); ``` We can then iterate over our search results: ```java List docs = mayaSearch.getDocuments(); for (Document doc : docs) { System.out.println(doc); } ``` This example just scratches the surface. You can atomically manipulate JSON documents and query them in a variety of ways. See the [RedisJSON docs](https://oss.redis.com/redisjson/), the [RediSearch](https://oss.redis.com/redisearch/) docs, and our course, ["Querying, Indexing, and Full-text Search in Redis"](https://university.redis.com/courses/ru203/), for a lot more examples. ================================================ FILE: docs/requirements.txt ================================================ mkdocs~=1.6 mkdocs-material~=9.5 pymdown-extensions~=10.8 mkdocs-macros-plugin~=1.0 mkdocs-glightbox ================================================ FILE: docs/transactions-multi.md ================================================ # Transactions/Multi ## Overview Transactions guarantee that all the included commands will execute to completion without being interrupted by commands from other clients. See the [Transactions](https://redis.io/docs/latest/develop/interact/transactions/) page for more information. To execute commands in a transaction, create a transaction object with the `multi()` command, call command methods on that object, and then call the transaction object's `exec()` method to execute it. You can access the results from commands in the transaction using `Response` objects. The `exec()` method also returns a `List` value that contains all the result values in the order the commands were executed. ## Immediate Transaction Start The simplest way to use transactions is to call `multi()` on your Jedis client, which immediately starts a transaction by sending the `MULTI` command to Redis. All subsequent commands are queued until `exec()` is called. ```java import redis.clients.jedis.RedisClient; import redis.clients.jedis.AbstractTransaction; import redis.clients.jedis.Response; import java.util.List; RedisClient jedis = RedisClient.create("redis://localhost:6379"); // Create a transaction that immediately sends MULTI try (AbstractTransaction tx = jedis.multi()) { // Commands are queued Response set1 = tx.set("counter:1", "0"); Response incr1 = tx.incrBy("counter:1", 1); Response incr2 = tx.incrBy("counter:1", 2); // Execute the transaction List results = tx.exec(); // Access results via Response objects System.out.println(incr1.get()); // 1 System.out.println(incr2.get()); // 3 // Or via the results list System.out.println(results.get(0)); // OK System.out.println(results.get(1)); // 1 System.out.println(results.get(2)); // 3 } jedis.close(); ``` ### Response Handling Commands invoked within a transaction return `Response` objects. These responses become available only after `exec()` is called: - Before `exec()`: Calling `response.get()` will throw `IllegalStateException` with the message "Please close pipeline or multi block before calling this method." - After `exec()`: Response objects contain the actual results from Redis The `exec()` method returns a `List` containing all command results in the order they were queued. ## Manual Transaction Start For more control, you can create a transaction without immediately sending `MULTI`. This is useful when you need to: - Execute commands before starting the transaction - Use `WATCH` to implement optimistic locking - Conditionally start the transaction Create a manual transaction by passing `false` to the `transaction()` method: ```java RedisClient jedis = RedisClient.create("redis://localhost:6379"); // Create transaction without sending MULTI try (AbstractTransaction tx = jedis.transaction(false)) { // Commands before multi() are executed immediately Response setBeforeMulti = tx.set("mykey", "initial_value"); Response getBeforeMulti = tx.get("mykey"); // These responses are available immediately System.out.println(setBeforeMulti.get()); // OK System.out.println(getBeforeMulti.get()); // initial_value // Now start the transaction tx.multi(); // Commands after multi() are queued Response set = tx.set("mykey", "new_value"); Response get = tx.get("mykey"); // Execute the transaction List results = tx.exec(); // Results from queued commands System.out.println(set.get()); // OK System.out.println(get.get()); // new_value } jedis.close(); ``` ### Using WATCH for Optimistic Locking The `WATCH` command monitors keys for changes. If any watched key is modified before `EXEC`, the transaction is aborted and `exec()` returns `null`. **Important:** `WATCH` must be executed through the transaction object to ensure it uses the transaction's dedicated connection. ```java try (AbstractTransaction tx = jedis.transaction(false)) { // WATCH must be on the transaction's dedicated connection tx.watch("counter"); // Read current value - can use the client directly String current = jedis.get("counter"); int newValue = Integer.parseInt(current) + 1; // Start transaction and queue update tx.multi(); tx.set("counter", String.valueOf(newValue)); // Returns null if key was modified by another client List results = tx.exec(); if (results == null) { System.out.println("Transaction aborted - key was modified"); } } ``` ## Connection Lifecycle A transaction acquires a dedicated connection that is held until `close()` is called. Ensure the transaction is closed on error: ```java try (AbstractTransaction tx = jedis.multi()) { tx.set("key", "value"); tx.exec(); } ``` **Important:** `WATCH` must be executed through the transaction object to ensure it uses the transaction's dedicated connection. ## Transaction Completion Complete a transaction by calling either: - **`exec()`** - Executes all queued commands atomically and returns a `List` with the results - **`discard()`** - Discards all queued commands without executing them ### Automatic Cleanup When using try-with-resources, `close()` automatically sends `DISCARD` (if in `MULTI` state) or `UNWATCH` (if in `WATCH` state) to ensure the connection is returned to the pool in a clean state. ================================================ FILE: docs/tutorials_examples.md ================================================ # Tutorials and Examples ## General * Redis for Java Developers: * Jedis Guide: * Connecting to a Redis server: * Using Jedis in production: ## Client-side Caching * Client-side Caching: ## JSON * Store, Read and Search JSON: ## Search * Vector Search: * Spring Boot Search: ================================================ FILE: docs/verifying-artifacts.md ================================================ ## Verifying contents Jedis artifacts published on Maven central are signed. For each artifact, there is an associated signature file with the `.asc` suffix. ## Keys used for signing ``` pub rsa3072 2023-12-17 [SC] 165E63A75659104C72B49129C2B44BE148BDCEC8 uid [ unknown] Redis OSS sig 3 C2B44BE148BDCEC8 2023-12-17 [self-signature] ``` A copy of this key is stored on the keyserver [Ubuntu keyserver](https://keyserver.ubuntu.com/pks/lookup?op=vindex&search=0xC2B44BE148BDCEC8) ### Before 2023-12-17 ``` pub rsa3072 2023-09-03 [SC] 6BF6C1905C9642D1181D157443543D884CC71C96 uid [ unknown] Redis OSS sig 3 43543D884CC71C96 2023-09-03 [self-signature] ``` A copy of this key is stored on the keyserver [Ubuntu keyserver](https://keyserver.ubuntu.com/pks/lookup?op=vindex&search=0x43543D884CC71C96) ### Before 2023-09-03 ``` pub rsa3072 2021-06-27 [SC] [expired: 2023-06-27] 1C913D6D36DAD0A679863F5CB68CD0A11D28B97D uid [ expired] RedisLabs OSS sig 3 B68CD0A11D28B97D 2021-06-27 [self-signature] ``` A copy of this key is stored on the keyserver [Ubuntu keyserver](https://keyserver.ubuntu.com/pks/lookup?op=vindex&search=0xB68CD0A11D28B97D) ### Before 2021-06-27 ``` pub rsa3072 2018-08-12 [SC] [expired: 2020-08-11] 4EC78BD38D30A2E85C02C39C2D62B50EF8D3297A uid [ expired] Guy Korlad sig 3 2D62B50EF8D3297A 2018-08-12 [self-signature] ``` A copy of this key is stored on the keyserver [Ubuntu keyserver](https://keyserver.ubuntu.com/pks/lookup?op=vindex&search=0x2D62B50EF8D3297A) ================================================ FILE: formatter-pom.xml ================================================ org.sonatype.oss oss-parent 7 4.0.0 jar redis.clients jedis Jedis 5.3.0 Jedis is a blazingly small and sane Redis java client. https://github.com/redis/jedis net.revelc.code.formatter formatter-maven-plugin 2.16.0 ${project.basedir}/hbase-formatter.xml validate ================================================ FILE: hbase-formatter.xml ================================================ ================================================ FILE: mkdocs.yml ================================================ site_name: Jedis repo_name: Jedis site_author: Redis, Inc. site_description: Jedis is a Redis client for the JVM. repo_url: https://github.com/redis/jedis remote_branch: gh-pages theme: name: material logo: assets/images/logo.png favicon: assets/images/favicon-16x16.png extra_css: - css/extra.css plugins: - search - macros: include_dir: . markdown_extensions: - pymdownx.highlight: anchor_linenums: true line_spans: __span pygments_lang_class: true - pymdownx.inlinehilite - pymdownx.snippets - pymdownx.superfences - admonition - pymdownx.details nav: - Home: index.md - Jedis Maven: jedis-maven.md - User Guide: - Transactions/Multi: transactions-multi.md - Migrating to newer versions: - Jedis 7: migration-guides/v6-to-v7.md - Jedis 6: migration-guides/v5-to-v6.md - Jedis 5: migration-guides/v4-to-v5.md - Jedis 4: - Breaking changes: migration-guides/v3-to-v4.md - Primitives: migration-guides/v3-to-v4-primitives.md - ZSET: migration-guides/v3-to-v4-zset-list.md - Using Jedis with ...: - Search: redisearch.md - JSON: redisjson.md - Failover: failover.md - Verifying artifacts: verifying-artifacts.md - FAQ: faq.md - API Reference: https://www.javadoc.io/doc/redis.clients/jedis/latest/index.html - Tutorials and Examples: tutorials_examples.md - Jedis Guide: https://redis.io/docs/latest/develop/connect/clients/java/jedis/ - Redis Command Reference: https://redis.io/docs/latest/commands/ - Advanced Usage: advanced-usage.md ================================================ FILE: pom.xml ================================================ org.sonatype.oss oss-parent 7 4.0.0 jar redis.clients jedis 8.0.0-SNAPSHOT Jedis Jedis is a blazingly small and sane Redis java client. https://github.com/redis/jedis Jedis Mailing List jedis_redis@googlegroups.com https://groups.google.com/group/jedis_redis MIT https://github.com/redis/jedis/blob/master/LICENSE repo github https://github.com/redis/jedis/issues scm:git:git@github.com:redis/jedis.git scm:git:git@github.com:redis/jedis.git https://github.com/redis/jedis/tree/master redis Redis Ltd. Redis https://redis.io github redis.clients.jedis 1.7.36 1.7.1 2.21.1 3.5.5 5.14.3 integration,scenario false false true org.junit junit-bom ${junit.version} pom import org.slf4j slf4j-api ${slf4j.version} org.apache.commons commons-pool2 2.12.1 org.json json 20251224 com.google.code.gson gson 2.13.2 redis.clients.authentication redis-authx-core 0.1.1-beta2 com.kohlschutter.junixsocket junixsocket-core 2.10.1 pom test org.locationtech.jts jts-core 1.20.0 test org.junit.jupiter junit-jupiter-api test org.junit.jupiter junit-jupiter-params test org.mockito mockito-junit-jupiter 4.11.0 test org.junit.jupiter junit-jupiter-engine test org.hamcrest hamcrest 3.0 test ch.qos.logback logback-classic 1.2.13 test org.mockito mockito-inline 4.11.0 test com.fasterxml.jackson.core jackson-databind ${jackson.version} test com.fasterxml.jackson.datatype jackson-datatype-jsr310 ${jackson.version} test net.javacrumbs.json-unit json-unit 2.40.1 test org.apache.httpcomponents.client5 httpclient5-fluent 5.6 test org.awaitility awaitility 4.3.0 test redis.clients.authentication redis-authx-entraid 0.1.1-beta2 test eu.rekawek.toxiproxy toxiproxy-java 2.1.11 test io.github.resilience4j resilience4j-all ${resilience4j.version} true io.github.resilience4j resilience4j-circuitbreaker ${resilience4j.version} true io.github.resilience4j resilience4j-retry ${resilience4j.version} true central https://central.sonatype.com/api/v1/publisher/deployments/upload/ central https://central.sonatype.com/repository/maven-snapshots/ src/main/resources true org.jacoco jacoco-maven-plugin 0.8.14 prepare-agent-ut prepare-agent argLine ${project.build.directory}/jacoco-ut.exec prepare-agent-it-tagged prepare-agent failsafeTaggedArgLine ${project.build.directory}/jacoco-it-tagged.exec prepare-agent-it-suffix prepare-agent failsafeSuffixArgLine ${project.build.directory}/jacoco-it-suffix.exec prepare-agent-scenario prepare-agent failsafeScenarioArgLine ${project.build.directory}/jacoco-scenario.exec merge-coverage post-integration-test merge ${project.build.directory} jacoco-ut.exec jacoco-it-tagged.exec jacoco-it-suffix.exec jacoco-scenario.exec ${project.build.directory}/jacoco-merged.exec report verify report ${project.build.directory}/jacoco-merged.exec maven-compiler-plugin 3.15.0 1.8 1.8 maven-surefire-plugin ${maven.surefire.version} ${skipUnitTests} @{argLine} ${JVM_OPTS} ${redis-hosts} ${excludedGroupsForUnitTests} **/examples/*.java **/scenario/*Test.java **/*IntegrationTest.java **/*IntegrationTests.java **/codegen/**/*.java maven-failsafe-plugin ${maven.surefire.version} @{failsafeSuffixArgLine} ${JVM_OPTS} ${redis-hosts} ${project.build.directory}/failsafe-summary.xml scenario **/*IntegrationTest.java **/*IntegrationTests.java **/examples/*.java **/mocked/*.java it-tagged integration-test ${skipIntegrationTests} @{failsafeTaggedArgLine} ${JVM_OPTS} integration scenario,unit **/*Test.java **/*Tests.java it-suffix integration-test ${skipIntegrationTests} @{failsafeSuffixArgLine} ${JVM_OPTS} scenario,unit **/*IT.java **/*ITs.java **/*IntegrationTest.java **/*IntegrationTests.java scenario-tests integration-test ${skipScenarioTests} @{failsafeScenarioArgLine} ${JVM_OPTS} scenario integration,unit **/*IT.java **/*ITs.java it-verify verify maven-source-plugin 3.4.0 true attach-sources jar maven-javadoc-plugin 3.12.0 8 false attach-javadoc jar maven-release-plugin 3.3.1 org.sonatype.central central-publishing-maven-plugin 0.10.0 true central true published net.revelc.code.formatter formatter-maven-plugin 2.16.0 ${project.basedir}/hbase-formatter.xml ${project.basedir} src/main/java/redis/clients/jedis/annots/*.java src/main/java/redis/clients/jedis/params/XCfgSetParams.java src/main/java/redis/clients/jedis/resps/StreamEntryDeletionResult.java src/main/java/redis/clients/jedisargs/StreamDeletionPolicy.java src/test/java/redis/clients/jedis/commands/StreamsCommandsTestBase.java src/test/java/redis/clients/jedis/params/XCfgSetParamsTest.java src/test/java/redis/clients/jedis/commands/jedis/ClusterStreamsCommandsTest.java src/test/java/redis/clients/jedis/commands/jedis/PooledStreamsCommandsTest.java src/test/java/redis/clients/jedis/resps/StreamEntryDeletionResultTest.java src/test/java/redis/clients/jedis/commands/commandobjects/CommandObjectsStringCommandsTest.java src/test/java/redis/clients/jedis/commands/jedis/BinaryValuesCommandsTest.java src/test/java/redis/clients/jedis/commands/jedis/StringValuesCommandsTest.java src/test/java/redis/clients/jedis/commands/unified/BinaryValuesCommandsTestBase.java src/test/java/redis/clients/jedis/commands/unified/StringValuesCommandsTestBase.java src/test/java/redis/clients/jedis/commands/unified/cluster/ClusterStringValuesCommandsTest.java **/VectorSet*.java **/VectorTestUtils.java **/VAddParams.java **/VSimParams.java **/VSimScoreAttribs.java **/*FunctionCommandsTest*.java **/Endpoint.java src/main/java/redis/clients/jedis/mcf/*.java src/test/java/redis/clients/jedis/failover/*.java src/test/java/redis/clients/jedis/mcf/*.java **/Health*.java **/*IT.java **/scenario/RestEndpointUtil.java src/main/java/redis/clients/jedis/MultiDbConfig.java src/main/java/redis/clients/jedis/HostAndPort.java **/builders/*.java **/MultiDb*.java **/*CommandFlags*.java **/*CompareCondition*.java **/*MSetExParams*.java **/*UnitTest.java **/DriverInfo.java **/ClientSetInfo*.java **/ClientCommandsTest*.java **/Delay*.java **/SentineledConnectionProviderReconnectionTest.java **/search/Apply.java **/search/Combiner.java **/search/Combiners.java **/search/Filter.java **/search/Limit.java **/search/Scorer.java **/search/Scorers.java **/search/hybrid/*.java **/search/FTHybrid*.java **/*Hotkeys*.java **/TestDataUtil*.java **/*JedisClientConfig*.java **/resps/LibraryInfoTest.java **/*Matchers.java **/*TestUtil.java **/executors/aggregators/*.java **/*MapMatcher.java validate maven-jar-plugin 3.5.0 ${project.build.outputDirectory}/META-INF/MANIFEST.MF ${jedis.module.name} org.apache.felix maven-bundle-plugin 5.1.9 bundle-manifest process-classes manifest release maven-gpg-plugin 3.2.8 --pinentry-mode loopback sign-artifacts verify sign doctests maven-surefire-plugin ${maven.surefire.version} **/examples/*Example.java tests-with-params with-param-names true maven-compiler-plugin 3.15.0 1.8 1.8 true maven-surefire-plugin ${maven.surefire.version} @{argLine} ${JVM_OPTS} scenario-tests true true false ================================================ FILE: src/main/java/redis/clients/jedis/AbstractPipeline.java ================================================ package redis.clients.jedis; import java.io.Closeable; public abstract class AbstractPipeline extends PipeliningBase implements Closeable { protected AbstractPipeline(CommandObjects commandObjects) { super(commandObjects); } @Override public abstract void close(); /** * Synchronize pipeline by reading all responses. */ public abstract void sync(); public Response publish(String channel, String message) { return appendCommand(commandObjects.publish(channel, message)); } public Response publish(byte[] channel, byte[] message) { return appendCommand(commandObjects.publish(channel, message)); } } ================================================ FILE: src/main/java/redis/clients/jedis/AbstractTransaction.java ================================================ package redis.clients.jedis; import java.io.Closeable; import java.util.List; public abstract class AbstractTransaction extends PipeliningBase implements Closeable { @Deprecated protected AbstractTransaction() { super(new CommandObjects()); } protected AbstractTransaction(CommandObjects commandObjects) { super(commandObjects); } public abstract void multi(); /** * Must be called before {@link AbstractTransaction#multi() MULTI}. */ public abstract String watch(final String... keys); /** * Must be called before {@link AbstractTransaction#multi() MULTI}. */ public abstract String watch(final byte[]... keys); public abstract String unwatch(); @Override public abstract void close(); public abstract List exec(); public abstract String discard(); public Response waitReplicas(int replicas, long timeout) { return appendCommand(commandObjects.waitReplicas(replicas, timeout)); } public Response publish(String channel, String message) { return appendCommand(commandObjects.publish(channel, message)); } public Response publish(byte[] channel, byte[] message) { return appendCommand(commandObjects.publish(channel, message)); } } ================================================ FILE: src/main/java/redis/clients/jedis/BinaryJedisPubSub.java ================================================ package redis.clients.jedis; public abstract class BinaryJedisPubSub extends JedisPubSubBase { @Override protected final byte[] encode(byte[] raw) { return raw; } } ================================================ FILE: src/main/java/redis/clients/jedis/BinaryJedisShardedPubSub.java ================================================ package redis.clients.jedis; public abstract class BinaryJedisShardedPubSub extends JedisShardedPubSubBase { @Override protected final byte[] encode(byte[] raw) { return raw; } } ================================================ FILE: src/main/java/redis/clients/jedis/Builder.java ================================================ package redis.clients.jedis; public abstract class Builder { public abstract T build(Object data); } ================================================ FILE: src/main/java/redis/clients/jedis/BuilderFactory.java ================================================ package redis.clients.jedis; import java.io.Serializable; import java.util.*; import java.util.stream.Collectors; import redis.clients.jedis.exceptions.JedisDataException; import redis.clients.jedis.resps.*; import redis.clients.jedis.resps.LCSMatchResult.MatchedPosition; import redis.clients.jedis.resps.LCSMatchResult.Position; import redis.clients.jedis.util.*; public final class BuilderFactory { public static final Builder RAW_OBJECT = new Builder() { @Override public Object build(Object data) { return data; } @Override public String toString() { return "Object"; } }; public static final Builder> RAW_OBJECT_LIST = new Builder>() { @Override public List build(Object data) { return (List) data; } @Override public String toString() { return "List"; } }; public static final Builder ENCODED_OBJECT = new Builder() { @Override public Object build(Object data) { return SafeEncoder.encodeObject(data); } @Override public String toString() { return "Object"; } }; public static final Builder> ENCODED_OBJECT_LIST = new Builder>() { @Override public List build(Object data) { return (List) SafeEncoder.encodeObject(data); } @Override public String toString() { return "List"; } }; public static final Builder LONG = new Builder() { @Override public Long build(Object data) { return (Long) data; } @Override public String toString() { return "Long"; } }; public static final Builder> LONG_LIST = new Builder>() { @Override @SuppressWarnings("unchecked") public List build(Object data) { if (null == data) { return null; } return (List) data; } @Override public String toString() { return "List"; } }; public static final Builder DOUBLE = new Builder() { @Override public Double build(Object data) { if (data == null) return null; else if (data instanceof Double) return (Double) data; else return DoublePrecision.parseFloatingPointNumber(STRING.build(data)); } @Override public String toString() { return "Double"; } }; public static final Builder> DOUBLE_LIST = new Builder>() { @Override @SuppressWarnings("unchecked") public List build(Object data) { if (null == data) return null; return ((List) data).stream().map(DOUBLE::build).collect(Collectors.toList()); } @Override public String toString() { return "List"; } }; public static final Builder BOOLEAN = new Builder() { @Override public Boolean build(Object data) { if (data == null) return null; else if (data instanceof Boolean) return (Boolean) data; return ((Long) data) == 1L; } @Override public String toString() { return "Boolean"; } }; public static final Builder> BOOLEAN_LIST = new Builder>() { @Override @SuppressWarnings("unchecked") public List build(Object data) { if (null == data) return null; return ((List) data).stream().map(BOOLEAN::build).collect(Collectors.toList()); } @Override public String toString() { return "List"; } }; public static final Builder> BOOLEAN_WITH_ERROR_LIST = new Builder>() { @Override @SuppressWarnings("unchecked") public List build(Object data) { if (null == data) return null; return ((List) data).stream() //.map((val) -> (val instanceof JedisDataException) ? val : BOOLEAN.build(val)) .map((val) -> (val instanceof JedisDataException) ? null : BOOLEAN.build(val)) .collect(Collectors.toList()); } @Override public String toString() { return "List"; } }; public static final Builder BINARY = new Builder() { @Override public byte[] build(Object data) { return (byte[]) data; } @Override public String toString() { return "byte[]"; } }; public static final Builder> BINARY_LIST = new Builder>() { @Override @SuppressWarnings("unchecked") public List build(Object data) { return (List) data; } @Override public String toString() { return "List"; } }; public static final Builder> BINARY_SET = new Builder>() { @Override public Set build(Object data) { if (null == data) { return null; } List l = BINARY_LIST.build(data); return SetFromList.of(l); } @Override public String toString() { return "Set"; } }; public static final Builder>> BINARY_PAIR_LIST = new Builder>>() { @Override @SuppressWarnings("unchecked") public List> build(Object data) { final List flatHash = (List) data; final List> pairList = new ArrayList<>(); final Iterator iterator = flatHash.iterator(); while (iterator.hasNext()) { pairList.add(new AbstractMap.SimpleEntry<>(iterator.next(), iterator.next())); } return pairList; } @Override public String toString() { return "List>"; } }; public static final Builder>> BINARY_PAIR_LIST_FROM_PAIRS = new Builder>>() { @Override @SuppressWarnings("unchecked") public List> build(Object data) { final List list = (List) data; final List> pairList = new ArrayList<>(); for (Object object : list) { final List flat = (List) object; pairList.add(new AbstractMap.SimpleEntry<>(flat.get(0), flat.get(1))); } return pairList; } @Override public String toString() { return "List>"; } }; public static final Builder STRING = new Builder() { @Override public String build(Object data) { return data == null ? null : SafeEncoder.encode((byte[]) data); } @Override public String toString() { return "String"; } }; public static final Builder> STRING_LIST = new Builder>() { @Override @SuppressWarnings("unchecked") public List build(Object data) { if (null == data) return null; return ((List) data).stream().map(STRING::build).collect(Collectors.toList()); } @Override public String toString() { return "List"; } }; public static final Builder> STRING_SET = new Builder>() { @Override @SuppressWarnings("unchecked") public Set build(Object data) { if (null == data) return null; return ((List) data).stream().map(STRING::build).collect(Collectors.toSet()); } @Override public String toString() { return "Set"; } }; public static final Builder> BINARY_MAP = new Builder>() { @Override @SuppressWarnings("unchecked") public Map build(Object data) { final List list = (List) data; if (list.isEmpty()) return Collections.emptyMap(); if (list.get(0) instanceof KeyValue) { final Map map = new JedisByteHashMap(); final Iterator iterator = list.iterator(); while (iterator.hasNext()) { KeyValue kv = (KeyValue) iterator.next(); map.put(BINARY.build(kv.getKey()), BINARY.build(kv.getValue())); } return map; } else { final Map map = new JedisByteHashMap(); final Iterator iterator = list.iterator(); while (iterator.hasNext()) { map.put(BINARY.build(iterator.next()), BINARY.build(iterator.next())); } return map; } } @Override public String toString() { return "Map"; } }; public static final Builder> STRING_MAP = new Builder>() { @Override @SuppressWarnings("unchecked") public Map build(Object data) { final List list = (List) data; if (list.isEmpty()) return Collections.emptyMap(); if (list.get(0) instanceof KeyValue) { final Map map = new HashMap<>(list.size(), 1f); final Iterator iterator = list.iterator(); while (iterator.hasNext()) { KeyValue kv = (KeyValue) iterator.next(); map.put(STRING.build(kv.getKey()), STRING.build(kv.getValue())); } return map; } else { final Map map = new HashMap<>(list.size() / 2, 1f); final Iterator iterator = list.iterator(); while (iterator.hasNext()) { map.put(STRING.build(iterator.next()), STRING.build(iterator.next())); } return map; } } @Override public String toString() { return "Map"; } }; public static final Builder> ENCODED_OBJECT_MAP = new Builder>() { @Override public Map build(Object data) { if (data == null) return null; final List list = (List) data; if (list.isEmpty()) return Collections.emptyMap(); if (list.get(0) instanceof KeyValue) { final Map map = new HashMap<>(list.size(), 1f); final Iterator iterator = list.iterator(); while (iterator.hasNext()) { KeyValue kv = (KeyValue) iterator.next(); map.put(STRING.build(kv.getKey()), ENCODED_OBJECT.build(kv.getValue())); } return map; } else { final Map map = new HashMap<>(list.size() / 2, 1f); final Iterator iterator = list.iterator(); while (iterator.hasNext()) { map.put(STRING.build(iterator.next()), ENCODED_OBJECT.build(iterator.next())); } return map; } } }; public static final Builder AGGRESSIVE_ENCODED_OBJECT = new Builder() { @Override public Object build(Object data) { if (data == null) return null; if (data instanceof List) { final List list = (List) data; if (list.isEmpty()) { return list == Protocol.PROTOCOL_EMPTY_MAP ? Collections.emptyMap() : Collections.emptyList(); } if (list.get(0) instanceof KeyValue) { return ((List) data).stream() .filter(kv -> kv != null && kv.getKey() != null && kv.getValue() != null) .collect(Collectors.toMap(kv -> STRING.build(kv.getKey()), kv -> this.build(kv.getValue()))); } else { return list.stream().map(this::build).collect(Collectors.toList()); } } else if (data instanceof byte[]) { return STRING.build(data); } else { return data; } } }; public static final Builder> AGGRESSIVE_ENCODED_OBJECT_MAP = new Builder>() { @Override public Map build(Object data) { return (Map) AGGRESSIVE_ENCODED_OBJECT.build(data); } }; public static final Builder>> STRING_PAIR_LIST = new Builder>>() { @Override @SuppressWarnings("unchecked") public List> build(Object data) { final List flatHash = (List) data; final List> pairList = new ArrayList<>(flatHash.size() / 2); final Iterator iterator = flatHash.iterator(); while (iterator.hasNext()) { pairList.add(KeyValue.of(STRING.build(iterator.next()), STRING.build(iterator.next()))); } return pairList; } @Override public String toString() { return "List>"; } }; public static final Builder>> STRING_PAIR_LIST_FROM_PAIRS = new Builder>>() { @Override @SuppressWarnings("unchecked") public List> build(Object data) { return ((List) data).stream().map(o -> (List) o) .map(l -> KeyValue.of(STRING.build(l.get(0)), STRING.build(l.get(1)))) .collect(Collectors.toList()); } @Override public String toString() { return "List>"; } }; public static final Builder> STRING_LONG_MAP = new Builder>() { @Override @SuppressWarnings("unchecked") public Map build(Object data) { final List list = (List) data; if (list.isEmpty()) return Collections.emptyMap(); if (list.get(0) instanceof KeyValue) { final Map map = new LinkedHashMap<>(list.size(), 1f); final Iterator iterator = list.iterator(); while (iterator.hasNext()) { KeyValue kv = (KeyValue) iterator.next(); map.put(STRING.build(kv.getKey()), LONG.build(kv.getValue())); } return map; } else { final Map map = new LinkedHashMap<>(list.size() / 2, 1f); final Iterator iterator = list.iterator(); while (iterator.hasNext()) { map.put(STRING.build(iterator.next()), LONG.build(iterator.next())); } return map; } } @Override public String toString() { return "Map"; } }; public static final Builder> STRING_DOUBLE_MAP = new Builder>() { @Override @SuppressWarnings("unchecked") public Map build(Object data) { final List list = (List) data; if (list.isEmpty()) return Collections.emptyMap(); if (list.get(0) instanceof KeyValue) { final Map map = new LinkedHashMap<>(list.size(), 1f); for (Object o : list) { KeyValue kv = (KeyValue) o; map.put(STRING.build(kv.getKey()), DOUBLE.build(kv.getValue())); } return map; } else { final Map map = new LinkedHashMap<>(list.size() / 2, 1f); final Iterator iterator = list.iterator(); while (iterator.hasNext()) { map.put(STRING.build(iterator.next()), DOUBLE.build(iterator.next())); } return map; } } @Override public String toString() { return "Map"; } }; public static final Builder> BINARY_DOUBLE_MAP = new Builder>() { @Override @SuppressWarnings("unchecked") public Map build(Object data) { final List list = (List) data; if (list.isEmpty()) return Collections.emptyMap(); final JedisByteMap map = new JedisByteMap<>(); if (list.get(0) instanceof KeyValue) { for (Object o : list) { KeyValue kv = (KeyValue) o; map.put(BINARY.build(kv.getKey()), DOUBLE.build(kv.getValue())); } return map; } else { final Iterator iterator = list.iterator(); while (iterator.hasNext()) { map.put(BINARY.build(iterator.next()), DOUBLE.build(iterator.next())); } return map; } } @Override public String toString() { return "Map"; } }; public static final Builder> KEYED_ELEMENT = new Builder>() { @Override @SuppressWarnings("unchecked") public KeyValue build(Object data) { if (data == null) return null; List l = (List) data; return KeyValue.of(STRING.build(l.get(0)), STRING.build(l.get(1))); } @Override public String toString() { return "KeyValue"; } }; public static final Builder> BINARY_KEYED_ELEMENT = new Builder>() { @Override @SuppressWarnings("unchecked") public KeyValue build(Object data) { if (data == null) return null; List l = (List) data; return KeyValue.of(BINARY.build(l.get(0)), BINARY.build(l.get(1))); } @Override public String toString() { return "KeyValue"; } }; public static final Builder> ZRANK_WITHSCORE_PAIR = new Builder>() { @Override public KeyValue build(Object data) { if (data == null) { return null; } List l = (List) data; return new KeyValue<>(LONG.build(l.get(0)), DOUBLE.build(l.get(1))); } @Override public String toString() { return "KeyValue"; } }; public static final Builder>> KEYED_STRING_LIST = new Builder>>() { @Override @SuppressWarnings("unchecked") public KeyValue> build(Object data) { if (data == null) return null; List l = (List) data; return new KeyValue<>(STRING.build(l.get(0)), STRING_LIST.build(l.get(1))); } @Override public String toString() { return "KeyValue>"; } }; public static final Builder> LONG_LONG_PAIR = new Builder>() { @Override @SuppressWarnings("unchecked") public KeyValue build(Object data) { if (data == null) return null; List dataList = (List) data; return new KeyValue<>(LONG.build(dataList.get(0)), LONG.build(dataList.get(1))); } }; public static final Builder>>> KEYED_STRING_LIST_LIST = new Builder>>>() { @Override public List>> build(Object data) { List list = (List) data; return list.stream().map(KEYED_STRING_LIST::build).collect(Collectors.toList()); } }; public static final Builder>> KEYED_BINARY_LIST = new Builder>>() { @Override @SuppressWarnings("unchecked") public KeyValue> build(Object data) { if (data == null) return null; List l = (List) data; return new KeyValue<>(BINARY.build(l.get(0)), BINARY_LIST.build(l.get(1))); } @Override public String toString() { return "KeyValue>"; } }; public static final Builder TUPLE = new Builder() { @Override @SuppressWarnings("unchecked") public Tuple build(Object data) { List l = (List) data; // never null if (l.isEmpty()) { return null; } return new Tuple(l.get(0), DOUBLE.build(l.get(1))); } @Override public String toString() { return "Tuple"; } }; public static final Builder> KEYED_TUPLE = new Builder>() { @Override @SuppressWarnings("unchecked") public KeyValue build(Object data) { if (data == null) return null; List l = (List) data; if (l.isEmpty()) return null; return KeyValue.of(STRING.build(l.get(0)), new Tuple(BINARY.build(l.get(1)), DOUBLE.build(l.get(2)))); } @Override public String toString() { return "KeyValue"; } }; public static final Builder> BINARY_KEYED_TUPLE = new Builder>() { @Override @SuppressWarnings("unchecked") public KeyValue build(Object data) { if (data == null) return null; List l = (List) data; if (l.isEmpty()) return null; return KeyValue.of(BINARY.build(l.get(0)), new Tuple(BINARY.build(l.get(1)), DOUBLE.build(l.get(2)))); } @Override public String toString() { return "KeyValue"; } }; public static final Builder> TUPLE_LIST = new Builder>() { @Override @SuppressWarnings("unchecked") public List build(Object data) { if (null == data) { return null; } List l = (List) data; final List result = new ArrayList<>(l.size() / 2); Iterator iterator = l.iterator(); while (iterator.hasNext()) { result.add(new Tuple(iterator.next(), DOUBLE.build(iterator.next()))); } return result; } @Override public String toString() { return "List"; } }; public static final Builder> TUPLE_LIST_RESP3 = new Builder>() { @Override @SuppressWarnings("unchecked") public List build(Object data) { if (null == data) return null; return ((List) data).stream().map(TUPLE::build).collect(Collectors.toList()); } @Override public String toString() { return "List"; } }; @Deprecated public static final Builder> TUPLE_ZSET = new Builder>() { @Override @SuppressWarnings("unchecked") public Set build(Object data) { if (null == data) { return null; } List l = (List) data; final Set result = new LinkedHashSet<>(l.size() / 2, 1); Iterator iterator = l.iterator(); while (iterator.hasNext()) { result.add(new Tuple(iterator.next(), DOUBLE.build(iterator.next()))); } return result; } @Override public String toString() { return "ZSet"; } }; @Deprecated public static final Builder> TUPLE_ZSET_RESP3 = new Builder>() { @Override @SuppressWarnings("unchecked") public Set build(Object data) { if (null == data) return null; return ((List) data).stream().map(TUPLE::build).collect(Collectors.toCollection(LinkedHashSet::new)); } @Override public String toString() { return "ZSet"; } }; private static final Builder> TUPLE_LIST_FROM_PAIRS = new Builder>() { @Override @SuppressWarnings("unchecked") public List build(Object data) { if (data == null) return null; return ((List>) data).stream().map(TUPLE::build).collect(Collectors.toList()); } @Override public String toString() { return "List"; } }; public static final Builder>> KEYED_TUPLE_LIST = new Builder>>() { @Override @SuppressWarnings("unchecked") public KeyValue> build(Object data) { if (data == null) return null; List l = (List) data; return new KeyValue<>(STRING.build(l.get(0)), TUPLE_LIST_FROM_PAIRS.build(l.get(1))); } @Override public String toString() { return "KeyValue>"; } }; public static final Builder>> BINARY_KEYED_TUPLE_LIST = new Builder>>() { @Override @SuppressWarnings("unchecked") public KeyValue> build(Object data) { if (data == null) return null; List l = (List) data; return new KeyValue<>(BINARY.build(l.get(0)), TUPLE_LIST_FROM_PAIRS.build(l.get(1))); } @Override public String toString() { return "KeyValue>"; } }; public static final Builder> SCAN_RESPONSE = new Builder>() { @Override public ScanResult build(Object data) { List result = (List) data; String newcursor = new String((byte[]) result.get(0)); List rawResults = (List) result.get(1); List results = new ArrayList<>(rawResults.size()); for (byte[] bs : rawResults) { results.add(SafeEncoder.encode(bs)); } return new ScanResult<>(newcursor, results); } }; public static final Builder>> HSCAN_RESPONSE = new Builder>>() { @Override public ScanResult> build(Object data) { List result = (List) data; String newcursor = new String((byte[]) result.get(0)); List rawResults = (List) result.get(1); List> results = new ArrayList<>(rawResults.size() / 2); Iterator iterator = rawResults.iterator(); while (iterator.hasNext()) { results.add(new AbstractMap.SimpleEntry<>(SafeEncoder.encode(iterator.next()), SafeEncoder.encode(iterator.next()))); } return new ScanResult<>(newcursor, results); } }; public static final Builder> SSCAN_RESPONSE = new Builder>() { @Override public ScanResult build(Object data) { List result = (List) data; String newcursor = new String((byte[]) result.get(0)); List rawResults = (List) result.get(1); List results = new ArrayList<>(rawResults.size()); for (byte[] bs : rawResults) { results.add(SafeEncoder.encode(bs)); } return new ScanResult<>(newcursor, results); } }; public static final Builder> ZSCAN_RESPONSE = new Builder>() { @Override public ScanResult build(Object data) { List result = (List) data; String newcursor = new String((byte[]) result.get(0)); List rawResults = (List) result.get(1); List results = new ArrayList<>(rawResults.size() / 2); Iterator iterator = rawResults.iterator(); while (iterator.hasNext()) { results.add(new Tuple(iterator.next(), BuilderFactory.DOUBLE.build(iterator.next()))); } return new ScanResult<>(newcursor, results); } }; public static final Builder> SCAN_BINARY_RESPONSE = new Builder>() { @Override public ScanResult build(Object data) { List result = (List) data; byte[] newcursor = (byte[]) result.get(0); List rawResults = (List) result.get(1); return new ScanResult<>(newcursor, rawResults); } }; public static final Builder>> HSCAN_BINARY_RESPONSE = new Builder>>() { @Override public ScanResult> build(Object data) { List result = (List) data; byte[] newcursor = (byte[]) result.get(0); List rawResults = (List) result.get(1); List> results = new ArrayList<>(rawResults.size() / 2); Iterator iterator = rawResults.iterator(); while (iterator.hasNext()) { results.add(new AbstractMap.SimpleEntry<>(iterator.next(), iterator.next())); } return new ScanResult<>(newcursor, results); } }; public static final Builder> SSCAN_BINARY_RESPONSE = new Builder>() { @Override public ScanResult build(Object data) { List result = (List) data; byte[] newcursor = (byte[]) result.get(0); List rawResults = (List) result.get(1); return new ScanResult<>(newcursor, rawResults); } }; public static final Builder> PUBSUB_NUMSUB_MAP = new Builder>() { @Override @SuppressWarnings("unchecked") public Map build(Object data) { final List flatHash = (List) data; final Map hash = new HashMap<>(flatHash.size() / 2, 1f); final Iterator iterator = flatHash.iterator(); while (iterator.hasNext()) { hash.put(SafeEncoder.encode((byte[]) iterator.next()), (Long) iterator.next()); } return hash; } @Override public String toString() { return "PUBSUB_NUMSUB_MAP"; } }; public static final Builder> GEO_COORDINATE_LIST = new Builder>() { @Override public List build(Object data) { if (null == data) { return null; } return interpretGeoposResult((List) data); } @Override public String toString() { return "List"; } private List interpretGeoposResult(List responses) { List responseCoordinate = new ArrayList<>(responses.size()); for (Object response : responses) { if (response == null) { responseCoordinate.add(null); } else { List respList = (List) response; GeoCoordinate coord = new GeoCoordinate(DOUBLE.build(respList.get(0)), DOUBLE.build(respList.get(1))); responseCoordinate.add(coord); } } return responseCoordinate; } }; public static final Builder> GEORADIUS_WITH_PARAMS_RESULT = new Builder>() { @Override public List build(Object data) { if (data == null) { return null; } List objectList = (List) data; List responses = new ArrayList<>(objectList.size()); if (objectList.isEmpty()) { return responses; } if (objectList.get(0) instanceof List) { // list of members with additional informations GeoRadiusResponse resp; for (Object obj : objectList) { List informations = (List) obj; resp = new GeoRadiusResponse((byte[]) informations.get(0)); int size = informations.size(); for (int idx = 1; idx < size; idx++) { Object info = informations.get(idx); if (info instanceof List) { // coordinate List coord = (List) info; resp.setCoordinate(new GeoCoordinate(DOUBLE.build(coord.get(0)), DOUBLE.build(coord.get(1)))); } else if (info instanceof Long) { // score resp.setRawScore(LONG.build(info)); } else { // distance resp.setDistance(DOUBLE.build(info)); } } responses.add(resp); } } else { // list of members for (Object obj : objectList) { responses.add(new GeoRadiusResponse((byte[]) obj)); } } return responses; } @Override public String toString() { return "GeoRadiusWithParamsResult"; } }; public static final Builder> COMMAND_DOCS_RESPONSE = new Builder>() { @Override public Map build(Object data) { if (data == null) return null; List list = (List) data; if (list.isEmpty()) return Collections.emptyMap(); if (list.get(0) instanceof KeyValue) { final Map map = new HashMap<>(list.size(), 1f); final Iterator iterator = list.iterator(); while (iterator.hasNext()) { KeyValue kv = (KeyValue) iterator.next(); map.put(STRING.build(kv.getKey()), new CommandDocument(ENCODED_OBJECT_MAP.build(kv.getValue()))); } return map; } else { final Map map = new HashMap<>(list.size() / 2, 1f); final Iterator iterator = list.iterator(); while (iterator.hasNext()) { map.put(STRING.build(iterator.next()), new CommandDocument(ENCODED_OBJECT_MAP.build(iterator.next()))); } return map; } } }; @Deprecated public static final Builder> COMMAND_INFO_RESPONSE = CommandInfo.COMMAND_INFO_RESPONSE; public static final Builder> LATENCY_LATEST_RESPONSE = new Builder>() { @Override public Map build(Object data) { if (data == null) { return null; } List rawList = (List) data; Map map = new HashMap<>(rawList.size()); for (Object rawLatencyLatestInfo : rawList) { if (rawLatencyLatestInfo == null) { continue; } LatencyLatestInfo latestInfo = LatencyLatestInfo.LATENCY_LATEST_BUILDER.build(rawLatencyLatestInfo); String name = latestInfo.getCommand(); map.put(name, latestInfo); } return map; } }; public static final Builder> LATENCY_HISTORY_RESPONSE = new Builder>() { @Override public List build(Object data) { if (data == null) { return null; } List rawList = (List) data; List response = new ArrayList<>(rawList.size()); for (Object rawLatencyHistoryInfo : rawList) { if (rawLatencyHistoryInfo == null) { continue; } LatencyHistoryInfo historyInfo = LatencyHistoryInfo.LATENCY_HISTORY_BUILDER.build(rawLatencyHistoryInfo); response.add(historyInfo); } return response; } }; private static final Builder>> CLUSTER_SHARD_SLOTS_RANGES = new Builder>>() { @Override public List> build(Object data) { if (null == data) { return null; } List rawSlots = (List) data; List> slotsRanges = new ArrayList<>(); for (int i = 0; i < rawSlots.size(); i += 2) { slotsRanges.add(Arrays.asList(rawSlots.get(i), rawSlots.get(i + 1))); } return slotsRanges; } }; private static final Builder> CLUSTER_SHARD_NODE_INFO_LIST = new Builder>() { final Map mappingFunctions = createDecoderMap(); private Map createDecoderMap() { Map tempMappingFunctions = new HashMap<>(); tempMappingFunctions.put(ClusterShardNodeInfo.ID, STRING); tempMappingFunctions.put(ClusterShardNodeInfo.ENDPOINT, STRING); tempMappingFunctions.put(ClusterShardNodeInfo.IP, STRING); tempMappingFunctions.put(ClusterShardNodeInfo.HOSTNAME, STRING); tempMappingFunctions.put(ClusterShardNodeInfo.PORT, LONG); tempMappingFunctions.put(ClusterShardNodeInfo.TLS_PORT, LONG); tempMappingFunctions.put(ClusterShardNodeInfo.ROLE, STRING); tempMappingFunctions.put(ClusterShardNodeInfo.REPLICATION_OFFSET, LONG); tempMappingFunctions.put(ClusterShardNodeInfo.HEALTH, STRING); return tempMappingFunctions; } @Override @SuppressWarnings("unchecked") public List build(Object data) { if (null == data) { return null; } List response = new ArrayList<>(); List clusterShardNodeInfos = (List) data; for (Object clusterShardNodeInfoObject : clusterShardNodeInfos) { List clusterShardNodeInfo = (List) clusterShardNodeInfoObject; Iterator iterator = clusterShardNodeInfo.iterator(); response.add(new ClusterShardNodeInfo(createMapFromDecodingFunctions(iterator, mappingFunctions))); } return response; } @Override public String toString() { return "List"; } }; public static final Builder> CLUSTER_SHARD_INFO_LIST = new Builder>() { final Map mappingFunctions = createDecoderMap(); private Map createDecoderMap() { Map tempMappingFunctions = new HashMap<>(); tempMappingFunctions.put(ClusterShardInfo.SLOTS, CLUSTER_SHARD_SLOTS_RANGES); tempMappingFunctions.put(ClusterShardInfo.NODES, CLUSTER_SHARD_NODE_INFO_LIST); return tempMappingFunctions; } @Override @SuppressWarnings("unchecked") public List build(Object data) { if (null == data) { return null; } List response = new ArrayList<>(); List clusterShardInfos = (List) data; for (Object clusterShardInfoObject : clusterShardInfos) { List clusterShardInfo = (List) clusterShardInfoObject; Iterator iterator = clusterShardInfo.iterator(); response.add(new ClusterShardInfo(createMapFromDecodingFunctions(iterator, mappingFunctions))); } return response; } @Override public String toString() { return "List"; } }; public static final Builder> MODULE_LIST = new Builder>() { @Override public List build(Object data) { if (data == null) { return null; } List> objectList = (List>) data; List responses = new ArrayList<>(objectList.size()); if (objectList.isEmpty()) { return responses; } for (List moduleResp : objectList) { if (moduleResp.get(0) instanceof KeyValue) { responses.add(new Module(STRING.build(((KeyValue) moduleResp.get(0)).getValue()), LONG.build(((KeyValue) moduleResp.get(1)).getValue()).intValue())); continue; } Module m = new Module(SafeEncoder.encode((byte[]) moduleResp.get(1)), ((Long) moduleResp.get(3)).intValue()); responses.add(m); } return responses; } @Override public String toString() { return "List"; } }; /** * Create a AccessControlUser object from the ACL GETUSER reply. */ public static final Builder ACCESS_CONTROL_USER = new Builder() { @Override public AccessControlUser build(Object data) { Map map = ENCODED_OBJECT_MAP.build(data); if (map == null) return null; return new AccessControlUser(map); } @Override public String toString() { return "AccessControlUser"; } }; /** * Create an Access Control Log Entry Result of ACL LOG command */ public static final Builder> ACCESS_CONTROL_LOG_ENTRY_LIST = new Builder>() { private final Map mappingFunctions = createDecoderMap(); private Map createDecoderMap() { Map tempMappingFunctions = new HashMap<>(); tempMappingFunctions.put(AccessControlLogEntry.COUNT, LONG); tempMappingFunctions.put(AccessControlLogEntry.REASON, STRING); tempMappingFunctions.put(AccessControlLogEntry.CONTEXT, STRING); tempMappingFunctions.put(AccessControlLogEntry.OBJECT, STRING); tempMappingFunctions.put(AccessControlLogEntry.USERNAME, STRING); tempMappingFunctions.put(AccessControlLogEntry.AGE_SECONDS, DOUBLE); tempMappingFunctions.put(AccessControlLogEntry.CLIENT_INFO, STRING); tempMappingFunctions.put(AccessControlLogEntry.ENTRY_ID, LONG); tempMappingFunctions.put(AccessControlLogEntry.TIMESTAMP_CREATED, LONG); tempMappingFunctions.put(AccessControlLogEntry.TIMESTAMP_LAST_UPDATED, LONG); return tempMappingFunctions; } @Override public List build(Object data) { if (null == data) { return null; } List list = new ArrayList<>(); List> logEntries = (List>) data; for (List logEntryData : logEntries) { Iterator logEntryDataIterator = logEntryData.iterator(); AccessControlLogEntry accessControlLogEntry = new AccessControlLogEntry( createMapFromDecodingFunctions(logEntryDataIterator, mappingFunctions, BACKUP_BUILDERS_FOR_DECODING_FUNCTIONS)); list.add(accessControlLogEntry); } return list; } @Override public String toString() { return "List"; } }; // Stream Builders --> public static final Builder STREAM_ENTRY_ID = new Builder() { @Override public StreamEntryID build(Object data) { if (null == data) { return null; } String id = SafeEncoder.encode((byte[]) data); return new StreamEntryID(id); } @Override public String toString() { return "StreamEntryID"; } }; public static final Builder> STREAM_ENTRY_ID_LIST = new Builder>() { @Override @SuppressWarnings("unchecked") public List build(Object data) { if (null == data) { return null; } List objectList = (List) data; List responses = new ArrayList<>(objectList.size()); if (!objectList.isEmpty()) { for(Object object : objectList) { responses.add(STREAM_ENTRY_ID.build(object)); } } return responses; } }; public static final Builder STREAM_ENTRY_DELETION_RESULT = new Builder() { @Override public StreamEntryDeletionResult build(Object data) { if (data == null) { return null; } return StreamEntryDeletionResult.fromLong((Long) data); } @Override public String toString() { return "StreamEntryDeletionResult"; } }; public static final Builder> STREAM_ENTRY_DELETION_RESULT_LIST = new Builder>() { @Override @SuppressWarnings("unchecked") public List build(Object data) { if (data == null) { return null; } List objectList = (List) data; List responses = new ArrayList<>(objectList.size()); for (Object object : objectList) { responses.add(STREAM_ENTRY_DELETION_RESULT.build(object)); } return responses; } @Override public String toString() { return "List"; } }; public static final Builder STREAM_ENTRY = new Builder() { @Override @SuppressWarnings("unchecked") public StreamEntry build(Object data) { if (null == data) { return null; } List objectList = (List) data; if (objectList.isEmpty()) { return null; } String entryIdString = SafeEncoder.encode((byte[]) objectList.get(0)); StreamEntryID entryID = new StreamEntryID(entryIdString); List hash = (List) objectList.get(1); Iterator hashIterator = hash.iterator(); Map map = new LinkedHashMap<>(hash.size() / 2, 1f); while (hashIterator.hasNext()) { map.put(SafeEncoder.encode(hashIterator.next()), SafeEncoder.encode(hashIterator.next())); } return new StreamEntry(entryID, map); } @Override public String toString() { return "StreamEntry"; } }; public static final Builder> STREAM_ENTRY_LIST = new Builder>() { @Override @SuppressWarnings("unchecked") public List build(Object data) { if (null == data) { return null; } List> objectList = (List>) data; List responses = new ArrayList<>(objectList.size() / 2); if (objectList.isEmpty()) { return responses; } for (ArrayList res : objectList) { if (res == null) { responses.add(null); continue; } String entryIdString = SafeEncoder.encode((byte[]) res.get(0)); StreamEntryID entryID = new StreamEntryID(entryIdString); List hash = (List) res.get(1); Map fieldsMap = null; if (hash != null) { Iterator hashIterator = hash.iterator(); fieldsMap = new LinkedHashMap<>(hash.size() / 2, 1f); while (hashIterator.hasNext()) { fieldsMap.put(SafeEncoder.encode(hashIterator.next()), SafeEncoder.encode(hashIterator.next())); } } if (res.size() >= 4) { Long millisElapsedFromDelivery = LONG.build(res.get(2)); Long deliveredCount = LONG.build(res.get(3)); responses.add(new StreamEntry(entryID, fieldsMap, millisElapsedFromDelivery, deliveredCount)); continue; } responses.add(new StreamEntry(entryID, fieldsMap)); } return responses; } @Override public String toString() { return "List"; } }; public static final Builder>> STREAM_AUTO_CLAIM_RESPONSE = new Builder>>() { @Override @SuppressWarnings("unchecked") public Map.Entry> build(Object data) { if (null == data) { return null; } List objectList = (List) data; return new AbstractMap.SimpleEntry<>(STREAM_ENTRY_ID.build(objectList.get(0)), STREAM_ENTRY_LIST.build(objectList.get(1))); } @Override public String toString() { return "Map.Entry>"; } }; public static final Builder>> STREAM_AUTO_CLAIM_JUSTID_RESPONSE = new Builder>>() { @Override @SuppressWarnings("unchecked") public Map.Entry> build(Object data) { if (null == data) { return null; } List objectList = (List) data; return new AbstractMap.SimpleEntry<>(STREAM_ENTRY_ID.build(objectList.get(0)), STREAM_ENTRY_ID_LIST.build(objectList.get(1))); } @Override public String toString() { return "Map.Entry>"; } }; /** * @deprecated Use {@link BuilderFactory#STREAM_AUTO_CLAIM_JUSTID_RESPONSE}. */ @Deprecated public static final Builder>> STREAM_AUTO_CLAIM_ID_RESPONSE = STREAM_AUTO_CLAIM_JUSTID_RESPONSE; public static final Builder>>> STREAM_READ_RESPONSE = new Builder>>>() { @Override public List>> build(Object data) { if (data == null) return null; List list = (List) data; if (list.isEmpty()) return Collections.emptyList(); if (list.get(0) instanceof KeyValue) { return ((List) list).stream() .map(kv -> new KeyValue<>(STRING.build(kv.getKey()), STREAM_ENTRY_LIST.build(kv.getValue()))) .collect(Collectors.toList()); } else { List>> result = new ArrayList<>(list.size()); for (Object anObj : list) { List streamObj = (List) anObj; String streamKey = STRING.build(streamObj.get(0)); List streamEntries = STREAM_ENTRY_LIST.build(streamObj.get(1)); result.add(KeyValue.of(streamKey, streamEntries)); } return result; } } @Override public String toString() { return "List>>"; } }; public static final Builder>> STREAM_READ_MAP_RESPONSE = new Builder>>() { @Override public Map> build(Object data) { if (data == null) return null; List list = (List) data; if (list.isEmpty()) return Collections.emptyMap(); if (list.get(0) instanceof KeyValue) { return ((List) list).stream() .collect(Collectors.toMap(kv -> STRING.build(kv.getKey()), kv -> STREAM_ENTRY_LIST.build(kv.getValue()), (v1, v2) -> v1, LinkedHashMap::new)); } else { Map> result = new LinkedHashMap<>(list.size(), 1f); for (Object anObj : list) { List streamObj = (List) anObj; String streamKey = STRING.build(streamObj.get(0)); List streamEntries = STREAM_ENTRY_LIST.build(streamObj.get(1)); result.put(streamKey, streamEntries); } return result; } } @Override public String toString() { return "Map>"; } }; public static final Builder> STREAM_PENDING_ENTRY_LIST = new Builder>() { @Override @SuppressWarnings("unchecked") public List build(Object data) { if (null == data) { return null; } List streamsEntries = (List) data; List result = new ArrayList<>(streamsEntries.size()); for (Object streamObj : streamsEntries) { List stream = (List) streamObj; String id = SafeEncoder.encode((byte[]) stream.get(0)); String consumerName = SafeEncoder.encode((byte[]) stream.get(1)); long idleTime = BuilderFactory.LONG.build(stream.get(2)); long deliveredTimes = BuilderFactory.LONG.build(stream.get(3)); result.add(new StreamPendingEntry(new StreamEntryID(id), consumerName, idleTime, deliveredTimes)); } return result; } @Override public String toString() { return "List"; } }; public static final Builder STREAM_INFO = new Builder() { Map mappingFunctions = createDecoderMap(); private Map createDecoderMap() { Map tempMappingFunctions = new HashMap<>(); tempMappingFunctions.put(StreamInfo.LAST_GENERATED_ID, STREAM_ENTRY_ID); tempMappingFunctions.put(StreamInfo.FIRST_ENTRY, STREAM_ENTRY); tempMappingFunctions.put(StreamInfo.LENGTH, LONG); tempMappingFunctions.put(StreamInfo.RADIX_TREE_KEYS, LONG); tempMappingFunctions.put(StreamInfo.RADIX_TREE_NODES, LONG); tempMappingFunctions.put(StreamInfo.LAST_ENTRY, STREAM_ENTRY); tempMappingFunctions.put(StreamInfo.GROUPS, LONG); tempMappingFunctions.put(StreamInfo.IDMP_DURATION, LONG); tempMappingFunctions.put(StreamInfo.IDMP_MAXSIZE, LONG); tempMappingFunctions.put(StreamInfo.PIDS_TRACKED, LONG); tempMappingFunctions.put(StreamInfo.IIDS_TRACKED, LONG); tempMappingFunctions.put(StreamInfo.IIDS_ADDED, LONG); tempMappingFunctions.put(StreamInfo.IIDS_DUPLICATES, LONG); return tempMappingFunctions; } @Override @SuppressWarnings("unchecked") public StreamInfo build(Object data) { if (null == data) { return null; } List streamsEntries = (List) data; Iterator iterator = streamsEntries.iterator(); return new StreamInfo(createMapFromDecodingFunctions(iterator, mappingFunctions)); } @Override public String toString() { return "StreamInfo"; } }; public static final Builder> STREAM_GROUP_INFO_LIST = new Builder>() { Map mappingFunctions = createDecoderMap(); private Map createDecoderMap() { Map tempMappingFunctions = new HashMap<>(); tempMappingFunctions.put(StreamGroupInfo.NAME, STRING); tempMappingFunctions.put(StreamGroupInfo.CONSUMERS, LONG); tempMappingFunctions.put(StreamGroupInfo.PENDING, LONG); tempMappingFunctions.put(StreamGroupInfo.LAST_DELIVERED, STREAM_ENTRY_ID); return tempMappingFunctions; } @Override @SuppressWarnings("unchecked") public List build(Object data) { if (null == data) { return null; } List list = new ArrayList<>(); List streamsEntries = (List) data; Iterator groupsArray = streamsEntries.iterator(); while (groupsArray.hasNext()) { List groupInfo = (List) groupsArray.next(); Iterator groupInfoIterator = groupInfo.iterator(); StreamGroupInfo streamGroupInfo = new StreamGroupInfo(createMapFromDecodingFunctions( groupInfoIterator, mappingFunctions)); list.add(streamGroupInfo); } return list; } @Override public String toString() { return "List"; } }; /** * @deprecated Use {@link BuilderFactory#STREAM_CONSUMER_INFO_LIST}. */ @Deprecated public static final Builder> STREAM_CONSUMERS_INFO_LIST = new Builder>() { Map mappingFunctions = createDecoderMap(); private Map createDecoderMap() { Map tempMappingFunctions = new HashMap<>(); tempMappingFunctions.put(StreamConsumersInfo.NAME, STRING); tempMappingFunctions.put(StreamConsumersInfo.IDLE, LONG); tempMappingFunctions.put(StreamConsumersInfo.PENDING, LONG); return tempMappingFunctions; } @Override @SuppressWarnings("unchecked") public List build(Object data) { if (null == data) { return null; } List list = new ArrayList<>(); List streamsEntries = (List) data; Iterator groupsArray = streamsEntries.iterator(); while (groupsArray.hasNext()) { List groupInfo = (List) groupsArray.next(); Iterator consumerInfoIterator = groupInfo.iterator(); StreamConsumersInfo streamGroupInfo = new StreamConsumersInfo( createMapFromDecodingFunctions(consumerInfoIterator, mappingFunctions)); list.add(streamGroupInfo); } return list; } @Override public String toString() { return "List"; } }; public static final Builder> STREAM_CONSUMER_INFO_LIST = new Builder>() { Map mappingFunctions = createDecoderMap(); private Map createDecoderMap() { Map tempMappingFunctions = new HashMap<>(); tempMappingFunctions.put(StreamConsumerInfo.NAME, STRING); tempMappingFunctions.put(StreamConsumerInfo.IDLE, LONG); tempMappingFunctions.put(StreamConsumerInfo.PENDING, LONG); return tempMappingFunctions; } @Override @SuppressWarnings("unchecked") public List build(Object data) { if (null == data) { return null; } List list = new ArrayList<>(); List streamsEntries = (List) data; Iterator groupsArray = streamsEntries.iterator(); while (groupsArray.hasNext()) { List groupInfo = (List) groupsArray.next(); Iterator consumerInfoIterator = groupInfo.iterator(); StreamConsumerInfo streamConsumerInfo = new StreamConsumerInfo( createMapFromDecodingFunctions(consumerInfoIterator, mappingFunctions)); list.add(streamConsumerInfo); } return list; } @Override public String toString() { return "List"; } }; private static final Builder> STREAM_CONSUMER_FULL_INFO_LIST = new Builder>() { final Map mappingFunctions = createDecoderMap(); private Map createDecoderMap() { Map tempMappingFunctions = new HashMap<>(); tempMappingFunctions.put(StreamConsumerFullInfo.NAME, STRING); tempMappingFunctions.put(StreamConsumerFullInfo.SEEN_TIME, LONG); tempMappingFunctions.put(StreamConsumerFullInfo.PEL_COUNT, LONG); tempMappingFunctions.put(StreamConsumerFullInfo.PENDING, ENCODED_OBJECT_LIST); return tempMappingFunctions; } @Override @SuppressWarnings("unchecked") public List build(Object data) { if (null == data) { return null; } List list = new ArrayList<>(); List streamsEntries = (List) data; for (Object streamsEntry : streamsEntries) { List consumerInfoList = (List) streamsEntry; Iterator consumerInfoIterator = consumerInfoList.iterator(); StreamConsumerFullInfo consumerInfo = new StreamConsumerFullInfo( createMapFromDecodingFunctions(consumerInfoIterator, mappingFunctions)); list.add(consumerInfo); } return list; } @Override public String toString() { return "List"; } }; private static final Builder> STREAM_GROUP_FULL_INFO_LIST = new Builder>() { final Map mappingFunctions = createDecoderMap(); private Map createDecoderMap() { Map tempMappingFunctions = new HashMap<>(); tempMappingFunctions.put(StreamGroupFullInfo.NAME, STRING); tempMappingFunctions.put(StreamGroupFullInfo.CONSUMERS, STREAM_CONSUMER_FULL_INFO_LIST); tempMappingFunctions.put(StreamGroupFullInfo.PENDING, ENCODED_OBJECT_LIST); tempMappingFunctions.put(StreamGroupFullInfo.LAST_DELIVERED, STREAM_ENTRY_ID); tempMappingFunctions.put(StreamGroupFullInfo.PEL_COUNT, LONG); return tempMappingFunctions; } @Override @SuppressWarnings("unchecked") public List build(Object data) { if (null == data) { return null; } List list = new ArrayList<>(); List streamsEntries = (List) data; for (Object streamsEntry : streamsEntries) { List groupInfo = (List) streamsEntry; Iterator groupInfoIterator = groupInfo.iterator(); StreamGroupFullInfo groupFullInfo = new StreamGroupFullInfo( createMapFromDecodingFunctions(groupInfoIterator, mappingFunctions)); list.add(groupFullInfo); } return list; } @Override public String toString() { return "List"; } }; public static final Builder STREAM_FULL_INFO = new Builder() { final Map mappingFunctions = createDecoderMap(); private Map createDecoderMap() { Map tempMappingFunctions = new HashMap<>(); tempMappingFunctions.put(StreamFullInfo.LAST_GENERATED_ID, STREAM_ENTRY_ID); tempMappingFunctions.put(StreamFullInfo.LENGTH, LONG); tempMappingFunctions.put(StreamFullInfo.RADIX_TREE_KEYS, LONG); tempMappingFunctions.put(StreamFullInfo.RADIX_TREE_NODES, LONG); tempMappingFunctions.put(StreamFullInfo.GROUPS, STREAM_GROUP_FULL_INFO_LIST); tempMappingFunctions.put(StreamFullInfo.ENTRIES, STREAM_ENTRY_LIST); return tempMappingFunctions; } @Override @SuppressWarnings("unchecked") public StreamFullInfo build(Object data) { if (null == data) { return null; } List streamsEntries = (List) data; Iterator iterator = streamsEntries.iterator(); return new StreamFullInfo(createMapFromDecodingFunctions(iterator, mappingFunctions)); } @Override public String toString() { return "StreamFullInfo"; } }; /** * @deprecated Use {@link BuilderFactory#STREAM_FULL_INFO}. */ @Deprecated public static final Builder STREAM_INFO_FULL = STREAM_FULL_INFO; public static final Builder VECTOR_INFO = new Builder() { final Map mappingFunctions = createDecoderMap(); private Map createDecoderMap() { Map tempMappingFunctions = new HashMap<>(); tempMappingFunctions.put(VectorInfo.VECTOR_DIM, LONG); tempMappingFunctions.put(VectorInfo.TYPE, STRING); tempMappingFunctions.put(VectorInfo.SIZE, LONG); tempMappingFunctions.put(VectorInfo.MAX_NODE_UID, LONG); tempMappingFunctions.put(VectorInfo.VSET_UID, LONG); tempMappingFunctions.put(VectorInfo.MAX_NODES, LONG); tempMappingFunctions.put(VectorInfo.PROJECTION_INPUT_DIM, LONG); tempMappingFunctions.put(VectorInfo.ATTRIBUTES_COUNT, LONG); tempMappingFunctions.put(VectorInfo.MAX_LEVEL, LONG); return tempMappingFunctions; } @Override @SuppressWarnings("unchecked") public VectorInfo build(Object data) { if (null == data) { return null; } List vectorEntries = (List) data; Iterator iterator = vectorEntries.iterator(); return new VectorInfo(createMapFromDecodingFunctions(iterator, mappingFunctions)); } @Override public String toString() { return "VectorInfo"; } }; public static final Builder STREAM_PENDING_SUMMARY = new Builder() { @Override @SuppressWarnings("unchecked") public StreamPendingSummary build(Object data) { if (null == data) { return null; } List objectList = (List) data; long total = LONG.build(objectList.get(0)); StreamEntryID minId = STREAM_ENTRY_ID.build(objectList.get(1)); StreamEntryID maxId = STREAM_ENTRY_ID.build(objectList.get(2)); Map map = objectList.get(3) == null ? null : ((List>) objectList.get(3)).stream().collect( Collectors.toMap(pair -> STRING.build(pair.get(0)), pair -> Long.parseLong(STRING.build(pair.get(1))))); return new StreamPendingSummary(total, minId, maxId, map); } @Override public String toString() { return "StreamPendingSummary"; } }; public static final Builder> STREAM_ENTRY_BINARY_LIST = new Builder>() { @Override @SuppressWarnings("unchecked") public List build(Object data) { if (null == data) { return null; } List> objectList = (List>) data; List responses = new ArrayList<>(objectList.size() / 2); if (objectList.isEmpty()) { return responses; } for (ArrayList res : objectList) { if (res == null) { responses.add(null); continue; } String entryIdString = SafeEncoder.encode((byte[]) res.get(0)); StreamEntryID entryID = new StreamEntryID(entryIdString); List hash = (List) res.get(1); Map map = null; if (hash != null) { Iterator hashIterator = hash.iterator(); map = new JedisByteHashMap(); while (hashIterator.hasNext()) { map.put(BINARY.build(hashIterator.next()), BINARY.build(hashIterator.next())); } } if (res.size() >= 4) { Long millisElapsedFromDelivery = LONG.build(res.get(2)); Long deliveredCount = LONG.build(res.get(3)); responses.add(new StreamEntryBinary(entryID, map, millisElapsedFromDelivery, deliveredCount)); continue; } responses.add(new StreamEntryBinary(entryID, map)); } return responses; } @Override public String toString() { return "List"; } }; public static final Builder>> STREAM_READ_BINARY_MAP_RESPONSE = new Builder>>() { @Override @SuppressWarnings("unchecked") public Map> build(Object data) { if (data == null) return null; List list = (List) data; if (list.isEmpty()) return Collections.emptyMap(); JedisByteMap> result = new JedisByteMap<>(); if (list.get(0) instanceof KeyValue) { ((List) list).forEach(kv -> result.put(BINARY.build(kv.getKey()), STREAM_ENTRY_BINARY_LIST.build(kv.getValue()))); return result; } else { for (Object anObj : list) { List streamObj = (List) anObj; byte[] streamKey = (byte[]) streamObj.get(0); List streamEntries = STREAM_ENTRY_BINARY_LIST.build(streamObj.get(1)); result.put(streamKey, streamEntries); } return result; } } @Override public String toString() { return "Map>"; } }; public static final Builder>>> STREAM_READ_BINARY_RESPONSE = new Builder>>>() { @Override @SuppressWarnings("unchecked") public List>> build(Object data) { if (data == null) return null; List list = (List) data; if (list.isEmpty()) return Collections.emptyList(); if (list.get(0) instanceof KeyValue) { return ((List) list).stream() .map(kv -> new KeyValue<>(BINARY.build(kv.getKey()), STREAM_ENTRY_BINARY_LIST.build(kv.getValue()))) .collect(Collectors.toList()); } else { List>> result = new ArrayList<>(list.size()); for (Object anObj : list) { List streamObj = (List) anObj; byte[] streamKey = BINARY.build(streamObj.get(0)); List streamEntries = STREAM_ENTRY_BINARY_LIST.build(streamObj.get(1)); result.add(KeyValue.of(streamKey, streamEntries)); } return result; } } @Override public String toString() { return "List>>"; } }; private static final List BACKUP_BUILDERS_FOR_DECODING_FUNCTIONS = Arrays.asList(STRING, LONG, DOUBLE); private static Map createMapFromDecodingFunctions(Iterator iterator, Map mappingFunctions) { return createMapFromDecodingFunctions(iterator, mappingFunctions, null); } private static Map createMapFromDecodingFunctions(Iterator iterator, Map mappingFunctions, Collection backupBuilders) { if (!iterator.hasNext()) { return Collections.emptyMap(); } Map resultMap = new HashMap<>(); while (iterator.hasNext()) { final Object tempObject = iterator.next(); final String mapKey; final Object rawValue; if (tempObject instanceof KeyValue) { KeyValue kv = (KeyValue) tempObject; mapKey = STRING.build(kv.getKey()); rawValue = kv.getValue(); } else { mapKey = STRING.build(tempObject); rawValue = iterator.next(); } if (mappingFunctions.containsKey(mapKey)) { resultMap.put(mapKey, mappingFunctions.get(mapKey).build(rawValue)); } else { // For future - if we don't find an element in our builder map Collection builders = backupBuilders != null ? backupBuilders : mappingFunctions.values(); for (Builder b : builders) { try { resultMap.put(mapKey, b.build(rawValue)); break; } catch (ClassCastException e) { // We continue with next builder } } } } return resultMap; } // <-- Stream Builders public static final Builder STR_ALGO_LCS_RESULT_BUILDER = new Builder() { @Override public LCSMatchResult build(Object data) { if (data == null) { return null; } if (data instanceof byte[]) { return new LCSMatchResult(STRING.build(data)); } else if (data instanceof Long) { return new LCSMatchResult(LONG.build(data)); } else { long len = 0; List matchedPositions = new ArrayList<>(); List objectList = (List) data; if (objectList.get(0) instanceof KeyValue) { Iterator iterator = objectList.iterator(); while (iterator.hasNext()) { KeyValue kv = (KeyValue) iterator.next(); if ("matches".equalsIgnoreCase(STRING.build(kv.getKey()))) { addMatchedPosition(matchedPositions, kv.getValue()); } else if ("len".equalsIgnoreCase(STRING.build(kv.getKey()))) { len = LONG.build(kv.getValue()); } } } else { for (int i = 0; i < objectList.size(); i += 2) { if ("matches".equalsIgnoreCase(STRING.build(objectList.get(i)))) { addMatchedPosition(matchedPositions, objectList.get(i + 1)); } else if ("len".equalsIgnoreCase(STRING.build(objectList.get(i)))) { len = LONG.build(objectList.get(i + 1)); } } } return new LCSMatchResult(matchedPositions, len); } } private void addMatchedPosition(List matchedPositions, Object o) { List matches = (List) o; for (Object obj : matches) { if (obj instanceof List) { List positions = (List) obj; Position a = new Position( LONG.build(((List) positions.get(0)).get(0)), LONG.build(((List) positions.get(0)).get(1)) ); Position b = new Position( LONG.build(((List) positions.get(1)).get(0)), LONG.build(((List) positions.get(1)).get(1)) ); long matchLen = 0; if (positions.size() >= 3) { matchLen = LONG.build(positions.get(2)); } matchedPositions.add(new MatchedPosition(a, b, matchLen)); } } } }; public static final Builder> STRING_MAP_FROM_PAIRS = new Builder>() { @Override @SuppressWarnings("unchecked") public Map build(Object data) { final List list = (List) data; if (list.isEmpty()) return Collections.emptyMap(); if (list.get(0) instanceof KeyValue) { return ((List) list).stream() .collect(Collectors.toMap(kv -> STRING.build(kv.getKey()), kv -> STRING.build(kv.getValue()))); } final Map map = new HashMap<>(list.size()); for (Object object : list) { if (object == null) continue; final List flat = (List) object; if (flat.isEmpty()) continue; map.put(STRING.build(flat.get(0)), STRING.build(flat.get(1))); } return map; } @Override public String toString() { return "Map"; } }; public static final Builder> ENCODED_OBJECT_MAP_FROM_PAIRS = new Builder>() { @Override @SuppressWarnings("unchecked") public Map build(Object data) { final List list = (List) data; if (list.isEmpty()) return Collections.emptyMap(); if (list.get(0) instanceof KeyValue) { return ((List) list).stream() .collect(Collectors.toMap(kv -> STRING.build(kv.getKey()), kv -> ENCODED_OBJECT.build(kv.getValue()))); } final Map map = new HashMap<>(list.size()); for (Object object : list) { if (object == null) continue; final List flat = (List) object; if (flat.isEmpty()) continue; map.put(STRING.build(flat.get(0)), STRING.build(flat.get(1))); } return map; } @Override public String toString() { return "Map"; } }; /** * @deprecated Use {@link LibraryInfo#LIBRARY_INFO_LIST}. */ @Deprecated public static final Builder> LIBRARY_LIST = LibraryInfo.LIBRARY_INFO_LIST; public static final Builder>> STRING_LIST_LIST = new Builder>>() { @Override @SuppressWarnings("unchecked") public List> build(Object data) { if (null == data) return null; return ((List) data).stream().map(STRING_LIST::build).collect(Collectors.toList()); } @Override public String toString() { return "List>"; } }; public static final Builder>> ENCODED_OBJECT_LIST_LIST = new Builder>>() { @Override @SuppressWarnings("unchecked") public List> build(Object data) { if (null == data) return null; return ((List) data).stream().map(ENCODED_OBJECT_LIST::build).collect(Collectors.toList()); } @Override public String toString() { return "List>"; } }; // Vector Set builders public static final Builder> VSIM_SCORE_ATTRIBS_MAP = new Builder>() { @Override @SuppressWarnings("unchecked") public Map build(Object data) { if (data == null) return null; List list = (List) data; if (list.isEmpty()) return Collections.emptyMap(); if (list.get(0) instanceof KeyValue) { final Map result = new LinkedHashMap<>(list.size(), 1f); for (Object o : list) { KeyValue kv = (KeyValue) o; List scoreAndAttribs = (List) kv.getValue(); result.put(STRING.build(kv.getKey()), new VSimScoreAttribs(DOUBLE.build(scoreAndAttribs.get(0)), STRING.build(scoreAndAttribs.get(1)))); } return result; } else { final Map result = new LinkedHashMap<>(list.size() / 3, 1f); for (int i = 0; i < list.size(); i += 3) { result.put(STRING.build(list.get(i)), new VSimScoreAttribs(DOUBLE.build(list.get(i + 1)), STRING.build(list.get(i + 2)))); } return result; } } @Override public String toString() { return "Map"; } }; public static final Builder> VSIM_SCORE_ATTRIBS_BINARY_MAP = new Builder>() { @Override @SuppressWarnings("unchecked") public Map build(Object data) { if (data == null) return null; List list = (List) data; if (list.isEmpty()) return Collections.emptyMap(); JedisByteMap result = new JedisByteMap<>(); if (list.get(0) instanceof KeyValue) { for (Object o : list) { KeyValue kv = (KeyValue) o; List scoreAndAttribs = (List) kv.getValue(); result.put(BINARY.build(kv.getKey()), new VSimScoreAttribs(DOUBLE.build(scoreAndAttribs.get(0)), STRING.build(scoreAndAttribs.get(1)))); } } else { for (int i = 0; i < list.size(); i += 3) { result.put(BINARY.build(list.get(i)), new VSimScoreAttribs(DOUBLE.build(list.get(i + 1)), STRING.build(list.get(i + 2)))); } } return result; } @Override public String toString() { return "Map"; } }; public static final Builder VEMB_RAW_RESULT = new Builder() { @Override @SuppressWarnings("unchecked") public RawVector build(Object data) { if (data == null) return null; List list = (List) data; String quantizationType = STRING.build(list.get(0)); byte[] rawData = (byte[]) list.get(1); Double norm = DOUBLE.build(list.get(2)); Double quantizationRange = list.size() > 3 ? DOUBLE.build(list.get(3)) : null; return new RawVector(quantizationType, rawData, norm, quantizationRange); } @Override public String toString() { return "RawVector"; } }; // VLINKS builders public static final Builder>> VLINKS_WITH_SCORES_RESULT = new Builder>>() { @Override @SuppressWarnings("unchecked") public List> build(Object data) { if (data == null) return null; List list = (List) data; List> result = new ArrayList<>(); for (Object scoresRaw : list) { if (scoresRaw == null) continue; Map scores = STRING_DOUBLE_MAP.build(scoresRaw); result.add(scores); } return result; } @Override public String toString() { return "List>"; } }; public static final Builder>> BINARY_LIST_LIST = new Builder>>() { @Override @SuppressWarnings("unchecked") public List> build(Object data) { if (null == data) return null; return ((List) data).stream().map(BINARY_LIST::build).collect(Collectors.toList()); } @Override public String toString() { return "List>"; } }; public static final Builder>> VLINKS_WITH_SCORES_RESULT_BINARY = new Builder>>() { @Override @SuppressWarnings("unchecked") public List> build(Object data) { if (data == null) return null; List list = (List) data; List> result = new ArrayList<>(); for (Object scoresRaw : list) { if (scoresRaw == null) continue; Map scores = BINARY_DOUBLE_MAP.build(scoresRaw); result.add(scores); } return result; } @Override public String toString() { return "List>"; } }; /** * A decorator to implement Set from List. Assume that given List do not contains duplicated * values. The resulting set displays the same ordering, concurrency, and performance * characteristics as the backing list. This class should be used only for Redis commands which * return Set result. */ protected static class SetFromList extends AbstractSet implements Serializable { private static final long serialVersionUID = -2850347066962734052L; private final List list; private SetFromList(List list) { this.list = list; } @Override public void clear() { list.clear(); } @Override public int size() { return list.size(); } @Override public boolean isEmpty() { return list.isEmpty(); } @Override public boolean contains(Object o) { return list.contains(o); } @Override public boolean remove(Object o) { return list.remove(o); } @Override public boolean add(E e) { return !contains(e) && list.add(e); } @Override public Iterator iterator() { return list.iterator(); } @Override public Object[] toArray() { return list.toArray(); } @Override public T[] toArray(T[] a) { return list.toArray(a); } @Override public String toString() { return list.toString(); } @Override public int hashCode() { return list.hashCode(); } @Override public boolean equals(Object o) { if (o == null) return false; if (o == this) return true; if (!(o instanceof Set)) return false; Collection c = (Collection) o; if (c.size() != size()) { return false; } return containsAll(c); } @Override public boolean containsAll(Collection c) { return list.containsAll(c); } @Override public boolean removeAll(Collection c) { return list.removeAll(c); } @Override public boolean retainAll(Collection c) { return list.retainAll(c); } protected static SetFromList of(List list) { if (list == null) { return null; } return new SetFromList<>(list); } } private BuilderFactory() { throw new InstantiationError("Must not instantiate this class"); } } ================================================ FILE: src/main/java/redis/clients/jedis/ClientSetInfoConfig.java ================================================ package redis.clients.jedis; import redis.clients.jedis.exceptions.JedisValidationException; /** * Configuration for CLIENT SETINFO command behaviour. *

* This class supports two modes of operation: *

    *
  • Advanced mode: Using {@link #withLibNameSuffix(String)} for advanced suffix customization, * where the provided string is already preformatted according to the rules of the `CLIENT SETINFO` * command
  • *
  • Simple mode: Using {@link #ClientSetInfoConfig(DriverInfo)} used when the command parameter * will be built by the driver based on the {@link DriverInfo} provided
  • *
*

* For backward compatibility, {@link #getUpstreamDrivers()} returns the upstream drivers string * when using driver info mode. * @see DriverInfo * @see CLIENT SETINFO */ public final class ClientSetInfoConfig { private final boolean disabled; private final DriverInfo driverInfo; /** * Creates a new ClientSetInfoConfig with default settings. *

* The default configuration uses the "jedis" library name without any upstream drivers. */ public ClientSetInfoConfig() { this(false); } /** * Creates a new ClientSetInfoConfig with the specified disabled state. *

* When disabled, the CLIENT SETINFO command will not be sent to Redis. * @param disabled {@code true} to disable CLIENT SETINFO, {@code false} otherwise */ public ClientSetInfoConfig(boolean disabled) { this.disabled = disabled; this.driverInfo = DriverInfo.builder().build(); } /** * Creates a new ClientSetInfoConfig with a library name suffix. *

* This constructor is for legacy compatibility. The suffix will be appended to "jedis" in * parentheses, resulting in a format like: {@code jedis(suffix)}. *

* For adding upstream driver information, use {@link #ClientSetInfoConfig(DriverInfo)} with a * {@link DriverInfo} that has upstream drivers. * @param libNameSuffix the suffix to append to "jedis" (will be placed in parentheses) * @throws JedisValidationException if libNameSuffix contains braces */ public ClientSetInfoConfig(String libNameSuffix) { this.disabled = false; this.driverInfo = DriverInfo.builder().addUpstreamDriver(libNameSuffix).build(); } /** * Creates a new ClientSetInfoConfig with the specified driver information. *

* This is the recommended constructor for setting up driver information with upstream drivers. * The driver information can optionally override the library name completely. * @param driverInfo the driver information, must not be {@code null} * @throws JedisValidationException if driverInfo is {@code null} */ public ClientSetInfoConfig(DriverInfo driverInfo) { if (driverInfo == null) { throw new JedisValidationException("DriverInfo must not be null"); } this.disabled = false; this.driverInfo = driverInfo; } /** * @return {@code true} if CLIENT SETINFO is disabled, {@code false} otherwise */ public boolean isDisabled() { return disabled; } /** * @return the driver information */ public DriverInfo getDriverInfo() { return driverInfo; } /** * Returns the formatted upstream drivers string. *

* Multiple drivers are separated by semicolons, with the most recently added driver appearing * first. *

* Examples: *

    *
  • {@code "spring-data-redis_v3.2.0"} - single upstream driver
  • *
  • {@code "lettuce-core_v6.4.1;spring-data-redis_v3.2.0"} - multiple upstream drivers
  • *
* @return the formatted upstream drivers string, or {@code null} if no upstream drivers are set */ public String getUpstreamDrivers() { return driverInfo.getUpstreamDrivers(); } /** * Default configuration that uses the Jedis library name without any upstream drivers. */ public static final ClientSetInfoConfig DEFAULT = new ClientSetInfoConfig(); /** * Configuration that disables CLIENT SETINFO command. */ public static final ClientSetInfoConfig DISABLED = new ClientSetInfoConfig(true); /** * Creates a new ClientSetInfoConfig with a library name suffix. *

* This is the legacy method for simple name customization. The provided suffix will be appended * to "jedis" in parentheses, resulting in a format like: {@code jedis(suffix)}. For adding * upstream driver information, use {@link #ClientSetInfoConfig(DriverInfo)} with a * * {@link DriverInfo} that has upstream drivers. * @param suffix the suffix to append to "jedis" (will be placed in parentheses) * @return a new ClientSetInfoConfig with the library name suffix */ public static ClientSetInfoConfig withLibNameSuffix(String suffix) { return new ClientSetInfoConfig(suffix); } } ================================================ FILE: src/main/java/redis/clients/jedis/ClusterCommandObjects.java ================================================ package redis.clients.jedis; import redis.clients.jedis.params.IParams; import redis.clients.jedis.params.MSetExParams; import redis.clients.jedis.params.ScanParams; import redis.clients.jedis.resps.HotkeysInfo; import redis.clients.jedis.params.HotkeysParams; import redis.clients.jedis.resps.ScanResult; import redis.clients.jedis.util.JedisClusterCRC16; import redis.clients.jedis.util.JedisClusterHashTag; import redis.clients.jedis.util.KeyValue; import java.util.*; import java.util.function.BiConsumer; import java.util.function.Function; import java.util.stream.Collectors; import static redis.clients.jedis.Protocol.Command.*; import static redis.clients.jedis.Protocol.Keyword.TYPE; public class ClusterCommandObjects extends CommandObjects { private static final String CLUSTER_UNSUPPORTED_MESSAGE = "Not supported in cluster mode."; private static final String SCAN_PATTERN_MESSAGE = "Cluster mode only supports SCAN command" + " with MATCH pattern containing hash-tag ( curly-brackets enclosed string )"; @Override public final CommandObject> scan(String cursor) { throw new IllegalArgumentException(SCAN_PATTERN_MESSAGE); } @Override public final CommandObject> scan(String cursor, ScanParams params) { String match = params.match(); if (match == null || !JedisClusterHashTag.isClusterCompliantMatchPattern(match)) { throw new IllegalArgumentException(SCAN_PATTERN_MESSAGE); } return new CommandObject<>(commandArguments(SCAN).add(cursor).addParams(params).addHashSlotKey(match), BuilderFactory.SCAN_RESPONSE); } @Override public final CommandObject> scan(String cursor, ScanParams params, String type) { String match = params.match(); if (match == null || !JedisClusterHashTag.isClusterCompliantMatchPattern(match)) { throw new IllegalArgumentException(SCAN_PATTERN_MESSAGE); } return new CommandObject<>(commandArguments(SCAN).add(cursor).addParams(params).addHashSlotKey(match).add(TYPE).add(type), BuilderFactory.SCAN_RESPONSE); } @Override public final CommandObject> scan(byte[] cursor) { throw new IllegalArgumentException(SCAN_PATTERN_MESSAGE); } @Override public final CommandObject> scan(byte[] cursor, ScanParams params) { byte[] match = params.binaryMatch(); if (match == null || !JedisClusterHashTag.isClusterCompliantMatchPattern(match)) { throw new IllegalArgumentException(SCAN_PATTERN_MESSAGE); } return new CommandObject<>(commandArguments(SCAN).add(cursor).addParams(params).addHashSlotKey(match), BuilderFactory.SCAN_BINARY_RESPONSE); } @Override public final CommandObject> scan(byte[] cursor, ScanParams params, byte[] type) { byte[] match = params.binaryMatch(); if (match == null || !JedisClusterHashTag.isClusterCompliantMatchPattern(match)) { throw new IllegalArgumentException(SCAN_PATTERN_MESSAGE); } return new CommandObject<>(commandArguments(SCAN).add(cursor).addParams(params).addHashSlotKey(match).add(TYPE).add(type), BuilderFactory.SCAN_BINARY_RESPONSE); } @Override public final CommandObject waitReplicas(int replicas, long timeout) { throw new UnsupportedOperationException(CLUSTER_UNSUPPORTED_MESSAGE); } @Override public CommandObject> waitAOF(long numLocal, long numReplicas, long timeout) { throw new UnsupportedOperationException(CLUSTER_UNSUPPORTED_MESSAGE); } @Override public CommandObject hotkeysStart(HotkeysParams params) { throw new UnsupportedOperationException(CLUSTER_UNSUPPORTED_MESSAGE); } @Override public CommandObject hotkeysStop() { throw new UnsupportedOperationException(CLUSTER_UNSUPPORTED_MESSAGE); } @Override public CommandObject hotkeysReset() { throw new UnsupportedOperationException(CLUSTER_UNSUPPORTED_MESSAGE); } @Override public CommandObject hotkeysGet() { throw new UnsupportedOperationException(CLUSTER_UNSUPPORTED_MESSAGE); } /** * Groups key-value pairs by their hash slot and creates separate CommandArguments for each slot. * This enables commands with multiple keys to be properly distributed across Redis cluster nodes * by ensuring that each resulting command only operates on keys that hash to the same slot. * * @param args the original command arguments to copy (the command itself will be preserved) * @param keysValues variable number of key-value pairs to be grouped (must be even length) * @param params additional parameters for the command (may be null) * @return a list of CommandArguments objects, each containing only keys/values that belong to the same hash slot * @throws IllegalArgumentException if keysValues has odd length */ protected List groupArgumentsByKeyValueHashSlot(CommandArguments args, String[] keysValues, IParams params) { return groupArgumentsByKeyValueHashSlotImpl( args, keysValues, params, JedisClusterCRC16::getSlot, CommandArguments::key, CommandArguments::add, false ); } /** * Groups key-value pairs by their hash slot and creates separate CommandArguments for each slot. * This enables commands with multiple keys to be properly distributed across Redis cluster nodes * by ensuring that each resulting command only operates on keys that hash to the same slot. * * @param args the original command arguments to copy (the command itself will be preserved) * @param keysValues variable number of key-value pairs to be grouped (must be even length) * @param params additional parameters for the command (may be null) * @return a list of CommandArguments objects, each containing only keys/values that belong to the same hash slot * @throws IllegalArgumentException if keysValues has odd length */ protected List groupArgumentsByKeyValueHashSlot(CommandArguments args, byte[][] keysValues, IParams params) { return groupArgumentsByKeyValueHashSlotImpl( args, keysValues, params, JedisClusterCRC16::getSlot, CommandArguments::key, CommandArguments::add, false ); } /** * Groups keys by their hash slot and creates separate CommandArguments for each slot. * This enables commands with multiple keys (like DEL, EXISTS, MGET) to be properly distributed * across Redis cluster nodes by ensuring that each resulting command only operates on keys * that hash to the same slot. * *

Order Preservation: This method preserves the order of keys as they appear in the input array. * Consecutive keys that hash to the same slot are grouped together into a single CommandArguments, * but non-consecutive keys with the same slot are kept in separate CommandArguments to maintain order. * For example, if input keys map to slots [A, B, A], the result will be 3 separate CommandArguments * (not 2), ensuring that when results are concatenated, they match the original input key order.

* * @param args the original command arguments to copy (the command itself will be preserved) * @param keys variable number of keys to be grouped * @param params additional parameters for the command (may be null) * @return a list of CommandArguments objects, each containing only keys that belong to the same hash slot, * preserving the original key order */ protected List groupArgumentsByKeyHashSlot(CommandArguments args, String[] keys, IParams params) { return groupArgumentsByKeyValueHashSlotImpl( args, keys, params, JedisClusterCRC16::getSlot, CommandArguments::key, null, false ); } /** * Groups keys by their hash slot and creates separate CommandArguments for each slot. * This enables commands with multiple keys (like DEL, EXISTS, MGET) to be properly distributed * across Redis cluster nodes by ensuring that each resulting command only operates on keys * that hash to the same slot. * *

Order Preservation: This method preserves the order of keys as they appear in the input array. * Consecutive keys that hash to the same slot are grouped together into a single CommandArguments, * but non-consecutive keys with the same slot are kept in separate CommandArguments to maintain order. * For example, if input keys map to slots [A, B, A], the result will be 3 separate CommandArguments * (not 2), ensuring that when results are concatenated, they match the original input key order.

* * @param args the original command arguments to copy (the command itself will be preserved) * @param keys variable number of keys to be grouped * @param params additional parameters for the command (may be null) * @return a list of CommandArguments objects, each containing only keys that belong to the same hash slot, * preserving the original key order */ protected List groupArgumentsByKeyHashSlot(CommandArguments args, byte[][] keys, IParams params) { return groupArgumentsByKeyValueHashSlotImpl( args, keys, params, JedisClusterCRC16::getSlot, CommandArguments::key, null, false ); } /** * Groups key-value pairs by their hash slot and creates separate CommandArguments for each slot. * Inserts the key count after the command but before the keys (for commands like MSETEX). * *

Order Preservation: This method preserves the order of key-value pairs as they appear * in the input array. Consecutive pairs that hash to the same slot are grouped together, but * non-consecutive pairs with the same slot are kept in separate CommandArguments to maintain order.

* * @param args the original command arguments to copy (the command itself will be preserved) * @param keysValues variable number of key-value pairs to be grouped (must be even length) * @param params additional parameters for the command (may be null) * @return a list of CommandArguments objects, each containing only keys/values that belong to the same hash slot * @throws IllegalArgumentException if keysValues has odd length */ protected List groupArgumentsByKeyValueHashSlotWithKeyCount(CommandArguments args, String[] keysValues, IParams params) { return groupArgumentsByKeyValueHashSlotImpl( args, keysValues, params, JedisClusterCRC16::getSlot, CommandArguments::key, CommandArguments::add, true ); } /** * Groups key-value pairs by their hash slot and creates separate CommandArguments for each slot. * Inserts the key count after the command but before the keys (for commands like MSETEX). * * @param args the original command arguments to copy (the command itself will be preserved) * @param keysValues variable number of key-value pairs to be grouped (must be even length) * @param params additional parameters for the command (may be null) * @return a list of CommandArguments objects, each containing only keys/values that belong to the same hash slot * @throws IllegalArgumentException if keysValues has odd length */ protected List groupArgumentsByKeyValueHashSlotWithKeyCount(CommandArguments args, byte[][] keysValues, IParams params) { return groupArgumentsByKeyValueHashSlotImpl( args, keysValues, params, JedisClusterCRC16::getSlot, CommandArguments::key, CommandArguments::add, true ); } /** * Internal helper method that implements the common logic for grouping keys (and optionally values) by hash slot. * When valueAdder is null, this method processes keys only (for commands like DEL, EXISTS, MGET). * When valueAdder is provided, this method processes key-value pairs (for commands like MSET). * *

Order Preservation: This method preserves the order of keys as they appear in the input array. * Consecutive keys that hash to the same slot are grouped together into a single CommandArguments, * but non-consecutive keys with the same slot are kept in separate CommandArguments to maintain order. * For example, if input keys map to slots [A, B, A], the result will be 3 separate CommandArguments * (not 2), preserving the original key order in the concatenated results.

* * @param the type of key/value elements (String or byte[]) * @param args the original command arguments to copy (the command itself will be preserved) * @param keysOrKeysValues array of keys (when valueAdder is null) or key-value pairs (when valueAdder is provided) * @param params additional parameters for the command (may be null) * @param slotCalculator function to calculate the hash slot for a key * @param keyAdder function to add a key to CommandArguments * @param valueAdder function to add a value to CommandArguments (may be null for key-only operations) * @param insertKeyCount if true, inserts the number of keys after the command but before the keys (for commands like MSETEX) * @return a list of CommandArguments objects, each containing only keys/values that belong to the same hash slot, * preserving the original key order * @throws IllegalArgumentException if valueAdder is provided and keysOrKeysValues has odd length */ private List groupArgumentsByKeyValueHashSlotImpl( CommandArguments args, T[] keysOrKeysValues, IParams params, Function slotCalculator, BiConsumer keyAdder, BiConsumer valueAdder, boolean insertKeyCount) { boolean keyValueMode = valueAdder != null; int step = keyValueMode ? 2 : 1; if (keyValueMode && keysOrKeysValues.length % 2 != 0) { throw new IllegalArgumentException("keysValues must contain an even number of elements (key-value pairs)"); } if (keysOrKeysValues.length == 0) { return new ArrayList<>(); } // Wrap slotCalculator to apply keyPreProcessor transformation before slot calculation. // This ensures keys are grouped by their actual slot (after preprocessing), not the original key's slot. Function effectiveSlotCalculator = keyPreProcessor != null ? key -> calculateSlotFromPreprocessedKey(keyPreProcessor.actualKey(key)) : slotCalculator; // Group consecutive keys with the same slot together, preserving input order // Non-consecutive keys with the same slot will be in separate commands List result = new ArrayList<>(); int currentSlot = -1; List currentGroup = new ArrayList<>(); for (int i = 0; i < keysOrKeysValues.length; i += step) { T key = keysOrKeysValues[i]; int slot = effectiveSlotCalculator.apply(key); if (slot != currentSlot && !currentGroup.isEmpty()) { // Slot changed - finalize the current group result.add(createCommandArgsForGroup(args, currentGroup, params, keyAdder, valueAdder, insertKeyCount, step)); currentGroup = new ArrayList<>(); } currentSlot = slot; currentGroup.add(key); if (keyValueMode) { currentGroup.add(keysOrKeysValues[i + 1]); } } // Finalize the last group if (!currentGroup.isEmpty()) { result.add(createCommandArgsForGroup(args, currentGroup, params, keyAdder, valueAdder, insertKeyCount, step)); } return result; } /** * Calculates the hash slot for a preprocessed key. * * @param preprocessedKey the key after preprocessing (may be String, byte[], or Rawable) * @return the hash slot for the key */ private int calculateSlotFromPreprocessedKey(Object preprocessedKey) { if (preprocessedKey instanceof byte[]) { return JedisClusterCRC16.getSlot((byte[]) preprocessedKey); } else if (preprocessedKey instanceof String) { return JedisClusterCRC16.getSlot((String) preprocessedKey); } else if (preprocessedKey instanceof redis.clients.jedis.args.Rawable) { return JedisClusterCRC16.getSlot(((redis.clients.jedis.args.Rawable) preprocessedKey).getRaw()); } throw new IllegalArgumentException("Unsupported key type: " + preprocessedKey.getClass().getName()); } /** * Helper method to create a CommandArguments for a group of keys/values. */ private CommandArguments createCommandArgsForGroup( CommandArguments args, List groupedElements, IParams params, BiConsumer keyAdder, BiConsumer valueAdder, boolean insertKeyCount, int step) { boolean keyValueMode = valueAdder != null; CommandArguments slotArgs = commandArguments(args.getCommand()); // Insert key count after command but before keys (e.g., numkeys for MSETEX) if (insertKeyCount) { int keyCount = groupedElements.size() / step; slotArgs.add(keyCount); } // Add keys (and optionally values) for this slot for (int i = 0; i < groupedElements.size(); i += step) { keyAdder.accept(slotArgs, groupedElements.get(i)); if (keyValueMode) { valueAdder.accept(slotArgs, groupedElements.get(i + 1)); } } // Add params if provided if (params != null) { slotArgs.addParams(params); } return slotArgs; } // ==================== Multi-Shard Command Methods ==================== // These methods split commands across multiple Redis cluster shards based on key hash slots. // They return List> where each CommandObject targets keys in the same hash slot. /** * Creates multiple DEL command objects, one for each hash slot group. * This enables the DEL command to be executed across multiple Redis cluster shards * when keys hash to different slots. * * @param keys the keys to delete * @return a list of CommandObject instances, each containing keys that belong to the same hash slot */ public List> delMultiShard(String... keys) { CommandArguments args = commandArguments(DEL); List groupedArgs = groupArgumentsByKeyHashSlot(args, keys, null); return groupedArgs.stream() .map(cmdArgs -> new CommandObject<>(cmdArgs, BuilderFactory.LONG)) .collect(Collectors.toList()); } /** * Creates multiple DEL command objects, one for each hash slot group. * This enables the DEL command to be executed across multiple Redis cluster shards * when keys hash to different slots. * * @param keys the keys to delete * @return a list of CommandObject instances, each containing keys that belong to the same hash slot */ public List> delMultiShard(byte[]... keys) { CommandArguments args = commandArguments(DEL); List groupedArgs = groupArgumentsByKeyHashSlot(args, keys, null); return groupedArgs.stream() .map(cmdArgs -> new CommandObject<>(cmdArgs, BuilderFactory.LONG)) .collect(Collectors.toList()); } /** * Creates multiple EXISTS command objects, one for each hash slot group. * This enables the EXISTS command to be executed across multiple Redis cluster shards * when keys hash to different slots. * * @param keys the keys to check for existence * @return a list of CommandObject instances, each containing keys that belong to the same hash slot */ public List> existsMultiShard(String... keys) { CommandArguments args = commandArguments(EXISTS); List groupedArgs = groupArgumentsByKeyHashSlot(args, keys, null); return groupedArgs.stream() .map(cmdArgs -> new CommandObject<>(cmdArgs, BuilderFactory.LONG)) .collect(Collectors.toList()); } /** * Creates multiple EXISTS command objects, one for each hash slot group. * This enables the EXISTS command to be executed across multiple Redis cluster shards * when keys hash to different slots. * * @param keys the keys to check for existence * @return a list of CommandObject instances, each containing keys that belong to the same hash slot */ public List> existsMultiShard(byte[]... keys) { CommandArguments args = commandArguments(EXISTS); List groupedArgs = groupArgumentsByKeyHashSlot(args, keys, null); return groupedArgs.stream() .map(cmdArgs -> new CommandObject<>(cmdArgs, BuilderFactory.LONG)) .collect(Collectors.toList()); } /** * Creates multiple MGET command objects, one for each hash slot group. * This enables the MGET command to be executed across multiple Redis cluster shards * when keys hash to different slots. * *

Order Preservation: The returned commands preserve the order of keys as they appear * in the input array. When the results from all commands are concatenated in order, the values * will correspond positionally to the input keys.

* * @param keys the keys to retrieve values for * @return a list of CommandObject instances, each containing keys that belong to the same hash slot */ public List>> mgetMultiShard(String... keys) { CommandArguments args = commandArguments(MGET); List groupedArgs = groupArgumentsByKeyHashSlot(args, keys, null); return groupedArgs.stream() .map(cmdArgs -> new CommandObject<>(cmdArgs, BuilderFactory.STRING_LIST)) .collect(Collectors.toList()); } /** * Creates multiple MGET command objects, one for each hash slot group. * This enables the MGET command to be executed across multiple Redis cluster shards * when keys hash to different slots. * *

Order Preservation: The returned commands preserve the order of keys as they appear * in the input array. When the results from all commands are concatenated in order, the values * will correspond positionally to the input keys.

* * @param keys the keys to retrieve values for * @return a list of CommandObject instances, each containing keys that belong to the same hash slot */ public List>> mgetMultiShard(byte[]... keys) { CommandArguments args = commandArguments(MGET); List groupedArgs = groupArgumentsByKeyHashSlot(args, keys, null); return groupedArgs.stream() .map(cmdArgs -> new CommandObject<>(cmdArgs, BuilderFactory.BINARY_LIST)) .collect(Collectors.toList()); } /** * Creates multiple MSET command objects, one for each hash slot group. * This enables the MSET command to be executed across multiple Redis cluster shards * when keys hash to different slots. * * @param keysvalues alternating keys and values (key1, value1, key2, value2, ...) * @return a list of CommandObject instances, each containing key-value pairs that belong to the same hash slot */ public List> msetMultiShard(String... keysvalues) { CommandArguments args = commandArguments(MSET); List groupedArgs = groupArgumentsByKeyValueHashSlot(args, keysvalues, null); return groupedArgs.stream() .map(cmdArgs -> new CommandObject<>(cmdArgs, BuilderFactory.STRING)) .collect(Collectors.toList()); } /** * Creates multiple MSET command objects, one for each hash slot group. * This enables the MSET command to be executed across multiple Redis cluster shards * when keys hash to different slots. * * @param keysvalues alternating keys and values (key1, value1, key2, value2, ...) * @return a list of CommandObject instances, each containing key-value pairs that belong to the same hash slot */ public List> msetMultiShard(byte[]... keysvalues) { CommandArguments args = commandArguments(MSET); List groupedArgs = groupArgumentsByKeyValueHashSlot(args, keysvalues, null); return groupedArgs.stream() .map(cmdArgs -> new CommandObject<>(cmdArgs, BuilderFactory.STRING)) .collect(Collectors.toList()); } /** * Creates multiple TOUCH command objects, one for each hash slot group. * This enables the TOUCH command to be executed across multiple Redis cluster shards * when keys hash to different slots. * * @param keys the keys to touch * @return a list of CommandObject instances, each containing keys that belong to the same hash slot */ public List> touchMultiShard(String... keys) { CommandArguments args = commandArguments(TOUCH); List groupedArgs = groupArgumentsByKeyHashSlot(args, keys, null); return groupedArgs.stream() .map(cmdArgs -> new CommandObject<>(cmdArgs, BuilderFactory.LONG)) .collect(Collectors.toList()); } /** * Creates multiple TOUCH command objects, one for each hash slot group. * This enables the TOUCH command to be executed across multiple Redis cluster shards * when keys hash to different slots. * * @param keys the keys to touch * @return a list of CommandObject instances, each containing keys that belong to the same hash slot */ public List> touchMultiShard(byte[]... keys) { CommandArguments args = commandArguments(TOUCH); List groupedArgs = groupArgumentsByKeyHashSlot(args, keys, null); return groupedArgs.stream() .map(cmdArgs -> new CommandObject<>(cmdArgs, BuilderFactory.LONG)) .collect(Collectors.toList()); } /** * Creates multiple UNLINK command objects, one for each hash slot group. * This enables the UNLINK command to be executed across multiple Redis cluster shards * when keys hash to different slots. * * @param keys the keys to unlink * @return a list of CommandObject instances, each containing keys that belong to the same hash slot */ public List> unlinkMultiShard(String... keys) { CommandArguments args = commandArguments(UNLINK); List groupedArgs = groupArgumentsByKeyHashSlot(args, keys, null); return groupedArgs.stream() .map(cmdArgs -> new CommandObject<>(cmdArgs, BuilderFactory.LONG)) .collect(Collectors.toList()); } /** * Creates multiple UNLINK command objects, one for each hash slot group. * This enables the UNLINK command to be executed across multiple Redis cluster shards * when keys hash to different slots. * * @param keys the keys to unlink * @return a list of CommandObject instances, each containing keys that belong to the same hash slot */ public List> unlinkMultiShard(byte[]... keys) { CommandArguments args = commandArguments(UNLINK); List groupedArgs = groupArgumentsByKeyHashSlot(args, keys, null); return groupedArgs.stream() .map(cmdArgs -> new CommandObject<>(cmdArgs, BuilderFactory.LONG)) .collect(Collectors.toList()); } /** * Creates multiple MSETEX command objects, one for each hash slot group. * This enables the MSETEX command to be executed across multiple Redis cluster shards * when keys hash to different slots. Each command preserves the provided parameters * (expiration, NX/XX conditions). * * @param params the MSETEX parameters (expiration, NX/XX conditions) * @param keysvalues alternating keys and values (key1, value1, key2, value2, ...) * @return a list of CommandObject instances, each containing key-value pairs that belong to the same hash slot */ public List> msetexMultiShard(MSetExParams params, String... keysvalues) { CommandArguments args = commandArguments(MSETEX); List groupedArgs = groupArgumentsByKeyValueHashSlotWithKeyCount(args, keysvalues, params); return groupedArgs.stream() .map(cmdArgs -> new CommandObject<>(cmdArgs, BuilderFactory.BOOLEAN)) .collect(Collectors.toList()); } /** * Creates multiple MSETEX command objects, one for each hash slot group. * This enables the MSETEX command to be executed across multiple Redis cluster shards * when keys hash to different slots. Each command preserves the provided parameters * (expiration, NX/XX conditions). * * @param params the MSETEX parameters (expiration, NX/XX conditions) * @param keysvalues alternating keys and values (key1, value1, key2, value2, ...) * @return a list of CommandObject instances, each containing key-value pairs that belong to the same hash slot */ public List> msetexMultiShard(MSetExParams params, byte[]... keysvalues) { CommandArguments args = commandArguments(MSETEX); List groupedArgs = groupArgumentsByKeyValueHashSlotWithKeyCount(args, keysvalues, params); return groupedArgs.stream() .map(cmdArgs -> new CommandObject<>(cmdArgs, BuilderFactory.BOOLEAN)) .collect(Collectors.toList()); } } ================================================ FILE: src/main/java/redis/clients/jedis/ClusterPipeline.java ================================================ package redis.clients.jedis; import java.time.Duration; import java.util.Set; import org.apache.commons.pool2.impl.GenericObjectPoolConfig; import redis.clients.jedis.exceptions.JedisClusterOperationException; import redis.clients.jedis.providers.ClusterConnectionProvider; import redis.clients.jedis.util.IOUtils; /** * Pipeline implementation for Redis Cluster mode. *

* ClusterPipeline allows batching multiple commands for efficient execution in a Redis Cluster * environment. Commands are automatically routed to the appropriate cluster nodes based on * key hash slots. *

*

* Important Limitations: *

*
    *
  • Single-node commands only: Only commands that can be routed to a single * node are supported. Commands requiring execution on multiple nodes (ALL_SHARDS, MULTI_SHARD, * ALL_NODES, or SPECIAL request policies) will throw {@link UnsupportedOperationException}.
  • *
  • Examples of unsupported commands: *
      *
    • {@code KEYS} - requires execution on all master shards
    • *
    • {@code MGET} with keys in different slots - requires execution on multiple shards
    • *
    • {@code SCRIPT LOAD} - requires execution on all nodes
    • *
    *
  • *
  • For multi-node commands, use the non-pipelined mode * of {@link RedisClusterClient} instead.
  • *
*

* Usage Pattern: *

*
{@code
 * try (RedisCluster cluster = new RedisCluster(nodes, config)) {
 *   // For single-node commands, use pipelined mode
 *   try (ClusterPipeline pipeline = cluster.pipelined()) {
 *     Response r1 = pipeline.set("key1", "value1");
 *     Response r2 = pipeline.get("key1");
 *     pipeline.sync();
 *
 *     System.out.println(r1.get()); // "OK"
 *     System.out.println(r2.get()); // "value1"
 *   }
 *
 *   // For multi-node commands, use non-pipelined mode
 *   Set allKeys = cluster.keys("*"); // Executes on all master shards
 *   List values = cluster.mget("key1", "key2", "key3"); // Cross-slot keys
 * }
 * }
* * @see MultiNodePipelineBase * @see redis.clients.jedis.RedisClusterClient */ public class ClusterPipeline extends MultiNodePipelineBase { private final ClusterConnectionProvider provider; private AutoCloseable closeable = null; public ClusterPipeline(Set clusterNodes, JedisClientConfig clientConfig) { this(new ClusterConnectionProvider(clusterNodes, clientConfig), createClusterCommandObjects(clientConfig.getRedisProtocol())); this.closeable = this.provider; } public ClusterPipeline(Set clusterNodes, JedisClientConfig clientConfig, GenericObjectPoolConfig poolConfig) { this(new ClusterConnectionProvider(clusterNodes, clientConfig, poolConfig), createClusterCommandObjects(clientConfig.getRedisProtocol())); this.closeable = this.provider; } public ClusterPipeline(Set clusterNodes, JedisClientConfig clientConfig, GenericObjectPoolConfig poolConfig, Duration topologyRefreshPeriod) { this(new ClusterConnectionProvider(clusterNodes, clientConfig, poolConfig, topologyRefreshPeriod), createClusterCommandObjects(clientConfig.getRedisProtocol())); this.closeable = this.provider; } public ClusterPipeline(ClusterConnectionProvider provider) { this(provider, new ClusterCommandObjects()); } public ClusterPipeline(ClusterConnectionProvider provider, ClusterCommandObjects commandObjects) { super(commandObjects); this.provider = provider; } ClusterPipeline(ClusterConnectionProvider provider, ClusterCommandObjects commandObjects, CommandFlagsRegistry commandFlagsRegistry) { super(commandObjects, commandFlagsRegistry); this.provider = provider; } private static ClusterCommandObjects createClusterCommandObjects(RedisProtocol protocol) { ClusterCommandObjects cco = new ClusterCommandObjects(); if (protocol == RedisProtocol.RESP3) cco.setProtocol(protocol); return cco; } @Override public void close() { try { super.close(); } finally { IOUtils.closeQuietly(closeable); } } @Override protected HostAndPort getNodeKey(CommandArguments args) { Set slots = args.getKeyHashSlots(); if (slots.size() > 1) { throw new JedisClusterOperationException("Cannot get NodeKey for command with multiple hash slots"); } if (slots.isEmpty()) { return null; // Let getConnection(null) handle it by using a random node } return provider.getNode(slots.iterator().next()); } @Override protected Connection getConnection(HostAndPort nodeKey) { return provider.getConnection(nodeKey); } public Response spublish(String channel, String message) { return appendCommand(commandObjects.spublish(channel, message)); } public Response spublish(byte[] channel, byte[] message) { return appendCommand(commandObjects.spublish(channel, message)); } } ================================================ FILE: src/main/java/redis/clients/jedis/CommandArguments.java ================================================ package redis.clients.jedis; import java.util.*; import redis.clients.jedis.annots.Experimental; import redis.clients.jedis.annots.Internal; import redis.clients.jedis.args.Rawable; import redis.clients.jedis.args.RawableFactory; import redis.clients.jedis.commands.ProtocolCommand; import redis.clients.jedis.params.IParams; import redis.clients.jedis.search.RediSearchUtil; import redis.clients.jedis.util.JedisClusterCRC16; public class CommandArguments implements Iterable { /** * Default initial capacity for the keys list. Most Redis commands have 1-3 keys, * so a small initial capacity avoids reallocations for common cases. */ private static final int DEFAULT_KEYS_CAPACITY = 4; private CommandKeyArgumentPreProcessor keyPreProc = null; private final ArrayList args; /** * Pre-allocated list for storing keys. Using ArrayList directly avoids the * memory reallocation overhead of transitioning from emptyList -> singletonList -> ArrayList. */ private final ArrayList keys; /** * Cached hash slots computed from keys. Null indicates the cache is invalid * and needs to be recomputed. The cache is invalidated when keys are added. */ private Set cachedHashSlots; private boolean blocking; private CommandArguments() { throw new InstantiationError(); } public CommandArguments(ProtocolCommand command) { args = new ArrayList<>(); args.add(command); keys = new ArrayList<>(DEFAULT_KEYS_CAPACITY); cachedHashSlots = null; } public ProtocolCommand getCommand() { return (ProtocolCommand) args.get(0); } @Experimental void setKeyArgumentPreProcessor(CommandKeyArgumentPreProcessor keyPreProcessor) { this.keyPreProc = keyPreProcessor; } public CommandArguments add(Rawable arg) { args.add(arg); return this; } public CommandArguments add(byte[] arg) { return add(RawableFactory.from(arg)); } public CommandArguments add(boolean arg) { return add(RawableFactory.from(arg)); } public CommandArguments add(int arg) { return add(RawableFactory.from(arg)); } public CommandArguments add(long arg) { return add(RawableFactory.from(arg)); } public CommandArguments add(double arg) { return add(RawableFactory.from(arg)); } public CommandArguments add(String arg) { return add(RawableFactory.from(arg)); } public CommandArguments add(Object arg) { if (arg == null) { throw new IllegalArgumentException("null is not a valid argument."); } else if (arg instanceof Rawable) { args.add((Rawable) arg); } else if (arg instanceof byte[]) { args.add(RawableFactory.from((byte[]) arg)); } else if (arg instanceof Boolean) { args.add(RawableFactory.from((Boolean) arg)); } else if (arg instanceof Integer) { args.add(RawableFactory.from((Integer) arg)); } else if (arg instanceof Long) { args.add(RawableFactory.from((Long) arg)); } else if (arg instanceof Double) { args.add(RawableFactory.from((Double) arg)); } else if (arg instanceof float[]) { args.add(RawableFactory.from(RediSearchUtil.toByteArray((float[]) arg))); } else if (arg instanceof String) { args.add(RawableFactory.from((String) arg)); } else if (arg instanceof GeoCoordinate) { GeoCoordinate geo = (GeoCoordinate) arg; args.add(RawableFactory.from(geo.getLongitude() + "," + geo.getLatitude())); } else { args.add(RawableFactory.from(String.valueOf(arg))); } return this; } public CommandArguments addObjects(Object... args) { for (Object arg : args) { add(arg); } return this; } public CommandArguments addObjects(Collection args) { args.forEach(arg -> add(arg)); return this; } public CommandArguments key(Object key) { if (keyPreProc != null) { key = keyPreProc.actualKey(key); } if (key instanceof Rawable) { Rawable raw = (Rawable) key; args.add(raw); // Extract raw bytes for hash slot computation to avoid ClassCastException in getKeyHashSlots() addHashSlotKey(raw.getRaw()); } else if (key instanceof byte[]) { byte[] raw = (byte[]) key; args.add(RawableFactory.from(raw)); addHashSlotKey(raw); } else if (key instanceof String) { String raw = (String) key; args.add(RawableFactory.from(raw)); addHashSlotKey(raw); } else { throw new IllegalArgumentException("\"" + key.toString() + "\" is not a valid argument."); } return this; } final CommandArguments addHashSlotKey(String key) { keys.add(key); // Invalidate cached hash slots since keys have changed cachedHashSlots = null; return this; } final CommandArguments addHashSlotKey(byte[] key) { keys.add(key); // Invalidate cached hash slots since keys have changed cachedHashSlots = null; return this; } public final CommandArguments keys(Object... keys) { Arrays.stream(keys).forEach(this::key); return this; } public final CommandArguments keys(Collection keys) { keys.forEach(this::key); return this; } public final CommandArguments addParams(IParams params) { params.addParams(this); return this; } protected final CommandArguments addHashSlotKeys(byte[]... keys) { for (byte[] key : keys) { addHashSlotKey(key); } return this; } protected final CommandArguments addHashSlotKeys(String... keys) { for (String key : keys) { addHashSlotKey(key); } return this; } public int size() { return args.size(); } /** * Get the argument at the specified index. * @param index the index of the argument to retrieve (0-based, where 0 is the command itself) * @return the Rawable argument at the specified index * @throws IndexOutOfBoundsException if the index is out of range */ public Rawable get(int index) { return args.get(index); } @Override public Iterator iterator() { return args.iterator(); } /** * Returns the keys used in this command. *

* Internal API: This method is internal and should not be used by external code. * It is exposed for internal use by caching ({@link redis.clients.jedis.csc.CacheKey#getRedisKeys()}) * and cluster operations. *

* Supported types: Keys are stored as either {@link String} or {@code byte[]} depending on * how they were added via {@link #key(Object)} or {@link #addHashSlotKey(String)}/{@link #addHashSlotKey(byte[])}. * Only {@link String} and {@code byte[]} are guaranteed to be supported by downstream consumers. *

* Type safety: Consumers must handle both {@link String} and {@code byte[]} types. * Passing other types may cause {@link IllegalArgumentException} when used with caching * (see {@link redis.clients.jedis.csc.AbstractCache#makeKeyForRedisKeysToCacheKeys(Object)}) * or cluster operations. *

* The returned list is unmodifiable to prevent external modification of the internal key tracking. * * @return unmodifiable list of keys ({@link String} or {@code byte[]}) */ @Internal public List getKeys() { return Collections.unmodifiableList(keys); } @Internal public Set getKeyHashSlots() { // Return cached slots if available (cache is invalidated when keys are added) if (cachedHashSlots != null) { return cachedHashSlots; } // Compute hash slots and cache the result Set slots = new HashSet<>(); for (Object key : keys) { if (key instanceof byte[]) { slots.add(JedisClusterCRC16.getSlot((byte[]) key)); } else { slots.add(JedisClusterCRC16.getSlot((String) key)); } } // Cache as unmodifiable set to prevent external modification cachedHashSlots = Collections.unmodifiableSet(slots); return cachedHashSlots; } /** * @return true if this command has no keys, false otherwise */ public boolean isKeyless() { return keys.isEmpty(); } public boolean isBlocking() { return blocking; } public CommandArguments blocking() { this.blocking = true; return this; } } ================================================ FILE: src/main/java/redis/clients/jedis/CommandFlagsRegistry.java ================================================ package redis.clients.jedis; import java.util.EnumSet; /** * Registry interface for command flags. Provides a mapping from Redis commands to their flags. This * interface allows for different implementations of the flags registry. */ public interface CommandFlagsRegistry { /** * Command flags based on command flags exposed by Redis. See * Command flags for more * details. *

* Flags description: *

    *
  • READONLY: Command doesn't modify data
  • *
  • WRITE: Command may modify data
  • *
  • DENYOOM: Command may increase memory usage (deny if out of memory)
  • *
  • ADMIN: Administrative command
  • *
  • PUBSUB: Pub/Sub related command
  • *
  • NOSCRIPT: Command not allowed in scripts
  • *
  • SORT_FOR_SCRIPT: Command output needs sorting for scripts
  • *
  • LOADING: Command allowed while database is loading
  • *
  • STALE: Command allowed on stale replicas
  • *
  • SKIP_MONITOR: Command not shown in MONITOR output
  • *
  • ASKING: Command allowed in cluster ASKING state
  • *
  • FAST: Command has O(1) time complexity
  • *
  • MOVABLEKEYS: Command key positions may vary
  • *
  • MODULE: Module command
  • *
  • BLOCKING: Command may block the client
  • *
  • NO_AUTH: Command allowed without authentication
  • *
  • NO_ASYNC_LOADING: Command not allowed during async loading
  • *
  • NO_MULTI: Command not allowed in MULTI/EXEC
  • *
  • NO_MANDATORY_KEYS: Command may work without keys
  • *
  • ALLOW_BUSY: Command allowed when server is busy
  • *
*/ enum CommandFlag { READONLY, WRITE, DENYOOM, ADMIN, PUBSUB, NOSCRIPT, SORT_FOR_SCRIPT, LOADING, STALE, SKIP_MONITOR, SKIP_SLOWLOG, ASKING, FAST, MOVABLEKEYS, MODULE, BLOCKING, NO_AUTH, NO_ASYNC_LOADING, NO_MULTI, NO_MANDATORY_KEYS, ALLOW_BUSY } /** * Request policy for commands in a clustered deployment. This tip helps clients determine which * shards to send the command to in clustering mode. See * Request * policy for more details. *

* Policy values: *

    *
  • DEFAULT: No specific request policy defined. For commands without key arguments, execute on * an arbitrary shard. For commands with key arguments, route to a single shard based on the hash * slot of input keys.
  • *
  • ALL_NODES: Execute the command on all nodes - masters and replicas alike. Example: CONFIG * SET. Used by commands that don't accept key name arguments and operate atomically per * shard.
  • *
  • ALL_SHARDS: Execute the command on all master shards. Example: DBSIZE. Used by commands * that don't accept key name arguments and operate atomically per shard.
  • *
  • MULTI_SHARD: Execute the command on several shards. The client should split the inputs * according to the hash slots of input key name arguments. Example: DEL, MSET, MGET.
  • *
  • SPECIAL: Indicates a non-trivial form of request policy. Example: SCAN.
  • *
*/ enum RequestPolicy { DEFAULT, ALL_NODES, ALL_SHARDS, MULTI_SHARD, SPECIAL } /** * Response policy for commands in a clustered deployment. This tip helps clients determine how to * aggregate replies from multiple shards in a cluster. See * * Response policy for more details. *

* Policy values: *

    *
  • DEFAULT: No specific response policy defined. For commands without key arguments, aggregate * all replies within a single nested data structure. For commands with key arguments, retain the * same order of replies as the input key names.
  • *
  • ONE_SUCCEEDED: Return success if at least one shard didn't reply with an error. Reply with * the first non-error reply obtained. Example: SCRIPT KILL.
  • *
  • ALL_SUCCEEDED: Return successfully only if there are no error replies. A single error reply * should disqualify the aggregate. Example: CONFIG SET, SCRIPT FLUSH.
  • *
  • AGG_LOGICAL_AND: Return the result of a logical AND operation on all replies. Only applies * to integer replies (0 or 1). Example: SCRIPT EXISTS.
  • *
  • AGG_LOGICAL_OR: Return the result of a logical OR operation on all replies. Only applies to * integer replies (0 or 1).
  • *
  • AGG_MIN: Return the minimal value from the replies. Only applies to numerical replies. * Example: WAIT.
  • *
  • AGG_MAX: Return the maximal value from the replies. Only applies to numerical replies.
  • *
  • AGG_SUM: Return the sum of replies. Only applies to numerical replies. Example: * DBSIZE.
  • *
  • SPECIAL: Indicates a non-trivial form of reply policy. Example: INFO.
  • *
*/ enum ResponsePolicy { DEFAULT, ONE_SUCCEEDED, ALL_SUCCEEDED, AGG_LOGICAL_AND, AGG_LOGICAL_OR, AGG_MIN, AGG_MAX, AGG_SUM, SPECIAL } /** * Get the flags for a given command. * @param commandArguments the command arguments containing the command and its parameters * @return EnumSet of CommandFlag for this command, or empty set if command has no flags */ EnumSet getFlags(CommandArguments commandArguments); /** * Get the request policy for a given command. The request policy helps clients determine which * shards to send the command to in a clustered deployment. * @param commandArguments the command arguments containing the command and its parameters * @return RequestPolicy for this command, or DEFAULT if no specific policy is defined */ RequestPolicy getRequestPolicy(CommandArguments commandArguments); /** * Get the response policy for a given command. The response policy helps clients determine how to * aggregate replies from multiple shards in a cluster. * @param commandArguments the command arguments containing the command and its parameters * @return ResponsePolicy for this command, or DEFAULT if no specific policy is defined */ ResponsePolicy getResponsePolicy(CommandArguments commandArguments); } ================================================ FILE: src/main/java/redis/clients/jedis/CommandKeyArgumentPreProcessor.java ================================================ package redis.clients.jedis; import redis.clients.jedis.annots.Experimental; @Experimental public interface CommandKeyArgumentPreProcessor { /** * @param paramKey key name in application * @return key name in Redis server */ Object actualKey(Object paramKey); } ================================================ FILE: src/main/java/redis/clients/jedis/CommandObject.java ================================================ package redis.clients.jedis; import java.util.Iterator; import redis.clients.jedis.args.Rawable; public class CommandObject { private final CommandArguments arguments; private final Builder builder; public CommandObject(CommandArguments args, Builder builder) { this.arguments = args; this.builder = builder; } public CommandArguments getArguments() { return arguments; } public Builder getBuilder() { return builder; } @Override public int hashCode() { int hashCode = 1; for (Rawable e : arguments) { hashCode = 31 * hashCode + e.hashCode(); } hashCode = 31 * hashCode + builder.hashCode(); return hashCode; } @Override public boolean equals(Object o) { if (o == this) { return true; } if (!(o instanceof CommandObject)) { return false; } Iterator e1 = arguments.iterator(); Iterator e2 = ((CommandObject) o).arguments.iterator(); while (e1.hasNext() && e2.hasNext()) { Rawable o1 = e1.next(); Rawable o2 = e2.next(); if (!(o1 == null ? o2 == null : o1.equals(o2))) { return false; } } if (e1.hasNext() || e2.hasNext()) { return false; } return builder == ((CommandObject) o).builder; } } ================================================ FILE: src/main/java/redis/clients/jedis/CommandObjects.java ================================================ package redis.clients.jedis; import static redis.clients.jedis.Protocol.Command.*; import static redis.clients.jedis.Protocol.Keyword.*; import java.util.*; import java.util.concurrent.atomic.AtomicInteger; import java.util.concurrent.locks.Lock; import java.util.concurrent.locks.ReentrantLock; import java.util.function.Supplier; import java.util.stream.Collectors; import org.json.JSONArray; import org.json.JSONObject; import redis.clients.jedis.Protocol.Command; import redis.clients.jedis.Protocol.Keyword; import redis.clients.jedis.annots.Experimental; import redis.clients.jedis.args.*; import redis.clients.jedis.bloom.*; import redis.clients.jedis.bloom.RedisBloomProtocol.*; import redis.clients.jedis.commands.ProtocolCommand; import redis.clients.jedis.json.*; import redis.clients.jedis.json.JsonProtocol.JsonCommand; import redis.clients.jedis.params.*; import redis.clients.jedis.resps.*; import redis.clients.jedis.search.*; import redis.clients.jedis.search.SearchProtocol.*; import redis.clients.jedis.search.SearchResult.SearchResultBuilder; import redis.clients.jedis.search.aggr.AggregationBuilder; import redis.clients.jedis.search.aggr.AggregationResult; import redis.clients.jedis.search.hybrid.FTHybridParams; import redis.clients.jedis.search.hybrid.HybridResult; import redis.clients.jedis.search.schemafields.SchemaField; import redis.clients.jedis.timeseries.*; import redis.clients.jedis.timeseries.TimeSeriesProtocol.*; import redis.clients.jedis.util.KeyValue; import redis.clients.jedis.util.CompareCondition; public class CommandObjects { private RedisProtocol protocol; // TODO: restrict? public final void setProtocol(RedisProtocol proto) { this.protocol = proto; } // TODO: remove? protected RedisProtocol getProtocol() { return protocol; } protected volatile CommandKeyArgumentPreProcessor keyPreProcessor = null; private Lock mapperLock = new ReentrantLock(true); private volatile JsonObjectMapper jsonObjectMapper; private final AtomicInteger searchDialect = new AtomicInteger(SearchProtocol.DEFAULT_DIALECT); @Experimental public void setKeyArgumentPreProcessor(CommandKeyArgumentPreProcessor keyPreProcessor) { this.keyPreProcessor = keyPreProcessor; } protected CommandArguments commandArguments(ProtocolCommand command) { CommandArguments comArgs = new CommandArguments(command); if (keyPreProcessor != null) comArgs.setKeyArgumentPreProcessor(keyPreProcessor); return comArgs; } private final CommandObject PING_COMMAND_OBJECT = new CommandObject<>(commandArguments(PING), BuilderFactory.STRING); public final CommandObject ping() { return PING_COMMAND_OBJECT; } public final CommandObject echo(String msg) { return new CommandObject<>(commandArguments(ECHO).add(msg), BuilderFactory.STRING); } private final CommandObject FLUSHALL_COMMAND_OBJECT = new CommandObject<>(commandArguments(FLUSHALL), BuilderFactory.STRING); public final CommandObject flushAll() { return FLUSHALL_COMMAND_OBJECT; } private final CommandObject FLUSHDB_COMMAND_OBJECT = new CommandObject<>(commandArguments(FLUSHDB), BuilderFactory.STRING); public final CommandObject flushDB() { return FLUSHDB_COMMAND_OBJECT; } public final CommandObject configSet(String parameter, String value) { return new CommandObject<>(commandArguments(Command.CONFIG).add(Keyword.SET).add(parameter).add(value), BuilderFactory.STRING); } private final CommandObject INFO_COMMAND_OBJECT = new CommandObject<>(commandArguments(Command.INFO), BuilderFactory.STRING); public final CommandObject info() { return INFO_COMMAND_OBJECT; } public final CommandObject info(String section) { return new CommandObject<>(commandArguments(Command.INFO).add(section), BuilderFactory.STRING); } // Key commands public final CommandObject exists(String key) { return new CommandObject<>(commandArguments(Command.EXISTS).key(key), BuilderFactory.BOOLEAN); } public final CommandObject exists(String... keys) { return new CommandObject<>(commandArguments(Command.EXISTS).keys((Object[]) keys), BuilderFactory.LONG); } public final CommandObject exists(byte[] key) { return new CommandObject<>(commandArguments(Command.EXISTS).key(key), BuilderFactory.BOOLEAN); } public final CommandObject exists(byte[]... keys) { return new CommandObject<>(commandArguments(Command.EXISTS).keys((Object[]) keys), BuilderFactory.LONG); } public final CommandObject persist(String key) { return new CommandObject<>(commandArguments(Command.PERSIST).key(key), BuilderFactory.LONG); } public final CommandObject persist(byte[] key) { return new CommandObject<>(commandArguments(Command.PERSIST).key(key), BuilderFactory.LONG); } public final CommandObject type(String key) { return new CommandObject<>(commandArguments(Command.TYPE).key(key), BuilderFactory.STRING); } public final CommandObject type(byte[] key) { return new CommandObject<>(commandArguments(Command.TYPE).key(key), BuilderFactory.STRING); } public final CommandObject dump(String key) { return new CommandObject<>(commandArguments(Command.DUMP).key(key), BuilderFactory.BINARY); } public final CommandObject dump(byte[] key) { return new CommandObject<>(commandArguments(Command.DUMP).key(key), BuilderFactory.BINARY); } public final CommandObject restore(String key, long ttl, byte[] serializedValue) { return new CommandObject<>(commandArguments(RESTORE).key(key).add(ttl) .add(serializedValue), BuilderFactory.STRING); } public final CommandObject restore(String key, long ttl, byte[] serializedValue, RestoreParams params) { return new CommandObject<>(commandArguments(RESTORE).key(key).add(ttl) .add(serializedValue).addParams(params), BuilderFactory.STRING); } public final CommandObject restore(byte[] key, long ttl, byte[] serializedValue) { return new CommandObject<>(commandArguments(RESTORE).key(key).add(ttl) .add(serializedValue), BuilderFactory.STRING); } public final CommandObject restore(byte[] key, long ttl, byte[] serializedValue, RestoreParams params) { return new CommandObject<>(commandArguments(RESTORE).key(key).add(ttl) .add(serializedValue).addParams(params), BuilderFactory.STRING); } public final CommandObject expire(String key, long seconds) { return new CommandObject<>(commandArguments(EXPIRE).key(key).add(seconds), BuilderFactory.LONG); } public final CommandObject expire(byte[] key, long seconds) { return new CommandObject<>(commandArguments(EXPIRE).key(key).add(seconds), BuilderFactory.LONG); } public final CommandObject expire(String key, long seconds, ExpiryOption expiryOption) { return new CommandObject<>(commandArguments(EXPIRE).key(key).add(seconds).add(expiryOption), BuilderFactory.LONG); } public final CommandObject expire(byte[] key, long seconds, ExpiryOption expiryOption) { return new CommandObject<>(commandArguments(EXPIRE).key(key).add(seconds).add(expiryOption), BuilderFactory.LONG); } public final CommandObject pexpire(String key, long milliseconds) { return new CommandObject<>(commandArguments(PEXPIRE).key(key).add(milliseconds), BuilderFactory.LONG); } public final CommandObject pexpire(byte[] key, long milliseconds) { return new CommandObject<>(commandArguments(PEXPIRE).key(key).add(milliseconds), BuilderFactory.LONG); } public final CommandObject pexpire(String key, long milliseconds, ExpiryOption expiryOption) { return new CommandObject<>(commandArguments(PEXPIRE).key(key).add(milliseconds).add(expiryOption), BuilderFactory.LONG); } public final CommandObject pexpire(byte[] key, long milliseconds, ExpiryOption expiryOption) { return new CommandObject<>(commandArguments(PEXPIRE).key(key).add(milliseconds).add(expiryOption), BuilderFactory.LONG); } public final CommandObject expireTime(String key) { return new CommandObject<>(commandArguments(EXPIRETIME).key(key), BuilderFactory.LONG); } public final CommandObject expireTime(byte[] key) { return new CommandObject<>(commandArguments(EXPIRETIME).key(key), BuilderFactory.LONG); } public final CommandObject pexpireTime(String key) { return new CommandObject<>(commandArguments(PEXPIRETIME).key(key), BuilderFactory.LONG); } public final CommandObject pexpireTime(byte[] key) { return new CommandObject<>(commandArguments(PEXPIRETIME).key(key), BuilderFactory.LONG); } public final CommandObject expireAt(String key, long unixTime) { return new CommandObject<>(commandArguments(EXPIREAT).key(key).add(unixTime), BuilderFactory.LONG); } public final CommandObject expireAt(byte[] key, long unixTime) { return new CommandObject<>(commandArguments(EXPIREAT).key(key).add(unixTime), BuilderFactory.LONG); } public final CommandObject expireAt(String key, long unixTime, ExpiryOption expiryOption) { return new CommandObject<>(commandArguments(EXPIREAT).key(key).add(unixTime).add(expiryOption), BuilderFactory.LONG); } public final CommandObject expireAt(byte[] key, long unixTime, ExpiryOption expiryOption) { return new CommandObject<>(commandArguments(EXPIREAT).key(key).add(unixTime).add(expiryOption), BuilderFactory.LONG); } public final CommandObject pexpireAt(String key, long millisecondsTimestamp) { return new CommandObject<>(commandArguments(PEXPIREAT).key(key).add(millisecondsTimestamp), BuilderFactory.LONG); } public final CommandObject pexpireAt(byte[] key, long millisecondsTimestamp) { return new CommandObject<>(commandArguments(PEXPIREAT).key(key).add(millisecondsTimestamp), BuilderFactory.LONG); } public final CommandObject pexpireAt(String key, long millisecondsTimestamp, ExpiryOption expiryOption) { return new CommandObject<>(commandArguments(PEXPIREAT).key(key).add(millisecondsTimestamp).add(expiryOption), BuilderFactory.LONG); } public final CommandObject pexpireAt(byte[] key, long millisecondsTimestamp, ExpiryOption expiryOption) { return new CommandObject<>(commandArguments(PEXPIREAT).key(key).add(millisecondsTimestamp).add(expiryOption), BuilderFactory.LONG); } public final CommandObject ttl(String key) { return new CommandObject<>(commandArguments(TTL).key(key), BuilderFactory.LONG); } public final CommandObject ttl(byte[] key) { return new CommandObject<>(commandArguments(TTL).key(key), BuilderFactory.LONG); } public final CommandObject pttl(String key) { return new CommandObject<>(commandArguments(PTTL).key(key), BuilderFactory.LONG); } public final CommandObject pttl(byte[] key) { return new CommandObject<>(commandArguments(PTTL).key(key), BuilderFactory.LONG); } public final CommandObject touch(String key) { return new CommandObject<>(commandArguments(TOUCH).key(key), BuilderFactory.LONG); } public final CommandObject touch(String... keys) { return new CommandObject<>(commandArguments(TOUCH).keys((Object[]) keys), BuilderFactory.LONG); } public final CommandObject touch(byte[] key) { return new CommandObject<>(commandArguments(TOUCH).key(key), BuilderFactory.LONG); } public final CommandObject touch(byte[]... keys) { return new CommandObject<>(commandArguments(TOUCH).keys((Object[]) keys), BuilderFactory.LONG); } public final CommandObject> sort(String key) { return new CommandObject<>(commandArguments(SORT).key(key), BuilderFactory.STRING_LIST); } public final CommandObject> sort(String key, SortingParams sortingParams) { return new CommandObject<>(commandArguments(SORT).key(key).addParams(sortingParams), BuilderFactory.STRING_LIST); } public final CommandObject> sort(byte[] key) { return new CommandObject<>(commandArguments(SORT).key(key), BuilderFactory.BINARY_LIST); } public final CommandObject> sort(byte[] key, SortingParams sortingParams) { return new CommandObject<>(commandArguments(SORT).key(key).addParams(sortingParams), BuilderFactory.BINARY_LIST); } public final CommandObject sort(String key, String dstkey) { return new CommandObject<>(commandArguments(SORT).key(key) .add(STORE).key(dstkey), BuilderFactory.LONG); } public final CommandObject sort(String key, SortingParams sortingParams, String dstkey) { return new CommandObject<>(commandArguments(SORT).key(key).addParams(sortingParams) .add(STORE).key(dstkey), BuilderFactory.LONG); } public final CommandObject sort(byte[] key, byte[] dstkey) { return new CommandObject<>(commandArguments(SORT).key(key) .add(STORE).key(dstkey), BuilderFactory.LONG); } public final CommandObject sort(byte[] key, SortingParams sortingParams, byte[] dstkey) { return new CommandObject<>(commandArguments(SORT).key(key).addParams(sortingParams) .add(STORE).key(dstkey), BuilderFactory.LONG); } public final CommandObject> sortReadonly(byte[] key, SortingParams sortingParams) { return new CommandObject<>(commandArguments(SORT_RO).key(key).addParams(sortingParams), BuilderFactory.BINARY_LIST); } public final CommandObject> sortReadonly(String key, SortingParams sortingParams) { return new CommandObject<>(commandArguments(SORT_RO).key(key).addParams(sortingParams), BuilderFactory.STRING_LIST); } public final CommandObject del(String key) { return new CommandObject<>(commandArguments(DEL).key(key), BuilderFactory.LONG); } public final CommandObject del(String... keys) { return new CommandObject<>(commandArguments(DEL).keys((Object[]) keys), BuilderFactory.LONG); } public final CommandObject del(byte[] key) { return new CommandObject<>(commandArguments(DEL).key(key), BuilderFactory.LONG); } public final CommandObject del(byte[]... keys) { return new CommandObject<>(commandArguments(DEL).keys((Object[]) keys), BuilderFactory.LONG); } public final CommandObject delex(String key, CompareCondition cond) { CommandArguments ca = commandArguments(Command.DELEX).key(key); cond.addTo(ca); return new CommandObject<>(ca, BuilderFactory.LONG); } public final CommandObject delex(byte[] key, CompareCondition cond) { CommandArguments ca = commandArguments(Command.DELEX).key(key); cond.addTo(ca); return new CommandObject<>(ca, BuilderFactory.LONG); } public final CommandObject unlink(String key) { return new CommandObject<>(commandArguments(UNLINK).key(key), BuilderFactory.LONG); } public final CommandObject unlink(String... keys) { return new CommandObject<>(commandArguments(UNLINK).keys((Object[]) keys), BuilderFactory.LONG); } public final CommandObject unlink(byte[] key) { return new CommandObject<>(commandArguments(UNLINK).key(key), BuilderFactory.LONG); } public final CommandObject unlink(byte[]... keys) { return new CommandObject<>(commandArguments(UNLINK).keys((Object[]) keys), BuilderFactory.LONG); } public final CommandObject copy(String srcKey, String dstKey, boolean replace) { CommandArguments args = commandArguments(Command.COPY).key(srcKey).key(dstKey); if (replace) { args.add(REPLACE); } return new CommandObject<>(args, BuilderFactory.BOOLEAN); } public final CommandObject copy(byte[] srcKey, byte[] dstKey, boolean replace) { CommandArguments args = commandArguments(Command.COPY).key(srcKey).key(dstKey); if (replace) { args.add(REPLACE); } return new CommandObject<>(args, BuilderFactory.BOOLEAN); } public final CommandObject rename(String oldkey, String newkey) { return new CommandObject<>(commandArguments(RENAME).key(oldkey).key(newkey), BuilderFactory.STRING); } public final CommandObject renamenx(String oldkey, String newkey) { return new CommandObject<>(commandArguments(RENAMENX).key(oldkey).key(newkey), BuilderFactory.LONG); } public final CommandObject rename(byte[] oldkey, byte[] newkey) { return new CommandObject<>(commandArguments(RENAME).key(oldkey).key(newkey), BuilderFactory.STRING); } public final CommandObject renamenx(byte[] oldkey, byte[] newkey) { return new CommandObject<>(commandArguments(RENAMENX).key(oldkey).key(newkey), BuilderFactory.LONG); } public CommandObject dbSize() { return new CommandObject<>(commandArguments(DBSIZE), BuilderFactory.LONG); } public CommandObject> keys(String pattern) { CommandArguments args = commandArguments(Command.KEYS).add(pattern); return new CommandObject<>(args, BuilderFactory.STRING_SET); } public CommandObject> keys(byte[] pattern) { CommandArguments args = commandArguments(Command.KEYS).add(pattern); return new CommandObject<>(args, BuilderFactory.BINARY_SET); } public CommandObject> scan(String cursor) { return new CommandObject<>(commandArguments(SCAN).add(cursor), BuilderFactory.SCAN_RESPONSE); } public CommandObject> scan(String cursor, ScanParams params) { return new CommandObject<>(commandArguments(SCAN).add(cursor).addParams(params), BuilderFactory.SCAN_RESPONSE); } public CommandObject> scan(String cursor, ScanParams params, String type) { return new CommandObject<>(commandArguments(SCAN).add(cursor).addParams(params).add(Keyword.TYPE).add(type), BuilderFactory.SCAN_RESPONSE); } public CommandObject> scan(byte[] cursor) { return new CommandObject<>(commandArguments(SCAN).add(cursor), BuilderFactory.SCAN_BINARY_RESPONSE); } public CommandObject> scan(byte[] cursor, ScanParams params) { return new CommandObject<>(commandArguments(SCAN).add(cursor).addParams(params), BuilderFactory.SCAN_BINARY_RESPONSE); } public CommandObject> scan(byte[] cursor, ScanParams params, byte[] type) { return new CommandObject<>(commandArguments(SCAN).add(cursor).addParams(params).add(Keyword.TYPE).add(type), BuilderFactory.SCAN_BINARY_RESPONSE); } public final CommandObject randomKey() { return new CommandObject<>(commandArguments(RANDOMKEY), BuilderFactory.STRING); } public final CommandObject randomBinaryKey() { return new CommandObject<>(commandArguments(RANDOMKEY), BuilderFactory.BINARY); } // Key commands // String commands public final CommandObject set(String key, String value) { return new CommandObject<>(commandArguments(Command.SET).key(key).add(value), BuilderFactory.STRING); } public final CommandObject set(String key, String value, SetParams params) { return new CommandObject<>(commandArguments(Command.SET).key(key).add(value).addParams(params), BuilderFactory.STRING); } public final CommandObject set(byte[] key, byte[] value) { return new CommandObject<>(commandArguments(Command.SET).key(key).add(value), BuilderFactory.STRING); } public final CommandObject set(byte[] key, byte[] value, SetParams params) { return new CommandObject<>(commandArguments(Command.SET).key(key).add(value).addParams(params), BuilderFactory.STRING); } public final CommandObject get(String key) { return new CommandObject<>(commandArguments(Command.GET).key(key), BuilderFactory.STRING); } public final CommandObject digestKey(String key) { return new CommandObject<>(commandArguments(Command.DIGEST).key(key), BuilderFactory.STRING); } public final CommandObject setGet(String key, String value) { return new CommandObject<>(commandArguments(Command.SET).key(key).add(value).add(Keyword.GET), BuilderFactory.STRING); } public final CommandObject setGet(String key, String value, SetParams params) { return new CommandObject<>(commandArguments(Command.SET).key(key).add(value).addParams(params).add(Keyword.GET), BuilderFactory.STRING); } public final CommandObject getDel(String key) { return new CommandObject<>(commandArguments(Command.GETDEL).key(key), BuilderFactory.STRING); } public final CommandObject getEx(String key, GetExParams params) { return new CommandObject<>(commandArguments(Command.GETEX).key(key).addParams(params), BuilderFactory.STRING); } public final CommandObject digestKey(byte[] key) { return new CommandObject<>(commandArguments(Command.DIGEST).key(key), BuilderFactory.BINARY); } public final CommandObject get(byte[] key) { return new CommandObject<>(commandArguments(Command.GET).key(key), BuilderFactory.BINARY); } public final CommandObject setGet(byte[] key, byte[] value) { return new CommandObject<>(commandArguments(Command.SET).key(key).add(value).add(Keyword.GET), BuilderFactory.BINARY); } public final CommandObject setGet(byte[] key, byte[] value, SetParams params) { return new CommandObject<>(commandArguments(Command.SET).key(key).add(value).addParams(params).add(Keyword.GET), BuilderFactory.BINARY); } public final CommandObject getDel(byte[] key) { return new CommandObject<>(commandArguments(Command.GETDEL).key(key), BuilderFactory.BINARY); } public final CommandObject getEx(byte[] key, GetExParams params) { return new CommandObject<>(commandArguments(Command.GETEX).key(key).addParams(params), BuilderFactory.BINARY); } /** * @deprecated Use {@link CommandObjects#setGet(java.lang.String, java.lang.String)}. */ @Deprecated public final CommandObject getSet(String key, String value) { return new CommandObject<>(commandArguments(Command.GETSET).key(key).add(value), BuilderFactory.STRING); } /** * @deprecated Use {@link CommandObjects#setGet(byte[], byte[])}. */ @Deprecated public final CommandObject getSet(byte[] key, byte[] value) { return new CommandObject<>(commandArguments(Command.GETSET).key(key).add(value), BuilderFactory.BINARY); } public final CommandObject setnx(String key, String value) { return new CommandObject<>(commandArguments(SETNX).key(key).add(value), BuilderFactory.LONG); } public final CommandObject setex(String key, long seconds, String value) { return new CommandObject<>(commandArguments(SETEX).key(key).add(seconds).add(value), BuilderFactory.STRING); } public final CommandObject psetex(String key, long milliseconds, String value) { return new CommandObject<>(commandArguments(PSETEX).key(key).add(milliseconds).add(value), BuilderFactory.STRING); } public final CommandObject setnx(byte[] key, byte[] value) { return new CommandObject<>(commandArguments(SETNX).key(key).add(value), BuilderFactory.LONG); } public final CommandObject setex(byte[] key, long seconds, byte[] value) { return new CommandObject<>(commandArguments(SETEX).key(key).add(seconds).add(value), BuilderFactory.STRING); } public final CommandObject psetex(byte[] key, long milliseconds, byte[] value) { return new CommandObject<>(commandArguments(PSETEX).key(key).add(milliseconds).add(value), BuilderFactory.STRING); } public final CommandObject setbit(String key, long offset, boolean value) { return new CommandObject<>(commandArguments(SETBIT).key(key).add(offset).add(value), BuilderFactory.BOOLEAN); } public final CommandObject setbit(byte[] key, long offset, boolean value) { return new CommandObject<>(commandArguments(SETBIT).key(key).add(offset).add(value), BuilderFactory.BOOLEAN); } public final CommandObject getbit(String key, long offset) { return new CommandObject<>(commandArguments(GETBIT).key(key).add(offset), BuilderFactory.BOOLEAN); } public final CommandObject getbit(byte[] key, long offset) { return new CommandObject<>(commandArguments(GETBIT).key(key).add(offset), BuilderFactory.BOOLEAN); } public final CommandObject setrange(String key, long offset, String value) { return new CommandObject<>(commandArguments(SETRANGE).key(key).add(offset).add(value), BuilderFactory.LONG); } public final CommandObject setrange(byte[] key, long offset, byte[] value) { return new CommandObject<>(commandArguments(SETRANGE).key(key).add(offset).add(value), BuilderFactory.LONG); } public final CommandObject getrange(String key, long startOffset, long endOffset) { return new CommandObject<>(commandArguments(GETRANGE).key(key).add(startOffset).add(endOffset), BuilderFactory.STRING); } public final CommandObject getrange(byte[] key, long startOffset, long endOffset) { return new CommandObject<>(commandArguments(GETRANGE).key(key).add(startOffset).add(endOffset), BuilderFactory.BINARY); } public final CommandObject> mget(String... keys) { return new CommandObject<>(commandArguments(MGET).keys((Object[]) keys), BuilderFactory.STRING_LIST); } public final CommandObject> mget(byte[]... keys) { return new CommandObject<>(commandArguments(MGET).keys((Object[]) keys), BuilderFactory.BINARY_LIST); } public final CommandObject mset(String... keysvalues) { return new CommandObject<>(addFlatKeyValueArgs(commandArguments(MSET), keysvalues), BuilderFactory.STRING); } public final CommandObject msetnx(String... keysvalues) { return new CommandObject<>(addFlatKeyValueArgs(commandArguments(MSETNX), keysvalues), BuilderFactory.LONG); } public final CommandObject msetex(MSetExParams params, String... keysvalues) { CommandArguments args = commandArguments(Command.MSETEX).add(keysvalues.length / 2); addFlatKeyValueArgs(args, keysvalues); args.addParams(params); return new CommandObject<>(args, BuilderFactory.BOOLEAN); } public final CommandObject msetex(MSetExParams params, byte[]... keysvalues) { CommandArguments args = commandArguments(Command.MSETEX).add(keysvalues.length / 2); addFlatKeyValueArgs(args, keysvalues); args.addParams(params); return new CommandObject<>(args, BuilderFactory.BOOLEAN); } public final CommandObject mset(byte[]... keysvalues) { return new CommandObject<>(addFlatKeyValueArgs(commandArguments(MSET), keysvalues), BuilderFactory.STRING); } public final CommandObject msetnx(byte[]... keysvalues) { return new CommandObject<>(addFlatKeyValueArgs(commandArguments(MSETNX), keysvalues), BuilderFactory.LONG); } public final CommandObject incr(String key) { return new CommandObject<>(commandArguments(Command.INCR).key(key), BuilderFactory.LONG); } public final CommandObject incrBy(String key, long increment) { return new CommandObject<>(commandArguments(INCRBY).key(key).add(increment), BuilderFactory.LONG); } public final CommandObject incrByFloat(String key, double increment) { return new CommandObject<>(commandArguments(INCRBYFLOAT).key(key).add(increment), BuilderFactory.DOUBLE); } public final CommandObject incr(byte[] key) { return new CommandObject<>(commandArguments(Command.INCR).key(key), BuilderFactory.LONG); } public final CommandObject incrBy(byte[] key, long increment) { return new CommandObject<>(commandArguments(INCRBY).key(key).add(increment), BuilderFactory.LONG); } public final CommandObject incrByFloat(byte[] key, double increment) { return new CommandObject<>(commandArguments(INCRBYFLOAT).key(key).add(increment), BuilderFactory.DOUBLE); } public final CommandObject decr(String key) { return new CommandObject<>(commandArguments(DECR).key(key), BuilderFactory.LONG); } public final CommandObject decrBy(String key, long decrement) { return new CommandObject<>(commandArguments(DECRBY).key(key).add(decrement), BuilderFactory.LONG); } public final CommandObject decr(byte[] key) { return new CommandObject<>(commandArguments(DECR).key(key), BuilderFactory.LONG); } public final CommandObject decrBy(byte[] key, long decrement) { return new CommandObject<>(commandArguments(DECRBY).key(key).add(decrement), BuilderFactory.LONG); } public final CommandObject append(String key, String value) { return new CommandObject<>(commandArguments(APPEND).key(key).add(value), BuilderFactory.LONG); } public final CommandObject append(byte[] key, byte[] value) { return new CommandObject<>(commandArguments(APPEND).key(key).add(value), BuilderFactory.LONG); } public final CommandObject substr(String key, int start, int end) { return new CommandObject<>(commandArguments(SUBSTR).key(key).add(start).add(end), BuilderFactory.STRING); } public final CommandObject substr(byte[] key, int start, int end) { return new CommandObject<>(commandArguments(SUBSTR).key(key).add(start).add(end), BuilderFactory.BINARY); } public final CommandObject strlen(String key) { return new CommandObject<>(commandArguments(STRLEN).key(key), BuilderFactory.LONG); } public final CommandObject strlen(byte[] key) { return new CommandObject<>(commandArguments(STRLEN).key(key), BuilderFactory.LONG); } public final CommandObject bitcount(String key) { return new CommandObject<>(commandArguments(BITCOUNT).key(key), BuilderFactory.LONG); } public final CommandObject bitcount(String key, long start, long end) { return new CommandObject<>(commandArguments(BITCOUNT).key(key).add(start).add(end), BuilderFactory.LONG); } public final CommandObject bitcount(String key, long start, long end, BitCountOption option) { return new CommandObject<>(commandArguments(BITCOUNT).key(key).add(start).add(end).add(option), BuilderFactory.LONG); } public final CommandObject bitcount(byte[] key) { return new CommandObject<>(commandArguments(BITCOUNT).key(key), BuilderFactory.LONG); } public final CommandObject bitcount(byte[] key, long start, long end) { return new CommandObject<>(commandArguments(BITCOUNT).key(key).add(start).add(end), BuilderFactory.LONG); } public final CommandObject bitcount(byte[] key, long start, long end, BitCountOption option) { return new CommandObject<>(commandArguments(BITCOUNT).key(key).add(start).add(end).add(option), BuilderFactory.LONG); } public final CommandObject bitpos(String key, boolean value) { return new CommandObject<>(commandArguments(BITPOS).key(key).add(value ? 1 : 0), BuilderFactory.LONG); } public final CommandObject bitpos(String key, boolean value, BitPosParams params) { return new CommandObject<>(commandArguments(BITPOS).key(key).add(value ? 1 : 0).addParams(params), BuilderFactory.LONG); } public final CommandObject bitpos(byte[] key, boolean value) { return new CommandObject<>(commandArguments(BITPOS).key(key).add(value ? 1 : 0), BuilderFactory.LONG); } public final CommandObject bitpos(byte[] key, boolean value, BitPosParams params) { return new CommandObject<>(commandArguments(BITPOS).key(key).add(value ? 1 : 0).addParams(params), BuilderFactory.LONG); } public final CommandObject> bitfield(String key, String... arguments) { return new CommandObject<>(commandArguments(BITFIELD).key(key).addObjects((Object[]) arguments), BuilderFactory.LONG_LIST); } public final CommandObject> bitfieldReadonly(String key, String... arguments) { return new CommandObject<>(commandArguments(BITFIELD_RO).key(key).addObjects((Object[]) arguments), BuilderFactory.LONG_LIST); } public final CommandObject> bitfield(byte[] key, byte[]... arguments) { return new CommandObject<>(commandArguments(BITFIELD).key(key).addObjects((Object[]) arguments), BuilderFactory.LONG_LIST); } public final CommandObject> bitfieldReadonly(byte[] key, byte[]... arguments) { return new CommandObject<>(commandArguments(BITFIELD_RO).key(key).addObjects((Object[]) arguments), BuilderFactory.LONG_LIST); } public final CommandObject bitop(BitOP op, String destKey, String... srcKeys) { return new CommandObject<>(commandArguments(BITOP).add(op).key(destKey).keys((Object[]) srcKeys), BuilderFactory.LONG); } public final CommandObject bitop(BitOP op, byte[] destKey, byte[]... srcKeys) { return new CommandObject<>(commandArguments(BITOP).add(op).key(destKey).keys((Object[]) srcKeys), BuilderFactory.LONG); } public final CommandObject lcs(String keyA, String keyB, LCSParams params) { return new CommandObject<>(commandArguments(Command.LCS).key(keyA).key(keyB) .addParams(params), BuilderFactory.STR_ALGO_LCS_RESULT_BUILDER); } public final CommandObject lcs(byte[] keyA, byte[] keyB, LCSParams params) { return new CommandObject<>(commandArguments(Command.LCS).key(keyA).key(keyB) .addParams(params), BuilderFactory.STR_ALGO_LCS_RESULT_BUILDER); } // String commands // List commands public final CommandObject rpush(String key, String... strings) { return new CommandObject<>(commandArguments(RPUSH).key(key).addObjects((Object[]) strings), BuilderFactory.LONG); } public final CommandObject rpush(byte[] key, byte[]... strings) { return new CommandObject<>(commandArguments(RPUSH).key(key).addObjects((Object[]) strings), BuilderFactory.LONG); } public final CommandObject lpush(String key, String... strings) { return new CommandObject<>(commandArguments(LPUSH).key(key).addObjects((Object[]) strings), BuilderFactory.LONG); } public final CommandObject lpush(byte[] key, byte[]... strings) { return new CommandObject<>(commandArguments(LPUSH).key(key).addObjects((Object[]) strings), BuilderFactory.LONG); } public final CommandObject llen(String key) { return new CommandObject<>(commandArguments(LLEN).key(key), BuilderFactory.LONG); } public final CommandObject llen(byte[] key) { return new CommandObject<>(commandArguments(LLEN).key(key), BuilderFactory.LONG); } public final CommandObject> lrange(String key, long start, long stop) { return new CommandObject<>(commandArguments(LRANGE).key(key).add(start).add(stop), BuilderFactory.STRING_LIST); } public final CommandObject> lrange(byte[] key, long start, long stop) { return new CommandObject<>(commandArguments(LRANGE).key(key).add(start).add(stop), BuilderFactory.BINARY_LIST); } public final CommandObject ltrim(String key, long start, long stop) { return new CommandObject<>(commandArguments(LTRIM).key(key).add(start).add(stop), BuilderFactory.STRING); } public final CommandObject ltrim(byte[] key, long start, long stop) { return new CommandObject<>(commandArguments(LTRIM).key(key).add(start).add(stop), BuilderFactory.STRING); } public final CommandObject lindex(String key, long index) { return new CommandObject<>(commandArguments(LINDEX).key(key).add(index), BuilderFactory.STRING); } public final CommandObject lindex(byte[] key, long index) { return new CommandObject<>(commandArguments(LINDEX).key(key).add(index), BuilderFactory.BINARY); } public final CommandObject lset(String key, long index, String value) { return new CommandObject<>(commandArguments(LSET).key(key).add(index).add(value), BuilderFactory.STRING); } public final CommandObject lset(byte[] key, long index, byte[] value) { return new CommandObject<>(commandArguments(LSET).key(key).add(index).add(value), BuilderFactory.STRING); } public final CommandObject lrem(String key, long count, String value) { return new CommandObject<>(commandArguments(LREM).key(key).add(count).add(value), BuilderFactory.LONG); } public final CommandObject lrem(byte[] key, long count, byte[] value) { return new CommandObject<>(commandArguments(LREM).key(key).add(count).add(value), BuilderFactory.LONG); } public final CommandObject lpop(String key) { return new CommandObject<>(commandArguments(LPOP).key(key), BuilderFactory.STRING); } public final CommandObject> lpop(String key, int count) { return new CommandObject<>(commandArguments(LPOP).key(key).add(count), BuilderFactory.STRING_LIST); } public final CommandObject lpop(byte[] key) { return new CommandObject<>(commandArguments(LPOP).key(key), BuilderFactory.BINARY); } public final CommandObject> lpop(byte[] key, int count) { return new CommandObject<>(commandArguments(LPOP).key(key).add(count), BuilderFactory.BINARY_LIST); } public final CommandObject rpop(String key) { return new CommandObject<>(commandArguments(RPOP).key(key), BuilderFactory.STRING); } public final CommandObject> rpop(String key, int count) { return new CommandObject<>(commandArguments(RPOP).key(key).add(count), BuilderFactory.STRING_LIST); } public final CommandObject rpop(byte[] key) { return new CommandObject<>(commandArguments(RPOP).key(key), BuilderFactory.BINARY); } public final CommandObject> rpop(byte[] key, int count) { return new CommandObject<>(commandArguments(RPOP).key(key).add(count), BuilderFactory.BINARY_LIST); } public final CommandObject lpos(String key, String element) { return new CommandObject<>(commandArguments(LPOS).key(key).add(element), BuilderFactory.LONG); } public final CommandObject lpos(String key, String element, LPosParams params) { return new CommandObject<>(commandArguments(LPOS).key(key).add(element).addParams(params), BuilderFactory.LONG); } public final CommandObject> lpos(String key, String element, LPosParams params, long count) { return new CommandObject<>(commandArguments(LPOS).key(key).add(element) .addParams(params).add(COUNT).add(count), BuilderFactory.LONG_LIST); } public final CommandObject lpos(byte[] key, byte[] element) { return new CommandObject<>(commandArguments(LPOS).key(key).add(element), BuilderFactory.LONG); } public final CommandObject lpos(byte[] key, byte[] element, LPosParams params) { return new CommandObject<>(commandArguments(LPOS).key(key).add(element).addParams(params), BuilderFactory.LONG); } public final CommandObject> lpos(byte[] key, byte[] element, LPosParams params, long count) { return new CommandObject<>(commandArguments(LPOS).key(key).add(element) .addParams(params).add(COUNT).add(count), BuilderFactory.LONG_LIST); } public final CommandObject linsert(String key, ListPosition where, String pivot, String value) { return new CommandObject<>(commandArguments(LINSERT).key(key).add(where) .add(pivot).add(value), BuilderFactory.LONG); } public final CommandObject linsert(byte[] key, ListPosition where, byte[] pivot, byte[] value) { return new CommandObject<>(commandArguments(LINSERT).key(key).add(where) .add(pivot).add(value), BuilderFactory.LONG); } public final CommandObject lpushx(String key, String... strings) { return new CommandObject<>(commandArguments(LPUSHX).key(key).addObjects((Object[]) strings), BuilderFactory.LONG); } public final CommandObject rpushx(String key, String... strings) { return new CommandObject<>(commandArguments(RPUSHX).key(key).addObjects((Object[]) strings), BuilderFactory.LONG); } public final CommandObject lpushx(byte[] key, byte[]... args) { return new CommandObject<>(commandArguments(LPUSHX).key(key).addObjects((Object[]) args), BuilderFactory.LONG); } public final CommandObject rpushx(byte[] key, byte[]... args) { return new CommandObject<>(commandArguments(RPUSHX).key(key).addObjects((Object[]) args), BuilderFactory.LONG); } public final CommandObject> blpop(int timeout, String key) { return new CommandObject<>(commandArguments(BLPOP).blocking().key(key).add(timeout), BuilderFactory.STRING_LIST); } public final CommandObject> blpop(int timeout, String... keys) { return new CommandObject<>(commandArguments(BLPOP).blocking().keys((Object[]) keys).add(timeout), BuilderFactory.STRING_LIST); } public final CommandObject> blpop(double timeout, String key) { return new CommandObject<>(commandArguments(BLPOP).blocking().key(key).add(timeout), BuilderFactory.KEYED_ELEMENT); } public final CommandObject> blpop(double timeout, String... keys) { return new CommandObject<>(commandArguments(BLPOP).blocking().keys((Object[]) keys).add(timeout), BuilderFactory.KEYED_ELEMENT); } public final CommandObject> blpop(int timeout, byte[]... keys) { return new CommandObject<>(commandArguments(BLPOP).blocking().keys((Object[]) keys).add(timeout), BuilderFactory.BINARY_LIST); } public final CommandObject> blpop(double timeout, byte[]... keys) { return new CommandObject<>(commandArguments(BLPOP).blocking().keys((Object[]) keys).add(timeout), BuilderFactory.BINARY_KEYED_ELEMENT); } public final CommandObject> brpop(int timeout, String key) { return new CommandObject<>(commandArguments(BRPOP).blocking().key(key).add(timeout), BuilderFactory.STRING_LIST); } public final CommandObject> brpop(int timeout, String... keys) { return new CommandObject<>(commandArguments(BRPOP).blocking().keys((Object[]) keys).add(timeout), BuilderFactory.STRING_LIST); } public final CommandObject> brpop(double timeout, String key) { return new CommandObject<>(commandArguments(BRPOP).blocking().key(key).add(timeout), BuilderFactory.KEYED_ELEMENT); } public final CommandObject> brpop(double timeout, String... keys) { return new CommandObject<>(commandArguments(BRPOP).blocking().keys((Object[]) keys).add(timeout), BuilderFactory.KEYED_ELEMENT); } public final CommandObject> brpop(int timeout, byte[]... keys) { return new CommandObject<>(commandArguments(BRPOP).blocking().keys((Object[]) keys).add(timeout), BuilderFactory.BINARY_LIST); } public final CommandObject> brpop(double timeout, byte[]... keys) { return new CommandObject<>(commandArguments(BRPOP).blocking().keys((Object[]) keys).add(timeout), BuilderFactory.BINARY_KEYED_ELEMENT); } public final CommandObject rpoplpush(String srckey, String dstkey) { return new CommandObject<>(commandArguments(RPOPLPUSH).key(srckey).key(dstkey), BuilderFactory.STRING); } public final CommandObject brpoplpush(String source, String destination, int timeout) { return new CommandObject<>(commandArguments(BRPOPLPUSH).blocking().key(source) .key(destination).add(timeout), BuilderFactory.STRING); } public final CommandObject rpoplpush(byte[] srckey, byte[] dstkey) { return new CommandObject<>(commandArguments(RPOPLPUSH).key(srckey).key(dstkey), BuilderFactory.BINARY); } public final CommandObject brpoplpush(byte[] source, byte[] destination, int timeout) { return new CommandObject<>(commandArguments(BRPOPLPUSH).blocking().key(source) .key(destination).add(timeout), BuilderFactory.BINARY); } public final CommandObject lmove(String srcKey, String dstKey, ListDirection from, ListDirection to) { return new CommandObject<>(commandArguments(LMOVE).key(srcKey).key(dstKey) .add(from).add(to), BuilderFactory.STRING); } public final CommandObject blmove(String srcKey, String dstKey, ListDirection from, ListDirection to, double timeout) { return new CommandObject<>(commandArguments(BLMOVE).blocking().key(srcKey) .key(dstKey).add(from).add(to).add(timeout), BuilderFactory.STRING); } public final CommandObject lmove(byte[] srcKey, byte[] dstKey, ListDirection from, ListDirection to) { return new CommandObject<>(commandArguments(LMOVE).key(srcKey).key(dstKey) .add(from).add(to), BuilderFactory.BINARY); } public final CommandObject blmove(byte[] srcKey, byte[] dstKey, ListDirection from, ListDirection to, double timeout) { return new CommandObject<>(commandArguments(BLMOVE).blocking().key(srcKey) .key(dstKey).add(from).add(to).add(timeout), BuilderFactory.BINARY); } public final CommandObject>> lmpop(ListDirection direction, String... keys) { return new CommandObject<>(commandArguments(LMPOP).add(keys.length).keys((Object[]) keys) .add(direction), BuilderFactory.KEYED_STRING_LIST); } public final CommandObject>> lmpop(ListDirection direction, int count, String... keys) { return new CommandObject<>(commandArguments(LMPOP).add(keys.length).keys((Object[]) keys) .add(direction).add(COUNT).add(count), BuilderFactory.KEYED_STRING_LIST); } public final CommandObject>> blmpop(double timeout, ListDirection direction, String... keys) { return new CommandObject<>(commandArguments(BLMPOP).blocking().add(timeout) .add(keys.length).keys((Object[]) keys).add(direction), BuilderFactory.KEYED_STRING_LIST); } public final CommandObject>> blmpop(double timeout, ListDirection direction, int count, String... keys) { return new CommandObject<>(commandArguments(BLMPOP).blocking().add(timeout) .add(keys.length).keys((Object[]) keys).add(direction).add(COUNT).add(count), BuilderFactory.KEYED_STRING_LIST); } public final CommandObject>> lmpop(ListDirection direction, byte[]... keys) { return new CommandObject<>(commandArguments(LMPOP).add(keys.length).keys((Object[]) keys) .add(direction), BuilderFactory.KEYED_BINARY_LIST); } public final CommandObject>> lmpop(ListDirection direction, int count, byte[]... keys) { return new CommandObject<>(commandArguments(LMPOP).add(keys.length).keys((Object[]) keys) .add(direction).add(COUNT).add(count), BuilderFactory.KEYED_BINARY_LIST); } public final CommandObject>> blmpop(double timeout, ListDirection direction, byte[]... keys) { return new CommandObject<>(commandArguments(BLMPOP).blocking().add(timeout) .add(keys.length).keys((Object[]) keys).add(direction), BuilderFactory.KEYED_BINARY_LIST); } public final CommandObject>> blmpop(double timeout, ListDirection direction, int count, byte[]... keys) { return new CommandObject<>(commandArguments(BLMPOP).blocking().add(timeout) .add(keys.length).keys((Object[]) keys).add(direction).add(COUNT).add(count), BuilderFactory.KEYED_BINARY_LIST); } // List commands // Hash commands public final CommandObject hset(String key, String field, String value) { return new CommandObject<>(commandArguments(HSET).key(key).add(field).add(value), BuilderFactory.LONG); } public final CommandObject hset(String key, Map hash) { return new CommandObject<>(addFlatMapArgs(commandArguments(HSET).key(key), hash), BuilderFactory.LONG); } public final CommandObject hsetex(String key, HSetExParams params, String field, String value) { return new CommandObject<>(commandArguments(HSETEX).key(key) .addParams(params).add(FIELDS).add(1).add(field).add(value), BuilderFactory.LONG); } public final CommandObject hsetex(String key, HSetExParams params, Map hash) { return new CommandObject<>(addFlatMapArgs(commandArguments(HSETEX).key(key) .addParams(params).add(FIELDS).add(hash.size()), hash), BuilderFactory.LONG); } public final CommandObject hget(String key, String field) { return new CommandObject<>(commandArguments(HGET).key(key).add(field), BuilderFactory.STRING); } public final CommandObject> hgetex(String key, HGetExParams params, String... fields) { return new CommandObject<>(commandArguments(Command.HGETEX).key(key) .addParams(params).add(FIELDS).add(fields.length).addObjects((Object[]) fields), BuilderFactory.STRING_LIST); } public final CommandObject> hgetdel(String key, String... fields) { return new CommandObject<>(commandArguments(HGETDEL).key(key) .add(FIELDS).add(fields.length).addObjects((Object[]) fields), BuilderFactory.STRING_LIST); } public final CommandObject hsetnx(String key, String field, String value) { return new CommandObject<>(commandArguments(HSETNX).key(key).add(field).add(value), BuilderFactory.LONG); } public final CommandObject hmset(String key, Map hash) { return new CommandObject<>(addFlatMapArgs(commandArguments(HMSET).key(key), hash), BuilderFactory.STRING); } public final CommandObject> hmget(String key, String... fields) { return new CommandObject<>(commandArguments(HMGET).key(key).addObjects((Object[]) fields), BuilderFactory.STRING_LIST); } public final CommandObject hset(byte[] key, byte[] field, byte[] value) { return new CommandObject<>(commandArguments(HSET).key(key).add(field).add(value), BuilderFactory.LONG); } public final CommandObject hset(byte[] key, Map hash) { return new CommandObject<>(addFlatMapArgs(commandArguments(HSET).key(key), hash), BuilderFactory.LONG); } public final CommandObject hsetex(byte[] key, HSetExParams params, byte[] field, byte[] value) { return new CommandObject<>(commandArguments(HSETEX).key(key) .addParams(params).add(FIELDS).add(1).add(field).add(value), BuilderFactory.LONG); } public final CommandObject hsetex(byte[] key, HSetExParams params, Map hash) { return new CommandObject<>(addFlatMapArgs(commandArguments(HSETEX).key(key) .addParams(params).add(FIELDS).add(hash.size()), hash), BuilderFactory.LONG); } public final CommandObject hget(byte[] key, byte[] field) { return new CommandObject<>(commandArguments(HGET).key(key).add(field), BuilderFactory.BINARY); } public final CommandObject> hgetex(byte[] key, HGetExParams params, byte[]... fields) { return new CommandObject<>(commandArguments(Command.HGETEX).key(key) .addParams(params).add(FIELDS).add(fields.length).addObjects((Object[]) fields), BuilderFactory.BINARY_LIST); } public final CommandObject> hgetdel(byte[] key, byte[]... fields) { return new CommandObject<>(commandArguments(HGETDEL).key(key).add(FIELDS) .add(fields.length).addObjects((Object[]) fields), BuilderFactory.BINARY_LIST); } public final CommandObject hsetnx(byte[] key, byte[] field, byte[] value) { return new CommandObject<>(commandArguments(HSETNX).key(key).add(field).add(value), BuilderFactory.LONG); } public final CommandObject hmset(byte[] key, Map hash) { return new CommandObject<>(addFlatMapArgs(commandArguments(HMSET).key(key), hash), BuilderFactory.STRING); } public final CommandObject> hmget(byte[] key, byte[]... fields) { return new CommandObject<>(commandArguments(HMGET).key(key).addObjects((Object[]) fields), BuilderFactory.BINARY_LIST); } public final CommandObject hincrBy(String key, String field, long value) { return new CommandObject<>(commandArguments(HINCRBY).key(key).add(field).add(value), BuilderFactory.LONG); } public final CommandObject hincrByFloat(String key, String field, double value) { return new CommandObject<>(commandArguments(HINCRBYFLOAT).key(key).add(field).add(value), BuilderFactory.DOUBLE); } public final CommandObject hexists(String key, String field) { return new CommandObject<>(commandArguments(HEXISTS).key(key).add(field), BuilderFactory.BOOLEAN); } public final CommandObject hdel(String key, String... field) { return new CommandObject<>(commandArguments(HDEL).key(key).addObjects((Object[]) field), BuilderFactory.LONG); } public final CommandObject hlen(String key) { return new CommandObject<>(commandArguments(HLEN).key(key), BuilderFactory.LONG); } public final CommandObject hincrBy(byte[] key, byte[] field, long value) { return new CommandObject<>(commandArguments(HINCRBY).key(key).add(field).add(value), BuilderFactory.LONG); } public final CommandObject hincrByFloat(byte[] key, byte[] field, double value) { return new CommandObject<>(commandArguments(HINCRBYFLOAT).key(key).add(field).add(value), BuilderFactory.DOUBLE); } public final CommandObject hexists(byte[] key, byte[] field) { return new CommandObject<>(commandArguments(HEXISTS).key(key).add(field), BuilderFactory.BOOLEAN); } public final CommandObject hdel(byte[] key, byte[]... field) { return new CommandObject<>(commandArguments(HDEL).key(key).addObjects((Object[]) field), BuilderFactory.LONG); } public final CommandObject hlen(byte[] key) { return new CommandObject<>(commandArguments(HLEN).key(key), BuilderFactory.LONG); } public final CommandObject> hkeys(String key) { return new CommandObject<>(commandArguments(HKEYS).key(key), BuilderFactory.STRING_SET); } public final CommandObject> hvals(String key) { return new CommandObject<>(commandArguments(HVALS).key(key), BuilderFactory.STRING_LIST); } public final CommandObject> hkeys(byte[] key) { return new CommandObject<>(commandArguments(HKEYS).key(key), BuilderFactory.BINARY_SET); } public final CommandObject> hvals(byte[] key) { return new CommandObject<>(commandArguments(HVALS).key(key), BuilderFactory.BINARY_LIST); } public final CommandObject> hgetAll(String key) { return new CommandObject<>(commandArguments(HGETALL).key(key), BuilderFactory.STRING_MAP); } public final CommandObject hrandfield(String key) { return new CommandObject<>(commandArguments(HRANDFIELD).key(key), BuilderFactory.STRING); } public final CommandObject> hrandfield(String key, long count) { return new CommandObject<>(commandArguments(HRANDFIELD).key(key).add(count), BuilderFactory.STRING_LIST); } public final CommandObject>> hrandfieldWithValues(String key, long count) { return new CommandObject<>(commandArguments(HRANDFIELD).key(key).add(count).add(WITHVALUES), protocol != RedisProtocol.RESP3 ? BuilderFactory.STRING_PAIR_LIST : BuilderFactory.STRING_PAIR_LIST_FROM_PAIRS); } public final CommandObject> hgetAll(byte[] key) { return new CommandObject<>(commandArguments(HGETALL).key(key), BuilderFactory.BINARY_MAP); } public final CommandObject hrandfield(byte[] key) { return new CommandObject<>(commandArguments(HRANDFIELD).key(key), BuilderFactory.BINARY); } public final CommandObject> hrandfield(byte[] key, long count) { return new CommandObject<>(commandArguments(HRANDFIELD).key(key).add(count), BuilderFactory.BINARY_LIST); } public final CommandObject>> hrandfieldWithValues(byte[] key, long count) { return new CommandObject<>(commandArguments(HRANDFIELD).key(key).add(count).add(WITHVALUES), protocol != RedisProtocol.RESP3 ? BuilderFactory.BINARY_PAIR_LIST : BuilderFactory.BINARY_PAIR_LIST_FROM_PAIRS); } public final CommandObject>> hscan(String key, String cursor, ScanParams params) { return new CommandObject<>(commandArguments(HSCAN).key(key).add(cursor).addParams(params), BuilderFactory.HSCAN_RESPONSE); } public final CommandObject> hscanNoValues(String key, String cursor, ScanParams params) { return new CommandObject<>(commandArguments(HSCAN).key(key).add(cursor).addParams(params).add(NOVALUES), BuilderFactory.SCAN_RESPONSE); } public final CommandObject>> hscan(byte[] key, byte[] cursor, ScanParams params) { return new CommandObject<>(commandArguments(HSCAN).key(key).add(cursor).addParams(params), BuilderFactory.HSCAN_BINARY_RESPONSE); } public final CommandObject> hscanNoValues(byte[] key, byte[] cursor, ScanParams params) { return new CommandObject<>(commandArguments(HSCAN).key(key).add(cursor).addParams(params).add(NOVALUES), BuilderFactory.SCAN_BINARY_RESPONSE); } public final CommandObject hstrlen(String key, String field) { return new CommandObject<>(commandArguments(HSTRLEN).key(key).add(field), BuilderFactory.LONG); } public final CommandObject hstrlen(byte[] key, byte[] field) { return new CommandObject<>(commandArguments(HSTRLEN).key(key).add(field), BuilderFactory.LONG); } public final CommandObject> hexpire(String key, long seconds, String... fields) { return new CommandObject<>(commandArguments(HEXPIRE).key(key).add(seconds) .add(FIELDS).add(fields.length).addObjects((Object[]) fields), BuilderFactory.LONG_LIST); } public final CommandObject> hexpire(String key, long seconds, ExpiryOption condition, String... fields) { return new CommandObject<>(commandArguments(HEXPIRE).key(key).add(seconds).add(condition) .add(FIELDS).add(fields.length).addObjects((Object[]) fields), BuilderFactory.LONG_LIST); } public final CommandObject> hpexpire(String key, long milliseconds, String... fields) { return new CommandObject<>(commandArguments(HPEXPIRE).key(key).add(milliseconds) .add(FIELDS).add(fields.length).addObjects((Object[]) fields), BuilderFactory.LONG_LIST); } public final CommandObject> hpexpire(String key, long milliseconds, ExpiryOption condition, String... fields) { return new CommandObject<>(commandArguments(HPEXPIRE).key(key).add(milliseconds).add(condition) .add(FIELDS).add(fields.length).addObjects((Object[]) fields), BuilderFactory.LONG_LIST); } public final CommandObject> hexpireAt(String key, long unixTimeSeconds, String... fields) { return new CommandObject<>(commandArguments(HEXPIREAT).key(key).add(unixTimeSeconds) .add(FIELDS).add(fields.length).addObjects((Object[]) fields), BuilderFactory.LONG_LIST); } public final CommandObject> hexpireAt(String key, long unixTimeSeconds, ExpiryOption condition, String... fields) { return new CommandObject<>(commandArguments(HEXPIREAT).key(key).add(unixTimeSeconds).add(condition) .add(FIELDS).add(fields.length).addObjects((Object[]) fields), BuilderFactory.LONG_LIST); } public final CommandObject> hpexpireAt(String key, long unixTimeMillis, String... fields) { return new CommandObject<>(commandArguments(HPEXPIREAT).key(key).add(unixTimeMillis) .add(FIELDS).add(fields.length).addObjects((Object[]) fields), BuilderFactory.LONG_LIST); } public final CommandObject> hpexpireAt(String key, long unixTimeMillis, ExpiryOption condition, String... fields) { return new CommandObject<>(commandArguments(HPEXPIREAT).key(key).add(unixTimeMillis).add(condition) .add(FIELDS).add(fields.length).addObjects((Object[]) fields), BuilderFactory.LONG_LIST); } public final CommandObject> hexpire(byte[] key, long seconds, byte[]... fields) { return new CommandObject<>(commandArguments(HEXPIRE).key(key).add(seconds) .add(FIELDS).add(fields.length).addObjects((Object[]) fields), BuilderFactory.LONG_LIST); } public final CommandObject> hexpire(byte[] key, long seconds, ExpiryOption condition, byte[]... fields) { return new CommandObject<>(commandArguments(HEXPIRE).key(key).add(seconds).add(condition) .add(FIELDS).add(fields.length).addObjects((Object[]) fields), BuilderFactory.LONG_LIST); } public final CommandObject> hpexpire(byte[] key, long milliseconds, byte[]... fields) { return new CommandObject<>(commandArguments(HPEXPIRE).key(key).add(milliseconds) .add(FIELDS).add(fields.length).addObjects((Object[]) fields), BuilderFactory.LONG_LIST); } public final CommandObject> hpexpire(byte[] key, long milliseconds, ExpiryOption condition, byte[]... fields) { return new CommandObject<>(commandArguments(HPEXPIRE).key(key).add(milliseconds).add(condition) .add(FIELDS).add(fields.length).addObjects((Object[]) fields), BuilderFactory.LONG_LIST); } public final CommandObject> hexpireAt(byte[] key, long unixTimeSeconds, byte[]... fields) { return new CommandObject<>(commandArguments(HEXPIREAT).key(key).add(unixTimeSeconds) .add(FIELDS).add(fields.length).addObjects((Object[]) fields), BuilderFactory.LONG_LIST); } public final CommandObject> hexpireAt(byte[] key, long unixTimeSeconds, ExpiryOption condition, byte[]... fields) { return new CommandObject<>(commandArguments(HEXPIREAT).key(key).add(unixTimeSeconds).add(condition) .add(FIELDS).add(fields.length).addObjects((Object[]) fields), BuilderFactory.LONG_LIST); } public final CommandObject> hpexpireAt(byte[] key, long unixTimeMillis, byte[]... fields) { return new CommandObject<>(commandArguments(HPEXPIREAT).key(key).add(unixTimeMillis) .add(FIELDS).add(fields.length).addObjects((Object[]) fields), BuilderFactory.LONG_LIST); } public final CommandObject> hpexpireAt(byte[] key, long unixTimeMillis, ExpiryOption condition, byte[]... fields) { return new CommandObject<>(commandArguments(HPEXPIREAT).key(key).add(unixTimeMillis).add(condition) .add(FIELDS).add(fields.length).addObjects((Object[]) fields), BuilderFactory.LONG_LIST); } public final CommandObject> hexpireTime(String key, String... fields) { return new CommandObject<>(commandArguments(HEXPIRETIME).key(key) .add(FIELDS).add(fields.length).addObjects((Object[]) fields), BuilderFactory.LONG_LIST); } public final CommandObject> hpexpireTime(String key, String... fields) { return new CommandObject<>(commandArguments(HPEXPIRETIME).key(key) .add(FIELDS).add(fields.length).addObjects((Object[]) fields), BuilderFactory.LONG_LIST); } public final CommandObject> httl(String key, String... fields) { return new CommandObject<>(commandArguments(HTTL).key(key) .add(FIELDS).add(fields.length).addObjects((Object[]) fields), BuilderFactory.LONG_LIST); } public final CommandObject> hpttl(String key, String... fields) { return new CommandObject<>(commandArguments(HPTTL).key(key) .add(FIELDS).add(fields.length).addObjects((Object[]) fields), BuilderFactory.LONG_LIST); } public final CommandObject> hexpireTime(byte[] key, byte[]... fields) { return new CommandObject<>(commandArguments(HEXPIRETIME).key(key) .add(FIELDS).add(fields.length).addObjects((Object[]) fields), BuilderFactory.LONG_LIST); } public final CommandObject> hpexpireTime(byte[] key, byte[]... fields) { return new CommandObject<>(commandArguments(HPEXPIRETIME).key(key) .add(FIELDS).add(fields.length).addObjects((Object[]) fields), BuilderFactory.LONG_LIST); } public final CommandObject> httl(byte[] key, byte[]... fields) { return new CommandObject<>(commandArguments(HTTL).key(key) .add(FIELDS).add(fields.length).addObjects((Object[]) fields), BuilderFactory.LONG_LIST); } public final CommandObject> hpttl(byte[] key, byte[]... fields) { return new CommandObject<>(commandArguments(HPTTL).key(key) .add(FIELDS).add(fields.length).addObjects((Object[]) fields), BuilderFactory.LONG_LIST); } public final CommandObject> hpersist(String key, String... fields) { return new CommandObject<>(commandArguments(HPERSIST).key(key) .add(FIELDS).add(fields.length).addObjects((Object[]) fields), BuilderFactory.LONG_LIST); } public final CommandObject> hpersist(byte[] key, byte[]... fields) { return new CommandObject<>(commandArguments(HPERSIST).key(key) .add(FIELDS).add(fields.length).addObjects((Object[]) fields), BuilderFactory.LONG_LIST); } // Hash commands // Set commands public final CommandObject sadd(String key, String... members) { return new CommandObject<>(commandArguments(SADD).key(key).addObjects((Object[]) members), BuilderFactory.LONG); } public final CommandObject sadd(byte[] key, byte[]... members) { return new CommandObject<>(commandArguments(SADD).key(key).addObjects((Object[]) members), BuilderFactory.LONG); } public final CommandObject> smembers(String key) { return new CommandObject<>(commandArguments(SMEMBERS).key(key), BuilderFactory.STRING_SET); } public final CommandObject> smembers(byte[] key) { return new CommandObject<>(commandArguments(SMEMBERS).key(key), BuilderFactory.BINARY_SET); } public final CommandObject srem(String key, String... members) { return new CommandObject<>(commandArguments(SREM).key(key).addObjects((Object[]) members), BuilderFactory.LONG); } public final CommandObject srem(byte[] key, byte[]... members) { return new CommandObject<>(commandArguments(SREM).key(key).addObjects((Object[]) members), BuilderFactory.LONG); } public final CommandObject spop(String key) { return new CommandObject<>(commandArguments(SPOP).key(key), BuilderFactory.STRING); } public final CommandObject spop(byte[] key) { return new CommandObject<>(commandArguments(SPOP).key(key), BuilderFactory.BINARY); } public final CommandObject> spop(String key, long count) { return new CommandObject<>(commandArguments(SPOP).key(key).add(count), BuilderFactory.STRING_SET); } public final CommandObject> spop(byte[] key, long count) { return new CommandObject<>(commandArguments(SPOP).key(key).add(count), BuilderFactory.BINARY_SET); } public final CommandObject scard(String key) { return new CommandObject<>(commandArguments(SCARD).key(key), BuilderFactory.LONG); } public final CommandObject scard(byte[] key) { return new CommandObject<>(commandArguments(SCARD).key(key), BuilderFactory.LONG); } public final CommandObject sismember(String key, String member) { return new CommandObject<>(commandArguments(SISMEMBER).key(key).add(member), BuilderFactory.BOOLEAN); } public final CommandObject sismember(byte[] key, byte[] member) { return new CommandObject<>(commandArguments(SISMEMBER).key(key).add(member), BuilderFactory.BOOLEAN); } public final CommandObject> smismember(String key, String... members) { return new CommandObject<>(commandArguments(SMISMEMBER).key(key).addObjects((Object[]) members), BuilderFactory.BOOLEAN_LIST); } public final CommandObject> smismember(byte[] key, byte[]... members) { return new CommandObject<>(commandArguments(SMISMEMBER).key(key).addObjects((Object[]) members), BuilderFactory.BOOLEAN_LIST); } public final CommandObject srandmember(String key) { return new CommandObject<>(commandArguments(SRANDMEMBER).key(key), BuilderFactory.STRING); } public final CommandObject srandmember(byte[] key) { return new CommandObject<>(commandArguments(SRANDMEMBER).key(key), BuilderFactory.BINARY); } public final CommandObject> srandmember(String key, int count) { return new CommandObject<>(commandArguments(SRANDMEMBER).key(key).add(count), BuilderFactory.STRING_LIST); } public final CommandObject> srandmember(byte[] key, int count) { return new CommandObject<>(commandArguments(SRANDMEMBER).key(key).add(count), BuilderFactory.BINARY_LIST); } public final CommandObject> sscan(String key, String cursor, ScanParams params) { return new CommandObject<>(commandArguments(SSCAN).key(key).add(cursor).addParams(params), BuilderFactory.SSCAN_RESPONSE); } public final CommandObject> sscan(byte[] key, byte[] cursor, ScanParams params) { return new CommandObject<>(commandArguments(SSCAN).key(key).add(cursor).addParams(params), BuilderFactory.SSCAN_BINARY_RESPONSE); } public final CommandObject> sdiff(String... keys) { return new CommandObject<>(commandArguments(SDIFF).keys((Object[]) keys), BuilderFactory.STRING_SET); } public final CommandObject sdiffstore(String dstkey, String... keys) { return new CommandObject<>(commandArguments(SDIFFSTORE).key(dstkey).keys((Object[]) keys), BuilderFactory.LONG); } public final CommandObject> sdiff(byte[]... keys) { return new CommandObject<>(commandArguments(SDIFF).keys((Object[]) keys), BuilderFactory.BINARY_SET); } public final CommandObject sdiffstore(byte[] dstkey, byte[]... keys) { return new CommandObject<>(commandArguments(SDIFFSTORE).key(dstkey).keys((Object[]) keys), BuilderFactory.LONG); } public final CommandObject> sinter(String... keys) { return new CommandObject<>(commandArguments(SINTER).keys((Object[]) keys), BuilderFactory.STRING_SET); } public final CommandObject sinterstore(String dstkey, String... keys) { return new CommandObject<>(commandArguments(SINTERSTORE).key(dstkey).keys((Object[]) keys), BuilderFactory.LONG); } public final CommandObject sintercard(String... keys) { return new CommandObject<>(commandArguments(SINTERCARD).add(keys.length).keys((Object[]) keys), BuilderFactory.LONG); } public final CommandObject sintercard(int limit, String... keys) { return new CommandObject<>(commandArguments(SINTERCARD).add(keys.length).keys((Object[]) keys).add(LIMIT).add(limit),BuilderFactory.LONG); } public final CommandObject> sinter(byte[]... keys) { return new CommandObject<>(commandArguments(SINTER).keys((Object[]) keys), BuilderFactory.BINARY_SET); } public final CommandObject sinterstore(byte[] dstkey, byte[]... keys) { return new CommandObject<>(commandArguments(SINTERSTORE).key(dstkey).keys((Object[]) keys), BuilderFactory.LONG); } public final CommandObject sintercard(byte[]... keys) { return new CommandObject<>(commandArguments(SINTERCARD).add(keys.length).keys((Object[]) keys), BuilderFactory.LONG); } public final CommandObject sintercard(int limit, byte[]... keys) { return new CommandObject<>(commandArguments(SINTERCARD).add(keys.length).keys((Object[]) keys).add(LIMIT).add(limit),BuilderFactory.LONG); } public final CommandObject> sunion(String... keys) { return new CommandObject<>(commandArguments(SUNION).keys((Object[]) keys), BuilderFactory.STRING_SET); } public final CommandObject sunionstore(String dstkey, String... keys) { return new CommandObject<>(commandArguments(SUNIONSTORE).key(dstkey).keys((Object[]) keys), BuilderFactory.LONG); } public final CommandObject> sunion(byte[]... keys) { return new CommandObject<>(commandArguments(SUNION).keys((Object[]) keys), BuilderFactory.BINARY_SET); } public final CommandObject sunionstore(byte[] dstkey, byte[]... keys) { return new CommandObject<>(commandArguments(SUNIONSTORE).key(dstkey).keys((Object[]) keys), BuilderFactory.LONG); } public final CommandObject smove(String srckey, String dstkey, String member) { return new CommandObject<>(commandArguments(SMOVE).key(srckey).key(dstkey).add(member), BuilderFactory.LONG); } public final CommandObject smove(byte[] srckey, byte[] dstkey, byte[] member) { return new CommandObject<>(commandArguments(SMOVE).key(srckey).key(dstkey).add(member), BuilderFactory.LONG); } // Set commands // Sorted Set commands public final CommandObject zadd(String key, double score, String member) { return new CommandObject<>(commandArguments(ZADD).key(key).add(score).add(member), BuilderFactory.LONG); } public final CommandObject zadd(String key, double score, String member, ZAddParams params) { return new CommandObject<>(commandArguments(ZADD).key(key).addParams(params) .add(score).add(member), BuilderFactory.LONG); } public final CommandObject zadd(String key, Map scoreMembers) { return new CommandObject<>(addSortedSetFlatMapArgs(commandArguments(ZADD).key(key), scoreMembers), BuilderFactory.LONG); } public final CommandObject zadd(String key, Map scoreMembers, ZAddParams params) { return new CommandObject<>(addSortedSetFlatMapArgs(commandArguments(ZADD).key(key).addParams(params), scoreMembers), BuilderFactory.LONG); } public final CommandObject zaddIncr(String key, double score, String member, ZAddParams params) { return new CommandObject<>(commandArguments(ZADD).key(key).add(Keyword.INCR) .addParams(params).add(score).add(member), BuilderFactory.DOUBLE); } public final CommandObject zadd(byte[] key, double score, byte[] member) { return new CommandObject<>(commandArguments(ZADD).key(key).add(score).add(member), BuilderFactory.LONG); } public final CommandObject zadd(byte[] key, double score, byte[] member, ZAddParams params) { return new CommandObject<>(commandArguments(ZADD).key(key).addParams(params) .add(score).add(member), BuilderFactory.LONG); } public final CommandObject zadd(byte[] key, Map scoreMembers) { return new CommandObject<>(addSortedSetFlatMapArgs(commandArguments(ZADD).key(key), scoreMembers), BuilderFactory.LONG); } public final CommandObject zadd(byte[] key, Map scoreMembers, ZAddParams params) { return new CommandObject<>(addSortedSetFlatMapArgs(commandArguments(ZADD).key(key).addParams(params), scoreMembers), BuilderFactory.LONG); } public final CommandObject zaddIncr(byte[] key, double score, byte[] member, ZAddParams params) { return new CommandObject<>(commandArguments(ZADD).key(key).add(Keyword.INCR) .addParams(params).add(score).add(member), BuilderFactory.DOUBLE); } public final CommandObject zincrby(String key, double increment, String member) { return new CommandObject<>(commandArguments(ZINCRBY).key(key).add(increment).add(member), BuilderFactory.DOUBLE); } public final CommandObject zincrby(String key, double increment, String member, ZIncrByParams params) { return new CommandObject<>(commandArguments(ZADD).key(key).addParams(params).add(increment).add(member), BuilderFactory.DOUBLE); } public final CommandObject zincrby(byte[] key, double increment, byte[] member) { return new CommandObject<>(commandArguments(ZINCRBY).key(key).add(increment).add(member), BuilderFactory.DOUBLE); } public final CommandObject zincrby(byte[] key, double increment, byte[] member, ZIncrByParams params) { return new CommandObject<>(commandArguments(ZADD).key(key).addParams(params).add(increment).add(member), BuilderFactory.DOUBLE); } public final CommandObject zrem(String key, String... members) { return new CommandObject<>(commandArguments(ZREM).key(key).addObjects((Object[]) members), BuilderFactory.LONG); } public final CommandObject zrem(byte[] key, byte[]... members) { return new CommandObject<>(commandArguments(ZREM).key(key).addObjects((Object[]) members), BuilderFactory.LONG); } public final CommandObject zrank(String key, String member) { return new CommandObject<>(commandArguments(ZRANK).key(key).add(member), BuilderFactory.LONG); } public final CommandObject zrevrank(String key, String member) { return new CommandObject<>(commandArguments(ZREVRANK).key(key).add(member), BuilderFactory.LONG); } public final CommandObject> zrankWithScore(String key, String member) { return new CommandObject<>(commandArguments(ZRANK).key(key).add(member).add(WITHSCORE), BuilderFactory.ZRANK_WITHSCORE_PAIR); } public final CommandObject> zrevrankWithScore(String key, String member) { return new CommandObject<>(commandArguments(ZREVRANK).key(key).add(member).add(WITHSCORE), BuilderFactory.ZRANK_WITHSCORE_PAIR); } public final CommandObject zrank(byte[] key, byte[] member) { return new CommandObject<>(commandArguments(ZRANK).key(key).add(member), BuilderFactory.LONG); } public final CommandObject zrevrank(byte[] key, byte[] member) { return new CommandObject<>(commandArguments(ZREVRANK).key(key).add(member), BuilderFactory.LONG); } public final CommandObject> zrankWithScore(byte[] key, byte[] member) { return new CommandObject<>(commandArguments(ZRANK).key(key).add(member).add(WITHSCORE), BuilderFactory.ZRANK_WITHSCORE_PAIR); } public final CommandObject> zrevrankWithScore(byte[] key, byte[] member) { return new CommandObject<>(commandArguments(ZREVRANK).key(key).add(member).add(WITHSCORE), BuilderFactory.ZRANK_WITHSCORE_PAIR); } public final CommandObject zrandmember(String key) { return new CommandObject<>(commandArguments(ZRANDMEMBER).key(key), BuilderFactory.STRING); } public final CommandObject> zrandmember(String key, long count) { return new CommandObject<>(commandArguments(ZRANDMEMBER).key(key).add(count), BuilderFactory.STRING_LIST); } public final CommandObject> zrandmemberWithScores(String key, long count) { return new CommandObject<>(commandArguments(ZRANDMEMBER).key(key).add(count).add(WITHSCORES), getTupleListBuilder()); } public final CommandObject zrandmember(byte[] key) { return new CommandObject<>(commandArguments(ZRANDMEMBER).key(key), BuilderFactory.BINARY); } public final CommandObject> zrandmember(byte[] key, long count) { return new CommandObject<>(commandArguments(ZRANDMEMBER).key(key).add(count), BuilderFactory.BINARY_LIST); } public final CommandObject> zrandmemberWithScores(byte[] key, long count) { return new CommandObject<>(commandArguments(ZRANDMEMBER).key(key).add(count).add(WITHSCORES), getTupleListBuilder()); } public final CommandObject zcard(String key) { return new CommandObject<>(commandArguments(ZCARD).key(key), BuilderFactory.LONG); } public final CommandObject zscore(String key, String member) { return new CommandObject<>(commandArguments(ZSCORE).key(key).add(member), BuilderFactory.DOUBLE); } public final CommandObject> zmscore(String key, String... members) { return new CommandObject<>(commandArguments(ZMSCORE).key(key).addObjects((Object[]) members), BuilderFactory.DOUBLE_LIST); } public final CommandObject zcard(byte[] key) { return new CommandObject<>(commandArguments(ZCARD).key(key), BuilderFactory.LONG); } public final CommandObject zscore(byte[] key, byte[] member) { return new CommandObject<>(commandArguments(ZSCORE).key(key).add(member), BuilderFactory.DOUBLE); } public final CommandObject> zmscore(byte[] key, byte[]... members) { return new CommandObject<>(commandArguments(ZMSCORE).key(key).addObjects((Object[]) members), BuilderFactory.DOUBLE_LIST); } public final CommandObject zpopmax(String key) { return new CommandObject<>(commandArguments(ZPOPMAX).key(key), BuilderFactory.TUPLE); } public final CommandObject> zpopmax(String key, int count) { return new CommandObject<>(commandArguments(ZPOPMAX).key(key).add(count), getTupleListBuilder()); } public final CommandObject zpopmin(String key) { return new CommandObject<>(commandArguments(ZPOPMIN).key(key), BuilderFactory.TUPLE); } public final CommandObject> zpopmin(String key, int count) { return new CommandObject<>(commandArguments(ZPOPMIN).key(key).add(count), getTupleListBuilder()); } public final CommandObject zpopmax(byte[] key) { return new CommandObject<>(commandArguments(ZPOPMAX).key(key), BuilderFactory.TUPLE); } public final CommandObject> zpopmax(byte[] key, int count) { return new CommandObject<>(commandArguments(ZPOPMAX).key(key).add(count), getTupleListBuilder()); } public final CommandObject zpopmin(byte[] key) { return new CommandObject<>(commandArguments(ZPOPMIN).key(key), BuilderFactory.TUPLE); } public final CommandObject> zpopmin(byte[] key, int count) { return new CommandObject<>(commandArguments(ZPOPMIN).key(key).add(count), getTupleListBuilder()); } public final CommandObject> bzpopmax(double timeout, String... keys) { return new CommandObject<>(commandArguments(BZPOPMAX).blocking().keys((Object[]) keys).add(timeout), BuilderFactory.KEYED_TUPLE); } public final CommandObject> bzpopmin(double timeout, String... keys) { return new CommandObject<>(commandArguments(BZPOPMIN).blocking().keys((Object[]) keys).add(timeout), BuilderFactory.KEYED_TUPLE); } public final CommandObject> bzpopmax(double timeout, byte[]... keys) { return new CommandObject<>(commandArguments(BZPOPMAX).blocking().keys((Object[]) keys) .add(timeout), BuilderFactory.BINARY_KEYED_TUPLE); } public final CommandObject> bzpopmin(double timeout, byte[]... keys) { return new CommandObject<>(commandArguments(BZPOPMIN).blocking().keys((Object[]) keys) .add(timeout), BuilderFactory.BINARY_KEYED_TUPLE); } public final CommandObject zcount(String key, double min, double max) { return new CommandObject<>(commandArguments(ZCOUNT).key(key).add(min).add(max), BuilderFactory.LONG); } public final CommandObject zcount(String key, String min, String max) { return new CommandObject<>(commandArguments(ZCOUNT).key(key).add(min).add(max), BuilderFactory.LONG); } public final CommandObject zcount(byte[] key, double min, double max) { return new CommandObject<>(commandArguments(ZCOUNT).key(key).add(min).add(max), BuilderFactory.LONG); } public final CommandObject zcount(byte[] key, byte[] min, byte[] max) { return new CommandObject<>(commandArguments(ZCOUNT).key(key).add(min).add(max), BuilderFactory.LONG); } public final CommandObject> zrange(String key, long start, long stop) { return new CommandObject<>(commandArguments(ZRANGE).key(key).add(start).add(stop), BuilderFactory.STRING_LIST); } public final CommandObject> zrevrange(String key, long start, long stop) { return new CommandObject<>(commandArguments(ZREVRANGE).key(key).add(start).add(stop), BuilderFactory.STRING_LIST); } public final CommandObject> zrangeWithScores(String key, long start, long stop) { return new CommandObject<>(commandArguments(ZRANGE).key(key) .add(start).add(stop).add(WITHSCORES), getTupleListBuilder()); } public final CommandObject> zrevrangeWithScores(String key, long start, long stop) { return new CommandObject<>(commandArguments(ZREVRANGE).key(key) .add(start).add(stop).add(WITHSCORES), getTupleListBuilder()); } public final CommandObject> zrange(String key, ZRangeParams zRangeParams) { return new CommandObject<>(commandArguments(ZRANGE).key(key).addParams(zRangeParams), BuilderFactory.STRING_LIST); } public final CommandObject> zrangeWithScores(String key, ZRangeParams zRangeParams) { return new CommandObject<>(commandArguments(ZRANGE).key(key).addParams(zRangeParams).add(WITHSCORES), getTupleListBuilder()); } public final CommandObject zrangestore(String dest, String src, ZRangeParams zRangeParams) { return new CommandObject<>(commandArguments(ZRANGESTORE).key(dest).add(src).addParams(zRangeParams), BuilderFactory.LONG); } public final CommandObject> zrangeByScore(String key, double min, double max) { return new CommandObject<>(commandArguments(ZRANGEBYSCORE).key(key).add(min).add(max), BuilderFactory.STRING_LIST); } public final CommandObject> zrangeByScore(String key, String min, String max) { return new CommandObject<>(commandArguments(ZRANGEBYSCORE).key(key).add(min).add(max), BuilderFactory.STRING_LIST); } public final CommandObject> zrevrangeByScore(String key, double max, double min) { return new CommandObject<>(commandArguments(ZREVRANGEBYSCORE).key(key).add(max).add(min), BuilderFactory.STRING_LIST); } public final CommandObject> zrevrangeByScore(String key, String max, String min) { return new CommandObject<>(commandArguments(ZREVRANGEBYSCORE).key(key).add(max).add(min), BuilderFactory.STRING_LIST); } public final CommandObject> zrangeByScore(String key, double min, double max, int offset, int count) { return new CommandObject<>(commandArguments(ZRANGEBYSCORE).key(key).add(min).add(max) .add(LIMIT).add(offset).add(count), BuilderFactory.STRING_LIST); } public final CommandObject> zrangeByScore(String key, String min, String max, int offset, int count) { return new CommandObject<>(commandArguments(ZRANGEBYSCORE).key(key).add(min).add(max) .add(LIMIT).add(offset).add(count), BuilderFactory.STRING_LIST); } public final CommandObject> zrevrangeByScore(String key, double max, double min, int offset, int count) { return new CommandObject<>(commandArguments(ZREVRANGEBYSCORE).key(key).add(max).add(min) .add(LIMIT).add(offset).add(count), BuilderFactory.STRING_LIST); } public final CommandObject> zrevrangeByScore(String key, String max, String min, int offset, int count) { return new CommandObject<>(commandArguments(ZREVRANGEBYSCORE).key(key).add(max).add(min) .add(LIMIT).add(offset).add(count), BuilderFactory.STRING_LIST); } public final CommandObject> zrangeByScoreWithScores(String key, double min, double max) { return new CommandObject<>(commandArguments(ZRANGEBYSCORE).key(key).add(min).add(max) .add(WITHSCORES), getTupleListBuilder()); } public final CommandObject> zrangeByScoreWithScores(String key, String min, String max) { return new CommandObject<>(commandArguments(ZRANGEBYSCORE).key(key).add(min).add(max) .add(WITHSCORES), getTupleListBuilder()); } public final CommandObject> zrevrangeByScoreWithScores(String key, double max, double min) { return new CommandObject<>(commandArguments(ZREVRANGEBYSCORE).key(key).add(max).add(min) .add(WITHSCORES), getTupleListBuilder()); } public final CommandObject> zrevrangeByScoreWithScores(String key, String max, String min) { return new CommandObject<>(commandArguments(ZREVRANGEBYSCORE).key(key).add(max).add(min) .add(WITHSCORES), getTupleListBuilder()); } public final CommandObject> zrangeByScoreWithScores(String key, double min, double max, int offset, int count) { return new CommandObject<>(commandArguments(ZRANGEBYSCORE).key(key).add(min).add(max) .add(LIMIT).add(offset).add(count).add(WITHSCORES), getTupleListBuilder()); } public final CommandObject> zrangeByScoreWithScores(String key, String min, String max, int offset, int count) { return new CommandObject<>(commandArguments(ZRANGEBYSCORE).key(key).add(min).add(max) .add(LIMIT).add(offset).add(count).add(WITHSCORES), getTupleListBuilder()); } public final CommandObject> zrevrangeByScoreWithScores(String key, double max, double min, int offset, int count) { return new CommandObject<>(commandArguments(ZREVRANGEBYSCORE).key(key).add(max).add(min) .add(LIMIT).add(offset).add(count).add(WITHSCORES), getTupleListBuilder()); } public final CommandObject> zrevrangeByScoreWithScores(String key, String max, String min, int offset, int count) { return new CommandObject<>(commandArguments(ZREVRANGEBYSCORE).key(key).add(max).add(min) .add(LIMIT).add(offset).add(count).add(WITHSCORES), getTupleListBuilder()); } public final CommandObject> zrange(byte[] key, long start, long stop) { return new CommandObject<>(commandArguments(ZRANGE).key(key).add(start).add(stop), BuilderFactory.BINARY_LIST); } public final CommandObject> zrevrange(byte[] key, long start, long stop) { return new CommandObject<>(commandArguments(ZREVRANGE).key(key).add(start).add(stop), BuilderFactory.BINARY_LIST); } public final CommandObject> zrangeWithScores(byte[] key, long start, long stop) { return new CommandObject<>(commandArguments(ZRANGE).key(key) .add(start).add(stop).add(WITHSCORES), getTupleListBuilder()); } public final CommandObject> zrevrangeWithScores(byte[] key, long start, long stop) { return new CommandObject<>(commandArguments(ZREVRANGE).key(key) .add(start).add(stop).add(WITHSCORES), getTupleListBuilder()); } public final CommandObject> zrange(byte[] key, ZRangeParams zRangeParams) { return new CommandObject<>(commandArguments(ZRANGE).key(key).addParams(zRangeParams), BuilderFactory.BINARY_LIST); } public final CommandObject> zrangeWithScores(byte[] key, ZRangeParams zRangeParams) { return new CommandObject<>(commandArguments(ZRANGE).key(key).addParams(zRangeParams).add(WITHSCORES), getTupleListBuilder()); } public final CommandObject zrangestore(byte[] dest, byte[] src, ZRangeParams zRangeParams) { return new CommandObject<>(commandArguments(ZRANGESTORE).key(dest).add(src).addParams(zRangeParams), BuilderFactory.LONG); } public final CommandObject> zrangeByScore(byte[] key, double min, double max) { return new CommandObject<>(commandArguments(ZRANGEBYSCORE).key(key).add(min).add(max), BuilderFactory.BINARY_LIST); } public final CommandObject> zrangeByScore(byte[] key, byte[] min, byte[] max) { return new CommandObject<>(commandArguments(ZRANGEBYSCORE).key(key).add(min).add(max), BuilderFactory.BINARY_LIST); } public final CommandObject> zrevrangeByScore(byte[] key, double max, double min) { return new CommandObject<>(commandArguments(ZREVRANGEBYSCORE).key(key).add(max).add(min), BuilderFactory.BINARY_LIST); } public final CommandObject> zrevrangeByScore(byte[] key, byte[] max, byte[] min) { return new CommandObject<>(commandArguments(ZREVRANGEBYSCORE).key(key).add(max).add(min), BuilderFactory.BINARY_LIST); } public final CommandObject> zrangeByScore(byte[] key, double min, double max, int offset, int count) { return new CommandObject<>(commandArguments(ZRANGEBYSCORE).key(key).add(min).add(max) .add(LIMIT).add(offset).add(count), BuilderFactory.BINARY_LIST); } public final CommandObject> zrangeByScore(byte[] key, byte[] min, byte[] max, int offset, int count) { return new CommandObject<>(commandArguments(ZRANGEBYSCORE).key(key).add(min).add(max) .add(LIMIT).add(offset).add(count), BuilderFactory.BINARY_LIST); } public final CommandObject> zrevrangeByScore(byte[] key, double max, double min, int offset, int count) { return new CommandObject<>(commandArguments(ZREVRANGEBYSCORE).key(key).add(max).add(min) .add(LIMIT).add(offset).add(count), BuilderFactory.BINARY_LIST); } public final CommandObject> zrevrangeByScore(byte[] key, byte[] max, byte[] min, int offset, int count) { return new CommandObject<>(commandArguments(ZREVRANGEBYSCORE).key(key).add(max).add(min) .add(LIMIT).add(offset).add(count), BuilderFactory.BINARY_LIST); } public final CommandObject> zrangeByScoreWithScores(byte[] key, double min, double max) { return new CommandObject<>(commandArguments(ZRANGEBYSCORE).key(key).add(min).add(max) .add(WITHSCORES), getTupleListBuilder()); } public final CommandObject> zrangeByScoreWithScores(byte[] key, byte[] min, byte[] max) { return new CommandObject<>(commandArguments(ZRANGEBYSCORE).key(key).add(min).add(max) .add(WITHSCORES), getTupleListBuilder()); } public final CommandObject> zrevrangeByScoreWithScores(byte[] key, double max, double min) { return new CommandObject<>(commandArguments(ZREVRANGEBYSCORE).key(key).add(max).add(min) .add(WITHSCORES), getTupleListBuilder()); } public final CommandObject> zrevrangeByScoreWithScores(byte[] key, byte[] max, byte[] min) { return new CommandObject<>(commandArguments(ZREVRANGEBYSCORE).key(key).add(max).add(min) .add(WITHSCORES), getTupleListBuilder()); } public final CommandObject> zrangeByScoreWithScores(byte[] key, double min, double max, int offset, int count) { return new CommandObject<>(commandArguments(ZRANGEBYSCORE).key(key).add(min).add(max) .add(LIMIT).add(offset).add(count).add(WITHSCORES), getTupleListBuilder()); } public final CommandObject> zrangeByScoreWithScores(byte[] key, byte[] min, byte[] max, int offset, int count) { return new CommandObject<>(commandArguments(ZRANGEBYSCORE).key(key).add(min).add(max) .add(LIMIT).add(offset).add(count).add(WITHSCORES), getTupleListBuilder()); } public final CommandObject> zrevrangeByScoreWithScores(byte[] key, double max, double min, int offset, int count) { return new CommandObject<>(commandArguments(ZREVRANGEBYSCORE).key(key).add(max).add(min) .add(LIMIT).add(offset).add(count).add(WITHSCORES), getTupleListBuilder()); } public final CommandObject> zrevrangeByScoreWithScores(byte[] key, byte[] max, byte[] min, int offset, int count) { return new CommandObject<>(commandArguments(ZREVRANGEBYSCORE).key(key).add(max).add(min) .add(LIMIT).add(offset).add(count).add(WITHSCORES), getTupleListBuilder()); } public final CommandObject zremrangeByRank(String key, long start, long stop) { return new CommandObject<>(commandArguments(ZREMRANGEBYRANK).key(key).add(start).add(stop), BuilderFactory.LONG); } public final CommandObject zremrangeByScore(String key, double min, double max) { return new CommandObject<>(commandArguments(ZREMRANGEBYSCORE).key(key).add(min).add(max), BuilderFactory.LONG); } public final CommandObject zremrangeByScore(String key, String min, String max) { return new CommandObject<>(commandArguments(ZREMRANGEBYSCORE).key(key).add(min).add(max), BuilderFactory.LONG); } public final CommandObject zremrangeByRank(byte[] key, long start, long stop) { return new CommandObject<>(commandArguments(ZREMRANGEBYRANK).key(key).add(start).add(stop), BuilderFactory.LONG); } public final CommandObject zremrangeByScore(byte[] key, double min, double max) { return new CommandObject<>(commandArguments(ZREMRANGEBYSCORE).key(key).add(min).add(max), BuilderFactory.LONG); } public final CommandObject zremrangeByScore(byte[] key, byte[] min, byte[] max) { return new CommandObject<>(commandArguments(ZREMRANGEBYSCORE).key(key).add(min).add(max), BuilderFactory.LONG); } public final CommandObject zlexcount(String key, String min, String max) { return new CommandObject<>(commandArguments(ZLEXCOUNT).key(key).add(min).add(max), BuilderFactory.LONG); } public final CommandObject> zrangeByLex(String key, String min, String max) { return new CommandObject<>(commandArguments(ZRANGEBYLEX).key(key).add(min).add(max), BuilderFactory.STRING_LIST); } public final CommandObject> zrangeByLex(String key, String min, String max, int offset, int count) { return new CommandObject<>(commandArguments(ZRANGEBYLEX).key(key).add(min).add(max) .add(LIMIT).add(offset).add(count), BuilderFactory.STRING_LIST); } public final CommandObject> zrevrangeByLex(String key, String max, String min) { return new CommandObject<>(commandArguments(ZREVRANGEBYLEX).key(key).add(max).add(min), BuilderFactory.STRING_LIST); } public final CommandObject> zrevrangeByLex(String key, String max, String min, int offset, int count) { return new CommandObject<>(commandArguments(ZREVRANGEBYLEX).key(key).add(max).add(min) .add(LIMIT).add(offset).add(count), BuilderFactory.STRING_LIST); } public final CommandObject zremrangeByLex(String key, String min, String max) { return new CommandObject<>(commandArguments(ZREMRANGEBYLEX).key(key).add(min).add(max), BuilderFactory.LONG); } public final CommandObject zlexcount(byte[] key, byte[] min, byte[] max) { return new CommandObject<>(commandArguments(ZLEXCOUNT).key(key).add(min).add(max), BuilderFactory.LONG); } public final CommandObject> zrangeByLex(byte[] key, byte[] min, byte[] max) { return new CommandObject<>(commandArguments(ZRANGEBYLEX).key(key).add(min).add(max), BuilderFactory.BINARY_LIST); } public final CommandObject> zrangeByLex(byte[] key, byte[] min, byte[] max, int offset, int count) { return new CommandObject<>(commandArguments(ZRANGEBYLEX).key(key).add(min).add(max) .add(LIMIT).add(offset).add(count), BuilderFactory.BINARY_LIST); } public final CommandObject> zrevrangeByLex(byte[] key, byte[] max, byte[] min) { return new CommandObject<>(commandArguments(ZREVRANGEBYLEX).key(key).add(max).add(min), BuilderFactory.BINARY_LIST); } public final CommandObject> zrevrangeByLex(byte[] key, byte[] max, byte[] min, int offset, int count) { return new CommandObject<>(commandArguments(ZREVRANGEBYLEX).key(key).add(max).add(min) .add(LIMIT).add(offset).add(count), BuilderFactory.BINARY_LIST); } public final CommandObject zremrangeByLex(byte[] key, byte[] min, byte[] max) { return new CommandObject<>(commandArguments(ZREMRANGEBYLEX).key(key).add(min).add(max), BuilderFactory.LONG); } public final CommandObject> zscan(String key, String cursor, ScanParams params) { return new CommandObject<>(commandArguments(ZSCAN).key(key).add(cursor).addParams(params), BuilderFactory.ZSCAN_RESPONSE); } public final CommandObject> zscan(byte[] key, byte[] cursor, ScanParams params) { return new CommandObject<>(commandArguments(ZSCAN).key(key).add(cursor).addParams(params), BuilderFactory.ZSCAN_RESPONSE); } public final CommandObject> zdiff(String... keys) { return new CommandObject<>(commandArguments(ZDIFF).add(keys.length).keys((Object[]) keys), BuilderFactory.STRING_LIST); } public final CommandObject> zdiffWithScores(String... keys) { return new CommandObject<>(commandArguments(ZDIFF).add(keys.length).keys((Object[]) keys) .add(WITHSCORES), getTupleListBuilder()); } /** * @deprecated Use {@link #zdiffstore(java.lang.String, java.lang.String...)}. */ @Deprecated public final CommandObject zdiffStore(String dstkey, String... keys) { return zdiffstore(dstkey, keys); } public final CommandObject zdiffstore(String dstkey, String... keys) { return new CommandObject<>(commandArguments(ZDIFFSTORE).key(dstkey) .add(keys.length).keys((Object[]) keys), BuilderFactory.LONG); } public final CommandObject> zdiff(byte[]... keys) { return new CommandObject<>(commandArguments(ZDIFF).add(keys.length).keys((Object[]) keys), BuilderFactory.BINARY_LIST); } public final CommandObject> zdiffWithScores(byte[]... keys) { return new CommandObject<>(commandArguments(ZDIFF).add(keys.length).keys((Object[]) keys) .add(WITHSCORES), getTupleListBuilder()); } /** * @deprecated Use {@link #zdiffstore(byte[], byte[][])}. */ @Deprecated public final CommandObject zdiffStore(byte[] dstkey, byte[]... keys) { return zdiffstore(dstkey, keys); } public final CommandObject zdiffstore(byte[] dstkey, byte[]... keys) { return new CommandObject<>(commandArguments(ZDIFFSTORE).key(dstkey) .add(keys.length).keys((Object[]) keys), BuilderFactory.LONG); } public final CommandObject> zinter(ZParams params, String... keys) { return new CommandObject<>(commandArguments(ZINTER).add(keys.length).keys((Object[]) keys) .addParams(params), BuilderFactory.STRING_LIST); } public final CommandObject> zinterWithScores(ZParams params, String... keys) { return new CommandObject<>(commandArguments(ZINTER).add(keys.length).keys((Object[]) keys) .addParams(params).add(WITHSCORES), getTupleListBuilder()); } public final CommandObject zinterstore(String dstkey, String... keys) { return new CommandObject<>(commandArguments(ZINTERSTORE).key(dstkey) .add(keys.length).keys((Object[]) keys), BuilderFactory.LONG); } public final CommandObject zinterstore(String dstkey, ZParams params, String... keys) { return new CommandObject<>(commandArguments(ZINTERSTORE).key(dstkey) .add(keys.length).keys((Object[]) keys).addParams(params), BuilderFactory.LONG); } public final CommandObject zintercard(String... keys) { return new CommandObject<>(commandArguments(ZINTERCARD).add(keys.length) .keys((Object[]) keys), BuilderFactory.LONG); } public final CommandObject zintercard(long limit, String... keys) { return new CommandObject<>(commandArguments(ZINTERCARD).add(keys.length) .keys((Object[]) keys).add(LIMIT).add(limit), BuilderFactory.LONG); } public final CommandObject zinterstore(byte[] dstkey, byte[]... sets) { return new CommandObject<>(commandArguments(ZINTERSTORE).key(dstkey) .add(sets.length).keys((Object[]) sets), BuilderFactory.LONG); } public final CommandObject zinterstore(byte[] dstkey, ZParams params, byte[]... sets) { return new CommandObject<>(commandArguments(ZINTERSTORE).key(dstkey) .add(sets.length).keys((Object[]) sets).addParams(params), BuilderFactory.LONG); } public final CommandObject zintercard(byte[]... keys) { return new CommandObject<>(commandArguments(ZINTERCARD).add(keys.length) .keys((Object[]) keys), BuilderFactory.LONG); } public final CommandObject zintercard(long limit, byte[]... keys) { return new CommandObject<>(commandArguments(ZINTERCARD).add(keys.length) .keys((Object[]) keys).add(LIMIT).add(limit), BuilderFactory.LONG); } public final CommandObject> zinter(ZParams params, byte[]... keys) { return new CommandObject<>(commandArguments(ZINTER).add(keys.length).keys((Object[]) keys) .addParams(params), BuilderFactory.BINARY_LIST); } public final CommandObject> zinterWithScores(ZParams params, byte[]... keys) { return new CommandObject<>(commandArguments(ZINTER).add(keys.length).keys((Object[]) keys) .addParams(params).add(WITHSCORES), getTupleListBuilder()); } public final CommandObject zunionstore(String dstkey, String... sets) { return new CommandObject<>(commandArguments(ZUNIONSTORE).key(dstkey) .add(sets.length).keys((Object[]) sets), BuilderFactory.LONG); } public final CommandObject zunionstore(String dstkey, ZParams params, String... sets) { return new CommandObject<>(commandArguments(ZUNIONSTORE).key(dstkey) .add(sets.length).keys((Object[]) sets).addParams(params), BuilderFactory.LONG); } public final CommandObject> zunion(ZParams params, String... keys) { return new CommandObject<>(commandArguments(ZUNION).add(keys.length).keys((Object[]) keys) .addParams(params), BuilderFactory.STRING_LIST); } public final CommandObject> zunionWithScores(ZParams params, String... keys) { return new CommandObject<>(commandArguments(ZUNION).add(keys.length).keys((Object[]) keys) .addParams(params).add(WITHSCORES), getTupleListBuilder()); } public final CommandObject zunionstore(byte[] dstkey, byte[]... sets) { return new CommandObject<>(commandArguments(ZUNIONSTORE).key(dstkey) .add(sets.length).keys((Object[]) sets), BuilderFactory.LONG); } public final CommandObject zunionstore(byte[] dstkey, ZParams params, byte[]... sets) { return new CommandObject<>(commandArguments(ZUNIONSTORE).key(dstkey) .add(sets.length).keys((Object[]) sets).addParams(params), BuilderFactory.LONG); } public final CommandObject> zunion(ZParams params, byte[]... keys) { return new CommandObject<>(commandArguments(ZUNION).add(keys.length).keys((Object[]) keys) .addParams(params), BuilderFactory.BINARY_LIST); } public final CommandObject> zunionWithScores(ZParams params, byte[]... keys) { return new CommandObject<>(commandArguments(ZUNION).add(keys.length).keys((Object[]) keys) .addParams(params).add(WITHSCORES), getTupleListBuilder()); } public final CommandObject>> zmpop(SortedSetOption option, String... keys) { return new CommandObject<>(commandArguments(ZMPOP).add(keys.length).keys((Object[]) keys) .add(option), BuilderFactory.KEYED_TUPLE_LIST); } public final CommandObject>> zmpop(SortedSetOption option, int count, String... keys) { return new CommandObject<>(commandArguments(ZMPOP).add(keys.length).keys((Object[]) keys) .add(option).add(COUNT).add(count), BuilderFactory.KEYED_TUPLE_LIST); } public final CommandObject>> bzmpop(double timeout, SortedSetOption option, String... keys) { return new CommandObject<>(commandArguments(BZMPOP).blocking().add(timeout).add(keys.length) .keys((Object[]) keys).add(option), BuilderFactory.KEYED_TUPLE_LIST); } public final CommandObject>> bzmpop(double timeout, SortedSetOption option, int count, String... keys) { return new CommandObject<>(commandArguments(BZMPOP).blocking().add(timeout).add(keys.length) .keys((Object[]) keys).add(option).add(COUNT).add(count), BuilderFactory.KEYED_TUPLE_LIST); } public final CommandObject>> zmpop(SortedSetOption option, byte[]... keys) { return new CommandObject<>(commandArguments(ZMPOP).add(keys.length).keys((Object[]) keys) .add(option), BuilderFactory.BINARY_KEYED_TUPLE_LIST); } public final CommandObject>> zmpop(SortedSetOption option, int count, byte[]... keys) { return new CommandObject<>(commandArguments(ZMPOP).add(keys.length).keys((Object[]) keys) .add(option).add(COUNT).add(count), BuilderFactory.BINARY_KEYED_TUPLE_LIST); } public final CommandObject>> bzmpop(double timeout, SortedSetOption option, byte[]... keys) { return new CommandObject<>(commandArguments(BZMPOP).blocking().add(timeout).add(keys.length) .keys((Object[]) keys).add(option), BuilderFactory.BINARY_KEYED_TUPLE_LIST); } public final CommandObject>> bzmpop(double timeout, SortedSetOption option, int count, byte[]... keys) { return new CommandObject<>(commandArguments(BZMPOP).blocking().add(timeout).add(keys.length) .keys((Object[]) keys).add(option).add(COUNT).add(count), BuilderFactory.BINARY_KEYED_TUPLE_LIST); } private Builder> getTupleListBuilder() { return protocol == RedisProtocol.RESP3 ? BuilderFactory.TUPLE_LIST_RESP3 : BuilderFactory.TUPLE_LIST; } // Sorted Set commands // Geo commands public final CommandObject geoadd(String key, double longitude, double latitude, String member) { return new CommandObject<>(commandArguments(GEOADD).key(key).add(longitude).add(latitude).add(member), BuilderFactory.LONG); } public final CommandObject geoadd(String key, Map memberCoordinateMap) { return new CommandObject<>(addGeoCoordinateFlatMapArgs(commandArguments(GEOADD).key(key), memberCoordinateMap), BuilderFactory.LONG); } public final CommandObject geoadd(String key, GeoAddParams params, Map memberCoordinateMap) { return new CommandObject<>(addGeoCoordinateFlatMapArgs(commandArguments(GEOADD).key(key).addParams(params), memberCoordinateMap), BuilderFactory.LONG); } public final CommandObject geodist(String key, String member1, String member2) { return new CommandObject<>(commandArguments(GEODIST).key(key).add(member1).add(member2), BuilderFactory.DOUBLE); } public final CommandObject geodist(String key, String member1, String member2, GeoUnit unit) { return new CommandObject<>(commandArguments(GEODIST).key(key).add(member1).add(member2).add(unit), BuilderFactory.DOUBLE); } public final CommandObject> geohash(String key, String... members) { return new CommandObject<>(commandArguments(GEOHASH).key(key).addObjects((Object[]) members), BuilderFactory.STRING_LIST); } public final CommandObject> geopos(String key, String... members) { return new CommandObject<>(commandArguments(GEOPOS).key(key).addObjects((Object[]) members), BuilderFactory.GEO_COORDINATE_LIST); } public final CommandObject geoadd(byte[] key, double longitude, double latitude, byte[] member) { return new CommandObject<>(commandArguments(GEOADD).key(key).add(longitude).add(latitude).add(member), BuilderFactory.LONG); } public final CommandObject geoadd(byte[] key, Map memberCoordinateMap) { return new CommandObject<>(addGeoCoordinateFlatMapArgs(commandArguments(GEOADD).key(key), memberCoordinateMap), BuilderFactory.LONG); } public final CommandObject geoadd(byte[] key, GeoAddParams params, Map memberCoordinateMap) { return new CommandObject<>(addGeoCoordinateFlatMapArgs(commandArguments(GEOADD).key(key).addParams(params), memberCoordinateMap), BuilderFactory.LONG); } public final CommandObject geodist(byte[] key, byte[] member1, byte[] member2) { return new CommandObject<>(commandArguments(GEODIST).key(key).add(member1).add(member2), BuilderFactory.DOUBLE); } public final CommandObject geodist(byte[] key, byte[] member1, byte[] member2, GeoUnit unit) { return new CommandObject<>(commandArguments(GEODIST).key(key).add(member1).add(member2).add(unit), BuilderFactory.DOUBLE); } public final CommandObject> geohash(byte[] key, byte[]... members) { return new CommandObject<>(commandArguments(GEOHASH).key(key).addObjects((Object[]) members), BuilderFactory.BINARY_LIST); } public final CommandObject> geopos(byte[] key, byte[]... members) { return new CommandObject<>(commandArguments(GEOPOS).key(key).addObjects((Object[]) members), BuilderFactory.GEO_COORDINATE_LIST); } public final CommandObject> georadius(String key, double longitude, double latitude, double radius, GeoUnit unit) { return new CommandObject<>(commandArguments(GEORADIUS).key(key).add(longitude).add(latitude) .add(radius).add(unit), BuilderFactory.GEORADIUS_WITH_PARAMS_RESULT); } public final CommandObject> georadius(String key, double longitude, double latitude, double radius, GeoUnit unit, GeoRadiusParam param) { return new CommandObject<>(commandArguments(GEORADIUS).key(key).add(longitude).add(latitude) .add(radius).add(unit).addParams(param), BuilderFactory.GEORADIUS_WITH_PARAMS_RESULT); } public final CommandObject> georadiusReadonly(String key, double longitude, double latitude, double radius, GeoUnit unit) { return new CommandObject<>(commandArguments(GEORADIUS_RO).key(key).add(longitude).add(latitude) .add(radius).add(unit), BuilderFactory.GEORADIUS_WITH_PARAMS_RESULT); } public final CommandObject> georadiusReadonly(String key, double longitude, double latitude, double radius, GeoUnit unit, GeoRadiusParam param) { return new CommandObject<>(commandArguments(GEORADIUS_RO).key(key).add(longitude).add(latitude) .add(radius).add(unit).addParams(param), BuilderFactory.GEORADIUS_WITH_PARAMS_RESULT); } public final CommandObject georadiusStore(String key, double longitude, double latitude, double radius, GeoUnit unit, GeoRadiusParam param, GeoRadiusStoreParam storeParam) { return new CommandObject<>(commandArguments(GEORADIUS).key(key).add(longitude).add(latitude) .add(radius).add(unit).addParams(param).addParams(storeParam), BuilderFactory.LONG); } public final CommandObject> georadiusByMember(String key, String member, double radius, GeoUnit unit) { return new CommandObject<>(commandArguments(GEORADIUSBYMEMBER).key(key).add(member) .add(radius).add(unit), BuilderFactory.GEORADIUS_WITH_PARAMS_RESULT); } public final CommandObject> georadiusByMember(String key, String member, double radius, GeoUnit unit, GeoRadiusParam param) { return new CommandObject<>(commandArguments(GEORADIUSBYMEMBER).key(key).add(member) .add(radius).add(unit).addParams(param), BuilderFactory.GEORADIUS_WITH_PARAMS_RESULT); } public final CommandObject> georadiusByMemberReadonly(String key, String member, double radius, GeoUnit unit) { return new CommandObject<>(commandArguments(GEORADIUSBYMEMBER_RO).key(key).add(member) .add(radius).add(unit), BuilderFactory.GEORADIUS_WITH_PARAMS_RESULT); } public final CommandObject> georadiusByMemberReadonly(String key, String member, double radius, GeoUnit unit, GeoRadiusParam param) { return new CommandObject<>(commandArguments(GEORADIUSBYMEMBER_RO).key(key).add(member) .add(radius).add(unit).addParams(param), BuilderFactory.GEORADIUS_WITH_PARAMS_RESULT); } public final CommandObject georadiusByMemberStore(String key, String member, double radius, GeoUnit unit, GeoRadiusParam param, GeoRadiusStoreParam storeParam) { return new CommandObject<>(commandArguments(GEORADIUSBYMEMBER).key(key).add(member) .add(radius).add(unit).addParams(param).addParams(storeParam), BuilderFactory.LONG); } public final CommandObject> georadius(byte[] key, double longitude, double latitude, double radius, GeoUnit unit) { return new CommandObject<>(commandArguments(GEORADIUS).key(key).add(longitude).add(latitude) .add(radius).add(unit), BuilderFactory.GEORADIUS_WITH_PARAMS_RESULT); } public final CommandObject> georadius(byte[] key, double longitude, double latitude, double radius, GeoUnit unit, GeoRadiusParam param) { return new CommandObject<>(commandArguments(GEORADIUS).key(key).add(longitude).add(latitude) .add(radius).add(unit).addParams(param), BuilderFactory.GEORADIUS_WITH_PARAMS_RESULT); } public final CommandObject> georadiusReadonly(byte[] key, double longitude, double latitude, double radius, GeoUnit unit) { return new CommandObject<>(commandArguments(GEORADIUS_RO).key(key).add(longitude).add(latitude) .add(radius).add(unit), BuilderFactory.GEORADIUS_WITH_PARAMS_RESULT); } public final CommandObject> georadiusReadonly(byte[] key, double longitude, double latitude, double radius, GeoUnit unit, GeoRadiusParam param) { return new CommandObject<>(commandArguments(GEORADIUS_RO).key(key).add(longitude).add(latitude) .add(radius).add(unit).addParams(param), BuilderFactory.GEORADIUS_WITH_PARAMS_RESULT); } public final CommandObject georadiusStore(byte[] key, double longitude, double latitude, double radius, GeoUnit unit, GeoRadiusParam param, GeoRadiusStoreParam storeParam) { return new CommandObject<>(commandArguments(GEORADIUS).key(key).add(longitude).add(latitude) .add(radius).add(unit).addParams(param).addParams(storeParam), BuilderFactory.LONG); } public final CommandObject> georadiusByMember(byte[] key, byte[] member, double radius, GeoUnit unit) { return new CommandObject<>(commandArguments(GEORADIUSBYMEMBER).key(key).add(member) .add(radius).add(unit), BuilderFactory.GEORADIUS_WITH_PARAMS_RESULT); } public final CommandObject> georadiusByMember(byte[] key, byte[] member, double radius, GeoUnit unit, GeoRadiusParam param) { return new CommandObject<>(commandArguments(GEORADIUSBYMEMBER).key(key).add(member) .add(radius).add(unit).addParams(param), BuilderFactory.GEORADIUS_WITH_PARAMS_RESULT); } public final CommandObject> georadiusByMemberReadonly(byte[] key, byte[] member, double radius, GeoUnit unit) { return new CommandObject<>(commandArguments(GEORADIUSBYMEMBER_RO).key(key).add(member) .add(radius).add(unit), BuilderFactory.GEORADIUS_WITH_PARAMS_RESULT); } public final CommandObject> georadiusByMemberReadonly(byte[] key, byte[] member, double radius, GeoUnit unit, GeoRadiusParam param) { return new CommandObject<>(commandArguments(GEORADIUSBYMEMBER_RO).key(key).add(member) .add(radius).add(unit).addParams(param), BuilderFactory.GEORADIUS_WITH_PARAMS_RESULT); } public final CommandObject georadiusByMemberStore(byte[] key, byte[] member, double radius, GeoUnit unit, GeoRadiusParam param, GeoRadiusStoreParam storeParam) { return new CommandObject<>(commandArguments(GEORADIUSBYMEMBER).key(key).add(member) .add(radius).add(unit).addParams(param).addParams(storeParam), BuilderFactory.LONG); } public final CommandObject> geosearch(String key, String member, double radius, GeoUnit unit) { return new CommandObject<>(commandArguments(GEOSEARCH).key(key).add(FROMMEMBER).add(member) .add(BYRADIUS).add(radius).add(unit), BuilderFactory.GEORADIUS_WITH_PARAMS_RESULT); } public final CommandObject> geosearch(String key, GeoCoordinate coord, double radius, GeoUnit unit) { return new CommandObject<>(commandArguments(GEOSEARCH).key(key) .add(FROMLONLAT).add(coord.getLongitude()).add(coord.getLatitude()) .add(BYRADIUS).add(radius).add(unit), BuilderFactory.GEORADIUS_WITH_PARAMS_RESULT); } public final CommandObject> geosearch(String key, String member, double width, double height, GeoUnit unit) { return new CommandObject<>(commandArguments(GEOSEARCH).key(key).add(FROMMEMBER).add(member) .add(BYBOX).add(width).add(height).add(unit), BuilderFactory.GEORADIUS_WITH_PARAMS_RESULT); } public final CommandObject> geosearch(String key, GeoCoordinate coord, double width, double height, GeoUnit unit) { return new CommandObject<>(commandArguments(GEOSEARCH).key(key) .add(FROMLONLAT).add(coord.getLongitude()).add(coord.getLatitude()) .add(BYBOX).add(width).add(height).add(unit), BuilderFactory.GEORADIUS_WITH_PARAMS_RESULT); } public final CommandObject> geosearch(String key, GeoSearchParam params) { return new CommandObject<>(commandArguments(GEOSEARCH).key(key).addParams(params), BuilderFactory.GEORADIUS_WITH_PARAMS_RESULT); } public final CommandObject geosearchStore(String dest, String src, String member, double radius, GeoUnit unit) { return new CommandObject<>(commandArguments(GEOSEARCHSTORE).key(dest).add(src).add(FROMMEMBER).add(member) .add(BYRADIUS).add(radius).add(unit), BuilderFactory.LONG); } public final CommandObject geosearchStore(String dest, String src, GeoCoordinate coord, double radius, GeoUnit unit) { return new CommandObject<>(commandArguments(GEOSEARCHSTORE).key(dest).add(src).add(FROMLONLAT).add(coord.getLongitude()) .add(coord.getLatitude()).add(BYRADIUS).add(radius).add(unit), BuilderFactory.LONG); } public final CommandObject geosearchStore(String dest, String src, String member, double width, double height, GeoUnit unit) { return new CommandObject<>(commandArguments(GEOSEARCHSTORE).key(dest).add(src).add(FROMMEMBER).add(member) .add(BYBOX).add(width).add(height).add(unit), BuilderFactory.LONG); } public final CommandObject geosearchStore(String dest, String src, GeoCoordinate coord, double width, double height, GeoUnit unit) { return new CommandObject<>(commandArguments(GEOSEARCHSTORE).key(dest).add(src) .add(FROMLONLAT).add(coord.getLongitude()).add(coord.getLatitude()) .add(BYBOX).add(width).add(height).add(unit), BuilderFactory.LONG); } public final CommandObject geosearchStore(String dest, String src, GeoSearchParam params) { return new CommandObject<>(commandArguments(GEOSEARCHSTORE).key(dest).add(src).addParams(params), BuilderFactory.LONG); } public final CommandObject geosearchStoreStoreDist(String dest, String src, GeoSearchParam params) { return new CommandObject<>(commandArguments(GEOSEARCHSTORE).key(dest).add(src).addParams(params).add(STOREDIST), BuilderFactory.LONG); } public final CommandObject> geosearch(byte[] key, byte[] member, double radius, GeoUnit unit) { return new CommandObject<>(commandArguments(GEOSEARCH).key(key).add(FROMMEMBER).add(member) .add(BYRADIUS).add(radius).add(unit), BuilderFactory.GEORADIUS_WITH_PARAMS_RESULT); } public final CommandObject> geosearch(byte[] key, GeoCoordinate coord, double radius, GeoUnit unit) { return new CommandObject<>(commandArguments(GEOSEARCH).key(key) .add(FROMLONLAT).add(coord.getLongitude()).add(coord.getLatitude()) .add(BYRADIUS).add(radius).add(unit), BuilderFactory.GEORADIUS_WITH_PARAMS_RESULT); } public final CommandObject> geosearch(byte[] key, byte[] member, double width, double height, GeoUnit unit) { return new CommandObject<>(commandArguments(GEOSEARCH).key(key).add(FROMMEMBER).add(member) .add(BYBOX).add(width).add(height).add(unit), BuilderFactory.GEORADIUS_WITH_PARAMS_RESULT); } public final CommandObject> geosearch(byte[] key, GeoCoordinate coord, double width, double height, GeoUnit unit) { return new CommandObject<>(commandArguments(GEOSEARCH).key(key) .add(FROMLONLAT).add(coord.getLongitude()).add(coord.getLatitude()) .add(BYBOX).add(width).add(height).add(unit), BuilderFactory.GEORADIUS_WITH_PARAMS_RESULT); } public final CommandObject> geosearch(byte[] key, GeoSearchParam params) { return new CommandObject<>(commandArguments(GEOSEARCH).key(key).addParams(params), BuilderFactory.GEORADIUS_WITH_PARAMS_RESULT); } public final CommandObject geosearchStore(byte[] dest, byte[] src, byte[] member, double radius, GeoUnit unit) { return new CommandObject<>(commandArguments(GEOSEARCHSTORE).key(dest).add(src).add(FROMMEMBER).add(member) .add(BYRADIUS).add(radius).add(unit), BuilderFactory.LONG); } public final CommandObject geosearchStore(byte[] dest, byte[] src, GeoCoordinate coord, double radius, GeoUnit unit) { return new CommandObject<>(commandArguments(GEOSEARCHSTORE).key(dest).add(src) .add(FROMLONLAT).add(coord.getLongitude()).add(coord.getLatitude()) .add(BYRADIUS).add(radius).add(unit), BuilderFactory.LONG); } public final CommandObject geosearchStore(byte[] dest, byte[] src, byte[] member, double width, double height, GeoUnit unit) { return new CommandObject<>(commandArguments(GEOSEARCHSTORE).key(dest).add(src).add(FROMMEMBER).add(member) .add(BYBOX).add(width).add(height).add(unit), BuilderFactory.LONG); } public final CommandObject geosearchStore(byte[] dest, byte[] src, GeoCoordinate coord, double width, double height, GeoUnit unit) { return new CommandObject<>(commandArguments(GEOSEARCHSTORE).key(dest).add(src) .add(FROMLONLAT).add(coord.getLongitude()).add(coord.getLatitude()) .add(BYBOX).add(width).add(height).add(unit), BuilderFactory.LONG); } public final CommandObject geosearchStore(byte[] dest, byte[] src, GeoSearchParam params) { return new CommandObject<>(commandArguments(GEOSEARCHSTORE).key(dest).add(src).addParams(params), BuilderFactory.LONG); } public final CommandObject geosearchStoreStoreDist(byte[] dest, byte[] src, GeoSearchParam params) { return new CommandObject<>(commandArguments(GEOSEARCHSTORE).key(dest).add(src).addParams(params).add(STOREDIST), BuilderFactory.LONG); } // Geo commands // Hyper Log Log commands public final CommandObject pfadd(String key, String... elements) { return new CommandObject<>(commandArguments(PFADD).key(key).addObjects((Object[]) elements), BuilderFactory.LONG); } public final CommandObject pfmerge(String destkey, String... sourcekeys) { return new CommandObject<>(commandArguments(PFMERGE).key(destkey).keys((Object[]) sourcekeys), BuilderFactory.STRING); } public final CommandObject pfadd(byte[] key, byte[]... elements) { return new CommandObject<>(commandArguments(PFADD).key(key).addObjects((Object[]) elements), BuilderFactory.LONG); } public final CommandObject pfmerge(byte[] destkey, byte[]... sourcekeys) { return new CommandObject<>(commandArguments(PFMERGE).key(destkey).keys((Object[]) sourcekeys), BuilderFactory.STRING); } public final CommandObject pfcount(String key) { return new CommandObject<>(commandArguments(PFCOUNT).key(key), BuilderFactory.LONG); } public final CommandObject pfcount(String... keys) { return new CommandObject<>(commandArguments(PFCOUNT).keys((Object[]) keys), BuilderFactory.LONG); } public final CommandObject pfcount(byte[] key) { return new CommandObject<>(commandArguments(PFCOUNT).key(key), BuilderFactory.LONG); } public final CommandObject pfcount(byte[]... keys) { return new CommandObject<>(commandArguments(PFCOUNT).keys((Object[]) keys), BuilderFactory.LONG); } // Hyper Log Log commands // Stream commands public final CommandObject xadd(String key, StreamEntryID id, Map hash) { return new CommandObject<>(addFlatMapArgs(commandArguments(XADD).key(key).add(id == null ? StreamEntryID.NEW_ENTRY : id), hash), BuilderFactory.STREAM_ENTRY_ID); } public final CommandObject xadd(String key, XAddParams params, Map hash) { return new CommandObject<>(addFlatMapArgs(commandArguments(XADD).key(key).addParams(params), hash), BuilderFactory.STREAM_ENTRY_ID); } public final CommandObject xlen(String key) { return new CommandObject<>(commandArguments(XLEN).key(key), BuilderFactory.LONG); } public final CommandObject xadd(byte[] key, XAddParams params, Map hash) { return new CommandObject<>(addFlatMapArgs(commandArguments(XADD).key(key).addParams(params), hash), BuilderFactory.BINARY); } public final CommandObject xlen(byte[] key) { return new CommandObject<>(commandArguments(XLEN).key(key), BuilderFactory.LONG); } public final CommandObject> xrange(String key, StreamEntryID start, StreamEntryID end) { return new CommandObject<>(commandArguments(XRANGE).key(key).add(start == null ? "-" : start).add(end == null ? "+" : end), BuilderFactory.STREAM_ENTRY_LIST); } public final CommandObject> xrange(String key, StreamEntryID start, StreamEntryID end, int count) { return new CommandObject<>(commandArguments(XRANGE).key(key).add(start == null ? "-" : start).add(end == null ? "+" : end) .add(COUNT).add(count), BuilderFactory.STREAM_ENTRY_LIST); } public final CommandObject> xrevrange(String key, StreamEntryID end, StreamEntryID start) { return new CommandObject<>(commandArguments(XREVRANGE).key(key).add(end == null ? "+" : end).add(start == null ? "-" : start), BuilderFactory.STREAM_ENTRY_LIST); } public final CommandObject> xrevrange(String key, StreamEntryID end, StreamEntryID start, int count) { return new CommandObject<>(commandArguments(XREVRANGE).key(key).add(end == null ? "+" : end).add(start == null ? "-" : start) .add(COUNT).add(count), BuilderFactory.STREAM_ENTRY_LIST); } public final CommandObject> xrange(String key, String start, String end) { return new CommandObject<>(commandArguments(XRANGE).key(key).add(start).add(end), BuilderFactory.STREAM_ENTRY_LIST); } public final CommandObject> xrange(String key, String start, String end, int count) { return new CommandObject<>(commandArguments(XRANGE).key(key).add(start).add(end).add(COUNT).add(count), BuilderFactory.STREAM_ENTRY_LIST); } public final CommandObject> xrevrange(String key, String end, String start) { return new CommandObject<>(commandArguments(XREVRANGE).key(key).add(end).add(start), BuilderFactory.STREAM_ENTRY_LIST); } public final CommandObject> xrevrange(String key, String end, String start, int count) { return new CommandObject<>(commandArguments(XREVRANGE).key(key).add(end).add(start).add(COUNT).add(count), BuilderFactory.STREAM_ENTRY_LIST); } public final CommandObject> xrange(byte[] key, byte[] start, byte[] end) { return new CommandObject<>(commandArguments(XRANGE).key(key).add(start == null ? "-" : start).add(end == null ? "+" : end), BuilderFactory.RAW_OBJECT_LIST); } public final CommandObject> xrange(byte[] key, byte[] start, byte[] end, int count) { return new CommandObject<>(commandArguments(XRANGE).key(key).add(start == null ? "-" : start).add(end == null ? "+" : end) .add(COUNT).add(count), BuilderFactory.RAW_OBJECT_LIST); } public final CommandObject> xrevrange(byte[] key, byte[] end, byte[] start) { return new CommandObject<>(commandArguments(XREVRANGE).key(key).add(end == null ? "+" : end).add(start == null ? "-" : start), BuilderFactory.RAW_OBJECT_LIST); } public final CommandObject> xrevrange(byte[] key, byte[] end, byte[] start, int count) { return new CommandObject<>(commandArguments(XREVRANGE).key(key).add(end == null ? "+" : end).add(start == null ? "-" : start) .add(COUNT).add(count), BuilderFactory.RAW_OBJECT_LIST); } public final CommandObject xack(String key, String group, StreamEntryID... ids) { return new CommandObject<>(commandArguments(XACK).key(key).add(group).addObjects((Object[]) ids), BuilderFactory.LONG); } public final CommandObject> xackdel(String key, String group, StreamEntryID... ids) { return new CommandObject<>(commandArguments(XACKDEL).key(key).add(group).add("IDS").add(ids.length).addObjects((Object[]) ids), BuilderFactory.STREAM_ENTRY_DELETION_RESULT_LIST); } public final CommandObject> xackdel(String key, String group, StreamDeletionPolicy trimMode, StreamEntryID... ids) { return new CommandObject<>(commandArguments(XACKDEL).key(key).add(group).add(trimMode).add("IDS").add(ids.length).addObjects((Object[]) ids), BuilderFactory.STREAM_ENTRY_DELETION_RESULT_LIST); } public final CommandObject xack(byte[] key, byte[] group, byte[]... ids) { return new CommandObject<>(commandArguments(XACK).key(key).add(group).addObjects((Object[]) ids), BuilderFactory.LONG); } public final CommandObject> xackdel(byte[] key, byte[] group, byte[]... ids) { return new CommandObject<>(commandArguments(XACKDEL).key(key).add(group).add("IDS").add(ids.length).addObjects((Object[]) ids), BuilderFactory.STREAM_ENTRY_DELETION_RESULT_LIST); } public final CommandObject> xackdel(byte[] key, byte[] group, StreamDeletionPolicy trimMode, byte[]... ids) { return new CommandObject<>(commandArguments(XACKDEL).key(key).add(group).add(trimMode).add("IDS").add(ids.length).addObjects((Object[]) ids), BuilderFactory.STREAM_ENTRY_DELETION_RESULT_LIST); } public final CommandObject xgroupCreate(String key, String groupName, StreamEntryID id, boolean makeStream) { CommandArguments args = commandArguments(XGROUP).add(CREATE).key(key) .add(groupName).add(id == null ? "0-0" : id); if (makeStream) args.add(MKSTREAM); return new CommandObject<>(args, BuilderFactory.STRING); } public final CommandObject xgroupSetID(String key, String groupName, StreamEntryID id) { return new CommandObject<>(commandArguments(XGROUP).add(SETID) .key(key).add(groupName).add(id), BuilderFactory.STRING); } public final CommandObject xgroupDestroy(String key, String groupName) { return new CommandObject<>(commandArguments(XGROUP).add(DESTROY) .key(key).add(groupName), BuilderFactory.LONG); } public final CommandObject xgroupCreateConsumer(String key, String groupName, String consumerName) { return new CommandObject<>(commandArguments(XGROUP).add(CREATECONSUMER) .key(key).add(groupName).add(consumerName), BuilderFactory.BOOLEAN); } public final CommandObject xgroupDelConsumer(String key, String groupName, String consumerName) { return new CommandObject<>(commandArguments(XGROUP).add(DELCONSUMER) .key(key).add(groupName).add(consumerName), BuilderFactory.LONG); } public final CommandObject xgroupCreate(byte[] key, byte[] groupName, byte[] id, boolean makeStream) { CommandArguments args = commandArguments(XGROUP).add(CREATE).key(key) .add(groupName).add(id); if (makeStream) args.add(MKSTREAM); return new CommandObject<>(args, BuilderFactory.STRING); } public final CommandObject xgroupSetID(byte[] key, byte[] groupName, byte[] id) { return new CommandObject<>(commandArguments(XGROUP).add(SETID) .key(key).add(groupName).add(id), BuilderFactory.STRING); } public final CommandObject xgroupDestroy(byte[] key, byte[] groupName) { return new CommandObject<>(commandArguments(XGROUP).add(DESTROY) .key(key).add(groupName), BuilderFactory.LONG); } public final CommandObject xgroupCreateConsumer(byte[] key, byte[] groupName, byte[] consumerName) { return new CommandObject<>(commandArguments(XGROUP).add(CREATECONSUMER) .key(key).add(groupName).add(consumerName), BuilderFactory.BOOLEAN); } public final CommandObject xgroupDelConsumer(byte[] key, byte[] groupName, byte[] consumerName) { return new CommandObject<>(commandArguments(XGROUP).add(DELCONSUMER) .key(key).add(groupName).add(consumerName), BuilderFactory.LONG); } public final CommandObject xdel(String key, StreamEntryID... ids) { return new CommandObject<>(commandArguments(XDEL).key(key).addObjects((Object[]) ids), BuilderFactory.LONG); } public final CommandObject> xdelex(String key, StreamEntryID... ids) { return new CommandObject<>(commandArguments(XDELEX).key(key).add("IDS").add(ids.length).addObjects((Object[]) ids), BuilderFactory.STREAM_ENTRY_DELETION_RESULT_LIST); } public final CommandObject> xdelex(String key, StreamDeletionPolicy trimMode, StreamEntryID... ids) { return new CommandObject<>(commandArguments(XDELEX).key(key).add(trimMode).add("IDS").add(ids.length).addObjects((Object[]) ids), BuilderFactory.STREAM_ENTRY_DELETION_RESULT_LIST); } public final CommandObject xtrim(String key, long maxLen, boolean approximate) { CommandArguments args = commandArguments(XTRIM).key(key).add(MAXLEN); if (approximate) args.add(Protocol.BYTES_TILDE); args.add(maxLen); return new CommandObject<>(args, BuilderFactory.LONG); } public final CommandObject xtrim(String key, XTrimParams params) { return new CommandObject<>(commandArguments(XTRIM).key(key).addParams(params), BuilderFactory.LONG); } public final CommandObject xdel(byte[] key, byte[]... ids) { return new CommandObject<>(commandArguments(XDEL).key(key).addObjects((Object[]) ids), BuilderFactory.LONG); } public final CommandObject> xdelex(byte[] key, byte[]... ids) { return new CommandObject<>(commandArguments(XDELEX).key(key).add("IDS").add(ids.length).addObjects((Object[]) ids), BuilderFactory.STREAM_ENTRY_DELETION_RESULT_LIST); } public final CommandObject> xdelex(byte[] key, StreamDeletionPolicy trimMode, byte[]... ids) { return new CommandObject<>(commandArguments(XDELEX).key(key).add(trimMode).add("IDS").add(ids.length).addObjects((Object[]) ids), BuilderFactory.STREAM_ENTRY_DELETION_RESULT_LIST); } public final CommandObject xtrim(byte[] key, long maxLen, boolean approximateLength) { CommandArguments args = commandArguments(XTRIM).key(key).add(MAXLEN); if (approximateLength) args.add(Protocol.BYTES_TILDE); args.add(maxLen); return new CommandObject<>(args, BuilderFactory.LONG); } public final CommandObject xtrim(byte[] key, XTrimParams params) { return new CommandObject<>(commandArguments(XTRIM).key(key).addParams(params), BuilderFactory.LONG); } public final CommandObject xpending(String key, String groupName) { return new CommandObject<>(commandArguments(XPENDING).key(key).add(groupName), BuilderFactory.STREAM_PENDING_SUMMARY); } public final CommandObject> xpending(String key, String groupName, XPendingParams params) { return new CommandObject<>(commandArguments(XPENDING).key(key).add(groupName) .addParams(params), BuilderFactory.STREAM_PENDING_ENTRY_LIST); } public final CommandObject xpending(byte[] key, byte[] groupName) { return new CommandObject<>(commandArguments(XPENDING).key(key).add(groupName), BuilderFactory.RAW_OBJECT); } public final CommandObject> xpending(byte[] key, byte[] groupName, XPendingParams params) { return new CommandObject<>(commandArguments(XPENDING).key(key).add(groupName) .addParams(params), BuilderFactory.RAW_OBJECT_LIST); } public final CommandObject> xclaim(String key, String group, String consumerName, long minIdleTime, XClaimParams params, StreamEntryID... ids) { return new CommandObject<>(commandArguments(XCLAIM).key(key).add(group) .add(consumerName).add(minIdleTime).addObjects((Object[]) ids).addParams(params), BuilderFactory.STREAM_ENTRY_LIST); } public final CommandObject> xclaimJustId(String key, String group, String consumerName, long minIdleTime, XClaimParams params, StreamEntryID... ids) { return new CommandObject<>(commandArguments(XCLAIM).key(key).add(group) .add(consumerName).add(minIdleTime).addObjects((Object[]) ids).addParams(params) .add(JUSTID), BuilderFactory.STREAM_ENTRY_ID_LIST); } public final CommandObject>> xautoclaim(String key, String group, String consumerName, long minIdleTime, StreamEntryID start, XAutoClaimParams params) { return new CommandObject<>(commandArguments(XAUTOCLAIM).key(key).add(group) .add(consumerName).add(minIdleTime).add(start).addParams(params), BuilderFactory.STREAM_AUTO_CLAIM_RESPONSE); } public final CommandObject>> xautoclaimJustId( String key, String group, String consumerName, long minIdleTime, StreamEntryID start, XAutoClaimParams params) { return new CommandObject<>(commandArguments(XAUTOCLAIM).key(key).add(group) .add(consumerName).add(minIdleTime).add(start).addParams(params) .add(JUSTID), BuilderFactory.STREAM_AUTO_CLAIM_JUSTID_RESPONSE); } public final CommandObject> xclaim(byte[] key, byte[] group, byte[] consumerName, long minIdleTime, XClaimParams params, byte[]... ids) { return new CommandObject<>(commandArguments(XCLAIM).key(key).add(group) .add(consumerName).add(minIdleTime).addObjects((Object[]) ids).addParams(params), BuilderFactory.BINARY_LIST); } public final CommandObject> xclaimJustId(byte[] key, byte[] group, byte[] consumerName, long minIdleTime, XClaimParams params, byte[]... ids) { return new CommandObject<>(commandArguments(XCLAIM).key(key).add(group) .add(consumerName).add(minIdleTime).addObjects((Object[]) ids).addParams(params) .add(JUSTID), BuilderFactory.BINARY_LIST); } public final CommandObject> xautoclaim(byte[] key, byte[] groupName, byte[] consumerName, long minIdleTime, byte[] start, XAutoClaimParams params) { return new CommandObject<>(commandArguments(XAUTOCLAIM).key(key).add(groupName) .add(consumerName).add(minIdleTime).add(start).addParams(params), BuilderFactory.RAW_OBJECT_LIST); } public final CommandObject> xautoclaimJustId(byte[] key, byte[] groupName, byte[] consumerName, long minIdleTime, byte[] start, XAutoClaimParams params) { return new CommandObject<>(commandArguments(XAUTOCLAIM).key(key).add(groupName) .add(consumerName).add(minIdleTime).add(start).addParams(params) .add(JUSTID), BuilderFactory.RAW_OBJECT_LIST); } public final CommandObject xinfoStream(String key) { return new CommandObject<>(commandArguments(XINFO).add(STREAM).key(key), BuilderFactory.STREAM_INFO); } public final CommandObject xinfoStream(byte[] key) { return new CommandObject<>(commandArguments(XINFO).add(STREAM).key(key), BuilderFactory.RAW_OBJECT); } public final CommandObject xinfoStreamFull(String key) { return new CommandObject<>(commandArguments(XINFO).add(STREAM).key(key).add(FULL), BuilderFactory.STREAM_FULL_INFO); } public final CommandObject xinfoStreamFull(String key, int count) { return new CommandObject<>(commandArguments(XINFO).add(STREAM).key(key).add(FULL).add(COUNT).add(count), BuilderFactory.STREAM_FULL_INFO); } public final CommandObject xinfoStreamFull(byte[] key, int count) { return new CommandObject<>(commandArguments(XINFO).add(STREAM).key(key).add(FULL).add(COUNT).add(count), BuilderFactory.RAW_OBJECT); } public final CommandObject xinfoStreamFull(byte[] key) { return new CommandObject<>(commandArguments(XINFO).add(STREAM).key(key).add(FULL), BuilderFactory.RAW_OBJECT); } public final CommandObject> xinfoGroups(String key) { return new CommandObject<>(commandArguments(XINFO).add(GROUPS).key(key), BuilderFactory.STREAM_GROUP_INFO_LIST); } public final CommandObject> xinfoGroups(byte[] key) { return new CommandObject<>(commandArguments(XINFO).add(GROUPS).key(key), BuilderFactory.RAW_OBJECT_LIST); } /** * @deprecated Use {@link #xinfoConsumers2(java.lang.String, java.lang.String)}. */ @Deprecated public final CommandObject> xinfoConsumers(String key, String group) { return new CommandObject<>(commandArguments(XINFO).add(CONSUMERS).key(key).add(group), BuilderFactory.STREAM_CONSUMERS_INFO_LIST); } public final CommandObject> xinfoConsumers2(String key, String group) { return new CommandObject<>(commandArguments(XINFO).add(CONSUMERS).key(key).add(group), BuilderFactory.STREAM_CONSUMER_INFO_LIST); } public final CommandObject> xinfoConsumers(byte[] key, byte[] group) { return new CommandObject<>(commandArguments(XINFO).add(CONSUMERS).key(key).add(group), BuilderFactory.RAW_OBJECT_LIST); } public final CommandObject>>> xread( XReadParams xReadParams, Map streams) { CommandArguments args = commandArguments(XREAD).addParams(xReadParams).add(STREAMS); Set> entrySet = streams.entrySet(); entrySet.forEach(entry -> args.key(entry.getKey())); entrySet.forEach(entry -> args.add(entry.getValue())); return new CommandObject<>(args, BuilderFactory.STREAM_READ_RESPONSE); } public final CommandObject>> xreadAsMap( XReadParams xReadParams, Map streams) { CommandArguments args = commandArguments(XREAD).addParams(xReadParams).add(STREAMS); Set> entrySet = streams.entrySet(); entrySet.forEach(entry -> args.key(entry.getKey())); entrySet.forEach(entry -> args.add(entry.getValue())); return new CommandObject<>(args, BuilderFactory.STREAM_READ_MAP_RESPONSE); } public final CommandObject>>> xreadGroup( String groupName, String consumer, XReadGroupParams xReadGroupParams, Map streams) { CommandArguments args = commandArguments(XREADGROUP) .add(GROUP).add(groupName).add(consumer) .addParams(xReadGroupParams).add(STREAMS); Set> entrySet = streams.entrySet(); entrySet.forEach(entry -> args.key(entry.getKey())); entrySet.forEach(entry -> args.add(entry.getValue())); return new CommandObject<>(args, BuilderFactory.STREAM_READ_RESPONSE); } public final CommandObject>> xreadGroupAsMap( String groupName, String consumer, XReadGroupParams xReadGroupParams, Map streams) { CommandArguments args = commandArguments(XREADGROUP) .add(GROUP).add(groupName).add(consumer) .addParams(xReadGroupParams).add(STREAMS); Set> entrySet = streams.entrySet(); entrySet.forEach(entry -> args.key(entry.getKey())); entrySet.forEach(entry -> args.add(entry.getValue())); return new CommandObject<>(args, BuilderFactory.STREAM_READ_MAP_RESPONSE); } /** * @deprecated As of Jedis 6.1.0, replaced by {@link #xreadBinary(XReadParams, Map)} or * {@link #xreadBinaryAsMap(XReadParams, Map)} for type safety and better stream entry parsing. */ @Deprecated public final CommandObject> xread(XReadParams xReadParams, Map.Entry... streams) { CommandArguments args = commandArguments(XREAD).addParams(xReadParams).add(STREAMS); for (Map.Entry entry : streams) { args.key(entry.getKey()); } for (Map.Entry entry : streams) { args.add(entry.getValue()); } return new CommandObject<>(args, BuilderFactory.RAW_OBJECT_LIST); } public final CommandObject>>> xreadBinary( XReadParams xReadParams, Map.Entry... streams) { CommandArguments args = commandArguments(XREAD).addParams(xReadParams).add(STREAMS); for (Map.Entry entry : streams) { args.key(entry.getKey()); } for (Map.Entry entry : streams) { args.add(entry.getValue()); } return new CommandObject<>(args, BuilderFactory.STREAM_READ_BINARY_RESPONSE); } public final CommandObject>> xreadBinaryAsMap( XReadParams xReadParams, Map.Entry... streams) { CommandArguments args = commandArguments(XREAD).addParams(xReadParams).add(STREAMS); for (Map.Entry entry : streams) { args.key(entry.getKey()); } for (Map.Entry entry : streams) { args.add(entry.getValue()); } return new CommandObject<>(args, BuilderFactory.STREAM_READ_BINARY_MAP_RESPONSE); } /** * @deprecated As of Jedis 6.1.0, use {@link #xreadGroupBinary(byte[], byte[], XReadGroupParams, Map)} or * {@link #xreadGroupBinaryAsMap(byte[], byte[], XReadGroupParams, Map)} instead. */ @Deprecated public final CommandObject> xreadGroup(byte[] groupName, byte[] consumer, XReadGroupParams xReadGroupParams, Map.Entry... streams) { CommandArguments args = commandArguments(XREADGROUP) .add(GROUP).add(groupName).add(consumer) .addParams(xReadGroupParams).add(STREAMS); for (Map.Entry entry : streams) { args.key(entry.getKey()); } for (Map.Entry entry : streams) { args.add(entry.getValue()); } return new CommandObject<>(args, BuilderFactory.RAW_OBJECT_LIST); } public final CommandObject>>> xreadGroupBinary( byte[] groupName, byte[] consumer, XReadGroupParams xReadGroupParams, Map.Entry... streams) { CommandArguments args = commandArguments(XREADGROUP) .add(GROUP).add(groupName).add(consumer) .addParams(xReadGroupParams).add(STREAMS); for (Map.Entry entry : streams) { args.key(entry.getKey()); } for (Map.Entry entry : streams) { args.add(entry.getValue()); } return new CommandObject<>(args, BuilderFactory.STREAM_READ_BINARY_RESPONSE); } public final CommandObject>> xreadGroupBinaryAsMap( byte[] groupName, byte[] consumer, XReadGroupParams xReadGroupParams, Map.Entry... streams) { CommandArguments args = commandArguments(XREADGROUP) .add(GROUP).add(groupName).add(consumer) .addParams(xReadGroupParams).add(STREAMS); for (Map.Entry entry : streams) { args.key(entry.getKey()); } for (Map.Entry entry : streams) { args.add(entry.getValue()); } return new CommandObject<>(args, BuilderFactory.STREAM_READ_BINARY_MAP_RESPONSE); } public final CommandObject>>> xreadBinary( XReadParams xReadParams, Map streams) { CommandArguments args = commandArguments(XREAD).addParams(xReadParams).add(STREAMS); Set> entrySet = streams.entrySet(); entrySet.forEach(entry -> args.key(entry.getKey())); entrySet.forEach(entry -> args.add(entry.getValue())); return new CommandObject<>(args, BuilderFactory.STREAM_READ_BINARY_RESPONSE); } public final CommandObject>> xreadBinaryAsMap( XReadParams xReadParams, Map streams) { CommandArguments args = commandArguments(XREAD).addParams(xReadParams).add(STREAMS); Set> entrySet = streams.entrySet(); entrySet.forEach(entry -> args.key(entry.getKey())); entrySet.forEach(entry -> args.add(entry.getValue())); return new CommandObject<>(args, BuilderFactory.STREAM_READ_BINARY_MAP_RESPONSE); } public final CommandObject>>> xreadGroupBinary( byte[] groupName, byte[] consumer, XReadGroupParams xReadGroupParams, Map streams) { CommandArguments args = commandArguments(XREADGROUP) .add(GROUP).add(groupName).add(consumer) .addParams(xReadGroupParams).add(STREAMS); Set> entrySet = streams.entrySet(); entrySet.forEach(entry -> args.key(entry.getKey())); entrySet.forEach(entry -> args.add(entry.getValue())); return new CommandObject<>(args, BuilderFactory.STREAM_READ_BINARY_RESPONSE); } public final CommandObject>> xreadGroupBinaryAsMap( byte[] groupName, byte[] consumer, XReadGroupParams xReadGroupParams, Map streams) { CommandArguments args = commandArguments(XREADGROUP) .add(GROUP).add(groupName).add(consumer) .addParams(xReadGroupParams).add(STREAMS); Set> entrySet = streams.entrySet(); entrySet.forEach(entry -> args.key(entry.getKey())); entrySet.forEach(entry -> args.add(entry.getValue())); return new CommandObject<>(args, BuilderFactory.STREAM_READ_BINARY_MAP_RESPONSE); } public final CommandObject xcfgset(String key, XCfgSetParams params) { return new CommandObject<>(commandArguments(XCFGSET).key(key).addParams(params), BuilderFactory.STRING); } public final CommandObject xcfgset(byte[] key, XCfgSetParams params) { return new CommandObject<>(commandArguments(XCFGSET).key(key).addParams(params), BuilderFactory.BINARY); } // Stream commands // Scripting commands public final CommandObject eval(String script) { return new CommandObject<>(commandArguments(EVAL).add(script).add(0), BuilderFactory.AGGRESSIVE_ENCODED_OBJECT); } public final CommandObject eval(String script, String sampleKey) { return new CommandObject<>(commandArguments(EVAL).add(script).add(0).addHashSlotKey(sampleKey), BuilderFactory.AGGRESSIVE_ENCODED_OBJECT); } public final CommandObject eval(String script, int keyCount, String... params) { return new CommandObject<>(commandArguments(EVAL).add(script).add(keyCount) .addObjects((Object[]) params).addHashSlotKeys(Arrays.copyOf(params, keyCount)), BuilderFactory.AGGRESSIVE_ENCODED_OBJECT); } public final CommandObject eval(String script, List keys, List args) { return new CommandObject<>(commandArguments(EVAL).add(script).add(keys.size()) .keys(keys).addObjects(args), BuilderFactory.AGGRESSIVE_ENCODED_OBJECT); } public final CommandObject evalReadonly(String script, List keys, List args) { return new CommandObject<>(commandArguments(EVAL_RO).add(script).add(keys.size()) .keys(keys).addObjects(args), BuilderFactory.AGGRESSIVE_ENCODED_OBJECT); } public final CommandObject eval(byte[] script) { return new CommandObject<>(commandArguments(EVAL).add(script).add(0), BuilderFactory.RAW_OBJECT); } public final CommandObject eval(byte[] script, byte[] sampleKey) { return new CommandObject<>(commandArguments(EVAL).add(script).add(0).addHashSlotKey(sampleKey), BuilderFactory.RAW_OBJECT); } public final CommandObject eval(byte[] script, int keyCount, byte[]... params) { return new CommandObject<>(commandArguments(EVAL).add(script).add(keyCount) .addObjects((Object[]) params).addHashSlotKeys(Arrays.copyOf(params, keyCount)), BuilderFactory.RAW_OBJECT); } public final CommandObject eval(byte[] script, List keys, List args) { return new CommandObject<>(commandArguments(EVAL).add(script).add(keys.size()) .keys(keys).addObjects(args), BuilderFactory.RAW_OBJECT); } public final CommandObject evalReadonly(byte[] script, List keys, List args) { return new CommandObject<>(commandArguments(EVAL_RO).add(script).add(keys.size()) .keys(keys).addObjects(args), BuilderFactory.RAW_OBJECT); } public final CommandObject evalsha(String sha1) { return new CommandObject<>(commandArguments(EVALSHA).add(sha1).add(0), BuilderFactory.AGGRESSIVE_ENCODED_OBJECT); } public final CommandObject evalsha(String sha1, String sampleKey) { return new CommandObject<>(commandArguments(EVALSHA).add(sha1).add(0).addHashSlotKey(sampleKey), BuilderFactory.AGGRESSIVE_ENCODED_OBJECT); } public final CommandObject evalsha(String sha1, int keyCount, String... params) { return new CommandObject<>(commandArguments(EVALSHA).add(sha1).add(keyCount) .addObjects((Object[]) params).addHashSlotKeys(Arrays.copyOf(params, keyCount)), BuilderFactory.AGGRESSIVE_ENCODED_OBJECT); } public final CommandObject evalsha(String sha1, List keys, List args) { return new CommandObject<>(commandArguments(EVALSHA).add(sha1).add(keys.size()) .keys(keys).addObjects(args), BuilderFactory.AGGRESSIVE_ENCODED_OBJECT); } public final CommandObject evalshaReadonly(String sha1, List keys, List args) { return new CommandObject<>(commandArguments(EVALSHA_RO).add(sha1).add(keys.size()) .keys(keys).addObjects(args), BuilderFactory.AGGRESSIVE_ENCODED_OBJECT); } public final CommandObject evalsha(byte[] sha1) { return new CommandObject<>(commandArguments(EVALSHA).add(sha1).add(0), BuilderFactory.RAW_OBJECT); } public final CommandObject evalsha(byte[] sha1, byte[] sampleKey) { return new CommandObject<>(commandArguments(EVALSHA).add(sha1).add(0).addHashSlotKey(sampleKey), BuilderFactory.RAW_OBJECT); } public final CommandObject evalsha(byte[] sha1, int keyCount, byte[]... params) { return new CommandObject<>(commandArguments(EVALSHA).add(sha1).add(keyCount) .addObjects((Object[]) params).addHashSlotKeys(Arrays.copyOf(params, keyCount)), BuilderFactory.RAW_OBJECT); } public final CommandObject evalsha(byte[] sha1, List keys, List args) { return new CommandObject<>(commandArguments(EVALSHA).add(sha1).add(keys.size()) .keys(keys).addObjects(args), BuilderFactory.RAW_OBJECT); } public final CommandObject evalshaReadonly(byte[] sha1, List keys, List args) { return new CommandObject<>(commandArguments(EVALSHA_RO).add(sha1).add(keys.size()) .keys(keys).addObjects(args), BuilderFactory.RAW_OBJECT); } public final CommandObject> scriptExists(List sha1s) { return new CommandObject<>(commandArguments(SCRIPT).add(Keyword.EXISTS).addObjects(sha1s), BuilderFactory.BOOLEAN_LIST); } public final CommandObject> scriptExists(String sampleKey, String... sha1s) { return new CommandObject<>(commandArguments(SCRIPT).add(Keyword.EXISTS).addObjects((Object[]) sha1s) .addHashSlotKey(sampleKey), BuilderFactory.BOOLEAN_LIST); } public final CommandObject scriptLoad(String script) { return new CommandObject<>(commandArguments(SCRIPT).add(LOAD).add(script), BuilderFactory.STRING); } public final CommandObject scriptLoad(String script, String sampleKey) { return new CommandObject<>(commandArguments(SCRIPT).add(LOAD).add(script).addHashSlotKey(sampleKey), BuilderFactory.STRING); } private final CommandObject SCRIPT_FLUSH_COMMAND_OBJECT = new CommandObject<>(commandArguments(SCRIPT).add(FLUSH), BuilderFactory.STRING); public final CommandObject scriptFlush() { return SCRIPT_FLUSH_COMMAND_OBJECT; } public final CommandObject scriptFlush(String sampleKey) { return new CommandObject<>(commandArguments(SCRIPT).add(FLUSH).addHashSlotKey(sampleKey), BuilderFactory.STRING); } public final CommandObject scriptFlush(String sampleKey, FlushMode flushMode) { return new CommandObject<>(commandArguments(SCRIPT).add(FLUSH).add(flushMode).addHashSlotKey(sampleKey), BuilderFactory.STRING); } private final CommandObject SCRIPT_KILL_COMMAND_OBJECT = new CommandObject<>(commandArguments(SCRIPT).add(KILL), BuilderFactory.STRING); public final CommandObject scriptKill() { return SCRIPT_KILL_COMMAND_OBJECT; } public final CommandObject scriptKill(String sampleKey) { return new CommandObject<>(commandArguments(SCRIPT).add(KILL).addHashSlotKey(sampleKey), BuilderFactory.STRING); } public final CommandObject> scriptExists(byte[] sampleKey, byte[]... sha1s) { return new CommandObject<>(commandArguments(SCRIPT).add(Keyword.EXISTS).addObjects((Object[]) sha1s) .addHashSlotKey(sampleKey), BuilderFactory.BOOLEAN_LIST); } public final CommandObject scriptLoad(byte[] script, byte[] sampleKey) { return new CommandObject<>(commandArguments(SCRIPT).add(LOAD).add(script).addHashSlotKey(sampleKey), BuilderFactory.BINARY); } public final CommandObject scriptFlush(byte[] sampleKey) { return new CommandObject<>(commandArguments(SCRIPT).add(FLUSH).addHashSlotKey(sampleKey), BuilderFactory.STRING); } public final CommandObject scriptFlush(byte[] sampleKey, FlushMode flushMode) { return new CommandObject<>(commandArguments(SCRIPT).add(FLUSH).add(flushMode).addHashSlotKey(sampleKey), BuilderFactory.STRING); } public final CommandObject scriptKill(byte[] sampleKey) { return new CommandObject<>(commandArguments(SCRIPT).add(KILL).addHashSlotKey(sampleKey), BuilderFactory.STRING); } private final CommandObject SLOWLOG_RESET_COMMAND_OBJECT = new CommandObject<>(commandArguments(SLOWLOG).add(Keyword.RESET), BuilderFactory.STRING); public final CommandObject slowlogReset() { return SLOWLOG_RESET_COMMAND_OBJECT; } // Hotkeys commands public CommandObject hotkeysStart(HotkeysParams params) { return new CommandObject<>(commandArguments(HOTKEYS).add(Keyword.START).addParams(params), BuilderFactory.STRING); } public CommandObject hotkeysStop() { return new CommandObject<>(commandArguments(HOTKEYS).add(Keyword.STOP), BuilderFactory.STRING); } public CommandObject hotkeysReset() { return new CommandObject<>(commandArguments(HOTKEYS).add(Keyword.RESET), BuilderFactory.STRING); } public CommandObject hotkeysGet() { return new CommandObject<>(commandArguments(HOTKEYS).add(Keyword.GET), HotkeysInfo.HOTKEYS_INFO_BUILDER); } // End Hotkeys commands public final CommandObject fcall(String name, List keys, List args) { return new CommandObject<>(commandArguments(FCALL).add(name).add(keys.size()) .keys(keys).addObjects(args), BuilderFactory.AGGRESSIVE_ENCODED_OBJECT); } public final CommandObject fcallReadonly(String name, List keys, List args) { return new CommandObject<>(commandArguments(FCALL_RO).add(name).add(keys.size()) .keys(keys).addObjects(args), BuilderFactory.AGGRESSIVE_ENCODED_OBJECT); } public final CommandObject functionDelete(String libraryName) { return new CommandObject<>(commandArguments(FUNCTION).add(DELETE).add(libraryName), BuilderFactory.STRING); } public final CommandObject> functionList() { return new CommandObject<>(commandArguments(FUNCTION).add(LIST), LibraryInfo.LIBRARY_INFO_LIST); } public final CommandObject> functionList(String libraryNamePattern) { return new CommandObject<>(commandArguments(FUNCTION).add(LIST).add(LIBRARYNAME) .add(libraryNamePattern), LibraryInfo.LIBRARY_INFO_LIST); } public final CommandObject> functionListWithCode() { return new CommandObject<>(commandArguments(FUNCTION).add(LIST).add(WITHCODE), LibraryInfo.LIBRARY_INFO_LIST); } public final CommandObject> functionListWithCode(String libraryNamePattern) { return new CommandObject<>(commandArguments(FUNCTION).add(LIST).add(LIBRARYNAME) .add(libraryNamePattern).add(WITHCODE), LibraryInfo.LIBRARY_INFO_LIST); } public final CommandObject functionLoad(String functionCode) { return new CommandObject<>(commandArguments(FUNCTION).add(LOAD).add(functionCode), BuilderFactory.STRING); } public final CommandObject functionLoadReplace(String functionCode) { return new CommandObject<>(commandArguments(FUNCTION).add(LOAD).add(REPLACE).add(functionCode), BuilderFactory.STRING); } public final CommandObject functionStats() { return new CommandObject<>(commandArguments(FUNCTION).add(STATS), FunctionStats.FUNCTION_STATS_BUILDER); } public final CommandObject functionStatsBinary() { return new CommandObject<>(commandArguments(FUNCTION).add(STATS), BuilderFactory.RAW_OBJECT); } public final CommandObject functionFlush() { return new CommandObject<>(commandArguments(FUNCTION).add(FLUSH), BuilderFactory.STRING); } public final CommandObject functionFlush(FlushMode mode) { return new CommandObject<>(commandArguments(FUNCTION).add(FLUSH).add(mode), BuilderFactory.STRING); } public final CommandObject functionKill() { return new CommandObject<>(commandArguments(FUNCTION).add(KILL), BuilderFactory.STRING); } public final CommandObject fcall(byte[] name, List keys, List args) { return new CommandObject<>(commandArguments(FCALL).add(name).add(keys.size()) .keys(keys).addObjects(args), BuilderFactory.RAW_OBJECT); } public final CommandObject fcallReadonly(byte[] name, List keys, List args) { return new CommandObject<>(commandArguments(FCALL_RO).add(name).add(keys.size()) .keys(keys).addObjects(args), BuilderFactory.RAW_OBJECT); } public final CommandObject functionDelete(byte[] libraryName) { return new CommandObject<>(commandArguments(FUNCTION).add(DELETE).add(libraryName), BuilderFactory.STRING); } public final CommandObject functionDump() { return new CommandObject<>(commandArguments(FUNCTION).add(Keyword.DUMP), BuilderFactory.BINARY); } public final CommandObject> functionListBinary() { return new CommandObject<>(commandArguments(FUNCTION).add(LIST), BuilderFactory.RAW_OBJECT_LIST); } public final CommandObject> functionList(byte[] libraryNamePattern) { return new CommandObject<>(commandArguments(FUNCTION).add(LIST).add(LIBRARYNAME) .add(libraryNamePattern), BuilderFactory.RAW_OBJECT_LIST); } public final CommandObject> functionListWithCodeBinary() { return new CommandObject<>(commandArguments(FUNCTION).add(LIST).add(WITHCODE), BuilderFactory.RAW_OBJECT_LIST); } public final CommandObject> functionListWithCode(byte[] libraryNamePattern) { return new CommandObject<>(commandArguments(FUNCTION).add(LIST).add(LIBRARYNAME). add(libraryNamePattern).add(WITHCODE), BuilderFactory.RAW_OBJECT_LIST); } public final CommandObject functionLoad(byte[] functionCode) { return new CommandObject<>(commandArguments(FUNCTION).add(LOAD).add(functionCode), BuilderFactory.STRING); } public final CommandObject functionLoadReplace(byte[] functionCode) { return new CommandObject<>(commandArguments(FUNCTION).add(LOAD).add(REPLACE).add(functionCode), BuilderFactory.STRING); } public final CommandObject functionRestore(byte[] serializedValue) { return new CommandObject<>(commandArguments(FUNCTION).add(RESTORE).add(serializedValue), BuilderFactory.STRING); } public final CommandObject functionRestore(byte[] serializedValue, FunctionRestorePolicy policy) { return new CommandObject<>(commandArguments(FUNCTION).add(RESTORE).add(serializedValue) .add(policy.getRaw()), BuilderFactory.STRING); } // Scripting commands // Miscellaneous commands public final CommandObject copy(String srcKey, String dstKey, int dstDB, boolean replace) { CommandArguments args = commandArguments(Command.COPY).key(srcKey).key(dstKey).add(DB).add(dstDB); if (replace) args.add(REPLACE); return new CommandObject<>(args, BuilderFactory.BOOLEAN); } public final CommandObject copy(byte[] srcKey, byte[] dstKey, int dstDB, boolean replace) { CommandArguments args = commandArguments(Command.COPY).key(srcKey).key(dstKey).add(DB).add(dstDB); if (replace) args.add(REPLACE); return new CommandObject<>(args, BuilderFactory.BOOLEAN); } public final CommandObject migrate(String host, int port, String key, int timeout) { return migrate(host, port, key, 0, timeout); } public final CommandObject migrate(String host, int port, String key, int destinationDB, int timeout) { return new CommandObject<>(commandArguments(MIGRATE).add(host).add(port).key(key) .add(destinationDB).add(timeout), BuilderFactory.STRING); } public final CommandObject migrate(String host, int port, int timeout, MigrateParams params, String... keys) { return migrate(host, port, 0, timeout, params, keys); } public final CommandObject migrate(String host, int port, int destinationDB, int timeout, MigrateParams params, String... keys) { return new CommandObject<>(commandArguments(MIGRATE).add(host).add(port).add(new byte[0]) .add(destinationDB).add(timeout).addParams(params).add(Keyword.KEYS).keys((Object[]) keys), BuilderFactory.STRING); } public final CommandObject migrate(String host, int port, byte[] key, int timeout) { return migrate(host, port, key, 0, timeout); } public final CommandObject migrate(String host, int port, byte[] key, int destinationDB, int timeout) { return new CommandObject<>(commandArguments(MIGRATE).add(host).add(port).key(key) .add(destinationDB).add(timeout), BuilderFactory.STRING); } public final CommandObject migrate(String host, int port, int timeout, MigrateParams params, byte[]... keys) { return migrate(host, port, 0, timeout, params, keys); } public final CommandObject migrate(String host, int port, int destinationDB, int timeout, MigrateParams params, byte[]... keys) { return new CommandObject<>(commandArguments(MIGRATE).add(host).add(port).add(new byte[0]) .add(destinationDB).add(timeout).addParams(params).add(Keyword.KEYS).keys((Object[]) keys), BuilderFactory.STRING); } public final CommandObject memoryUsage(String key) { return new CommandObject<>(commandArguments(MEMORY).add(USAGE).key(key), BuilderFactory.LONG); } public final CommandObject memoryUsage(String key, int samples) { return new CommandObject<>(commandArguments(MEMORY).add(USAGE).key(key).add(SAMPLES).add(samples), BuilderFactory.LONG); } public final CommandObject memoryUsage(byte[] key) { return new CommandObject<>(commandArguments(MEMORY).add(USAGE).key(key), BuilderFactory.LONG); } public final CommandObject memoryUsage(byte[] key, int samples) { return new CommandObject<>(commandArguments(MEMORY).add(USAGE).key(key).add(SAMPLES).add(samples), BuilderFactory.LONG); } public final CommandObject objectRefcount(String key) { return new CommandObject<>(commandArguments(OBJECT).add(REFCOUNT).key(key), BuilderFactory.LONG); } public final CommandObject objectEncoding(String key) { return new CommandObject<>(commandArguments(OBJECT).add(ENCODING).key(key), BuilderFactory.STRING); } public final CommandObject objectIdletime(String key) { return new CommandObject<>(commandArguments(OBJECT).add(IDLETIME).key(key), BuilderFactory.LONG); } public final CommandObject objectFreq(String key) { return new CommandObject<>(commandArguments(OBJECT).add(FREQ).key(key), BuilderFactory.LONG); } public final CommandObject objectRefcount(byte[] key) { return new CommandObject<>(commandArguments(OBJECT).add(REFCOUNT).key(key), BuilderFactory.LONG); } public final CommandObject objectEncoding(byte[] key) { return new CommandObject<>(commandArguments(OBJECT).add(ENCODING).key(key), BuilderFactory.BINARY); } public final CommandObject objectIdletime(byte[] key) { return new CommandObject<>(commandArguments(OBJECT).add(IDLETIME).key(key), BuilderFactory.LONG); } public final CommandObject objectFreq(byte[] key) { return new CommandObject<>(commandArguments(OBJECT).add(FREQ).key(key), BuilderFactory.LONG); } public CommandObject waitReplicas(int replicas, long timeout) { return new CommandObject<>(commandArguments(WAIT).add(replicas).add(timeout), BuilderFactory.LONG); } public final CommandObject waitReplicas(String sampleKey, int replicas, long timeout) { return new CommandObject<>(commandArguments(WAIT).add(replicas).add(timeout).addHashSlotKey(sampleKey), BuilderFactory.LONG); } public final CommandObject waitReplicas(byte[] sampleKey, int replicas, long timeout) { return new CommandObject<>(commandArguments(WAIT).add(replicas).add(timeout).addHashSlotKey(sampleKey), BuilderFactory.LONG); } public CommandObject> waitAOF(long numLocal, long numReplicas, long timeout) { return new CommandObject<>(commandArguments(WAITAOF).add(numLocal).add(numReplicas).add(timeout), BuilderFactory.LONG_LONG_PAIR); } public CommandObject> waitAOF(byte[] sampleKey, long numLocal, long numReplicas, long timeout) { return new CommandObject<>(commandArguments(WAITAOF).add(numLocal).add(numReplicas).add(timeout).addHashSlotKey(sampleKey), BuilderFactory.LONG_LONG_PAIR); } public CommandObject> waitAOF(String sampleKey, long numLocal, long numReplicas, long timeout) { return new CommandObject<>(commandArguments(WAITAOF).add(numLocal).add(numReplicas).add(timeout).addHashSlotKey(sampleKey), BuilderFactory.LONG_LONG_PAIR); } public final CommandObject publish(String channel, String message) { return new CommandObject<>(commandArguments(PUBLISH).add(channel).add(message), BuilderFactory.LONG); } public final CommandObject publish(byte[] channel, byte[] message) { return new CommandObject<>(commandArguments(PUBLISH).add(channel).add(message), BuilderFactory.LONG); } public final CommandObject spublish(String channel, String message) { return new CommandObject<>(commandArguments(SPUBLISH).key(channel).add(message), BuilderFactory.LONG); } public final CommandObject spublish(byte[] channel, byte[] message) { return new CommandObject<>(commandArguments(SPUBLISH).key(channel).add(message), BuilderFactory.LONG); } // Miscellaneous commands // RediSearch commands public final CommandObject hsetObject(String key, String field, Object value) { return new CommandObject<>(commandArguments(HSET).key(key).add(field).add(value), BuilderFactory.LONG); } public final CommandObject hsetObject(String key, Map hash) { return new CommandObject<>(addFlatMapArgs(commandArguments(HSET).key(key), hash), BuilderFactory.LONG); } private boolean isRoundRobinSearchCommand(SearchCommand sc) { return !(sc.equals(SearchCommand.SUGGET) || sc.equals(SearchCommand.SUGADD) || sc.equals( SearchCommand.SUGLEN) || sc.equals(SearchCommand.SUGDEL) || sc.equals( SearchCommand.CURSOR)); } private CommandArguments checkAndRoundRobinSearchCommand(SearchCommand sc, String idx) { CommandArguments ca = commandArguments(sc); if (isRoundRobinSearchCommand(sc)) { ca.add(idx); } else { ca.key(idx); } return ca; } private CommandArguments checkAndRoundRobinSearchCommand(SearchCommand sc, String idx1, String idx2) { CommandArguments ca = commandArguments(sc); if (isRoundRobinSearchCommand(sc)) { ca.add(idx1).add(idx2); } else { ca.key(idx1).key(idx2); } return ca; } private CommandArguments checkAndRoundRobinSearchCommand(SearchCommand sc, byte[] indexName) { CommandArguments ca = commandArguments(sc); if (isRoundRobinSearchCommand(sc)) { ca.add(indexName); } else { ca.key(indexName); } return ca; } private CommandObject directSearchCommand(CommandObject object, String indexName) { object.getArguments().addHashSlotKey(indexName); return object; } public final CommandObject ftCreate(String indexName, IndexOptions indexOptions, Schema schema) { CommandArguments args = checkAndRoundRobinSearchCommand(SearchCommand.CREATE, indexName) .addParams(indexOptions).add(SearchKeyword.SCHEMA); schema.fields.forEach(field -> args.addParams(field)); return new CommandObject<>(args, BuilderFactory.STRING); } public final CommandObject ftCreate(String indexName, FTCreateParams createParams, Iterable schemaFields) { CommandArguments args = checkAndRoundRobinSearchCommand(SearchCommand.CREATE, indexName) .addParams(createParams).add(SearchKeyword.SCHEMA); schemaFields.forEach(field -> args.addParams(field)); return new CommandObject<>(args, BuilderFactory.STRING); } public final CommandObject ftAlter(String indexName, Schema schema) { CommandArguments args = checkAndRoundRobinSearchCommand(SearchCommand.ALTER, indexName) .add(SearchKeyword.SCHEMA).add(SearchKeyword.ADD); schema.fields.forEach(field -> args.addParams(field)); return new CommandObject<>(args, BuilderFactory.STRING); } public final CommandObject ftAlter(String indexName, Iterable schemaFields) { CommandArguments args = checkAndRoundRobinSearchCommand(SearchCommand.ALTER, indexName) .add(SearchKeyword.SCHEMA).add(SearchKeyword.ADD); schemaFields.forEach(field -> args.addParams(field)); return new CommandObject<>(args, BuilderFactory.STRING); } public final CommandObject ftAliasAdd(String aliasName, String indexName) { return new CommandObject<>(checkAndRoundRobinSearchCommand(SearchCommand.ALIASADD, aliasName, indexName), BuilderFactory.STRING); } public final CommandObject ftAliasUpdate(String aliasName, String indexName) { return new CommandObject<>(checkAndRoundRobinSearchCommand(SearchCommand.ALIASUPDATE, aliasName, indexName), BuilderFactory.STRING); } public final CommandObject ftAliasDel(String aliasName) { return new CommandObject<>(checkAndRoundRobinSearchCommand(SearchCommand.ALIASDEL, aliasName), BuilderFactory.STRING); } public final CommandObject ftDropIndex(String indexName) { return new CommandObject<>(checkAndRoundRobinSearchCommand(SearchCommand.DROPINDEX, indexName), BuilderFactory.STRING); } public final CommandObject ftDropIndexDD(String indexName) { return new CommandObject<>(checkAndRoundRobinSearchCommand(SearchCommand.DROPINDEX, indexName).add(SearchKeyword.DD), BuilderFactory.STRING); } public final CommandObject ftSearch(String indexName, String query) { return new CommandObject<>(checkAndRoundRobinSearchCommand(SearchCommand.SEARCH, indexName).add(query), getSearchResultBuilder(null, () -> new SearchResultBuilder(true, false, true))); } public final CommandObject ftSearch(String indexName, String query, FTSearchParams params) { return new CommandObject<>(checkAndRoundRobinSearchCommand(SearchCommand.SEARCH, indexName) .add(query).addParams(params.dialectOptional(searchDialect.get())), getSearchResultBuilder(params.getReturnFieldDecodeMap(), () -> new SearchResultBuilder( !params.getNoContent(), params.getWithScores(), true, params.getReturnFieldDecodeMap()))); } public final CommandObject ftSearch(String indexName, Query query) { return new CommandObject<>(checkAndRoundRobinSearchCommand(SearchCommand.SEARCH, indexName) .addParams(query.dialectOptional(searchDialect.get())), getSearchResultBuilder(null, () -> new SearchResultBuilder(!query.getNoContent(), query.getWithScores(), true))); } @Deprecated public final CommandObject ftSearch(byte[] indexName, Query query) { if (protocol == RedisProtocol.RESP3) { throw new UnsupportedOperationException("binary ft.search is not implemented with resp3."); } return new CommandObject<>(checkAndRoundRobinSearchCommand(SearchCommand.SEARCH, indexName) .addParams(query.dialectOptional(searchDialect.get())), getSearchResultBuilder(null, () -> new SearchResultBuilder(!query.getNoContent(), query.getWithScores(), false))); } public final CommandObject ftExplain(String indexName, Query query) { return new CommandObject<>(checkAndRoundRobinSearchCommand(SearchCommand.EXPLAIN, indexName) .addParams(query.dialectOptional(searchDialect.get())), BuilderFactory.STRING); } public final CommandObject> ftExplainCLI(String indexName, Query query) { return new CommandObject<>(checkAndRoundRobinSearchCommand(SearchCommand.EXPLAINCLI, indexName) .addParams(query.dialectOptional(searchDialect.get())), BuilderFactory.STRING_LIST); } public final CommandObject ftAggregate(String indexName, AggregationBuilder aggr) { return new CommandObject<>(checkAndRoundRobinSearchCommand(SearchCommand.AGGREGATE, indexName) .addParams(aggr.dialectOptional(searchDialect.get())), !aggr.isWithCursor() ? AggregationResult.SEARCH_AGGREGATION_RESULT : AggregationResult.SEARCH_AGGREGATION_RESULT_WITH_CURSOR); } public final CommandObject ftCursorRead(String indexName, long cursorId, int count) { return new CommandObject<>(commandArguments(SearchCommand.CURSOR).add(SearchKeyword.READ) .key(indexName).add(cursorId).add(SearchKeyword.COUNT).add(count), AggregationResult.SEARCH_AGGREGATION_RESULT_WITH_CURSOR); } public final CommandObject ftCursorDel(String indexName, long cursorId) { return new CommandObject<>(commandArguments(SearchCommand.CURSOR).add(SearchKeyword.DEL) .key(indexName).add(cursorId), BuilderFactory.STRING); } public final CommandObject> ftProfileAggregate( String indexName, FTProfileParams profileParams, AggregationBuilder aggr) { return new CommandObject<>(checkAndRoundRobinSearchCommand(SearchCommand.PROFILE, indexName) .add(SearchKeyword.AGGREGATE).addParams(profileParams).add(SearchKeyword.QUERY) .addParams(aggr.dialectOptional(searchDialect.get())), new SearchProfileResponseBuilder<>( !aggr.isWithCursor() ? AggregationResult.SEARCH_AGGREGATION_RESULT : AggregationResult.SEARCH_AGGREGATION_RESULT_WITH_CURSOR)); } public final CommandObject> ftProfileSearch( String indexName, FTProfileParams profileParams, Query query) { return new CommandObject<>(checkAndRoundRobinSearchCommand(SearchCommand.PROFILE, indexName) .add(SearchKeyword.SEARCH).addParams(profileParams).add(SearchKeyword.QUERY) .addParams(query.dialectOptional(searchDialect.get())), new SearchProfileResponseBuilder<>(getSearchResultBuilder(null, () -> new SearchResultBuilder(!query.getNoContent(), query.getWithScores(), true)))); } public final CommandObject> ftProfileSearch( String indexName, FTProfileParams profileParams, String query, FTSearchParams searchParams) { return new CommandObject<>(checkAndRoundRobinSearchCommand(SearchCommand.PROFILE, indexName) .add(SearchKeyword.SEARCH).addParams(profileParams).add(SearchKeyword.QUERY).add(query) .addParams(searchParams.dialectOptional(searchDialect.get())), new SearchProfileResponseBuilder<>(getSearchResultBuilder(searchParams.getReturnFieldDecodeMap(), () -> new SearchResultBuilder(!searchParams.getNoContent(), searchParams.getWithScores(), true, searchParams.getReturnFieldDecodeMap())))); } private Builder getSearchResultBuilder( Map isReturnFieldDecode, Supplier> resp2) { if (protocol == RedisProtocol.RESP3) { return isReturnFieldDecode == null ? SearchResult.SEARCH_RESULT_BUILDER : new SearchResult.PerFieldDecoderSearchResultBuilder(isReturnFieldDecode); } return resp2.get(); } public final CommandObject ftSynUpdate(String indexName, String synonymGroupId, String... terms) { return new CommandObject<>(checkAndRoundRobinSearchCommand(SearchCommand.SYNUPDATE, indexName) .add(synonymGroupId).addObjects((Object[]) terms), BuilderFactory.STRING); } public final CommandObject>> ftSynDump(String indexName) { return new CommandObject<>(checkAndRoundRobinSearchCommand(SearchCommand.SYNDUMP, indexName), SearchBuilderFactory.SEARCH_SYNONYM_GROUPS); } public final CommandObject ftDictAdd(String dictionary, String... terms) { return new CommandObject<>(commandArguments(SearchCommand.DICTADD).add(dictionary).addObjects((Object[]) terms), BuilderFactory.LONG); } public final CommandObject ftDictDel(String dictionary, String... terms) { return new CommandObject<>(commandArguments(SearchCommand.DICTDEL).add(dictionary).addObjects((Object[]) terms), BuilderFactory.LONG); } public final CommandObject> ftDictDump(String dictionary) { return new CommandObject<>(commandArguments(SearchCommand.DICTDUMP).add(dictionary), BuilderFactory.STRING_SET); } public final CommandObject ftDictAddBySampleKey(String indexName, String dictionary, String... terms) { return directSearchCommand(ftDictAdd(dictionary, terms), indexName); } public final CommandObject ftDictDelBySampleKey(String indexName, String dictionary, String... terms) { return directSearchCommand(ftDictDel(dictionary, terms), indexName); } public final CommandObject> ftDictDumpBySampleKey(String indexName, String dictionary) { return directSearchCommand(ftDictDump(dictionary), indexName); } public final CommandObject>> ftSpellCheck(String index, String query) { return new CommandObject<>(checkAndRoundRobinSearchCommand(SearchCommand.SPELLCHECK, index).add(query), SearchBuilderFactory.SEARCH_SPELLCHECK_RESPONSE); } public final CommandObject>> ftSpellCheck(String index, String query, FTSpellCheckParams spellCheckParams) { return new CommandObject<>(checkAndRoundRobinSearchCommand(SearchCommand.SPELLCHECK, index).add(query) .addParams(spellCheckParams.dialectOptional(searchDialect.get())), SearchBuilderFactory.SEARCH_SPELLCHECK_RESPONSE); } public final CommandObject> ftInfo(String indexName) { return new CommandObject<>(checkAndRoundRobinSearchCommand(SearchCommand.INFO, indexName), protocol == RedisProtocol.RESP3 ? BuilderFactory.AGGRESSIVE_ENCODED_OBJECT_MAP : BuilderFactory.ENCODED_OBJECT_MAP); } public final CommandObject> ftTagVals(String indexName, String fieldName) { return new CommandObject<>(checkAndRoundRobinSearchCommand(SearchCommand.TAGVALS, indexName) .add(fieldName), BuilderFactory.STRING_SET); } @Experimental public final CommandObject ftHybrid(String indexName, FTHybridParams hybridParams) { return new CommandObject<>(checkAndRoundRobinSearchCommand(SearchCommand.HYBRID, indexName) .addParams(hybridParams), HybridResult.HYBRID_RESULT_BUILDER); } @Deprecated public final CommandObject> ftConfigGet(String option) { return new CommandObject<>(commandArguments(SearchCommand.CONFIG).add(SearchKeyword.GET).add(option), protocol == RedisProtocol.RESP3 ? BuilderFactory.AGGRESSIVE_ENCODED_OBJECT_MAP : BuilderFactory.ENCODED_OBJECT_MAP_FROM_PAIRS); } @Deprecated public final CommandObject> ftConfigGet(String indexName, String option) { return directSearchCommand(ftConfigGet(option), indexName); } @Deprecated public final CommandObject ftConfigSet(String option, String value) { return new CommandObject<>(commandArguments(SearchCommand.CONFIG).add(SearchKeyword.SET).add(option).add(value), BuilderFactory.STRING); } @Deprecated public final CommandObject ftConfigSet(String indexName, String option, String value) { return directSearchCommand(ftConfigSet(option, value), indexName); } public final CommandObject ftSugAdd(String key, String string, double score) { return new CommandObject<>(commandArguments(SearchCommand.SUGADD).key(key).add(string).add(score), BuilderFactory.LONG); } public final CommandObject ftSugAddIncr(String key, String string, double score) { return new CommandObject<>(commandArguments(SearchCommand.SUGADD).key(key).add(string).add(score).add(SearchKeyword.INCR), BuilderFactory.LONG); } public final CommandObject> ftSugGet(String key, String prefix) { return new CommandObject<>(commandArguments(SearchCommand.SUGGET).key(key).add(prefix), BuilderFactory.STRING_LIST); } public final CommandObject> ftSugGet(String key, String prefix, boolean fuzzy, int max) { CommandArguments args = commandArguments(SearchCommand.SUGGET).key(key).add(prefix); if (fuzzy) args.add(SearchKeyword.FUZZY); args.add(SearchKeyword.MAX).add(max); return new CommandObject<>(args, BuilderFactory.STRING_LIST); } public final CommandObject> ftSugGetWithScores(String key, String prefix) { return new CommandObject<>(commandArguments(SearchCommand.SUGGET).key(key).add(prefix) .add(SearchKeyword.WITHSCORES), BuilderFactory.TUPLE_LIST); } public final CommandObject> ftSugGetWithScores(String key, String prefix, boolean fuzzy, int max) { CommandArguments args = commandArguments(SearchCommand.SUGGET).key(key).add(prefix); if (fuzzy) args.add(SearchKeyword.FUZZY); args.add(SearchKeyword.MAX).add(max); args.add(SearchKeyword.WITHSCORES); return new CommandObject<>(args, BuilderFactory.TUPLE_LIST); } public final CommandObject ftSugDel(String key, String string) { return new CommandObject<>(commandArguments(SearchCommand.SUGDEL).key(key).add(string), BuilderFactory.BOOLEAN); } public final CommandObject ftSugLen(String key) { return new CommandObject<>(commandArguments(SearchCommand.SUGLEN).key(key), BuilderFactory.LONG); } public final CommandObject> ftList() { return new CommandObject<>(commandArguments(SearchCommand._LIST), BuilderFactory.STRING_SET); } // RediSearch commands // RedisJSON commands public final CommandObject jsonSet(String key, Path2 path, Object object) { return new CommandObject<>(commandArguments(JsonCommand.SET).key(key).add(path).add(object), BuilderFactory.STRING); } public final CommandObject jsonSetWithEscape(String key, Path2 path, Object object) { return new CommandObject<>(commandArguments(JsonCommand.SET).key(key).add(path).add( getJsonObjectMapper().toJson(object)), BuilderFactory.STRING); } @Deprecated public final CommandObject jsonSet(String key, Path path, Object pojo) { return new CommandObject<>(commandArguments(JsonCommand.SET).key(key).add(path).add( getJsonObjectMapper().toJson(pojo)), BuilderFactory.STRING); } @Deprecated public final CommandObject jsonSetWithPlainString(String key, Path path, String string) { return new CommandObject<>(commandArguments(JsonCommand.SET).key(key).add(path).add(string), BuilderFactory.STRING); } public final CommandObject jsonSet(String key, Path2 path, Object object, JsonSetParams params) { return new CommandObject<>(commandArguments(JsonCommand.SET).key(key).add(path).add(object).addParams(params), BuilderFactory.STRING); } public final CommandObject jsonSetWithEscape(String key, Path2 path, Object object, JsonSetParams params) { return new CommandObject<>(commandArguments(JsonCommand.SET).key(key).add(path).add( getJsonObjectMapper().toJson(object)).addParams(params), BuilderFactory.STRING); } @Deprecated public final CommandObject jsonSet(String key, Path path, Object pojo, JsonSetParams params) { return new CommandObject<>(commandArguments(JsonCommand.SET).key(key).add(path).add( getJsonObjectMapper().toJson(pojo)).addParams(params), BuilderFactory.STRING); } public final CommandObject jsonMerge(String key, Path2 path, Object object) { return new CommandObject<>(commandArguments(JsonCommand.MERGE).key(key).add(path).add(object), BuilderFactory.STRING); } @Deprecated public final CommandObject jsonMerge(String key, Path path, Object pojo) { return new CommandObject<>(commandArguments(JsonCommand.MERGE).key(key).add(path).add( getJsonObjectMapper().toJson(pojo)), BuilderFactory.STRING); } public final CommandObject jsonGet(String key) { return new CommandObject<>(commandArguments(JsonCommand.GET).key(key), JSON_GENERIC_OBJECT); } @Deprecated public final CommandObject jsonGet(String key, Class clazz) { return new CommandObject<>(commandArguments(JsonCommand.GET).key(key), new JsonObjectBuilder<>(clazz)); } public final CommandObject jsonGet(String key, Path2... paths) { return new CommandObject<>(commandArguments(JsonCommand.GET).key(key).addObjects((Object[]) paths), JsonBuilderFactory.JSON_OBJECT); } @Deprecated public final CommandObject jsonGet(String key, Path... paths) { return new CommandObject<>(commandArguments(JsonCommand.GET).key(key).addObjects((Object[]) paths), JSON_GENERIC_OBJECT); } @Deprecated public final CommandObject jsonGetAsPlainString(String key, Path path) { return new CommandObject<>(commandArguments(JsonCommand.GET).key(key).add(path), BuilderFactory.STRING); } @Deprecated public final CommandObject jsonGet(String key, Class clazz, Path... paths) { return new CommandObject<>(commandArguments(JsonCommand.GET).key(key).addObjects((Object[]) paths), new JsonObjectBuilder<>(clazz)); } public final CommandObject> jsonMGet(Path2 path, String... keys) { return new CommandObject<>(commandArguments(JsonCommand.MGET).keys((Object[]) keys).add(path), JsonBuilderFactory.JSON_ARRAY_LIST); } @Deprecated public final CommandObject> jsonMGet(Path path, Class clazz, String... keys) { return new CommandObject<>(commandArguments(JsonCommand.MGET).keys((Object[]) keys).add(path), new JsonObjectListBuilder<>(clazz)); } public final CommandObject jsonDel(String key) { return new CommandObject<>(commandArguments(JsonCommand.DEL).key(key), BuilderFactory.LONG); } public final CommandObject jsonDel(String key, Path2 path) { return new CommandObject<>(commandArguments(JsonCommand.DEL).key(key).add(path), BuilderFactory.LONG); } @Deprecated public final CommandObject jsonDel(String key, Path path) { return new CommandObject<>(commandArguments(JsonCommand.DEL).key(key).add(path), BuilderFactory.LONG); } public final CommandObject jsonClear(String key) { return new CommandObject<>(commandArguments(JsonCommand.CLEAR).key(key), BuilderFactory.LONG); } public final CommandObject jsonClear(String key, Path2 path) { return new CommandObject<>(commandArguments(JsonCommand.CLEAR).key(key).add(path), BuilderFactory.LONG); } @Deprecated public final CommandObject jsonClear(String key, Path path) { return new CommandObject<>(commandArguments(JsonCommand.CLEAR).key(key).add(path), BuilderFactory.LONG); } public final CommandObject> jsonToggle(String key, Path2 path) { return new CommandObject<>(commandArguments(JsonCommand.TOGGLE).key(key).add(path), BuilderFactory.BOOLEAN_LIST); } @Deprecated public final CommandObject jsonToggle(String key, Path path) { return new CommandObject<>(commandArguments(JsonCommand.TOGGLE).key(key).add(path), BuilderFactory.STRING); } @Deprecated public final CommandObject> jsonType(String key) { return new CommandObject<>(commandArguments(JsonCommand.TYPE).key(key), JsonBuilderFactory.JSON_TYPE); } public final CommandObject>> jsonType(String key, Path2 path) { return new CommandObject<>(commandArguments(JsonCommand.TYPE).key(key).add(path), protocol != RedisProtocol.RESP3 ? JsonBuilderFactory.JSON_TYPE_LIST : JsonBuilderFactory.JSON_TYPE_RESPONSE_RESP3_COMPATIBLE); } @Deprecated public final CommandObject> jsonType(String key, Path path) { return new CommandObject<>(commandArguments(JsonCommand.TYPE).key(key).add(path), JsonBuilderFactory.JSON_TYPE); } @Deprecated public final CommandObject jsonStrAppend(String key, Object string) { return new CommandObject<>(commandArguments(JsonCommand.STRAPPEND).key(key).add( getJsonObjectMapper().toJson(string)), BuilderFactory.LONG); } public final CommandObject> jsonStrAppend(String key, Path2 path, Object string) { return new CommandObject<>(commandArguments(JsonCommand.STRAPPEND).key(key).add(path).add( getJsonObjectMapper().toJson(string)), BuilderFactory.LONG_LIST); } @Deprecated public final CommandObject jsonStrAppend(String key, Path path, Object string) { return new CommandObject<>(commandArguments(JsonCommand.STRAPPEND).key(key).add(path).add( getJsonObjectMapper().toJson(string)), BuilderFactory.LONG); } @Deprecated public final CommandObject jsonStrLen(String key) { return new CommandObject<>(commandArguments(JsonCommand.STRLEN).key(key), BuilderFactory.LONG); } public final CommandObject> jsonStrLen(String key, Path2 path) { return new CommandObject<>(commandArguments(JsonCommand.STRLEN).key(key).add(path), BuilderFactory.LONG_LIST); } @Deprecated public final CommandObject jsonStrLen(String key, Path path) { return new CommandObject<>(commandArguments(JsonCommand.STRLEN).key(key).add(path), BuilderFactory.LONG); } public final CommandObject jsonNumIncrBy(String key, Path2 path, double value) { return new CommandObject<>(commandArguments(JsonCommand.NUMINCRBY).key(key).add(path).add(value), JsonBuilderFactory.JSON_ARRAY_OR_DOUBLE_LIST); } @Deprecated public final CommandObject jsonNumIncrBy(String key, Path path, double value) { return new CommandObject<>(commandArguments(JsonCommand.NUMINCRBY).key(key).add(path).add(value), BuilderFactory.DOUBLE); } @Deprecated public final CommandObject jsonArrAppend(String key, String path, JSONObject... objects) { CommandArguments args = commandArguments(JsonCommand.ARRAPPEND).key(key).add(path); for (Object object : objects) { args.add(object); } return new CommandObject<>(args, BuilderFactory.LONG); } public final CommandObject> jsonArrAppend(String key, Path2 path, Object... objects) { CommandArguments args = commandArguments(JsonCommand.ARRAPPEND).key(key).add(path).addObjects(objects); return new CommandObject<>(args, BuilderFactory.LONG_LIST); } public final CommandObject> jsonArrAppendWithEscape(String key, Path2 path, Object... objects) { CommandArguments args = commandArguments(JsonCommand.ARRAPPEND).key(key).add(path); for (Object object : objects) { args.add(getJsonObjectMapper().toJson(object)); } return new CommandObject<>(args, BuilderFactory.LONG_LIST); } @Deprecated public final CommandObject jsonArrAppend(String key, Path path, Object... pojos) { CommandArguments args = commandArguments(JsonCommand.ARRAPPEND).key(key).add(path); for (Object pojo : pojos) { args.add(getJsonObjectMapper().toJson(pojo)); } return new CommandObject<>(args, BuilderFactory.LONG); } public final CommandObject> jsonArrIndex(String key, Path2 path, Object scalar) { return new CommandObject<>(commandArguments(JsonCommand.ARRINDEX).key(key).add(path).add(scalar), BuilderFactory.LONG_LIST); } public final CommandObject> jsonArrIndexWithEscape(String key, Path2 path, Object scalar) { return new CommandObject<>(commandArguments(JsonCommand.ARRINDEX).key(key).add(path).add( getJsonObjectMapper().toJson(scalar)), BuilderFactory.LONG_LIST); } @Deprecated public final CommandObject jsonArrIndex(String key, Path path, Object scalar) { return new CommandObject<>(commandArguments(JsonCommand.ARRINDEX).key(key).add(path).add( getJsonObjectMapper().toJson(scalar)), BuilderFactory.LONG); } public final CommandObject> jsonArrInsert(String key, Path2 path, int index, Object... objects) { CommandArguments args = commandArguments(JsonCommand.ARRINSERT).key(key).add(path).add(index).addObjects(objects); return new CommandObject<>(args, BuilderFactory.LONG_LIST); } public final CommandObject> jsonArrInsertWithEscape(String key, Path2 path, int index, Object... objects) { CommandArguments args = commandArguments(JsonCommand.ARRINSERT).key(key).add(path).add(index); for (Object object : objects) { args.add(getJsonObjectMapper().toJson(object)); } return new CommandObject<>(args, BuilderFactory.LONG_LIST); } @Deprecated public final CommandObject jsonArrInsert(String key, Path path, int index, Object... pojos) { CommandArguments args = commandArguments(JsonCommand.ARRINSERT).key(key).add(path).add(index); for (Object pojo : pojos) { args.add(getJsonObjectMapper().toJson(pojo)); } return new CommandObject<>(args, BuilderFactory.LONG); } @Deprecated public final CommandObject jsonArrPop(String key) { return new CommandObject<>(commandArguments(JsonCommand.ARRPOP).key(key), new JsonObjectBuilder<>(Object.class)); } @Deprecated public final CommandObject jsonArrPop(String key, Class clazz) { return new CommandObject<>(commandArguments(JsonCommand.ARRPOP).key(key), new JsonObjectBuilder<>(clazz)); } public final CommandObject> jsonArrPop(String key, Path2 path) { return new CommandObject<>(commandArguments(JsonCommand.ARRPOP).key(key).add(path), new JsonObjectListBuilder<>(Object.class)); } @Deprecated public final CommandObject jsonArrPop(String key, Path path) { return new CommandObject<>(commandArguments(JsonCommand.ARRPOP).key(key).add(path), new JsonObjectBuilder<>(Object.class)); } @Deprecated public final CommandObject jsonArrPop(String key, Class clazz, Path path) { return new CommandObject<>(commandArguments(JsonCommand.ARRPOP).key(key).add(path), new JsonObjectBuilder<>(clazz)); } public final CommandObject> jsonArrPop(String key, Path2 path, int index) { return new CommandObject<>(commandArguments(JsonCommand.ARRPOP).key(key).add(path).add(index), new JsonObjectListBuilder<>(Object.class)); } @Deprecated public final CommandObject jsonArrPop(String key, Path path, int index) { return new CommandObject<>(commandArguments(JsonCommand.ARRPOP).key(key).add(path).add(index), new JsonObjectBuilder<>(Object.class)); } @Deprecated public final CommandObject jsonArrPop(String key, Class clazz, Path path, int index) { return new CommandObject<>(commandArguments(JsonCommand.ARRPOP).key(key).add(path).add(index), new JsonObjectBuilder<>(clazz)); } @Deprecated public final CommandObject jsonArrLen(String key) { return new CommandObject<>(commandArguments(JsonCommand.ARRLEN).key(key), BuilderFactory.LONG); } public final CommandObject> jsonArrLen(String key, Path2 path) { return new CommandObject<>(commandArguments(JsonCommand.ARRLEN).key(key).add(path), BuilderFactory.LONG_LIST); } @Deprecated public final CommandObject jsonArrLen(String key, Path path) { return new CommandObject<>(commandArguments(JsonCommand.ARRLEN).key(key).add(path), BuilderFactory.LONG); } public final CommandObject> jsonArrTrim(String key, Path2 path, int start, int stop) { return new CommandObject<>(commandArguments(JsonCommand.ARRTRIM).key(key).add(path).add(start).add(stop), BuilderFactory.LONG_LIST); } @Deprecated public final CommandObject jsonArrTrim(String key, Path path, int start, int stop) { return new CommandObject<>(commandArguments(JsonCommand.ARRTRIM).key(key).add(path).add(start).add(stop), BuilderFactory.LONG); } @Deprecated public final CommandObject jsonObjLen(String key) { return new CommandObject<>(commandArguments(JsonCommand.OBJLEN).key(key), BuilderFactory.LONG); } @Deprecated public final CommandObject jsonObjLen(String key, Path path) { return new CommandObject<>(commandArguments(JsonCommand.OBJLEN).key(key).add(path), BuilderFactory.LONG); } public final CommandObject> jsonObjLen(String key, Path2 path) { return new CommandObject<>(commandArguments(JsonCommand.OBJLEN).key(key).add(path), BuilderFactory.LONG_LIST); } @Deprecated public final CommandObject> jsonObjKeys(String key) { return new CommandObject<>(commandArguments(JsonCommand.OBJKEYS).key(key), BuilderFactory.STRING_LIST); } @Deprecated public final CommandObject> jsonObjKeys(String key, Path path) { return new CommandObject<>(commandArguments(JsonCommand.OBJKEYS).key(key).add(path), BuilderFactory.STRING_LIST); } public final CommandObject>> jsonObjKeys(String key, Path2 path) { return new CommandObject<>(commandArguments(JsonCommand.OBJKEYS).key(key).add(path), BuilderFactory.STRING_LIST_LIST); } @Deprecated public final CommandObject jsonDebugMemory(String key) { return new CommandObject<>(commandArguments(JsonCommand.DEBUG).add("MEMORY").key(key), BuilderFactory.LONG); } @Deprecated public final CommandObject jsonDebugMemory(String key, Path path) { return new CommandObject<>(commandArguments(JsonCommand.DEBUG).add("MEMORY").key(key).add(path), BuilderFactory.LONG); } public final CommandObject> jsonDebugMemory(String key, Path2 path) { return new CommandObject<>(commandArguments(JsonCommand.DEBUG).add("MEMORY").key(key).add(path), BuilderFactory.LONG_LIST); } // RedisJSON commands // RedisTimeSeries commands public final CommandObject tsCreate(String key) { return new CommandObject<>(commandArguments(TimeSeriesCommand.CREATE).key(key), BuilderFactory.STRING); } public final CommandObject tsCreate(String key, TSCreateParams createParams) { return new CommandObject<>(commandArguments(TimeSeriesCommand.CREATE).key(key).addParams(createParams), BuilderFactory.STRING); } public final CommandObject tsDel(String key, long fromTimestamp, long toTimestamp) { return new CommandObject<>(commandArguments(TimeSeriesCommand.DEL).key(key) .add(fromTimestamp).add(toTimestamp), BuilderFactory.LONG); } public final CommandObject tsAlter(String key, TSAlterParams alterParams) { return new CommandObject<>(commandArguments(TimeSeriesCommand.ALTER).key(key).addParams(alterParams), BuilderFactory.STRING); } public final CommandObject tsAdd(String key, double value) { return new CommandObject<>(commandArguments(TimeSeriesCommand.ADD).key(key).add(Protocol.BYTES_ASTERISK).add(value), BuilderFactory.LONG); } public final CommandObject tsAdd(String key, long timestamp, double value) { return new CommandObject<>(commandArguments(TimeSeriesCommand.ADD).key(key).add(timestamp).add(value), BuilderFactory.LONG); } @Deprecated public final CommandObject tsAdd(String key, long timestamp, double value, TSCreateParams createParams) { return new CommandObject<>(commandArguments(TimeSeriesCommand.ADD).key(key).add(timestamp).add(value) .addParams(createParams), BuilderFactory.LONG); } public final CommandObject tsAdd(String key, long timestamp, double value, TSAddParams addParams) { return new CommandObject<>(commandArguments(TimeSeriesCommand.ADD).key(key).add(timestamp).add(value) .addParams(addParams), BuilderFactory.LONG); } public final CommandObject> tsMAdd(Map.Entry... entries) { CommandArguments args = commandArguments(TimeSeriesCommand.MADD); for (Map.Entry entry : entries) { args.key(entry.getKey()).add(entry.getValue().getTimestamp()).add(entry.getValue().getValue()); } return new CommandObject<>(args, BuilderFactory.LONG_LIST); } public final CommandObject tsIncrBy(String key, double value) { return new CommandObject<>(commandArguments(TimeSeriesCommand.INCRBY).key(key).add(value), BuilderFactory.LONG); } public final CommandObject tsIncrBy(String key, double value, long timestamp) { return new CommandObject<>(commandArguments(TimeSeriesCommand.INCRBY).key(key).add(value) .add(TimeSeriesKeyword.TIMESTAMP).add(timestamp), BuilderFactory.LONG); } public final CommandObject tsIncrBy(String key, double addend, TSIncrByParams incrByParams) { return new CommandObject<>(commandArguments(TimeSeriesCommand.INCRBY).key(key).add(addend) .addParams(incrByParams), BuilderFactory.LONG); } public final CommandObject tsDecrBy(String key, double value) { return new CommandObject<>(commandArguments(TimeSeriesCommand.DECRBY).key(key).add(value), BuilderFactory.LONG); } public final CommandObject tsDecrBy(String key, double value, long timestamp) { return new CommandObject<>(commandArguments(TimeSeriesCommand.DECRBY).key(key).add(value) .add(TimeSeriesKeyword.TIMESTAMP).add(timestamp), BuilderFactory.LONG); } public final CommandObject tsDecrBy(String key, double subtrahend, TSDecrByParams decrByParams) { return new CommandObject<>(commandArguments(TimeSeriesCommand.DECRBY).key(key).add(subtrahend) .addParams(decrByParams), BuilderFactory.LONG); } public final CommandObject> tsRange(String key, long fromTimestamp, long toTimestamp) { return new CommandObject<>(commandArguments(TimeSeriesCommand.RANGE).key(key) .add(fromTimestamp).add(toTimestamp), TimeSeriesBuilderFactory.TIMESERIES_ELEMENT_LIST); } public final CommandObject> tsRange(String key, TSRangeParams rangeParams) { return new CommandObject<>(commandArguments(TimeSeriesCommand.RANGE).key(key) .addParams(rangeParams), TimeSeriesBuilderFactory.TIMESERIES_ELEMENT_LIST); } public final CommandObject> tsRevRange(String key, long fromTimestamp, long toTimestamp) { return new CommandObject<>(commandArguments(TimeSeriesCommand.REVRANGE).key(key) .add(fromTimestamp).add(toTimestamp), TimeSeriesBuilderFactory.TIMESERIES_ELEMENT_LIST); } public final CommandObject> tsRevRange(String key, TSRangeParams rangeParams) { return new CommandObject<>(commandArguments(TimeSeriesCommand.REVRANGE).key(key) .addParams(rangeParams), TimeSeriesBuilderFactory.TIMESERIES_ELEMENT_LIST); } public final CommandObject> tsMRange(long fromTimestamp, long toTimestamp, String... filters) { return new CommandObject<>(commandArguments(TimeSeriesCommand.MRANGE).add(fromTimestamp) .add(toTimestamp).add(TimeSeriesKeyword.FILTER).addObjects((Object[]) filters), getTimeseriesMultiRangeResponseBuilder()); } public final CommandObject> tsMRange(TSMRangeParams multiRangeParams) { return new CommandObject<>(commandArguments(TimeSeriesCommand.MRANGE) .addParams(multiRangeParams), getTimeseriesMultiRangeResponseBuilder()); } public final CommandObject> tsMRevRange(long fromTimestamp, long toTimestamp, String... filters) { return new CommandObject<>(commandArguments(TimeSeriesCommand.MREVRANGE).add(fromTimestamp) .add(toTimestamp).add(TimeSeriesKeyword.FILTER).addObjects((Object[]) filters), getTimeseriesMultiRangeResponseBuilder()); } public final CommandObject> tsMRevRange(TSMRangeParams multiRangeParams) { return new CommandObject<>(commandArguments(TimeSeriesCommand.MREVRANGE).addParams(multiRangeParams), getTimeseriesMultiRangeResponseBuilder()); } public final CommandObject tsGet(String key) { return new CommandObject<>(commandArguments(TimeSeriesCommand.GET).key(key), TimeSeriesBuilderFactory.TIMESERIES_ELEMENT); } public final CommandObject tsGet(String key, TSGetParams getParams) { return new CommandObject<>(commandArguments(TimeSeriesCommand.GET).key(key).addParams(getParams), TimeSeriesBuilderFactory.TIMESERIES_ELEMENT); } public final CommandObject> tsMGet(TSMGetParams multiGetParams, String... filters) { return new CommandObject<>(commandArguments(TimeSeriesCommand.MGET).addParams(multiGetParams) .add(TimeSeriesKeyword.FILTER).addObjects((Object[]) filters), protocol == RedisProtocol.RESP3 ? TimeSeriesBuilderFactory.TIMESERIES_MGET_RESPONSE_RESP3 : TimeSeriesBuilderFactory.TIMESERIES_MGET_RESPONSE); } public final CommandObject tsCreateRule(String sourceKey, String destKey, AggregationType aggregationType, long timeBucket) { return new CommandObject<>(commandArguments(TimeSeriesCommand.CREATERULE).key(sourceKey).key(destKey) .add(TimeSeriesKeyword.AGGREGATION).add(aggregationType).add(timeBucket), BuilderFactory.STRING); } public final CommandObject tsCreateRule(String sourceKey, String destKey, AggregationType aggregationType, long bucketDuration, long alignTimestamp) { return new CommandObject<>(commandArguments(TimeSeriesCommand.CREATERULE).key(sourceKey).key(destKey) .add(TimeSeriesKeyword.AGGREGATION).add(aggregationType).add(bucketDuration).add(alignTimestamp), BuilderFactory.STRING); } public final CommandObject tsDeleteRule(String sourceKey, String destKey) { return new CommandObject<>(commandArguments(TimeSeriesCommand.DELETERULE).key(sourceKey).key(destKey), BuilderFactory.STRING); } public final CommandObject> tsQueryIndex(String... filters) { return new CommandObject<>(commandArguments(TimeSeriesCommand.QUERYINDEX) .addObjects((Object[]) filters), BuilderFactory.STRING_LIST); } public final CommandObject tsInfo(String key) { return new CommandObject<>(commandArguments(TimeSeriesCommand.INFO).key(key), getTimeseriesInfoBuilder()); } public final CommandObject tsInfoDebug(String key) { return new CommandObject<>(commandArguments(TimeSeriesCommand.INFO).key(key).add(TimeSeriesKeyword.DEBUG), getTimeseriesInfoBuilder()); } private Builder> getTimeseriesMultiRangeResponseBuilder() { return protocol == RedisProtocol.RESP3 ? TimeSeriesBuilderFactory.TIMESERIES_MRANGE_RESPONSE_RESP3 : TimeSeriesBuilderFactory.TIMESERIES_MRANGE_RESPONSE; } private Builder getTimeseriesInfoBuilder() { return protocol == RedisProtocol.RESP3 ? TSInfo.TIMESERIES_INFO_RESP3 : TSInfo.TIMESERIES_INFO; } // RedisTimeSeries commands // RedisBloom commands public final CommandObject bfReserve(String key, double errorRate, long capacity) { return new CommandObject<>(commandArguments(BloomFilterCommand.RESERVE).key(key) .add(errorRate).add(capacity), BuilderFactory.STRING); } public final CommandObject bfReserve(String key, double errorRate, long capacity, BFReserveParams reserveParams) { return new CommandObject<>(commandArguments(BloomFilterCommand.RESERVE).key(key) .add(errorRate).add(capacity).addParams(reserveParams), BuilderFactory.STRING); } public final CommandObject bfAdd(String key, String item) { return new CommandObject<>(commandArguments(BloomFilterCommand.ADD).key(key).add(item), BuilderFactory.BOOLEAN); } public final CommandObject> bfMAdd(String key, String... items) { return new CommandObject<>(commandArguments(BloomFilterCommand.MADD).key(key). addObjects((Object[]) items), BuilderFactory.BOOLEAN_WITH_ERROR_LIST); } public final CommandObject> bfInsert(String key, String... items) { return new CommandObject<>(commandArguments(BloomFilterCommand.INSERT).key(key) .add(RedisBloomKeyword.ITEMS).addObjects((Object[]) items), BuilderFactory.BOOLEAN_WITH_ERROR_LIST); } public final CommandObject> bfInsert(String key, BFInsertParams insertParams, String... items) { return new CommandObject<>(commandArguments(BloomFilterCommand.INSERT).key(key).addParams(insertParams) .add(RedisBloomKeyword.ITEMS).addObjects((Object[]) items), BuilderFactory.BOOLEAN_WITH_ERROR_LIST); } public final CommandObject bfExists(String key, String item) { return new CommandObject<>(commandArguments(BloomFilterCommand.EXISTS).key(key).add(item), BuilderFactory.BOOLEAN); } public final CommandObject> bfMExists(String key, String... items) { return new CommandObject<>(commandArguments(BloomFilterCommand.MEXISTS).key(key). addObjects((Object[]) items), BuilderFactory.BOOLEAN_LIST); } public final CommandObject> bfScanDump(String key, long iterator) { return new CommandObject<>(commandArguments(BloomFilterCommand.SCANDUMP).key(key).add(iterator), BLOOM_SCANDUMP_RESPONSE); } public final CommandObject bfLoadChunk(String key, long iterator, byte[] data) { return new CommandObject<>(commandArguments(BloomFilterCommand.LOADCHUNK).key(key).add(iterator).add(data), BuilderFactory.STRING); } public final CommandObject bfCard(String key) { return new CommandObject<>(commandArguments(BloomFilterCommand.CARD).key(key), BuilderFactory.LONG); } public final CommandObject> bfInfo(String key) { return new CommandObject<>(commandArguments(BloomFilterCommand.INFO).key(key), BuilderFactory.ENCODED_OBJECT_MAP); } public final CommandObject cfReserve(String key, long capacity) { return new CommandObject<>(commandArguments(CuckooFilterCommand.RESERVE).key(key).add(capacity), BuilderFactory.STRING); } public final CommandObject cfReserve(String key, long capacity, CFReserveParams reserveParams) { return new CommandObject<>(commandArguments(CuckooFilterCommand.RESERVE).key(key).add(capacity).addParams(reserveParams), BuilderFactory.STRING); } public final CommandObject cfAdd(String key, String item) { return new CommandObject<>(commandArguments(CuckooFilterCommand.ADD).key(key).add(item), BuilderFactory.BOOLEAN); } public final CommandObject cfAddNx(String key, String item) { return new CommandObject<>(commandArguments(CuckooFilterCommand.ADDNX).key(key).add(item), BuilderFactory.BOOLEAN); } public final CommandObject> cfInsert(String key, String... items) { return new CommandObject<>(commandArguments(CuckooFilterCommand.INSERT).key(key) .add(RedisBloomKeyword.ITEMS).addObjects((Object[]) items), BuilderFactory.BOOLEAN_LIST); } public final CommandObject> cfInsert(String key, CFInsertParams insertParams, String... items) { return new CommandObject<>(commandArguments(CuckooFilterCommand.INSERT).key(key) .addParams(insertParams).add(RedisBloomKeyword.ITEMS).addObjects((Object[]) items), BuilderFactory.BOOLEAN_LIST); } public final CommandObject> cfInsertNx(String key, String... items) { return new CommandObject<>(commandArguments(CuckooFilterCommand.INSERTNX).key(key) .add(RedisBloomKeyword.ITEMS).addObjects((Object[]) items), BuilderFactory.BOOLEAN_LIST); } public final CommandObject> cfInsertNx(String key, CFInsertParams insertParams, String... items) { return new CommandObject<>(commandArguments(CuckooFilterCommand.INSERTNX).key(key) .addParams(insertParams).add(RedisBloomKeyword.ITEMS).addObjects((Object[]) items), BuilderFactory.BOOLEAN_LIST); } public final CommandObject cfExists(String key, String item) { return new CommandObject<>(commandArguments(CuckooFilterCommand.EXISTS).key(key).add(item), BuilderFactory.BOOLEAN); } public final CommandObject> cfMExists(String key, String... items) { return new CommandObject<>(commandArguments(CuckooFilterCommand.MEXISTS).key(key) .addObjects((Object[]) items), BuilderFactory.BOOLEAN_LIST); } public final CommandObject cfDel(String key, String item) { return new CommandObject<>(commandArguments(CuckooFilterCommand.DEL).key(key).add(item), BuilderFactory.BOOLEAN); } public final CommandObject cfCount(String key, String item) { return new CommandObject<>(commandArguments(CuckooFilterCommand.COUNT).key(key).add(item), BuilderFactory.LONG); } public final CommandObject> cfScanDump(String key, long iterator) { return new CommandObject<>(commandArguments(CuckooFilterCommand.SCANDUMP).key(key).add(iterator), BLOOM_SCANDUMP_RESPONSE); } public final CommandObject cfLoadChunk(String key, long iterator, byte[] data) { return new CommandObject<>(commandArguments(CuckooFilterCommand.LOADCHUNK).key(key).add(iterator).add(data), BuilderFactory.STRING); } public final CommandObject> cfInfo(String key) { return new CommandObject<>(commandArguments(CuckooFilterCommand.INFO).key(key), BuilderFactory.ENCODED_OBJECT_MAP); } public final CommandObject cmsInitByDim(String key, long width, long depth) { return new CommandObject<>(commandArguments(CountMinSketchCommand.INITBYDIM).key(key).add(width) .add(depth), BuilderFactory.STRING); } public final CommandObject cmsInitByProb(String key, double error, double probability) { return new CommandObject<>(commandArguments(CountMinSketchCommand.INITBYPROB).key(key).add(error) .add(probability), BuilderFactory.STRING); } public final CommandObject> cmsIncrBy(String key, Map itemIncrements) { CommandArguments args = commandArguments(CountMinSketchCommand.INCRBY).key(key); itemIncrements.entrySet().forEach(entry -> args.add(entry.getKey()).add(entry.getValue())); return new CommandObject<>(args, BuilderFactory.LONG_LIST); } public final CommandObject> cmsQuery(String key, String... items) { return new CommandObject<>(commandArguments(CountMinSketchCommand.QUERY).key(key) .addObjects((Object[]) items), BuilderFactory.LONG_LIST); } public final CommandObject cmsMerge(String destKey, String... keys) { return new CommandObject<>(commandArguments(CountMinSketchCommand.MERGE).key(destKey) .add(keys.length).keys((Object[]) keys), BuilderFactory.STRING); } public final CommandObject cmsMerge(String destKey, Map keysAndWeights) { CommandArguments args = commandArguments(CountMinSketchCommand.MERGE).key(destKey); args.add(keysAndWeights.size()); keysAndWeights.entrySet().forEach(entry -> args.key(entry.getKey())); args.add(RedisBloomKeyword.WEIGHTS); keysAndWeights.entrySet().forEach(entry -> args.add(entry.getValue())); return new CommandObject<>(args, BuilderFactory.STRING); } public final CommandObject> cmsInfo(String key) { return new CommandObject<>(commandArguments(CountMinSketchCommand.INFO).key(key), BuilderFactory.ENCODED_OBJECT_MAP); } public final CommandObject topkReserve(String key, long topk) { return new CommandObject<>(commandArguments(TopKCommand.RESERVE).key(key).add(topk), BuilderFactory.STRING); } public final CommandObject topkReserve(String key, long topk, long width, long depth, double decay) { return new CommandObject<>(commandArguments(TopKCommand.RESERVE).key(key).add(topk) .add(width).add(depth).add(decay), BuilderFactory.STRING); } public final CommandObject> topkAdd(String key, String... items) { return new CommandObject<>(commandArguments(TopKCommand.ADD).key(key).addObjects((Object[]) items), BuilderFactory.STRING_LIST); } public final CommandObject> topkIncrBy(String key, Map itemIncrements) { CommandArguments args = commandArguments(TopKCommand.INCRBY).key(key); itemIncrements.entrySet().forEach(entry -> args.add(entry.getKey()).add(entry.getValue())); return new CommandObject<>(args, BuilderFactory.STRING_LIST); } public final CommandObject> topkQuery(String key, String... items) { return new CommandObject<>(commandArguments(TopKCommand.QUERY).key(key).addObjects((Object[]) items), BuilderFactory.BOOLEAN_LIST); } public final CommandObject> topkList(String key) { return new CommandObject<>(commandArguments(TopKCommand.LIST).key(key), BuilderFactory.STRING_LIST); } public final CommandObject> topkListWithCount(String key) { return new CommandObject<>(commandArguments(TopKCommand.LIST).key(key) .add(RedisBloomKeyword.WITHCOUNT), BuilderFactory.STRING_LONG_MAP); } public final CommandObject> topkInfo(String key) { return new CommandObject<>(commandArguments(TopKCommand.INFO).key(key), BuilderFactory.ENCODED_OBJECT_MAP); } public final CommandObject tdigestCreate(String key) { return new CommandObject<>(commandArguments(TDigestCommand.CREATE).key(key), BuilderFactory.STRING); } public final CommandObject tdigestCreate(String key, int compression) { return new CommandObject<>(commandArguments(TDigestCommand.CREATE).key(key).add(RedisBloomKeyword.COMPRESSION) .add(compression), BuilderFactory.STRING); } public final CommandObject tdigestReset(String key) { return new CommandObject<>(commandArguments(TDigestCommand.RESET).key(key), BuilderFactory.STRING); } public final CommandObject tdigestMerge(String destinationKey, String... sourceKeys) { return new CommandObject<>(commandArguments(TDigestCommand.MERGE).key(destinationKey) .add(sourceKeys.length).keys((Object[]) sourceKeys), BuilderFactory.STRING); } public final CommandObject tdigestMerge(TDigestMergeParams mergeParams, String destinationKey, String... sourceKeys) { return new CommandObject<>(commandArguments(TDigestCommand.MERGE).key(destinationKey) .add(sourceKeys.length).keys((Object[]) sourceKeys).addParams(mergeParams), BuilderFactory.STRING); } public final CommandObject> tdigestInfo(String key) { return new CommandObject<>(commandArguments(TDigestCommand.INFO).key(key), BuilderFactory.ENCODED_OBJECT_MAP); } public final CommandObject tdigestAdd(String key, double... values) { return new CommandObject<>(addFlatArgs(commandArguments(TDigestCommand.ADD).key(key), values), BuilderFactory.STRING); } public final CommandObject> tdigestCDF(String key, double... values) { return new CommandObject<>(addFlatArgs(commandArguments(TDigestCommand.CDF).key(key), values), BuilderFactory.DOUBLE_LIST); } public final CommandObject> tdigestQuantile(String key, double... quantiles) { return new CommandObject<>(addFlatArgs(commandArguments(TDigestCommand.QUANTILE).key(key), quantiles), BuilderFactory.DOUBLE_LIST); } public final CommandObject tdigestMin(String key) { return new CommandObject<>(commandArguments(TDigestCommand.MIN).key(key), BuilderFactory.DOUBLE); } public final CommandObject tdigestMax(String key) { return new CommandObject<>(commandArguments(TDigestCommand.MAX).key(key), BuilderFactory.DOUBLE); } public final CommandObject tdigestTrimmedMean(String key, double lowCutQuantile, double highCutQuantile) { return new CommandObject<>(commandArguments(TDigestCommand.TRIMMED_MEAN).key(key).add(lowCutQuantile) .add(highCutQuantile), BuilderFactory.DOUBLE); } public final CommandObject> tdigestRank(String key, double... values) { return new CommandObject<>(addFlatArgs(commandArguments(TDigestCommand.RANK).key(key), values), BuilderFactory.LONG_LIST); } public final CommandObject> tdigestRevRank(String key, double... values) { return new CommandObject<>(addFlatArgs(commandArguments(TDigestCommand.REVRANK).key(key), values), BuilderFactory.LONG_LIST); } public final CommandObject> tdigestByRank(String key, long... ranks) { return new CommandObject<>(addFlatArgs(commandArguments(TDigestCommand.BYRANK).key(key), ranks), BuilderFactory.DOUBLE_LIST); } public final CommandObject> tdigestByRevRank(String key, long... ranks) { return new CommandObject<>(addFlatArgs(commandArguments(TDigestCommand.BYREVRANK).key(key), ranks), BuilderFactory.DOUBLE_LIST); } // RedisBloom commands // Transaction commands public final CommandObject watch(String... keys) { return new CommandObject<>(commandArguments(WATCH).keys((Object[]) keys), BuilderFactory.STRING); } public final CommandObject watch(byte[]... keys) { return new CommandObject<>(commandArguments(WATCH).keys((Object[]) keys), BuilderFactory.STRING); } // Transaction commands /** * Get the instance for JsonObjectMapper if not null, otherwise a new instance reference with * default implementation will be created and returned. *

This process of checking whether or not * the instance reference exists follows 'double-checked lock optimization' approach to reduce the overhead of * acquiring a lock by testing the lock criteria (the "lock hint") before acquiring the lock.

* @return the JsonObjectMapper instance reference * @see DefaultGsonObjectMapper */ private JsonObjectMapper getJsonObjectMapper() { JsonObjectMapper localRef = this.jsonObjectMapper; if (Objects.isNull(localRef)) { mapperLock.lock(); try { localRef = this.jsonObjectMapper; if (Objects.isNull(localRef)) { this.jsonObjectMapper = localRef = new DefaultGsonObjectMapper(); } } finally { mapperLock.unlock(); } } return localRef; } public void setJsonObjectMapper(JsonObjectMapper jsonObjectMapper) { this.jsonObjectMapper = jsonObjectMapper; } public void setDefaultSearchDialect(int dialect) { if (dialect == 0) throw new IllegalArgumentException("DIALECT=0 cannot be set."); this.searchDialect.set(dialect); } private class SearchProfileResponseBuilder extends Builder> { private static final String PROFILE_STR_REDIS7 = "profile"; private static final String PROFILE_STR_REDIS8 = "Profile"; private static final String RESULTS_STR_REDIS7 = "results"; private static final String RESULTS_STR_REDIS8 = "Results"; private final Builder resultsBuilder; public SearchProfileResponseBuilder(Builder resultsBuilder) { this.resultsBuilder = resultsBuilder; } @Override public Map.Entry build(Object data) { List list = (List) data; if (list == null || list.isEmpty()) return null; if (list.get(0) instanceof KeyValue) { // RESP3 Object resultsData = null, profileData = null; for (KeyValue keyValue : (List) data) { String keyStr = BuilderFactory.STRING.build(keyValue.getKey()); switch (keyStr) { case PROFILE_STR_REDIS7: case PROFILE_STR_REDIS8: profileData = keyValue.getValue(); break; case RESULTS_STR_REDIS7: resultsData = data; break; case RESULTS_STR_REDIS8: resultsData = keyValue.getValue(); break; } } assert resultsData != null : "Could not detect Results data."; assert profileData != null : "Could not detect Profile data."; return KeyValue.of(resultsBuilder.build(resultsData), ProfilingInfo.PROFILING_INFO_BUILDER.build(profileData)); } return KeyValue.of(resultsBuilder.build(list.get(0)), ProfilingInfo.PROFILING_INFO_BUILDER.build(list.get(1))); } } private class JsonObjectBuilder extends Builder { private final Class clazz; public JsonObjectBuilder(Class clazz) { this.clazz = clazz; } @Override public T build(Object data) { return getJsonObjectMapper().fromJson(BuilderFactory.STRING.build(data), clazz); } } /** * {@link JsonObjectBuilder} for {@code Object.class}. */ private final Builder JSON_GENERIC_OBJECT = new JsonObjectBuilder<>(Object.class); private class JsonObjectListBuilder extends Builder> { private final Class clazz; public JsonObjectListBuilder(Class clazz) { this.clazz = clazz; } @Override public List build(Object data) { if (data == null) { return null; } List list = BuilderFactory.STRING_LIST.build(data); return list.stream().map(s -> getJsonObjectMapper().fromJson(s, clazz)).collect(Collectors.toList()); } } private static final Builder> BLOOM_SCANDUMP_RESPONSE = new Builder>() { @Override public Map.Entry build(Object data) { List list = (List) data; return new KeyValue<>(BuilderFactory.LONG.build(list.get(0)), BuilderFactory.BINARY.build(list.get(1))); } }; private CommandArguments addFlatArgs(CommandArguments args, long... values) { for (long value : values) { args.add(value); } return args; } private CommandArguments addFlatArgs(CommandArguments args, double... values) { for (double value : values) { args.add(value); } return args; } private CommandArguments addFlatKeyValueArgs(CommandArguments args, String... keyvalues) { for (int i = 0; i < keyvalues.length; i += 2) { args.key(keyvalues[i]).add(keyvalues[i + 1]); } return args; } private CommandArguments addFlatKeyValueArgs(CommandArguments args, byte[]... keyvalues) { for (int i = 0; i < keyvalues.length; i += 2) { args.key(keyvalues[i]).add(keyvalues[i + 1]); } return args; } private CommandArguments addFlatMapArgs(CommandArguments args, Map map) { for (Map.Entry entry : map.entrySet()) { args.add(entry.getKey()); args.add(entry.getValue()); } return args; } private CommandArguments addSortedSetFlatMapArgs(CommandArguments args, Map map) { for (Map.Entry entry : map.entrySet()) { args.add(entry.getValue()); args.add(entry.getKey()); } return args; } private CommandArguments addGeoCoordinateFlatMapArgs(CommandArguments args, Map map) { for (Map.Entry entry : map.entrySet()) { GeoCoordinate ord = entry.getValue(); args.add(ord.getLongitude()); args.add(ord.getLatitude()); args.add(entry.getKey()); } return args; } // Vector Set commands public final CommandObject vadd(String key, float[] vector, String element) { return vadd(key, vector, element, null); } public final CommandObject vadd(String key, float[] vector, String element, VAddParams params) { CommandArguments args = commandArguments(Command.VADD).key(key); addVectors(vector, args); args.add(element); addOptionalParams(params, args); return new CommandObject<>(args, BuilderFactory.BOOLEAN); } public final CommandObject vaddFP32(String key, byte[] vectorBlob, String element) { return vaddFP32(key, vectorBlob, element, null); } public final CommandObject vaddFP32(String key, byte[] vectorBlob, String element, VAddParams params) { CommandArguments args = commandArguments(Command.VADD).key(key); args.add(Keyword.FP32).add(vectorBlob).add(element); addOptionalParams(params, args); return new CommandObject<>(args, BuilderFactory.BOOLEAN); } public final CommandObject vadd(byte[] key, float[] vector, byte[] element) { return vadd(key, vector, element, null); } public final CommandObject vadd(byte[] key, float[] vector, byte[] element, VAddParams params) { CommandArguments args = commandArguments(Command.VADD).key(key); addVectors(vector, args); args.add(element); addOptionalParams(params, args); return new CommandObject<>(args, BuilderFactory.BOOLEAN); } public final CommandObject vaddFP32(byte[] key, byte[] vectorBlob, byte[] element) { return vaddFP32(key, vectorBlob, element, null); } public final CommandObject vaddFP32(byte[] key, byte[] vectorBlob, byte[] element, VAddParams params) { CommandArguments args = commandArguments(Command.VADD).key(key); args.add(Keyword.FP32).add(vectorBlob).add(element); addOptionalParams(params, args); return new CommandObject<>(args, BuilderFactory.BOOLEAN); } public final CommandObject vadd(String key, float[] vector, String element, int reduceDim, VAddParams params) { CommandArguments args = commandArguments(Command.VADD).key(key); args.add(Keyword.REDUCE).add(reduceDim); addVectors(vector, args); args.add(element); addOptionalParams(params, args); return new CommandObject<>(args, BuilderFactory.BOOLEAN); } public final CommandObject vaddFP32(String key, byte[] vectorBlob, String element, int reduceDim, VAddParams params) { CommandArguments args = commandArguments(Command.VADD).key(key); args.add(Keyword.REDUCE).add(reduceDim); args.add(Keyword.FP32).add(vectorBlob).add(element); args.addParams(params); return new CommandObject<>(args, BuilderFactory.BOOLEAN); } public final CommandObject vadd(byte[] key, float[] vector, byte[] element, int reduceDim, VAddParams params) { CommandArguments args = commandArguments(Command.VADD).key(key); args.add(Keyword.REDUCE).add(reduceDim); addVectors(vector, args); args.add(element); addOptionalParams(params, args); return new CommandObject<>(args, BuilderFactory.BOOLEAN); } private static void addOptionalParams(VAddParams params, CommandArguments args) { if (params != null) { args.addParams(params); } } private static void addVectors(float[] vector, CommandArguments args) { args.add(Keyword.VALUES).add(vector.length); for (float value : vector) { args.add(value); } } public final CommandObject vaddFP32(byte[] key, byte[] vectorBlob, byte[] element, int reduceDim, VAddParams params) { CommandArguments args = commandArguments(Command.VADD).key(key); args.add(Keyword.REDUCE).add(reduceDim); args.add(Keyword.FP32).add(vectorBlob).add(element); addOptionalParams(params, args); return new CommandObject<>(args, BuilderFactory.BOOLEAN); } public final CommandObject> vsim(String key, float[] vector) { return vsim(key, vector, null); } public final CommandObject> vsim(String key, float[] vector, VSimParams params) { CommandArguments args = commandArguments(Command.VSIM).key(key); addVectors(vector, args); addOptionalParams(params, args); return new CommandObject<>(args, BuilderFactory.STRING_LIST); } private static void addOptionalParams(VSimParams params, CommandArguments args) { if (params != null) { args.addParams(params); } } public final CommandObject> vsimWithScores(String key, float[] vector, VSimParams params) { CommandArguments args = commandArguments(Command.VSIM).key(key); addVectors(vector, args); args.add(Keyword.WITHSCORES); addOptionalParams(params, args); return new CommandObject<>(args, BuilderFactory.STRING_DOUBLE_MAP); } public final CommandObject> vsimWithScoresAndAttribs(String key, float[] vector, VSimParams params) { CommandArguments args = commandArguments(Command.VSIM).key(key); addVectors(vector, args); args.add(Keyword.WITHSCORES); args.add(Keyword.WITHATTRIBS); addOptionalParams(params, args); return new CommandObject<>(args, BuilderFactory.VSIM_SCORE_ATTRIBS_MAP); } public final CommandObject> vsimByElement(String key, String element) { return vsimByElement(key, element, null); } public final CommandObject> vsimByElement(String key, String element, VSimParams params) { CommandArguments args = commandArguments(Command.VSIM).key(key); args.add(Keyword.ELE).add(element); addOptionalParams(params, args); return new CommandObject<>(args, BuilderFactory.STRING_LIST); } public final CommandObject> vsimByElementWithScores(String key, String element, VSimParams params) { CommandArguments args = commandArguments(Command.VSIM).key(key); args.add(Keyword.ELE).add(element); args.add(Keyword.WITHSCORES); addOptionalParams(params, args); return new CommandObject<>(args, BuilderFactory.STRING_DOUBLE_MAP); } public final CommandObject> vsimByElementWithScoresAndAttribs(String key, String element, VSimParams params) { CommandArguments args = commandArguments(Command.VSIM).key(key); args.add(Keyword.ELE).add(element); args.add(Keyword.WITHSCORES); args.add(Keyword.WITHATTRIBS); addOptionalParams(params, args); return new CommandObject<>(args, BuilderFactory.VSIM_SCORE_ATTRIBS_MAP); } public final CommandObject> vsim(byte[] key, float[] vector) { return vsim(key, vector, null); } public final CommandObject> vsim(byte[] key, float[] vector, VSimParams params) { CommandArguments args = commandArguments(Command.VSIM).key(key); addVectors(vector, args); addOptionalParams(params, args); return new CommandObject<>(args, BuilderFactory.BINARY_LIST); } public final CommandObject> vsimWithScores(byte[] key, float[] vector, VSimParams params) { CommandArguments args = commandArguments(Command.VSIM).key(key); addVectors(vector, args); args.add(Keyword.WITHSCORES); addOptionalParams(params, args); return new CommandObject<>(args, BuilderFactory.BINARY_DOUBLE_MAP); } public final CommandObject> vsimWithScoresAndAttribs(byte[] key, float[] vector, VSimParams params) { CommandArguments args = commandArguments(Command.VSIM).key(key); addVectors(vector, args); args.add(Keyword.WITHSCORES); args.add(Keyword.WITHATTRIBS); addOptionalParams(params, args); return new CommandObject<>(args, BuilderFactory.VSIM_SCORE_ATTRIBS_BINARY_MAP); } public final CommandObject> vsimByElement(byte[] key, byte[] element) { return vsimByElement(key, element, null); } public final CommandObject> vsimByElement(byte[] key, byte[] element, VSimParams params) { CommandArguments args = commandArguments(Command.VSIM).key(key); args.add(Keyword.ELE).add(element); addOptionalParams(params, args); return new CommandObject<>(args, BuilderFactory.BINARY_LIST); } public final CommandObject> vsimByElementWithScores(byte[] key, byte[] element, VSimParams params) { CommandArguments args = commandArguments(Command.VSIM).key(key); args.add(Keyword.ELE).add(element); args.add(Keyword.WITHSCORES); addOptionalParams(params, args); return new CommandObject<>(args, BuilderFactory.BINARY_DOUBLE_MAP); } public final CommandObject> vsimByElementWithScoresAndAttribs(byte[] key, byte[] element, VSimParams params) { CommandArguments args = commandArguments(Command.VSIM).key(key); args.add(Keyword.ELE).add(element); args.add(Keyword.WITHSCORES); args.add(Keyword.WITHATTRIBS); addOptionalParams(params, args); return new CommandObject<>(args, BuilderFactory.VSIM_SCORE_ATTRIBS_BINARY_MAP); } public final CommandObject vdim(String key) { return new CommandObject<>(commandArguments(Command.VDIM).key(key), BuilderFactory.LONG); } public final CommandObject vdim(byte[] key) { return new CommandObject<>(commandArguments(Command.VDIM).key(key), BuilderFactory.LONG); } public final CommandObject vcard(String key) { return new CommandObject<>(commandArguments(Command.VCARD).key(key), BuilderFactory.LONG); } public final CommandObject vcard(byte[] key) { return new CommandObject<>(commandArguments(Command.VCARD).key(key), BuilderFactory.LONG); } public final CommandObject> vemb(String key, String element) { return new CommandObject<>(commandArguments(Command.VEMB).key(key).add(element), BuilderFactory.DOUBLE_LIST); } public final CommandObject vembRaw(String key, String element) { return new CommandObject<>(commandArguments(Command.VEMB).key(key).add(element).add(Keyword.RAW), BuilderFactory.VEMB_RAW_RESULT); } public final CommandObject> vemb(byte[] key, byte[] element) { return new CommandObject<>(commandArguments(Command.VEMB).key(key).add(element), BuilderFactory.DOUBLE_LIST); } public final CommandObject vembRaw(byte[] key, byte[] element) { return new CommandObject<>(commandArguments(Command.VEMB).key(key).add(element).add(Keyword.RAW), BuilderFactory.VEMB_RAW_RESULT); } public final CommandObject vrem(String key, String element) { return new CommandObject<>(commandArguments(Command.VREM).key(key).add(element), BuilderFactory.BOOLEAN); } public final CommandObject vrem(byte[] key, byte[] element) { return new CommandObject<>(commandArguments(Command.VREM).key(key).add(element), BuilderFactory.BOOLEAN); } public final CommandObject>> vlinks(String key, String element) { return new CommandObject<>(commandArguments(Command.VLINKS).key(key).add(element), BuilderFactory.STRING_LIST_LIST); } public final CommandObject>> vlinksWithScores(String key, String element) { return new CommandObject<>(commandArguments(Command.VLINKS).key(key).add(element).add(Keyword.WITHSCORES), BuilderFactory.VLINKS_WITH_SCORES_RESULT); } public final CommandObject>> vlinks(byte[] key, byte[] element) { return new CommandObject<>(commandArguments(Command.VLINKS).key(key).add(element), BuilderFactory.BINARY_LIST_LIST); } public final CommandObject>> vlinksWithScores(byte[] key, byte[] element) { return new CommandObject<>(commandArguments(Command.VLINKS).key(key).add(element).add(Keyword.WITHSCORES), BuilderFactory.VLINKS_WITH_SCORES_RESULT_BINARY); } public final CommandObject vrandmember(String key) { return new CommandObject<>(commandArguments(Command.VRANDMEMBER).key(key), BuilderFactory.STRING); } public final CommandObject> vrandmember(String key, int count) { return new CommandObject<>(commandArguments(Command.VRANDMEMBER).key(key).add(count), BuilderFactory.STRING_LIST); } public final CommandObject vrandmember(byte[] key) { return new CommandObject<>(commandArguments(Command.VRANDMEMBER).key(key), BuilderFactory.BINARY); } public final CommandObject> vrandmember(byte[] key, int count) { return new CommandObject<>(commandArguments(Command.VRANDMEMBER).key(key).add(count), BuilderFactory.BINARY_LIST); } public final CommandObject vgetattr(String key, String element) { return new CommandObject<>(commandArguments(Command.VGETATTR).key(key).add(element), BuilderFactory.STRING); } public final CommandObject vgetattr(byte[] key, byte[] element) { return new CommandObject<>(commandArguments(Command.VGETATTR).key(key).add(element), BuilderFactory.BINARY); } public final CommandObject vsetattr(String key, String element, String attributes) { return new CommandObject<>(commandArguments(Command.VSETATTR).key(key).add(element).add(attributes), BuilderFactory.BOOLEAN); } public final CommandObject vsetattr(byte[] key, byte[] element, byte[] attributes) { return new CommandObject<>(commandArguments(Command.VSETATTR).key(key).add(element).add(attributes), BuilderFactory.BOOLEAN); } public final CommandObject vinfo(String key) { return new CommandObject<>(commandArguments(Command.VINFO).key(key), BuilderFactory.VECTOR_INFO); } } ================================================ FILE: src/main/java/redis/clients/jedis/Connection.java ================================================ package redis.clients.jedis; import static redis.clients.jedis.util.SafeEncoder.encode; import java.io.Closeable; import java.io.IOException; import java.net.Socket; import java.net.SocketAddress; import java.net.SocketException; import java.nio.ByteBuffer; import java.nio.CharBuffer; import java.util.ArrayList; import java.util.Arrays; import java.util.List; import java.util.Map; import java.util.function.Supplier; import java.util.concurrent.atomic.AtomicReference; import redis.clients.jedis.Protocol.Command; import redis.clients.jedis.Protocol.Keyword; import redis.clients.jedis.annots.Experimental; import redis.clients.jedis.args.ClientAttributeOption; import redis.clients.jedis.args.Rawable; import redis.clients.jedis.authentication.AuthXManager; import redis.clients.jedis.commands.ProtocolCommand; import redis.clients.jedis.exceptions.JedisConnectionException; import redis.clients.jedis.exceptions.JedisDataException; import redis.clients.jedis.exceptions.JedisException; import redis.clients.jedis.exceptions.JedisValidationException; import redis.clients.jedis.util.IOUtils; import redis.clients.jedis.util.RedisInputStream; import redis.clients.jedis.util.RedisOutputStream; public class Connection implements Closeable { public static class Builder { private JedisSocketFactory socketFactory; private JedisClientConfig clientConfig; public Builder socketFactory(JedisSocketFactory socketFactory) { this.socketFactory = socketFactory; return this; } public Builder clientConfig(JedisClientConfig clientConfig) { this.clientConfig = clientConfig; return this; } public JedisSocketFactory getSocketFactory() { return socketFactory; } public JedisClientConfig getClientConfig() { return clientConfig; } public Connection build() { Connection conn = new Connection(this); conn.initializeFromClientConfig(); return conn; } } public static Builder builder() { return new Builder(); } private ConnectionPool memberOf; protected RedisProtocol protocol; private final JedisSocketFactory socketFactory; private Socket socket; private RedisOutputStream outputStream; private RedisInputStream inputStream; private int soTimeout = 0; private int infiniteSoTimeout = 0; private boolean broken = false; private boolean strValActive; private String strVal; protected String server; protected String version; private AtomicReference currentCredentials = new AtomicReference<>(null); private AuthXManager authXManager; private JedisClientConfig clientConfig; public Connection() { this(Protocol.DEFAULT_HOST, Protocol.DEFAULT_PORT); } public Connection(final String host, final int port) { this(new HostAndPort(host, port)); } public Connection(final HostAndPort hostAndPort) { this(new DefaultJedisSocketFactory(hostAndPort)); } public Connection(final HostAndPort hostAndPort, final JedisClientConfig clientConfig) { this(new DefaultJedisSocketFactory(hostAndPort, clientConfig), clientConfig); } public Connection(final JedisSocketFactory socketFactory) { this.socketFactory = socketFactory; } public Connection(final JedisSocketFactory socketFactory, JedisClientConfig clientConfig) { this.socketFactory = socketFactory; this.clientConfig = clientConfig; initializeFromClientConfig(clientConfig); } protected Connection(Builder builder) { this.socketFactory = builder.getSocketFactory(); this.clientConfig = builder.getClientConfig(); } @Override public String toString() { return getClass().getSimpleName() + "{" + socketFactory + "}"; } @Experimental public String toIdentityString() { if (strValActive == broken && strVal != null) { return strVal; } String className = getClass().getSimpleName(); int id = hashCode(); if (socket == null) { return String.format("%s{id: 0x%X}", className, id); } SocketAddress remoteAddr = socket.getRemoteSocketAddress(); SocketAddress localAddr = socket.getLocalSocketAddress(); if (remoteAddr != null) { strVal = String.format("%s{id: 0x%X, L:%s %c R:%s}", className, id, localAddr, (broken ? '!' : '-'), remoteAddr); } else if (localAddr != null) { strVal = String.format("%s{id: 0x%X, L:%s}", className, id, localAddr); } else { strVal = String.format("%s{id: 0x%X}", className, id); } strValActive = broken; return strVal; } public final RedisProtocol getRedisProtocol() { return protocol; } public final void setHandlingPool(final ConnectionPool pool) { this.memberOf = pool; } /** * Returns the host and port of the Redis server this connection is connected to. * * @return the host and port, or null if not available */ public final HostAndPort getHostAndPort() { return ((DefaultJedisSocketFactory) socketFactory).getHostAndPort(); } public int getSoTimeout() { return soTimeout; } public void setSoTimeout(int soTimeout) { this.soTimeout = soTimeout; if (this.socket != null) { try { this.socket.setSoTimeout(soTimeout); } catch (SocketException ex) { setBroken(); throw new JedisConnectionException(ex); } } } public void setTimeoutInfinite() { try { if (!isConnected()) { connect(); } socket.setSoTimeout(infiniteSoTimeout); } catch (SocketException ex) { setBroken(); throw new JedisConnectionException(ex); } } public void rollbackTimeout() { try { socket.setSoTimeout(this.soTimeout); } catch (SocketException ex) { setBroken(); throw new JedisConnectionException(ex); } } public Object executeCommand(final ProtocolCommand cmd) { return executeCommand(new CommandArguments(cmd)); } public Object executeCommand(final CommandArguments args) { sendCommand(args); return getOne(); } public T executeCommand(final CommandObject commandObject) { final CommandArguments args = commandObject.getArguments(); sendCommand(args); if (!args.isBlocking()) { return commandObject.getBuilder().build(getOne()); } else { try { setTimeoutInfinite(); return commandObject.getBuilder().build(getOne()); } finally { rollbackTimeout(); } } } public void sendCommand(final ProtocolCommand cmd) { sendCommand(new CommandArguments(cmd)); } public void sendCommand(final ProtocolCommand cmd, Rawable keyword) { sendCommand(new CommandArguments(cmd).add(keyword)); } public void sendCommand(final ProtocolCommand cmd, final String... args) { sendCommand(new CommandArguments(cmd).addObjects((Object[]) args)); } public void sendCommand(final ProtocolCommand cmd, final byte[]... args) { sendCommand(new CommandArguments(cmd).addObjects((Object[]) args)); } public void sendCommand(final CommandArguments args) { try { connect(); Protocol.sendCommand(outputStream, args); } catch (JedisConnectionException ex) { /* * When client send request which formed by invalid protocol, Redis send back error message * before close connection. We try to read it to provide reason of failure. */ try { String errorMessage = Protocol.readErrorLineIfPossible(inputStream); if (errorMessage != null && errorMessage.length() > 0) { ex = new JedisConnectionException(errorMessage, ex.getCause()); } } catch (Exception e) { /* * Catch any IOException or JedisConnectionException occurred from InputStream#read and just * ignore. This approach is safe because reading error message is optional and connection * will eventually be closed. */ } // Any other exceptions related to connection? setBroken(); throw ex; } } public void connect() throws JedisConnectionException { if (!isConnected()) { try { socket = socketFactory.createSocket(); soTimeout = socket.getSoTimeout(); // ? outputStream = new RedisOutputStream(socket.getOutputStream()); inputStream = new RedisInputStream(socket.getInputStream()); broken = false; // unset broken status when connection is (re)initialized } catch (JedisConnectionException jce) { setBroken(); throw jce; } catch (IOException ioe) { setBroken(); throw new JedisConnectionException("Failed to create input/output stream", ioe); } finally { if (broken) { IOUtils.closeQuietly(socket); } } } } @Override public void close() { if (this.memberOf != null) { ConnectionPool pool = this.memberOf; this.memberOf = null; if (isBroken()) { pool.returnBrokenResource(this); } else { pool.returnResource(this); } } else { disconnect(); } } /** * Close the socket and disconnect the server. */ public void disconnect() { if (isConnected()) { try { outputStream.flush(); socket.close(); } catch (IOException ex) { throw new JedisConnectionException(ex); } finally { IOUtils.closeQuietly(socket); setBroken(); } } } public void forceDisconnect() throws IOException { // setBroken() must be called first here, // otherwise a concurrent close attempt would call 'returnResource' (instead of // 'returnBrokenResource'), // assuming it's an open/healthy connection whereas this individual socket is already closed. setBroken(); IOUtils.closeQuietly(socket); } public boolean isConnected() { return socket != null && socket.isBound() && !socket.isClosed() && socket.isConnected() && !socket.isInputShutdown() && !socket.isOutputShutdown(); } public boolean isBroken() { return broken; } public void setBroken() { broken = true; } public String getStatusCodeReply() { flush(); final byte[] resp = (byte[]) readProtocolWithCheckingBroken(); if (null == resp) { return null; } else { return encode(resp); } } public String getBulkReply() { final byte[] result = getBinaryBulkReply(); if (null != result) { return encode(result); } else { return null; } } public byte[] getBinaryBulkReply() { flush(); return (byte[]) readProtocolWithCheckingBroken(); } public Long getIntegerReply() { flush(); return (Long) readProtocolWithCheckingBroken(); } public List getMultiBulkReply() { return BuilderFactory.STRING_LIST.build(getBinaryMultiBulkReply()); } @SuppressWarnings("unchecked") public List getBinaryMultiBulkReply() { flush(); return (List) readProtocolWithCheckingBroken(); } /** * @deprecated Use {@link Connection#getUnflushedObject()}. */ @Deprecated @SuppressWarnings("unchecked") public List getUnflushedObjectMultiBulkReply() { return (List) readProtocolWithCheckingBroken(); } @SuppressWarnings("unchecked") public Object getUnflushedObject() { return readProtocolWithCheckingBroken(); } public List getObjectMultiBulkReply() { flush(); return (List) readProtocolWithCheckingBroken(); } @SuppressWarnings("unchecked") public List getIntegerMultiBulkReply() { flush(); return (List) readProtocolWithCheckingBroken(); } public Object getOne() { flush(); return readProtocolWithCheckingBroken(); } protected void flush() { try { outputStream.flush(); } catch (IOException ex) { setBroken(); throw new JedisConnectionException(ex); } } @Experimental protected Object protocolRead(RedisInputStream is) { return Protocol.read(is); } @Experimental protected void protocolReadPushes(RedisInputStream is) { } protected Object readProtocolWithCheckingBroken() { if (broken) { throw new JedisConnectionException("Attempting to read from a broken connection."); } try { return protocolRead(inputStream); } catch (JedisConnectionException exc) { broken = true; throw exc; } } protected void readPushesWithCheckingBroken() { if (broken) { throw new JedisConnectionException("Attempting to read from a broken connection."); } try { if (inputStream.available() > 0) { protocolReadPushes(inputStream); } } catch (IOException e) { broken = true; throw new JedisConnectionException("Failed to check buffer on connection.", e); } catch (JedisConnectionException exc) { setBroken(); throw exc; } } public List getMany(final int count) { flush(); final List responses = new ArrayList<>(count); for (int i = 0; i < count; i++) { try { responses.add(readProtocolWithCheckingBroken()); } catch (JedisDataException e) { responses.add(e); } } return responses; } /** * Check if the client name libname, libver, characters are legal * @param info the name * @return Returns true if legal, false throws exception * @throws JedisException if characters illegal */ private static boolean validateClientInfo(String info) { for (int i = 0; i < info.length(); i++) { char c = info.charAt(i); if (c < '!' || c > '~') { throw new JedisValidationException( "client info cannot contain spaces, " + "newlines or special characters."); } } return true; } public void initializeFromClientConfig() { this.initializeFromClientConfig(clientConfig); } protected void initializeFromClientConfig(final JedisClientConfig config) { try { this.soTimeout = config.getSocketTimeoutMillis(); this.infiniteSoTimeout = config.getBlockingSocketTimeoutMillis(); connect(); protocol = config.getRedisProtocol(); Supplier credentialsProvider = config.getCredentialsProvider(); authXManager = config.getAuthXManager(); if (authXManager != null) { credentialsProvider = authXManager; } if (credentialsProvider instanceof RedisCredentialsProvider) { final RedisCredentialsProvider redisCredentialsProvider = (RedisCredentialsProvider) credentialsProvider; try { redisCredentialsProvider.prepare(); helloAndAuth(protocol, redisCredentialsProvider.get()); } finally { redisCredentialsProvider.cleanUp(); } } else { helloAndAuth(protocol, credentialsProvider != null ? credentialsProvider.get() : new DefaultRedisCredentials(config.getUser(), config.getPassword())); } List fireAndForgetMsg = new ArrayList<>(); String clientName = config.getClientName(); if (clientName != null && validateClientInfo(clientName)) { fireAndForgetMsg .add(new CommandArguments(Command.CLIENT).add(Keyword.SETNAME).add(clientName)); } ClientSetInfoConfig setInfoConfig = config.getClientSetInfoConfig(); if (setInfoConfig == null) { setInfoConfig = ClientSetInfoConfig.DEFAULT; } if (!setInfoConfig.isDisabled()) { fireAndForgetMsg.add( new CommandArguments(Command.CLIENT).add(Keyword.SETINFO).add(ClientAttributeOption.LIB_NAME.getRaw()) .add(setInfoConfig.getDriverInfo().getFormattedName())); String libVersion = JedisMetaInfo.getVersion(); if (libVersion != null && validateClientInfo(libVersion)) { fireAndForgetMsg.add( new CommandArguments(Command.CLIENT).add(Keyword.SETINFO).add(ClientAttributeOption.LIB_VER.getRaw()) .add(libVersion)); } } // set READONLY flag to ALL connections (including master nodes) when enable read from replica if (config.isReadOnlyForRedisClusterReplicas()) { fireAndForgetMsg.add(new CommandArguments(Command.READONLY)); } for (CommandArguments arg : fireAndForgetMsg) { sendCommand(arg); } getMany(fireAndForgetMsg.size()); int dbIndex = config.getDatabase(); if (dbIndex > 0) { select(dbIndex); } } catch (JedisException je) { try { disconnect(); } catch (Exception e) { // the first exception 'je' will be thrown } throw je; } } private void helloAndAuth(final RedisProtocol protocol, final RedisCredentials credentials) { Map helloResult = null; if (protocol != null && credentials != null && credentials.getUser() != null) { byte[] rawPass = encodeToBytes(credentials.getPassword()); try { helloResult = hello(encode(protocol.version()), Keyword.AUTH.getRaw(), encode(credentials.getUser()), rawPass); } finally { Arrays.fill(rawPass, (byte) 0); // clear sensitive data } } else { authenticate(credentials); helloResult = protocol == null ? null : hello(encode(protocol.version())); } if (helloResult != null) { server = (String) helloResult.get("server"); version = (String) helloResult.get("version"); } // clearing 'char[] credentials.getPassword()' should be // handled in RedisCredentialsProvider.cleanUp() } public void setCredentials(RedisCredentials credentials) { currentCredentials.set(credentials); } private String authenticate(RedisCredentials credentials) { if (credentials == null || credentials.getPassword() == null) { return null; } byte[] rawPass = encodeToBytes(credentials.getPassword()); try { if (credentials.getUser() == null) { sendCommand(Command.AUTH, rawPass); } else { sendCommand(Command.AUTH, encode(credentials.getUser()), rawPass); } } finally { Arrays.fill(rawPass, (byte) 0); // clear sensitive data } return getStatusCodeReply(); } public String reAuthenticate() { return authenticate(currentCredentials.getAndSet(null)); } protected Map hello(byte[]... args) { sendCommand(Command.HELLO, args); return BuilderFactory.ENCODED_OBJECT_MAP.build(getOne()); } protected byte[] encodeToBytes(char[] chars) { // Source: https://stackoverflow.com/a/9670279/4021802 ByteBuffer passBuf = Protocol.CHARSET.encode(CharBuffer.wrap(chars)); byte[] rawPass = Arrays.copyOfRange(passBuf.array(), passBuf.position(), passBuf.limit()); Arrays.fill(passBuf.array(), (byte) 0); // clear sensitive data return rawPass; } public String select(final int index) { sendCommand(Command.SELECT, Protocol.toByteArray(index)); return getStatusCodeReply(); } public boolean ping() { sendCommand(Command.PING); String status = getStatusCodeReply(); if (!"PONG".equals(status)) { throw new JedisException(status); } return true; } protected boolean isTokenBasedAuthenticationEnabled() { return authXManager != null; } protected AuthXManager getAuthXManager() { return authXManager; } } ================================================ FILE: src/main/java/redis/clients/jedis/ConnectionFactory.java ================================================ package redis.clients.jedis; import org.apache.commons.pool2.PooledObject; import org.apache.commons.pool2.PooledObjectFactory; import org.apache.commons.pool2.impl.DefaultPooledObject; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import java.util.function.Supplier; import redis.clients.jedis.annots.Experimental; import redis.clients.jedis.authentication.AuthXManager; import redis.clients.jedis.authentication.JedisAuthenticationException; import redis.clients.jedis.authentication.AuthXEventListener; import redis.clients.jedis.csc.Cache; import redis.clients.jedis.csc.CacheConnection; import redis.clients.jedis.exceptions.JedisException; /** * PoolableObjectFactory custom impl. */ public class ConnectionFactory implements PooledObjectFactory { public static class Builder { private JedisClientConfig clientConfig; private Connection.Builder connectionBuilder; private JedisSocketFactory jedisSocketFactory; private Cache cache; private HostAndPort hostAndPort; // Fluent API methods (preferred) public Builder clientConfig(JedisClientConfig clientConfig) { this.clientConfig = clientConfig; return this; } public Builder connectionBuilder(Connection.Builder connectionBuilder) { this.connectionBuilder = connectionBuilder; return this; } public Builder socketFactory(JedisSocketFactory jedisSocketFactory) { this.jedisSocketFactory = jedisSocketFactory; return this; } public Builder cache(Cache cache) { this.cache = cache; return this; } public Builder hostAndPort(HostAndPort hostAndPort) { this.hostAndPort = hostAndPort; return this; } public Connection.Builder getConnectionBuilder() { return connectionBuilder; } public JedisSocketFactory getJedisSocketFactory() { return jedisSocketFactory; } public JedisClientConfig getClientConfig() { return clientConfig; } public Cache getCache() { return cache; } public ConnectionFactory build() { withDefaults(); return new ConnectionFactory(this); } private Builder withDefaults() { if (jedisSocketFactory == null) { this.jedisSocketFactory = createDefaultSocketFactory(); } if (connectionBuilder == null) { this.connectionBuilder = createDefaultConnectionBuilder(); } return this; } private JedisSocketFactory createDefaultSocketFactory() { if (clientConfig == null) { clientConfig = DefaultJedisClientConfig.builder().build(); } if (hostAndPort == null) { throw new IllegalStateException("HostAndPort is required when no socketFactory is provided"); } return new DefaultJedisSocketFactory(hostAndPort, clientConfig); } private Connection.Builder createDefaultConnectionBuilder() { Connection.Builder connBuilder = cache == null ? Connection.builder() : CacheConnection.builder(cache); connBuilder.socketFactory(jedisSocketFactory).clientConfig(clientConfig); return connBuilder; } } public static Builder builder() { return new Builder(); } private static final Logger logger = LoggerFactory.getLogger(ConnectionFactory.class); private final JedisClientConfig clientConfig; private Supplier objectMaker; private Connection.Builder connectionBuilder; private AuthXEventListener authXEventListener; public ConnectionFactory(final HostAndPort hostAndPort) { this(builder().hostAndPort(hostAndPort).withDefaults()); } public ConnectionFactory(final HostAndPort hostAndPort, final JedisClientConfig clientConfig) { this(builder().hostAndPort(hostAndPort).clientConfig(clientConfig).withDefaults()); } @Experimental public ConnectionFactory(final HostAndPort hostAndPort, final JedisClientConfig clientConfig, Cache csCache) { this(builder().hostAndPort(hostAndPort).clientConfig(clientConfig).cache(csCache).withDefaults()); } public ConnectionFactory(final JedisSocketFactory jedisSocketFactory, final JedisClientConfig clientConfig) { this(builder().socketFactory(jedisSocketFactory).clientConfig(clientConfig).withDefaults()); } public ConnectionFactory(Builder builder) { this.clientConfig = builder.getClientConfig(); this.connectionBuilder = builder.getConnectionBuilder(); initAuthXManager(); } private void initAuthXManager() { AuthXManager authXManager = clientConfig.getAuthXManager(); if (authXManager == null) { this.objectMaker = () -> build(); this.authXEventListener = AuthXEventListener.NOOP_LISTENER; } else { this.objectMaker = () -> (Connection) authXManager.addConnection(build()); this.authXEventListener = authXManager.getListener(); authXManager.start(); } } private Connection build() { return connectionBuilder.build(); } @Override public void activateObject(PooledObject pooledConnection) throws Exception { // what to do ?? } @Override public void destroyObject(PooledObject pooledConnection) throws Exception { final Connection jedis = pooledConnection.getObject(); if (jedis.isConnected()) { try { jedis.close(); } catch (RuntimeException e) { logger.debug("Error while close", e); } } } @Override public PooledObject makeObject() throws Exception { try { Connection jedis = objectMaker.get(); return new DefaultPooledObject<>(jedis); } catch (JedisException je) { logger.debug("Error while makeObject", je); throw je; } } @Override public void passivateObject(PooledObject pooledConnection) throws Exception { // TODO maybe should select db 0? Not sure right now. Connection jedis = pooledConnection.getObject(); reAuthenticate(jedis); } @Override public boolean validateObject(PooledObject pooledConnection) { final Connection jedis = pooledConnection.getObject(); try { // check HostAndPort ?? if (!jedis.isConnected()) { return false; } reAuthenticate(jedis); return jedis.ping(); } catch (final Exception e) { logger.warn("Error while validating pooled Connection object.", e); return false; } } private void reAuthenticate(Connection jedis) throws Exception { try { String result = jedis.reAuthenticate(); if (result != null && !result.equals("OK")) { String msg = "Re-authentication failed with server response: " + result; Exception failedAuth = new JedisAuthenticationException(msg); logger.error(failedAuth.getMessage(), failedAuth); authXEventListener.onConnectionAuthenticationError(failedAuth); return; } } catch (Exception e) { logger.error("Error while re-authenticating connection", e); authXEventListener.onConnectionAuthenticationError(e); throw e; } } } ================================================ FILE: src/main/java/redis/clients/jedis/ConnectionPool.java ================================================ package redis.clients.jedis; import org.apache.commons.pool2.PooledObjectFactory; import org.apache.commons.pool2.impl.GenericObjectPoolConfig; import redis.clients.authentication.core.Token; import redis.clients.jedis.annots.Experimental; import redis.clients.jedis.authentication.AuthXManager; import redis.clients.jedis.csc.Cache; import redis.clients.jedis.exceptions.JedisException; import redis.clients.jedis.util.Pool; public class ConnectionPool extends Pool { private AuthXManager authXManager; // Primary constructors using factory public ConnectionPool(PooledObjectFactory factory) { super(factory); } public ConnectionPool(PooledObjectFactory factory, GenericObjectPoolConfig poolConfig) { super(factory, poolConfig); } // Convenience constructors public ConnectionPool(HostAndPort hostAndPort, JedisClientConfig clientConfig) { this(new ConnectionFactory(hostAndPort, clientConfig)); attachAuthenticationListener(clientConfig.getAuthXManager()); } public ConnectionPool(HostAndPort hostAndPort, JedisClientConfig clientConfig, GenericObjectPoolConfig poolConfig) { this(new ConnectionFactory(hostAndPort, clientConfig), poolConfig); attachAuthenticationListener(clientConfig.getAuthXManager()); } @Experimental public ConnectionPool(HostAndPort hostAndPort, JedisClientConfig clientConfig, Cache clientSideCache) { this(new ConnectionFactory(hostAndPort, clientConfig, clientSideCache)); attachAuthenticationListener(clientConfig.getAuthXManager()); } @Experimental public ConnectionPool(HostAndPort hostAndPort, JedisClientConfig clientConfig, Cache clientSideCache, GenericObjectPoolConfig poolConfig) { this(new ConnectionFactory(hostAndPort, clientConfig, clientSideCache), poolConfig); attachAuthenticationListener(clientConfig.getAuthXManager()); } @Override public Connection getResource() { Connection conn = super.getResource(); conn.setHandlingPool(this); return conn; } @Override public void close() { try { if (authXManager != null) { authXManager.stop(); } } finally { super.close(); } } protected void attachAuthenticationListener(AuthXManager authXManager) { this.authXManager = authXManager; if (authXManager != null) { authXManager.addPostAuthenticationHook(this::postAuthentication); } } protected void detachAuthenticationListener() { if (authXManager != null) { authXManager.removePostAuthenticationHook(this::postAuthentication); } } private void postAuthentication(Token token) { try { // this is to trigger validations on each connection via ConnectionFactory evict(); } catch (Exception e) { throw new JedisException("Failed to evict connections from pool", e); } } } ================================================ FILE: src/main/java/redis/clients/jedis/ConnectionPoolConfig.java ================================================ package redis.clients.jedis; import java.time.Duration; import org.apache.commons.pool2.impl.GenericObjectPoolConfig; public class ConnectionPoolConfig extends GenericObjectPoolConfig { public ConnectionPoolConfig() { // defaults to make your life with connection pool easier :) setTestWhileIdle(true); setMinEvictableIdleTime(Duration.ofMillis(60000)); setTimeBetweenEvictionRuns(Duration.ofMillis(30000)); setNumTestsPerEvictionRun(-1); } } ================================================ FILE: src/main/java/redis/clients/jedis/DefaultJedisClientConfig.java ================================================ package redis.clients.jedis; import java.net.URI; import java.util.function.Supplier; import javax.net.ssl.HostnameVerifier; import javax.net.ssl.SSLParameters; import javax.net.ssl.SSLSocketFactory; import redis.clients.jedis.authentication.AuthXManager; import redis.clients.jedis.util.JedisAsserts; import redis.clients.jedis.util.JedisURIHelper; public final class DefaultJedisClientConfig implements JedisClientConfig { private final RedisProtocol redisProtocol; private final int connectionTimeoutMillis; private final int socketTimeoutMillis; private final int blockingSocketTimeoutMillis; private volatile Supplier credentialsProvider; private final int database; private final String clientName; private final boolean ssl; private final SSLSocketFactory sslSocketFactory; private final SSLParameters sslParameters; private final SslOptions sslOptions; private final HostnameVerifier hostnameVerifier; private final HostAndPortMapper hostAndPortMapper; private final ClientSetInfoConfig clientSetInfoConfig; private final boolean readOnlyForRedisClusterReplicas; private final AuthXManager authXManager; private DefaultJedisClientConfig(DefaultJedisClientConfig.Builder builder) { this.redisProtocol = builder.redisProtocol; this.connectionTimeoutMillis = builder.connectionTimeoutMillis; this.socketTimeoutMillis = builder.socketTimeoutMillis; this.blockingSocketTimeoutMillis = builder.blockingSocketTimeoutMillis; this.credentialsProvider = builder.credentialsProvider; this.database = builder.database; this.clientName = builder.clientName; this.ssl = builder.ssl; this.sslSocketFactory = builder.sslSocketFactory; this.sslParameters = builder.sslParameters; this.sslOptions = builder.sslOptions; this.hostnameVerifier = builder.hostnameVerifier; this.hostAndPortMapper = builder.hostAndPortMapper; this.clientSetInfoConfig = builder.clientSetInfoConfig; this.readOnlyForRedisClusterReplicas = builder.readOnlyForRedisClusterReplicas; this.authXManager = builder.authXManager; } @Override public RedisProtocol getRedisProtocol() { return redisProtocol; } @Override public int getConnectionTimeoutMillis() { return connectionTimeoutMillis; } @Override public int getSocketTimeoutMillis() { return socketTimeoutMillis; } @Override public int getBlockingSocketTimeoutMillis() { return blockingSocketTimeoutMillis; } @Override public String getUser() { return credentialsProvider.get().getUser(); } @Override public String getPassword() { char[] password = credentialsProvider.get().getPassword(); return password == null ? null : new String(password); } @Override public Supplier getCredentialsProvider() { return credentialsProvider; } @Override public AuthXManager getAuthXManager() { return authXManager; } @Override public int getDatabase() { return database; } @Override public String getClientName() { return clientName; } @Override public boolean isSsl() { return ssl; } @Override public SSLSocketFactory getSslSocketFactory() { return sslSocketFactory; } @Override public SSLParameters getSslParameters() { return sslParameters; } @Override public SslOptions getSslOptions() { return sslOptions; } @Override public HostnameVerifier getHostnameVerifier() { return hostnameVerifier; } @Override public HostAndPortMapper getHostAndPortMapper() { return hostAndPortMapper; } @Override public ClientSetInfoConfig getClientSetInfoConfig() { return clientSetInfoConfig; } @Override public boolean isReadOnlyForRedisClusterReplicas() { return readOnlyForRedisClusterReplicas; } public static Builder builder() { return new Builder(); } /** * Creates a new Builder pre-initialized with settings from the provided Redis URI. *

* The URI format is: * {@code redis[s]://[username:password@]host:port[/database][?protocol=version]} *

*

* Settings extracted from URI: *

    *
  • Credentials (username/password) if present in URI
  • *
  • Database index if specified in path
  • *
  • SSL enabled if scheme is "rediss"
  • *
  • Protocol version if specified in query parameters
  • *
* @param redisUri the Redis URI to extract settings from * @return a new Builder pre-initialized from the URI */ public static Builder builder(URI redisUri) { JedisAsserts.notNull(redisUri, "Redis URI must not be null"); JedisAsserts.isTrue(JedisURIHelper.isValid(redisUri), "Invalid Redis URI"); Builder builder = new Builder(); // Extract and apply credentials if present String uriUser = JedisURIHelper.getUser(redisUri); String uriPassword = JedisURIHelper.getPassword(redisUri); if (uriUser != null || uriPassword != null) { builder.credentials(new DefaultRedisCredentials(uriUser, uriPassword)); } if (JedisURIHelper.hasDbIndex(redisUri)) { builder.database(JedisURIHelper.getDBIndex(redisUri)); } // Apply protocol if specified RedisProtocol uriProtocol = JedisURIHelper.getRedisProtocol(redisUri); if (uriProtocol != null) { builder.protocol(uriProtocol); } if (JedisURIHelper.isRedisSSLScheme(redisUri)) { builder.ssl(true); } else if (JedisURIHelper.isRedisScheme(redisUri)) { builder.ssl(false); } return builder; } public static class Builder { private RedisProtocol redisProtocol = null; private int connectionTimeoutMillis = Protocol.DEFAULT_TIMEOUT; private int socketTimeoutMillis = Protocol.DEFAULT_TIMEOUT; private int blockingSocketTimeoutMillis = 0; private String user = null; private String password = null; private Supplier credentialsProvider; private int database = Protocol.DEFAULT_DATABASE; private String clientName = null; private boolean ssl = false; private SSLSocketFactory sslSocketFactory = null; private SSLParameters sslParameters = null; private SslOptions sslOptions = null; private HostnameVerifier hostnameVerifier = null; private HostAndPortMapper hostAndPortMapper = null; private ClientSetInfoConfig clientSetInfoConfig = ClientSetInfoConfig.DEFAULT; private boolean readOnlyForRedisClusterReplicas = false; private AuthXManager authXManager = null; private Builder() { } public DefaultJedisClientConfig build() { if (credentialsProvider == null) { credentialsProvider = new DefaultRedisCredentialsProvider( new DefaultRedisCredentials(user, password)); } return new DefaultJedisClientConfig(this); } /** * Shortcut to * {@link redis.clients.jedis.DefaultJedisClientConfig.Builder#protocol(RedisProtocol)} with * {@link RedisProtocol#RESP3}. * @return this */ public Builder resp3() { return protocol(RedisProtocol.RESP3); } public Builder protocol(RedisProtocol protocol) { this.redisProtocol = protocol; return this; } public Builder timeoutMillis(int timeoutMillis) { this.connectionTimeoutMillis = timeoutMillis; this.socketTimeoutMillis = timeoutMillis; return this; } public Builder connectionTimeoutMillis(int connectionTimeoutMillis) { this.connectionTimeoutMillis = connectionTimeoutMillis; return this; } public Builder socketTimeoutMillis(int socketTimeoutMillis) { this.socketTimeoutMillis = socketTimeoutMillis; return this; } public Builder blockingSocketTimeoutMillis(int blockingSocketTimeoutMillis) { this.blockingSocketTimeoutMillis = blockingSocketTimeoutMillis; return this; } public Builder user(String user) { this.user = user; return this; } public Builder password(String password) { this.password = password; return this; } public Builder credentials(RedisCredentials credentials) { this.credentialsProvider = new DefaultRedisCredentialsProvider(credentials); return this; } public Builder credentialsProvider(Supplier credentials) { this.credentialsProvider = credentials; return this; } public Builder database(int database) { this.database = database; return this; } public Builder clientName(String clientName) { this.clientName = clientName; return this; } public Builder ssl(boolean ssl) { this.ssl = ssl; return this; } public Builder sslSocketFactory(SSLSocketFactory sslSocketFactory) { this.sslSocketFactory = sslSocketFactory; return this; } public Builder sslParameters(SSLParameters sslParameters) { this.sslParameters = sslParameters; return this; } public Builder sslOptions(SslOptions sslOptions) { this.sslOptions = sslOptions; return this; } public Builder hostnameVerifier(HostnameVerifier hostnameVerifier) { this.hostnameVerifier = hostnameVerifier; return this; } public Builder hostAndPortMapper(HostAndPortMapper hostAndPortMapper) { this.hostAndPortMapper = hostAndPortMapper; return this; } public Builder clientSetInfoConfig(ClientSetInfoConfig setInfoConfig) { this.clientSetInfoConfig = setInfoConfig; return this; } public Builder readOnlyForRedisClusterReplicas() { this.readOnlyForRedisClusterReplicas = true; return this; } public Builder authXManager(AuthXManager authXManager) { this.authXManager = authXManager; return this; } public Builder from(JedisClientConfig instance) { this.redisProtocol = instance.getRedisProtocol(); this.connectionTimeoutMillis = instance.getConnectionTimeoutMillis(); this.socketTimeoutMillis = instance.getSocketTimeoutMillis(); this.blockingSocketTimeoutMillis = instance.getBlockingSocketTimeoutMillis(); this.credentialsProvider = instance.getCredentialsProvider(); this.database = instance.getDatabase(); this.clientName = instance.getClientName(); this.ssl = instance.isSsl(); this.sslSocketFactory = instance.getSslSocketFactory(); this.sslParameters = instance.getSslParameters(); this.sslOptions = instance.getSslOptions(); this.hostnameVerifier = instance.getHostnameVerifier(); this.hostAndPortMapper = instance.getHostAndPortMapper(); this.clientSetInfoConfig = instance.getClientSetInfoConfig(); this.readOnlyForRedisClusterReplicas = instance.isReadOnlyForRedisClusterReplicas(); this.authXManager = instance.getAuthXManager(); return this; } } /** * @deprecated Use {@link redis.clients.jedis.DefaultJedisClientConfig.Builder}. */ @Deprecated public static DefaultJedisClientConfig create(int connectionTimeoutMillis, int soTimeoutMillis, int blockingSocketTimeoutMillis, String user, String password, int database, String clientName, boolean ssl, SSLSocketFactory sslSocketFactory, SSLParameters sslParameters, HostnameVerifier hostnameVerifier, HostAndPortMapper hostAndPortMapper) { Builder builder = builder(); builder.connectionTimeoutMillis(connectionTimeoutMillis).socketTimeoutMillis(soTimeoutMillis) .blockingSocketTimeoutMillis(blockingSocketTimeoutMillis); if (user != null || password != null) { // deliberately not handling 'user != null && password == null' here builder.credentials(new DefaultRedisCredentials(user, password)); } builder.database(database).clientName(clientName); builder.ssl(ssl).sslSocketFactory(sslSocketFactory).sslParameters(sslParameters) .hostnameVerifier(hostnameVerifier); builder.hostAndPortMapper(hostAndPortMapper); return builder.build(); } /** * @deprecated Use * {@link redis.clients.jedis.DefaultJedisClientConfig.Builder#from(redis.clients.jedis.JedisClientConfig)}. */ @Deprecated public static DefaultJedisClientConfig copyConfig(JedisClientConfig copy) { Builder builder = builder(); builder.protocol(copy.getRedisProtocol()); builder.connectionTimeoutMillis(copy.getConnectionTimeoutMillis()); builder.socketTimeoutMillis(copy.getSocketTimeoutMillis()); builder.blockingSocketTimeoutMillis(copy.getBlockingSocketTimeoutMillis()); Supplier credentialsProvider = copy.getCredentialsProvider(); if (credentialsProvider != null) { builder.credentialsProvider(credentialsProvider); } else { builder.user(copy.getUser()); builder.password(copy.getPassword()); } builder.database(copy.getDatabase()); builder.clientName(copy.getClientName()); builder.ssl(copy.isSsl()); builder.sslSocketFactory(copy.getSslSocketFactory()); builder.sslParameters(copy.getSslParameters()); builder.hostnameVerifier(copy.getHostnameVerifier()); builder.sslOptions(copy.getSslOptions()); builder.hostAndPortMapper(copy.getHostAndPortMapper()); builder.clientSetInfoConfig(copy.getClientSetInfoConfig()); if (copy.isReadOnlyForRedisClusterReplicas()) { builder.readOnlyForRedisClusterReplicas(); } builder.authXManager(copy.getAuthXManager()); return builder.build(); } } ================================================ FILE: src/main/java/redis/clients/jedis/DefaultJedisSocketFactory.java ================================================ package redis.clients.jedis; import java.io.IOException; import java.net.InetAddress; import java.net.InetSocketAddress; import java.net.Socket; import java.security.GeneralSecurityException; import java.util.Arrays; import java.util.Collections; import java.util.List; import javax.net.ssl.HostnameVerifier; import javax.net.ssl.SSLContext; import javax.net.ssl.SSLParameters; import javax.net.ssl.SSLSocket; import javax.net.ssl.SSLSocketFactory; import redis.clients.jedis.exceptions.JedisConnectionException; import redis.clients.jedis.util.IOUtils; public class DefaultJedisSocketFactory implements JedisSocketFactory { protected static final HostAndPort DEFAULT_HOST_AND_PORT = new HostAndPort(Protocol.DEFAULT_HOST, Protocol.DEFAULT_PORT); private volatile HostAndPort hostAndPort = DEFAULT_HOST_AND_PORT; private int connectionTimeout = Protocol.DEFAULT_TIMEOUT; private int socketTimeout = Protocol.DEFAULT_TIMEOUT; private boolean ssl = false; private SSLSocketFactory sslSocketFactory = null; private SslOptions sslOptions = null; private SSLParameters sslParameters = null; private HostnameVerifier hostnameVerifier = null; private HostAndPortMapper hostAndPortMapper = null; public DefaultJedisSocketFactory() { } public DefaultJedisSocketFactory(HostAndPort hostAndPort) { this(hostAndPort, null); } public DefaultJedisSocketFactory(JedisClientConfig config) { this(null, config); } public DefaultJedisSocketFactory(HostAndPort hostAndPort, JedisClientConfig config) { if (hostAndPort != null) { this.hostAndPort = hostAndPort; } if (config != null) { this.connectionTimeout = config.getConnectionTimeoutMillis(); this.socketTimeout = config.getSocketTimeoutMillis(); this.ssl = config.isSsl(); this.sslSocketFactory = config.getSslSocketFactory(); this.sslParameters = config.getSslParameters(); this.sslOptions = config.getSslOptions(); this.hostnameVerifier = config.getHostnameVerifier(); this.hostAndPortMapper = config.getHostAndPortMapper(); } } private Socket connectToFirstSuccessfulHost(HostAndPort hostAndPort) throws Exception { List hosts = Arrays.asList(InetAddress.getAllByName(hostAndPort.getHost())); if (hosts.size() > 1) { Collections.shuffle(hosts); } JedisConnectionException jce = new JedisConnectionException("Failed to connect to " + hostAndPort + "."); for (InetAddress host : hosts) { try { Socket socket = new Socket(); socket.setReuseAddress(true); socket.setKeepAlive(true); // Will monitor the TCP connection is valid socket.setTcpNoDelay(true); // Socket buffer Whetherclosed, to ensure timely delivery of data socket.setSoLinger(true, 0); // Control calls close () method, the underlying socket is closed immediately // Passing 'host' directly will avoid another call to InetAddress.getByName() inside the InetSocketAddress constructor. // For machines with ipv4 and ipv6, but the startNode uses ipv4 to connect, the ipv6 connection may fail. socket.connect(new InetSocketAddress(host, hostAndPort.getPort()), connectionTimeout); return socket; } catch (Exception e) { jce.addSuppressed(e); } } throw jce; } @Override public Socket createSocket() throws JedisConnectionException { Socket socket = null; try { HostAndPort _hostAndPort = getSocketHostAndPort(); socket = connectToFirstSuccessfulHost(_hostAndPort); socket.setSoTimeout(socketTimeout); if (ssl || sslOptions != null) { socket = createSslSocket(_hostAndPort, socket); } return socket; } catch (Exception ex) { IOUtils.closeQuietly(socket); if (ex instanceof JedisConnectionException) { throw (JedisConnectionException) ex; } else { throw new JedisConnectionException("Failed to create socket.", ex); } } } /** * ssl enable check is done before this. */ private Socket createSslSocket(HostAndPort _hostAndPort, Socket socket) throws IOException, GeneralSecurityException { Socket plainSocket = socket; SSLSocketFactory _sslSocketFactory; SSLParameters _sslParameters; if (sslOptions != null) { SSLContext _sslContext = sslOptions.createSslContext(); _sslSocketFactory = _sslContext.getSocketFactory(); _sslParameters = sslOptions.getSslParameters(); } else { _sslSocketFactory = this.sslSocketFactory; _sslParameters = this.sslParameters; } if (_sslSocketFactory == null) { _sslSocketFactory = (SSLSocketFactory) SSLSocketFactory.getDefault(); } SSLSocket sslSocket = (SSLSocket) _sslSocketFactory.createSocket(socket, _hostAndPort.getHost(), _hostAndPort.getPort(), true); if (_sslParameters != null) { sslSocket.setSSLParameters(_sslParameters); } // allowing HostnameVerifier for both SslOptions and legacy ssl config if (hostnameVerifier != null && !hostnameVerifier.verify(_hostAndPort.getHost(), sslSocket.getSession())) { String message = String.format("The connection to '%s' failed ssl/tls hostname verification.", _hostAndPort.getHost()); throw new JedisConnectionException(message); } return new SSLSocketWrapper(sslSocket, plainSocket); } public void updateHostAndPort(HostAndPort hostAndPort) { this.hostAndPort = hostAndPort; } public HostAndPort getHostAndPort() { return this.hostAndPort; } protected HostAndPort getSocketHostAndPort() { HostAndPortMapper mapper = hostAndPortMapper; HostAndPort hap = this.hostAndPort; if (mapper != null) { HostAndPort mapped = mapper.getHostAndPort(hap); if (mapped != null) { return mapped; } } return hap; } @Override public String toString() { return "DefaultJedisSocketFactory{" + hostAndPort.toString() + "}"; } } ================================================ FILE: src/main/java/redis/clients/jedis/DefaultRedisCredentials.java ================================================ package redis.clients.jedis; public final class DefaultRedisCredentials implements RedisCredentials { private final String user; private final char[] password; public DefaultRedisCredentials(String user, char[] password) { this.user = user; this.password = password; } public DefaultRedisCredentials(String user, CharSequence password) { this.user = user; this.password = password == null ? null : password instanceof String ? ((String) password).toCharArray() : toCharArray(password); } @Override public String getUser() { return user; } @Override public char[] getPassword() { return password; } private static char[] toCharArray(CharSequence seq) { final int len = seq.length(); char[] arr = new char[len]; for (int i = 0; i < len; i++) { arr[i] = seq.charAt(i); } return arr; } } ================================================ FILE: src/main/java/redis/clients/jedis/DefaultRedisCredentialsProvider.java ================================================ package redis.clients.jedis; public final class DefaultRedisCredentialsProvider implements RedisCredentialsProvider { private volatile RedisCredentials credentials; public DefaultRedisCredentialsProvider(RedisCredentials credentials) { this.credentials = credentials; } public void setCredentials(RedisCredentials credentials) { this.credentials = credentials; } @Override public RedisCredentials get() { return this.credentials; } } ================================================ FILE: src/main/java/redis/clients/jedis/DriverInfo.java ================================================ package redis.clients.jedis; import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; import java.util.HashSet; import java.util.List; import java.util.Set; import redis.clients.jedis.exceptions.JedisValidationException; /** * Immutable class representing driver information for Redis client identification. *

* This class is used to identify the client library and any upstream drivers (such as Spring Data * Redis or Spring Session) when connecting to Redis. The information is sent via the * {@code CLIENT SETINFO} command. *

* The formatted name follows the pattern: {@code name(driver1_vVersion1;driver2_vVersion2)} * @see ClientSetInfoConfig * @see CLIENT SETINFO */ public final class DriverInfo { /** * Set of brace characters that are not allowed in driver names or versions. These characters are * used to delimit the driver information in the formatted output and would break parsing. */ private static final Set BRACES = Collections .unmodifiableSet(new HashSet<>(Arrays.asList('(', ')', '[', ']', '{', '}'))); private final String name; private final List upstreamDrivers; private DriverInfo(String name, List upstreamDrivers) { this.name = name; this.upstreamDrivers = Collections.unmodifiableList(upstreamDrivers); } /** * Creates a new {@link Builder} with default values. *

* The default name is "Jedis" (from {@link JedisMetaInfo#getArtifactId()}). * @return a new builder instance */ public static Builder builder() { return new Builder(); } /** * Creates a new {@link Builder} initialized with values from an existing {@link DriverInfo}. * @param driverInfo the existing driver info to copy from, must not be {@code null} * @return a new builder instance initialized with the existing values * @throws JedisValidationException if driverInfo is {@code null} */ public static Builder builder(DriverInfo driverInfo) { if (driverInfo == null) { throw new JedisValidationException("DriverInfo must not be null"); } return new Builder(driverInfo); } /** * Returns the formatted name including upstream drivers or legacy suffix. *

* If a legacy suffix is set, returns the name followed by the suffix in parentheses. Otherwise, * if upstream drivers are present, returns the name followed by upstream drivers in parentheses, * separated by semicolons. If neither is set, returns just the name. *

* Examples: *

    *
  • {@code "jedis"} - no upstream drivers or suffix
  • *
  • {@code "jedis(my-suffix)"} - legacy suffix mode
  • *
  • {@code "jedis(spring-data-redis_v3.2.0)"} - one upstream driver
  • *
  • {@code "jedis(spring-session_v3.3.0;spring-data-redis_v3.2.0)"} - multiple upstream * drivers
  • *
* @return the formatted name for use in CLIENT SETINFO */ public String getFormattedName() { if (upstreamDrivers.isEmpty()) { return name; } return String.format("%s(%s)", name, String.join(";", upstreamDrivers)); } /** * Returns the base library name without upstream driver information. * @return the library name */ public String getName() { return name; } /** * Returns the formatted upstream drivers string (without the base library name). *

* Multiple drivers are separated by semicolons, with the most recently added driver appearing * first. *

* Examples: *

    *
  • {@code "spring-data-redis_v3.2.0"} - single upstream driver
  • *
  • {@code "spring-session_v3.3.0;spring-data-redis_v3.2.0"} - multiple upstream drivers
  • *
* @return the formatted upstream drivers string, or {@code null} if no upstream drivers are set */ public String getUpstreamDrivers() { if (upstreamDrivers.isEmpty()) { return ""; } return String.join(";", upstreamDrivers); } @Override public String toString() { return getFormattedName(); } /** * Builder for creating {@link DriverInfo} instances. */ public static class Builder { private String name; private final List upstreamDrivers; private Builder() { this.name = JedisMetaInfo.getArtifactId(); this.upstreamDrivers = new ArrayList<>(); } private Builder(DriverInfo driverInfo) { this.name = driverInfo.name; this.upstreamDrivers = new ArrayList<>(driverInfo.upstreamDrivers); } /** * Sets the base library name. *

* This overrides the default name ("Jedis"). Use this when you want to completely customize the * library identification. * @param name the library name, must not be {@code null} * @return this builder * @throws JedisValidationException if name is {@code null} */ public Builder name(String name) { if (name == null) { throw new JedisValidationException("Name must not be null"); } this.name = name; return this; } /** * Adds an upstream driver to the driver information. *

* Upstream drivers are prepended to the list, so the most recently added driver appears first * in the formatted output. *

* The driver name must follow Maven artifactId naming conventions: lowercase letters, digits, * hyphens, and underscores only, starting with a lowercase letter. Dots are only allowed after * digits (for Scala cross-version naming like akka-redis_2.13). *

* Both values must not contain spaces, newlines, non-printable characters, or brace characters * as these would violate the format of the Redis CLIENT LIST reply. * @param driverName the name of the upstream driver (e.g., "spring-data-redis"), must not be * {@code null} * @param driverVersion the version of the upstream driver (e.g., "3.2.0"), must not be * {@code null} * @return this builder * @throws JedisValidationException if the driver name or version is {@code null} or has invalid * format * @see Maven * Naming Conventions * @see CLIENT SETINFO */ public Builder addUpstreamDriver(String driverName, String driverVersion) { if (driverName == null) { throw new JedisValidationException("Driver name must not be null"); } if (driverVersion == null) { throw new JedisValidationException("Driver version must not be null"); } validateDriverField(driverName, "Driver name"); validateDriverField(driverVersion, "Driver version"); String formattedDriverInfo = formatDriverInfo(driverName, driverVersion); this.upstreamDrivers.add(0, formattedDriverInfo); return this; } public Builder addUpstreamDriver(String driverName) { if (driverName == null) { throw new JedisValidationException("Driver name must not be null"); } validateDriverField(driverName, "Driver name"); this.upstreamDrivers.add(0, driverName); return this; } /** * Builds and returns a new immutable {@link DriverInfo} instance. * @return a new DriverInfo instance */ public DriverInfo build() { return new DriverInfo(name, upstreamDrivers); } } /** * Validates that the value does not contain characters that would violate the format of the Redis * CLIENT LIST reply. *

* Only printable ASCII characters (0x21-0x7E, i.e., '!' to '~') are allowed, excluding braces. * @param value the value to validate * @param fieldName the name of the field for error messages (e.g., "Driver name", "Driver * version") * @throws JedisValidationException if the value is empty or contains invalid characters * @see CLIENT SETINFO */ private static void validateDriverField(String value, String fieldName) { if (value.trim().isEmpty()) { throw new JedisValidationException(fieldName + " must not be empty"); } validateNoInvalidCharacters(value, fieldName); } /** * Validates that the value does not contain characters that would violate the format of the Redis * CLIENT LIST reply: non-printable characters, spaces, or brace characters. *

* Only printable ASCII characters (0x21-0x7E, i.e., '!' to '~') are allowed, excluding braces. * @param value the value to validate * @param fieldName the name of the field for error messages * @throws JedisValidationException if the value contains invalid characters * @see CLIENT SETINFO */ private static void validateNoInvalidCharacters(String value, String fieldName) { for (int i = 0; i < value.length(); i++) { char c = value.charAt(i); if (c < '!' || c > '~' || BRACES.contains(c)) { throw new JedisValidationException( fieldName + " must not contain spaces, newlines, non-printable characters, or braces"); } } } private static String formatDriverInfo(String driverName, String driverVersion) { return driverName + "_v" + driverVersion; } } ================================================ FILE: src/main/java/redis/clients/jedis/Endpoint.java ================================================ package redis.clients.jedis; public interface Endpoint { String getHost(); int getPort(); } ================================================ FILE: src/main/java/redis/clients/jedis/GeoCoordinate.java ================================================ package redis.clients.jedis; public class GeoCoordinate { private double longitude; private double latitude; public GeoCoordinate(double longitude, double latitude) { this.longitude = longitude; this.latitude = latitude; } public double getLongitude() { return longitude; } public double getLatitude() { return latitude; } @Override public boolean equals(Object o) { if (o == null) return false; if (o == this) return true; if (!(o instanceof GeoCoordinate)) return false; GeoCoordinate that = (GeoCoordinate) o; if (Double.compare(that.longitude, longitude) != 0) return false; return Double.compare(that.latitude, latitude) == 0; } @Override public int hashCode() { // follows IntelliJ default hashCode implementation int result; long temp; temp = Double.doubleToLongBits(longitude); result = (int) (temp ^ (temp >>> 32)); temp = Double.doubleToLongBits(latitude); result = 31 * result + (int) (temp ^ (temp >>> 32)); return result; } @Override public String toString() { return "(" + longitude + "," + latitude + ")"; } } ================================================ FILE: src/main/java/redis/clients/jedis/HostAndPort.java ================================================ package redis.clients.jedis; import java.io.Serializable; public class HostAndPort implements Serializable, Endpoint { private static final long serialVersionUID = -519876229978427751L; private final String host; private final int port; public HostAndPort(String host, int port) { this.host = host; this.port = port; } @Override public String getHost() { return host; } @Override public int getPort() { return port; } @Override public boolean equals(Object obj) { if (obj == null) return false; if (obj == this) return true; if (!(obj instanceof HostAndPort)) return false; HostAndPort other = (HostAndPort) obj; return this.port == other.port && this.host.equals(other.host); } @Override public int hashCode() { return 31 * host.hashCode() + port; } @Override public String toString() { return host + ":" + port; } /** * Creates HostAndPort with unconverted host. * @param string String to parse. Must be in "host:port" format. Port is mandatory. * @return parsed HostAndPort */ public static HostAndPort from(String string) { int lastColon = string.lastIndexOf(":"); String host = string.substring(0, lastColon); int port = Integer.parseInt(string.substring(lastColon + 1)); return new HostAndPort(host, port); } } ================================================ FILE: src/main/java/redis/clients/jedis/HostAndPortMapper.java ================================================ package redis.clients.jedis; /** * An interface for mapping Redis node addresses. *

* It is used to translate an advertised server address to one that is reachable by the client, * especially in network topologies involving NAT or containerization. */ @FunctionalInterface public interface HostAndPortMapper { /** * @param hap The original address from the server. * @return The translated, reachable address. */ HostAndPort getHostAndPort(HostAndPort hap); } ================================================ FILE: src/main/java/redis/clients/jedis/Jedis.java ================================================ package redis.clients.jedis; import static redis.clients.jedis.Protocol.Command.*; import static redis.clients.jedis.Protocol.Keyword.*; import static redis.clients.jedis.Protocol.SentinelKeyword.*; import static redis.clients.jedis.Protocol.toByteArray; import static redis.clients.jedis.util.SafeEncoder.encode; import java.io.Closeable; import java.net.URI; import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.Map.Entry; import java.util.Set; import java.util.stream.Collectors; import java.util.Arrays; import javax.net.ssl.HostnameVerifier; import javax.net.ssl.SSLParameters; import javax.net.ssl.SSLSocketFactory; import redis.clients.jedis.Protocol.*; import redis.clients.jedis.args.*; import redis.clients.jedis.commands.*; import redis.clients.jedis.util.CompareCondition; import redis.clients.jedis.exceptions.InvalidURIException; import redis.clients.jedis.exceptions.JedisConnectionException; import redis.clients.jedis.exceptions.JedisException; import redis.clients.jedis.params.*; import redis.clients.jedis.resps.*; import redis.clients.jedis.util.JedisURIHelper; import redis.clients.jedis.util.KeyValue; import redis.clients.jedis.util.Pool; /** * Jedis is a lightweight Redis client that uses a single, non-pooled connection to Redis. *

* Important: For most production use cases, {@link RedisClient} is the recommended and * preferred option. {@code RedisClient} provides connection pooling, better resource management, * and improved performance for typical applications. *

*

* When to use Jedis: *

*
    *
  • Short-lived scripts or utilities: When you need a simple, lightweight client for * one-off operations or command-line tools.
  • *
  • Testing and development: For unit tests or local development where connection pooling * overhead is unnecessary.
  • *
  • Fine-grained connection control: Advanced scenarios requiring explicit control over * individual connections, such as managing connection lifecycle manually or implementing custom * connection strategies.
  • *
  • Single-threaded applications: Applications that execute Redis commands sequentially * from a single thread and don't benefit from connection pooling.
  • *
*

* When to use RedisClient instead: *

*
    *
  • Production applications: Any multi-threaded or high-throughput application should use * {@link RedisClient} for its connection pooling capabilities.
  • *
  • Web applications: Server applications handling concurrent requests benefit from * connection pooling to avoid connection overhead.
  • *
  • Long-running services: Applications that maintain persistent connections to Redis * should use {@link RedisClient} for better resource management.
  • *
  • Default choice: If you're unsure which to use, choose {@link RedisClient}.
  • *
*

* Usage example: *

* *
 * {
 *   @code
 *   // Simple usage for a short-lived operation
 *   try (Jedis jedis = new Jedis("localhost", 6379)) {
 *     jedis.set("key", "value");
 *     String value = jedis.get("key");
 *   }
 * }
 * 
*

* Note: Each {@code Jedis} instance maintains a single connection. For concurrent access * from multiple threads, either use {@link RedisClient} with connection pooling, or create * separate {@code Jedis} instances per thread (not recommended for production). *

* * @see RedisClient for the recommended pooled client for production use * @see JedisPool for legacy pooled connections (deprecated, use RedisClient instead) */ public class Jedis implements ServerCommands, DatabaseCommands, JedisCommands, JedisBinaryCommands, ControlCommands, ControlBinaryCommands, ClusterCommands, ModuleCommands, GenericControlCommands, SentinelCommands, CommandCommands, Closeable { protected final Connection connection; private final CommandObjects commandObjects = new CommandObjects(); private int db = 0; private Transaction transaction = null; private boolean isInMulti = false; private boolean isInWatch = false; private Pipeline pipeline = null; protected static final byte[][] DUMMY_ARRAY = new byte[0][]; private Pool dataSource = null; public Jedis() { connection = new Connection(); } /** * This constructor only accepts a URI string. {@link JedisURIHelper#isValid(java.net.URI)} can be * used before this. * @param url */ public Jedis(final String url) { this(URI.create(url)); } public Jedis(final HostAndPort hp) { connection = new Connection(hp); } public Jedis(final String host, final int port) { connection = new Connection(host, port); } public Jedis(final String host, final int port, final JedisClientConfig config) { this(new HostAndPort(host, port), config); } public Jedis(final HostAndPort hostPort, final JedisClientConfig config) { connection = new Connection(hostPort, config); RedisProtocol proto = config.getRedisProtocol(); if (proto != null) commandObjects.setProtocol(proto); } public Jedis(final String host, final int port, final boolean ssl) { this(host, port, DefaultJedisClientConfig.builder().ssl(ssl).build()); } public Jedis(final String host, final int port, final boolean ssl, final SSLSocketFactory sslSocketFactory, final SSLParameters sslParameters, final HostnameVerifier hostnameVerifier) { this(host, port, DefaultJedisClientConfig.builder().ssl(ssl) .sslSocketFactory(sslSocketFactory).sslParameters(sslParameters) .hostnameVerifier(hostnameVerifier).build()); } public Jedis(final String host, final int port, final int timeout) { this(host, port, timeout, timeout); } public Jedis(final String host, final int port, final int timeout, final boolean ssl) { this(host, port, timeout, timeout, ssl); } public Jedis(final String host, final int port, final int timeout, final boolean ssl, final SSLSocketFactory sslSocketFactory, final SSLParameters sslParameters, final HostnameVerifier hostnameVerifier) { this(host, port, timeout, timeout, ssl, sslSocketFactory, sslParameters, hostnameVerifier); } public Jedis(final String host, final int port, final int connectionTimeout, final int soTimeout) { this(host, port, DefaultJedisClientConfig.builder() .connectionTimeoutMillis(connectionTimeout).socketTimeoutMillis(soTimeout).build()); } public Jedis(final String host, final int port, final int connectionTimeout, final int soTimeout, final int infiniteSoTimeout) { this(host, port, DefaultJedisClientConfig.builder() .connectionTimeoutMillis(connectionTimeout).socketTimeoutMillis(soTimeout) .blockingSocketTimeoutMillis(infiniteSoTimeout).build()); } public Jedis(final String host, final int port, final int connectionTimeout, final int soTimeout, final boolean ssl) { this(host, port, DefaultJedisClientConfig.builder() .connectionTimeoutMillis(connectionTimeout).socketTimeoutMillis(soTimeout).ssl(ssl) .build()); } public Jedis(final String host, final int port, final int connectionTimeout, final int soTimeout, final boolean ssl, final SSLSocketFactory sslSocketFactory, final SSLParameters sslParameters, final HostnameVerifier hostnameVerifier) { this(host, port, DefaultJedisClientConfig.builder() .connectionTimeoutMillis(connectionTimeout).socketTimeoutMillis(soTimeout).ssl(ssl) .sslSocketFactory(sslSocketFactory).sslParameters(sslParameters) .hostnameVerifier(hostnameVerifier).build()); } public Jedis(final String host, final int port, final int connectionTimeout, final int soTimeout, final int infiniteSoTimeout, final boolean ssl, final SSLSocketFactory sslSocketFactory, final SSLParameters sslParameters, final HostnameVerifier hostnameVerifier) { this(host, port, DefaultJedisClientConfig.builder() .connectionTimeoutMillis(connectionTimeout).socketTimeoutMillis(soTimeout) .blockingSocketTimeoutMillis(infiniteSoTimeout).ssl(ssl) .sslSocketFactory(sslSocketFactory).sslParameters(sslParameters) .hostnameVerifier(hostnameVerifier).build()); } public Jedis(URI uri) { if (!JedisURIHelper.isValid(uri)) { throw new InvalidURIException(String.format( "Cannot open Redis connection due invalid URI \"%s\".", uri.toString())); } connection = new Connection(new HostAndPort(uri.getHost(), uri.getPort()), DefaultJedisClientConfig.builder().user(JedisURIHelper.getUser(uri)) .password(JedisURIHelper.getPassword(uri)).database(JedisURIHelper.getDBIndex(uri)) .protocol(JedisURIHelper.getRedisProtocol(uri)) .ssl(JedisURIHelper.isRedisSSLScheme(uri)).build()); } public Jedis(URI uri, final SSLSocketFactory sslSocketFactory, final SSLParameters sslParameters, final HostnameVerifier hostnameVerifier) { this(uri, DefaultJedisClientConfig.builder().sslSocketFactory(sslSocketFactory) .sslParameters(sslParameters).hostnameVerifier(hostnameVerifier).build()); } public Jedis(final URI uri, final int timeout) { this(uri, timeout, timeout); } public Jedis(final URI uri, final int timeout, final SSLSocketFactory sslSocketFactory, final SSLParameters sslParameters, final HostnameVerifier hostnameVerifier) { this(uri, timeout, timeout, sslSocketFactory, sslParameters, hostnameVerifier); } public Jedis(final URI uri, final int connectionTimeout, final int soTimeout) { this(uri, DefaultJedisClientConfig.builder().connectionTimeoutMillis(connectionTimeout) .socketTimeoutMillis(soTimeout).build()); } public Jedis(final URI uri, final int connectionTimeout, final int soTimeout, final SSLSocketFactory sslSocketFactory, final SSLParameters sslParameters, final HostnameVerifier hostnameVerifier) { this(uri, DefaultJedisClientConfig.builder().connectionTimeoutMillis(connectionTimeout) .socketTimeoutMillis(soTimeout).sslSocketFactory(sslSocketFactory) .sslParameters(sslParameters).hostnameVerifier(hostnameVerifier).build()); } public Jedis(final URI uri, final int connectionTimeout, final int soTimeout, final int infiniteSoTimeout, final SSLSocketFactory sslSocketFactory, final SSLParameters sslParameters, final HostnameVerifier hostnameVerifier) { this(uri, DefaultJedisClientConfig.builder().connectionTimeoutMillis(connectionTimeout) .socketTimeoutMillis(soTimeout).blockingSocketTimeoutMillis(infiniteSoTimeout) .sslSocketFactory(sslSocketFactory).sslParameters(sslParameters) .hostnameVerifier(hostnameVerifier).build()); } /** * Create a new Jedis with the provided URI and JedisClientConfig object. Note that all fields * that can be parsed from the URI will be used instead of the corresponding configuration values. This includes * the following fields: user, password, database, protocol version, and whether to use SSL. * * For example, if the URI is "redis://user:password@localhost:6379/1", the user and password fields will be set * to "user" and "password" respectively, the database field will be set to 1. Those fields will be ignored * from the JedisClientConfig object. * * @param uri The URI to connect to * @param config The JedisClientConfig object to use */ public Jedis(final URI uri, JedisClientConfig config) { if (!JedisURIHelper.isValid(uri)) { throw new InvalidURIException(String.format( "Cannot open Redis connection due invalid URI \"%s\".", uri.toString())); } connection = new Connection(new HostAndPort(uri.getHost(), uri.getPort()), DefaultJedisClientConfig.builder() .connectionTimeoutMillis(config.getConnectionTimeoutMillis()) .socketTimeoutMillis(config.getSocketTimeoutMillis()) .blockingSocketTimeoutMillis(config.getBlockingSocketTimeoutMillis()) .user(JedisURIHelper.getUser(uri)).password(JedisURIHelper.getPassword(uri)) .database(JedisURIHelper.getDBIndex(uri)).clientName(config.getClientName()) .protocol(JedisURIHelper.getRedisProtocol(uri)) .ssl(JedisURIHelper.isRedisSSLScheme(uri)).sslSocketFactory(config.getSslSocketFactory()) .sslParameters(config.getSslParameters()).hostnameVerifier(config.getHostnameVerifier()) .build()); RedisProtocol proto = config.getRedisProtocol(); if (proto != null) commandObjects.setProtocol(proto); } public Jedis(final JedisSocketFactory jedisSocketFactory) { connection = new Connection(jedisSocketFactory); } public Jedis(final JedisSocketFactory jedisSocketFactory, final JedisClientConfig clientConfig) { connection = new Connection(jedisSocketFactory, clientConfig); RedisProtocol proto = clientConfig.getRedisProtocol(); if (proto != null) commandObjects.setProtocol(proto); } public Jedis(final Connection connection) { this.connection = connection; } @Override public String toString() { return "Jedis{" + connection + '}'; } // Legacy public Connection getClient() { return getConnection(); } public Connection getConnection() { return connection; } // Legacy public void connect() { connection.connect(); } /** * Closing the socket will disconnect the server connection. */ public void disconnect() { connection.disconnect(); } public boolean isConnected() { return connection.isConnected(); } public boolean isBroken() { return connection.isBroken(); } public void resetState() { if (isConnected()) { if (transaction != null) { transaction.close(); } if (pipeline != null) { pipeline.close(); } // connection.resetState(); if (isInWatch) { connection.sendCommand(UNWATCH); connection.getStatusCodeReply(); isInWatch = false; } } transaction = null; pipeline = null; } protected void setDataSource(Pool jedisPool) { this.dataSource = jedisPool; } @Override public void close() { if (dataSource != null) { Pool pool = this.dataSource; this.dataSource = null; if (isBroken()) { pool.returnBrokenResource(this); } else { pool.returnResource(this); } } else { connection.close(); } } // Legacy public Transaction multi() { transaction = new Transaction(getConnection()) { @Override protected void onAfterExec() { resetState(); } @Override protected void onAfterDiscard() { resetState(); } }; return transaction; } // Legacy public Pipeline pipelined() { pipeline = new Pipeline(this); return pipeline; } // Legacy protected void checkIsInMultiOrPipeline() { // if (connection.isInMulti()) { if (transaction != null) { throw new IllegalStateException( "Cannot use Jedis when in Multi. Please use Transaction or reset jedis state."); } else if (pipeline != null && pipeline.hasPipelinedResponse()) { throw new IllegalStateException( "Cannot use Jedis when in Pipeline. Please use Pipeline or reset jedis state."); } } public int getDB() { return this.db; } /** * @return PONG */ @Override public String ping() { checkIsInMultiOrPipeline(); connection.sendCommand(Command.PING); return connection.getStatusCodeReply(); } /** * Works same as {@link Jedis#ping()} but returns argument message instead of PONG. * @param message * @return message */ public byte[] ping(final byte[] message) { checkIsInMultiOrPipeline(); connection.sendCommand(Command.PING, message); return connection.getBinaryBulkReply(); } /** * Select the DB with having the specified zero-based numeric index. For default every new * connection is automatically selected to DB 0. * @param index * @return OK */ @Override public String select(final int index) { checkIsInMultiOrPipeline(); connection.sendCommand(SELECT, toByteArray(index)); String statusCodeReply = connection.getStatusCodeReply(); this.db = index; return statusCodeReply; } @Override public String swapDB(final int index1, final int index2) { checkIsInMultiOrPipeline(); connection.sendCommand(SWAPDB, toByteArray(index1), toByteArray(index2)); return connection.getStatusCodeReply(); } /** * Delete all the keys of the currently selected DB. This command never fails. * @return OK */ @Override public String flushDB() { checkIsInMultiOrPipeline(); return connection.executeCommand(commandObjects.flushDB()); } /** * Delete all the keys of the currently selected DB. This command never fails. * @param flushMode * @return OK */ @Override public String flushDB(FlushMode flushMode) { checkIsInMultiOrPipeline(); connection.sendCommand(FLUSHDB, flushMode.getRaw()); return connection.getStatusCodeReply(); } /** * Delete all the keys of all the existing databases, not just the currently selected one. This * command never fails. * @return OK */ @Override public String flushAll() { checkIsInMultiOrPipeline(); return connection.executeCommand(commandObjects.flushAll()); } /** * Delete all the keys of all the existing databases, not just the currently selected one. This * command never fails. * @param flushMode * @return OK */ @Override public String flushAll(FlushMode flushMode) { checkIsInMultiOrPipeline(); connection.sendCommand(FLUSHALL, flushMode.getRaw()); return connection.getStatusCodeReply(); } /** * COPY source destination [DB destination-db] [REPLACE] * * @param srcKey the source key. * @param dstKey the destination key. * @param db * @param replace */ @Override public boolean copy(byte[] srcKey, byte[] dstKey, int db, boolean replace) { checkIsInMultiOrPipeline(); return connection.executeCommand(commandObjects.copy(srcKey, dstKey, db, replace)); } /** * COPY source destination [DB destination-db] [REPLACE] * * @param srcKey the source key. * @param dstKey the destination key. * @param replace */ @Override public boolean copy(byte[] srcKey, byte[] dstKey, boolean replace) { checkIsInMultiOrPipeline(); return connection.executeCommand(commandObjects.copy(srcKey, dstKey, replace)); } /** * Set the string value as value of the key. The string can't be longer than 1073741824 bytes (1 * GB). *

* Time complexity: O(1) * @param key * @param value * @return OK */ @Override public String set(final byte[] key, final byte[] value) { checkIsInMultiOrPipeline(); return connection.executeCommand(commandObjects.set(key, value)); } /** * Set the string value as value of the key. The string can't be longer than 1073741824 bytes (1 * GB). * @param key * @param value * @param params NX|XX, NX -- Only set the key if it does not already exist. XX -- Only set the * key if it already exists. EX|PX, expire time units: EX = seconds; PX = milliseconds * @return simple-string-reply {@code OK} if {@code SET} was executed correctly, or {@code null} * if the {@code SET} operation was not performed because the user specified the NX or XX option * but the condition was not met. */ @Override public String set(final byte[] key, final byte[] value, final SetParams params) { checkIsInMultiOrPipeline(); return connection.executeCommand(commandObjects.set(key, value, params)); } /** * Get the value of the specified key. If the key does not exist the special value 'nil' is * returned. If the value stored at key is not a string an error is returned because GET can only * handle string values. *

* Time complexity: O(1) * @param key * @return Bulk reply */ @Override public byte[] get(final byte[] key) { checkIsInMultiOrPipeline(); return connection.executeCommand(commandObjects.get(key)); } @Override public byte[] digestKey(final byte[] key) { checkIsInMultiOrPipeline(); return connection.executeCommand(commandObjects.digestKey(key)); } @Override public byte[] setGet(final byte[] key, final byte[] value) { checkIsInMultiOrPipeline(); return connection.executeCommand(commandObjects.setGet(key, value)); } @Override public byte[] setGet(final byte[] key, final byte[] value, final SetParams params) { checkIsInMultiOrPipeline(); return connection.executeCommand(commandObjects.setGet(key, value, params)); } /** * Get the value of key and delete the key. This command is similar to GET, except for the fact * that it also deletes the key on success (if and only if the key's value type is a string). *

* Time complexity: O(1) * @param key * @return The value of key */ @Override public byte[] getDel(final byte[] key) { checkIsInMultiOrPipeline(); return connection.executeCommand(commandObjects.getDel(key)); } @Override public byte[] getEx(final byte[] key, final GetExParams params) { checkIsInMultiOrPipeline(); return connection.executeCommand(commandObjects.getEx(key, params)); } /** * Test if the specified keys exist. The command returns the number of keys exist. * Time complexity: O(N) * @param keys * @return An integer greater than 0 if one or more keys exist, 0 if none of the specified keys exist */ @Override public long exists(final byte[]... keys) { checkIsInMultiOrPipeline(); return connection.executeCommand(commandObjects.exists(keys)); } /** * Test if the specified key exists. The command returns true if the key exists, otherwise false is * returned. Note that even keys set with an empty string as value will return true. Time * complexity: O(1) * @param key * @return {@code true} if the key exists, otherwise {@code false} */ @Override public boolean exists(final byte[] key) { checkIsInMultiOrPipeline(); return connection.executeCommand(commandObjects.exists(key)); } /** * Remove the specified keys. If a given key does not exist no operation is performed for this * key. The command returns the number of keys removed. Time complexity: O(1) * @param keys * @return The number of keys that were removed */ @Override public long del(final byte[]... keys) { checkIsInMultiOrPipeline(); return connection.executeCommand(commandObjects.del(keys)); } @Override public long delex(final byte[] key, final CompareCondition condition) { checkIsInMultiOrPipeline(); return connection.executeCommand(commandObjects.delex(key, condition)); } @Override public long del(final byte[] key) { checkIsInMultiOrPipeline(); return connection.executeCommand(commandObjects.del(key)); } /** * This command is very similar to DEL: it removes the specified keys. Just like DEL a key is * ignored if it does not exist. However, the command performs the actual memory reclaiming in a * different thread, so it is not blocking, while DEL is. This is where the command name comes * from: the command just unlinks the keys from the keyspace. The actual removal will happen later * asynchronously. *

* Time complexity: O(1) for each key removed regardless of its size. Then the command does O(N) * work in a different thread in order to reclaim memory, where N is the number of allocations the * deleted objects where composed of. * @param keys * @return The number of keys that were unlinked */ @Override public long unlink(final byte[]... keys) { checkIsInMultiOrPipeline(); return connection.executeCommand(commandObjects.unlink(keys)); } @Override public long unlink(final byte[] key) { checkIsInMultiOrPipeline(); return connection.executeCommand(commandObjects.unlink(key)); } /** * Return the type of the value stored at key in form of a string. The type can be one of "none", * "string", "list", "set". "none" is returned if the key does not exist. Time complexity: O(1) * @param key * @return "none" if the key does not exist, "string" if the key contains a String value, "list" * if the key contains a List value, "set" if the key contains a Set value, "zset" if the key * contains a Sorted Set value, "hash" if the key contains a Hash value */ @Override public String type(final byte[] key) { checkIsInMultiOrPipeline(); return connection.executeCommand(commandObjects.type(key)); } /** * Returns all the keys matching the glob-style pattern as space separated strings. For example if * you have in the database the keys "foo" and "foobar" the command "KEYS foo*" will return * "foo foobar". *

* Note that while the time complexity for this operation is O(n) the constant times are pretty * low. For example Redis running on an entry level laptop can scan a 1 million keys database in * 40 milliseconds. Still it's better to consider this one of the slow commands that may ruin * the DB performance if not used with care. *

* In other words this command is intended only for debugging and special operations like creating * a script to change the DB schema. Don't use it in your normal code. Use Redis Sets in order to * group together a subset of objects. *

* Glob style patterns examples: *

    *
  • h?llo will match hello hallo hhllo *
  • h*llo will match hllo heeeello *
  • h[ae]llo will match hello and hallo, but not hillo *
*

* Use \ to escape special chars if you want to match them verbatim. *

* Time complexity: O(n) (with n being the number of keys in the DB, and assuming keys and pattern * of limited length) * @param pattern * @return Multi bulk reply */ @Override public Set keys(final byte[] pattern) { checkIsInMultiOrPipeline(); return connection.executeCommand(commandObjects.keys(pattern)); } /** * Return a randomly selected key from the currently selected DB. *

* Time complexity: O(1) * @return The randomly selected key or an empty string is the database is empty */ @Override public byte[] randomBinaryKey() { checkIsInMultiOrPipeline(); return connection.executeCommand(commandObjects.randomBinaryKey()); } /** * Atomically renames the key oldkey to newkey. If the source and destination name are the same an * error is returned. If newkey already exists it is overwritten. *

* Time complexity: O(1) * @param oldkey * @param newkey * @return OK */ @Override public String rename(final byte[] oldkey, final byte[] newkey) { checkIsInMultiOrPipeline(); return connection.executeCommand(commandObjects.rename(oldkey, newkey)); } /** * Rename oldkey into newkey but fails if the destination key newkey already exists. *

* Time complexity: O(1) * @param oldkey * @param newkey * @return 1 if the key was renamed 0 if the target key already exist */ @Override public long renamenx(final byte[] oldkey, final byte[] newkey) { checkIsInMultiOrPipeline(); return connection.executeCommand(commandObjects.renamenx(oldkey, newkey)); } /** * Return the number of keys in the currently selected database. * @return The number of keys */ @Override public long dbSize() { checkIsInMultiOrPipeline(); connection.sendCommand(DBSIZE); return connection.getIntegerReply(); } /** * Set a timeout on the specified key. After the timeout the key will be automatically deleted by * the server. A key with an associated timeout is said to be volatile in Redis terminology. *

* Volatile keys are stored on disk like the other keys, the timeout is persistent too like all * the other aspects of the dataset. Saving a dataset containing expires and stopping the server * does not stop the flow of time as Redis stores on disk the time when the key will no longer be * available as Unix time, and not the remaining seconds. *

* Since Redis 2.1.3 you can update the value of the timeout of a key already having an expire * set. It is also possible to undo the expire at all turning the key into a normal key using the * {@link Jedis#persist(byte[]) PERSIST} command. *

* Time complexity: O(1) * @see Expire Command * @param key * @param seconds * @return 1: the timeout was set. 0: the timeout was not set. */ @Override public long expire(final byte[] key, final long seconds) { checkIsInMultiOrPipeline(); return connection.executeCommand(commandObjects.expire(key, seconds)); } @Override public long expire(final byte[] key, final long seconds, final ExpiryOption expiryOption) { checkIsInMultiOrPipeline(); return connection.executeCommand((commandObjects.expire(key, seconds, expiryOption))); } /** * Set a timeout on the specified key. After the timeout the key will be automatically deleted by * the server. A key with an associated timeout is said to be volatile in Redis terminology. *

* Volatile keys are stored on disk like the other keys, the timeout is persistent too like all * the other aspects of the dataset. Saving a dataset containing expires and stopping the server * does not stop the flow of time as Redis stores on disk the time when the key will no longer be * available as Unix time, and not the remaining milliseconds. *

* Since Redis 2.1.3 you can update the value of the timeout of a key already having an expire * set. It is also possible to undo the expire at all turning the key into a normal key using the * {@link Jedis#persist(byte[]) PERSIST} command. *

* Time complexity: O(1) * @see PEXPIRE Command * @param key * @param milliseconds * @return 1: the timeout was set. 0: the timeout was not set. */ @Override public long pexpire(final byte[] key, final long milliseconds) { checkIsInMultiOrPipeline(); return connection.executeCommand(commandObjects.pexpire(key, milliseconds)); } @Override public long pexpire(final byte[] key, final long milliseconds, final ExpiryOption expiryOption) { checkIsInMultiOrPipeline(); return connection.executeCommand(commandObjects.pexpire(key, milliseconds, expiryOption)); } @Override public long expireTime(final byte[] key) { checkIsInMultiOrPipeline(); return connection.executeCommand((commandObjects.expireTime(key))); } @Override public long pexpireTime(final byte[] key) { checkIsInMultiOrPipeline(); return connection.executeCommand(commandObjects.pexpireTime(key)); } /** * EXPIREAT works exactly like {@link Jedis#expire(byte[], long) EXPIRE} but instead to get the * number of seconds representing the Time To Live of the key as a second argument (that is a * relative way of specifying the TTL), it takes an absolute one in the form of a UNIX timestamp * (Number of seconds elapsed since 1 Gen 1970). *

* EXPIREAT was introduced in order to implement the Append Only File persistence mode so that * EXPIRE commands are automatically translated into EXPIREAT commands for the append only file. * Of course EXPIREAT can also used by programmers that need a way to simply specify that a given * key should expire at a given time in the future. *

* Since Redis 2.1.3 you can update the value of the timeout of a key already having an expire * set. It is also possible to undo the expire at all turning the key into a normal key using the * {@link Jedis#persist(byte[]) PERSIST} command. *

* Time complexity: O(1) * @see Expire Command * @param key * @param unixTime * @return 1: the timeout was set. 0: the timeout was not set since * the key already has an associated timeout (this may happen only in Redis versions < * 2.1.3, Redis >= 2.1.3 will happily update the timeout), or the key does not exist. */ @Override public long expireAt(final byte[] key, final long unixTime) { checkIsInMultiOrPipeline(); return connection.executeCommand(commandObjects.expireAt(key, unixTime)); } @Override public long expireAt(byte[] key, long unixTime, ExpiryOption expiryOption) { checkIsInMultiOrPipeline(); return connection.executeCommand(commandObjects.expireAt(key, unixTime, expiryOption)); } @Override public long pexpireAt(final byte[] key, final long millisecondsTimestamp) { checkIsInMultiOrPipeline(); return connection.executeCommand(commandObjects.pexpireAt(key, millisecondsTimestamp)); } @Override public long pexpireAt(byte[] key, long millisecondsTimestamp, ExpiryOption expiryOption) { checkIsInMultiOrPipeline(); return connection.executeCommand(commandObjects.pexpireAt(key, millisecondsTimestamp, expiryOption)); } /** * The TTL command returns the remaining time to live in seconds of a key that has an * {@link Jedis#expire(byte[], long) EXPIRE} set. This introspection capability allows a Redis * connection to check how many seconds a given key will continue to be part of the dataset. * @param key * @return TTL in seconds, or a negative value in order to signal an error */ @Override public long ttl(final byte[] key) { checkIsInMultiOrPipeline(); return connection.executeCommand(commandObjects.ttl(key)); } /** * Alters the last access time of a key(s). A key is ignored if it does not exist. * Time complexity: O(N) where N is the number of keys that will be touched. * @param keys * @return The number of keys that were touched. */ @Override public long touch(final byte[]... keys) { checkIsInMultiOrPipeline(); return connection.executeCommand(commandObjects.touch(keys)); } @Override public long touch(final byte[] key) { checkIsInMultiOrPipeline(); return connection.executeCommand(commandObjects.touch(key)); } /** * Move the specified key from the currently selected DB to the specified destination DB. Note * that this command returns 1 only if the key was successfully moved, and 0 if the target key was * already there or if the source key was not found at all, so it is possible to use MOVE as a * locking primitive. * @param key * @param dbIndex * @return 1 if the key was moved 0 if the key was not moved because * already present on the target DB or was not found in the current DB. */ @Override public long move(final byte[] key, final int dbIndex) { checkIsInMultiOrPipeline(); connection.sendCommand(MOVE, key, toByteArray(dbIndex)); return connection.getIntegerReply(); } /** * GETSET is an atomic set this value and return the old value command. Set key to the string * value and return the old value stored at key. The string can't be longer than 1073741824 bytes * (1 GB). *

* Time complexity: O(1) * @param key * @param value * @return Bulk reply * @deprecated Use {@link Jedis#setGet(byte[], byte[])}. */ @Deprecated @Override public byte[] getSet(final byte[] key, final byte[] value) { checkIsInMultiOrPipeline(); return connection.executeCommand(commandObjects.getSet(key, value)); } /** * Get the values of all the specified keys. If one or more keys don't exist or is not of type * String, a 'nil' value is returned instead of the value of the specified key, but the operation * never fails. *

* Time complexity: O(1) for every key * @param keys * @return Multi bulk reply */ @Override public List mget(final byte[]... keys) { checkIsInMultiOrPipeline(); return connection.executeCommand(commandObjects.mget(keys)); } /** * SETNX works exactly like {@link Jedis#set(byte[], byte[]) SET} with the only difference that if * the key already exists no operation is performed. SETNX actually means "SET if Not eXists". *

* Time complexity: O(1) * @param key * @param value * @return 1 if the key was set 0 if the key was not set * @deprecated Use {@link Jedis#set(byte[], byte[], redis.clients.jedis.params.SetParams)} with {@link redis.clients.jedis.params.SetParams#nx()}. * Deprecated in Jedis 7.3.0. Mirrors Redis deprecation since 2.6.12. */ @Deprecated @Override public long setnx(final byte[] key, final byte[] value) { checkIsInMultiOrPipeline(); return connection.executeCommand(commandObjects.setnx(key, value)); } /** * The command is exactly equivalent to the following group of commands: * {@link Jedis#set(byte[], byte[]) SET} + {@link Jedis#expire(byte[], long) EXPIRE}. The * operation is atomic. *

* Time complexity: O(1) * @param key * @param seconds * @param value * @return OK * @deprecated Use {@link Jedis#set(byte[], byte[], redis.clients.jedis.params.SetParams)} with {@link redis.clients.jedis.params.SetParams#ex(long)}. * Deprecated in Jedis 7.3.0. Mirrors Redis deprecation since 2.6.12. */ @Deprecated @Override public String setex(final byte[] key, final long seconds, final byte[] value) { checkIsInMultiOrPipeline(); return connection.executeCommand(commandObjects.setex(key, seconds, value)); } /** * Set the respective keys to the respective values. MSET will replace old values with new * values, while {@link Jedis#msetnx(byte[][]) MSETNX} will not perform any operation at all even * if just a single key already exists. *

* Because of this semantic MSETNX can be used in order to set different keys representing * different fields of an unique logic object in a way that ensures that either all the fields or * none at all are set. *

* Both MSET and MSETNX are atomic operations. This means that for instance if the keys A and B * are modified, another connection talking to Redis can either see the changes to both A and B at * once, or no modification at all. * @see Jedis#msetnx(byte[][]) * @param keysvalues * @return OK */ @Override public String mset(final byte[]... keysvalues) { checkIsInMultiOrPipeline(); return connection.executeCommand(commandObjects.mset(keysvalues)); } /** * Set the respective keys to the respective values. {@link Jedis#mset(byte[][]) MSET} will * replace old values with new values, while MSETNX will not perform any operation at all even if * just a single key already exists. *

* Because of this semantic MSETNX can be used in order to set different keys representing * different fields of an unique logic object in a way that ensures that either all the fields or * none at all are set. *

* Both MSET and MSETNX are atomic operations. This means that for instance if the keys A and B * are modified, another connection talking to Redis can either see the changes to both A and B at * once, or no modification at all. * @see Jedis#mset(byte[][]) * @param keysvalues * @return 1 if the all the keys were set 0 if no key was set (at * least one key already existed) */ @Override public long msetnx(final byte[]... keysvalues) { checkIsInMultiOrPipeline(); return connection.executeCommand(commandObjects.msetnx(keysvalues)); } @Override public boolean msetex(final MSetExParams params, final byte[]... keysvalues) { checkIsInMultiOrPipeline(); return connection.executeCommand(commandObjects.msetex(params, keysvalues)); } /** * DECRBY work just like {@link Jedis#decr(byte[]) DECR} but instead to decrement by 1 the * decrement is integer. *

* DECR commands are limited to 64-bit signed integers. *

* Note: this is actually a string operation, that is, in Redis there are not "integer" types. * Simply the string stored at the key is parsed as a base 10 64-bit signed integer, incremented, * and then converted back as a string. *

* Time complexity: O(1) * @see Jedis#incr(byte[]) * @see Jedis#decr(byte[]) * @see Jedis#incrBy(byte[], long) * @param key * @param decrement * @return The value of key after the decrement */ @Override public long decrBy(final byte[] key, final long decrement) { checkIsInMultiOrPipeline(); return connection.executeCommand(commandObjects.decrBy(key, decrement)); } /** * Decrement the number stored at key by one. If the key does not exist or contains a value of a * wrong type, set the key to the value of "0" before to perform the decrement operation. *

* DECR commands are limited to 64-bit signed integers. *

* Note: this is actually a string operation, that is, in Redis there are not "integer" types. * Simply the string stored at the key is parsed as a base 10 64-bit signed integer, incremented, * and then converted back as a string. *

* Time complexity: O(1) * @see Jedis#incr(byte[]) * @see Jedis#incrBy(byte[], long) * @see Jedis#decrBy(byte[], long) * @param key * @return The value of key after the decrement */ @Override public long decr(final byte[] key) { checkIsInMultiOrPipeline(); return connection.executeCommand(commandObjects.decr(key)); } /** * INCRBY work just like {@link Jedis#incr(byte[]) INCR} but instead to increment by 1 the * increment is integer. *

* INCR commands are limited to 64-bit signed integers. *

* Note: this is actually a string operation, that is, in Redis there are not "integer" types. * Simply the string stored at the key is parsed as a base 10 64-bit signed integer, incremented, * and then converted back as a string. *

* Time complexity: O(1) * @see Jedis#incr(byte[]) * @see Jedis#decr(byte[]) * @see Jedis#decrBy(byte[], long) * @param key * @param increment * @return The value of key after the increment */ @Override public long incrBy(final byte[] key, final long increment) { checkIsInMultiOrPipeline(); return connection.executeCommand(commandObjects.incrBy(key, increment)); } /** * INCRBYFLOAT work just like {@link Jedis#incrBy(byte[], long)} INCRBY} but increments by floats * instead of integers. *

* INCRBYFLOAT commands are limited to double precision floating point values. *

* Note: this is actually a string operation, that is, in Redis there are not "double" types. * Simply the string stored at the key is parsed as a base double precision floating point value, * incremented, and then converted back as a string. There is no DECRYBYFLOAT but providing a * negative value will work as expected. *

* Time complexity: O(1) * @see Jedis#incr(byte[]) * @see Jedis#decr(byte[]) * @see Jedis#decrBy(byte[], long) * @param key the key to increment * @param increment the value to increment by * @return The value of key after the increment */ @Override public double incrByFloat(final byte[] key, final double increment) { checkIsInMultiOrPipeline(); return connection.executeCommand(commandObjects.incrByFloat(key, increment)); } /** * Increment the number stored at key by one. If the key does not exist or contains a value of a * wrong type, set the key to the value of "0" before to perform the increment operation. *

* INCR commands are limited to 64-bit signed integers. *

* Note: this is actually a string operation, that is, in Redis there are not "integer" types. * Simply the string stored at the key is parsed as a base 10 64-bit signed integer, incremented, * and then converted back as a string. *

* Time complexity: O(1) * @see Jedis#incrBy(byte[], long) * @see Jedis#decr(byte[]) * @see Jedis#decrBy(byte[], long) * @param key * @return The value of key after the increment */ @Override public long incr(final byte[] key) { checkIsInMultiOrPipeline(); return connection.executeCommand(commandObjects.incr(key)); } /** * If the key already exists and is a string, this command appends the provided value at the end * of the string. If the key does not exist it is created and set as an empty string, so APPEND * will be very similar to SET in this special case. *

* Time complexity: O(1). The amortized time complexity is O(1) assuming the appended value is * small and the already present value is of any size, since the dynamic string library used by * Redis will double the free space available on every reallocation. * @param key * @param value * @return The total length of the string after the append operation */ @Override public long append(final byte[] key, final byte[] value) { checkIsInMultiOrPipeline(); return connection.executeCommand(commandObjects.append(key, value)); } /** * Return a subset of the string from offset start to offset end (both offsets are inclusive). * Negative offsets can be used in order to provide an offset starting from the end of the string. * So -1 means the last char, -2 the penultimate and so forth. *

* The function handles out of range requests without raising an error, but just limiting the * resulting range to the actual length of the string. *

* Time complexity: O(start+n) (with start being the start index and n the total length of the * requested range). Note that the lookup part of this command is O(1) so for small strings this * is actually an O(1) command. * @param key * @param start * @param end * @return Bulk reply * @deprecated Use {@link Jedis#getrange(byte[], long, long)} instead. * Deprecated in Jedis 7.3.0. Mirrors Redis deprecation since 2.0.0. */ @Deprecated @Override public byte[] substr(final byte[] key, final int start, final int end) { checkIsInMultiOrPipeline(); return connection.executeCommand(commandObjects.substr(key, start, end)); } /** * Set the specified hash field to the specified value. *

* If key does not exist, a new key holding a hash is created. *

* Time complexity: O(1) * @param key * @param field * @param value * @return If the field already exists, and the HSET just produced an update of the value, 0 is * returned, otherwise if a new field is created 1 is returned. */ @Override public long hset(final byte[] key, final byte[] field, final byte[] value) { checkIsInMultiOrPipeline(); return connection.executeCommand(commandObjects.hset(key, field, value)); } @Override public long hset(final byte[] key, final Map hash) { checkIsInMultiOrPipeline(); return connection.executeCommand(commandObjects.hset(key, hash)); } @Override public long hsetex(byte[] key, HSetExParams params, byte[] field, byte[] value) { checkIsInMultiOrPipeline(); return connection.executeCommand(commandObjects.hsetex(key, params, field, value)); } @Override public long hsetex(byte[] key, HSetExParams params, Map hash){ checkIsInMultiOrPipeline(); return connection.executeCommand(commandObjects.hsetex(key, params, hash)); } /** * If key holds a hash, retrieve the value associated to the specified field. *

* If the field is not found or the key does not exist, a special 'nil' value is returned. *

* Time complexity: O(1) * @param key * @param field * @return Bulk reply */ @Override public byte[] hget(final byte[] key, final byte[] field) { checkIsInMultiOrPipeline(); return connection.executeCommand(commandObjects.hget(key, field)); } @Override public List hgetex(byte[] key, HGetExParams params, byte[]... fields){ checkIsInMultiOrPipeline(); return connection.executeCommand(commandObjects.hgetex(key, params, fields)); } @Override public List hgetdel(byte[] key, byte[]... fields){ checkIsInMultiOrPipeline(); return connection.executeCommand(commandObjects.hgetdel(key, fields)); } /** * Set the specified hash field to the specified value if the field not exists. Time * complexity: O(1) * @param key * @param field * @param value * @return If the field already exists, 0 is returned, otherwise if a new field is created 1 is * returned. */ @Override public long hsetnx(final byte[] key, final byte[] field, final byte[] value) { checkIsInMultiOrPipeline(); return connection.executeCommand(commandObjects.hsetnx(key, field, value)); } /** * Set the respective fields to the respective values. HMSET replaces old values with new values. *

* If key does not exist, a new key holding a hash is created. *

* Time complexity: O(N) (with N being the number of fields) * @param key * @param hash * @return OK * @deprecated Use {@link Jedis#hset(byte[], Map)} instead. * Deprecated in Jedis 7.3.0. Mirrors Redis deprecation since 4.0.0. */ @Deprecated @Override public String hmset(final byte[] key, final Map hash) { checkIsInMultiOrPipeline(); return connection.executeCommand(commandObjects.hmset(key, hash)); } /** * Retrieve the values associated to the specified fields. *

* If some of the specified fields do not exist, nil values are returned. Non existing keys are * considered like empty hashes. *

* Time complexity: O(N) (with N being the number of fields) * @param key * @param fields * @return A list of all the values associated with the specified fields, in the same order of the request */ @Override public List hmget(final byte[] key, final byte[]... fields) { checkIsInMultiOrPipeline(); return connection.executeCommand(commandObjects.hmget(key, fields)); } /** * Increment the number stored at field in the hash at key by value. If key does not exist, a new * key holding a hash is created. If field does not exist or holds a string, the value is set to 0 * before applying the operation. Since the value argument is signed you can use this command to * perform both increments and decrements. *

* The range of values supported by HINCRBY is limited to 64-bit signed integers. *

* Time complexity: O(1) * @param key * @param field * @param value * @return The value of key after the increment */ @Override public long hincrBy(final byte[] key, final byte[] field, final long value) { checkIsInMultiOrPipeline(); return connection.executeCommand(commandObjects.hincrBy(key, field, value)); } /** * Increment the number stored at field in the hash at key by a double precision floating point * value. If key does not exist, a new key holding a hash is created. If field does not exist or * holds a string, the value is set to 0 before applying the operation. Since the value argument * is signed you can use this command to perform both increments and decrements. *

* The range of values supported by HINCRBYFLOAT is limited to double precision floating point * values. *

* Time complexity: O(1) * @param key * @param field * @param value * @return The new value at field after the increment operation */ @Override public double hincrByFloat(final byte[] key, final byte[] field, final double value) { checkIsInMultiOrPipeline(); return connection.executeCommand(commandObjects.hincrByFloat(key, field, value)); } /** * Test for existence of a specified field in a hash. Time complexity: O(1) * @param key * @param field * @return {@code true} if the hash stored at key contains the specified field, {@code false} if the key is * not found or the field is not present. */ @Override public boolean hexists(final byte[] key, final byte[] field) { checkIsInMultiOrPipeline(); return connection.executeCommand(commandObjects.hexists(key, field)); } /** * Remove the specified field from an hash stored at key. *

* Time complexity: O(1) * @param key * @param fields * @return If the field was present in the hash it is deleted and 1 is returned, otherwise 0 is * returned and no operation is performed. */ @Override public long hdel(final byte[] key, final byte[]... fields) { checkIsInMultiOrPipeline(); return connection.executeCommand(commandObjects.hdel(key, fields)); } /** * Return the number of items in a hash. *

* Time complexity: O(1) * @param key * @return The number of entries (fields) contained in the hash stored at key. If the specified * key does not exist, 0 is returned assuming an empty hash. */ @Override public long hlen(final byte[] key) { checkIsInMultiOrPipeline(); return connection.executeCommand(commandObjects.hlen(key)); } /** * Return all the fields in a hash. *

* Time complexity: O(N), where N is the total number of entries * @param key * @return All the fields names contained into a hash. */ @Override public Set hkeys(final byte[] key) { checkIsInMultiOrPipeline(); return connection.executeCommand(commandObjects.hkeys(key)); } /** * Return all the values in a hash. *

* Time complexity: O(N), where N is the total number of entries * @param key * @return All the fields values contained into a hash. */ @Override public List hvals(final byte[] key) { checkIsInMultiOrPipeline(); return connection.executeCommand(commandObjects.hvals(key)); } /** * Return all the fields and associated values in a hash. *

* Time complexity: O(N), where N is the total number of entries * @param key * @return All the fields and values contained into a hash. */ @Override public Map hgetAll(final byte[] key) { checkIsInMultiOrPipeline(); return connection.executeCommand(commandObjects.hgetAll(key)); } /** * Get one random field from a hash. *

* Time complexity: O(N), where N is the number of fields returned * @param key * @return one random field from a hash. */ @Override public byte[] hrandfield(final byte[] key) { checkIsInMultiOrPipeline(); return connection.executeCommand(commandObjects.hrandfield(key)); } /** * Get multiple random fields from a hash. *

* Time complexity: O(N), where N is the number of fields returned * @param key * @return Multiple random fields from a hash. */ @Override public List hrandfield(final byte[] key, final long count) { checkIsInMultiOrPipeline(); return connection.executeCommand(commandObjects.hrandfield(key, count)); } /** * Get one or multiple random fields with values from a hash. *

* Time complexity: O(N), where N is the number of fields returned * @param key * @return One or multiple random fields with values from a hash. */ @Override public List> hrandfieldWithValues(final byte[] key, final long count) { checkIsInMultiOrPipeline(); return connection.executeCommand(commandObjects.hrandfieldWithValues(key, count)); } /** * Add the string value to the head (LPUSH) or tail (RPUSH) of the list stored at key. If the key * does not exist an empty list is created just before the append operation. If the key exists but * is not a List an error is returned. *

* Time complexity: O(1) * @param key * @param strings * @return The number of elements inside the list after the push operation */ @Override public long rpush(final byte[] key, final byte[]... strings) { checkIsInMultiOrPipeline(); return connection.executeCommand(commandObjects.rpush(key, strings)); } /** * Add the string value to the head (LPUSH) or tail (RPUSH) of the list stored at key. If the key * does not exist an empty list is created just before the append operation. If the key exists but * is not a List an error is returned. *

* Time complexity: O(1) * @param key * @param strings * @return The number of elements inside the list after the push operation */ @Override public long lpush(final byte[] key, final byte[]... strings) { checkIsInMultiOrPipeline(); return connection.executeCommand(commandObjects.lpush(key, strings)); } /** * Return the length of the list stored at the specified key. If the key does not exist zero is * returned (the same behaviour as for empty lists). If the value stored at key is not a list an * error is returned. *

* Time complexity: O(1) * @param key * @return The length of the list */ @Override public long llen(final byte[] key) { checkIsInMultiOrPipeline(); return connection.executeCommand(commandObjects.llen(key)); } /** * Return the specified elements of the list stored at the specified key. Start and end are * zero-based indexes. 0 is the first element of the list (the list head), 1 the next element and * so on. *

* For example LRANGE foobar 0 2 will return the first three elements of the list. *

* start and end can also be negative numbers indicating offsets from the end of the list. For * example -1 is the last element of the list, -2 the penultimate element and so on. *

* Consistency with range functions in various programming languages *

* Note that if you have a list of numbers from 0 to 100, LRANGE 0 10 will return 11 elements, * that is, rightmost item is included. This may or may not be consistent with behavior of * range-related functions in your programming language of choice (think Ruby's Range.new, * Array#slice or Python's range() function). *

* LRANGE behavior is consistent with one of Tcl. *

* Out-of-range indexes *

* Indexes out of range will not produce an error: if start is over the end of the list, or start * > end, an empty list is returned. If end is over the end of the list Redis will threat it * just like the last element of the list. *

* Time complexity: O(start+n) (with n being the length of the range and start being the start * offset) * @param key * @param start * @param stop * @return A list of elements in the specified range */ @Override public List lrange(final byte[] key, final long start, final long stop) { checkIsInMultiOrPipeline(); return connection.executeCommand(commandObjects.lrange(key, start, stop)); } /** * Trim an existing list so that it will contain only the specified range of elements specified. * Start and end are zero-based indexes. 0 is the first element of the list (the list head), 1 the * next element and so on. *

* For example LTRIM foobar 0 2 will modify the list stored at foobar key so that only the first * three elements of the list will remain. *

* start and end can also be negative numbers indicating offsets from the end of the list. For * example -1 is the last element of the list, -2 the penultimate element and so on. *

* Indexes out of range will not produce an error: if start is over the end of the list, or start * > end, an empty list is left as value. If end over the end of the list Redis will threat it * just like the last element of the list. *

* Hint: the obvious use of LTRIM is together with LPUSH/RPUSH. For example: *

* {@code lpush("mylist", "someelement"); ltrim("mylist", 0, 99); * } *

* The above two commands will push elements in the list taking care that the list will not grow * without limits. This is very useful when using Redis to store logs for example. It is important * to note that when used in this way LTRIM is an O(1) operation because in the average case just * one element is removed from the tail of the list. *

* Time complexity: O(n) (with n being len of list - len of range) * @param key * @param start * @param stop * @return OK */ @Override public String ltrim(final byte[] key, final long start, final long stop) { checkIsInMultiOrPipeline(); return connection.executeCommand(commandObjects.ltrim(key, start, stop)); } /** * Return the specified element of the list stored at the specified key. 0 is the first element, 1 * the second and so on. Negative indexes are supported, for example -1 is the last element, -2 * the penultimate and so on. *

* If the value stored at key is not of list type an error is returned. If the index is out of * range a 'nil' reply is returned. *

* Note that even if the average time complexity is O(n) asking for the first or the last element * of the list is O(1). *

* Time complexity: O(n) (with n being the length of the list) * @param key * @param index * @return The requested element */ @Override public byte[] lindex(final byte[] key, final long index) { checkIsInMultiOrPipeline(); return connection.executeCommand(commandObjects.lindex(key, index)); } /** * Set a new value as the element at index position of the List at key. *

* Out of range indexes will generate an error. *

* Similarly to other list commands accepting indexes, the index can be negative to access * elements starting from the end of the list. So -1 is the last element, -2 is the penultimate, * and so forth. *

* Time complexity: *

* O(N) (with N being the length of the list), setting the first or last elements of the list is * O(1). * @see Jedis#lindex(byte[], long) * @param key * @param index * @param value * @return OK */ @Override public String lset(final byte[] key, final long index, final byte[] value) { checkIsInMultiOrPipeline(); return connection.executeCommand(commandObjects.lset(key, index, value)); } /** * Remove the first count occurrences of the value element from the list. If count is zero all the * elements are removed. If count is negative elements are removed from tail to head, instead to * go from head to tail that is the normal behaviour. So for example LREM with count -2 and hello * as value to remove against the list (a,b,c,hello,x,hello,hello) will leave the list * (a,b,c,hello,x). The number of removed elements is returned as an integer, see below for more * information about the returned value. Note that non existing keys are considered like empty * lists by LREM, so LREM against non existing keys will always return 0. *

* Time complexity: O(N) (with N being the length of the list) * @param key * @param count * @param value * @return The number of removed elements if the operation succeeded */ @Override public long lrem(final byte[] key, final long count, final byte[] value) { checkIsInMultiOrPipeline(); return connection.executeCommand(commandObjects.lrem(key, count, value)); } /** * Atomically return and remove the first (LPOP) or last (RPOP) element of the list. For example * if the list contains the elements "a","b","c" LPOP will return "a" and the list will become * "b","c". *

* If the key does not exist or the list is already empty the special value 'nil' is returned. * @see Jedis#rpop(byte[]) * @param key * @return Bulk reply */ @Override public byte[] lpop(final byte[] key) { checkIsInMultiOrPipeline(); return connection.executeCommand(commandObjects.lpop(key)); } @Override public List lpop(final byte[] key, final int count) { checkIsInMultiOrPipeline(); return connection.executeCommand(commandObjects.lpop(key, count)); } /** * Returns the index of the first matching element inside a redis list. If the element is found, * its index (the zero-based position in the list) is returned. Otherwise, if no match is found, * 'nil' is returned. *

* Time complexity: O(N) where N is the number of elements in the list * @see Jedis#lpos(byte[], byte[]) * @param key * @param element * @return The index of first matching element in the list. Value will * be 'nil' when the element is not present in the list. */ @Override public Long lpos(final byte[] key, final byte[] element) { checkIsInMultiOrPipeline(); return connection.executeCommand(commandObjects.lpos(key, element)); } /** * In case there are multiple matches Rank option specifies the "rank" of the element to return. * A rank of 1 returns the first match, 2 to return the second match, and so forth. * If list `foo` has elements ("a","b","c","1","2","3","c","c"), The function call to get the * index of second occurrence of "c" will be as follows lpos("foo","c", LPosParams.lPosParams().rank(2)). *

* Maxlen option compares the element provided only with a given maximum number of list items. * A value of 1000 will make sure that the command performs only 1000 comparisons. The * comparison is made for the first part or the last part depending on the fact we use a positive or * negative rank. * Following is how we could use the Maxlen option lpos("foo", "b", LPosParams.lPosParams().rank(1).maxlen(2)). * @see Jedis#lpos(byte[], byte[], LPosParams) * @param key * @param element * @param params * @return The index of first matching element in the list. Value will be 'nil' when the element * is not present in the list */ @Override public Long lpos(final byte[] key, final byte[] element, final LPosParams params) { checkIsInMultiOrPipeline(); return connection.executeCommand(commandObjects.lpos(key, element, params)); } /** * Count will return list of position of all the first N matching elements. It is possible to * specify 0 as the number of matches, as a way to tell the command we want all the matches * found returned as an array of indexes. When count is used and no match is found, an empty list * is returned. *

* Time complexity: O(N) where N is the number of elements in the list * @see Jedis#lpos(byte[], byte[], LPosParams, long) * @param key * @param element * @param params * @param count * @return A list containing position of the matching elements inside the list */ @Override public List lpos(final byte[] key, final byte[] element, final LPosParams params, final long count) { checkIsInMultiOrPipeline(); return connection.executeCommand(commandObjects.lpos(key, element, params, count)); } /** * Atomically return and remove the first (LPOP) or last (RPOP) element of the list. For example * if the list contains the elements "a","b","c" LPOP will return "a" and the list will become * "b","c". *

* If the key does not exist or the list is already empty the special value 'nil' is returned. * @see Jedis#lpop(byte[]) * @param key * @return Bulk reply */ @Override public byte[] rpop(final byte[] key) { checkIsInMultiOrPipeline(); return connection.executeCommand(commandObjects.rpop(key)); } @Override public List rpop(final byte[] key, final int count) { checkIsInMultiOrPipeline(); return connection.executeCommand(commandObjects.rpop(key, count)); } /** * Atomically return and remove the last (tail) element of the srckey list, and push the element * as the first (head) element of the dstkey list. For example if the source list contains the * elements "a","b","c" and the destination list contains the elements "foo","bar" after an * RPOPLPUSH command the content of the two lists will be "a","b" and "c","foo","bar". *

* If the key does not exist or the list is already empty the special value 'nil' is returned. If * the srckey and dstkey are the same the operation is equivalent to removing the last element * from the list and pushing it as first element of the list, so it's a "list rotation" command. *

* Time complexity: O(1) * @param srckey * @param dstkey * @return Bulk reply * @deprecated Use {@link Jedis#lmove(byte[], byte[], ListDirection, ListDirection)} with * {@link ListDirection#RIGHT} and {@link ListDirection#LEFT}. * Deprecated in Jedis 7.3.0. Mirrors Redis deprecation since 6.2.0. */ @Deprecated @Override public byte[] rpoplpush(final byte[] srckey, final byte[] dstkey) { checkIsInMultiOrPipeline(); return connection.executeCommand(commandObjects.rpoplpush(srckey, dstkey)); } /** * Add the specified member to the set value stored at key. If member is already a member of the * set no operation is performed. If key does not exist a new set with the specified member as * sole member is created. If the key exists but does not hold a set value an error is returned. *

* Time complexity O(1) * @param key * @param members * @return The number of elements that were added to the set, not including all the elements already * present in the set */ @Override public long sadd(final byte[] key, final byte[]... members) { checkIsInMultiOrPipeline(); return connection.executeCommand(commandObjects.sadd(key, members)); } /** * Return all the members (elements) of the set value stored at key. This is just syntax glue for * {@link Jedis#sinter(byte[][])} SINTER}. *

* Time complexity O(N) * @param key the key of the set * @return All elements of the set */ @Override public Set smembers(final byte[] key) { checkIsInMultiOrPipeline(); return connection.executeCommand(commandObjects.smembers(key)); } /** * Remove the specified member from the set value stored at key. If member was not a member of the * set no operation is performed. If key does not hold a set value an error is returned. *

* Time complexity O(1) * @param key the key of the set * @param members the set member to remove * @return The number of members that were removed from the set, not including non-existing members */ @Override public long srem(final byte[] key, final byte[]... members) { checkIsInMultiOrPipeline(); return connection.executeCommand(commandObjects.srem(key, members)); } /** * Remove a random element from a Set returning it as return value. If the Set is empty or the key * does not exist, a nil object is returned. *

* The {@link Jedis#srandmember(byte[])} command does a similar work but the returned element is * not removed from the Set. *

* Time complexity O(1) * @param key * @return The removed member, or nil when key does not exist */ @Override public byte[] spop(final byte[] key) { checkIsInMultiOrPipeline(); return connection.executeCommand(commandObjects.spop(key)); } @Override public Set spop(final byte[] key, final long count) { checkIsInMultiOrPipeline(); return connection.executeCommand(commandObjects.spop(key, count)); } /** * Move the specified member from the set at srckey to the set at dstkey. This operation is * atomic, in every given moment the element will appear to be in the source or destination set * for accessing clients. *

* If the source set does not exist or does not contain the specified element no operation is * performed and zero is returned, otherwise the element is removed from the source set and added * to the destination set. On success one is returned, even if the element was already present in * the destination set. *

* An error is raised if the source or destination keys contain a non Set value. *

* Time complexity O(1) * @param srckey * @param dstkey * @param member * @return 1 if the element was moved, 0 if no operation was performed */ @Override public long smove(final byte[] srckey, final byte[] dstkey, final byte[] member) { checkIsInMultiOrPipeline(); return connection.executeCommand(commandObjects.smove(srckey, dstkey, member)); } /** * Return the set cardinality (number of elements). If the key does not exist 0 is returned, like * for empty sets. * @param key * @return The cardinality (number of elements) of the set */ @Override public long scard(final byte[] key) { checkIsInMultiOrPipeline(); return connection.executeCommand(commandObjects.scard(key)); } /** * Return true if member is a member of the set stored at key, otherwise false is returned. *

* Time complexity O(1) * @param key * @param member * @return {@code true} if the element is a member of the set, {@code false} otherwise */ @Override public boolean sismember(final byte[] key, final byte[] member) { checkIsInMultiOrPipeline(); return connection.executeCommand(commandObjects.sismember(key, member)); } /** * Returns whether each member is a member of the set stored at key. *

* Time complexity O(N) where N is the number of elements being checked for membership * @param key * @param members * @return List representing the membership of the given elements, in the same order as they are requested */ @Override public List smismember(final byte[] key, final byte[]... members) { checkIsInMultiOrPipeline(); return connection.executeCommand(commandObjects.smismember(key, members)); } /** * Return the members of a set resulting from the intersection of all the sets hold at the * specified keys. Like in {@link Jedis#lrange(byte[], long, long)} LRANGE} the result is sent to * the connection as a multi-bulk reply (see the protocol specification for more information). If * just a single key is specified, then this command produces the same result as * {@link Jedis#smembers(byte[]) SMEMBERS}. Actually SMEMBERS is just syntax sugar for SINTER. *

* Non existing keys are considered like empty sets, so if one of the keys is missing an empty set * is returned (since the intersection with an empty set always is an empty set). *

* Time complexity O(N*M) worst case where N is the cardinality of the smallest set and M the * number of sets * @param keys * @return A set with members of the resulting set */ @Override public Set sinter(final byte[]... keys) { checkIsInMultiOrPipeline(); return connection.executeCommand(commandObjects.sinter(keys)); } /** * This command works exactly like {@link Jedis#sinter(byte[][]) SINTER} but instead of being * returned the resulting set is stored as dstkey. *

* Time complexity O(N*M) worst case where N is the cardinality of the smallest set and M the * number of sets * @param dstkey * @param keys * @return The number of elements in the resulting set */ @Override public long sinterstore(final byte[] dstkey, final byte[]... keys) { checkIsInMultiOrPipeline(); return connection.executeCommand(commandObjects.sinterstore(dstkey, keys)); } /** * This command works exactly like {@link Jedis#sinter(byte[][]) SINTER} but instead of returning * the result set, it returns just the cardinality of the result. LIMIT defaults to 0 and means unlimited *

* Time complexity O(N*M) worst case where N is the cardinality of the smallest * @param keys * @return The cardinality of the set which would result from the intersection of all the given sets */ @Override public long sintercard(byte[]... keys) { checkIsInMultiOrPipeline(); return connection.executeCommand(commandObjects.sintercard(keys)); } /** * This command works exactly like {@link Jedis#sinter(byte[][]) SINTER} but instead of returning * the result set, it returns just the cardinality of the result. *

* Time complexity O(N*M) worst case where N is the cardinality of the smallest * @param limit If the intersection cardinality reaches limit partway through the computation, * the algorithm will exit and yield limit as the cardinality. * @param keys * @return The cardinality of the set which would result from the intersection of all the given sets */ @Override public long sintercard(int limit, byte[]... keys) { checkIsInMultiOrPipeline(); return connection.executeCommand(commandObjects.sintercard(limit, keys)); } /** * Return the members of a set resulting from the union of all the sets hold at the specified * keys. Like in {@link Jedis#lrange(byte[], long, long)} LRANGE} the result is sent to the * connection as a multi-bulk reply (see the protocol specification for more information). If just * a single key is specified, then this command produces the same result as * {@link Jedis#smembers(byte[]) SMEMBERS}. *

* Non existing keys are considered like empty sets. *

* Time complexity O(N) where N is the total number of elements in all the provided sets * @param keys * @return A set with members of the resulting set */ @Override public Set sunion(final byte[]... keys) { checkIsInMultiOrPipeline(); return connection.executeCommand(commandObjects.sunion(keys)); } /** * This command works exactly like {@link Jedis#sunion(byte[][]) SUNION} but instead of being * returned the resulting set is stored as dstkey. Any existing value in dstkey will be * over-written. *

* Time complexity O(N) where N is the total number of elements in all the provided sets * @param dstkey * @param keys * @return The number of elements in the resulting set */ @Override public long sunionstore(final byte[] dstkey, final byte[]... keys) { checkIsInMultiOrPipeline(); return connection.executeCommand(commandObjects.sunionstore(dstkey, keys)); } /** * Return the difference between the Set stored at key1 and all the Sets key2, ..., keyN *

* Example: * *

   * key1 = [x, a, b, c]
   * key2 = [c]
   * key3 = [a, d]
   * SDIFF key1,key2,key3 => [x, b]
   * 
* * Non existing keys are considered like empty sets. *

* Time complexity: *

* O(N) with N being the total number of elements of all the sets * @param keys * @return A set with members of the resulting set */ @Override public Set sdiff(final byte[]... keys) { checkIsInMultiOrPipeline(); return connection.executeCommand(commandObjects.sdiff(keys)); } /** * This command works exactly like {@link Jedis#sdiff(byte[][]) SDIFF} but instead of being returned * the resulting set is stored in dstkey. * @param dstkey * @param keys * @return The number of elements in the resulting set */ @Override public long sdiffstore(final byte[] dstkey, final byte[]... keys) { checkIsInMultiOrPipeline(); return connection.executeCommand(commandObjects.sdiffstore(dstkey, keys)); } /** * Return a random element from a Set, without removing the element. If the Set is empty or the * key does not exist, a nil object is returned. *

* The SPOP command does a similar work but the returned element is popped (removed) from the Set. *

* Time complexity O(1) * @param key * @return The randomly selected element */ @Override public byte[] srandmember(final byte[] key) { checkIsInMultiOrPipeline(); return connection.executeCommand(commandObjects.srandmember(key)); } @Override public List srandmember(final byte[] key, final int count) { checkIsInMultiOrPipeline(); return connection.executeCommand(commandObjects.srandmember(key, count)); } /** * Add the specified member having the specified score to the sorted set stored at key. If member * is already a member of the sorted set the score is updated, and the element reinserted in the * right position to ensure sorting. If key does not exist a new sorted set with the specified * member as sole member is created. If the key exists but does not hold a sorted set value an * error is returned. *

* The score value can be the string representation of a double precision floating point number. *

* Time complexity O(log(N)) with N being the number of elements in the sorted set * @param key * @param score * @param member * @return 1 if the new element was added, 0 if the element was already a member of the sorted * set and the score was updated */ @Override public long zadd(final byte[] key, final double score, final byte[] member) { checkIsInMultiOrPipeline(); return connection.executeCommand(commandObjects.zadd(key, score, member)); } @Override public long zadd(final byte[] key, final double score, final byte[] member, final ZAddParams params) { checkIsInMultiOrPipeline(); return connection.executeCommand(commandObjects.zadd(key, score, member, params)); } @Override public long zadd(final byte[] key, final Map scoreMembers) { checkIsInMultiOrPipeline(); return connection.executeCommand(commandObjects.zadd(key, scoreMembers)); } @Override public long zadd(final byte[] key, final Map scoreMembers, final ZAddParams params) { checkIsInMultiOrPipeline(); return connection.executeCommand(commandObjects.zadd(key, scoreMembers, params)); } @Override public Double zaddIncr(final byte[] key, final double score, final byte[] member, final ZAddParams params) { checkIsInMultiOrPipeline(); return connection.executeCommand(commandObjects.zaddIncr(key, score, member, params)); } @Override public List zrange(final byte[] key, final long start, final long stop) { checkIsInMultiOrPipeline(); return connection.executeCommand(commandObjects.zrange(key, start, stop)); } /** * Remove the specified member from the sorted set value stored at key. If member was not a member * of the set no operation is performed. If key does not not hold a set value an error is * returned. *

* Time complexity O(log(N)) with N being the number of elements in the sorted set * @param key * @param members * @return 1 if the new element was removed, 0 if the new element was not a member of the set */ @Override public long zrem(final byte[] key, final byte[]... members) { checkIsInMultiOrPipeline(); return connection.executeCommand(commandObjects.zrem(key, members)); } /** * If member already exists in the sorted set adds the increment to its score and updates the * position of the element in the sorted set accordingly. If member does not already exist in the * sorted set it is added with increment as score (that is, like if the previous score was * virtually zero). If key does not exist a new sorted set with the specified member as sole * member is created. If the key exists but does not hold a sorted set value an error is returned. *

* The score value can be the string representation of a double precision floating point number. * It's possible to provide a negative value to perform a decrement. *

* For an introduction to sorted sets check the Introduction to Redis data types page. *

* Time complexity O(log(N)) with N being the number of elements in the sorted set * @param key * @param increment * @param member * @return The new score */ @Override public double zincrby(final byte[] key, final double increment, final byte[] member) { checkIsInMultiOrPipeline(); return connection.executeCommand(commandObjects.zincrby(key, increment, member)); } @Override public Double zincrby(final byte[] key, final double increment, final byte[] member, final ZIncrByParams params) { checkIsInMultiOrPipeline(); return connection.executeCommand(commandObjects.zincrby(key, increment, member, params)); } /** * Return the rank (or index) or member in the sorted set at key, with scores being ordered from * low to high. *

* When the given member does not exist in the sorted set, the special value 'nil' is returned. * The returned rank (or index) of the member is 0-based for both commands. *

* Time complexity: *

* O(log(N)) * @see Jedis#zrevrank(byte[], byte[]) * @param key * @param member * @return The element as an integer if the element exists. A 'nil' bulk reply if there is no such element */ @Override public Long zrank(final byte[] key, final byte[] member) { checkIsInMultiOrPipeline(); return connection.executeCommand(commandObjects.zrank(key, member)); } /** * Return the rank (or index) or member in the sorted set at key, with scores being ordered from * high to low. *

* When the given member does not exist in the sorted set, the special value 'nil' is returned. * The returned rank (or index) of the member is 0-based for both commands. *

* Time complexity: *

* O(log(N)) * @see Jedis#zrank(byte[], byte[]) * @param key * @param member * @return The element as an integer if the element exists. A 'nil' bulk reply if there is no such element. */ @Override public Long zrevrank(final byte[] key, final byte[] member) { checkIsInMultiOrPipeline(); return connection.executeCommand(commandObjects.zrevrank(key, member)); } /** * Returns the rank and the score of member in the sorted set stored at key, with the scores * ordered from low to high. * @param key the key * @param member the member * @return the KeyValue contains rank and score. */ @Override public KeyValue zrankWithScore(byte[] key, byte[] member) { checkIsInMultiOrPipeline(); return connection.executeCommand(commandObjects.zrankWithScore(key, member)); } /** * Returns the rank and the score of member in the sorted set stored at key, with the scores * ordered from high to low. * @param key the key * @param member the member * @return the KeyValue contains rank and score. */ @Override public KeyValue zrevrankWithScore(byte[] key, byte[] member) { checkIsInMultiOrPipeline(); return connection.executeCommand(commandObjects.zrevrankWithScore(key, member)); } @Override public List zrevrange(final byte[] key, final long start, final long stop) { checkIsInMultiOrPipeline(); return connection.executeCommand(commandObjects.zrevrange(key, start, stop)); } @Override public List zrangeWithScores(final byte[] key, final long start, final long stop) { checkIsInMultiOrPipeline(); return connection.executeCommand(commandObjects.zrangeWithScores(key, start, stop)); } @Override public List zrevrangeWithScores(final byte[] key, final long start, final long stop) { checkIsInMultiOrPipeline(); return connection.executeCommand(commandObjects.zrevrangeWithScores(key, start, stop)); } @Override public List zrange(byte[] key, ZRangeParams zRangeParams) { checkIsInMultiOrPipeline(); return connection.executeCommand(commandObjects.zrange(key, zRangeParams)); } @Override public List zrangeWithScores(byte[] key, ZRangeParams zRangeParams) { checkIsInMultiOrPipeline(); return connection.executeCommand(commandObjects.zrangeWithScores(key, zRangeParams)); } @Override public long zrangestore(byte[] dest, byte[] src, ZRangeParams zRangeParams) { checkIsInMultiOrPipeline(); return connection.executeCommand(commandObjects.zrangestore(dest, src, zRangeParams)); } @Override public byte[] zrandmember(final byte[] key) { checkIsInMultiOrPipeline(); return connection.executeCommand(commandObjects.zrandmember(key)); } @Override public List zrandmember(final byte[] key, final long count) { checkIsInMultiOrPipeline(); return connection.executeCommand(commandObjects.zrandmember(key, count)); } @Override public List zrandmemberWithScores(final byte[] key, final long count) { checkIsInMultiOrPipeline(); return connection.executeCommand(commandObjects.zrandmemberWithScores(key, count)); } /** * Return the sorted set cardinality (number of elements). If the key does not exist 0 is * returned, like for empty sorted sets. *

* Time complexity O(1) * @param key * @return The cardinality (number of elements) of the set as an integer. */ @Override public long zcard(final byte[] key) { checkIsInMultiOrPipeline(); return connection.executeCommand(commandObjects.zcard(key)); } /** * Return the score of the specified element of the sorted set at key. If the specified element * does not exist in the sorted set, or the key does not exist at all, a special 'nil' value is * returned. *

* Time complexity: O(1) * @param key * @param member * @return The score */ @Override public Double zscore(final byte[] key, final byte[] member) { checkIsInMultiOrPipeline(); return connection.executeCommand(commandObjects.zscore(key, member)); } /** * Returns the scores associated with the specified members in the sorted set stored at key. * For every member that does not exist in the sorted set, a nil value is returned. *

* Time complexity: O(N) where N is the number of members being requested. * @param key * @param members * @return The scores */ @Override public List zmscore(final byte[] key, final byte[]... members) { checkIsInMultiOrPipeline(); return connection.executeCommand(commandObjects.zmscore(key, members)); } @Override public Tuple zpopmax(final byte[] key) { checkIsInMultiOrPipeline(); return connection.executeCommand(commandObjects.zpopmax(key)); } @Override public List zpopmax(final byte[] key, final int count) { checkIsInMultiOrPipeline(); return connection.executeCommand(commandObjects.zpopmax(key, count)); } @Override public Tuple zpopmin(final byte[] key) { checkIsInMultiOrPipeline(); return connection.executeCommand(commandObjects.zpopmin(key)); } @Override public List zpopmin(final byte[] key, final int count) { checkIsInMultiOrPipeline(); return connection.executeCommand(commandObjects.zpopmin(key, count)); } public String watch(final byte[]... keys) { checkIsInMultiOrPipeline(); connection.sendCommand(WATCH, keys); // return connection.getStatusCodeReply(); String status = connection.getStatusCodeReply(); isInWatch = true; return status; } public String unwatch() { checkIsInMultiOrPipeline(); connection.sendCommand(UNWATCH); return connection.getStatusCodeReply(); } /** * Sort a Set or a List. *

* Sort the elements contained in the List, Set, or Sorted Set value at key. By default sorting is * numeric with elements being compared as double precision floating point numbers. This is the * simplest form of SORT. * @see Jedis#sort(byte[], byte[]) * @see Jedis#sort(byte[], SortingParams) * @see Jedis#sort(byte[], SortingParams, byte[]) * @param key * @return Assuming the Set/List at key contains a list of numbers, the return value will be the * list of numbers ordered from the smallest to the biggest number. */ @Override public List sort(final byte[] key) { checkIsInMultiOrPipeline(); return connection.executeCommand(commandObjects.sort(key)); } /** * Sort a Set or a List accordingly to the specified parameters. *

* examples: *

* Given are the following sets and key/values: * *

   * x = [1, 2, 3]
   * y = [a, b, c]
   *
   * k1 = z
   * k2 = y
   * k3 = x
   *
   * w1 = 9
   * w2 = 8
   * w3 = 7
   * 
* * Sort Order: * *
   * sort(x) or sort(x, sp.asc())
   * -> [1, 2, 3]
   *
   * sort(x, sp.desc())
   * -> [3, 2, 1]
   *
   * sort(y)
   * -> [c, a, b]
   *
   * sort(y, sp.alpha())
   * -> [a, b, c]
   *
   * sort(y, sp.alpha().desc())
   * -> [c, a, b]
   * 
* * Limit (e.g. for Pagination): * *
   * sort(x, sp.limit(0, 2))
   * -> [1, 2]
   *
   * sort(y, sp.alpha().desc().limit(1, 2))
   * -> [b, a]
   * 
* * Sorting by external keys: * *
   * sort(x, sb.by(w*))
   * -> [3, 2, 1]
   *
   * sort(x, sb.by(w*).desc())
   * -> [1, 2, 3]
   * 
* * Getting external keys: * *
   * sort(x, sp.by(w*).get(k*))
   * -> [x, y, z]
   *
   * sort(x, sp.by(w*).get(#).get(k*))
   * -> [3, x, 2, y, 1, z]
   * 
* @see Jedis#sort(byte[]) * @see Jedis#sort(byte[], SortingParams, byte[]) * @param key * @param sortingParams * @return a list of sorted elements. */ @Override public List sort(final byte[] key, final SortingParams sortingParams) { checkIsInMultiOrPipeline(); return connection.executeCommand(commandObjects.sort(key, sortingParams)); } /** * Sort a Set or a List accordingly to the specified parameters and store the result at dstkey. * @see Jedis#sort(byte[], SortingParams) * @see Jedis#sort(byte[]) * @see Jedis#sort(byte[], byte[]) * @param key * @param sortingParams * @param dstkey * @return The number of elements of the list at dstkey. */ @Override public long sort(final byte[] key, final SortingParams sortingParams, final byte[] dstkey) { checkIsInMultiOrPipeline(); return connection.executeCommand(commandObjects.sort(key, sortingParams, dstkey)); } /** * Sort a Set or a List and Store the Result at dstkey. *

* Sort the elements contained in the List, Set, or Sorted Set value at key and store the result * at dstkey. By default sorting is numeric with elements being compared as double precision * floating point numbers. This is the simplest form of SORT. * @see Jedis#sort(byte[]) * @see Jedis#sort(byte[], SortingParams) * @see Jedis#sort(byte[], SortingParams, byte[]) * @param key * @param dstkey * @return The number of elements of the list at dstkey. */ @Override public long sort(final byte[] key, final byte[] dstkey) { checkIsInMultiOrPipeline(); return connection.executeCommand(commandObjects.sort(key, dstkey)); } @Override public List sortReadonly(byte[] key, SortingParams sortingParams) { checkIsInMultiOrPipeline(); return connection.executeCommand(commandObjects.sortReadonly(key, sortingParams)); } /** * Pop an element from a list, push it to another list and return it * @param srcKey * @param dstKey * @param from * @param to * @return The element being popped and pushed */ @Override public byte[] lmove(byte[] srcKey, byte[] dstKey, ListDirection from, ListDirection to) { checkIsInMultiOrPipeline(); return connection.executeCommand(commandObjects.lmove(srcKey, dstKey, from, to)); } /** * Pop an element from a list, push it to another list and return it; or block until one is available * @param srcKey * @param dstKey * @param from * @param to * @param timeout * @return The element being popped and pushed */ @Override public byte[] blmove(byte[] srcKey, byte[] dstKey, ListDirection from, ListDirection to, double timeout) { checkIsInMultiOrPipeline(); return connection.executeCommand(commandObjects.blmove(srcKey, dstKey, from, to, timeout)); } /** * BLPOP (and BRPOP) is a blocking list pop primitive. You can see this commands as blocking * versions of LPOP and RPOP able to block if the specified keys don't exist or contain empty * lists. *

* The following is a description of the exact semantic. We describe BLPOP but the two commands * are identical, the only difference is that BLPOP pops the element from the left (head) of the * list, and BRPOP pops from the right (tail). *

* Non blocking behavior *

* When BLPOP is called, if at least one of the specified keys contain a non empty list, an * element is popped from the head of the list and returned to the caller together with the name * of the key (BLPOP returns a two elements array, the first element is the key, the second the * popped value). *

* Keys are scanned from left to right, so for instance if you issue BLPOP list1 list2 list3 0 * against a dataset where list1 does not exist but list2 and list3 contain non empty lists, BLPOP * guarantees to return an element from the list stored at list2 (since it is the first non empty * list starting from the left). *

* Blocking behavior *

* If none of the specified keys exist or contain non empty lists, BLPOP blocks until some other * connection performs a LPUSH or an RPUSH operation against one of the lists. *

* Once new data is present on one of the lists, the connection finally returns with the name of the * key unblocking it and the popped value. *

* When blocking, if a non-zero timeout is specified, the connection will unblock returning a nil * special value if the specified amount of seconds passed without a push operation against at * least one of the specified keys. *

* The timeout argument is interpreted as an integer value. A timeout of zero means instead to * block forever. *

* Multiple clients blocking for the same keys *

* Multiple clients can block for the same key. They are put into a queue, so the first to be * served will be the one that started to wait earlier, in a first-blpopping first-served fashion. *

* blocking POP inside a MULTI/EXEC transaction *

* BLPOP and BRPOP can be used with pipelining (sending multiple commands and reading the replies * in batch), but it does not make sense to use BLPOP or BRPOP inside a MULTI/EXEC block (a Redis * transaction). *

* The behavior of BLPOP inside MULTI/EXEC when the list is empty is to return a multi-bulk nil * reply, exactly what happens when the timeout is reached. If you like science fiction, think at * it like if inside MULTI/EXEC the time will flow at infinite speed :) *

* Time complexity: O(1) * @param timeout * @param keys * @return BLPOP returns a two-elements array via a multi bulk reply in order to return both the * unblocking key and the popped value. *

When a non-zero timeout is specified, and the BLPOP operation timed out, the return value is a nil multi bulk reply. Most connection values will return false or nil accordingly to the programming language used. */ @Override public List blpop(final int timeout, final byte[]... keys) { return connection.executeCommand(commandObjects.blpop(timeout, keys)); } @Override public KeyValue blpop(final double timeout, final byte[]... keys) { return connection.executeCommand(commandObjects.blpop(timeout, keys)); } /** * BLPOP (and BRPOP) is a blocking list pop primitive. You can see this commands as blocking * versions of LPOP and RPOP able to block if the specified keys don't exist or contain empty * lists. *

* The following is a description of the exact semantic. We describe BLPOP but the two commands * are identical, the only difference is that BLPOP pops the element from the left (head) of the * list, and BRPOP pops from the right (tail). *

* Non blocking behavior *

* When BLPOP is called, if at least one of the specified keys contain a non empty list, an * element is popped from the head of the list and returned to the caller together with the name * of the key (BLPOP returns a two elements array, the first element is the key, the second the * popped value). *

* Keys are scanned from left to right, so for instance if you issue BLPOP list1 list2 list3 0 * against a dataset where list1 does not exist but list2 and list3 contain non empty lists, BLPOP * guarantees to return an element from the list stored at list2 (since it is the first non empty * list starting from the left). *

* Blocking behavior *

* If none of the specified keys exist or contain non empty lists, BLPOP blocks until some other * connection performs a LPUSH or an RPUSH operation against one of the lists. *

* Once new data is present on one of the lists, the connection finally returns with the name of the * key unblocking it and the popped value. *

* When blocking, if a non-zero timeout is specified, the connection will unblock returning a nil * special value if the specified amount of seconds passed without a push operation against at * least one of the specified keys. *

* The timeout argument is interpreted as an integer value. A timeout of zero means instead to * block forever. *

* Multiple clients blocking for the same keys *

* Multiple clients can block for the same key. They are put into a queue, so the first to be * served will be the one that started to wait earlier, in a first-blpopping first-served fashion. *

* blocking POP inside a MULTI/EXEC transaction *

* BLPOP and BRPOP can be used with pipelining (sending multiple commands and reading the replies * in batch), but it does not make sense to use BLPOP or BRPOP inside a MULTI/EXEC block (a Redis * transaction). *

* The behavior of BLPOP inside MULTI/EXEC when the list is empty is to return a multi-bulk nil * reply, exactly what happens when the timeout is reached. If you like science fiction, think at * it like if inside MULTI/EXEC the time will flow at infinite speed :) *

* Time complexity: O(1) * @param timeout * @param keys * @return BLPOP returns a two-elements array via a multi bulk reply in order to return both the * unblocking key and the popped value. *

When a non-zero timeout is specified, and the BLPOP operation timed out, the return value is a nil multi bulk reply. Most connection values will return false or nil accordingly to the programming language used. */ @Override public List brpop(final int timeout, final byte[]... keys) { return connection.executeCommand(commandObjects.brpop(timeout, keys)); } @Override public KeyValue brpop(final double timeout, final byte[]... keys) { return connection.executeCommand(commandObjects.brpop(timeout, keys)); } @Override public KeyValue> lmpop(ListDirection direction, byte[]... keys) { checkIsInMultiOrPipeline(); return connection.executeCommand(commandObjects.lmpop(direction, keys)); } @Override public KeyValue> lmpop(ListDirection direction, int count, byte[]... keys) { checkIsInMultiOrPipeline(); return connection.executeCommand(commandObjects.lmpop(direction, count, keys)); } @Override public KeyValue> blmpop(double timeout, ListDirection direction, byte[]... keys) { checkIsInMultiOrPipeline(); return connection.executeCommand(commandObjects.blmpop(timeout, direction, keys)); } @Override public KeyValue> blmpop(double timeout, ListDirection direction, int count, byte[]... keys) { checkIsInMultiOrPipeline(); return connection.executeCommand(commandObjects.blmpop(timeout, direction, count, keys)); } @Override public KeyValue bzpopmax(final double timeout, final byte[]... keys) { return connection.executeCommand(commandObjects.bzpopmax(timeout, keys)); } @Override public KeyValue bzpopmin(final double timeout, final byte[]... keys) { return connection.executeCommand(commandObjects.bzpopmin(timeout, keys)); } /** * Request for authentication in a password protected Redis server. A Redis server can be * instructed to require a password before to allow clients to issue commands. This is done using * the requirepass directive in the Redis configuration file. If the password given by the connection * is correct the server replies with an OK status code reply and starts accepting commands from * the connection. Otherwise, an error is returned and the clients needs to try a new password. Note * that for the high performance nature of Redis it is possible to try a lot of passwords in * parallel in very short time, so make sure to generate a strong and very long password so that * this attack is infeasible. * @param password * @return OK */ @Override public String auth(final String password) { checkIsInMultiOrPipeline(); connection.sendCommand(Command.AUTH, password); return connection.getStatusCodeReply(); } /** * Request for authentication with a Redis Server that is using ACL where user are authenticated with * username and password. * See https://redis.io/topics/acl * @param user * @param password * @return OK */ @Override public String auth(final String user, final String password) { checkIsInMultiOrPipeline(); connection.sendCommand(Command.AUTH, user, password); return connection.getStatusCodeReply(); } @Override public long zcount(final byte[] key, final double min, final double max) { checkIsInMultiOrPipeline(); return connection.executeCommand(commandObjects.zcount(key, min, max)); } @Override public long zcount(final byte[] key, final byte[] min, final byte[] max) { checkIsInMultiOrPipeline(); return connection.executeCommand(commandObjects.zcount(key, min, max)); } @Override public List zdiff(final byte[]... keys) { checkIsInMultiOrPipeline(); return connection.executeCommand(commandObjects.zdiff(keys)); } @Override public List zdiffWithScores(final byte[]... keys) { checkIsInMultiOrPipeline(); return connection.executeCommand(commandObjects.zdiffWithScores(keys)); } @Override @Deprecated public long zdiffStore(final byte[] dstkey, final byte[]... keys) { checkIsInMultiOrPipeline(); return connection.executeCommand(commandObjects.zdiffStore(dstkey, keys)); } @Override public long zdiffstore(final byte[] dstkey, final byte[]... keys) { checkIsInMultiOrPipeline(); return connection.executeCommand(commandObjects.zdiffstore(dstkey, keys)); } /** * Return the all the elements in the sorted set at key with a score between min and max * (including elements with score equal to min or max). *

* The elements having the same score are returned sorted lexicographically as ASCII strings (this * follows from a property of Redis sorted sets and does not involve further computation). *

* Using the optional {@link Jedis#zrangeByScore(byte[], double, double, int, int) LIMIT} it is * possible to get only a range of the matching elements in an SQL-alike way. Note that if the * offset is large the commands needs to traverse the list for offset elements and this adds up to * the O(M) figure. *

* The {@link Jedis#zcount(byte[], double, double) ZCOUNT} command is similar to * {@link Jedis#zrangeByScore(byte[], double, double) ZRANGEBYSCORE} but instead of returning the * actual elements in the specified interval, it just returns the number of matching elements. *

* Exclusive intervals and infinity *

* min and max can be -inf and +inf, so that you are not required to know what's the greatest or * smallest element in order to take, for instance, elements "up to a given value". *

* Also while the interval is for default closed (inclusive) it is possible to specify open * intervals prefixing the score with a "(" character, so for instance: *

* {@code ZRANGEBYSCORE zset (1.3 5} *

* Will return all the values with score > 1.3 and <= 5, while for instance: *

* {@code ZRANGEBYSCORE zset (5 (10} *

* Will return all the values with score > 5 and < 10 (5 and 10 excluded). *

* Time complexity: *

* O(log(N))+O(M) with N being the number of elements in the sorted set and M the number of * elements returned by the command, so if M is constant (for instance you always ask for the * first ten elements with LIMIT) you can consider it O(log(N)) * @see Jedis#zrangeByScore(byte[], double, double) * @see Jedis#zrangeByScore(byte[], double, double, int, int) * @see Jedis#zrangeByScoreWithScores(byte[], double, double) * @see Jedis#zrangeByScoreWithScores(byte[], double, double, int, int) * @see Jedis#zcount(byte[], double, double) * @param key * @param min * @param max * @return A list of elements in the specified score range */ @Override public List zrangeByScore(final byte[] key, final double min, final double max) { checkIsInMultiOrPipeline(); return connection.executeCommand(commandObjects.zrangeByScore(key, min, max)); } @Override public List zrangeByScore(final byte[] key, final byte[] min, final byte[] max) { checkIsInMultiOrPipeline(); return connection.executeCommand(commandObjects.zrangeByScore(key, min, max)); } /** * Return the all the elements in the sorted set at key with a score between min and max * (including elements with score equal to min or max). *

* The elements having the same score are returned sorted lexicographically as ASCII strings (this * follows from a property of Redis sorted sets and does not involve further computation). *

* Using the optional {@link Jedis#zrangeByScore(byte[], double, double, int, int) LIMIT} it is * possible to get only a range of the matching elements in an SQL-alike way. Note that if offset * is large the commands needs to traverse the list for offset elements and this adds up to the * O(M) figure. *

* The {@link Jedis#zcount(byte[], double, double) ZCOUNT} command is similar to * {@link Jedis#zrangeByScore(byte[], double, double) ZRANGEBYSCORE} but instead of returning the * actual elements in the specified interval, it just returns the number of matching elements. *

* Exclusive intervals and infinity *

* min and max can be -inf and +inf, so that you are not required to know what's the greatest or * smallest element in order to take, for instance, elements "up to a given value". *

* Also while the interval is for default closed (inclusive) it is possible to specify open * intervals prefixing the score with a "(" character, so for instance: *

* {@code ZRANGEBYSCORE zset (1.3 5} *

* Will return all the values with score > 1.3 and <= 5, while for instance: *

* {@code ZRANGEBYSCORE zset (5 (10} *

* Will return all the values with score > 5 and < 10 (5 and 10 excluded). *

* Time complexity: *

* O(log(N))+O(M) with N being the number of elements in the sorted set and M the number of * elements returned by the command, so if M is constant (for instance you always ask for the * first ten elements with LIMIT) you can consider it O(log(N)) * @see Jedis#zrangeByScore(byte[], double, double) * @see Jedis#zrangeByScore(byte[], double, double, int, int) * @see Jedis#zrangeByScoreWithScores(byte[], double, double) * @see Jedis#zrangeByScoreWithScores(byte[], double, double, int, int) * @see Jedis#zcount(byte[], double, double) * @param key * @param min * @param max * @param offset * @param count * @return A list of elements in the specified score range */ @Override public List zrangeByScore(final byte[] key, final double min, final double max, final int offset, final int count) { checkIsInMultiOrPipeline(); return connection.executeCommand(commandObjects.zrangeByScore(key, min, max, offset, count)); } @Override public List zrangeByScore(final byte[] key, final byte[] min, final byte[] max, final int offset, final int count) { checkIsInMultiOrPipeline(); return connection.executeCommand(commandObjects.zrangeByScore(key, min, max, offset, count)); } /** * Return the all the elements in the sorted set at key with a score between min and max * (including elements with score equal to min or max). *

* The elements having the same score are returned sorted lexicographically as ASCII strings (this * follows from a property of Redis sorted sets and does not involve further computation). *

* Using the optional {@link Jedis#zrangeByScore(byte[], double, double, int, int) LIMIT} it is * possible to get only a range of the matching elements in an SQL-alike way. Note that if offset * is large the commands needs to traverse the list for offset elements and this adds up to the * O(M) figure. *

* The {@link Jedis#zcount(byte[], double, double) ZCOUNT} command is similar to * {@link Jedis#zrangeByScore(byte[], double, double) ZRANGEBYSCORE} but instead of returning the * actual elements in the specified interval, it just returns the number of matching elements. *

* Exclusive intervals and infinity *

* min and max can be -inf and +inf, so that you are not required to know what's the greatest or * smallest element in order to take, for instance, elements "up to a given value". *

* Also while the interval is for default closed (inclusive) it is possible to specify open * intervals prefixing the score with a "(" character, so for instance: *

* {@code ZRANGEBYSCORE zset (1.3 5} *

* Will return all the values with score > 1.3 and <= 5, while for instance: *

* {@code ZRANGEBYSCORE zset (5 (10} *

* Will return all the values with score > 5 and < 10 (5 and 10 excluded). *

* Time complexity: *

* O(log(N))+O(M) with N being the number of elements in the sorted set and M the number of * elements returned by the command, so if M is constant (for instance you always ask for the * first ten elements with LIMIT) you can consider it O(log(N)) * @see Jedis#zrangeByScore(byte[], double, double) * @see Jedis#zrangeByScore(byte[], double, double, int, int) * @see Jedis#zrangeByScoreWithScores(byte[], double, double) * @see Jedis#zrangeByScoreWithScores(byte[], double, double, int, int) * @see Jedis#zcount(byte[], double, double) * @param key * @param min * @param max * @return A list of elements in the specified score range */ @Override public List zrangeByScoreWithScores(final byte[] key, final double min, final double max) { checkIsInMultiOrPipeline(); return connection.executeCommand(commandObjects.zrangeByScoreWithScores(key, min, max)); } @Override public List zrangeByScoreWithScores(final byte[] key, final byte[] min, final byte[] max) { checkIsInMultiOrPipeline(); return connection.executeCommand(commandObjects.zrangeByScoreWithScores(key, min, max)); } /** * Return the all the elements in the sorted set at key with a score between min and max * (including elements with score equal to min or max). *

* The elements having the same score are returned sorted lexicographically as ASCII strings (this * follows from a property of Redis sorted sets and does not involve further computation). *

* Using the optional {@link Jedis#zrangeByScore(byte[], double, double, int, int) LIMIT} it is * possible to get only a range of the matching elements in an SQL-alike way. Note that if offset * is large the commands needs to traverse the list for offset elements and this adds up to the * O(M) figure. *

* The {@link Jedis#zcount(byte[], double, double) ZCOUNT} command is similar to * {@link Jedis#zrangeByScore(byte[], double, double) ZRANGEBYSCORE} but instead of returning the * actual elements in the specified interval, it just returns the number of matching elements. *

* Exclusive intervals and infinity *

* min and max can be -inf and +inf, so that you are not required to know what's the greatest or * smallest element in order to take, for instance, elements "up to a given value". *

* Also while the interval is for default closed (inclusive) it is possible to specify open * intervals prefixing the score with a "(" character, so for instance: *

* {@code ZRANGEBYSCORE zset (1.3 5} *

* Will return all the values with score > 1.3 and <= 5, while for instance: *

* {@code ZRANGEBYSCORE zset (5 (10} *

* Will return all the values with score > 5 and < 10 (5 and 10 excluded). *

* Time complexity: *

* O(log(N))+O(M) with N being the number of elements in the sorted set and M the number of * elements returned by the command, so if M is constant (for instance you always ask for the * first ten elements with LIMIT) you can consider it O(log(N)) * @see Jedis#zrangeByScore(byte[], double, double) * @see Jedis#zrangeByScore(byte[], double, double, int, int) * @see Jedis#zrangeByScoreWithScores(byte[], double, double) * @see Jedis#zrangeByScoreWithScores(byte[], double, double, int, int) * @see Jedis#zcount(byte[], double, double) * @param key * @param min * @param max * @param offset * @param count * @return A list of elements in the specified score range */ @Override public List zrangeByScoreWithScores(final byte[] key, final double min, final double max, final int offset, final int count) { checkIsInMultiOrPipeline(); return connection.executeCommand(commandObjects.zrangeByScoreWithScores(key, min, max, offset, count)); } @Override public List zrangeByScoreWithScores(final byte[] key, final byte[] min, final byte[] max, final int offset, final int count) { checkIsInMultiOrPipeline(); return connection.executeCommand(commandObjects.zrangeByScoreWithScores(key, min, max, offset, count)); } @Override public List zrevrangeByScore(final byte[] key, final double max, final double min) { checkIsInMultiOrPipeline(); return connection.executeCommand(commandObjects.zrevrangeByScore(key, max, min)); } @Override public List zrevrangeByScore(final byte[] key, final byte[] max, final byte[] min) { checkIsInMultiOrPipeline(); return connection.executeCommand(commandObjects.zrevrangeByScore(key, max, min)); } @Override public List zrevrangeByScore(final byte[] key, final double max, final double min, final int offset, final int count) { checkIsInMultiOrPipeline(); return connection.executeCommand(commandObjects.zrevrangeByScore(key, max, min, offset, count)); } @Override public List zrevrangeByScore(final byte[] key, final byte[] max, final byte[] min, final int offset, final int count) { checkIsInMultiOrPipeline(); return connection.executeCommand(commandObjects.zrevrangeByScore(key, max, min, offset, count)); } @Override public List zrevrangeByScoreWithScores(final byte[] key, final double max, final double min) { checkIsInMultiOrPipeline(); return connection.executeCommand(commandObjects.zrevrangeByScoreWithScores(key, max, min)); } @Override public List zrevrangeByScoreWithScores(final byte[] key, final double max, final double min, final int offset, final int count) { checkIsInMultiOrPipeline(); return connection.executeCommand(commandObjects.zrevrangeByScoreWithScores(key, max, min, offset, count)); } @Override public List zrevrangeByScoreWithScores(final byte[] key, final byte[] max, final byte[] min) { checkIsInMultiOrPipeline(); return connection.executeCommand(commandObjects.zrevrangeByScoreWithScores(key, max, min)); } @Override public List zrevrangeByScoreWithScores(final byte[] key, final byte[] max, final byte[] min, final int offset, final int count) { checkIsInMultiOrPipeline(); return connection.executeCommand(commandObjects.zrevrangeByScoreWithScores(key, max, min, offset, count)); } /** * Remove all elements in the sorted set at key with rank between start and end. Start and end are * 0-based with rank 0 being the element with the lowest score. Both start and end can be negative * numbers, where they indicate offsets starting at the element with the highest rank. For * example: -1 is the element with the highest score, -2 the element with the second highest score * and so forth. *

* Time complexity: O(log(N))+O(M) with N being the number of elements in the sorted set * and M the number of elements removed by the operation * @param key * @param start * @param stop * @return The number of elements removed */ @Override public long zremrangeByRank(final byte[] key, final long start, final long stop) { checkIsInMultiOrPipeline(); return connection.executeCommand(commandObjects.zremrangeByRank(key, start, stop)); } /** * Remove all the elements in the sorted set at key with a score between min and max (including * elements with score equal to min or max). *

* Time complexity: *

* O(log(N))+O(M) with N being the number of elements in the sorted set and M the number of * elements removed by the operation * @param key * @param min * @param max * @return The number of elements removed */ @Override public long zremrangeByScore(final byte[] key, final double min, final double max) { checkIsInMultiOrPipeline(); return connection.executeCommand(commandObjects.zremrangeByScore(key, min, max)); } @Override public long zremrangeByScore(final byte[] key, final byte[] min, final byte[] max) { checkIsInMultiOrPipeline(); return connection.executeCommand(commandObjects.zremrangeByScore(key, min, max)); } /** * Add multiple sorted sets, This command is similar to ZUNIONSTORE, but instead of storing the * resulting sorted set, it is returned to the connection. * @param params * @param keys * @return The result of the union */ @Override public List zunion(final ZParams params, final byte[]... keys) { checkIsInMultiOrPipeline(); return connection.executeCommand(commandObjects.zunion(params, keys)); } /** * Add multiple sorted sets with scores, This command is similar to ZUNIONSTORE, but instead of * storing the resulting sorted set, it is returned to the connection. * @param params * @param keys * @return The result of the union with their scores */ @Override public List zunionWithScores(final ZParams params, final byte[]... keys) { checkIsInMultiOrPipeline(); return connection.executeCommand(commandObjects.zunionWithScores(params, keys)); } /** * Creates a union or intersection of N sorted sets given by keys k1 through kN, and stores it at * dstkey. It is mandatory to provide the number of input keys N, before passing the input keys * and the other (optional) arguments. *

* As the terms imply, the {@link Jedis#zinterstore(byte[], byte[][])} ZINTERSTORE} command * requires an element to be present in each of the given inputs to be inserted in the result. The * {@link Jedis#zunionstore(byte[], byte[][])} command inserts all elements across all inputs. *

* Using the WEIGHTS option, it is possible to add weight to each input sorted set. This means * that the score of each element in the sorted set is first multiplied by this weight before * being passed to the aggregation. When this option is not given, all weights default to 1. *

* With the AGGREGATE option, it is possible to specify how the results of the union or * intersection are aggregated. This option defaults to SUM, where the score of an element is * summed across the inputs where it exists. When this option is set to be either MIN or MAX, the * resulting set will contain the minimum or maximum score of an element across the inputs where * it exists. *

* Time complexity: O(N) + O(M log(M)) with N being the sum of the sizes of the input * sorted sets, and M being the number of elements in the resulting sorted set * @param dstkey * @param sets * @return The number of elements in the sorted set at dstkey */ @Override public long zunionstore(final byte[] dstkey, final byte[]... sets) { checkIsInMultiOrPipeline(); return connection.executeCommand(commandObjects.zunionstore(dstkey, sets)); } /** * Creates a union or intersection of N sorted sets given by keys k1 through kN, and stores it at * dstkey. It is mandatory to provide the number of input keys N, before passing the input keys * and the other (optional) arguments. *

* As the terms imply, the {@link Jedis#zinterstore(byte[], byte[][]) ZINTERSTORE} command * requires an element to be present in each of the given inputs to be inserted in the result. The * {@link Jedis#zunionstore(byte[], byte[][]) ZUNIONSTORE} command inserts all elements across * all inputs. *

* Using the WEIGHTS option, it is possible to add weight to each input sorted set. This means * that the score of each element in the sorted set is first multiplied by this weight before * being passed to the aggregation. When this option is not given, all weights default to 1. *

* With the AGGREGATE option, it is possible to specify how the results of the union or * intersection are aggregated. This option defaults to SUM, where the score of an element is * summed across the inputs where it exists. When this option is set to be either MIN or MAX, the * resulting set will contain the minimum or maximum score of an element across the inputs where * it exists. *

* Time complexity: O(N) + O(M log(M)) with N being the sum of the sizes of the input * sorted sets, and M being the number of elements in the resulting sorted set * @param dstkey * @param sets * @param params * @return The number of elements in the sorted set at dstkey */ @Override public long zunionstore(final byte[] dstkey, final ZParams params, final byte[]... sets) { checkIsInMultiOrPipeline(); return connection.executeCommand(commandObjects.zunionstore(dstkey, params, sets)); } /** * Intersect multiple sorted sets, This command is similar to ZINTERSTORE, but instead of storing * the resulting sorted set, it is returned to the connection. * @param params * @param keys * @return The result of the intersection */ @Override public List zinter(final ZParams params, final byte[]... keys) { checkIsInMultiOrPipeline(); return connection.executeCommand(commandObjects.zinter(params, keys)); } /** * Intersect multiple sorted sets, This command is similar to ZINTERSTORE, but instead of storing * the resulting sorted set, it is returned to the connection. * @param params * @param keys * @return The result of the intersection with scores */ @Override public List zinterWithScores(final ZParams params, final byte[]... keys) { checkIsInMultiOrPipeline(); return connection.executeCommand(commandObjects.zinterWithScores(params, keys)); } /** * Creates a union or intersection of N sorted sets given by keys k1 through kN, and stores it at * dstkey. It is mandatory to provide the number of input keys N, before passing the input keys * and the other (optional) arguments. *

* As the terms imply, the {@link Jedis#zinterstore(byte[], byte[][]) ZINTERSTORE} command * requires an element to be present in each of the given inputs to be inserted in the result. The * {@link Jedis#zunionstore(byte[], byte[][]) ZUNIONSTORE} command inserts all elements across all * inputs. *

* Using the WEIGHTS option, it is possible to add weight to each input sorted set. This means * that the score of each element in the sorted set is first multiplied by this weight before * being passed to the aggregation. When this option is not given, all weights default to 1. *

* With the AGGREGATE option, it is possible to specify how the results of the union or * intersection are aggregated. This option defaults to SUM, where the score of an element is * summed across the inputs where it exists. When this option is set to be either MIN or MAX, the * resulting set will contain the minimum or maximum score of an element across the inputs where * it exists. *

* Time complexity: O(N) + O(M log(M)) with N being the sum of the sizes of the input * sorted sets, and M being the number of elements in the resulting sorted set * @param dstkey * @param sets * @return The number of elements in the sorted set at dstkey */ @Override public long zinterstore(final byte[] dstkey, final byte[]... sets) { checkIsInMultiOrPipeline(); return connection.executeCommand(commandObjects.zinterstore(dstkey, sets)); } /** * Creates a union or intersection of N sorted sets given by keys k1 through kN, and stores it at * dstkey. It is mandatory to provide the number of input keys N, before passing the input keys * and the other (optional) arguments. *

* As the terms imply, the {@link Jedis#zinterstore(byte[], byte[][]) ZINTERSTORE} command * requires an element to be present in each of the given inputs to be inserted in the result. The * {@link Jedis#zunionstore(byte[], byte[][]) ZUNIONSTORE} command inserts all elements across all * inputs. *

* Using the WEIGHTS option, it is possible to add weight to each input sorted set. This means * that the score of each element in the sorted set is first multiplied by this weight before * being passed to the aggregation. When this option is not given, all weights default to 1. *

* With the AGGREGATE option, it is possible to specify how the results of the union or * intersection are aggregated. This option defaults to SUM, where the score of an element is * summed across the inputs where it exists. When this option is set to be either MIN or MAX, the * resulting set will contain the minimum or maximum score of an element across the inputs where * it exists. *

* Time complexity: O(N) + O(M log(M)) with N being the sum of the sizes of the input * sorted sets, and M being the number of elements in the resulting sorted set * @param dstkey * @param sets * @param params * @return The number of elements in the sorted set at dstkey */ @Override public long zinterstore(final byte[] dstkey, final ZParams params, final byte[]... sets) { checkIsInMultiOrPipeline(); return connection.executeCommand(commandObjects.zinterstore(dstkey, params, sets)); } @Override public long zintercard(byte[]... keys) { checkIsInMultiOrPipeline(); return connection.executeCommand(commandObjects.zintercard(keys)); } @Override public long zintercard(long limit, byte[]... keys) { checkIsInMultiOrPipeline(); return connection.executeCommand(commandObjects.zintercard(limit, keys)); } @Override public long zlexcount(final byte[] key, final byte[] min, final byte[] max) { checkIsInMultiOrPipeline(); return connection.executeCommand(commandObjects.zlexcount(key, min, max)); } @Override public List zrangeByLex(final byte[] key, final byte[] min, final byte[] max) { checkIsInMultiOrPipeline(); return connection.executeCommand(commandObjects.zrangeByLex(key, min, max)); } @Override public List zrangeByLex(final byte[] key, final byte[] min, final byte[] max, final int offset, final int count) { checkIsInMultiOrPipeline(); return connection.executeCommand(commandObjects.zrangeByLex(key, min, max, offset, count)); } @Override public List zrevrangeByLex(final byte[] key, final byte[] max, final byte[] min) { checkIsInMultiOrPipeline(); return connection.executeCommand(commandObjects.zrevrangeByLex(key, max, min)); } @Override public List zrevrangeByLex(final byte[] key, final byte[] max, final byte[] min, final int offset, final int count) { checkIsInMultiOrPipeline(); return connection.executeCommand(commandObjects.zrevrangeByLex(key, max, min, offset, count)); } @Override public long zremrangeByLex(final byte[] key, final byte[] min, final byte[] max) { checkIsInMultiOrPipeline(); return connection.executeCommand(commandObjects.zremrangeByLex(key, min, max)); } @Override public KeyValue> zmpop(SortedSetOption option, byte[]... keys) { checkIsInMultiOrPipeline(); return connection.executeCommand(commandObjects.zmpop(option, keys)); } @Override public KeyValue> zmpop(SortedSetOption option, int count, byte[]... keys) { checkIsInMultiOrPipeline(); return connection.executeCommand(commandObjects.zmpop(option, count, keys)); } @Override public KeyValue> bzmpop(double timeout, SortedSetOption option, byte[]... keys) { checkIsInMultiOrPipeline(); return connection.executeCommand(commandObjects.bzmpop(timeout, option, keys)); } @Override public KeyValue> bzmpop(double timeout, SortedSetOption option, int count, byte[]... keys) { checkIsInMultiOrPipeline(); return connection.executeCommand(commandObjects.bzmpop(timeout, option, count, keys)); } /** * Synchronously save the DB on disk. *

* Save the whole dataset on disk (this means that all the databases are saved, as well as keys * with an EXPIRE set (the expire is preserved). The server hangs while the saving is not * completed, no connection is served in the meanwhile. An OK code is returned when the DB was * fully stored in disk. *

* The background variant of this command is {@link Jedis#bgsave() BGSAVE} that is able to perform * the saving in the background while the server continues serving other clients. *

* @return OK */ @Override public String save() { connection.sendCommand(Command.SAVE); return connection.getStatusCodeReply(); } /** * Asynchronously save the DB on disk. *

* Save the DB in background. The OK code is immediately returned. Redis forks, the parent * continues to server the clients, the child saves the DB on disk then exit. A connection my be able * to check if the operation succeeded using the LASTSAVE command. * @return OK */ @Override public String bgsave() { connection.sendCommand(BGSAVE); return connection.getStatusCodeReply(); } @Override public String bgsaveSchedule() { connection.sendCommand(BGSAVE, SCHEDULE); return connection.getStatusCodeReply(); } /** * Rewrite the append only file in background when it gets too big. Please for detailed * information about the Redis Append Only File check the Append Only File Howto. *

* BGREWRITEAOF rewrites the Append Only File in background when it gets too big. The Redis Append * Only File is a Journal, so every operation modifying the dataset is logged in the Append Only * File (and replayed at startup). This means that the Append Only File always grows. In order to * rebuild its content the BGREWRITEAOF creates a new version of the append only file starting * directly form the dataset in memory in order to guarantee the generation of the minimal number * of commands needed to rebuild the database. *

* @return OK */ @Override public String bgrewriteaof() { connection.sendCommand(BGREWRITEAOF); return connection.getStatusCodeReply(); } /** * Return the UNIX time stamp of the last successfully saving of the dataset on disk. *

* Return the UNIX TIME of the last DB save executed with success. A connection may check if a * {@link Jedis#bgsave() BGSAVE} command succeeded reading the LASTSAVE value, then issuing a * BGSAVE command and checking at regular intervals every N seconds if LASTSAVE changed. * @return An UNIX time stamp */ @Override public long lastsave() { connection.sendCommand(LASTSAVE); return connection.getIntegerReply(); } /** * Synchronously save the DB on disk, then shutdown the server. *

* Stop all the clients, save the DB, then quit the server. This commands makes sure that the DB * is switched off without the lost of any data. * @throws JedisException with the status code reply on error. On success nothing is thrown since * the server quits and the connection is closed. */ @Override public void shutdown() throws JedisException { connection.sendCommand(SHUTDOWN); try { throw new JedisException(connection.getStatusCodeReply()); } catch (JedisConnectionException jce) { // expected connection.setBroken(); } } @Override public void shutdown(ShutdownParams shutdownParams) throws JedisException { connection.sendCommand(new CommandArguments(SHUTDOWN).addParams(shutdownParams)); try { throw new JedisException(connection.getStatusCodeReply()); } catch (JedisConnectionException jce) { // expected connection.setBroken(); } } @Override public String shutdownAbort() { connection.sendCommand(SHUTDOWN, ABORT); return connection.getStatusCodeReply(); } /** * Provide information and statistics about the server. *

* The info command returns different information and statistics about the server in an format * that's simple to parse by computers and easy to read by humans. *

* Format of the returned String: *

* All the fields are in the form field:value * *

   * redis_version:0.07
   * connected_clients:1
   * connected_slaves:0
   * used_memory:3187
   * changes_since_last_save:0
   * last_save_time:1237655729
   * total_connections_received:1
   * total_commands_processed:1
   * uptime_in_seconds:25
   * uptime_in_days:0
   * 
* * Notes *

* used_memory is returned in bytes, and is the total number of bytes allocated by the program * using malloc. *

* uptime_in_days is redundant since the uptime in seconds contains already the full uptime * information, this field is only mainly present for humans. *

* changes_since_last_save does not refer to the number of key changes, but to the number of * operations that produced some kind of change in the dataset. *

* @return Bulk reply */ @Override public String info() { connection.sendCommand(Command.INFO); return connection.getBulkReply(); } @Override public String info(final String section) { connection.sendCommand(Command.INFO, section); return connection.getBulkReply(); } /** * Dump all the received requests in real time. *

* MONITOR is a debugging command that outputs the whole sequence of commands received by the * Redis server. is very handy in order to understand what is happening into the database. This * command is used directly via telnet. * @param jedisMonitor */ public void monitor(final JedisMonitor jedisMonitor) { // connection.monitor(); connection.sendCommand(Command.MONITOR); connection.getStatusCodeReply(); jedisMonitor.proceed(connection); } /** * Change the replication settings. *

* The SLAVEOF command can change the replication settings of a slave on the fly. If a Redis * server is already acting as slave, the command SLAVEOF NO ONE will turn off the replication * turning the Redis server into a MASTER. In the proper form SLAVEOF hostname port will make the * server a slave of the specific server listening at the specified hostname and port. *

* If a server is already a slave of some master, SLAVEOF hostname port will stop the replication * against the old server and start the synchronization against the new one discarding the old * dataset. *

* The form SLAVEOF no one will stop replication turning the server into a MASTER but will not * discard the replication. So if the old master stop working it is possible to turn the slave * into a master and set the application to use the new master in read/write. Later when the other * Redis server will be fixed it can be configured in order to work as slave. * @param host * @param port * @return OK * @deprecated Use {@link Jedis#replicaof(java.lang.String, int)}. */ @Override @Deprecated public String slaveof(final String host, final int port) { connection.sendCommand(SLAVEOF, encode(host), toByteArray(port)); return connection.getStatusCodeReply(); } /** * @deprecated Use {@link Jedis#replicaofNoOne()}. */ @Override @Deprecated public String slaveofNoOne() { connection.sendCommand(SLAVEOF, NO.getRaw(), ONE.getRaw()); return connection.getStatusCodeReply(); } @Override public String replicaof(final String host, final int port) { connection.sendCommand(REPLICAOF, encode(host), toByteArray(port)); return connection.getStatusCodeReply(); } @Override public String replicaofNoOne() { connection.sendCommand(REPLICAOF, NO.getRaw(), ONE.getRaw()); return connection.getStatusCodeReply(); } @Override public List roleBinary() { checkIsInMultiOrPipeline(); connection.sendCommand(ROLE); return BuilderFactory.RAW_OBJECT_LIST.build(connection.getOne()); } /** * Retrieve the configuration of a running Redis server. Not all the configuration parameters are * supported. *

* CONFIG GET returns the current configuration parameters. This sub command only accepts a single * argument, that is glob style pattern. All the configuration parameters matching this parameter * are reported as a list of key-value pairs. *

* Example: * *

   * $ redis-cli config get '*'
   * 1. "dbfilename"
   * 2. "dump.rdb"
   * 3. "requirepass"
   * 4. (nil)
   * 5. "masterauth"
   * 6. (nil)
   * 7. "maxmemory"
   * 8. "0\n"
   * 9. "appendfsync"
   * 10. "everysec"
   * 11. "save"
   * 12. "3600 1 300 100 60 10000"
   *
   * $ redis-cli config get 'm*'
   * 1. "masterauth"
   * 2. (nil)
   * 3. "maxmemory"
   * 4. "0\n"
   * 
* @param pattern * @return Bulk reply. */ @Override public Map configGet(final byte[] pattern) { checkIsInMultiOrPipeline(); connection.sendCommand(Command.CONFIG, Keyword.GET.getRaw(), pattern); return BuilderFactory.BINARY_MAP.build(connection.getOne()); } @Override public Map configGet(byte[]... patterns) { checkIsInMultiOrPipeline(); connection.sendCommand(Command.CONFIG, joinParameters(Keyword.GET.getRaw(), patterns)); return BuilderFactory.BINARY_MAP.build(connection.getOne()); } /** * Reset the stats returned by INFO */ @Override public String configResetStat() { checkIsInMultiOrPipeline(); connection.sendCommand(Command.CONFIG, Keyword.RESETSTAT); return connection.getStatusCodeReply(); } /** * The CONFIG REWRITE command rewrites the redis.conf file the server was started with, applying * the minimal changes needed to make it reflect the configuration currently used by the server, * which may be different compared to the original one because of the use of the CONFIG SET * command. *

* The rewrite is performed in a very conservative way: *

    *
  • Comments and the overall structure of the original redis.conf are preserved as much as * possible.
  • *
  • If an option already exists in the old redis.conf file, it will be rewritten at the same * position (line number).
  • *
  • If an option was not already present, but it is set to its default value, it is not added * by the rewrite process.
  • *
  • If an option was not already present, but it is set to a non-default value, it is appended * at the end of the file.
  • *
  • Non used lines are blanked. For instance if you used to have multiple save directives, but * the current configuration has fewer or none as you disabled RDB persistence, all the lines will * be blanked.
  • *
*

* CONFIG REWRITE is also able to rewrite the configuration file from scratch if the original one * no longer exists for some reason. However, if the server was started without a configuration * file at all, the CONFIG REWRITE will just return an error. * @return OK when the configuration was rewritten properly. Otherwise, an error is returned. */ @Override public String configRewrite() { checkIsInMultiOrPipeline(); connection.sendCommand(Command.CONFIG, Keyword.REWRITE); return connection.getStatusCodeReply(); } /** * Alter the configuration of a running Redis server. Not all the configuration parameters are * supported. *

* The list of configuration parameters supported by CONFIG SET can be obtained issuing a * {@link Jedis#configGet(byte[]) CONFIG GET *} command. *

* The configuration set using CONFIG SET is immediately loaded by the Redis server that will * start acting as specified starting from the next command. *

* Parameters value format *

* The value of the configuration parameter is the same as the one of the same parameter in the * Redis configuration file, with the following exceptions: *

*

    *
  • The save parameter is a list of space-separated integers. Every pair of integers specify * the time and number of changes limit to trigger a save. For instance the command CONFIG SET * save "3600 10 60 10000" will configure the server to issue a background saving of the RDB file * every 3600 seconds if there are at least 10 changes in the dataset, and every 60 seconds if * there are at least 10000 changes. To completely disable automatic snapshots just set the * parameter as an empty string. *
  • All the integer parameters representing memory are returned and accepted only using bytes * as unit. *
* @param parameter * @param value * @return OK */ @Override public String configSet(final byte[] parameter, final byte[] value) { checkIsInMultiOrPipeline(); connection.sendCommand(Command.CONFIG, Keyword.SET.getRaw(), parameter, value); return connection.getStatusCodeReply(); } @Override public String configSet(final byte[]... parameterValues) { checkIsInMultiOrPipeline(); connection.sendCommand(Command.CONFIG, joinParameters(Keyword.SET.getRaw(), parameterValues)); return connection.getStatusCodeReply(); } @Override public String configSetBinary(Map parameterValues) { checkIsInMultiOrPipeline(); CommandArguments args = new CommandArguments(Command.CONFIG).add(Keyword.SET); parameterValues.forEach((k, v) -> args.add(k).add(v)); connection.sendCommand(args); return connection.getStatusCodeReply(); } @Override public long strlen(final byte[] key) { checkIsInMultiOrPipeline(); return connection.executeCommand(commandObjects.strlen(key)); } @Override public LCSMatchResult lcs(final byte[] keyA, final byte[] keyB, final LCSParams params) { checkIsInMultiOrPipeline(); return connection.executeCommand(commandObjects.lcs(keyA, keyB, params)); } @Override public long lpushx(final byte[] key, final byte[]... strings) { checkIsInMultiOrPipeline(); return connection.executeCommand(commandObjects.lpushx(key, strings)); } /** * Undo a {@link Jedis#expire(byte[], long) expire} at turning the expire key into a normal key. *

* Time complexity: O(1) * @param key * @return 1 if the key is now persist, 0 if the key is not persist (only happens when key not set) */ @Override public long persist(final byte[] key) { checkIsInMultiOrPipeline(); return connection.executeCommand(commandObjects.persist(key)); } @Override public long rpushx(final byte[] key, final byte[]... strings) { checkIsInMultiOrPipeline(); return connection.executeCommand(commandObjects.rpushx(key, strings)); } @Override public byte[] echo(final byte[] string) { checkIsInMultiOrPipeline(); connection.sendCommand(ECHO, string); return connection.getBinaryBulkReply(); } @Override public long linsert(final byte[] key, final ListPosition where, final byte[] pivot, final byte[] value) { checkIsInMultiOrPipeline(); return connection.executeCommand(commandObjects.linsert(key, where, pivot, value)); } /** * Pop a value from a list, push it to another list and return it; or block until one is available * @deprecated Use {@link Jedis#blmove(byte[], byte[], ListDirection, ListDirection, double)} with * {@link ListDirection#RIGHT} and {@link ListDirection#LEFT}. * Deprecated in Jedis 7.3.0. Mirrors Redis deprecation since 6.2.0. */ @Deprecated @Override public byte[] brpoplpush(final byte[] source, final byte[] destination, final int timeout) { checkIsInMultiOrPipeline(); return connection.executeCommand(commandObjects.brpoplpush(source, destination, timeout)); } /** * Sets or clears the bit at offset in the string value stored at key */ @Override public boolean setbit(final byte[] key, final long offset, final boolean value) { checkIsInMultiOrPipeline(); return connection.executeCommand(commandObjects.setbit(key, offset, value)); } /** * Returns the bit value at offset in the string value stored at key */ @Override public boolean getbit(final byte[] key, final long offset) { checkIsInMultiOrPipeline(); return connection.executeCommand(commandObjects.getbit(key, offset)); } @Override public long bitpos(final byte[] key, final boolean value) { return bitpos(key, value, new BitPosParams()); } @Override public long bitpos(final byte[] key, final boolean value, final BitPosParams params) { checkIsInMultiOrPipeline(); return connection.executeCommand(commandObjects.bitpos(key, value, params)); } @Override public long setrange(final byte[] key, final long offset, final byte[] value) { checkIsInMultiOrPipeline(); return connection.executeCommand(commandObjects.setrange(key, offset, value)); } @Override public byte[] getrange(final byte[] key, final long startOffset, final long endOffset) { checkIsInMultiOrPipeline(); return connection.executeCommand(commandObjects.getrange(key, startOffset, endOffset)); } public long publish(final byte[] channel, final byte[] message) { checkIsInMultiOrPipeline(); return connection.executeCommand(commandObjects.publish(channel, message)); } public void subscribe(BinaryJedisPubSub jedisPubSub, final byte[]... channels) { jedisPubSub.proceed(connection, channels); } public void psubscribe(BinaryJedisPubSub jedisPubSub, final byte[]... patterns) { jedisPubSub.proceedWithPatterns(connection, patterns); } /** * Evaluates scripts using the Lua interpreter built into Redis starting from version 2.6.0. * @param script * @param keys * @param args * @return Script result */ @Override public Object eval(final byte[] script, final List keys, final List args) { checkIsInMultiOrPipeline(); return connection.executeCommand(commandObjects.eval(script, keys, args)); } @Override public Object evalReadonly(byte[] script, List keys, List args) { checkIsInMultiOrPipeline(); return connection.executeCommand(commandObjects.evalReadonly(script, keys, args)); } protected static byte[][] getParamsWithBinary(List keys, List args) { final int keyCount = keys.size(); final int argCount = args.size(); byte[][] params = new byte[keyCount + argCount][]; for (int i = 0; i < keyCount; i++) params[i] = keys.get(i); for (int i = 0; i < argCount; i++) params[keyCount + i] = args.get(i); return params; } @Override public Object eval(final byte[] script, final int keyCount, final byte[]... params) { checkIsInMultiOrPipeline(); return connection.executeCommand(commandObjects.eval(script, keyCount, params)); } @Override public Object eval(final byte[] script) { checkIsInMultiOrPipeline(); return connection.executeCommand(commandObjects.eval(script)); } @Override public Object evalsha(final byte[] sha1) { checkIsInMultiOrPipeline(); return connection.executeCommand(commandObjects.evalsha(sha1)); } @Override public Object evalsha(final byte[] sha1, final List keys, final List args) { checkIsInMultiOrPipeline(); return connection.executeCommand(commandObjects.evalsha(sha1, keys, args)); } @Override public Object evalshaReadonly(byte[] sha1, List keys, List args) { checkIsInMultiOrPipeline(); return connection.executeCommand(commandObjects.evalshaReadonly(sha1, keys, args)); } @Override public Object evalsha(final byte[] sha1, final int keyCount, final byte[]... params) { checkIsInMultiOrPipeline(); return connection.executeCommand(commandObjects.evalsha(sha1, keyCount, params)); } @Override public String scriptFlush() { connection.sendCommand(SCRIPT, FLUSH); return connection.getStatusCodeReply(); } @Override public String scriptFlush(final FlushMode flushMode) { connection.sendCommand(SCRIPT, FLUSH.getRaw(), flushMode.getRaw()); return connection.getStatusCodeReply(); } @Override public Boolean scriptExists(final byte[] sha1) { byte[][] a = new byte[1][]; a[0] = sha1; return scriptExists(a).get(0); } @Override public List scriptExists(final byte[]... sha1) { connection.sendCommand(SCRIPT, joinParameters(Keyword.EXISTS.getRaw(), sha1)); return BuilderFactory.BOOLEAN_LIST.build(connection.getOne()); } @Override public byte[] scriptLoad(final byte[] script) { connection.sendCommand(SCRIPT, LOAD.getRaw(), script); return connection.getBinaryBulkReply(); } @Override public String scriptKill() { return connection.executeCommand(commandObjects.scriptKill()); } @Override public String slowlogReset() { return connection.executeCommand(commandObjects.slowlogReset()); } @Override public long slowlogLen() { connection.sendCommand(SLOWLOG, LEN); return connection.getIntegerReply(); } @Override public List slowlogGetBinary() { connection.sendCommand(SLOWLOG, Keyword.GET); return connection.getObjectMultiBulkReply(); } @Override public List slowlogGetBinary(final long entries) { connection.sendCommand(SLOWLOG, Keyword.GET.getRaw(), toByteArray(entries)); return connection.getObjectMultiBulkReply(); } @Override public Long objectRefcount(final byte[] key) { connection.sendCommand(OBJECT, REFCOUNT.getRaw(), key); return connection.getIntegerReply(); } @Override public byte[] objectEncoding(final byte[] key) { connection.sendCommand(OBJECT, ENCODING.getRaw(), key); return connection.getBinaryBulkReply(); } @Override public Long objectIdletime(final byte[] key) { connection.sendCommand(OBJECT, IDLETIME.getRaw(), key); return connection.getIntegerReply(); } @Override public List objectHelpBinary() { connection.sendCommand(OBJECT, HELP); return connection.getBinaryMultiBulkReply(); } @Override public Long objectFreq(final byte[] key) { connection.sendCommand(OBJECT, FREQ.getRaw(), key); return connection.getIntegerReply(); } @Override public long bitcount(final byte[] key) { checkIsInMultiOrPipeline(); return connection.executeCommand(commandObjects.bitcount(key)); } @Override public long bitcount(final byte[] key, final long start, final long end) { checkIsInMultiOrPipeline(); return connection.executeCommand(commandObjects.bitcount(key, start, end)); } @Override public long bitcount(final byte[] key, final long start, final long end, final BitCountOption option) { checkIsInMultiOrPipeline(); return connection.executeCommand(commandObjects.bitcount(key, start, end, option)); } @Override public long bitop(final BitOP op, final byte[] destKey, final byte[]... srcKeys) { checkIsInMultiOrPipeline(); return connection.executeCommand(commandObjects.bitop(op, destKey, srcKeys)); } @Override public byte[] dump(final byte[] key) { checkIsInMultiOrPipeline(); return connection.executeCommand(commandObjects.dump(key)); } @Override public String restore(final byte[] key, final long ttl, final byte[] serializedValue) { checkIsInMultiOrPipeline(); return connection.executeCommand(commandObjects.restore(key, ttl, serializedValue)); } @Override public String restore(final byte[] key, final long ttl, final byte[] serializedValue, final RestoreParams params) { checkIsInMultiOrPipeline(); return connection.executeCommand(commandObjects.restore(key, ttl, serializedValue, params)); } @Override public long pttl(final byte[] key) { checkIsInMultiOrPipeline(); return connection.executeCommand(commandObjects.pttl(key)); } /** * PSETEX works exactly like {@link Jedis#setex(byte[], long, byte[])} with the sole difference * that the expire time is specified in milliseconds instead of seconds. Time complexity: O(1) * @param key * @param milliseconds * @param value * @return OK * @deprecated Use {@link Jedis#set(byte[], byte[], redis.clients.jedis.params.SetParams)} with {@link redis.clients.jedis.params.SetParams#px(long)}. * Deprecated in Jedis 7.3.0. Mirrors Redis deprecation since 2.6.12. */ @Deprecated @Override public String psetex(final byte[] key, final long milliseconds, final byte[] value) { checkIsInMultiOrPipeline(); return connection.executeCommand(commandObjects.psetex(key, milliseconds, value)); } @Override public byte[] memoryDoctorBinary() { checkIsInMultiOrPipeline(); connection.sendCommand(MEMORY, DOCTOR); return connection.getBinaryBulkReply(); } @Override public Long memoryUsage(final byte[] key) { checkIsInMultiOrPipeline(); connection.sendCommand(MEMORY, USAGE.getRaw(), key); return connection.getIntegerReply(); } @Override public Long memoryUsage(final byte[] key, final int samples) { checkIsInMultiOrPipeline(); connection.sendCommand(MEMORY, USAGE.getRaw(), key, SAMPLES.getRaw(), toByteArray(samples)); return connection.getIntegerReply(); } @Override public String failover() { checkIsInMultiOrPipeline(); connection.sendCommand(Command.FAILOVER); connection.setTimeoutInfinite(); try { return connection.getStatusCodeReply(); } finally { connection.rollbackTimeout(); } } @Override public String failover(FailoverParams failoverParams) { checkIsInMultiOrPipeline(); CommandArguments args = new CommandArguments(Command.FAILOVER).addParams(failoverParams); connection.sendCommand(args); connection.setTimeoutInfinite(); try { return connection.getStatusCodeReply(); } finally { connection.rollbackTimeout(); } } @Override public String failoverAbort() { checkIsInMultiOrPipeline(); connection.sendCommand(Command.FAILOVER, ABORT); return connection.getStatusCodeReply(); } @Override public byte[] aclWhoAmIBinary() { checkIsInMultiOrPipeline(); connection.sendCommand(ACL, WHOAMI); return connection.getBinaryBulkReply(); } @Override public byte[] aclGenPassBinary() { checkIsInMultiOrPipeline(); connection.sendCommand(ACL, GENPASS); return connection.getBinaryBulkReply(); } @Override public byte[] aclGenPassBinary(int bits) { checkIsInMultiOrPipeline(); connection.sendCommand(ACL, GENPASS.getRaw(), toByteArray(bits)); return connection.getBinaryBulkReply(); } @Override public List aclListBinary() { checkIsInMultiOrPipeline(); connection.sendCommand(ACL, LIST); return connection.getBinaryMultiBulkReply(); } @Override public List aclUsersBinary() { checkIsInMultiOrPipeline(); connection.sendCommand(ACL, USERS); return connection.getBinaryMultiBulkReply(); } @Override public AccessControlUser aclGetUser(byte[] name) { checkIsInMultiOrPipeline(); connection.sendCommand(ACL, GETUSER.getRaw(), name); return BuilderFactory.ACCESS_CONTROL_USER.build(connection.getObjectMultiBulkReply()); } @Override public String aclSetUser(byte[] name) { checkIsInMultiOrPipeline(); connection.sendCommand(ACL, SETUSER.getRaw(), name); return connection.getStatusCodeReply(); } @Override public String aclSetUser(byte[] name, byte[]... rules) { checkIsInMultiOrPipeline(); connection.sendCommand(ACL, joinParameters(SETUSER.getRaw(), name, rules)); return connection.getStatusCodeReply(); } @Override public long aclDelUser(byte[]... names) { checkIsInMultiOrPipeline(); connection.sendCommand(ACL, joinParameters(DELUSER.getRaw(), names)); return connection.getIntegerReply(); } @Override public List aclCatBinary() { checkIsInMultiOrPipeline(); connection.sendCommand(ACL, CAT); return connection.getBinaryMultiBulkReply(); } @Override public List aclCat(byte[] category) { checkIsInMultiOrPipeline(); connection.sendCommand(ACL, CAT.getRaw(), category); return connection.getBinaryMultiBulkReply(); } @Override public List aclLogBinary() { checkIsInMultiOrPipeline(); connection.sendCommand(ACL, LOG); return connection.getBinaryMultiBulkReply(); } @Override public List aclLogBinary(int limit) { checkIsInMultiOrPipeline(); connection.sendCommand(ACL, LOG.getRaw(), toByteArray(limit)); return connection.getBinaryMultiBulkReply(); } @Override public String aclLogReset() { checkIsInMultiOrPipeline(); connection.sendCommand(ACL, LOG.getRaw(), Keyword.RESET.getRaw()); return connection.getStatusCodeReply(); } @Override public String clientKill(final byte[] ipPort) { checkIsInMultiOrPipeline(); connection.sendCommand(CLIENT, KILL.getRaw(), ipPort); return this.connection.getStatusCodeReply(); } @Override public String clientKill(final String ip, final int port) { return clientKill(ip + ':' + port); } @Override public long clientKill(ClientKillParams params) { checkIsInMultiOrPipeline(); connection.sendCommand(new CommandArguments(CLIENT).add(KILL).addParams(params)); return this.connection.getIntegerReply(); } @Override public byte[] clientGetnameBinary() { checkIsInMultiOrPipeline(); connection.sendCommand(CLIENT, GETNAME); return connection.getBinaryBulkReply(); } @Override public byte[] clientListBinary() { checkIsInMultiOrPipeline(); connection.sendCommand(CLIENT, LIST); return connection.getBinaryBulkReply(); } @Override public byte[] clientListBinary(ClientType type) { checkIsInMultiOrPipeline(); connection.sendCommand(CLIENT, LIST.getRaw(), type.getRaw()); return connection.getBinaryBulkReply(); } @Override public byte[] clientListBinary(final long... clientIds) { checkIsInMultiOrPipeline(); connection.sendCommand(CLIENT, clientListParams(clientIds)); return connection.getBinaryBulkReply(); } private byte[][] clientListParams(final long... clientIds) { final byte[][] params = new byte[2 + clientIds.length][]; int index = 0; params[index++] = Keyword.LIST.getRaw(); params[index++] = ID.getRaw(); for (final long clientId : clientIds) { params[index++] = toByteArray(clientId); } return params; } @Override public byte[] clientInfoBinary() { checkIsInMultiOrPipeline(); connection.sendCommand(CLIENT, Keyword.INFO); return connection.getBinaryBulkReply(); } @Override public String clientSetInfo(ClientAttributeOption attr, byte[] value) { checkIsInMultiOrPipeline(); connection.sendCommand(CLIENT, SETINFO.getRaw(), attr.getRaw(), value); return connection.getStatusCodeReply(); } @Override public String clientSetname(final byte[] name) { checkIsInMultiOrPipeline(); connection.sendCommand(CLIENT, SETNAME.getRaw(), name); return connection.getBulkReply(); } @Override public long clientId() { checkIsInMultiOrPipeline(); connection.sendCommand(CLIENT, ID); return connection.getIntegerReply(); } /** * Unblock a connection blocked in a blocking command from a different connection. * @param clientId */ @Override public long clientUnblock(final long clientId) { checkIsInMultiOrPipeline(); connection.sendCommand(CLIENT, UNBLOCK.getRaw(), toByteArray(clientId)); return connection.getIntegerReply(); } /** * Unblock a connection blocked in a blocking command from a different connection. * @param clientId * @param unblockType */ @Override public long clientUnblock(final long clientId, final UnblockType unblockType) { checkIsInMultiOrPipeline(); connection.sendCommand(CLIENT, UNBLOCK.getRaw(), toByteArray(clientId), unblockType.getRaw()); return connection.getIntegerReply(); } @Override public String clientPause(final long timeout) { checkIsInMultiOrPipeline(); connection.sendCommand(CLIENT, PAUSE.getRaw(), toByteArray(timeout)); return connection.getBulkReply(); } @Override public String clientPause(final long timeout, final ClientPauseMode mode) { checkIsInMultiOrPipeline(); connection.sendCommand(CLIENT, PAUSE.getRaw(), toByteArray(timeout), mode.getRaw()); return connection.getBulkReply(); } @Override public String clientUnpause() { checkIsInMultiOrPipeline(); connection.sendCommand(CLIENT, UNPAUSE); return connection.getBulkReply(); } @Override public String clientNoEvictOn() { checkIsInMultiOrPipeline(); connection.sendCommand(CLIENT, "NO-EVICT", "ON"); return connection.getBulkReply(); } @Override public String clientNoEvictOff() { checkIsInMultiOrPipeline(); connection.sendCommand(CLIENT, "NO-EVICT", "OFF"); return connection.getBulkReply(); } @Override public String clientNoTouchOn() { checkIsInMultiOrPipeline(); connection.sendCommand(CLIENT, "NO-TOUCH", "ON"); return connection.getStatusCodeReply(); } @Override public String clientNoTouchOff() { checkIsInMultiOrPipeline(); connection.sendCommand(CLIENT, "NO-TOUCH", "OFF"); return connection.getStatusCodeReply(); } @Override public TrackingInfo clientTrackingInfo() { checkIsInMultiOrPipeline(); connection.sendCommand(CLIENT, "TRACKINGINFO"); return TrackingInfo.TRACKING_INFO_BUILDER.build(connection.getOne()); } public List time() { checkIsInMultiOrPipeline(); connection.sendCommand(Command.TIME); return connection.getMultiBulkReply(); } @Override public String migrate(final String host, final int port, final byte[] key, final int destinationDb, final int timeout) { checkIsInMultiOrPipeline(); return connection.executeCommand(commandObjects.migrate(host, port, key, destinationDb, timeout)); } @Override public String migrate(final String host, final int port, final int destinationDB, final int timeout, final MigrateParams params, final byte[]... keys) { checkIsInMultiOrPipeline(); return connection.executeCommand(commandObjects.migrate(host, port, destinationDB, timeout, params, keys)); } @Override public String migrate(String host, int port, byte[] key, int timeout) { checkIsInMultiOrPipeline(); return connection.executeCommand(commandObjects.migrate(host, port, key, timeout)); } @Override public String migrate(String host, int port, int timeout, MigrateParams params, byte[]... keys) { checkIsInMultiOrPipeline(); return connection.executeCommand(commandObjects.migrate(host, port, timeout, params, keys)); } @Override public long waitReplicas(final int replicas, final long timeout) { checkIsInMultiOrPipeline(); connection.sendCommand(WAIT, toByteArray(replicas), toByteArray(timeout)); return connection.getIntegerReply(); } @Override public KeyValue waitAOF(long numLocal, long numReplicas, long timeout) { checkIsInMultiOrPipeline(); connection.sendCommand(WAITAOF, toByteArray(numLocal), toByteArray(numReplicas), toByteArray(timeout)); return BuilderFactory.LONG_LONG_PAIR.build(connection.getOne()); } @Override public long pfadd(final byte[] key, final byte[]... elements) { checkIsInMultiOrPipeline(); return connection.executeCommand(commandObjects.pfadd(key, elements)); } @Override public long pfcount(final byte[] key) { checkIsInMultiOrPipeline(); return connection.executeCommand(commandObjects.pfcount(key)); } @Override public String pfmerge(final byte[] destkey, final byte[]... sourcekeys) { checkIsInMultiOrPipeline(); return connection.executeCommand(commandObjects.pfmerge(destkey, sourcekeys)); } @Override public long pfcount(final byte[]... keys) { checkIsInMultiOrPipeline(); return connection.executeCommand(commandObjects.pfcount(keys)); } @Override public ScanResult scan(final byte[] cursor) { checkIsInMultiOrPipeline(); return connection.executeCommand(commandObjects.scan(cursor)); } @Override public ScanResult scan(final byte[] cursor, final ScanParams params) { checkIsInMultiOrPipeline(); return connection.executeCommand(commandObjects.scan(cursor, params)); } @Override public ScanResult scan(final byte[] cursor, final ScanParams params, final byte[] type) { checkIsInMultiOrPipeline(); return connection.executeCommand(commandObjects.scan(cursor, params, type)); } @Override public ScanResult> hscan(final byte[] key, final byte[] cursor, final ScanParams params) { checkIsInMultiOrPipeline(); return connection.executeCommand(commandObjects.hscan(key, cursor, params)); } @Override public ScanResult hscanNoValues(final byte[] key, final byte[] cursor, final ScanParams params) { checkIsInMultiOrPipeline(); return connection.executeCommand(commandObjects.hscanNoValues(key, cursor, params)); } @Override public ScanResult sscan(final byte[] key, final byte[] cursor) { return sscan(key, cursor, new ScanParams()); } @Override public ScanResult sscan(final byte[] key, final byte[] cursor, final ScanParams params) { checkIsInMultiOrPipeline(); return connection.executeCommand(commandObjects.sscan(key, cursor, params)); } @Override public ScanResult zscan(final byte[] key, final byte[] cursor) { return zscan(key, cursor, new ScanParams()); } @Override public ScanResult zscan(final byte[] key, final byte[] cursor, final ScanParams params) { checkIsInMultiOrPipeline(); return connection.executeCommand(commandObjects.zscan(key, cursor, params)); } @Override public long geoadd(final byte[] key, final double longitude, final double latitude, final byte[] member) { checkIsInMultiOrPipeline(); return connection.executeCommand(commandObjects.geoadd(key, longitude, latitude, member)); } @Override public long geoadd(final byte[] key, final Map memberCoordinateMap) { checkIsInMultiOrPipeline(); return connection.executeCommand(commandObjects.geoadd(key, memberCoordinateMap)); } @Override public long geoadd(final byte[] key, final GeoAddParams params, final Map memberCoordinateMap) { checkIsInMultiOrPipeline(); return connection.executeCommand(commandObjects.geoadd(key, params, memberCoordinateMap)); } @Override public Double geodist(final byte[] key, final byte[] member1, final byte[] member2) { checkIsInMultiOrPipeline(); return connection.executeCommand(commandObjects.geodist(key, member1, member2)); } @Override public Double geodist(final byte[] key, final byte[] member1, final byte[] member2, final GeoUnit unit) { checkIsInMultiOrPipeline(); return connection.executeCommand(commandObjects.geodist(key, member1, member2, unit)); } @Override public List geohash(final byte[] key, final byte[]... members) { checkIsInMultiOrPipeline(); return connection.executeCommand(commandObjects.geohash(key, members)); } @Override public List geopos(final byte[] key, final byte[]... members) { checkIsInMultiOrPipeline(); return connection.executeCommand(commandObjects.geopos(key, members)); } @Override public List georadius(final byte[] key, final double longitude, final double latitude, final double radius, final GeoUnit unit) { checkIsInMultiOrPipeline(); return connection.executeCommand(commandObjects.georadius(key, longitude, latitude, radius, unit)); } @Override public List georadiusReadonly(final byte[] key, final double longitude, final double latitude, final double radius, final GeoUnit unit) { checkIsInMultiOrPipeline(); return connection.executeCommand(commandObjects.georadiusReadonly(key, longitude, latitude, radius, unit)); } @Override public List georadius(final byte[] key, final double longitude, final double latitude, final double radius, final GeoUnit unit, final GeoRadiusParam param) { checkIsInMultiOrPipeline(); return connection.executeCommand(commandObjects.georadius(key, longitude, latitude, radius, unit, param)); } @Override public long georadiusStore(final byte[] key, final double longitude, final double latitude, final double radius, final GeoUnit unit, final GeoRadiusParam param, final GeoRadiusStoreParam storeParam) { checkIsInMultiOrPipeline(); return connection.executeCommand(commandObjects.georadiusStore(key, longitude, latitude, radius, unit, param, storeParam)); } @Override public List georadiusReadonly(final byte[] key, final double longitude, final double latitude, final double radius, final GeoUnit unit, final GeoRadiusParam param) { checkIsInMultiOrPipeline(); return connection.executeCommand(commandObjects.georadiusReadonly(key, longitude, latitude, radius, unit, param)); } @Override public List georadiusByMember(final byte[] key, final byte[] member, final double radius, final GeoUnit unit) { checkIsInMultiOrPipeline(); return connection.executeCommand(commandObjects.georadiusByMember(key, member, radius, unit)); } @Override public List georadiusByMemberReadonly(final byte[] key, final byte[] member, final double radius, final GeoUnit unit) { checkIsInMultiOrPipeline(); return connection.executeCommand(commandObjects.georadiusByMemberReadonly(key, member, radius, unit)); } @Override public List georadiusByMember(final byte[] key, final byte[] member, final double radius, final GeoUnit unit, final GeoRadiusParam param) { checkIsInMultiOrPipeline(); return connection.executeCommand(commandObjects.georadiusByMember(key, member, radius, unit, param)); } @Override public long georadiusByMemberStore(final byte[] key, final byte[] member, final double radius, final GeoUnit unit, final GeoRadiusParam param, final GeoRadiusStoreParam storeParam) { checkIsInMultiOrPipeline(); return connection.executeCommand(commandObjects.georadiusByMemberStore(key, member, radius, unit, param, storeParam)); } @Override public List geosearch(byte[] key, byte[] member, double radius, GeoUnit unit) { checkIsInMultiOrPipeline(); return connection.executeCommand(commandObjects.geosearch(key, member, radius, unit)); } @Override public List geosearch(byte[] key, GeoCoordinate coord, double radius, GeoUnit unit) { checkIsInMultiOrPipeline(); return connection.executeCommand(commandObjects.geosearch(key, coord, radius, unit)); } @Override public List geosearch(byte[] key, byte[] member, double width, double height, GeoUnit unit) { checkIsInMultiOrPipeline(); return connection.executeCommand(commandObjects.geosearch(key, member, width, height, unit)); } @Override public List geosearch(byte[] key, GeoCoordinate coord, double width, double height, GeoUnit unit) { checkIsInMultiOrPipeline(); return connection.executeCommand(commandObjects.geosearch(key, coord, width, height, unit)); } @Override public List geosearch(byte[] key, GeoSearchParam params) { checkIsInMultiOrPipeline(); return connection.executeCommand(commandObjects.geosearch(key, params)); } @Override public long geosearchStore(byte[] dest, byte[] src, byte[] member, double radius, GeoUnit unit) { checkIsInMultiOrPipeline(); return connection.executeCommand(commandObjects.geosearchStore(dest, src, member, radius, unit)); } @Override public long geosearchStore(byte[] dest, byte[] src, GeoCoordinate coord, double radius, GeoUnit unit) { checkIsInMultiOrPipeline(); return connection.executeCommand(commandObjects.geosearchStore(dest, src, coord, radius, unit)); } @Override public long geosearchStore(byte[] dest, byte[] src, byte[] member, double width, double height, GeoUnit unit) { checkIsInMultiOrPipeline(); return connection.executeCommand(commandObjects.geosearchStore(dest, src, member, width, height, unit)); } @Override public long geosearchStore(byte[] dest, byte[] src, GeoCoordinate coord, double width, double height, GeoUnit unit) { checkIsInMultiOrPipeline(); return connection.executeCommand(commandObjects.geosearchStore(dest, src, coord, width, height, unit)); } @Override public long geosearchStore(byte[] dest, byte[] src, GeoSearchParam params) { checkIsInMultiOrPipeline(); return connection.executeCommand(commandObjects.geosearchStore(dest, src, params)); } @Override public long geosearchStoreStoreDist(byte[] dest, byte[] src, GeoSearchParam params) { checkIsInMultiOrPipeline(); return connection.executeCommand(commandObjects.geosearchStoreStoreDist(dest, src, params)); } @Override public List georadiusByMemberReadonly(final byte[] key, final byte[] member, final double radius, final GeoUnit unit, final GeoRadiusParam param) { checkIsInMultiOrPipeline(); return connection.executeCommand(commandObjects.georadiusByMemberReadonly(key, member, radius, unit, param)); } @Override public List bitfield(final byte[] key, final byte[]... arguments) { checkIsInMultiOrPipeline(); return connection.executeCommand(commandObjects.bitfield(key, arguments)); } @Override public List bitfieldReadonly(byte[] key, final byte[]... arguments) { checkIsInMultiOrPipeline(); return connection.executeCommand(commandObjects.bitfieldReadonly(key, arguments)); } @Override public long hstrlen(final byte[] key, final byte[] field) { checkIsInMultiOrPipeline(); return connection.executeCommand(commandObjects.hstrlen(key, field)); } @Override public List hexpire(byte[] key, long seconds, byte[]... fields) { checkIsInMultiOrPipeline(); return connection.executeCommand(commandObjects.hexpire(key, seconds, fields)); } @Override public List hexpire(byte[] key, long seconds, ExpiryOption condition, byte[]... fields) { checkIsInMultiOrPipeline(); return connection.executeCommand(commandObjects.hexpire(key, seconds, condition, fields)); } @Override public List hpexpire(byte[] key, long milliseconds, byte[]... fields) { checkIsInMultiOrPipeline(); return connection.executeCommand(commandObjects.hpexpire(key, milliseconds, fields)); } @Override public List hpexpire(byte[] key, long milliseconds, ExpiryOption condition, byte[]... fields) { checkIsInMultiOrPipeline(); return connection.executeCommand(commandObjects.hpexpire(key, milliseconds, condition, fields)); } @Override public List hexpireAt(byte[] key, long unixTimeSeconds, byte[]... fields) { checkIsInMultiOrPipeline(); return connection.executeCommand(commandObjects.hexpireAt(key, unixTimeSeconds, fields)); } @Override public List hexpireAt(byte[] key, long unixTimeSeconds, ExpiryOption condition, byte[]... fields) { checkIsInMultiOrPipeline(); return connection.executeCommand(commandObjects.hexpireAt(key, unixTimeSeconds, condition, fields)); } @Override public List hpexpireAt(byte[] key, long unixTimeMillis, byte[]... fields) { checkIsInMultiOrPipeline(); return connection.executeCommand(commandObjects.hpexpireAt(key, unixTimeMillis, fields)); } @Override public List hpexpireAt(byte[] key, long unixTimeMillis, ExpiryOption condition, byte[]... fields) { checkIsInMultiOrPipeline(); return connection.executeCommand(commandObjects.hpexpireAt(key, unixTimeMillis, condition, fields)); } @Override public List hexpireTime(byte[] key, byte[]... fields) { checkIsInMultiOrPipeline(); return connection.executeCommand(commandObjects.hexpireTime(key, fields)); } @Override public List hpexpireTime(byte[] key, byte[]... fields) { checkIsInMultiOrPipeline(); return connection.executeCommand(commandObjects.hpexpireTime(key, fields)); } @Override public List httl(byte[] key, byte[]... fields) { checkIsInMultiOrPipeline(); return connection.executeCommand(commandObjects.httl(key, fields)); } @Override public List hpttl(byte[] key, byte[]... fields) { checkIsInMultiOrPipeline(); return connection.executeCommand(commandObjects.hpttl(key, fields)); } @Override public List hpersist(byte[] key, byte[]... fields) { checkIsInMultiOrPipeline(); return connection.executeCommand(commandObjects.hpersist(key, fields)); } /** * @deprecated As of Jedis 6.1.0, use * {@link #xreadBinary(XReadParams, Map)} or * {@link #xreadBinaryAsMap(XReadParams, Map)} for type safety and better stream entry * parsing. */ @Deprecated @Override public List xread(XReadParams xReadParams, Entry... streams) { checkIsInMultiOrPipeline(); return connection.executeCommand(commandObjects.xread(xReadParams, streams)); } /** * @deprecated As of Jedis 6.1.0, use * {@link #xreadGroupBinary(byte[], byte[], XReadGroupParams, Map)} or * {@link #xreadGroupBinaryAsMap(byte[], byte[], XReadGroupParams, Map)} instead. */ @Deprecated @Override public List xreadGroup(byte[] groupName, byte[] consumer, XReadGroupParams xReadGroupParams, Entry... streams) { checkIsInMultiOrPipeline(); return connection.executeCommand(commandObjects.xreadGroup(groupName, consumer, xReadGroupParams, streams)); } @Override public List>> xreadBinary(XReadParams xReadParams, Map streams) { checkIsInMultiOrPipeline(); return connection.executeCommand(commandObjects.xreadBinary(xReadParams, streams)); } @Override public Map> xreadBinaryAsMap(XReadParams xReadParams, Map streams) { checkIsInMultiOrPipeline(); return connection.executeCommand(commandObjects.xreadBinaryAsMap(xReadParams, streams)); } @Override public List>> xreadGroupBinary(byte[] groupName, byte[] consumer, XReadGroupParams xReadGroupParams, Map streams) { checkIsInMultiOrPipeline(); return connection.executeCommand(commandObjects.xreadGroupBinary(groupName, consumer, xReadGroupParams, streams)); } @Override public Map> xreadGroupBinaryAsMap(byte[] groupName, byte[] consumer, XReadGroupParams xReadGroupParams, Map streams) { checkIsInMultiOrPipeline(); return connection.executeCommand(commandObjects.xreadGroupBinaryAsMap(groupName, consumer, xReadGroupParams, streams)); } @Override public byte[] xadd(final byte[] key, final XAddParams params, final Map hash) { checkIsInMultiOrPipeline(); return connection.executeCommand(commandObjects.xadd(key, params, hash)); } @Override public long xlen(byte[] key) { checkIsInMultiOrPipeline(); return connection.executeCommand(commandObjects.xlen(key)); } @Override public List xrange(byte[] key, byte[] start, byte[] end) { checkIsInMultiOrPipeline(); return connection.executeCommand(commandObjects.xrange(key, start, end)); } @Override public List xrange(byte[] key, byte[] start, byte[] end, int count) { checkIsInMultiOrPipeline(); return connection.executeCommand(commandObjects.xrange(key, start, end, count)); } @Override public List xrevrange(byte[] key, byte[] end, byte[] start) { checkIsInMultiOrPipeline(); return connection.executeCommand(commandObjects.xrevrange(key, end, start)); } @Override public List xrevrange(byte[] key, byte[] end, byte[] start, int count) { checkIsInMultiOrPipeline(); return connection.executeCommand(commandObjects.xrevrange(key, end, start, count)); } @Override public long xack(byte[] key, byte[] group, byte[]... ids) { checkIsInMultiOrPipeline(); return connection.executeCommand(commandObjects.xack(key, group, ids)); } @Override public List xackdel(byte[] key, byte[] group, byte[]... ids) { checkIsInMultiOrPipeline(); return connection.executeCommand(commandObjects.xackdel(key, group, ids)); } @Override public List xackdel(byte[] key, byte[] group, StreamDeletionPolicy trimMode, byte[]... ids) { checkIsInMultiOrPipeline(); return connection.executeCommand(commandObjects.xackdel(key, group, trimMode, ids)); } @Override public String xgroupCreate(byte[] key, byte[] consumer, byte[] id, boolean makeStream) { checkIsInMultiOrPipeline(); return connection.executeCommand(commandObjects.xgroupCreate(key, consumer, id, makeStream)); } @Override public String xgroupSetID(byte[] key, byte[] consumer, byte[] id) { checkIsInMultiOrPipeline(); return connection.executeCommand(commandObjects.xgroupSetID(key, consumer, id)); } @Override public long xgroupDestroy(byte[] key, byte[] consumer) { checkIsInMultiOrPipeline(); return connection.executeCommand(commandObjects.xgroupDestroy(key, consumer)); } @Override public boolean xgroupCreateConsumer(byte[] key, byte[] groupName, byte[] consumerName) { checkIsInMultiOrPipeline(); return connection.executeCommand(commandObjects.xgroupCreateConsumer(key, groupName, consumerName)); } @Override public long xgroupDelConsumer(byte[] key, byte[] groupName, byte[] consumerName) { checkIsInMultiOrPipeline(); return connection.executeCommand(commandObjects.xgroupDelConsumer(key, groupName, consumerName)); } @Override public long xdel(byte[] key, byte[]... ids) { checkIsInMultiOrPipeline(); return connection.executeCommand(commandObjects.xdel(key, ids)); } @Override public List xdelex(byte[] key, byte[]... ids) { checkIsInMultiOrPipeline(); return connection.executeCommand(commandObjects.xdelex(key, ids)); } @Override public List xdelex(byte[] key, StreamDeletionPolicy trimMode, byte[]... ids) { checkIsInMultiOrPipeline(); return connection.executeCommand(commandObjects.xdelex(key, trimMode, ids)); } @Override public long xtrim(byte[] key, long maxLen, boolean approximateLength) { checkIsInMultiOrPipeline(); return connection.executeCommand(commandObjects.xtrim(key, maxLen, approximateLength)); } @Override public long xtrim(byte[] key, XTrimParams params) { checkIsInMultiOrPipeline(); return connection.executeCommand(commandObjects.xtrim(key, params)); } @Override public Object xpending(final byte[] key, final byte[] groupName) { checkIsInMultiOrPipeline(); return connection.executeCommand(commandObjects.xpending(key, groupName)); } @Override public List xpending(final byte[] key, final byte[] groupName, final XPendingParams params) { checkIsInMultiOrPipeline(); return connection.executeCommand(commandObjects.xpending(key, groupName, params)); } @Override public List xclaim(byte[] key, byte[] group, byte[] consumerName, long minIdleTime, XClaimParams params, byte[]... ids) { checkIsInMultiOrPipeline(); return connection.executeCommand(commandObjects.xclaim(key, group, consumerName, minIdleTime, params, ids)); } @Override public List xclaimJustId(byte[] key, byte[] group, byte[] consumerName, long minIdleTime, XClaimParams params, byte[]... ids) { checkIsInMultiOrPipeline(); return connection.executeCommand(commandObjects.xclaimJustId(key, group, consumerName, minIdleTime, params, ids)); } @Override public List xautoclaim(byte[] key, byte[] groupName, byte[] consumerName, long minIdleTime, byte[] start, XAutoClaimParams params) { checkIsInMultiOrPipeline(); return connection.executeCommand(commandObjects.xautoclaim(key, groupName, consumerName, minIdleTime, start, params)); } @Override public List xautoclaimJustId(byte[] key, byte[] groupName, byte[] consumerName, long minIdleTime, byte[] start, XAutoClaimParams params) { checkIsInMultiOrPipeline(); return connection.executeCommand(commandObjects.xautoclaimJustId(key, groupName, consumerName, minIdleTime, start, params)); } @Override public Object xinfoStream(byte[] key) { checkIsInMultiOrPipeline(); return connection.executeCommand(commandObjects.xinfoStream(key)); } @Override public Object xinfoStreamFull(byte[] key) { checkIsInMultiOrPipeline(); return connection.executeCommand(commandObjects.xinfoStreamFull(key)); } @Override public Object xinfoStreamFull(byte[] key, int count) { checkIsInMultiOrPipeline(); return connection.executeCommand(commandObjects.xinfoStreamFull(key, count)); } @Override public List xinfoGroups(byte[] key) { checkIsInMultiOrPipeline(); return connection.executeCommand(commandObjects.xinfoGroups(key)); } @Override public List xinfoConsumers(byte[] key, byte[] group) { checkIsInMultiOrPipeline(); return connection.executeCommand(commandObjects.xinfoConsumers(key, group)); } @Override public byte[] xcfgset(byte[] key, XCfgSetParams params) { checkIsInMultiOrPipeline(); return connection.executeCommand(commandObjects.xcfgset(key, params)); } public Object sendCommand(ProtocolCommand cmd, byte[]... args) { checkIsInMultiOrPipeline(); connection.sendCommand(cmd, args); return connection.getOne(); } public Object sendBlockingCommand(ProtocolCommand cmd, byte[]... args) { checkIsInMultiOrPipeline(); connection.sendCommand(cmd, args); connection.setTimeoutInfinite(); try { return connection.getOne(); } finally { connection.rollbackTimeout(); } } public Object sendCommand(ProtocolCommand cmd) { return sendCommand(cmd, DUMMY_ARRAY); } /** * COPY source destination [DB destination-db] [REPLACE] * * @param srcKey the source key. * @param dstKey the destination key. * @param db * @param replace */ @Override public boolean copy(String srcKey, String dstKey, int db, boolean replace) { checkIsInMultiOrPipeline(); return connection.executeCommand(commandObjects.copy(srcKey, dstKey, db, replace)); } /** * COPY source destination [REPLACE] * * @param srcKey the source key. * @param dstKey the destination key. * @param replace */ @Override public boolean copy(String srcKey, String dstKey, boolean replace) { checkIsInMultiOrPipeline(); return connection.executeCommand(commandObjects.copy(srcKey, dstKey, replace)); } /** * Works same as {@link #ping()} but returns argument message instead of PONG. * @param message * @return message */ @Override public String ping(final String message) { checkIsInMultiOrPipeline(); connection.sendCommand(Command.PING, message); return connection.getBulkReply(); } /** * Set the string value as value of the key. The string can't be longer than 1073741824 bytes (1 * GB). *

* Time complexity: O(1) * @param key * @param value * @return OK */ @Override public String set(final String key, final String value) { checkIsInMultiOrPipeline(); return connection.executeCommand(commandObjects.set(key, value)); } /** * Set the string value as value of the key. The string can't be longer than 1073741824 bytes (1 * GB). * @param key * @param value * @param params NX|XX, NX -- Only set the key if it does not already exist. XX -- Only set the * key if it already exists. EX|PX, expire time units: EX = seconds; PX = milliseconds * @return simple-string-reply {@code OK} if {@code SET} was executed correctly, or {@code null} * if the {@code SET} operation was not performed because the user specified the NX or XX option * but the condition was not met. */ @Override public String set(final String key, final String value, final SetParams params) { checkIsInMultiOrPipeline(); return connection.executeCommand(commandObjects.set(key, value, params)); } /** * Get the value of the specified key. If the key does not exist the special value 'nil' is * returned. If the value stored at key is not a string an error is returned because GET can only * handle string values. *

* Time complexity: O(1) * @param key * @return Bulk reply */ @Override public String get(final String key) { checkIsInMultiOrPipeline(); return connection.executeCommand(commandObjects.get(key)); } @Override public String setGet(final String key, final String value) { checkIsInMultiOrPipeline(); return connection.executeCommand(commandObjects.setGet(key, value)); } @Override public String setGet(final String key, final String value, final SetParams params) { checkIsInMultiOrPipeline(); return connection.executeCommand(commandObjects.setGet(key, value, params)); } /** * Get the value of key and delete the key. This command is similar to GET, except for the fact * that it also deletes the key on success (if and only if the key's value type is a string). *

* Time complexity: O(1) * @param key * @return The value of key */ @Override public String getDel(final String key) { checkIsInMultiOrPipeline(); return connection.executeCommand(commandObjects.getDel(key)); } @Override public String getEx(String key, GetExParams params) { checkIsInMultiOrPipeline(); return connection.executeCommand(commandObjects.getEx(key, params)); } @Override public String digestKey(final String key) { checkIsInMultiOrPipeline(); return connection.executeCommand(commandObjects.digestKey(key)); } @Override public long delex(final String key, final CompareCondition condition) { checkIsInMultiOrPipeline(); return connection.executeCommand(commandObjects.delex(key, condition)); } /** * Test if the specified keys exist. The command returns the number of keys exist. * Time complexity: O(N) * @param keys * @return The number of keys that exist from those specified as {@code keys} */ @Override public long exists(final String... keys) { checkIsInMultiOrPipeline(); return connection.executeCommand(commandObjects.exists(keys)); } /** * Test if the specified key exists. The command returns true if the key exists, otherwise false is * returned. Note that even keys set with an empty string as value will return true. Time * complexity: O(1) * @param key * @return {@code true} if the key exists, otherwise {@code false} */ @Override public boolean exists(final String key) { checkIsInMultiOrPipeline(); return connection.executeCommand(commandObjects.exists(key)); } /** * Remove the specified keys. If a given key does not exist no operation is performed for this * key. The command returns the number of keys removed. Time complexity: O(1) * @param keys * @return An integer greater than 0 if one or more keys were removed, 0 if none of the specified keys existed */ @Override public long del(final String... keys) { checkIsInMultiOrPipeline(); return connection.executeCommand(commandObjects.del(keys)); } @Override public long del(final String key) { checkIsInMultiOrPipeline(); return connection.executeCommand(commandObjects.del(key)); } /** * This command is very similar to DEL: it removes the specified keys. Just like DEL a key is * ignored if it does not exist. However, the command performs the actual memory reclaiming in a * different thread, so it is not blocking, while DEL is. This is where the command name comes * from: the command just unlinks the keys from the keyspace. The actual removal will happen later * asynchronously. *

* Time complexity: O(1) for each key removed regardless of its size. Then the command does O(N) * work in a different thread in order to reclaim memory, where N is the number of allocations the * deleted objects where composed of. * @param keys * @return The number of keys that were unlinked */ @Override public long unlink(final String... keys) { checkIsInMultiOrPipeline(); return connection.executeCommand(commandObjects.unlink(keys)); } @Override public long unlink(final String key) { checkIsInMultiOrPipeline(); return connection.executeCommand(commandObjects.unlink(key)); } /** * Return the type of the value stored at key in form of a string. The type can be one of "none", * "string", "list", "set". "none" is returned if the key does not exist. Time complexity: O(1) * @param key * @return "none" if the key does not exist, "string" if the key contains a String value, "list" * if the key contains a List value, "set" if the key contains a Set value, "zset" if the key * contains a Sorted Set value, "hash" if the key contains a Hash value */ @Override public String type(final String key) { checkIsInMultiOrPipeline(); return connection.executeCommand(commandObjects.type(key)); } @Override public Set keys(final String pattern) { checkIsInMultiOrPipeline(); return connection.executeCommand(commandObjects.keys(pattern)); } /** * Return a randomly selected key from the currently selected DB. *

* Time complexity: O(1) * @return Randomly selected key or an empty string if the database is empty */ @Override public String randomKey() { checkIsInMultiOrPipeline(); return connection.executeCommand(commandObjects.randomKey()); } /** * Atomically renames the key oldkey to newkey. If the source and destination name are the same an * error is returned. If newkey already exists it is overwritten. *

* Time complexity: O(1) * @param oldkey * @param newkey * @return OK */ @Override public String rename(final String oldkey, final String newkey) { checkIsInMultiOrPipeline(); return connection.executeCommand(commandObjects.rename(oldkey, newkey)); } /** * Rename oldkey into newkey but fails if the destination key newkey already exists. *

* Time complexity: O(1) * @param oldkey * @param newkey * @return 1 if the key was renamed, 0 if the target key already exist */ @Override public long renamenx(final String oldkey, final String newkey) { checkIsInMultiOrPipeline(); return connection.executeCommand(commandObjects.renamenx(oldkey, newkey)); } /** * Set a timeout on the specified key. After the timeout the key will be automatically deleted by * the server. A key with an associated timeout is said to be volatile in Redis terminology. *

* Volatile keys are stored on disk like the other keys, the timeout is persistent too like all * the other aspects of the dataset. Saving a dataset containing expires and stopping the server * does not stop the flow of time as Redis stores on disk the time when the key will no longer be * available as Unix time, and not the remaining seconds. *

* Since Redis 2.1.3 you can update the value of the timeout of a key already having an expire * set. It is also possible to undo the expire at all turning the key into a normal key using the * {@link Jedis#persist(String) PERSIST} command. *

* Time complexity: O(1) * @see Expire Command * @param key * @param seconds * @return 1: the timeout was set. 0: the timeout was not set since * the key already has an associated timeout (this may happen only in Redis versions < * 2.1.3, Redis >= 2.1.3 will happily update the timeout), or the key does not exist. */ @Override public long expire(final String key, final long seconds) { checkIsInMultiOrPipeline(); return connection.executeCommand(commandObjects.expire(key, seconds)); } /** * Similar to {@link Jedis#expire(String, long) EXPIRE} but with optional expiry setting. * @see Jedis#expire(String, long) * @param key * @param seconds time to expire * @param expiryOption can be NX, XX, GT or LT * @return 1 if the timeout was set, 0 otherwise. Since the key already has an associated timeout * (this may happen only in Redis versions < 2.1.3, Redis >= 2.1.3 will happily update the timeout), * or the key does not exist. */ @Override public long expire(final String key, final long seconds, final ExpiryOption expiryOption) { checkIsInMultiOrPipeline(); return connection.executeCommand(commandObjects.expire(key, seconds, expiryOption)); } @Override public long pexpire(final String key, final long milliseconds) { checkIsInMultiOrPipeline(); return connection.executeCommand(commandObjects.pexpire(key, milliseconds)); } @Override public long pexpire(final String key, final long milliseconds, final ExpiryOption expiryOption) { checkIsInMultiOrPipeline(); return connection.executeCommand(commandObjects.pexpire(key, milliseconds, expiryOption)); } /** * Returns the absolute Unix timestamp (since January 1, 1970) in seconds at which the given key will expire. *

* The command returns -1 if the key exists but has no associated expiration time, and -2 if the key does not exist. *

* Time complexity: O(1) * @param key * @return Expiration Unix timestamp in seconds, or a negative value in order to signal an error: * -1 if the key exists but has no associated expiration time, and -2 if the key does not exist. */ @Override public long expireTime(final String key) { checkIsInMultiOrPipeline(); return connection.executeCommand(commandObjects.expireTime(key)); } /** * Similar to {@link Jedis#expireTime(String) EXPIRETIME} but returns the absolute Unix expiration * timestamp in milliseconds instead of seconds. *

* Time complexity: O(1) * @see Jedis#expireTime(String) * @param key * @return Expiration Unix timestamp in milliseconds, or a negative value in order to signal an error: * -1 if the key exists but has no associated expiration time, and -2 if the key does not exist. */ @Override public long pexpireTime(final String key) { checkIsInMultiOrPipeline(); return connection.executeCommand(commandObjects.pexpireTime(key)); } /** * EXPIREAT works exactly like {@link Jedis#expire(String, long) EXPIRE} but instead to get the * number of seconds representing the Time To Live of the key as a second argument (that is a * relative way of specifying the TTL), it takes an absolute one in the form of a UNIX timestamp * (Number of seconds elapsed since 1 Gen 1970). *

* EXPIREAT was introduced in order to implement the Append Only File persistence mode so that * EXPIRE commands are automatically translated into EXPIREAT commands for the append only file. * Of course EXPIREAT can also used by programmers that need a way to simply specify that a given * key should expire at a given time in the future. *

* Since Redis 2.1.3 you can update the value of the timeout of a key already having an expire * set. It is also possible to undo the expire at all turning the key into a normal key using the * {@link Jedis#persist(String) PERSIST} command. *

* Time complexity: O(1) * @see Expire Command * @param key * @param unixTime * @return 1: the timeout was set. 0: the timeout was not set since * the key already has an associated timeout (this may happen only in Redis versions < * 2.1.3, Redis >= 2.1.3 will happily update the timeout), or the key does not exist. */ @Override public long expireAt(final String key, final long unixTime) { checkIsInMultiOrPipeline(); return connection.executeCommand(commandObjects.expireAt(key, unixTime)); } /** * Similar to {@link Jedis#expireAt(String, long) EXPIREAT} but with {@code ExpiryOption}. * @see Jedis#expireAt(String, long) * @param key * @param unixTime time to expire * @param expiryOption can be NX, XX, GT or LT * @return 1 if the timeout was set, 0 otherwise. * e.g. key doesn't exist, or operation skipped due to the provided arguments. */ @Override public long expireAt(String key, long unixTime, ExpiryOption expiryOption) { checkIsInMultiOrPipeline(); return connection.executeCommand(commandObjects.expireAt(key, unixTime, expiryOption)); } /** * This command works exactly like {@link Jedis#expireAt(String, long) EXPIREAT} but * Unix time at which the key will expire is specified in milliseconds instead of seconds. *

* Time complexity: O(1) * @param key * @param millisecondsTimestamp time to expire * @return 1 if the timeout was set, 0 otherwise. * e.g. key doesn't exist, or operation skipped due to the provided arguments. */ @Override public long pexpireAt(final String key, final long millisecondsTimestamp) { checkIsInMultiOrPipeline(); return connection.executeCommand(commandObjects.pexpireAt(key, millisecondsTimestamp)); } /** * ExpireAt Command * Similar to {@link Jedis#pexpireAt(String, long) PEXPIREAT} but with {@code ExpiryOption}. * @see Jedis#pexpireAt(String, long) * @param key * @param millisecondsTimestamp time to expire * @param expiryOption can be NX, XX, GT or LT * @return 1 if the timeout was set, 0 otherwise. * e.g. key doesn't exist, or operation skipped due to the provided arguments. */ @Override public long pexpireAt(String key, long millisecondsTimestamp, ExpiryOption expiryOption) { checkIsInMultiOrPipeline(); return connection.executeCommand(commandObjects.pexpireAt(key, millisecondsTimestamp, expiryOption)); } /** * The TTL command returns the remaining time to live in seconds of a key that has an * {@link Jedis#expire(String, long) EXPIRE} set. This introspection capability allows a Redis * connection to check how many seconds a given key will continue to be part of the dataset. * @param key * @return TTL in seconds, or a negative value in order to signal an error */ @Override public long ttl(final String key) { checkIsInMultiOrPipeline(); return connection.executeCommand(commandObjects.ttl(key)); } /** * Alters the last access time of a key(s). A key is ignored if it does not exist. * Time complexity: O(N) where N is the number of keys that will be touched. * @param keys * @return The number of keys that were touched. */ @Override public long touch(final String... keys) { checkIsInMultiOrPipeline(); return connection.executeCommand(commandObjects.touch(keys)); } @Override public long touch(final String key) { checkIsInMultiOrPipeline(); return connection.executeCommand(commandObjects.touch(key)); } /** * Move the specified key from the currently selected DB to the specified destination DB. Note * that this command returns 1 only if the key was successfully moved, and 0 if the target key was * already there or if the source key was not found at all, so it is possible to use MOVE as a * locking primitive. * @param key * @param dbIndex * @return 1 if the key was moved, 0 if the key was not moved because already present on the target * DB or was not found in the current DB */ @Override public long move(final String key, final int dbIndex) { checkIsInMultiOrPipeline(); connection.sendCommand(MOVE, encode(key), toByteArray(dbIndex)); return connection.getIntegerReply(); } /** * GETSET is an atomic set this value and return the old value command. Set key to the string * value and return the old value stored at key. The string can't be longer than 1073741824 bytes * (1 GB). *

* Time complexity: O(1) * @param key * @param value * @return Bulk reply * @deprecated Use {@link Jedis#setGet(java.lang.String, java.lang.String)}. */ @Deprecated @Override public String getSet(final String key, final String value) { checkIsInMultiOrPipeline(); return connection.executeCommand(commandObjects.getSet(key, value)); } /** * Get the values of all the specified keys. If one or more keys don't exist or is not of type * String, a 'nil' value is returned instead of the value of the specified key, but the operation * never fails. *

* Time complexity: O(1) for every key * @param keys * @return Multi bulk reply */ @Override public List mget(final String... keys) { checkIsInMultiOrPipeline(); return connection.executeCommand(commandObjects.mget(keys)); } /** * SETNX works exactly like {@link Jedis#set(String, String) SET} with the only difference that if * the key already exists no operation is performed. SETNX actually means "SET if Not eXists". *

* Time complexity: O(1) * @param key * @param value * @return 1 if the key was set, 0 if the key was not set * @deprecated Use {@link Jedis#set(String, String, redis.clients.jedis.params.SetParams)} with {@link redis.clients.jedis.params.SetParams#nx()}. * Deprecated in Jedis 7.3.0. Mirrors Redis deprecation since 2.6.12. */ @Deprecated @Override public long setnx(final String key, final String value) { checkIsInMultiOrPipeline(); return connection.executeCommand(commandObjects.setnx(key, value)); } /** * The command is exactly equivalent to the following group of commands: * {@link Jedis#set(String, String) SET} + {@link Jedis#expire(String, long) EXPIRE}. The * operation is atomic. *

* Time complexity: O(1) * @param key * @param seconds * @param value * @return OK * @deprecated Use {@link Jedis#set(String, String, redis.clients.jedis.params.SetParams)} with {@link redis.clients.jedis.params.SetParams#ex(long)}. * Deprecated in Jedis 7.3.0. Mirrors Redis deprecation since 2.6.12. */ @Deprecated @Override public String setex(final String key, final long seconds, final String value) { checkIsInMultiOrPipeline(); return connection.executeCommand(commandObjects.setex(key, seconds, value)); } /** * Set the respective keys to the respective values. MSET will replace old values with new * values, while {@link Jedis#msetnx(String...) MSETNX} will not perform any operation at all even * if just a single key already exists. *

* Because of this semantic MSETNX can be used in order to set different keys representing * different fields of an unique logic object in a way that ensures that either all the fields or * none at all are set. *

* Both MSET and MSETNX are atomic operations. This means that for instance if the keys A and B * are modified, another connection talking to Redis can either see the changes to both A and B at * once, or no modification at all. * @see Jedis#msetnx(String...) * @param keysvalues * @return OK */ @Override public String mset(final String... keysvalues) { checkIsInMultiOrPipeline(); return connection.executeCommand(commandObjects.mset(keysvalues)); } /** * Set the respective keys to the respective values. {@link Jedis#mset(String...) MSET} will * replace old values with new values, while MSETNX will not perform any operation at all even if * just a single key already exists. *

* Because of this semantic MSETNX can be used in order to set different keys representing * different fields of an unique logic object in a way that ensures that either all the fields or * none at all are set. *

* Both MSET and MSETNX are atomic operations. This means that for instance if the keys A and B * are modified, another connection talking to Redis can either see the changes to both A and B at * once, or no modification at all. * @see Jedis#mset(String...) * @param keysvalues * @return 1 if the all the keys were set, 0 if no key was set (at least one key already existed) */ @Override public long msetnx(final String... keysvalues) { checkIsInMultiOrPipeline(); return connection.executeCommand(commandObjects.msetnx(keysvalues)); } @Override public boolean msetex(final MSetExParams params, final String... keysvalues) { checkIsInMultiOrPipeline(); return connection.executeCommand(commandObjects.msetex(params, keysvalues)); } /** * IDECRBY work just like {@link Jedis#decr(String) INCR} but instead to decrement by 1 the * decrement is integer. *

* INCR commands are limited to 64-bit signed integers. *

* Note: this is actually a string operation, that is, in Redis there are not "integer" types. * Simply the string stored at the key is parsed as a base 10 64-bit signed integer, incremented, * and then converted back as a string. *

* Time complexity: O(1) * @see Jedis#incr(String) * @see Jedis#decr(String) * @see Jedis#incrBy(String, long) * @param key * @param decrement * @return The value of key after the decrement */ @Override public long decrBy(final String key, final long decrement) { checkIsInMultiOrPipeline(); return connection.executeCommand(commandObjects.decrBy(key, decrement)); } /** * Decrement the number stored at key by one. If the key does not exist or contains a value of a * wrong type, set the key to the value of "0" before to perform the decrement operation. *

* INCR commands are limited to 64-bit signed integers. *

* Note: this is actually a string operation, that is, in Redis there are not "integer" types. * Simply the string stored at the key is parsed as a base 10 64-bit signed integer, incremented, * and then converted back as a string. *

* Time complexity: O(1) * @see Jedis#incr(String) * @see Jedis#incrBy(String, long) * @see Jedis#decrBy(String, long) * @param key * @return The value of key after the decrement */ @Override public long decr(final String key) { checkIsInMultiOrPipeline(); return connection.executeCommand(commandObjects.decr(key)); } /** * INCRBY work just like {@link Jedis#incr(String) INCR} but instead to increment by 1 the * increment is integer. *

* INCR commands are limited to 64-bit signed integers. *

* Note: this is actually a string operation, that is, in Redis there are not "integer" types. * Simply the string stored at the key is parsed as a base 10 64-bit signed integer, incremented, * and then converted back as a string. *

* Time complexity: O(1) * @see Jedis#incr(String) * @see Jedis#decr(String) * @see Jedis#decrBy(String, long) * @param key * @param increment * @return The value of key after the increment */ @Override public long incrBy(final String key, final long increment) { checkIsInMultiOrPipeline(); return connection.executeCommand(commandObjects.incrBy(key, increment)); } /** * INCRBYFLOAT *

* INCRBYFLOAT commands are limited to double precision floating point values. *

* Note: this is actually a string operation, that is, in Redis there are not "double" types. * Simply the string stored at the key is parsed as a base double precision floating point value, * incremented, and then converted back as a string. There is no DECRYBYFLOAT but providing a * negative value will work as expected. *

* Time complexity: O(1) * @param key * @param increment * @return The value of key after the increment */ @Override public double incrByFloat(final String key, final double increment) { checkIsInMultiOrPipeline(); return connection.executeCommand(commandObjects.incrByFloat(key, increment)); } /** * Increment the number stored at key by one. If the key does not exist or contains a value of a * wrong type, set the key to the value of "0" before to perform the increment operation. *

* INCR commands are limited to 64-bit signed integers. *

* Note: this is actually a string operation, that is, in Redis there are not "integer" types. * Simply the string stored at the key is parsed as a base 10 64-bit signed integer, incremented, * and then converted back as a string. *

* Time complexity: O(1) * @see Jedis#incrBy(String, long) * @see Jedis#decr(String) * @see Jedis#decrBy(String, long) * @param key * @return The value of key after the increment */ @Override public long incr(final String key) { checkIsInMultiOrPipeline(); return connection.executeCommand(commandObjects.incr(key)); } /** * If the key already exists and is a string, this command appends the provided value at the end * of the string. If the key does not exist it is created and set as an empty string, so APPEND * will be very similar to SET in this special case. *

* Time complexity: O(1). The amortized time complexity is O(1) assuming the appended value is * small and the already present value is of any size, since the dynamic string library used by * Redis will double the free space available on every reallocation. * @param key * @param value * @return The total length of the string after the append operation. */ @Override public long append(final String key, final String value) { checkIsInMultiOrPipeline(); return connection.executeCommand(commandObjects.append(key, value)); } /** * Return a subset of the string from offset start to offset end (both offsets are inclusive). * Negative offsets can be used in order to provide an offset starting from the end of the string. * So -1 means the last char, -2 the penultimate and so forth. *

* The function handles out of range requests without raising an error, but just limiting the * resulting range to the actual length of the string. *

* Time complexity: O(start+n) (with start being the start index and n the total length of the * requested range). Note that the lookup part of this command is O(1) so for small strings this * is actually an O(1) command. * @param key * @param start * @param end * @return The substring * @deprecated Use {@link Jedis#getrange(String, long, long)} instead. * Deprecated in Jedis 7.3.0. Mirrors Redis deprecation since 2.0.0. */ @Deprecated @Override public String substr(final String key, final int start, final int end) { checkIsInMultiOrPipeline(); return connection.executeCommand(commandObjects.substr(key, start, end)); } /** * Set the specified hash field to the specified value. *

* If key does not exist, a new key holding a hash is created. *

* Time complexity: O(1) * @param key * @param field * @param value * @return If the field already exists, and the HSET just produced an update of the value, 0 is * returned, otherwise if a new field is created 1 is returned. */ @Override public long hset(final String key, final String field, final String value) { checkIsInMultiOrPipeline(); return connection.executeCommand(commandObjects.hset(key, field, value)); } @Override public long hset(final String key, final Map hash) { checkIsInMultiOrPipeline(); return connection.executeCommand(commandObjects.hset(key, hash)); } @Override public long hsetex(String key, HSetExParams params, String field, String value) { checkIsInMultiOrPipeline(); return connection.executeCommand(commandObjects.hsetex(key, params, field, value)); } @Override public long hsetex(String key, HSetExParams params, Map hash) { checkIsInMultiOrPipeline(); return connection.executeCommand(commandObjects.hsetex(key, params, hash)); } /** * If key holds a hash, retrieve the value associated to the specified field. *

* If the field is not found or the key does not exist, a special 'nil' value is returned. *

* Time complexity: O(1) * @param key * @param field * @return Bulk reply */ @Override public String hget(final String key, final String field) { checkIsInMultiOrPipeline(); return connection.executeCommand(commandObjects.hget(key, field)); } @Override public List hgetex(String key, HGetExParams params, String... fields) { checkIsInMultiOrPipeline(); return connection.executeCommand(commandObjects.hgetex(key, params, fields)); } @Override public List hgetdel(String key, String... fields) { checkIsInMultiOrPipeline(); return connection.executeCommand(commandObjects.hgetdel(key, fields)); } /** * Set the specified hash field to the specified value if the field not exists. Time * complexity: O(1) * @param key * @param field * @param value * @return If the field already exists, 0 is returned, otherwise if a new field is created 1 is * returned. */ @Override public long hsetnx(final String key, final String field, final String value) { checkIsInMultiOrPipeline(); return connection.executeCommand(commandObjects.hsetnx(key, field, value)); } /** * Set the respective fields to the respective values. HMSET replaces old values with new values. *

* If key does not exist, a new key holding a hash is created. *

* Time complexity: O(N) (with N being the number of fields) * @param key * @param hash * @return Return OK or Exception if hash is empty * @deprecated Use {@link Jedis#hset(String, Map)} instead. * Deprecated in Jedis 7.3.0. Mirrors Redis deprecation since 4.0.0. */ @Deprecated @Override public String hmset(final String key, final Map hash) { checkIsInMultiOrPipeline(); return connection.executeCommand(commandObjects.hmset(key, hash)); } /** * Retrieve the values associated to the specified fields. *

* If some of the specified fields do not exist, nil values are returned. Non existing keys are * considered like empty hashes. *

* Time complexity: O(N) (with N being the number of fields) * @param key * @param fields * @return A list of all the values associated with the specified fields, in the same order of the request. */ @Override public List hmget(final String key, final String... fields) { checkIsInMultiOrPipeline(); return connection.executeCommand(commandObjects.hmget(key, fields)); } /** * Increment the number stored at field in the hash at key by value. If key does not exist, a new * key holding a hash is created. If field does not exist or holds a string, the value is set to 0 * before applying the operation. Since the value argument is signed you can use this command to * perform both increments and decrements. *

* The range of values supported by HINCRBY is limited to 64-bit signed integers. *

* Time complexity: O(1) * @param key * @param field * @param value * @return The value of key after the increment */ @Override public long hincrBy(final String key, final String field, final long value) { checkIsInMultiOrPipeline(); return connection.executeCommand(commandObjects.hincrBy(key, field, value)); } /** * Increment the number stored at field in the hash at key by a double precision floating point * value. If key does not exist, a new key holding a hash is created. If field does not exist or * holds a string, the value is set to 0 before applying the operation. Since the value argument * is signed you can use this command to perform both increments and decrements. *

* The range of values supported by HINCRBYFLOAT is limited to double precision floating point * values. *

* Time complexity: O(1) * @param key * @param field * @param value * @return The new value at field after the increment operation */ @Override public double hincrByFloat(final String key, final String field, final double value) { checkIsInMultiOrPipeline(); return connection.executeCommand(commandObjects.hincrByFloat(key, field, value)); } /** * Test for existence of a specified field in a hash. Time complexity: O(1) * @param key * @param field * @return {@code true} if the hash stored at key contains the specified field, {@code false} if the key is * not found or the field is not present. */ @Override public boolean hexists(final String key, final String field) { checkIsInMultiOrPipeline(); return connection.executeCommand(commandObjects.hexists(key, field)); } /** * Remove the specified field(s) from a hash stored at key. Specified fields that do not exist * within this hash are ignored. *

* Time complexity: O(1) * @param key * @param fields * @return The number of fields that were removed from the hash, not including specified but * non-existing fields. If key does not exist, it is treated as an empty hash and this * command returns 0. */ @Override public long hdel(final String key, final String... fields) { checkIsInMultiOrPipeline(); return connection.executeCommand(commandObjects.hdel(key, fields)); } /** * Return the number of items in a hash. *

* Time complexity: O(1) * @param key * @return The number of entries (fields) contained in the hash stored at key. If the specified * key does not exist, 0 is returned assuming an empty hash. */ @Override public long hlen(final String key) { checkIsInMultiOrPipeline(); return connection.executeCommand(commandObjects.hlen(key)); } /** * Return all the fields in a hash. *

* Time complexity: O(N), where N is the total number of entries * @param key * @return All the fields names contained into a hash. */ @Override public Set hkeys(final String key) { checkIsInMultiOrPipeline(); return connection.executeCommand(commandObjects.hkeys(key)); } /** * Return all the values in a hash. *

* Time complexity: O(N), where N is the total number of entries * @param key * @return All the fields values contained into a hash. */ @Override public List hvals(final String key) { checkIsInMultiOrPipeline(); return connection.executeCommand(commandObjects.hvals(key)); } /** * Return all the fields and associated values in a hash. *

* Time complexity: O(N), where N is the total number of entries * @param key * @return All the fields and values contained into a hash. */ @Override public Map hgetAll(final String key) { checkIsInMultiOrPipeline(); return connection.executeCommand(commandObjects.hgetAll(key)); } /** * Get one random field from a hash. *

* Time complexity: O(N), where N is the number of fields returned * @param key * @return one random field from a hash. */ @Override public String hrandfield(final String key) { checkIsInMultiOrPipeline(); return connection.executeCommand(commandObjects.hrandfield(key)); } /** * Get multiple random fields from a hash. *

* Time complexity: O(N), where N is the number of fields returned * @param key * @param count * @return multiple random fields from a hash. */ @Override public List hrandfield(final String key, final long count) { checkIsInMultiOrPipeline(); return connection.executeCommand(commandObjects.hrandfield(key, count)); } /** * Get one or multiple random fields with values from a hash. *

* Time complexity: O(N), where N is the number of fields returned * @param key * @param count * @return one or multiple random fields with values from a hash. */ @Override public List> hrandfieldWithValues(final String key, final long count) { checkIsInMultiOrPipeline(); return connection.executeCommand(commandObjects.hrandfieldWithValues(key, count)); } /** * Add the string value to the head (LPUSH) or tail (RPUSH) of the list stored at key. If the key * does not exist an empty list is created just before the append operation. If the key exists but * is not a List an error is returned. *

* Time complexity: O(1) * @param key * @param strings * @return The number of elements inside the list after the push operation */ @Override public long rpush(final String key, final String... strings) { checkIsInMultiOrPipeline(); return connection.executeCommand(commandObjects.rpush(key, strings)); } /** * Add the string value to the head (LPUSH) or tail (RPUSH) of the list stored at key. If the key * does not exist an empty list is created just before the append operation. If the key exists but * is not a List an error is returned. *

* Time complexity: O(1) * @param key * @param strings * @return The number of elements inside the list after the push operation */ @Override public long lpush(final String key, final String... strings) { checkIsInMultiOrPipeline(); return connection.executeCommand(commandObjects.lpush(key, strings)); } /** * Return the length of the list stored at the specified key. If the key does not exist zero is * returned (the same behaviour as for empty lists). If the value stored at key is not a list an * error is returned. *

* Time complexity: O(1) * @param key * @return The length of the list */ @Override public long llen(final String key) { checkIsInMultiOrPipeline(); return connection.executeCommand(commandObjects.llen(key)); } /** * Return the specified elements of the list stored at the specified key. Start and end are * zero-based indexes. 0 is the first element of the list (the list head), 1 the next element and * so on. *

* For example LRANGE foobar 0 2 will return the first three elements of the list. *

* start and end can also be negative numbers indicating offsets from the end of the list. For * example -1 is the last element of the list, -2 the penultimate element and so on. *

* Consistency with range functions in various programming languages *

* Note that if you have a list of numbers from 0 to 100, LRANGE 0 10 will return 11 elements, * that is, rightmost item is included. This may or may not be consistent with behavior of * range-related functions in your programming language of choice (think Ruby's Range.new, * Array#slice or Python's range() function). *

* LRANGE behavior is consistent with one of Tcl. *

* Out-of-range indexes *

* Indexes out of range will not produce an error: if start is over the end of the list, or start * > end, an empty list is returned. If end is over the end of the list Redis will threat it * just like the last element of the list. *

* Time complexity: O(start+n) (with n being the length of the range and start being the start * offset) * @param key * @param start * @param stop * @return A list of elements in the specified range */ @Override public List lrange(final String key, final long start, final long stop) { checkIsInMultiOrPipeline(); return connection.executeCommand(commandObjects.lrange(key, start, stop)); } /** * Trim an existing list so that it will contain only the specified range of elements specified. * Start and end are zero-based indexes. 0 is the first element of the list (the list head), 1 the * next element and so on. *

* For example LTRIM foobar 0 2 will modify the list stored at foobar key so that only the first * three elements of the list will remain. *

* start and end can also be negative numbers indicating offsets from the end of the list. For * example -1 is the last element of the list, -2 the penultimate element and so on. *

* Indexes out of range will not produce an error: if start is over the end of the list, or start * > end, an empty list is left as value. If end over the end of the list Redis will threat it * just like the last element of the list. *

* Hint: the obvious use of LTRIM is together with LPUSH/RPUSH. For example: *

* {@code lpush("mylist", "someelement"); ltrim("mylist", 0, 99); * } *

* The above two commands will push elements in the list taking care that the list will not grow * without limits. This is very useful when using Redis to store logs for example. It is important * to note that when used in this way LTRIM is an O(1) operation because in the average case just * one element is removed from the tail of the list. *

* Time complexity: O(n) (with n being len of list - len of range) * @param key * @param start * @param stop * @return OK */ @Override public String ltrim(final String key, final long start, final long stop) { checkIsInMultiOrPipeline(); return connection.executeCommand(commandObjects.ltrim(key, start, stop)); } /** * Return the specified element of the list stored at the specified key. 0 is the first element, 1 * the second and so on. Negative indexes are supported, for example -1 is the last element, -2 * the penultimate and so on. *

* If the value stored at key is not of list type an error is returned. If the index is out of * range a 'nil' reply is returned. *

* Note that even if the average time complexity is O(n) asking for the first or the last element * of the list is O(1). *

* Time complexity: O(n) (with n being the length of the list) * @param key * @param index * @return The requested element */ @Override public String lindex(final String key, final long index) { checkIsInMultiOrPipeline(); return connection.executeCommand(commandObjects.lindex(key, index)); } /** * Set a new value as the element at index position of the List at key. *

* Out of range indexes will generate an error. *

* Similarly to other list commands accepting indexes, the index can be negative to access * elements starting from the end of the list. So -1 is the last element, -2 is the penultimate, * and so forth. *

* Time complexity: *

* O(N) (with N being the length of the list), setting the first or last elements of the list is * O(1). * @see Jedis#lindex(String, long) * @param key * @param index * @param value * @return OK */ @Override public String lset(final String key, final long index, final String value) { checkIsInMultiOrPipeline(); return connection.executeCommand(commandObjects.lset(key, index, value)); } /** * Remove the first count occurrences of the value element from the list. If count is zero all the * elements are removed. If count is negative elements are removed from tail to head, instead to * go from head to tail that is the normal behaviour. So for example LREM with count -2 and hello * as value to remove against the list (a,b,c,hello,x,hello,hello) will leave the list * (a,b,c,hello,x). The number of removed elements is returned as an integer, see below for more * information about the returned value. Note that non existing keys are considered like empty * lists by LREM, so LREM against non existing keys will always return 0. *

* Time complexity: O(N) (with N being the length of the list) * @param key * @param count * @param value * @return The number of removed elements if the operation succeeded */ @Override public long lrem(final String key, final long count, final String value) { checkIsInMultiOrPipeline(); return connection.executeCommand(commandObjects.lrem(key, count, value)); } /** * Atomically return and remove the first (LPOP) or last (RPOP) element of the list. For example * if the list contains the elements "a","b","c" LPOP will return "a" and the list will become * "b","c". *

* If the key does not exist or the list is already empty the special value 'nil' is returned. * @see Jedis#rpop(String) * @param key * @return Bulk reply */ @Override public String lpop(final String key) { checkIsInMultiOrPipeline(); return connection.executeCommand(commandObjects.lpop(key)); } @Override public List lpop(final String key, final int count) { checkIsInMultiOrPipeline(); return connection.executeCommand(commandObjects.lpop(key, count)); } @Override public Long lpos(final String key, final String element) { checkIsInMultiOrPipeline(); return connection.executeCommand(commandObjects.lpos(key, element)); } @Override public Long lpos(final String key, final String element, final LPosParams params) { checkIsInMultiOrPipeline(); return connection.executeCommand(commandObjects.lpos(key, element, params)); } @Override public List lpos(final String key, final String element, final LPosParams params, final long count) { checkIsInMultiOrPipeline(); return connection.executeCommand(commandObjects.lpos(key, element, params, count)); } /** * Atomically return and remove the first (LPOP) or last (RPOP) element of the list. For example * if the list contains the elements "a","b","c" RPOP will return "c" and the list will become * "a","b". *

* If the key does not exist or the list is already empty the special value 'nil' is returned. * @see Jedis#lpop(String) * @param key * @return Bulk reply */ @Override public String rpop(final String key) { checkIsInMultiOrPipeline(); return connection.executeCommand(commandObjects.rpop(key)); } @Override public List rpop(final String key, final int count) { checkIsInMultiOrPipeline(); return connection.executeCommand(commandObjects.rpop(key, count)); } /** * Atomically return and remove the last (tail) element of the srckey list, and push the element * as the first (head) element of the dstkey list. For example if the source list contains the * elements "a","b","c" and the destination list contains the elements "foo","bar" after an * RPOPLPUSH command the content of the two lists will be "a","b" and "c","foo","bar". *

* If the key does not exist or the list is already empty the special value 'nil' is returned. If * the srckey and dstkey are the same the operation is equivalent to removing the last element * from the list and pushing it as first element of the list, so it's a "list rotation" command. *

* Time complexity: O(1) * @param srckey * @param dstkey * @return Bulk reply * @deprecated Use {@link Jedis#lmove(String, String, ListDirection, ListDirection)} with * {@link ListDirection#RIGHT} and {@link ListDirection#LEFT}. * Deprecated in Jedis 7.3.0. Mirrors Redis deprecation since 6.2.0. */ @Deprecated @Override public String rpoplpush(final String srckey, final String dstkey) { checkIsInMultiOrPipeline(); return connection.executeCommand(commandObjects.rpoplpush(srckey, dstkey)); } /** * Add the specified member to the set value stored at key. If member is already a member of the * set no operation is performed. If key does not exist a new set with the specified member as * sole member is created. If the key exists but does not hold a set value an error is returned. *

* Time complexity O(1) * @param key * @param members * @return 1 if the new element was added, 0 if the element was already a member of the set */ @Override public long sadd(final String key, final String... members) { checkIsInMultiOrPipeline(); return connection.executeCommand(commandObjects.sadd(key, members)); } /** * Return all the members (elements) of the set value stored at key. This is just syntax glue for * {@link Jedis#sinter(String...) SINTER}. *

* Time complexity O(N) * @param key * @return Multi bulk reply */ @Override public Set smembers(final String key) { checkIsInMultiOrPipeline(); return connection.executeCommand(commandObjects.smembers(key)); } /** * Remove the specified member from the set value stored at key. If member was not a member of the * set no operation is performed. If key does not hold a set value an error is returned. *

* Time complexity O(1) * @param key * @param members * @return 1 if the new element was removed, 0 if the new element was not a member of the set */ @Override public long srem(final String key, final String... members) { checkIsInMultiOrPipeline(); return connection.executeCommand(commandObjects.srem(key, members)); } /** * Remove a random element from a Set returning it as return value. If the Set is empty or the key * does not exist, a nil object is returned. *

* The {@link Jedis#srandmember(String)} command does a similar work but the returned element is * not removed from the Set. *

* Time complexity O(1) * @param key * @return Bulk reply */ @Override public String spop(final String key) { checkIsInMultiOrPipeline(); return connection.executeCommand(commandObjects.spop(key)); } @Override public Set spop(final String key, final long count) { checkIsInMultiOrPipeline(); return connection.executeCommand(commandObjects.spop(key, count)); } /** * Move the specified member from the set at srckey to the set at dstkey. This operation is * atomic, in every given moment the element will appear to be in the source or destination set * for accessing clients. *

* If the source set does not exist or does not contain the specified element no operation is * performed and zero is returned, otherwise the element is removed from the source set and added * to the destination set. On success one is returned, even if the element was already present in * the destination set. *

* An error is raised if the source or destination keys contain a non Set value. *

* Time complexity O(1) * @param srckey * @param dstkey * @param member * @return 1 if the element was moved, 0 if the element was not found * on the first set and no operation was performed */ @Override public long smove(final String srckey, final String dstkey, final String member) { checkIsInMultiOrPipeline(); return connection.executeCommand(commandObjects.smove(srckey, dstkey, member)); } /** * Return the set cardinality (number of elements). If the key does not exist 0 is returned, like * for empty sets. * @param key * @return The cardinality (number of elements) of the set as an integer */ @Override public long scard(final String key) { checkIsInMultiOrPipeline(); return connection.executeCommand(commandObjects.scard(key)); } /** * Return true if member is a member of the set stored at key, otherwise false is returned. *

* Time complexity O(1) * @param key * @param member * @return {@code true} if the element is a member of the set, {@code false} otherwise */ @Override public boolean sismember(final String key, final String member) { checkIsInMultiOrPipeline(); return connection.executeCommand(commandObjects.sismember(key, member)); } /** * Returns whether each member is a member of the set stored at key. *

* Time complexity O(N) where N is the number of elements being checked for membership * @param key * @param members * @return List representing the membership of the given elements, in the same order as they are requested */ @Override public List smismember(final String key, final String... members) { checkIsInMultiOrPipeline(); return connection.executeCommand(commandObjects.smismember(key, members)); } /** * Return the members of a set resulting from the intersection of all the sets hold at the * specified keys. Like in {@link Jedis#lrange(String, long, long) LRANGE} the result is sent to * the connection as a multi-bulk reply (see the protocol specification for more information). If * just a single key is specified, then this command produces the same result as * {@link Jedis#smembers(String) SMEMBERS}. Actually SMEMBERS is just syntax sugar for SINTER. *

* Non existing keys are considered like empty sets, so if one of the keys is missing an empty set * is returned (since the intersection with an empty set always is an empty set). *

* Time complexity O(N*M) worst case where N is the cardinality of the smallest set and M the * number of sets * @param keys * @return A set with members of the resulting set */ @Override public Set sinter(final String... keys) { checkIsInMultiOrPipeline(); return connection.executeCommand(commandObjects.sinter(keys)); } /** * This command works exactly like {@link Jedis#sinter(String...) SINTER} but instead of being * returned the resulting set is stored as dstkey. *

* Time complexity O(N*M) worst case where N is the cardinality of the smallest set and M the * number of sets * @param dstkey * @param keys * @return The number of elements in the resulting set */ @Override public long sinterstore(final String dstkey, final String... keys) { checkIsInMultiOrPipeline(); return connection.executeCommand(commandObjects.sinterstore(dstkey, keys)); } /** * This command works exactly like {@link Jedis#sinter(String...) SINTER} but instead of returning * the result set, it returns just the cardinality of the result. *

* Time complexity O(N*M) worst case where N is the cardinality of the smallest * @param keys * @return The cardinality of the set which would result from the intersection of all the given sets */ @Override public long sintercard(String... keys) { checkIsInMultiOrPipeline(); return connection.executeCommand(commandObjects.sintercard(keys)); } /** * This command works exactly like {@link Jedis#sinter(String...) SINTER} but instead of returning * the result set, it returns just the cardinality of the result. *

* Time complexity O(N*M) worst case where N is the cardinality of the smallest * @param limit If the intersection cardinality reaches limit partway through the computation, * the algorithm will exit and yield limit as the cardinality. * @param keys * @return The cardinality of the set which would result from the intersection of all the given sets */ @Override public long sintercard(int limit, String... keys) { checkIsInMultiOrPipeline(); return connection.executeCommand(commandObjects.sintercard(limit, keys)); } /** * Return the members of a set resulting from the union of all the sets hold at the specified * keys. Like in {@link Jedis#lrange(String, long, long) LRANGE} the result is sent to the * connection as a multi-bulk reply (see the protocol specification for more information). If just * a single key is specified, then this command produces the same result as * {@link Jedis#smembers(String) SMEMBERS}. *

* Non existing keys are considered like empty sets. *

* Time complexity O(N) where N is the total number of elements in all the provided sets * @param keys * @return A set with members of the resulting set */ @Override public Set sunion(final String... keys) { checkIsInMultiOrPipeline(); return connection.executeCommand(commandObjects.sunion(keys)); } /** * This command works exactly like {@link Jedis#sunion(String...) SUNION} but instead of being * returned the resulting set is stored as dstkey. Any existing value in dstkey will be * over-written. *

* Time complexity O(N) where N is the total number of elements in all the provided sets * @param dstkey * @param keys * @return The number of elements in the resulting set */ @Override public long sunionstore(final String dstkey, final String... keys) { checkIsInMultiOrPipeline(); return connection.executeCommand(commandObjects.sunionstore(dstkey, keys)); } /** * Return the difference between the Set stored at key1 and all the Sets key2, ..., keyN *

* Example: * *

   * key1 = [x, a, b, c]
   * key2 = [c]
   * key3 = [a, d]
   * SDIFF key1,key2,key3 => [x, b]
   * 
* * Non existing keys are considered like empty sets. *

* Time complexity: *

* O(N) with N being the total number of elements of all the sets * @param keys * @return A set with members of the resulting set */ @Override public Set sdiff(final String... keys) { checkIsInMultiOrPipeline(); return connection.executeCommand(commandObjects.sdiff(keys)); } /** * This command works exactly like {@link Jedis#sdiff(String...) SDIFF} but instead of being * returned the resulting set is stored in dstkey. * @param dstkey * @param keys * @return The number of elements in the resulting set */ @Override public long sdiffstore(final String dstkey, final String... keys) { checkIsInMultiOrPipeline(); return connection.executeCommand(commandObjects.sdiffstore(dstkey, keys)); } /** * Return a random element from a Set, without removing the element. If the Set is empty or the * key does not exist, a nil object is returned. *

* The SPOP command does a similar work but the returned element is popped (removed) from the Set. *

* Time complexity O(1) * @param key * @return The randomly selected element */ @Override public String srandmember(final String key) { checkIsInMultiOrPipeline(); return connection.executeCommand(commandObjects.srandmember(key)); } /** * Return a random elements from a Set, without removing the elements. If the Set is empty or the * key does not exist, an empty list is returned. *

* The SPOP command does a similar work but the returned elements is popped (removed) from the Set. *

* Time complexity O(1) * @param key * @param count if positive, return an array of distinct elements. * If negative the behavior changes and the command is allowed to * return the same element multiple times * @return A list of randomly selected elements */ @Override public List srandmember(final String key, final int count) { checkIsInMultiOrPipeline(); return connection.executeCommand(commandObjects.srandmember(key, count)); } /** * Add the specified member having the specified score to the sorted set stored at key. If member * is already a member of the sorted set the score is updated, and the element reinserted in the * right position to ensure sorting. If key does not exist a new sorted set with the specified * member as sole member is created. If the key exists but does not hold a sorted set value an * error is returned. *

* The score value can be the string representation of a double precision floating point number. *

* Time complexity O(log(N)) with N being the number of elements in the sorted set * @param key * @param score * @param member * @return 1 if the new element was added, 0 if the element was already a member of the sorted * set and the score was updated */ @Override public long zadd(final String key, final double score, final String member) { checkIsInMultiOrPipeline(); return connection.executeCommand(commandObjects.zadd(key, score, member)); } @Override public long zadd(final String key, final double score, final String member, final ZAddParams params) { checkIsInMultiOrPipeline(); return connection.executeCommand(commandObjects.zadd(key, score, member, params)); } @Override public long zadd(final String key, final Map scoreMembers) { checkIsInMultiOrPipeline(); return connection.executeCommand(commandObjects.zadd(key, scoreMembers)); } @Override public long zadd(final String key, final Map scoreMembers, final ZAddParams params) { checkIsInMultiOrPipeline(); return connection.executeCommand(commandObjects.zadd(key, scoreMembers, params)); } @Override public Double zaddIncr(final String key, final double score, final String member, final ZAddParams params) { checkIsInMultiOrPipeline(); return connection.executeCommand(commandObjects.zaddIncr(key, score, member, params)); } @Override public List zdiff(String... keys) { checkIsInMultiOrPipeline(); return connection.executeCommand(commandObjects.zdiff(keys)); } @Override public List zdiffWithScores(String... keys) { checkIsInMultiOrPipeline(); return connection.executeCommand(commandObjects.zdiffWithScores(keys)); } @Override @Deprecated public long zdiffStore(final String dstkey, final String... keys) { checkIsInMultiOrPipeline(); return connection.executeCommand(commandObjects.zdiffStore(dstkey, keys)); } @Override public long zdiffstore(final String dstkey, final String... keys) { checkIsInMultiOrPipeline(); return connection.executeCommand(commandObjects.zdiffstore(dstkey, keys)); } @Override public List zrange(final String key, final long start, final long stop) { checkIsInMultiOrPipeline(); return connection.executeCommand(commandObjects.zrange(key, start, stop)); } /** * Remove the specified member from the sorted set value stored at key. If member was not a member * of the set no operation is performed. If key does not hold a set value an error is returned. *

* Time complexity O(log(N)) with N being the number of elements in the sorted set * @param key * @param members * @return 1 if the new element was removed, 0 if the new element was not a member of the set */ @Override public long zrem(final String key, final String... members) { checkIsInMultiOrPipeline(); return connection.executeCommand(commandObjects.zrem(key, members)); } /** * If member already exists in the sorted set adds the increment to its score and updates the * position of the element in the sorted set accordingly. If member does not already exist in the * sorted set it is added with increment as score (that is, like if the previous score was * virtually zero). If key does not exist a new sorted set with the specified member as sole * member is created. If the key exists but does not hold a sorted set value an error is returned. *

* The score value can be the string representation of a double precision floating point number. * It's possible to provide a negative value to perform a decrement. *

* For an introduction to sorted sets check the Introduction to Redis data types page. *

* Time complexity O(log(N)) with N being the number of elements in the sorted set * @param key * @param increment * @param member * @return The new score */ @Override public double zincrby(final String key, final double increment, final String member) { checkIsInMultiOrPipeline(); return connection.executeCommand(commandObjects.zincrby(key, increment, member)); } @Override public Double zincrby(final String key, final double increment, final String member, final ZIncrByParams params) { checkIsInMultiOrPipeline(); return connection.executeCommand(commandObjects.zincrby(key, increment, member, params)); } /** * Return the rank (or index) of member in the sorted set at key, with scores being ordered from * low to high. *

* When the given member does not exist in the sorted set, the special value 'nil' is returned. * The returned rank (or index) of the member is 0-based for both commands. *

* Time complexity: *

* O(log(N)) * @see Jedis#zrevrank(String, String) * @param key * @param member * @return The element as an integer if the element exists. A 'nil' bulk reply if there is no such element. */ @Override public Long zrank(final String key, final String member) { checkIsInMultiOrPipeline(); return connection.executeCommand(commandObjects.zrank(key, member)); } /** * Return the rank (or index) of member in the sorted set at key, with scores being ordered from * high to low. *

* When the given member does not exist in the sorted set, the special value 'nil' is returned. * The returned rank (or index) of the member is 0-based for both commands. *

* Time complexity: *

* O(log(N)) * @see Jedis#zrank(String, String) * @param key * @param member * @return The element as an integer if the element exists. A 'nil' bulk reply if there is no such element. */ @Override public Long zrevrank(final String key, final String member) { checkIsInMultiOrPipeline(); return connection.executeCommand(commandObjects.zrevrank(key, member)); } /** * Returns the rank and the score of member in the sorted set stored at key, with the scores * ordered from low to high. * @param key the key * @param member the member * @return the KeyValue contains rank and score. */ @Override public KeyValue zrankWithScore(String key, String member) { checkIsInMultiOrPipeline(); return connection.executeCommand(commandObjects.zrankWithScore(key, member)); } /** * Returns the rank and the score of member in the sorted set stored at key, with the scores * ordered from high to low. * @param key the key * @param member the member * @return the KeyValue contains rank and score. */ @Override public KeyValue zrevrankWithScore(String key, String member) { checkIsInMultiOrPipeline(); return connection.executeCommand(commandObjects.zrevrankWithScore(key, member)); } /** * @deprecated Use {@link Jedis#zrange(String, ZRangeParams)} with {@link ZRangeParams#rev()}. * Deprecated in Jedis 7.3.0. Mirrors Redis deprecation since 6.2.0. */ @Deprecated @Override public List zrevrange(final String key, final long start, final long stop) { checkIsInMultiOrPipeline(); return connection.executeCommand(commandObjects.zrevrange(key, start, stop)); } @Override public List zrangeWithScores(final String key, final long start, final long stop) { checkIsInMultiOrPipeline(); return connection.executeCommand(commandObjects.zrangeWithScores(key, start, stop)); } /** * @deprecated Use {@link Jedis#zrangeWithScores(String, ZRangeParams)} with {@link ZRangeParams#rev()}. * Deprecated in Jedis 7.3.0. Mirrors Redis deprecation since 6.2.0. */ @Deprecated @Override public List zrevrangeWithScores(final String key, final long start, final long stop) { checkIsInMultiOrPipeline(); return connection.executeCommand(commandObjects.zrevrangeWithScores(key, start, stop)); } @Override public List zrange(String key, ZRangeParams zRangeParams) { checkIsInMultiOrPipeline(); return connection.executeCommand(commandObjects.zrange(key, zRangeParams)); } @Override public List zrangeWithScores(String key, ZRangeParams zRangeParams) { checkIsInMultiOrPipeline(); return connection.executeCommand(commandObjects.zrangeWithScores(key, zRangeParams)); } @Override public long zrangestore(String dest, String src, ZRangeParams zRangeParams) { checkIsInMultiOrPipeline(); return connection.executeCommand(commandObjects.zrangestore(dest, src, zRangeParams)); } @Override public String zrandmember(final String key) { checkIsInMultiOrPipeline(); return connection.executeCommand(commandObjects.zrandmember(key)); } @Override public List zrandmember(final String key, final long count) { checkIsInMultiOrPipeline(); return connection.executeCommand(commandObjects.zrandmember(key, count)); } @Override public List zrandmemberWithScores(final String key, final long count) { checkIsInMultiOrPipeline(); return connection.executeCommand(commandObjects.zrandmemberWithScores(key, count)); } /** * Return the sorted set cardinality (number of elements). If the key does not exist 0 is * returned, like for empty sorted sets. *

* Time complexity O(1) * @param key * @return The cardinality (number of elements) of the set as an integer */ @Override public long zcard(final String key) { checkIsInMultiOrPipeline(); return connection.executeCommand(commandObjects.zcard(key)); } /** * Return the score of the specified element of the sorted set at key. If the specified element * does not exist in the sorted set, or the key does not exist at all, a special 'nil' value is * returned. *

* Time complexity: O(1) * @param key * @param member * @return The score */ @Override public Double zscore(final String key, final String member) { checkIsInMultiOrPipeline(); return connection.executeCommand(commandObjects.zscore(key, member)); } /** * Returns the scores associated with the specified members in the sorted set stored at key. For * every member that does not exist in the sorted set, a nil value is returned. *

* Time complexity: O(N) where N is the number of members being requested. * @param key * @param members * @return The scores */ @Override public List zmscore(final String key, final String... members) { checkIsInMultiOrPipeline(); return connection.executeCommand(commandObjects.zmscore(key, members)); } @Override public Tuple zpopmax(final String key) { checkIsInMultiOrPipeline(); return connection.executeCommand(commandObjects.zpopmax(key)); } @Override public List zpopmax(final String key, final int count) { checkIsInMultiOrPipeline(); return connection.executeCommand(commandObjects.zpopmax(key, count)); } @Override public Tuple zpopmin(final String key) { checkIsInMultiOrPipeline(); return connection.executeCommand(commandObjects.zpopmin(key)); } @Override public List zpopmin(final String key, final int count) { checkIsInMultiOrPipeline(); return connection.executeCommand(commandObjects.zpopmin(key, count)); } public String watch(final String... keys) { checkIsInMultiOrPipeline(); connection.sendCommand(WATCH, keys); // return connection.getStatusCodeReply(); String status = connection.getStatusCodeReply(); isInWatch = true; return status; } /** * Sort a Set or a List. *

* Sort the elements contained in the List, Set, or Sorted Set value at key. By default sorting is * numeric with elements being compared as double precision floating point numbers. This is the * simplest form of SORT. * @see Jedis#sort(String, String) * @see Jedis#sort(String, SortingParams) * @see Jedis#sort(String, SortingParams, String) * @param key * @return Assuming the Set/List at key contains a list of numbers, the return value will be the * list of numbers ordered from the smallest to the biggest number. */ @Override public List sort(final String key) { checkIsInMultiOrPipeline(); return connection.executeCommand(commandObjects.sort(key)); } /** * Sort a Set or a List accordingly to the specified parameters. *

* examples: *

* Given are the following sets and key/values: * *

   * x = [1, 2, 3]
   * y = [a, b, c]
   *
   * k1 = z
   * k2 = y
   * k3 = x
   *
   * w1 = 9
   * w2 = 8
   * w3 = 7
   * 
* * Sort Order: * *
   * sort(x) or sort(x, sp.asc())
   * -> [1, 2, 3]
   *
   * sort(x, sp.desc())
   * -> [3, 2, 1]
   *
   * sort(y)
   * -> [c, a, b]
   *
   * sort(y, sp.alpha())
   * -> [a, b, c]
   *
   * sort(y, sp.alpha().desc())
   * -> [c, a, b]
   * 
* * Limit (e.g. for Pagination): * *
   * sort(x, sp.limit(0, 2))
   * -> [1, 2]
   *
   * sort(y, sp.alpha().desc().limit(1, 2))
   * -> [b, a]
   * 
* * Sorting by external keys: * *
   * sort(x, sb.by(w*))
   * -> [3, 2, 1]
   *
   * sort(x, sb.by(w*).desc())
   * -> [1, 2, 3]
   * 
* * Getting external keys: * *
   * sort(x, sp.by(w*).get(k*))
   * -> [x, y, z]
   *
   * sort(x, sp.by(w*).get(#).get(k*))
   * -> [3, x, 2, y, 1, z]
   * 
* @see Jedis#sort(String) * @see Jedis#sort(String, SortingParams, String) * @param key * @param sortingParams * @return a list of sorted elements. */ @Override public List sort(final String key, final SortingParams sortingParams) { checkIsInMultiOrPipeline(); return connection.executeCommand(commandObjects.sort(key, sortingParams)); } /** * Sort a Set or a List accordingly to the specified parameters and store the result at dstkey. * @see Jedis#sort(String, SortingParams) * @see Jedis#sort(String) * @see Jedis#sort(String, String) * @param key * @param sortingParams * @param dstkey * @return The number of elements of the list at dstkey */ @Override public long sort(final String key, final SortingParams sortingParams, final String dstkey) { checkIsInMultiOrPipeline(); return connection.executeCommand(commandObjects.sort(key, sortingParams, dstkey)); } @Override public List sortReadonly(String key, SortingParams sortingParams) { checkIsInMultiOrPipeline(); return connection.executeCommand(commandObjects.sortReadonly(key, sortingParams)); } /** * Sort a Set or a List and Store the Result at dstkey. *

* Sort the elements contained in the List, Set, or Sorted Set value at key and store the result * at dstkey. By default sorting is numeric with elements being compared as double precision * floating point numbers. This is the simplest form of SORT. * @see Jedis#sort(String) * @see Jedis#sort(String, SortingParams) * @see Jedis#sort(String, SortingParams, String) * @param key * @param dstkey * @return The number of elements of the list at dstkey */ @Override public long sort(final String key, final String dstkey) { checkIsInMultiOrPipeline(); return connection.executeCommand(commandObjects.sort(key, dstkey)); } @Override public String lmove(final String srcKey, final String dstKey, final ListDirection from, final ListDirection to) { checkIsInMultiOrPipeline(); return connection.executeCommand(commandObjects.lmove(srcKey, dstKey, from, to)); } @Override public String blmove(final String srcKey, final String dstKey, final ListDirection from, final ListDirection to, final double timeout) { checkIsInMultiOrPipeline(); return connection.executeCommand(commandObjects.blmove(srcKey, dstKey, from, to, timeout)); } /** * BLPOP (and BRPOP) is a blocking list pop primitive. You can see this commands as blocking * versions of LPOP and RPOP able to block if the specified keys don't exist or contain empty * lists. *

* The following is a description of the exact semantic. We describe BLPOP but the two commands * are identical, the only difference is that BLPOP pops the element from the left (head) of the * list, and BRPOP pops from the right (tail). *

* Non blocking behavior *

* When BLPOP is called, if at least one of the specified keys contain a non empty list, an * element is popped from the head of the list and returned to the caller together with the name * of the key (BLPOP returns a two elements array, the first element is the key, the second the * popped value). *

* Keys are scanned from left to right, so for instance if you issue BLPOP list1 list2 list3 0 * against a dataset where list1 does not exist but list2 and list3 contain non empty lists, BLPOP * guarantees to return an element from the list stored at list2 (since it is the first non empty * list starting from the left). *

* Blocking behavior *

* If none of the specified keys exist or contain non empty lists, BLPOP blocks until some other * connection performs a LPUSH or an RPUSH operation against one of the lists. *

* Once new data is present on one of the lists, the connection finally returns with the name of the * key unblocking it and the popped value. *

* When blocking, if a non-zero timeout is specified, the connection will unblock returning a nil * special value if the specified amount of seconds passed without a push operation against at * least one of the specified keys. *

* The timeout argument is interpreted as an integer value. A timeout of zero means instead to * block forever. *

* Multiple clients blocking for the same keys *

* Multiple clients can block for the same key. They are put into a queue, so the first to be * served will be the one that started to wait earlier, in a first-blpopping first-served fashion. *

* blocking POP inside a MULTI/EXEC transaction *

* BLPOP and BRPOP can be used with pipelining (sending multiple commands and reading the replies * in batch), but it does not make sense to use BLPOP or BRPOP inside a MULTI/EXEC block (a Redis * transaction). *

* The behavior of BLPOP inside MULTI/EXEC when the list is empty is to return a multi-bulk nil * reply, exactly what happens when the timeout is reached. If you like science fiction, think at * it like if inside MULTI/EXEC the time will flow at infinite speed :) *

* Time complexity: O(1) * @see Jedis#brpop(int, String...) * @param timeout * @param keys * @return BLPOP returns a two-elements array via a multi bulk reply in order to return both the * unblocking key and the popped value. *

When a non-zero timeout is specified, and the BLPOP operation timed out, the return value is a nil multi bulk reply. Most connection values will return false or nil accordingly to the programming language used. */ @Override public List blpop(final int timeout, final String... keys) { checkIsInMultiOrPipeline(); return connection.executeCommand(commandObjects.blpop(timeout, keys)); } @Override public KeyValue blpop(final double timeout, final String... keys) { checkIsInMultiOrPipeline(); return connection.executeCommand(commandObjects.blpop(timeout, keys)); } /** * BLPOP (and BRPOP) is a blocking list pop primitive. You can see this commands as blocking * versions of LPOP and RPOP able to block if the specified keys don't exist or contain empty * lists. *

* The following is a description of the exact semantic. We describe BLPOP but the two commands * are identical, the only difference is that BLPOP pops the element from the left (head) of the * list, and BRPOP pops from the right (tail). *

* Non blocking behavior *

* When BLPOP is called, if at least one of the specified keys contain a non empty list, an * element is popped from the head of the list and returned to the caller together with the name * of the key (BLPOP returns a two elements array, the first element is the key, the second the * popped value). *

* Keys are scanned from left to right, so for instance if you issue BLPOP list1 list2 list3 0 * against a dataset where list1 does not exist but list2 and list3 contain non empty lists, BLPOP * guarantees to return an element from the list stored at list2 (since it is the first non empty * list starting from the left). *

* Blocking behavior *

* If none of the specified keys exist or contain non empty lists, BLPOP blocks until some other * connection performs a LPUSH or an RPUSH operation against one of the lists. *

* Once new data is present on one of the lists, the connection finally returns with the name of the * key unblocking it and the popped value. *

* When blocking, if a non-zero timeout is specified, the connection will unblock returning a nil * special value if the specified amount of seconds passed without a push operation against at * least one of the specified keys. *

* The timeout argument is interpreted as an integer value. A timeout of zero means instead to * block forever. *

* Multiple clients blocking for the same keys *

* Multiple clients can block for the same key. They are put into a queue, so the first to be * served will be the one that started to wait earlier, in a first-blpopping first-served fashion. *

* blocking POP inside a MULTI/EXEC transaction *

* BLPOP and BRPOP can be used with pipelining (sending multiple commands and reading the replies * in batch), but it does not make sense to use BLPOP or BRPOP inside a MULTI/EXEC block (a Redis * transaction). *

* The behavior of BLPOP inside MULTI/EXEC when the list is empty is to return a multi-bulk nil * reply, exactly what happens when the timeout is reached. If you like science fiction, think at * it like if inside MULTI/EXEC the time will flow at infinite speed :) *

* Time complexity: O(1) * @see Jedis#blpop(int, String...) * @param timeout * @param keys * @return BLPOP returns a two-elements array via a multi bulk reply in order to return both the * unblocking key and the popped value. *

When a non-zero timeout is specified, and the BLPOP operation timed out, the return value is a nil multi bulk reply. Most connection values will return false or nil accordingly to the programming language used. */ @Override public List brpop(final int timeout, final String... keys) { checkIsInMultiOrPipeline(); return connection.executeCommand(commandObjects.brpop(timeout, keys)); } @Override public KeyValue brpop(final double timeout, final String... keys) { checkIsInMultiOrPipeline(); return connection.executeCommand(commandObjects.brpop(timeout, keys)); } @Override public KeyValue> lmpop(ListDirection direction, String... keys) { checkIsInMultiOrPipeline(); return connection.executeCommand(commandObjects.lmpop(direction, keys)); } @Override public KeyValue> lmpop(ListDirection direction, int count, String... keys) { checkIsInMultiOrPipeline(); return connection.executeCommand(commandObjects.lmpop(direction, count, keys)); } @Override public KeyValue> blmpop(double timeout, ListDirection direction, String... keys) { checkIsInMultiOrPipeline(); return connection.executeCommand(commandObjects.blmpop(timeout, direction, keys)); } @Override public KeyValue> blmpop(double timeout, ListDirection direction, int count, String... keys) { checkIsInMultiOrPipeline(); return connection.executeCommand(commandObjects.blmpop(timeout, direction, count, keys)); } @Override public KeyValue bzpopmax(double timeout, String... keys) { checkIsInMultiOrPipeline(); return connection.executeCommand(commandObjects.bzpopmax(timeout, keys)); } @Override public KeyValue bzpopmin(double timeout, String... keys) { checkIsInMultiOrPipeline(); return connection.executeCommand(commandObjects.bzpopmin(timeout, keys)); } @Override public List blpop(final int timeout, final String key) { checkIsInMultiOrPipeline(); return connection.executeCommand(commandObjects.blpop(timeout, key)); } @Override public KeyValue blpop(double timeout, String key) { checkIsInMultiOrPipeline(); return connection.executeCommand(commandObjects.blpop(timeout, key)); } @Override public List brpop(final int timeout, final String key) { checkIsInMultiOrPipeline(); return connection.executeCommand(commandObjects.brpop(timeout, key)); } @Override public KeyValue brpop(double timeout, String key) { checkIsInMultiOrPipeline(); return connection.executeCommand(commandObjects.brpop(timeout, key)); } @Override public long zcount(final String key, final double min, final double max) { checkIsInMultiOrPipeline(); return connection.executeCommand(commandObjects.zcount(key, min, max)); } @Override public long zcount(final String key, final String min, final String max) { checkIsInMultiOrPipeline(); return connection.executeCommand(commandObjects.zcount(key, min, max)); } /** * Return the all the elements in the sorted set at key with a score between min and max * (including elements with score equal to min or max). *

* The elements having the same score are returned sorted lexicographically as ASCII strings (this * follows from a property of Redis sorted sets and does not involve further computation). *

* Using the optional {@link Jedis#zrangeByScore(String, double, double, int, int) LIMIT} it is * possible to get only a range of the matching elements in an SQL-alike way. Note that if offset * is large the commands needs to traverse the list for offset elements and this adds up to the * O(M) figure. *

* The {@link Jedis#zcount(String, double, double) ZCOUNT} command is similar to * {@link Jedis#zrangeByScore(String, double, double) ZRANGEBYSCORE} but instead of returning the * actual elements in the specified interval, it just returns the number of matching elements. *

* Exclusive intervals and infinity *

* min and max can be -inf and +inf, so that you are not required to know what's the greatest or * smallest element in order to take, for instance, elements "up to a given value". *

* Also while the interval is for default closed (inclusive) it is possible to specify open * intervals prefixing the score with a "(" character, so for instance: *

* {@code ZRANGEBYSCORE zset (1.3 5} *

* Will return all the values with score > 1.3 and <= 5, while for instance: *

* {@code ZRANGEBYSCORE zset (5 (10} *

* Will return all the values with score > 5 and < 10 (5 and 10 excluded). *

* Time complexity: *

* O(log(N))+O(M) with N being the number of elements in the sorted set and M the number of * elements returned by the command, so if M is constant (for instance you always ask for the * first ten elements with LIMIT) you can consider it O(log(N)) * @see Jedis#zrangeByScore(String, double, double) * @see Jedis#zrangeByScore(String, double, double, int, int) * @see Jedis#zrangeByScoreWithScores(String, double, double) * @see Jedis#zrangeByScoreWithScores(String, String, String) * @see Jedis#zrangeByScoreWithScores(String, double, double, int, int) * @see Jedis#zcount(String, double, double) * @param key * @param min a double or Double.NEGATIVE_INFINITY for "-inf" * @param max a double or Double.POSITIVE_INFINITY for "+inf" * @return A list of elements in the specified score range * @deprecated Use {@link Jedis#zrange(String, ZRangeParams)} with {@link ZRangeParams#zrangeByScoreParams(double, double)}. * Deprecated in Jedis 7.3.0. Mirrors Redis deprecation since 6.2.0. */ @Deprecated @Override public List zrangeByScore(final String key, final double min, final double max) { checkIsInMultiOrPipeline(); return connection.executeCommand(commandObjects.zrangeByScore(key, min, max)); } /** * @deprecated Use {@link Jedis#zrange(String, ZRangeParams)} with {@link ZRangeParams#zrangeByScoreParams(double, double)}. * Deprecated in Jedis 7.3.0. Mirrors Redis deprecation since 6.2.0. */ @Deprecated @Override public List zrangeByScore(final String key, final String min, final String max) { checkIsInMultiOrPipeline(); return connection.executeCommand(commandObjects.zrangeByScore(key, min, max)); } /** * Return the all the elements in the sorted set at key with a score between min and max * (including elements with score equal to min or max). *

* The elements having the same score are returned sorted lexicographically as ASCII strings (this * follows from a property of Redis sorted sets and does not involve further computation). *

* Using the optional {@link Jedis#zrangeByScore(String, double, double, int, int) LIMIT} it is * possible to get only a range of the matching elements in an SQL-alike way. Note that if offset * is large the commands needs to traverse the list for offset elements and this adds up to the * O(M) figure. *

* The {@link Jedis#zcount(String, double, double) ZCOUNT} command is similar to * {@link Jedis#zrangeByScore(String, double, double) ZRANGEBYSCORE} but instead of returning the * actual elements in the specified interval, it just returns the number of matching elements. *

* Exclusive intervals and infinity *

* min and max can be -inf and +inf, so that you are not required to know what's the greatest or * smallest element in order to take, for instance, elements "up to a given value". *

* Also while the interval is for default closed (inclusive) it is possible to specify open * intervals prefixing the score with a "(" character, so for instance: *

* {@code ZRANGEBYSCORE zset (1.3 5} *

* Will return all the values with score > 1.3 and <= 5, while for instance: *

* {@code ZRANGEBYSCORE zset (5 (10} *

* Will return all the values with score > 5 and < 10 (5 and 10 excluded). *

* Time complexity: *

* O(log(N))+O(M) with N being the number of elements in the sorted set and M the number of * elements returned by the command, so if M is constant (for instance you always ask for the * first ten elements with LIMIT) you can consider it O(log(N)) * @see Jedis#zrangeByScore(String, double, double) * @see Jedis#zrangeByScore(String, double, double, int, int) * @see Jedis#zrangeByScoreWithScores(String, double, double) * @see Jedis#zrangeByScoreWithScores(String, double, double, int, int) * @see Jedis#zcount(String, double, double) * @param key * @param min * @param max * @param offset * @param count * @return A list of elements in the specified score range * @deprecated Use {@link Jedis#zrange(String, ZRangeParams)} with {@link ZRangeParams#zrangeByScoreParams(double, double)}. * Deprecated in Jedis 7.3.0. Mirrors Redis deprecation since 6.2.0. */ @Deprecated @Override public List zrangeByScore(final String key, final double min, final double max, final int offset, final int count) { checkIsInMultiOrPipeline(); return connection.executeCommand(commandObjects.zrangeByScore(key, min, max, offset, count)); } /** * @deprecated Use {@link Jedis#zrange(String, ZRangeParams)} with {@link ZRangeParams#zrangeByScoreParams(double, double)}. * Deprecated in Jedis 7.3.0. Mirrors Redis deprecation since 6.2.0. */ @Deprecated @Override public List zrangeByScore(final String key, final String min, final String max, final int offset, final int count) { checkIsInMultiOrPipeline(); return connection.executeCommand(commandObjects.zrangeByScore(key, min, max, offset, count)); } /** * Return the all the elements in the sorted set at key with a score between min and max * (including elements with score equal to min or max). *

* The elements having the same score are returned sorted lexicographically as ASCII strings (this * follows from a property of Redis sorted sets and does not involve further computation). *

* Using the optional {@link Jedis#zrangeByScore(String, double, double, int, int) LIMIT} it is * possible to get only a range of the matching elements in an SQL-alike way. Note that if offset * is large the commands needs to traverse the list for offset elements and this adds up to the * O(M) figure. *

* The {@link Jedis#zcount(String, double, double) ZCOUNT} command is similar to * {@link Jedis#zrangeByScore(String, double, double) ZRANGEBYSCORE} but instead of returning the * actual elements in the specified interval, it just returns the number of matching elements. *

* Exclusive intervals and infinity *

* min and max can be -inf and +inf, so that you are not required to know what's the greatest or * smallest element in order to take, for instance, elements "up to a given value". *

* Also while the interval is for default closed (inclusive) it is possible to specify open * intervals prefixing the score with a "(" character, so for instance: *

* {@code ZRANGEBYSCORE zset (1.3 5} *

* Will return all the values with score > 1.3 and <= 5, while for instance: *

* {@code ZRANGEBYSCORE zset (5 (10} *

* Will return all the values with score > 5 and < 10 (5 and 10 excluded). *

* Time complexity: *

* O(log(N))+O(M) with N being the number of elements in the sorted set and M the number of * elements returned by the command, so if M is constant (for instance you always ask for the * first ten elements with LIMIT) you can consider it O(log(N)) * @see Jedis#zrangeByScore(String, double, double) * @see Jedis#zrangeByScore(String, double, double, int, int) * @see Jedis#zrangeByScoreWithScores(String, double, double) * @see Jedis#zrangeByScoreWithScores(String, double, double, int, int) * @see Jedis#zcount(String, double, double) * @param key * @param min * @param max * @return A list of elements in the specified score range * @deprecated Use {@link Jedis#zrangeWithScores(String, ZRangeParams)} with {@link ZRangeParams#zrangeByScoreParams(double, double)}. * Deprecated in Jedis 7.3.0. Mirrors Redis deprecation since 6.2.0. */ @Deprecated @Override public List zrangeByScoreWithScores(final String key, final double min, final double max) { checkIsInMultiOrPipeline(); return connection.executeCommand(commandObjects.zrangeByScoreWithScores(key, min, max)); } /** * @deprecated Use {@link Jedis#zrangeWithScores(String, ZRangeParams)} with {@link ZRangeParams#zrangeByScoreParams(double, double)}. * Deprecated in Jedis 7.3.0. Mirrors Redis deprecation since 6.2.0. */ @Deprecated @Override public List zrangeByScoreWithScores(final String key, final String min, final String max) { checkIsInMultiOrPipeline(); return connection.executeCommand(commandObjects.zrangeByScoreWithScores(key, min, max)); } /** * Return the all the elements in the sorted set at key with a score between min and max * (including elements with score equal to min or max). *

* The elements having the same score are returned sorted lexicographically as ASCII strings (this * follows from a property of Redis sorted sets and does not involve further computation). *

* Using the optional {@link Jedis#zrangeByScore(String, double, double, int, int) LIMIT} it is * possible to get only a range of the matching elements in an SQL-alike way. Note that if offset * is large the commands needs to traverse the list for offset elements and this adds up to the * O(M) figure. *

* The {@link Jedis#zcount(String, double, double) ZCOUNT} command is similar to * {@link Jedis#zrangeByScore(String, double, double) ZRANGEBYSCORE} but instead of returning the * actual elements in the specified interval, it just returns the number of matching elements. *

* Exclusive intervals and infinity *

* min and max can be -inf and +inf, so that you are not required to know what's the greatest or * smallest element in order to take, for instance, elements "up to a given value". *

* Also while the interval is for default closed (inclusive) it is possible to specify open * intervals prefixing the score with a "(" character, so for instance: *

* {@code ZRANGEBYSCORE zset (1.3 5} *

* Will return all the values with score > 1.3 and <= 5, while for instance: *

* {@code ZRANGEBYSCORE zset (5 (10} *

* Will return all the values with score > 5 and < 10 (5 and 10 excluded). *

* Time complexity: *

* O(log(N))+O(M) with N being the number of elements in the sorted set and M the number of * elements returned by the command, so if M is constant (for instance you always ask for the * first ten elements with LIMIT) you can consider it O(log(N)) * @see Jedis#zrangeByScore(String, double, double) * @see Jedis#zrangeByScore(String, double, double, int, int) * @see Jedis#zrangeByScoreWithScores(String, double, double) * @see Jedis#zrangeByScoreWithScores(String, double, double, int, int) * @see Jedis#zcount(String, double, double) * @param key * @param min * @param max * @param offset * @param count * @return A list of elements in the specified score range * @deprecated Use {@link Jedis#zrangeWithScores(String, ZRangeParams)} with {@link ZRangeParams#zrangeByScoreParams(double, double)}. * Deprecated in Jedis 7.3.0. Mirrors Redis deprecation since 6.2.0. */ @Deprecated @Override public List zrangeByScoreWithScores(final String key, final double min, final double max, final int offset, final int count) { checkIsInMultiOrPipeline(); return connection.executeCommand(commandObjects.zrangeByScoreWithScores(key, min, max, offset, count)); } /** * @deprecated Use {@link Jedis#zrangeWithScores(String, ZRangeParams)} with {@link ZRangeParams#zrangeByScoreParams(double, double)}. * Deprecated in Jedis 7.3.0. Mirrors Redis deprecation since 6.2.0. */ @Deprecated @Override public List zrangeByScoreWithScores(final String key, final String min, final String max, final int offset, final int count) { checkIsInMultiOrPipeline(); return connection.executeCommand(commandObjects.zrangeByScoreWithScores(key, min, max, offset, count)); } /** * @deprecated Use {@link Jedis#zrange(String, ZRangeParams)} with {@link ZRangeParams#zrangeByScoreParams(double, double)} and {@link ZRangeParams#rev()}. * Deprecated in Jedis 7.3.0. Mirrors Redis deprecation since 6.2.0. */ @Deprecated @Override public List zrevrangeByScore(final String key, final double max, final double min) { checkIsInMultiOrPipeline(); return connection.executeCommand(commandObjects.zrevrangeByScore(key, max, min)); } /** * @deprecated Use {@link Jedis#zrange(String, ZRangeParams)} with {@link ZRangeParams#zrangeByScoreParams(double, double)} and {@link ZRangeParams#rev()}. * Deprecated in Jedis 7.3.0. Mirrors Redis deprecation since 6.2.0. */ @Deprecated @Override public List zrevrangeByScore(final String key, final String max, final String min) { checkIsInMultiOrPipeline(); return connection.executeCommand(commandObjects.zrevrangeByScore(key, max, min)); } /** * @deprecated Use {@link Jedis#zrange(String, ZRangeParams)} with {@link ZRangeParams#zrangeByScoreParams(double, double)} and {@link ZRangeParams#rev()}. * Deprecated in Jedis 7.3.0. Mirrors Redis deprecation since 6.2.0. */ @Deprecated @Override public List zrevrangeByScore(final String key, final double max, final double min, final int offset, final int count) { checkIsInMultiOrPipeline(); return connection.executeCommand(commandObjects.zrevrangeByScore(key, max, min, offset, count)); } /** * @deprecated Use {@link Jedis#zrangeWithScores(String, ZRangeParams)} with {@link ZRangeParams#zrangeByScoreParams(double, double)} and {@link ZRangeParams#rev()}. * Deprecated in Jedis 7.3.0. Mirrors Redis deprecation since 6.2.0. */ @Deprecated @Override public List zrevrangeByScoreWithScores(final String key, final double max, final double min) { checkIsInMultiOrPipeline(); return connection.executeCommand(commandObjects.zrevrangeByScoreWithScores(key, max, min)); } /** * @deprecated Use {@link Jedis#zrangeWithScores(String, ZRangeParams)} with {@link ZRangeParams#zrangeByScoreParams(double, double)} and {@link ZRangeParams#rev()}. * Deprecated in Jedis 7.3.0. Mirrors Redis deprecation since 6.2.0. */ @Deprecated @Override public List zrevrangeByScoreWithScores(final String key, final double max, final double min, final int offset, final int count) { checkIsInMultiOrPipeline(); return connection.executeCommand(commandObjects.zrevrangeByScoreWithScores(key, max, min, offset, count)); } /** * @deprecated Use {@link Jedis#zrangeWithScores(String, ZRangeParams)} with {@link ZRangeParams#zrangeByScoreParams(double, double)} and {@link ZRangeParams#rev()}. * Deprecated in Jedis 7.3.0. Mirrors Redis deprecation since 6.2.0. */ @Deprecated @Override public List zrevrangeByScoreWithScores(final String key, final String max, final String min, final int offset, final int count) { checkIsInMultiOrPipeline(); return connection.executeCommand(commandObjects.zrevrangeByScoreWithScores(key, max, min, offset, count)); } /** * @deprecated Use {@link Jedis#zrange(String, ZRangeParams)} with {@link ZRangeParams#zrangeByScoreParams(double, double)} and {@link ZRangeParams#rev()}. * Deprecated in Jedis 7.3.0. Mirrors Redis deprecation since 6.2.0. */ @Deprecated @Override public List zrevrangeByScore(final String key, final String max, final String min, final int offset, final int count) { checkIsInMultiOrPipeline(); return connection.executeCommand(commandObjects.zrevrangeByScore(key, max, min, offset, count)); } /** * @deprecated Use {@link Jedis#zrangeWithScores(String, ZRangeParams)} with {@link ZRangeParams#zrangeByScoreParams(double, double)} and {@link ZRangeParams#rev()}. * Deprecated in Jedis 7.3.0. Mirrors Redis deprecation since 6.2.0. */ @Deprecated @Override public List zrevrangeByScoreWithScores(final String key, final String max, final String min) { checkIsInMultiOrPipeline(); return connection.executeCommand(commandObjects.zrevrangeByScoreWithScores(key, max, min)); } /** * Remove all elements in the sorted set at key with rank between start and end. Start and end are * 0-based with rank 0 being the element with the lowest score. Both start and end can be negative * numbers, where they indicate offsets starting at the element with the highest rank. For * example: -1 is the element with the highest score, -2 the element with the second highest score * and so forth. *

* Time complexity: O(log(N))+O(M) with N being the number of elements in the sorted set * and M the number of elements removed by the operation * @param key * @param start * @param stop */ @Override public long zremrangeByRank(final String key, final long start, final long stop) { checkIsInMultiOrPipeline(); return connection.executeCommand(commandObjects.zremrangeByRank(key, start, stop)); } /** * Remove all the elements in the sorted set at key with a score between min and max (including * elements with score equal to min or max). *

* Time complexity: *

* O(log(N))+O(M) with N being the number of elements in the sorted set and M the number of * elements removed by the operation * @param key * @param min * @param max * @return The number of elements removed */ @Override public long zremrangeByScore(final String key, final double min, final double max) { checkIsInMultiOrPipeline(); return connection.executeCommand(commandObjects.zremrangeByScore(key, min, max)); } @Override public long zremrangeByScore(final String key, final String min, final String max) { checkIsInMultiOrPipeline(); return connection.executeCommand(commandObjects.zremrangeByScore(key, min, max)); } /** * Add multiple sorted sets, This command is similar to ZUNIONSTORE, but instead of storing the * resulting sorted set, it is returned to the connection. * @param params * @param keys * @return A set with members of the resulting set */ @Override public List zunion(ZParams params, String... keys) { checkIsInMultiOrPipeline(); return connection.executeCommand(commandObjects.zunion(params, keys)); } /** * Add multiple sorted sets with scores, This command is similar to ZUNIONSTORE, but instead of * storing the resulting sorted set, it is returned to the connection. * @param params * @param keys * @return A set with members of the resulting set with scores */ @Override public List zunionWithScores(ZParams params, String... keys) { checkIsInMultiOrPipeline(); return connection.executeCommand(commandObjects.zunionWithScores(params, keys)); } /** * Creates a union or intersection of N sorted sets given by keys k1 through kN, and stores it at * dstkey. It is mandatory to provide the number of input keys N, before passing the input keys * and the other (optional) arguments. *

* As the terms imply, the {@link Jedis#zinterstore(String, String...) ZINTERSTORE} command * requires an element to be present in each of the given inputs to be inserted in the result. The * {@link Jedis#zunionstore(String, String...) ZUNIONSTORE} command inserts all elements across * all inputs. *

* Using the WEIGHTS option, it is possible to add weight to each input sorted set. This means * that the score of each element in the sorted set is first multiplied by this weight before * being passed to the aggregation. When this option is not given, all weights default to 1. *

* With the AGGREGATE option, it is possible to specify how the results of the union or * intersection are aggregated. This option defaults to SUM, where the score of an element is * summed across the inputs where it exists. When this option is set to be either MIN or MAX, the * resulting set will contain the minimum or maximum score of an element across the inputs where * it exists. *

* Time complexity: O(N) + O(M log(M)) with N being the sum of the sizes of the input * sorted sets, and M being the number of elements in the resulting sorted set * @see Jedis#zunionstore(String, String...) * @see Jedis#zunionstore(String, ZParams, String...) * @see Jedis#zinterstore(String, String...) * @see Jedis#zinterstore(String, ZParams, String...) * @param dstkey * @param sets * @return The number of elements in the sorted set at dstkey */ @Override public long zunionstore(final String dstkey, final String... sets) { checkIsInMultiOrPipeline(); return connection.executeCommand(commandObjects.zunionstore(dstkey, sets)); } /** * Creates a union or intersection of N sorted sets given by keys k1 through kN, and stores it at * dstkey. It is mandatory to provide the number of input keys N, before passing the input keys * and the other (optional) arguments. *

* As the terms imply, the {@link Jedis#zinterstore(String, String...) ZINTERSTORE} command * requires an element to be present in each of the given inputs to be inserted in the result. The * {@link Jedis#zunionstore(String, String...) ZUNIONSTORE} command inserts all elements across * all inputs. *

* Using the WEIGHTS option, it is possible to add weight to each input sorted set. This means * that the score of each element in the sorted set is first multiplied by this weight before * being passed to the aggregation. When this option is not given, all weights default to 1. *

* With the AGGREGATE option, it is possible to specify how the results of the union or * intersection are aggregated. This option defaults to SUM, where the score of an element is * summed across the inputs where it exists. When this option is set to be either MIN or MAX, the * resulting set will contain the minimum or maximum score of an element across the inputs where * it exists. *

* Time complexity: O(N) + O(M log(M)) with N being the sum of the sizes of the input * sorted sets, and M being the number of elements in the resulting sorted set * @see Jedis#zunionstore(String, String...) * @see Jedis#zunionstore(String, ZParams, String...) * @see Jedis#zinterstore(String, String...) * @see Jedis#zinterstore(String, ZParams, String...) * @param dstkey * @param sets * @param params * @return The number of elements in the sorted set at dstkey */ @Override public long zunionstore(final String dstkey, final ZParams params, final String... sets) { checkIsInMultiOrPipeline(); return connection.executeCommand(commandObjects.zunionstore(dstkey, params, sets)); } /** * Intersect multiple sorted sets, This command is similar to ZINTERSTORE, but instead of storing * the resulting sorted set, it is returned to the connection. * @param params * @param keys * @return A set with members of the resulting set */ @Override public List zinter(final ZParams params, final String... keys) { checkIsInMultiOrPipeline(); return connection.executeCommand(commandObjects.zinter(params, keys)); } /** * Intersect multiple sorted sets, This command is similar to ZINTERSTORE, but instead of storing * the resulting sorted set, it is returned to the connection. * @param params * @param keys * @return A set with members of the resulting set with scores */ @Override public List zinterWithScores(final ZParams params, final String... keys) { checkIsInMultiOrPipeline(); return connection.executeCommand(commandObjects.zinterWithScores(params, keys)); } @Override public long zintercard(String... keys) { checkIsInMultiOrPipeline(); return connection.executeCommand(commandObjects.zintercard(keys)); } @Override public long zintercard(long limit, String... keys) { checkIsInMultiOrPipeline(); return connection.executeCommand(commandObjects.zintercard(limit, keys)); } /** * Creates a union or intersection of N sorted sets given by keys k1 through kN, and stores it at * dstkey. It is mandatory to provide the number of input keys N, before passing the input keys * and the other (optional) arguments. *

* As the terms imply, the {@link Jedis#zinterstore(String, String...) ZINTERSTORE} command * requires an element to be present in each of the given inputs to be inserted in the result. The * {@link Jedis#zunionstore(String, String...) ZUNIONSTORE} command inserts all elements across * all inputs. *

* Using the WEIGHTS option, it is possible to add weight to each input sorted set. This means * that the score of each element in the sorted set is first multiplied by this weight before * being passed to the aggregation. When this option is not given, all weights default to 1. *

* With the AGGREGATE option, it is possible to specify how the results of the union or * intersection are aggregated. This option defaults to SUM, where the score of an element is * summed across the inputs where it exists. When this option is set to be either MIN or MAX, the * resulting set will contain the minimum or maximum score of an element across the inputs where * it exists. *

* Time complexity: O(N) + O(M log(M)) with N being the sum of the sizes of the input * sorted sets, and M being the number of elements in the resulting sorted set * @see Jedis#zunionstore(String, String...) * @see Jedis#zunionstore(String, ZParams, String...) * @see Jedis#zinterstore(String, String...) * @see Jedis#zinterstore(String, ZParams, String...) * @param dstkey * @param sets * @return The number of elements in the sorted set at dstkey */ @Override public long zinterstore(final String dstkey, final String... sets) { checkIsInMultiOrPipeline(); return connection.executeCommand(commandObjects.zinterstore(dstkey, sets)); } /** * Creates a union or intersection of N sorted sets given by keys k1 through kN, and stores it at * dstkey. It is mandatory to provide the number of input keys N, before passing the input keys * and the other (optional) arguments. *

* As the terms imply, the {@link Jedis#zinterstore(String, String...) ZINTERSTORE} command * requires an element to be present in each of the given inputs to be inserted in the result. The * {@link Jedis#zunionstore(String, String...) ZUNIONSTORE} command inserts all elements across * all inputs. *

* Using the WEIGHTS option, it is possible to add weight to each input sorted set. This means * that the score of each element in the sorted set is first multiplied by this weight before * being passed to the aggregation. When this option is not given, all weights default to 1. *

* With the AGGREGATE option, it is possible to specify how the results of the union or * intersection are aggregated. This option defaults to SUM, where the score of an element is * summed across the inputs where it exists. When this option is set to be either MIN or MAX, the * resulting set will contain the minimum or maximum score of an element across the inputs where * it exists. *

* Time complexity: O(N) + O(M log(M)) with N being the sum of the sizes of the input * sorted sets, and M being the number of elements in the resulting sorted set * @see Jedis#zunionstore(String, String...) * @see Jedis#zunionstore(String, ZParams, String...) * @see Jedis#zinterstore(String, String...) * @see Jedis#zinterstore(String, ZParams, String...) * @param dstkey * @param sets * @param params * @return The number of elements in the sorted set at dstkey */ @Override public long zinterstore(final String dstkey, final ZParams params, final String... sets) { checkIsInMultiOrPipeline(); return connection.executeCommand(commandObjects.zinterstore(dstkey, params, sets)); } @Override public long zlexcount(final String key, final String min, final String max) { checkIsInMultiOrPipeline(); return connection.executeCommand(commandObjects.zlexcount(key, min, max)); } /** * @deprecated Use {@link Jedis#zrange(String, ZRangeParams)} with {@link ZRangeParams#zrangeByLexParams(String, String)}. * Deprecated in Jedis 7.3.0. Mirrors Redis deprecation since 6.2.0. */ @Deprecated @Override public List zrangeByLex(final String key, final String min, final String max) { checkIsInMultiOrPipeline(); return connection.executeCommand(commandObjects.zrangeByLex(key, min, max)); } /** * @deprecated Use {@link Jedis#zrange(String, ZRangeParams)} with {@link ZRangeParams#zrangeByLexParams(String, String)}. * Deprecated in Jedis 7.3.0. Mirrors Redis deprecation since 6.2.0. */ @Deprecated @Override public List zrangeByLex(final String key, final String min, final String max, final int offset, final int count) { checkIsInMultiOrPipeline(); return connection.executeCommand(commandObjects.zrangeByLex(key, min, max, offset, count)); } /** * @deprecated Use {@link Jedis#zrange(String, ZRangeParams)} with {@link ZRangeParams#zrangeByLexParams(String, String)} and {@link ZRangeParams#rev()}. * Deprecated in Jedis 7.3.0. Mirrors Redis deprecation since 6.2.0. */ @Deprecated @Override public List zrevrangeByLex(final String key, final String max, final String min) { checkIsInMultiOrPipeline(); return connection.executeCommand(commandObjects.zrevrangeByLex(key, max, min)); } /** * @deprecated Use {@link Jedis#zrange(String, ZRangeParams)} with {@link ZRangeParams#zrangeByLexParams(String, String)} and {@link ZRangeParams#rev()}. * Deprecated in Jedis 7.3.0. Mirrors Redis deprecation since 6.2.0. */ @Deprecated @Override public List zrevrangeByLex(final String key, final String max, final String min, final int offset, final int count) { checkIsInMultiOrPipeline(); return connection.executeCommand(commandObjects.zrevrangeByLex(key, max, min, offset, count)); } @Override public long zremrangeByLex(final String key, final String min, final String max) { checkIsInMultiOrPipeline(); return connection.executeCommand(commandObjects.zremrangeByLex(key, min, max)); } @Override public KeyValue> zmpop(SortedSetOption option, String... keys) { checkIsInMultiOrPipeline(); return connection.executeCommand(commandObjects.zmpop(option, keys)); } @Override public KeyValue> zmpop(SortedSetOption option, int count, String... keys) { checkIsInMultiOrPipeline(); return connection.executeCommand(commandObjects.zmpop(option, count, keys)); } @Override public KeyValue> bzmpop(double timeout, SortedSetOption option, String... keys) { checkIsInMultiOrPipeline(); return connection.executeCommand(commandObjects.bzmpop(timeout, option, keys)); } @Override public KeyValue> bzmpop(double timeout, SortedSetOption option, int count, String... keys) { checkIsInMultiOrPipeline(); return connection.executeCommand(commandObjects.bzmpop(timeout, option, count, keys)); } @Override public long strlen(final String key) { checkIsInMultiOrPipeline(); return connection.executeCommand(commandObjects.strlen(key)); } /** * Calculate the longest common subsequence of keyA and keyB. * @param keyA * @param keyB * @param params * @return According to LCSParams to decide to return content to fill LCSMatchResult. */ @Override public LCSMatchResult lcs(final String keyA, final String keyB, final LCSParams params) { checkIsInMultiOrPipeline(); return connection.executeCommand(commandObjects.lcs(keyA, keyB, params)); } @Override public long lpushx(final String key, final String... strings) { checkIsInMultiOrPipeline(); return connection.executeCommand(commandObjects.lpushx(key, strings)); } /** * Undo a {@link Jedis#expire(String, long) expire} at turning the expire key into a normal key. *

* Time complexity: O(1) * @param key * @return 1 if the key is now persist, 0 if the key is not persist (only happens when key not set) */ @Override public long persist(final String key) { checkIsInMultiOrPipeline(); return connection.executeCommand(commandObjects.persist(key)); } @Override public long rpushx(final String key, final String... strings) { checkIsInMultiOrPipeline(); return connection.executeCommand(commandObjects.rpushx(key, strings)); } @Override public String echo(final String string) { checkIsInMultiOrPipeline(); connection.sendCommand(ECHO, string); return connection.getBulkReply(); } @Override public long linsert(final String key, final ListPosition where, final String pivot, final String value) { checkIsInMultiOrPipeline(); return connection.executeCommand(commandObjects.linsert(key, where, pivot, value)); } /** * Pop a value from a list, push it to another list and return it; or block until one is available * @param source * @param destination * @param timeout * @return The element * @deprecated Use {@link Jedis#blmove(String, String, ListDirection, ListDirection, double)} with * {@link ListDirection#RIGHT} and {@link ListDirection#LEFT}. * Deprecated in Jedis 7.3.0. Mirrors Redis deprecation since 6.2.0. */ @Deprecated @Override public String brpoplpush(final String source, final String destination, final int timeout) { checkIsInMultiOrPipeline(); return connection.executeCommand(commandObjects.brpoplpush(source, destination, timeout)); } /** * Sets or clears the bit at offset in the string value stored at key * @param key * @param offset * @param value */ @Override public boolean setbit(final String key, final long offset, final boolean value) { checkIsInMultiOrPipeline(); return connection.executeCommand(commandObjects.setbit(key, offset, value)); } /** * Returns the bit value at offset in the string value stored at key * @param key * @param offset */ @Override public boolean getbit(final String key, final long offset) { checkIsInMultiOrPipeline(); return connection.executeCommand(commandObjects.getbit(key, offset)); } @Override public long setrange(final String key, final long offset, final String value) { checkIsInMultiOrPipeline(); return connection.executeCommand(commandObjects.setrange(key, offset, value)); } @Override public String getrange(final String key, final long startOffset, final long endOffset) { checkIsInMultiOrPipeline(); return connection.executeCommand(commandObjects.getrange(key, startOffset, endOffset)); } @Override public long bitpos(final String key, final boolean value) { checkIsInMultiOrPipeline(); return connection.executeCommand(commandObjects.bitpos(key, value)); } @Override public long bitpos(final String key, final boolean value, final BitPosParams params) { checkIsInMultiOrPipeline(); return connection.executeCommand(commandObjects.bitpos(key, value, params)); } @Override public List role() { checkIsInMultiOrPipeline(); connection.sendCommand(ROLE); return BuilderFactory.ENCODED_OBJECT_LIST.build(connection.getOne()); } /** * Retrieve the configuration of a running Redis server. Not all the configuration parameters are * supported. *

* CONFIG GET returns the current configuration parameters. This sub command only accepts a single * argument, that is glob style pattern. All the configuration parameters matching this parameter * are reported as a list of key-value pairs. *

* Example: * *

   * $ redis-cli config get '*'
   * 1. "dbfilename"
   * 2. "dump.rdb"
   * 3. "requirepass"
   * 4. (nil)
   * 5. "masterauth"
   * 6. (nil)
   * 7. "maxmemory"
   * 8. "0\n"
   * 9. "appendfsync"
   * 10. "everysec"
   * 11. "save"
   * 12. "3600 1 300 100 60 10000"
   *
   * $ redis-cli config get 'm*'
   * 1. "masterauth"
   * 2. (nil)
   * 3. "maxmemory"
   * 4. "0\n"
   * 
* @param pattern * @return Bulk reply. */ @Override public Map configGet(final String pattern) { checkIsInMultiOrPipeline(); connection.sendCommand(Command.CONFIG, Keyword.GET.name(), pattern); return BuilderFactory.STRING_MAP.build(connection.getOne()); } @Override public Map configGet(String... patterns) { checkIsInMultiOrPipeline(); connection.sendCommand(Command.CONFIG, joinParameters(Keyword.GET.name(), patterns)); return BuilderFactory.STRING_MAP.build(connection.getOne()); } /** * Alter the configuration of a running Redis server. Not all the configuration parameters are * supported. *

* The list of configuration parameters supported by CONFIG SET can be obtained issuing a * {@link Jedis#configGet(String) CONFIG GET *} command. *

* The configuration set using CONFIG SET is immediately loaded by the Redis server that will * start acting as specified starting from the next command. *

* Parameters value format *

* The value of the configuration parameter is the same as the one of the same parameter in the * Redis configuration file, with the following exceptions: *

*

    *
  • The save parameter is a list of space-separated integers. Every pair of integers specify * the time and number of changes limit to trigger a save. For instance the command CONFIG SET * save "3600 10 60 10000" will configure the server to issue a background saving of the RDB file * every 3600 seconds if there are at least 10 changes in the dataset, and every 60 seconds if * there are at least 10000 changes. To completely disable automatic snapshots just set the * parameter as an empty string. *
  • All the integer parameters representing memory are returned and accepted only using bytes * as unit. *
* @param parameter * @param value * @return OK */ @Override public String configSet(final String parameter, final String value) { checkIsInMultiOrPipeline(); connection.sendCommand(Command.CONFIG, Keyword.SET.name(), parameter, value); return connection.getStatusCodeReply(); } @Override public String configSet(final String... parameterValues) { checkIsInMultiOrPipeline(); connection.sendCommand(Command.CONFIG, joinParameters(Keyword.SET.name(), parameterValues)); return connection.getStatusCodeReply(); } @Override public String configSet(Map parameterValues) { checkIsInMultiOrPipeline(); CommandArguments args = new CommandArguments(Command.CONFIG).add(Keyword.SET); parameterValues.forEach((k, v) -> args.add(k).add(v)); connection.sendCommand(args); return connection.getStatusCodeReply(); } public long publish(final String channel, final String message) { checkIsInMultiOrPipeline(); connection.sendCommand(PUBLISH, channel, message); return connection.getIntegerReply(); } public void subscribe(final JedisPubSub jedisPubSub, final String... channels) { jedisPubSub.proceed(connection, channels); } public void psubscribe(final JedisPubSub jedisPubSub, final String... patterns) { jedisPubSub.proceedWithPatterns(connection, patterns); } public List pubsubChannels() { checkIsInMultiOrPipeline(); connection.sendCommand(PUBSUB, CHANNELS); return connection.getMultiBulkReply(); } public List pubsubChannels(final String pattern) { checkIsInMultiOrPipeline(); connection.sendCommand(PUBSUB, CHANNELS.name(), pattern); return connection.getMultiBulkReply(); } public Long pubsubNumPat() { checkIsInMultiOrPipeline(); connection.sendCommand(PUBSUB, NUMPAT); return connection.getIntegerReply(); } public Map pubsubNumSub(String... channels) { checkIsInMultiOrPipeline(); connection.sendCommand(PUBSUB, joinParameters(NUMSUB.name(), channels)); return BuilderFactory.PUBSUB_NUMSUB_MAP.build(connection.getOne()); } public List pubsubShardChannels() { checkIsInMultiOrPipeline(); connection.sendCommand(PUBSUB, SHARDCHANNELS); return connection.getMultiBulkReply(); } public List pubsubShardChannels(final String pattern) { checkIsInMultiOrPipeline(); connection.sendCommand(PUBSUB, SHARDCHANNELS.name(), pattern); return connection.getMultiBulkReply(); } public Map pubsubShardNumSub(String... channels) { checkIsInMultiOrPipeline(); connection.sendCommand(PUBSUB, joinParameters(SHARDNUMSUB.name(), channels)); return BuilderFactory.PUBSUB_NUMSUB_MAP.build(connection.getOne()); } @Override public Object eval(final String script, final int keyCount, final String... params) { checkIsInMultiOrPipeline(); return connection.executeCommand(commandObjects.eval(script, keyCount, params)); } @Override public Object eval(final String script, final List keys, final List args) { checkIsInMultiOrPipeline(); return connection.executeCommand(commandObjects.eval(script, keys, args)); } @Override public Object evalReadonly(String script, List keys, List args) { checkIsInMultiOrPipeline(); return connection.executeCommand(commandObjects.evalReadonly(script, keys, args)); } @Override public Object eval(final String script) { checkIsInMultiOrPipeline(); return connection.executeCommand(commandObjects.eval(script)); } @Override public Object evalsha(final String sha1) { checkIsInMultiOrPipeline(); return connection.executeCommand(commandObjects.evalsha(sha1)); } @Override public Object evalsha(final String sha1, final List keys, final List args) { checkIsInMultiOrPipeline(); return connection.executeCommand(commandObjects.evalsha(sha1, keys, args)); } @Override public Object evalshaReadonly(String sha1, List keys, List args) { checkIsInMultiOrPipeline(); return connection.executeCommand(commandObjects.evalshaReadonly(sha1, keys, args)); } @Override public Object evalsha(final String sha1, final int keyCount, final String... params) { checkIsInMultiOrPipeline(); return connection.executeCommand(commandObjects.evalsha(sha1, keyCount, params)); } @Override public Boolean scriptExists(final String sha1) { String[] a = new String[1]; a[0] = sha1; return scriptExists(a).get(0); } @Override public List scriptExists(final String... sha1) { connection.sendCommand(SCRIPT, joinParameters(Keyword.EXISTS.name(), sha1)); return BuilderFactory.BOOLEAN_LIST.build(connection.getOne()); } @Override public String scriptLoad(final String script) { connection.sendCommand(SCRIPT, LOAD.name(), script); return connection.getBulkReply(); } @Override public List slowlogGet() { connection.sendCommand(SLOWLOG, Keyword.GET); return Slowlog.from(connection.getObjectMultiBulkReply()); } @Override public List slowlogGet(final long entries) { connection.sendCommand(SLOWLOG, Keyword.GET.getRaw(), toByteArray(entries)); return Slowlog.from(connection.getObjectMultiBulkReply()); } @Override public Long objectRefcount(final String key) { connection.sendCommand(OBJECT, REFCOUNT.name(), key); return connection.getIntegerReply(); } @Override public String objectEncoding(final String key) { connection.sendCommand(OBJECT, ENCODING.name(), key); return connection.getBulkReply(); } @Override public Long objectIdletime(final String key) { connection.sendCommand(OBJECT, IDLETIME.name(), key); return connection.getIntegerReply(); } @Override public List objectHelp() { connection.sendCommand(OBJECT, HELP); return connection.getMultiBulkReply(); } @Override public Long objectFreq(final String key) { connection.sendCommand(OBJECT, FREQ.name(), key); return connection.getIntegerReply(); } @Override public long bitcount(final String key) { checkIsInMultiOrPipeline(); return connection.executeCommand(commandObjects.bitcount(key)); } @Override public long bitcount(final String key, final long start, final long end) { checkIsInMultiOrPipeline(); return connection.executeCommand(commandObjects.bitcount(key, start, end)); } @Override public long bitcount(final String key, final long start, final long end, final BitCountOption option) { checkIsInMultiOrPipeline(); return connection.executeCommand(commandObjects.bitcount(key, start, end, option)); } @Override public long bitop(final BitOP op, final String destKey, final String... srcKeys) { checkIsInMultiOrPipeline(); return connection.executeCommand(commandObjects.bitop(op, destKey, srcKeys)); } @Override public long commandCount() { checkIsInMultiOrPipeline(); connection.sendCommand(COMMAND, COUNT); return connection.getIntegerReply(); } @Override public Map commandDocs(String... commands) { checkIsInMultiOrPipeline(); connection.sendCommand(COMMAND, joinParameters(DOCS.name(), commands)); return BuilderFactory.COMMAND_DOCS_RESPONSE.build(connection.getOne()); } @Override public List commandGetKeys(String... command) { checkIsInMultiOrPipeline(); connection.sendCommand(COMMAND, joinParameters(GETKEYS.name(), command)); return BuilderFactory.STRING_LIST.build(connection.getOne()); } @Override public List>> commandGetKeysAndFlags(String... command) { checkIsInMultiOrPipeline(); connection.sendCommand(COMMAND, joinParameters(GETKEYSANDFLAGS.name(), command)); return BuilderFactory.KEYED_STRING_LIST_LIST.build(connection.getOne()); } @Override public Map commandInfo(String... commands) { checkIsInMultiOrPipeline(); connection.sendCommand(COMMAND, joinParameters(Keyword.INFO.name(), commands)); return CommandInfo.COMMAND_INFO_RESPONSE.build(connection.getOne()); } @Override public Map command() { checkIsInMultiOrPipeline(); connection.sendCommand(COMMAND); return CommandInfo.COMMAND_INFO_RESPONSE.build(connection.getOne()); } @Override public List commandList() { checkIsInMultiOrPipeline(); connection.sendCommand(COMMAND, LIST); return BuilderFactory.STRING_LIST.build(connection.getOne()); } @Override public List commandListFilterBy(CommandListFilterByParams filterByParams) { checkIsInMultiOrPipeline(); CommandArguments args = new CommandArguments(COMMAND).add(LIST).addParams(filterByParams); connection.sendCommand(args); return BuilderFactory.STRING_LIST.build(connection.getOne()); } @Override public String sentinelMyId() { connection.sendCommand(SENTINEL, MYID); return connection.getBulkReply(); } /** *
   * redis 127.0.0.1:26381> sentinel masters
   * 1)  1) "name"
   *     2) "mymaster"
   *     3) "ip"
   *     4) "127.0.0.1"
   *     5) "port"
   *     6) "6379"
   *     7) "runid"
   *     8) "93d4d4e6e9c06d0eea36e27f31924ac26576081d"
   *     9) "flags"
   *    10) "master"
   *    11) "pending-commands"
   *    12) "0"
   *    13) "last-ok-ping-reply"
   *    14) "423"
   *    15) "last-ping-reply"
   *    16) "423"
   *    17) "info-refresh"
   *    18) "6107"
   *    19) "num-slaves"
   *    20) "1"
   *    21) "num-other-sentinels"
   *    22) "2"
   *    23) "quorum"
   *    24) "2"
   *
   * 
*/ @Override public List> sentinelMasters() { connection.sendCommand(SENTINEL, MASTERS); return connection.getObjectMultiBulkReply().stream() .map(BuilderFactory.STRING_MAP::build).collect(Collectors.toList()); } @Override public Map sentinelMaster(String masterName) { connection.sendCommand(SENTINEL, MASTER.name(), masterName); return BuilderFactory.STRING_MAP.build(connection.getOne()); } @Override public List> sentinelSentinels(String masterName) { connection.sendCommand(SENTINEL, SENTINELS.name(), masterName); return connection.getObjectMultiBulkReply().stream() .map(BuilderFactory.STRING_MAP::build).collect(Collectors.toList()); } /** *
   * redis 127.0.0.1:26381> sentinel get-master-addr-by-name mymaster
   * 1) "127.0.0.1"
   * 2) "6379"
   * 
* @param masterName * @return two elements list of strings : host and port. */ @Override public List sentinelGetMasterAddrByName(String masterName) { connection.sendCommand(SENTINEL, GET_MASTER_ADDR_BY_NAME.getRaw(), encode(masterName)); return connection.getMultiBulkReply(); } /** *
   * redis 127.0.0.1:26381> sentinel reset mymaster
   * (integer) 1
   * 
* @param pattern */ @Override public Long sentinelReset(String pattern) { connection.sendCommand(SENTINEL, SentinelKeyword.RESET.name(), pattern); return connection.getIntegerReply(); } /** *
   * redis 127.0.0.1:26381> sentinel slaves mymaster
   * 1)  1) "name"
   *     2) "127.0.0.1:6380"
   *     3) "ip"
   *     4) "127.0.0.1"
   *     5) "port"
   *     6) "6380"
   *     7) "runid"
   *     8) "d7f6c0ca7572df9d2f33713df0dbf8c72da7c039"
   *     9) "flags"
   *    10) "slave"
   *    11) "pending-commands"
   *    12) "0"
   *    13) "last-ok-ping-reply"
   *    14) "47"
   *    15) "last-ping-reply"
   *    16) "47"
   *    17) "info-refresh"
   *    18) "657"
   *    19) "master-link-down-time"
   *    20) "0"
   *    21) "master-link-status"
   *    22) "ok"
   *    23) "master-host"
   *    24) "localhost"
   *    25) "master-port"
   *    26) "6379"
   *    27) "slave-priority"
   *    28) "100"
   * 
* @param masterName */ @Override @Deprecated public List> sentinelSlaves(String masterName) { connection.sendCommand(SENTINEL, SLAVES.name(), masterName); return connection.getObjectMultiBulkReply().stream() .map(BuilderFactory.STRING_MAP::build).collect(Collectors.toList()); } @Override public List> sentinelReplicas(String masterName) { connection.sendCommand(SENTINEL, REPLICAS.name(), masterName); return connection.getObjectMultiBulkReply().stream() .map(BuilderFactory.STRING_MAP::build).collect(Collectors.toList()); } @Override public String sentinelFailover(String masterName) { connection.sendCommand(SENTINEL, SentinelKeyword.FAILOVER.name(), masterName); return connection.getStatusCodeReply(); } @Override public String sentinelMonitor(String masterName, String ip, int port, int quorum) { CommandArguments args = new CommandArguments(SENTINEL).add(SentinelKeyword.MONITOR) .add(masterName).add(ip).add(port).add(quorum); connection.sendCommand(args); return connection.getStatusCodeReply(); } @Override public String sentinelRemove(String masterName) { connection.sendCommand(SENTINEL, REMOVE.name(), masterName); return connection.getStatusCodeReply(); } @Override public String sentinelSet(String masterName, Map parameterMap) { CommandArguments args = new CommandArguments(SENTINEL).add(SentinelKeyword.SET).add(masterName); parameterMap.entrySet().forEach(entry -> args.add(entry.getKey()).add(entry.getValue())); connection.sendCommand(args); return connection.getStatusCodeReply(); } @Override public byte[] dump(final String key) { checkIsInMultiOrPipeline(); return connection.executeCommand(commandObjects.dump(key)); } @Override public String restore(final String key, final long ttl, final byte[] serializedValue) { checkIsInMultiOrPipeline(); return connection.executeCommand(commandObjects.restore(key, ttl, serializedValue)); } @Override public String restore(final String key, final long ttl, final byte[] serializedValue, final RestoreParams params) { checkIsInMultiOrPipeline(); return connection.executeCommand(commandObjects.restore(key, ttl, serializedValue, params)); } @Override public long pttl(final String key) { checkIsInMultiOrPipeline(); return connection.executeCommand(commandObjects.pttl(key)); } /** * PSETEX works exactly like {@link Jedis#setex(String, long, String)} with the sole difference * that the expire time is specified in milliseconds instead of seconds. Time complexity: O(1) * @param key * @param milliseconds * @param value * @return OK * @deprecated Use {@link Jedis#set(String, String, redis.clients.jedis.params.SetParams)} with {@link redis.clients.jedis.params.SetParams#px(long)}. * Deprecated in Jedis 7.3.0. Mirrors Redis deprecation since 2.6.12. */ @Deprecated @Override public String psetex(final String key, final long milliseconds, final String value) { checkIsInMultiOrPipeline(); return connection.executeCommand(commandObjects.psetex(key, milliseconds, value)); } @Override public String aclSetUser(final String name) { checkIsInMultiOrPipeline(); connection.sendCommand(ACL, SETUSER.name(), name); return connection.getStatusCodeReply(); } @Override public String aclSetUser(String name, String... rules) { checkIsInMultiOrPipeline(); connection.sendCommand(ACL, joinParameters(SETUSER.name(), name, rules)); return connection.getStatusCodeReply(); } @Override public long aclDelUser(final String... names) { checkIsInMultiOrPipeline(); connection.sendCommand(ACL, joinParameters(DELUSER.name(), names)); return connection.getIntegerReply(); } @Override public AccessControlUser aclGetUser(final String name) { checkIsInMultiOrPipeline(); connection.sendCommand(ACL, GETUSER.name(), name); return BuilderFactory.ACCESS_CONTROL_USER.build(connection.getOne()); } @Override public List aclUsers() { checkIsInMultiOrPipeline(); connection.sendCommand(ACL, USERS); return BuilderFactory.STRING_LIST.build(connection.getObjectMultiBulkReply()); } @Override public List aclList() { checkIsInMultiOrPipeline(); connection.sendCommand(ACL, LIST); return connection.getMultiBulkReply(); } @Override public String aclWhoAmI() { checkIsInMultiOrPipeline(); connection.sendCommand(ACL, WHOAMI); return connection.getStatusCodeReply(); } @Override public List aclCat() { checkIsInMultiOrPipeline(); connection.sendCommand(ACL, CAT); return BuilderFactory.STRING_LIST.build(connection.getOne()); } @Override public List aclCat(String category) { checkIsInMultiOrPipeline(); connection.sendCommand(ACL, CAT.name(), category); return BuilderFactory.STRING_LIST.build(connection.getOne()); } @Override public List aclLog() { checkIsInMultiOrPipeline(); connection.sendCommand(ACL, LOG); return BuilderFactory.ACCESS_CONTROL_LOG_ENTRY_LIST.build(connection.getOne()); } @Override public List aclLog(int limit) { checkIsInMultiOrPipeline(); connection.sendCommand(ACL, LOG.getRaw(), toByteArray(limit)); return BuilderFactory.ACCESS_CONTROL_LOG_ENTRY_LIST.build(connection.getOne()); } @Override public String aclLoad() { checkIsInMultiOrPipeline(); connection.sendCommand(ACL, LOAD); return connection.getStatusCodeReply(); } @Override public String aclSave() { checkIsInMultiOrPipeline(); connection.sendCommand(ACL, Keyword.SAVE); return connection.getStatusCodeReply(); } @Override public String aclGenPass() { connection.sendCommand(ACL, GENPASS); return connection.getBulkReply(); } @Override public String aclGenPass(int bits) { checkIsInMultiOrPipeline(); connection.sendCommand(ACL, GENPASS.getRaw(), toByteArray(bits)); return connection.getBulkReply(); } @Override public String aclDryRun(String username, String command, String... args) { checkIsInMultiOrPipeline(); String[] allArgs = new String[3 + args.length]; allArgs[0] = DRYRUN.name(); allArgs[1] = username; allArgs[2] = command; System.arraycopy(args, 0, allArgs, 3, args.length); connection.sendCommand(ACL, allArgs); return connection.getBulkReply(); } @Override public String aclDryRun(String username, CommandArguments commandArgs) { checkIsInMultiOrPipeline(); CommandArguments allArgs = new CommandArguments(ACL).add(DRYRUN).add(username); Iterator it = commandArgs.iterator(); while (it.hasNext()) allArgs.add(it.next()); connection.sendCommand(allArgs); return connection.getBulkReply(); } @Override public byte[] aclDryRunBinary(byte[] username, byte[] command, byte[]... args) { checkIsInMultiOrPipeline(); byte[][] allArgs = new byte[3 + args.length][]; allArgs[0] = DRYRUN.getRaw(); allArgs[1] = username; allArgs[2] = command; System.arraycopy(args, 0, allArgs, 3, args.length); connection.sendCommand(ACL, allArgs); return connection.getBinaryBulkReply(); } @Override public byte[] aclDryRunBinary(byte[] username, CommandArguments commandArgs) { checkIsInMultiOrPipeline(); CommandArguments allArgs = new CommandArguments(ACL).add(DRYRUN).add(username); Iterator it = commandArgs.iterator(); while (it.hasNext()) allArgs.add(it.next()); connection.sendCommand(allArgs); return connection.getBinaryBulkReply(); } @Override public String clientKill(final String ipPort) { checkIsInMultiOrPipeline(); connection.sendCommand(CLIENT, KILL.name(), ipPort); return connection.getStatusCodeReply(); } @Override public String clientGetname() { checkIsInMultiOrPipeline(); connection.sendCommand(CLIENT, GETNAME); return connection.getBulkReply(); } @Override public String clientList() { checkIsInMultiOrPipeline(); connection.sendCommand(CLIENT, LIST); return connection.getBulkReply(); } @Override public String clientList(ClientType type) { checkIsInMultiOrPipeline(); connection.sendCommand(CLIENT, LIST.getRaw(), Keyword.TYPE.getRaw(), type.getRaw()); return connection.getBulkReply(); } @Override public String clientList(final long... clientIds) { checkIsInMultiOrPipeline(); connection.sendCommand(CLIENT, clientListParams(clientIds)); return connection.getBulkReply(); } @Override public String clientInfo() { checkIsInMultiOrPipeline(); connection.sendCommand(CLIENT, Keyword.INFO); return connection.getBulkReply(); } @Override public String clientSetInfo(ClientAttributeOption attr, String value) { checkIsInMultiOrPipeline(); connection.sendCommand(CLIENT, SETINFO.getRaw(), attr.getRaw(), encode(value)); return connection.getStatusCodeReply(); } @Override public String clientSetname(final String name) { checkIsInMultiOrPipeline(); connection.sendCommand(CLIENT, SETNAME.name(), name); return connection.getStatusCodeReply(); } @Override public String migrate(final String host, final int port, final String key, final int destinationDb, final int timeout) { checkIsInMultiOrPipeline(); return connection.executeCommand(commandObjects.migrate(host, port, key, destinationDb, timeout)); } @Override public String migrate(final String host, final int port, final int destinationDB, final int timeout, final MigrateParams params, final String... keys) { checkIsInMultiOrPipeline(); return connection.executeCommand(commandObjects.migrate(host, port, destinationDB, timeout, params, keys)); } @Override public String migrate(String host, int port, String key, int timeout) { checkIsInMultiOrPipeline(); return connection.executeCommand(commandObjects.migrate(host, port, key, timeout)); } @Override public String migrate(String host, int port, int timeout, MigrateParams params, String... keys) { checkIsInMultiOrPipeline(); return connection.executeCommand(commandObjects.migrate(host, port, timeout, params, keys)); } @Override public ScanResult scan(final String cursor) { return connection.executeCommand(commandObjects.scan(cursor)); } @Override public ScanResult scan(final String cursor, final ScanParams params) { return connection.executeCommand(commandObjects.scan(cursor, params)); } @Override public ScanResult scan(final String cursor, final ScanParams params, final String type) { checkIsInMultiOrPipeline(); return connection.executeCommand(commandObjects.scan(cursor, params, type)); } @Override public ScanResult> hscan(final String key, final String cursor, final ScanParams params) { checkIsInMultiOrPipeline(); return connection.executeCommand(commandObjects.hscan(key, cursor, params)); } @Override public ScanResult hscanNoValues(final String key, final String cursor, final ScanParams params) { checkIsInMultiOrPipeline(); return connection.executeCommand(commandObjects.hscanNoValues(key, cursor, params)); } @Override public ScanResult sscan(final String key, final String cursor, final ScanParams params) { checkIsInMultiOrPipeline(); return connection.executeCommand(commandObjects.sscan(key, cursor, params)); } @Override public ScanResult zscan(final String key, final String cursor, final ScanParams params) { checkIsInMultiOrPipeline(); return connection.executeCommand(commandObjects.zscan(key, cursor, params)); } @Override public String readonly() { checkIsInMultiOrPipeline(); connection.sendCommand(READONLY); return connection.getStatusCodeReply(); } @Override public String readwrite() { checkIsInMultiOrPipeline(); connection.sendCommand(READWRITE); return connection.getStatusCodeReply(); } @Override public String clusterNodes() { checkIsInMultiOrPipeline(); connection.sendCommand(CLUSTER, ClusterKeyword.NODES); return connection.getBulkReply(); } @Override public String clusterMeet(final String ip, final int port) { checkIsInMultiOrPipeline(); connection.sendCommand(CLUSTER, ClusterKeyword.MEET.name(), ip, Integer.toString(port)); return connection.getStatusCodeReply(); } @Override public String clusterReset() { checkIsInMultiOrPipeline(); connection.sendCommand(CLUSTER, ClusterKeyword.RESET); return connection.getStatusCodeReply(); } @Override public String clusterReset(final ClusterResetType resetType) { checkIsInMultiOrPipeline(); connection.sendCommand(CLUSTER, ClusterKeyword.RESET.getRaw(), resetType.getRaw()); return connection.getStatusCodeReply(); } @Override public String clusterAddSlots(final int... slots) { checkIsInMultiOrPipeline(); connection.sendCommand(CLUSTER, joinParameters(ClusterKeyword.ADDSLOTS.getRaw(), joinParameters(slots))); return connection.getStatusCodeReply(); } @Override public String clusterDelSlots(final int... slots) { checkIsInMultiOrPipeline(); connection.sendCommand(CLUSTER, joinParameters(ClusterKeyword.DELSLOTS.getRaw(), joinParameters(slots))); return connection.getStatusCodeReply(); } @Override public String clusterInfo() { checkIsInMultiOrPipeline(); connection.sendCommand(CLUSTER, ClusterKeyword.INFO); return connection.getStatusCodeReply(); } @Override public List clusterGetKeysInSlot(final int slot, final int count) { checkIsInMultiOrPipeline(); connection.sendCommand(CLUSTER, ClusterKeyword.GETKEYSINSLOT.getRaw(), toByteArray(slot), toByteArray(count)); return connection.getMultiBulkReply(); } @Override public List clusterGetKeysInSlotBinary(final int slot, final int count) { checkIsInMultiOrPipeline(); connection.sendCommand(CLUSTER, ClusterKeyword.GETKEYSINSLOT.getRaw(), toByteArray(slot), toByteArray(count)); return connection.getBinaryMultiBulkReply(); } @Override public String clusterSetSlotNode(final int slot, final String nodeId) { checkIsInMultiOrPipeline(); connection.sendCommand(CLUSTER, ClusterKeyword.SETSLOT.getRaw(), toByteArray(slot), ClusterKeyword.NODE.getRaw(), encode(nodeId)); return connection.getStatusCodeReply(); } @Override public String clusterSetSlotMigrating(final int slot, final String nodeId) { checkIsInMultiOrPipeline(); connection.sendCommand(CLUSTER, ClusterKeyword.SETSLOT.getRaw(), toByteArray(slot), ClusterKeyword.MIGRATING.getRaw(), encode(nodeId)); return connection.getStatusCodeReply(); } @Override public String clusterSetSlotImporting(final int slot, final String nodeId) { checkIsInMultiOrPipeline(); connection.sendCommand(CLUSTER, ClusterKeyword.SETSLOT.getRaw(), toByteArray(slot), ClusterKeyword.IMPORTING.getRaw(), encode(nodeId)); return connection.getStatusCodeReply(); } @Override public String clusterSetSlotStable(final int slot) { checkIsInMultiOrPipeline(); connection.sendCommand(CLUSTER, ClusterKeyword.SETSLOT.getRaw(), toByteArray(slot), ClusterKeyword.STABLE.getRaw()); return connection.getStatusCodeReply(); } @Override public String clusterForget(final String nodeId) { checkIsInMultiOrPipeline(); connection.sendCommand(CLUSTER, ClusterKeyword.FORGET.name(), nodeId); return connection.getStatusCodeReply(); } @Override public String clusterFlushSlots() { checkIsInMultiOrPipeline(); connection.sendCommand(CLUSTER, ClusterKeyword.FLUSHSLOTS); return connection.getStatusCodeReply(); } @Override public long clusterKeySlot(final String key) { checkIsInMultiOrPipeline(); connection.sendCommand(CLUSTER, ClusterKeyword.KEYSLOT.name(), key); return connection.getIntegerReply(); } @Override public long clusterCountFailureReports(final String nodeId) { checkIsInMultiOrPipeline(); connection.sendCommand(CLUSTER, "COUNT-FAILURE-REPORTS", nodeId); return connection.getIntegerReply(); } @Override public long clusterCountKeysInSlot(final int slot) { checkIsInMultiOrPipeline(); connection.sendCommand(CLUSTER, ClusterKeyword.COUNTKEYSINSLOT.getRaw(), toByteArray(slot)); return connection.getIntegerReply(); } @Override public String clusterSaveConfig() { checkIsInMultiOrPipeline(); connection.sendCommand(CLUSTER, ClusterKeyword.SAVECONFIG); return connection.getStatusCodeReply(); } @Override public String clusterSetConfigEpoch(long configEpoch) { checkIsInMultiOrPipeline(); connection.sendCommand(CLUSTER, "SET-CONFIG-EPOCH", Long.toString(configEpoch)); return connection.getStatusCodeReply(); } @Override public String clusterBumpEpoch() { checkIsInMultiOrPipeline(); connection.sendCommand(CLUSTER, ClusterKeyword.BUMPEPOCH); return connection.getBulkReply(); } @Override public String clusterReplicate(final String nodeId) { checkIsInMultiOrPipeline(); connection.sendCommand(CLUSTER, ClusterKeyword.REPLICATE.name(), nodeId); return connection.getStatusCodeReply(); } @Override @Deprecated public List clusterSlaves(final String nodeId) { checkIsInMultiOrPipeline(); connection.sendCommand(CLUSTER, ClusterKeyword.SLAVES.name(), nodeId); return connection.getMultiBulkReply(); } @Override public List clusterReplicas(final String nodeId) { checkIsInMultiOrPipeline(); connection.sendCommand(CLUSTER, ClusterKeyword.REPLICAS.name(), nodeId); return connection.getMultiBulkReply(); } @Override public String clusterFailover() { checkIsInMultiOrPipeline(); connection.sendCommand(CLUSTER, ClusterKeyword.FAILOVER); return connection.getStatusCodeReply(); } @Override public String clusterFailover(ClusterFailoverOption failoverOption) { checkIsInMultiOrPipeline(); connection.sendCommand(CLUSTER, ClusterKeyword.FAILOVER.getRaw(), failoverOption.getRaw()); return connection.getStatusCodeReply(); } @Override @Deprecated public List clusterSlots() { checkIsInMultiOrPipeline(); connection.sendCommand(CLUSTER, ClusterKeyword.SLOTS); return connection.getObjectMultiBulkReply(); } @Override public List clusterShards() { checkIsInMultiOrPipeline(); connection.sendCommand(CLUSTER, ClusterKeyword.SHARDS); return BuilderFactory.CLUSTER_SHARD_INFO_LIST.build(connection.getObjectMultiBulkReply()); } @Override public String clusterMyId() { checkIsInMultiOrPipeline(); connection.sendCommand(CLUSTER, ClusterKeyword.MYID); return connection.getBulkReply(); } @Override public String clusterMyShardId() { checkIsInMultiOrPipeline(); connection.sendCommand(CLUSTER, ClusterKeyword.MYSHARDID); return connection.getBulkReply(); } @Override public List> clusterLinks() { checkIsInMultiOrPipeline(); connection.sendCommand(CLUSTER, ClusterKeyword.LINKS); return connection.getObjectMultiBulkReply().stream() .map(BuilderFactory.ENCODED_OBJECT_MAP::build).collect(Collectors.toList()); } @Override public String clusterAddSlotsRange(int... ranges) { checkIsInMultiOrPipeline(); connection.sendCommand(CLUSTER, joinParameters(ClusterKeyword.ADDSLOTSRANGE.getRaw(), joinParameters(ranges))); return connection.getStatusCodeReply(); } @Override public String clusterDelSlotsRange(int... ranges) { checkIsInMultiOrPipeline(); connection.sendCommand(CLUSTER, joinParameters(ClusterKeyword.DELSLOTSRANGE.getRaw(), joinParameters(ranges))); return connection.getStatusCodeReply(); } @Override public String asking() { checkIsInMultiOrPipeline(); connection.sendCommand(ASKING); return connection.getStatusCodeReply(); } @Override public long pfadd(final String key, final String... elements) { checkIsInMultiOrPipeline(); return connection.executeCommand(commandObjects.pfadd(key, elements)); } @Override public long pfcount(final String key) { checkIsInMultiOrPipeline(); return connection.executeCommand(commandObjects.pfcount(key)); } @Override public long pfcount(final String... keys) { checkIsInMultiOrPipeline(); return connection.executeCommand(commandObjects.pfcount(keys)); } @Override public String pfmerge(final String destkey, final String... sourcekeys) { checkIsInMultiOrPipeline(); return connection.executeCommand(commandObjects.pfmerge(destkey, sourcekeys)); } @Override public Object fcall(final String name, final List keys, final List args) { return connection.executeCommand(commandObjects.fcall(name, keys, args)); } @Override public Object fcallReadonly(final String name, final List keys, final List args) { return connection.executeCommand(commandObjects.fcallReadonly(name, keys, args)); } @Override public String functionDelete(final String libraryName) { checkIsInMultiOrPipeline(); return connection.executeCommand(commandObjects.functionDelete(libraryName)); } @Override public String functionLoad(final String functionCode) { checkIsInMultiOrPipeline(); return connection.executeCommand(commandObjects.functionLoad(functionCode)); } @Override public String functionLoadReplace(final String functionCode) { checkIsInMultiOrPipeline(); return connection.executeCommand(commandObjects.functionLoadReplace(functionCode)); } @Override public FunctionStats functionStats() { checkIsInMultiOrPipeline(); return connection.executeCommand(commandObjects.functionStats()); } @Override public String functionFlush() { checkIsInMultiOrPipeline(); return connection.executeCommand(commandObjects.functionFlush()); } @Override public String functionFlush(final FlushMode mode) { checkIsInMultiOrPipeline(); return connection.executeCommand(commandObjects.functionFlush(mode)); } @Override public String functionKill() { checkIsInMultiOrPipeline(); return connection.executeCommand(commandObjects.functionKill()); } @Override public List functionList() { checkIsInMultiOrPipeline(); return connection.executeCommand(commandObjects.functionList()); } @Override public List functionList(final String libraryNamePattern) { checkIsInMultiOrPipeline(); return connection.executeCommand(commandObjects.functionList(libraryNamePattern)); } @Override public List functionListWithCode() { checkIsInMultiOrPipeline(); return connection.executeCommand(commandObjects.functionListWithCode()); } @Override public List functionListWithCode(String libraryNamePattern) { checkIsInMultiOrPipeline(); return connection.executeCommand(commandObjects.functionListWithCode(libraryNamePattern)); } @Override public long geoadd(final String key, final double longitude, final double latitude, final String member) { checkIsInMultiOrPipeline(); return connection.executeCommand(commandObjects.geoadd(key, longitude, latitude, member)); } @Override public long geoadd(final String key, final Map memberCoordinateMap) { checkIsInMultiOrPipeline(); return connection.executeCommand(commandObjects.geoadd(key, memberCoordinateMap)); } @Override public long geoadd(final String key, final GeoAddParams params, final Map memberCoordinateMap) { checkIsInMultiOrPipeline(); return connection.executeCommand(commandObjects.geoadd(key, params, memberCoordinateMap)); } @Override public Double geodist(final String key, final String member1, final String member2) { checkIsInMultiOrPipeline(); return connection.executeCommand(commandObjects.geodist(key, member1, member2)); } @Override public Double geodist(final String key, final String member1, final String member2, final GeoUnit unit) { checkIsInMultiOrPipeline(); return connection.executeCommand(commandObjects.geodist(key, member1, member2, unit)); } @Override public List geohash(final String key, String... members) { checkIsInMultiOrPipeline(); return connection.executeCommand(commandObjects.geohash(key, members)); } @Override public List geopos(final String key, String... members) { checkIsInMultiOrPipeline(); return connection.executeCommand(commandObjects.geopos(key, members)); } /** * @deprecated Use {@link Jedis#geosearch(String, GeoSearchParam)} instead. * Deprecated in Jedis 7.3.0. Mirrors Redis deprecation since 6.2.0. */ @Deprecated @Override public List georadius(final String key, final double longitude, final double latitude, final double radius, final GeoUnit unit) { checkIsInMultiOrPipeline(); return connection.executeCommand(commandObjects.georadius(key, longitude, latitude, radius, unit)); } /** * @deprecated Use {@link Jedis#geosearch(String, GeoSearchParam)} instead. * Deprecated in Jedis 7.3.0. Mirrors Redis deprecation since 6.2.0. */ @Deprecated @Override public List georadiusReadonly(final String key, final double longitude, final double latitude, final double radius, final GeoUnit unit) { checkIsInMultiOrPipeline(); return connection.executeCommand(commandObjects.georadiusReadonly(key, longitude, latitude, radius, unit)); } /** * @deprecated Use {@link Jedis#geosearch(String, GeoSearchParam)} instead. * Deprecated in Jedis 7.3.0. Mirrors Redis deprecation since 6.2.0. */ @Deprecated @Override public List georadius(final String key, final double longitude, final double latitude, final double radius, final GeoUnit unit, final GeoRadiusParam param) { checkIsInMultiOrPipeline(); return connection.executeCommand(commandObjects.georadius(key, longitude, latitude, radius, unit, param)); } /** * @deprecated Use {@link Jedis#geosearchStore(String, String, GeoSearchParam)} instead. * Deprecated in Jedis 7.3.0. Mirrors Redis deprecation since 6.2.0. */ @Deprecated @Override public long georadiusStore(final String key, double longitude, double latitude, double radius, GeoUnit unit, GeoRadiusParam param, GeoRadiusStoreParam storeParam) { checkIsInMultiOrPipeline(); return connection.executeCommand(commandObjects.georadiusStore(key, longitude, latitude, radius, unit, param, storeParam)); } /** * @deprecated Use {@link Jedis#geosearch(String, GeoSearchParam)} instead. * Deprecated in Jedis 7.3.0. Mirrors Redis deprecation since 6.2.0. */ @Deprecated @Override public List georadiusReadonly(final String key, final double longitude, final double latitude, final double radius, final GeoUnit unit, final GeoRadiusParam param) { checkIsInMultiOrPipeline(); return connection.executeCommand(commandObjects.georadiusReadonly(key, longitude, latitude, radius, unit, param)); } /** * @deprecated Use {@link Jedis#geosearch(String, GeoSearchParam)} instead. * Deprecated in Jedis 7.3.0. Mirrors Redis deprecation since 6.2.0. */ @Deprecated @Override public List georadiusByMember(final String key, final String member, final double radius, final GeoUnit unit) { checkIsInMultiOrPipeline(); return connection.executeCommand(commandObjects.georadiusByMember(key, member, radius, unit)); } /** * @deprecated Use {@link Jedis#geosearch(String, GeoSearchParam)} instead. * Deprecated in Jedis 7.3.0. Mirrors Redis deprecation since 6.2.0. */ @Deprecated @Override public List georadiusByMemberReadonly(final String key, final String member, final double radius, final GeoUnit unit) { checkIsInMultiOrPipeline(); return connection.executeCommand(commandObjects.georadiusByMemberReadonly(key, member, radius, unit)); } /** * @deprecated Use {@link Jedis#geosearch(String, GeoSearchParam)} instead. * Deprecated in Jedis 7.3.0. Mirrors Redis deprecation since 6.2.0. */ @Deprecated @Override public List georadiusByMember(final String key, final String member, final double radius, final GeoUnit unit, final GeoRadiusParam param) { checkIsInMultiOrPipeline(); return connection.executeCommand(commandObjects.georadiusByMember(key, member, radius, unit, param)); } /** * @deprecated Use {@link Jedis#geosearchStore(String, String, GeoSearchParam)} instead. * Deprecated in Jedis 7.3.0. Mirrors Redis deprecation since 6.2.0. */ @Deprecated @Override public long georadiusByMemberStore(final String key, String member, double radius, GeoUnit unit, GeoRadiusParam param, GeoRadiusStoreParam storeParam) { checkIsInMultiOrPipeline(); return connection.executeCommand(commandObjects.georadiusByMemberStore(key, member, radius, unit, param, storeParam)); } /** * @deprecated Use {@link Jedis#geosearch(String, GeoSearchParam)} instead. * Deprecated in Jedis 7.3.0. Mirrors Redis deprecation since 6.2.0. */ @Deprecated @Override public List georadiusByMemberReadonly(final String key, final String member, final double radius, final GeoUnit unit, final GeoRadiusParam param) { checkIsInMultiOrPipeline(); return connection.executeCommand(commandObjects.georadiusByMemberReadonly(key, member, radius, unit, param)); } @Override public List geosearch(String key, String member, double radius, GeoUnit unit) { checkIsInMultiOrPipeline(); return connection.executeCommand(commandObjects.geosearch(key, member, radius, unit)); } @Override public List geosearch(String key, GeoCoordinate coord, double radius, GeoUnit unit) { checkIsInMultiOrPipeline(); return connection.executeCommand(commandObjects.geosearch(key, coord, radius, unit)); } @Override public List geosearch(String key, String member, double width, double height, GeoUnit unit) { checkIsInMultiOrPipeline(); return connection.executeCommand(commandObjects.geosearch(key, member, width, height, unit)); } @Override public List geosearch(String key, GeoCoordinate coord, double width, double height, GeoUnit unit) { checkIsInMultiOrPipeline(); return connection.executeCommand(commandObjects.geosearch(key, coord, width, height, unit)); } @Override public List geosearch(String key, GeoSearchParam params) { checkIsInMultiOrPipeline(); return connection.executeCommand(commandObjects.geosearch(key, params)); } @Override public long geosearchStore(String dest, String src, String member, double radius, GeoUnit unit) { checkIsInMultiOrPipeline(); return connection.executeCommand(commandObjects.geosearchStore(dest, src, member, radius, unit)); } @Override public long geosearchStore(String dest, String src, GeoCoordinate coord, double radius, GeoUnit unit) { checkIsInMultiOrPipeline(); return connection.executeCommand(commandObjects.geosearchStore(dest, src, coord, radius, unit)); } @Override public long geosearchStore(String dest, String src, String member, double width, double height, GeoUnit unit) { checkIsInMultiOrPipeline(); return connection.executeCommand(commandObjects.geosearchStore(dest, src, member, width, height, unit)); } @Override public long geosearchStore(String dest, String src, GeoCoordinate coord, double width, double height, GeoUnit unit) { checkIsInMultiOrPipeline(); return connection.executeCommand(commandObjects.geosearchStore(dest, src, coord, width, height, unit)); } @Override public long geosearchStore(String dest, String src, GeoSearchParam params) { checkIsInMultiOrPipeline(); return connection.executeCommand(commandObjects.geosearchStore(dest, src, params)); } @Override public long geosearchStoreStoreDist(String dest, String src, GeoSearchParam params) { checkIsInMultiOrPipeline(); return connection.executeCommand(commandObjects.geosearchStoreStoreDist(dest, src, params)); } @Override public String moduleLoad(final String path) { checkIsInMultiOrPipeline(); connection.sendCommand(Command.MODULE, LOAD.name(), path); return connection.getStatusCodeReply(); } @Override public String moduleLoad(String path, String... args) { checkIsInMultiOrPipeline(); connection.sendCommand(Command.MODULE, joinParameters(LOAD.name(), path, args)); return connection.getStatusCodeReply(); } @Override public String moduleLoadEx(String path, ModuleLoadExParams params) { checkIsInMultiOrPipeline(); connection.sendCommand(new CommandArguments(Command.MODULE).add(LOADEX).add(path) .addParams(params)); return connection.getStatusCodeReply(); } @Override public String moduleUnload(final String name) { checkIsInMultiOrPipeline(); connection.sendCommand(Command.MODULE, UNLOAD.name(), name); return connection.getStatusCodeReply(); } @Override public List moduleList() { checkIsInMultiOrPipeline(); connection.sendCommand(Command.MODULE, LIST); return BuilderFactory.MODULE_LIST.build(connection.getOne()); } @Override public List bitfield(final String key, final String... arguments) { checkIsInMultiOrPipeline(); return connection.executeCommand(commandObjects.bitfield(key, arguments)); } @Override public List bitfieldReadonly(final String key, final String... arguments) { checkIsInMultiOrPipeline(); return connection.executeCommand(commandObjects.bitfieldReadonly(key, arguments)); } @Override public long hstrlen(final String key, final String field) { checkIsInMultiOrPipeline(); return connection.executeCommand(commandObjects.hstrlen(key, field)); } @Override public List hexpire(String key, long seconds, String... fields) { checkIsInMultiOrPipeline(); return connection.executeCommand(commandObjects.hexpire(key, seconds, fields)); } @Override public List hexpire(String key, long seconds, ExpiryOption condition, String... fields) { checkIsInMultiOrPipeline(); return connection.executeCommand(commandObjects.hexpire(key, seconds, condition, fields)); } @Override public List hpexpire(String key, long milliseconds, String... fields) { checkIsInMultiOrPipeline(); return connection.executeCommand(commandObjects.hpexpire(key, milliseconds, fields)); } @Override public List hpexpire(String key, long milliseconds, ExpiryOption condition, String... fields) { checkIsInMultiOrPipeline(); return connection.executeCommand(commandObjects.hpexpire(key, milliseconds, condition, fields)); } @Override public List hexpireAt(String key, long unixTimeSeconds, String... fields) { checkIsInMultiOrPipeline(); return connection.executeCommand(commandObjects.hexpireAt(key, unixTimeSeconds, fields)); } @Override public List hexpireAt(String key, long unixTimeSeconds, ExpiryOption condition, String... fields) { checkIsInMultiOrPipeline(); return connection.executeCommand(commandObjects.hexpireAt(key, unixTimeSeconds, condition, fields)); } @Override public List hpexpireAt(String key, long unixTimeMillis, String... fields) { checkIsInMultiOrPipeline(); return connection.executeCommand(commandObjects.hpexpireAt(key, unixTimeMillis, fields)); } @Override public List hpexpireAt(String key, long unixTimeMillis, ExpiryOption condition, String... fields) { checkIsInMultiOrPipeline(); return connection.executeCommand(commandObjects.hpexpireAt(key, unixTimeMillis, condition, fields)); } @Override public List hexpireTime(String key, String... fields) { checkIsInMultiOrPipeline(); return connection.executeCommand(commandObjects.hexpireTime(key, fields)); } @Override public List hpexpireTime(String key, String... fields) { checkIsInMultiOrPipeline(); return connection.executeCommand(commandObjects.hpexpireTime(key, fields)); } @Override public List httl(String key, String... fields) { checkIsInMultiOrPipeline(); return connection.executeCommand(commandObjects.httl(key, fields)); } @Override public List hpttl(String key, String... fields) { checkIsInMultiOrPipeline(); return connection.executeCommand(commandObjects.hpttl(key, fields)); } @Override public List hpersist(String key, String... fields) { checkIsInMultiOrPipeline(); return connection.executeCommand(commandObjects.hpersist(key, fields)); } @Override public String memoryDoctor() { checkIsInMultiOrPipeline(); connection.sendCommand(MEMORY, DOCTOR); return connection.getBulkReply(); } @Override public Long memoryUsage(final String key) { checkIsInMultiOrPipeline(); connection.sendCommand(MEMORY, USAGE.name(), key); return connection.getIntegerReply(); } @Override public Long memoryUsage(final String key, final int samples) { checkIsInMultiOrPipeline(); connection.sendCommand(MEMORY, USAGE.getRaw(), encode(key), SAMPLES.getRaw(), toByteArray(samples)); return connection.getIntegerReply(); } @Override public String memoryPurge() { checkIsInMultiOrPipeline(); connection.sendCommand(MEMORY, PURGE); return connection.getBulkReply(); } @Override public Map memoryStats() { checkIsInMultiOrPipeline(); connection.sendCommand(MEMORY, STATS); return BuilderFactory.ENCODED_OBJECT_MAP.build(connection.getOne()); } @Override public String lolwut() { checkIsInMultiOrPipeline(); connection.sendCommand(LOLWUT); return connection.getBulkReply(); } @Override public String lolwut(LolwutParams lolwutParams) { checkIsInMultiOrPipeline(); connection.sendCommand(new CommandArguments(LOLWUT).addParams(lolwutParams)); return connection.getBulkReply(); } @Override public String reset() { connection.sendCommand(Command.RESET); return connection.getStatusCodeReply(); } @Override public String latencyDoctor() { checkIsInMultiOrPipeline(); connection.sendCommand(LATENCY, DOCTOR); return connection.getBulkReply(); } public Map latencyLatest() { checkIsInMultiOrPipeline(); connection.sendCommand(LATENCY, LATEST); return BuilderFactory.LATENCY_LATEST_RESPONSE.build(connection.getOne()); } public List latencyHistory(LatencyEvent event) { checkIsInMultiOrPipeline(); connection.sendCommand(new CommandArguments(LATENCY).add(HISTORY).add(event)); return BuilderFactory.LATENCY_HISTORY_RESPONSE.build(connection.getOne()); } public long latencyReset(LatencyEvent... events) { checkIsInMultiOrPipeline(); CommandArguments arguments = new CommandArguments(LATENCY).add(Keyword.RESET); Arrays.stream(events).forEach(arguments::add); connection.sendCommand(arguments); return connection.getIntegerReply(); } @Override public String hotkeysStart(HotkeysParams params) { checkIsInMultiOrPipeline(); return connection.executeCommand(commandObjects.hotkeysStart(params)); } @Override public String hotkeysStop() { checkIsInMultiOrPipeline(); return connection.executeCommand(commandObjects.hotkeysStop()); } @Override public String hotkeysReset() { checkIsInMultiOrPipeline(); return connection.executeCommand(commandObjects.hotkeysReset()); } @Override public HotkeysInfo hotkeysGet() { checkIsInMultiOrPipeline(); return connection.executeCommand(commandObjects.hotkeysGet()); } @Override public StreamEntryID xadd(final String key, final StreamEntryID id, final Map hash) { checkIsInMultiOrPipeline(); return connection.executeCommand(commandObjects.xadd(key, id, hash)); } @Override public StreamEntryID xadd(final String key, final XAddParams params, final Map hash) { checkIsInMultiOrPipeline(); return connection.executeCommand(commandObjects.xadd(key, params, hash)); } @Override public long xlen(final String key) { checkIsInMultiOrPipeline(); return connection.executeCommand(commandObjects.xlen(key)); } @Override public List xrange(final String key, final StreamEntryID start, final StreamEntryID end) { checkIsInMultiOrPipeline(); return connection.executeCommand(commandObjects.xrange(key, start, end)); } @Override public List xrange(final String key, final StreamEntryID start, final StreamEntryID end, final int count) { checkIsInMultiOrPipeline(); return connection.executeCommand(commandObjects.xrange(key, start, end, count)); } @Override public List xrevrange(final String key, final StreamEntryID end, final StreamEntryID start) { checkIsInMultiOrPipeline(); return connection.executeCommand(commandObjects.xrevrange(key, end, start)); } @Override public List xrevrange(final String key, final StreamEntryID end, final StreamEntryID start, final int count) { checkIsInMultiOrPipeline(); return connection.executeCommand(commandObjects.xrevrange(key, end, start, count)); } @Override public List xrange(final String key, final String start, final String end) { checkIsInMultiOrPipeline(); return connection.executeCommand(commandObjects.xrange(key, start, end)); } @Override public List xrange(final String key, final String start, final String end, final int count) { checkIsInMultiOrPipeline(); return connection.executeCommand(commandObjects.xrange(key, start, end, count)); } @Override public List xrevrange(final String key, final String end, final String start) { checkIsInMultiOrPipeline(); return connection.executeCommand(commandObjects.xrevrange(key, end, start)); } @Override public List xrevrange(final String key, final String end, final String start, final int count) { checkIsInMultiOrPipeline(); return connection.executeCommand(commandObjects.xrevrange(key, end, start, count)); } @Override public List>> xread(final XReadParams xReadParams, final Map streams) { checkIsInMultiOrPipeline(); return connection.executeCommand(commandObjects.xread(xReadParams, streams)); } @Override public Map> xreadAsMap(final XReadParams xReadParams, final Map streams) { checkIsInMultiOrPipeline(); return connection.executeCommand(commandObjects.xreadAsMap(xReadParams, streams)); } @Override public long xack(final String key, final String group, final StreamEntryID... ids) { checkIsInMultiOrPipeline(); return connection.executeCommand(commandObjects.xack(key, group, ids)); } @Override public List xackdel(final String key, final String group, final StreamEntryID... ids) { checkIsInMultiOrPipeline(); return connection.executeCommand(commandObjects.xackdel(key, group, ids)); } @Override public List xackdel(final String key, final String group, final StreamDeletionPolicy trimMode, final StreamEntryID... ids) { checkIsInMultiOrPipeline(); return connection.executeCommand(commandObjects.xackdel(key, group, trimMode, ids)); } @Override public String xgroupCreate(final String key, final String groupName, final StreamEntryID id, final boolean makeStream) { checkIsInMultiOrPipeline(); return connection.executeCommand(commandObjects.xgroupCreate(key, groupName, id, makeStream)); } @Override public String xgroupSetID(final String key, final String groupName, final StreamEntryID id) { checkIsInMultiOrPipeline(); return connection.executeCommand(commandObjects.xgroupSetID(key, groupName, id)); } @Override public long xgroupDestroy(final String key, final String groupName) { checkIsInMultiOrPipeline(); return connection.executeCommand(commandObjects.xgroupDestroy(key, groupName)); } @Override public boolean xgroupCreateConsumer(String key, String groupName, String consumerName) { checkIsInMultiOrPipeline(); return connection.executeCommand(commandObjects.xgroupCreateConsumer(key, groupName, consumerName)); } @Override public long xgroupDelConsumer(final String key, final String groupName, final String consumerName) { checkIsInMultiOrPipeline(); return connection.executeCommand(commandObjects.xgroupDelConsumer(key, groupName, consumerName)); } @Override public long xdel(final String key, final StreamEntryID... ids) { checkIsInMultiOrPipeline(); return connection.executeCommand(commandObjects.xdel(key, ids)); } @Override public List xdelex(final String key, final StreamEntryID... ids) { checkIsInMultiOrPipeline(); return connection.executeCommand(commandObjects.xdelex(key, ids)); } @Override public List xdelex(final String key, final StreamDeletionPolicy trimMode, final StreamEntryID... ids) { checkIsInMultiOrPipeline(); return connection.executeCommand(commandObjects.xdelex(key, trimMode, ids)); } @Override public long xtrim(final String key, final long maxLen, final boolean approximateLength) { checkIsInMultiOrPipeline(); return connection.executeCommand(commandObjects.xtrim(key, maxLen, approximateLength)); } @Override public long xtrim(final String key, final XTrimParams params) { checkIsInMultiOrPipeline(); return connection.executeCommand(commandObjects.xtrim(key, params)); } @Override public List>> xreadGroup(final String groupName, final String consumer, final XReadGroupParams xReadGroupParams, final Map streams) { checkIsInMultiOrPipeline(); return connection.executeCommand(commandObjects.xreadGroup(groupName, consumer, xReadGroupParams, streams)); } @Override public Map> xreadGroupAsMap(final String groupName, final String consumer, final XReadGroupParams xReadGroupParams, final Map streams) { checkIsInMultiOrPipeline(); return connection.executeCommand(commandObjects.xreadGroupAsMap(groupName, consumer, xReadGroupParams, streams)); } @Override public StreamPendingSummary xpending(final String key, final String groupName) { checkIsInMultiOrPipeline(); return connection.executeCommand(commandObjects.xpending(key, groupName)); } @Override public List xpending(final String key, final String groupName, final XPendingParams params) { checkIsInMultiOrPipeline(); return connection.executeCommand(commandObjects.xpending(key, groupName, params)); } @Override public List xclaim(String key, String group, String consumerName, long minIdleTime, XClaimParams params, StreamEntryID... ids) { checkIsInMultiOrPipeline(); return connection.executeCommand(commandObjects.xclaim(key, group, consumerName, minIdleTime, params, ids)); } @Override public List xclaimJustId(String key, String group, String consumerName, long minIdleTime, XClaimParams params, StreamEntryID... ids) { checkIsInMultiOrPipeline(); return connection.executeCommand(commandObjects.xclaimJustId(key, group, consumerName, minIdleTime, params, ids)); } @Override public Map.Entry> xautoclaim(String key, String group, String consumerName, long minIdleTime, StreamEntryID start, XAutoClaimParams params) { checkIsInMultiOrPipeline(); return connection.executeCommand(commandObjects.xautoclaim(key, group, consumerName, minIdleTime, start, params)); } @Override public Map.Entry> xautoclaimJustId(String key, String group, String consumerName, long minIdleTime, StreamEntryID start, XAutoClaimParams params) { checkIsInMultiOrPipeline(); return connection.executeCommand(commandObjects.xautoclaimJustId(key, group, consumerName, minIdleTime, start, params)); } @Override public StreamInfo xinfoStream(String key) { return connection.executeCommand(commandObjects.xinfoStream(key)); } @Override public StreamFullInfo xinfoStreamFull(String key) { checkIsInMultiOrPipeline(); return connection.executeCommand(commandObjects.xinfoStreamFull(key)); } @Override public String xcfgset(String key, redis.clients.jedis.params.XCfgSetParams params) { checkIsInMultiOrPipeline(); return connection.executeCommand(commandObjects.xcfgset(key, params)); } @Override public StreamFullInfo xinfoStreamFull(String key, int count) { checkIsInMultiOrPipeline(); return connection.executeCommand(commandObjects.xinfoStreamFull(key, count)); } @Override public List xinfoGroups(String key) { return connection.executeCommand(commandObjects.xinfoGroups(key)); } @Override public List xinfoConsumers(String key, String group) { return connection.executeCommand(commandObjects.xinfoConsumers(key, group)); } @Override public List xinfoConsumers2(String key, String group) { return connection.executeCommand(commandObjects.xinfoConsumers2(key, group)); } @Override public Object fcall(final byte[] name, final List keys, final List args) { checkIsInMultiOrPipeline(); return connection.executeCommand(commandObjects.fcall(name, keys, args)); } @Override public Object fcallReadonly(final byte[] name, final List keys, final List args) { checkIsInMultiOrPipeline(); return connection.executeCommand(commandObjects.fcallReadonly(name, keys, args)); } @Override public String functionDelete(final byte[] libraryName) { checkIsInMultiOrPipeline(); return connection.executeCommand(commandObjects.functionDelete(libraryName)); } @Override public byte[] functionDump() { checkIsInMultiOrPipeline(); return connection.executeCommand(commandObjects.functionDump()); } @Override public List functionListBinary() { checkIsInMultiOrPipeline(); return connection.executeCommand(commandObjects.functionListBinary()); } @Override public List functionList(final byte[] libraryNamePattern) { checkIsInMultiOrPipeline(); return connection.executeCommand(commandObjects.functionList(libraryNamePattern)); } @Override public List functionListWithCodeBinary() { checkIsInMultiOrPipeline(); return connection.executeCommand(commandObjects.functionListWithCodeBinary()); } @Override public List functionListWithCode(final byte[] libraryNamePattern) { checkIsInMultiOrPipeline(); return connection.executeCommand(commandObjects.functionListWithCode(libraryNamePattern)); } @Override public String functionLoad(final byte[] functionCode) { checkIsInMultiOrPipeline(); return connection.executeCommand(commandObjects.functionLoad(functionCode)); } @Override public String functionLoadReplace(final byte[] functionCode) { checkIsInMultiOrPipeline(); return connection.executeCommand(commandObjects.functionLoadReplace(functionCode)); } @Override public String functionRestore(final byte[] serializedValue) { checkIsInMultiOrPipeline(); return connection.executeCommand(commandObjects.functionRestore(serializedValue)); } @Override public String functionRestore(final byte[] serializedValue, final FunctionRestorePolicy policy) { checkIsInMultiOrPipeline(); return connection.executeCommand(commandObjects.functionRestore(serializedValue, policy)); } @Override public Object functionStatsBinary() { checkIsInMultiOrPipeline(); return connection.executeCommand(commandObjects.functionStatsBinary()); } public Object sendCommand(ProtocolCommand cmd, String... args) { checkIsInMultiOrPipeline(); connection.sendCommand(cmd, args); return connection.getOne(); } public Object sendBlockingCommand(ProtocolCommand cmd, String... args) { checkIsInMultiOrPipeline(); connection.sendCommand(cmd, args); connection.setTimeoutInfinite(); try { return connection.getOne(); } finally { connection.rollbackTimeout(); } } private static byte[][] joinParameters(int... params) { byte[][] result = new byte[params.length][]; for (int i = 0; i < params.length; i++) { result[i] = toByteArray(params[i]); } return result; } private static byte[][] joinParameters(byte[] first, byte[][] rest) { byte[][] result = new byte[rest.length + 1][]; result[0] = first; System.arraycopy(rest, 0, result, 1, rest.length); return result; } private static byte[][] joinParameters(byte[] first, byte[] second, byte[][] rest) { byte[][] result = new byte[rest.length + 2][]; result[0] = first; result[1] = second; System.arraycopy(rest, 0, result, 2, rest.length); return result; } private static String[] joinParameters(String first, String[] rest) { String[] result = new String[rest.length + 1]; result[0] = first; System.arraycopy(rest, 0, result, 1, rest.length); return result; } private static String[] joinParameters(String first, String second, String[] rest) { String[] result = new String[rest.length + 2]; result[0] = first; result[1] = second; System.arraycopy(rest, 0, result, 2, rest.length); return result; } // Vector Set commands @Override public boolean vadd(String key, float[] vector, String element) { checkIsInMultiOrPipeline(); return connection.executeCommand(commandObjects.vadd(key, vector, element)); } @Override public boolean vadd(String key, float[] vector, String element, VAddParams params) { checkIsInMultiOrPipeline(); return connection.executeCommand(commandObjects.vadd(key, vector, element, params)); } @Override public boolean vaddFP32(String key, byte[] vectorBlob, String element) { checkIsInMultiOrPipeline(); return connection.executeCommand(commandObjects.vaddFP32(key, vectorBlob, element)); } @Override public boolean vaddFP32(String key, byte[] vectorBlob, String element, VAddParams params) { checkIsInMultiOrPipeline(); return connection.executeCommand(commandObjects.vaddFP32(key, vectorBlob, element, params)); } @Override public boolean vadd(String key, float[] vector, String element, int reduceDim, VAddParams params) { checkIsInMultiOrPipeline(); return connection.executeCommand(commandObjects.vadd(key, vector, element, reduceDim, params)); } @Override public boolean vaddFP32(String key, byte[] vectorBlob, String element, int reduceDim, VAddParams params) { checkIsInMultiOrPipeline(); return connection.executeCommand(commandObjects.vaddFP32(key, vectorBlob, element, reduceDim, params)); } @Override public List vsim(String key, float[] vector) { checkIsInMultiOrPipeline(); return connection.executeCommand(commandObjects.vsim(key, vector)); } @Override public List vsim(String key, float[] vector, VSimParams params) { checkIsInMultiOrPipeline(); return connection.executeCommand(commandObjects.vsim(key, vector, params)); } @Override public Map vsimWithScores(String key, float[] vector, VSimParams params) { checkIsInMultiOrPipeline(); return connection.executeCommand(commandObjects.vsimWithScores(key, vector, params)); } @Override public Map vsimWithScoresAndAttribs(String key, float[] vector, VSimParams params) { checkIsInMultiOrPipeline(); return connection.executeCommand(commandObjects.vsimWithScoresAndAttribs(key, vector, params)); } @Override public List vsimByElement(String key, String element) { checkIsInMultiOrPipeline(); return connection.executeCommand(commandObjects.vsimByElement(key, element)); } @Override public List vsimByElement(String key, String element, VSimParams params) { checkIsInMultiOrPipeline(); return connection.executeCommand(commandObjects.vsimByElement(key, element, params)); } @Override public Map vsimByElementWithScores(String key, String element, VSimParams params) { checkIsInMultiOrPipeline(); return connection.executeCommand(commandObjects.vsimByElementWithScores(key, element, params)); } @Override public Map vsimByElementWithScoresAndAttribs(String key, String element, VSimParams params) { checkIsInMultiOrPipeline(); return connection.executeCommand(commandObjects.vsimByElementWithScoresAndAttribs(key, element, params)); } @Override public long vdim(String key) { checkIsInMultiOrPipeline(); return connection.executeCommand(commandObjects.vdim(key)); } @Override public long vcard(String key) { checkIsInMultiOrPipeline(); return connection.executeCommand(commandObjects.vcard(key)); } @Override public List vemb(String key, String element) { checkIsInMultiOrPipeline(); return connection.executeCommand(commandObjects.vemb(key, element)); } @Override public RawVector vembRaw(String key, String element) { checkIsInMultiOrPipeline(); return connection.executeCommand(commandObjects.vembRaw(key, element)); } @Override public boolean vrem(String key, String element) { checkIsInMultiOrPipeline(); return connection.executeCommand(commandObjects.vrem(key, element)); } @Override public List> vlinks(String key, String element) { checkIsInMultiOrPipeline(); return connection.executeCommand(commandObjects.vlinks(key, element)); } @Override public List> vlinksWithScores(String key, String element) { checkIsInMultiOrPipeline(); return connection.executeCommand(commandObjects.vlinksWithScores(key, element)); } @Override public String vrandmember(String key) { checkIsInMultiOrPipeline(); return connection.executeCommand(commandObjects.vrandmember(key)); } @Override public List vrandmember(String key, int count) { checkIsInMultiOrPipeline(); return connection.executeCommand(commandObjects.vrandmember(key, count)); } @Override public String vgetattr(String key, String element) { checkIsInMultiOrPipeline(); return connection.executeCommand(commandObjects.vgetattr(key, element)); } @Override public boolean vsetattr(String key, String element, String attributes) { checkIsInMultiOrPipeline(); return connection.executeCommand(commandObjects.vsetattr(key, element, attributes)); } @Override public VectorInfo vinfo(String key) { checkIsInMultiOrPipeline(); return connection.executeCommand(commandObjects.vinfo(key)); } // Binary vector set commands @Override public boolean vadd(byte[] key, float[] vector, byte[] element) { checkIsInMultiOrPipeline(); return connection.executeCommand(commandObjects.vadd(key, vector, element)); } @Override public boolean vadd(byte[] key, float[] vector, byte[] element, VAddParams params) { checkIsInMultiOrPipeline(); return connection.executeCommand(commandObjects.vadd(key, vector, element, params)); } @Override public boolean vaddFP32(byte[] key, byte[] vectorBlob, byte[] element) { checkIsInMultiOrPipeline(); return connection.executeCommand(commandObjects.vaddFP32(key, vectorBlob, element)); } @Override public boolean vaddFP32(byte[] key, byte[] vectorBlob, byte[] element, VAddParams params) { checkIsInMultiOrPipeline(); return connection.executeCommand(commandObjects.vaddFP32(key, vectorBlob, element, params)); } @Override public boolean vadd(byte[] key, float[] vector, byte[] element, int reduceDim, VAddParams params) { checkIsInMultiOrPipeline(); return connection.executeCommand(commandObjects.vadd(key, vector, element, reduceDim, params)); } @Override public boolean vaddFP32(byte[] key, byte[] vectorBlob, byte[] element, int reduceDim, VAddParams params) { checkIsInMultiOrPipeline(); return connection.executeCommand(commandObjects.vaddFP32(key, vectorBlob, element, reduceDim, params)); } @Override public List vsim(byte[] key, float[] vector) { checkIsInMultiOrPipeline(); return connection.executeCommand(commandObjects.vsim(key, vector)); } @Override public List vsim(byte[] key, float[] vector, VSimParams params) { checkIsInMultiOrPipeline(); return connection.executeCommand(commandObjects.vsim(key, vector, params)); } @Override public Map vsimWithScores(byte[] key, float[] vector, VSimParams params) { checkIsInMultiOrPipeline(); return connection.executeCommand(commandObjects.vsimWithScores(key, vector, params)); } @Override public Map vsimWithScoresAndAttribs(byte[] key, float[] vector, VSimParams params) { checkIsInMultiOrPipeline(); return connection.executeCommand(commandObjects.vsimWithScoresAndAttribs(key, vector, params)); } @Override public List vsimByElement(byte[] key, byte[] element) { checkIsInMultiOrPipeline(); return connection.executeCommand(commandObjects.vsimByElement(key, element)); } @Override public List vsimByElement(byte[] key, byte[] element, VSimParams params) { checkIsInMultiOrPipeline(); return connection.executeCommand(commandObjects.vsimByElement(key, element, params)); } @Override public Map vsimByElementWithScores(byte[] key, byte[] element, VSimParams params) { checkIsInMultiOrPipeline(); return connection.executeCommand(commandObjects.vsimByElementWithScores(key, element, params)); } @Override public Map vsimByElementWithScoresAndAttribs(byte[] key, byte[] element, VSimParams params) { checkIsInMultiOrPipeline(); return connection.executeCommand(commandObjects.vsimByElementWithScoresAndAttribs(key, element, params)); } @Override public long vdim(byte[] key) { checkIsInMultiOrPipeline(); return connection.executeCommand(commandObjects.vdim(key)); } @Override public long vcard(byte[] key) { checkIsInMultiOrPipeline(); return connection.executeCommand(commandObjects.vcard(key)); } @Override public List vemb(byte[] key, byte[] element) { checkIsInMultiOrPipeline(); return connection.executeCommand(commandObjects.vemb(key, element)); } @Override public RawVector vembRaw(byte[] key, byte[] element) { checkIsInMultiOrPipeline(); return connection.executeCommand(commandObjects.vembRaw(key, element)); } @Override public boolean vrem(byte[] key, byte[] element) { checkIsInMultiOrPipeline(); return connection.executeCommand(commandObjects.vrem(key, element)); } @Override public List> vlinks(byte[] key, byte[] element) { checkIsInMultiOrPipeline(); return connection.executeCommand(commandObjects.vlinks(key, element)); } @Override public List> vlinksWithScores(byte[] key, byte[] element) { checkIsInMultiOrPipeline(); return connection.executeCommand(commandObjects.vlinksWithScores(key, element)); } @Override public byte[] vrandmember(byte[] key) { checkIsInMultiOrPipeline(); return connection.executeCommand(commandObjects.vrandmember(key)); } @Override public List vrandmember(byte[] key, int count) { checkIsInMultiOrPipeline(); return connection.executeCommand(commandObjects.vrandmember(key, count)); } @Override public byte[] vgetattr(byte[] key, byte[] element) { checkIsInMultiOrPipeline(); return connection.executeCommand(commandObjects.vgetattr(key, element)); } @Override public boolean vsetattr(byte[] key, byte[] element, byte[] attributes) { checkIsInMultiOrPipeline(); return connection.executeCommand(commandObjects.vsetattr(key, element, attributes)); } } ================================================ FILE: src/main/java/redis/clients/jedis/JedisClientConfig.java ================================================ package redis.clients.jedis; import java.util.function.Supplier; import javax.net.ssl.HostnameVerifier; import javax.net.ssl.SSLParameters; import javax.net.ssl.SSLSocketFactory; import redis.clients.jedis.authentication.AuthXManager; public interface JedisClientConfig { default RedisProtocol getRedisProtocol() { return null; } /** * @return Connection timeout in milliseconds */ default int getConnectionTimeoutMillis() { return Protocol.DEFAULT_TIMEOUT; } /** * @return Socket timeout in milliseconds */ default int getSocketTimeoutMillis() { return Protocol.DEFAULT_TIMEOUT; } /** * @return Socket timeout (in milliseconds) to use during blocking operation. Default is '0', * which means to block forever. */ default int getBlockingSocketTimeoutMillis() { return 0; } /** * @return Redis ACL user */ default String getUser() { return null; } default String getPassword() { return null; } // TODO: return null default Supplier getCredentialsProvider() { return new DefaultRedisCredentialsProvider( new DefaultRedisCredentials(getUser(), getPassword())); } default AuthXManager getAuthXManager() { return null; } default int getDatabase() { return Protocol.DEFAULT_DATABASE; } default String getClientName() { return null; } /** * @return {@code true} - to create TLS connection(s). {@code false} - otherwise. */ default boolean isSsl() { return false; } default SSLSocketFactory getSslSocketFactory() { return null; } default SSLParameters getSslParameters() { return null; } /** * {@link JedisClientConfig#isSsl()}, {@link JedisClientConfig#getSslSocketFactory()} and * {@link JedisClientConfig#getSslParameters()} will be ignored if * {@link JedisClientConfig#getSslOptions() this} is set. * @return ssl options */ default SslOptions getSslOptions() { return null; } default HostnameVerifier getHostnameVerifier() { return null; } default HostAndPortMapper getHostAndPortMapper() { return null; } /** * Execute READONLY command to connections. *

* READONLY command is specific to Redis Cluster replica nodes. So this config param is only * intended for Redis Cluster connections. * @return {@code true} - to execute READONLY command to connection(s). {@code false} - otherwise. */ default boolean isReadOnlyForRedisClusterReplicas() { return false; } /** * Modify the behavior of internally executing CLIENT SETINFO command. * @return CLIENT SETINFO config */ default ClientSetInfoConfig getClientSetInfoConfig() { return ClientSetInfoConfig.DEFAULT; } } ================================================ FILE: src/main/java/redis/clients/jedis/JedisCluster.java ================================================ package redis.clients.jedis; import java.time.Duration; import java.util.Collections; import java.util.Map; import java.util.Set; import org.apache.commons.pool2.impl.GenericObjectPoolConfig; import redis.clients.jedis.annots.Experimental; import redis.clients.jedis.builders.ClusterClientBuilder; import redis.clients.jedis.executors.ClusterCommandExecutor; import redis.clients.jedis.executors.CommandExecutor; import redis.clients.jedis.providers.ClusterConnectionProvider; import redis.clients.jedis.csc.Cache; import redis.clients.jedis.csc.CacheConfig; import redis.clients.jedis.csc.CacheFactory; import redis.clients.jedis.providers.ConnectionProvider; import redis.clients.jedis.util.JedisClusterCRC16; /** * JedisCluster is a client for Redis Cluster deployments. * * @deprecated Use {@link RedisClusterClient} instead. RedisClusterClient provides the same functionality * with a cleaner API and simplified constructor options. For basic usage, simple * constructors are available. For advanced configuration, use {@link RedisClusterClient#builder()}. */ @Deprecated public class JedisCluster extends UnifiedJedis { public static final String INIT_NO_ERROR_PROPERTY = "jedis.cluster.initNoError"; /** * Default timeout in milliseconds. */ public static final int DEFAULT_TIMEOUT = 2000; /** * Default amount of attempts for executing a command */ public static final int DEFAULT_MAX_ATTEMPTS = 5; private final CommandFlagsRegistry commandFlagsRegistry; /** * Creates a JedisCluster instance. The provided node is used to make the first contact with the cluster. *

* Here, the default timeout of {@value redis.clients.jedis.JedisCluster#DEFAULT_TIMEOUT} ms is being used with * {@value redis.clients.jedis.JedisCluster#DEFAULT_MAX_ATTEMPTS} maximum attempts. * @param node Node to first connect to. */ public JedisCluster(HostAndPort node) { this(Collections.singleton(node)); } /** * Creates a JedisCluster instance. The provided node is used to make the first contact with the cluster. *

* Here, the default timeout of {@value redis.clients.jedis.JedisCluster#DEFAULT_TIMEOUT} ms is being used with * {@value redis.clients.jedis.JedisCluster#DEFAULT_MAX_ATTEMPTS} maximum attempts. * @param node Node to first connect to. * @param timeout connection and socket timeout in milliseconds. */ public JedisCluster(HostAndPort node, int timeout) { this(Collections.singleton(node), timeout); } /** * Creates a JedisCluster instance. The provided node is used to make the first contact with the cluster.
* You can specify the timeout and the maximum attempts. * @param node Node to first connect to. * @param timeout connection and socket timeout in milliseconds. * @param maxAttempts maximum attempts for executing a command. */ public JedisCluster(HostAndPort node, int timeout, int maxAttempts) { this(Collections.singleton(node), timeout, maxAttempts); } public JedisCluster(HostAndPort node, final GenericObjectPoolConfig poolConfig) { this(Collections.singleton(node), poolConfig); } public JedisCluster(HostAndPort node, int timeout, final GenericObjectPoolConfig poolConfig) { this(Collections.singleton(node), timeout, poolConfig); } public JedisCluster(HostAndPort node, int timeout, int maxAttempts, final GenericObjectPoolConfig poolConfig) { this(Collections.singleton(node), timeout, maxAttempts, poolConfig); } public JedisCluster(HostAndPort node, int connectionTimeout, int soTimeout, int maxAttempts, final GenericObjectPoolConfig poolConfig) { this(Collections.singleton(node), connectionTimeout, soTimeout, maxAttempts, poolConfig); } public JedisCluster(HostAndPort node, int connectionTimeout, int soTimeout, int maxAttempts, String password, final GenericObjectPoolConfig poolConfig) { this(Collections.singleton(node), connectionTimeout, soTimeout, maxAttempts, password, poolConfig); } public JedisCluster(HostAndPort node, int connectionTimeout, int soTimeout, int maxAttempts, String password, String clientName, final GenericObjectPoolConfig poolConfig) { this(Collections.singleton(node), connectionTimeout, soTimeout, maxAttempts, password, clientName, poolConfig); } public JedisCluster(HostAndPort node, int connectionTimeout, int soTimeout, int maxAttempts, String user, String password, String clientName, final GenericObjectPoolConfig poolConfig) { this(Collections.singleton(node), connectionTimeout, soTimeout, maxAttempts, user, password, clientName, poolConfig); } public JedisCluster(HostAndPort node, int connectionTimeout, int soTimeout, int maxAttempts, String password, String clientName, final GenericObjectPoolConfig poolConfig, boolean ssl) { this(Collections.singleton(node), connectionTimeout, soTimeout, maxAttempts, password, clientName, poolConfig, ssl); } public JedisCluster(HostAndPort node, int connectionTimeout, int soTimeout, int maxAttempts, String user, String password, String clientName, final GenericObjectPoolConfig poolConfig, boolean ssl) { this(Collections.singleton(node), connectionTimeout, soTimeout, maxAttempts, user, password, clientName, poolConfig, ssl); } public JedisCluster(HostAndPort node, final JedisClientConfig clientConfig, int maxAttempts, final GenericObjectPoolConfig poolConfig) { this(Collections.singleton(node), clientConfig, maxAttempts, poolConfig); } /** * Creates a JedisCluster with multiple entry points. *

* Here, the default timeout of {@value redis.clients.jedis.JedisCluster#DEFAULT_TIMEOUT} ms is being used with * {@value redis.clients.jedis.JedisCluster#DEFAULT_MAX_ATTEMPTS} maximum attempts. * @param nodes Nodes to connect to. */ public JedisCluster(Set nodes) { this(nodes, DEFAULT_TIMEOUT); } /** * Creates a JedisCluster with multiple entry points. *

* Here, the default timeout of {@value redis.clients.jedis.JedisCluster#DEFAULT_TIMEOUT} ms is being used with * {@value redis.clients.jedis.JedisCluster#DEFAULT_MAX_ATTEMPTS} maximum attempts. * @param nodes Nodes to connect to. * @param timeout connection and socket timeout in milliseconds. */ public JedisCluster(Set nodes, int timeout) { this(nodes, DefaultJedisClientConfig.builder().timeoutMillis(timeout).build()); } /** * Creates a JedisCluster with multiple entry points.
* You can specify the timeout and the maximum attempts. * @param nodes Nodes to connect to. * @param timeout connection and socket timeout in milliseconds. * @param maxAttempts maximum attempts for executing a command. */ public JedisCluster(Set nodes, int timeout, int maxAttempts) { this(nodes, DefaultJedisClientConfig.builder().timeoutMillis(timeout).build(), maxAttempts); } public JedisCluster(Set nodes, String user, String password) { this(nodes, DefaultJedisClientConfig.builder().user(user).password(password).build()); } public JedisCluster(Set nodes, String user, String password, HostAndPortMapper hostAndPortMap) { this(nodes, DefaultJedisClientConfig.builder().user(user).password(password) .hostAndPortMapper(hostAndPortMap).build()); } public JedisCluster(Set nodes, final GenericObjectPoolConfig poolConfig) { this(nodes, DEFAULT_TIMEOUT, DEFAULT_MAX_ATTEMPTS, poolConfig); } public JedisCluster(Set nodes, int timeout, final GenericObjectPoolConfig poolConfig) { this(nodes, timeout, DEFAULT_MAX_ATTEMPTS, poolConfig); } public JedisCluster(Set clusterNodes, int timeout, int maxAttempts, final GenericObjectPoolConfig poolConfig) { this(clusterNodes, timeout, timeout, maxAttempts, poolConfig); } public JedisCluster(Set clusterNodes, int connectionTimeout, int soTimeout, int maxAttempts, final GenericObjectPoolConfig poolConfig) { this(clusterNodes, connectionTimeout, soTimeout, maxAttempts, null, poolConfig); } public JedisCluster(Set clusterNodes, int connectionTimeout, int soTimeout, int maxAttempts, String password, GenericObjectPoolConfig poolConfig) { this(clusterNodes, connectionTimeout, soTimeout, maxAttempts, password, null, poolConfig); } public JedisCluster(Set clusterNodes, int connectionTimeout, int soTimeout, int maxAttempts, String password, String clientName, GenericObjectPoolConfig poolConfig) { this(clusterNodes, connectionTimeout, soTimeout, maxAttempts, null, password, clientName, poolConfig); } public JedisCluster(Set clusterNodes, int connectionTimeout, int soTimeout, int maxAttempts, String user, String password, String clientName, GenericObjectPoolConfig poolConfig) { this(clusterNodes, DefaultJedisClientConfig.builder().connectionTimeoutMillis(connectionTimeout) .socketTimeoutMillis(soTimeout).user(user).password(password).clientName(clientName).build(), maxAttempts, poolConfig); } public JedisCluster(Set clusterNodes, int connectionTimeout, int soTimeout, int infiniteSoTimeout, int maxAttempts, String user, String password, String clientName, GenericObjectPoolConfig poolConfig) { this(clusterNodes, DefaultJedisClientConfig.builder().connectionTimeoutMillis(connectionTimeout) .socketTimeoutMillis(soTimeout).blockingSocketTimeoutMillis(infiniteSoTimeout) .user(user).password(password).clientName(clientName).build(), maxAttempts, poolConfig); } public JedisCluster(Set clusterNodes, int connectionTimeout, int soTimeout, int maxAttempts, String password, String clientName, GenericObjectPoolConfig poolConfig, boolean ssl) { this(clusterNodes, connectionTimeout, soTimeout, maxAttempts, null, password, clientName, poolConfig, ssl); } public JedisCluster(Set clusterNodes, int connectionTimeout, int soTimeout, int maxAttempts, String user, String password, String clientName, GenericObjectPoolConfig poolConfig, boolean ssl) { this(clusterNodes, DefaultJedisClientConfig.builder().connectionTimeoutMillis(connectionTimeout) .socketTimeoutMillis(soTimeout).user(user).password(password).clientName(clientName).ssl(ssl).build(), maxAttempts, poolConfig); } public JedisCluster(Set clusterNodes, JedisClientConfig clientConfig) { this(clusterNodes, clientConfig, DEFAULT_MAX_ATTEMPTS); } public JedisCluster(Set clusterNodes, JedisClientConfig clientConfig, int maxAttempts) { this(clusterNodes, clientConfig, maxAttempts, Duration.ofMillis((long) clientConfig.getSocketTimeoutMillis() * maxAttempts)); } /** * Creates a JedisCluster with multiple entry points.
* You can specify the timeout and the maximum attempts.
* * Additionally, you are free to provide a {@link JedisClientConfig} instance.
* You can use the {@link DefaultJedisClientConfig#builder()} builder pattern to customize your configuration, including socket timeouts, * username and passwords as well as SSL related parameters. * * @param clusterNodes Nodes to connect to. * @param clientConfig Client configuration parameters. * @param maxAttempts maximum attempts for executing a command. * @param maxTotalRetriesDuration Maximum time used for reconnecting. */ public JedisCluster(Set clusterNodes, JedisClientConfig clientConfig, int maxAttempts, Duration maxTotalRetriesDuration) { this(new ClusterConnectionProvider(clusterNodes, clientConfig), maxAttempts, maxTotalRetriesDuration, clientConfig.getRedisProtocol()); } public JedisCluster(Set clusterNodes, JedisClientConfig clientConfig, GenericObjectPoolConfig poolConfig) { this(clusterNodes, clientConfig, DEFAULT_MAX_ATTEMPTS, poolConfig); } public JedisCluster(Set clusterNodes, JedisClientConfig clientConfig, int maxAttempts, GenericObjectPoolConfig poolConfig) { this(clusterNodes, clientConfig, maxAttempts, Duration.ofMillis((long) clientConfig.getSocketTimeoutMillis() * maxAttempts), poolConfig); } public JedisCluster(Set clusterNodes, JedisClientConfig clientConfig, int maxAttempts, Duration maxTotalRetriesDuration, GenericObjectPoolConfig poolConfig) { this(new ClusterConnectionProvider(clusterNodes, clientConfig, poolConfig), maxAttempts, maxTotalRetriesDuration, clientConfig.getRedisProtocol()); } public JedisCluster(Set clusterNodes, JedisClientConfig clientConfig, GenericObjectPoolConfig poolConfig, Duration topologyRefreshPeriod, int maxAttempts, Duration maxTotalRetriesDuration) { this(new ClusterConnectionProvider(clusterNodes, clientConfig, poolConfig, topologyRefreshPeriod), maxAttempts, maxTotalRetriesDuration, clientConfig.getRedisProtocol()); } // Uses a fetched connection to process protocol. Should be avoided if possible. public JedisCluster(ClusterConnectionProvider provider, int maxAttempts, Duration maxTotalRetriesDuration) { super(provider, maxAttempts, maxTotalRetriesDuration); this.commandFlagsRegistry = StaticCommandFlagsRegistry.registry(); } private JedisCluster(ClusterConnectionProvider provider, int maxAttempts, Duration maxTotalRetriesDuration, RedisProtocol protocol) { super(provider, maxAttempts, maxTotalRetriesDuration, protocol); this.commandFlagsRegistry = StaticCommandFlagsRegistry.registry(); } @Experimental public JedisCluster(Set hnp, JedisClientConfig jedisClientConfig, CacheConfig cacheConfig) { this(hnp, jedisClientConfig, CacheFactory.getCache(cacheConfig)); } @Experimental public JedisCluster(Set clusterNodes, JedisClientConfig clientConfig, Cache clientSideCache) { this(clusterNodes, clientConfig, clientSideCache, DEFAULT_MAX_ATTEMPTS, Duration.ofMillis(DEFAULT_MAX_ATTEMPTS * clientConfig.getSocketTimeoutMillis())); } @Experimental public JedisCluster(Set clusterNodes, JedisClientConfig clientConfig, Cache clientSideCache, int maxAttempts, Duration maxTotalRetriesDuration) { this(new ClusterConnectionProvider(clusterNodes, clientConfig, clientSideCache), maxAttempts, maxTotalRetriesDuration, clientConfig.getRedisProtocol(), clientSideCache); } @Experimental public JedisCluster(Set clusterNodes, JedisClientConfig clientConfig, Cache clientSideCache, int maxAttempts, Duration maxTotalRetriesDuration, GenericObjectPoolConfig poolConfig) { this(new ClusterConnectionProvider(clusterNodes, clientConfig, clientSideCache, poolConfig), maxAttempts, maxTotalRetriesDuration, clientConfig.getRedisProtocol(), clientSideCache); } @Experimental public JedisCluster(Set clusterNodes, JedisClientConfig clientConfig, Cache clientSideCache, GenericObjectPoolConfig poolConfig) { this(new ClusterConnectionProvider(clusterNodes, clientConfig, clientSideCache, poolConfig), DEFAULT_MAX_ATTEMPTS, Duration.ofMillis(DEFAULT_MAX_ATTEMPTS * clientConfig.getSocketTimeoutMillis()), clientConfig.getRedisProtocol(), clientSideCache); } @Experimental public JedisCluster(Set clusterNodes, JedisClientConfig clientConfig, Cache clientSideCache, GenericObjectPoolConfig poolConfig, Duration topologyRefreshPeriod, int maxAttempts, Duration maxTotalRetriesDuration) { this(new ClusterConnectionProvider(clusterNodes, clientConfig, clientSideCache, poolConfig, topologyRefreshPeriod), maxAttempts, maxTotalRetriesDuration, clientConfig.getRedisProtocol(), clientSideCache); } @Experimental private JedisCluster(ClusterConnectionProvider provider, int maxAttempts, Duration maxTotalRetriesDuration, RedisProtocol protocol, Cache clientSideCache) { super(provider, maxAttempts, maxTotalRetriesDuration, protocol, clientSideCache); this.commandFlagsRegistry = StaticCommandFlagsRegistry.registry(); } private JedisCluster(CommandExecutor commandExecutor, ConnectionProvider connectionProvider, CommandObjects commandObjects, RedisProtocol redisProtocol, Cache cache, CommandFlagsRegistry commandFlagsRegistry) { super(commandExecutor, connectionProvider, commandObjects, redisProtocol, cache); this.commandFlagsRegistry = commandFlagsRegistry; } /** * Fluent builder for {@link JedisCluster} (Redis Cluster). *

* Obtain an instance via {@link #builder()}. *

*/ static public class Builder extends ClusterClientBuilder { @Override protected JedisCluster createClient() { return new JedisCluster(commandExecutor, connectionProvider, commandObjects, clientConfig.getRedisProtocol(), cache, getCommandFlags()); } } /** * Create a new builder for configuring JedisCluster instances. * @return a new {@link JedisCluster.Builder} instance */ public static Builder builder() { return new Builder(); } /** * Returns all nodes that were configured to connect to in key-value pairs ({@link Map}).
* Key is the HOST:PORT and the value is the connection pool. * @return the map of all connections. */ public Map getClusterNodes() { return ((ClusterConnectionProvider) provider).getNodes(); } /** * Returns the connection for one of the 16,384 slots. * @param slot the slot to retrieve the connection for. * @return connection of the provided slot. {@code close()} of this connection must be called after use. */ public Connection getConnectionFromSlot(int slot) { return ((ClusterConnectionProvider) provider).getConnectionFromSlot(slot); } // commands public long spublish(String channel, String message) { return executeCommand(commandObjects.spublish(channel, message)); } public long spublish(byte[] channel, byte[] message) { return executeCommand(commandObjects.spublish(channel, message)); } public void ssubscribe(final JedisShardedPubSub jedisPubSub, final String... channels) { try (Connection connection = getConnectionFromSlot(JedisClusterCRC16.getSlot(channels[0]))) { jedisPubSub.proceed(connection, channels); } } public void ssubscribe(BinaryJedisShardedPubSub jedisPubSub, final byte[]... channels) { try (Connection connection = getConnectionFromSlot(JedisClusterCRC16.getSlot(channels[0]))) { jedisPubSub.proceed(connection, channels); } } // commands @Override public ClusterPipeline pipelined() { return new ClusterPipeline((ClusterConnectionProvider) provider, (ClusterCommandObjects) commandObjects, commandFlagsRegistry); } /** * @param doMulti param * @return nothing * @throws UnsupportedOperationException */ @Override public AbstractTransaction transaction(boolean doMulti) { throw new UnsupportedOperationException(); } public final T executeCommandToReplica(CommandObject commandObject) { if (!(executor instanceof ClusterCommandExecutor)) { throw new UnsupportedOperationException("Support only execute to replica in ClusterCommandExecutor"); } return ((ClusterCommandExecutor) executor).executeCommandToReplica(commandObject); } } ================================================ FILE: src/main/java/redis/clients/jedis/JedisClusterInfoCache.java ================================================ package redis.clients.jedis; import java.time.Duration; import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; import java.util.HashMap; import java.util.HashSet; import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.Map.Entry; import java.util.Objects; import java.util.Set; import java.util.concurrent.Executors; import java.util.concurrent.ScheduledExecutorService; import java.util.concurrent.TimeUnit; import java.util.concurrent.locks.Lock; import java.util.concurrent.locks.ReentrantLock; import java.util.concurrent.locks.ReentrantReadWriteLock; import org.apache.commons.pool2.impl.GenericObjectPoolConfig; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import redis.clients.jedis.annots.Experimental; import redis.clients.jedis.annots.Internal; import redis.clients.jedis.csc.Cache; import redis.clients.jedis.exceptions.JedisClusterOperationException; import redis.clients.jedis.exceptions.JedisException; import redis.clients.jedis.util.SafeEncoder; import static redis.clients.jedis.RedisClusterClient.INIT_NO_ERROR_PROPERTY; @Internal public class JedisClusterInfoCache { private static final Logger logger = LoggerFactory.getLogger(JedisClusterInfoCache.class); private final Map nodes = new HashMap<>(); private final Map primaryNodesCache = new HashMap<>(); private final ConnectionPool[] slots = new ConnectionPool[Protocol.CLUSTER_HASHSLOTS]; private final HostAndPort[] slotNodes = new HostAndPort[Protocol.CLUSTER_HASHSLOTS]; private final List[] replicaSlots; private final ReentrantReadWriteLock rwl = new ReentrantReadWriteLock(); private final Lock r = rwl.readLock(); private final Lock w = rwl.writeLock(); private final Lock rediscoverLock = new ReentrantLock(); private final GenericObjectPoolConfig poolConfig; private final JedisClientConfig clientConfig; private final Cache clientSideCache; private final Set startNodes; private static final int MASTER_NODE_INDEX = 2; /** * The single thread executor for the topology refresh task. */ private ScheduledExecutorService topologyRefreshExecutor = null; class TopologyRefreshTask implements Runnable { @Override public void run() { logger.debug("Cluster topology refresh run, old nodes: {}", nodes.keySet()); renewClusterSlots(null); logger.debug("Cluster topology refresh run, new nodes: {}", nodes.keySet()); } } public JedisClusterInfoCache(final JedisClientConfig clientConfig, final Set startNodes) { this(clientConfig, null, null, startNodes); } @Experimental public JedisClusterInfoCache(final JedisClientConfig clientConfig, Cache clientSideCache, final Set startNodes) { this(clientConfig, clientSideCache, null, startNodes); } public JedisClusterInfoCache(final JedisClientConfig clientConfig, final GenericObjectPoolConfig poolConfig, final Set startNodes) { this(clientConfig, null, poolConfig, startNodes); } @Experimental public JedisClusterInfoCache(final JedisClientConfig clientConfig, Cache clientSideCache, final GenericObjectPoolConfig poolConfig, final Set startNodes) { this(clientConfig, clientSideCache, poolConfig, startNodes, null); } public JedisClusterInfoCache(final JedisClientConfig clientConfig, final GenericObjectPoolConfig poolConfig, final Set startNodes, final Duration topologyRefreshPeriod) { this(clientConfig, null, poolConfig, startNodes, topologyRefreshPeriod); } @Experimental public JedisClusterInfoCache(final JedisClientConfig clientConfig, Cache clientSideCache, final GenericObjectPoolConfig poolConfig, final Set startNodes, final Duration topologyRefreshPeriod) { this.poolConfig = poolConfig; this.clientConfig = clientConfig; this.clientSideCache = clientSideCache; this.startNodes = startNodes; if (clientConfig.getAuthXManager() != null) { clientConfig.getAuthXManager().start(); } if (topologyRefreshPeriod != null) { logger.info("Cluster topology refresh start, period: {}, startNodes: {}", topologyRefreshPeriod, startNodes); topologyRefreshExecutor = Executors.newSingleThreadScheduledExecutor(); topologyRefreshExecutor.scheduleWithFixedDelay(new TopologyRefreshTask(), topologyRefreshPeriod.toMillis(), topologyRefreshPeriod.toMillis(), TimeUnit.MILLISECONDS); } if (clientConfig.isReadOnlyForRedisClusterReplicas()) { replicaSlots = new ArrayList[Protocol.CLUSTER_HASHSLOTS]; } else { replicaSlots = null; } } /** * Check whether the number and order of slots in the cluster topology are equal to CLUSTER_HASHSLOTS * @param slotsInfo the cluster topology * @return if slots is ok, return true, elese return false. */ private boolean checkClusterSlotSequence(List slotsInfo) { List slots = new ArrayList<>(); for (Object slotInfoObj : slotsInfo) { List slotInfo = (List)slotInfoObj; slots.addAll(getAssignedSlotArray(slotInfo)); } Collections.sort(slots); if (slots.size() != Protocol.CLUSTER_HASHSLOTS) { return false; } for (int i = 0; i < Protocol.CLUSTER_HASHSLOTS; ++i) { if (i != slots.get(i)) { return false; } } return true; } public void discoverClusterNodesAndSlots(Connection jedis) { List slotsInfo = executeClusterSlots(jedis); if (System.getProperty(INIT_NO_ERROR_PROPERTY) == null) { if (slotsInfo.isEmpty()) { throw new JedisClusterOperationException("Cluster slots list is empty."); } if (!checkClusterSlotSequence(slotsInfo)) { throw new JedisClusterOperationException("Cluster slots have holes."); } } w.lock(); try { reset(); for (Object slotInfoObj : slotsInfo) { List slotInfo = (List) slotInfoObj; if (slotInfo.size() <= MASTER_NODE_INDEX) { continue; } List slotNums = getAssignedSlotArray(slotInfo); // hostInfos int size = slotInfo.size(); for (int i = MASTER_NODE_INDEX; i < size; i++) { List hostInfos = (List) slotInfo.get(i); if (hostInfos.isEmpty()) { continue; } HostAndPort targetNode = generateHostAndPort(hostInfos); setupNodeIfNotExist(targetNode); if (i == MASTER_NODE_INDEX) { primaryNodesCache.put(getNodeKey(targetNode), getNode(targetNode)); assignSlotsToNode(slotNums, targetNode); } else if (clientConfig.isReadOnlyForRedisClusterReplicas()) { assignSlotsToReplicaNode(slotNums, targetNode); } } } } finally { w.unlock(); } } public void renewClusterSlots(Connection jedis) { // If rediscovering is already in process - no need to start one more same rediscovering, just return if (rediscoverLock.tryLock()) { try { // First, if jedis is available, use jedis renew. if (jedis != null) { try { discoverClusterSlots(jedis); return; } catch (JedisException e) { // try nodes from all pools } } // Then, we use startNodes to try, as long as startNodes is available, // whether it is vip, domain, or physical ip, it will succeed. if (startNodes != null) { for (HostAndPort hostAndPort : startNodes) { try (Connection j = new Connection(hostAndPort, clientConfig)) { discoverClusterSlots(j); return; } catch (JedisException e) { // try next nodes } } } // Finally, we go back to the ShuffledNodesPool and try the remaining physical nodes. for (ConnectionPool jp : getShuffledNodesPool()) { try (Connection j = jp.getResource()) { // If already tried in startNodes, skip this node. if (startNodes != null && startNodes.contains(j.getHostAndPort())) { continue; } discoverClusterSlots(j); return; } catch (JedisException e) { // try next nodes } } } finally { rediscoverLock.unlock(); } } } private void discoverClusterSlots(Connection jedis) { List slotsInfo = executeClusterSlots(jedis); if (System.getProperty(INIT_NO_ERROR_PROPERTY) == null) { if (slotsInfo.isEmpty()) { throw new JedisClusterOperationException("Cluster slots list is empty."); } if (!checkClusterSlotSequence(slotsInfo)) { throw new JedisClusterOperationException("Cluster slots have holes."); } } w.lock(); try { resetSlots(); if (clientSideCache != null) { clientSideCache.flush(); } Set hostAndPortKeys = new HashSet<>(); for (Object slotInfoObj : slotsInfo) { List slotInfo = (List) slotInfoObj; if (slotInfo.size() <= MASTER_NODE_INDEX) { continue; } List slotNums = getAssignedSlotArray(slotInfo); int size = slotInfo.size(); for (int i = MASTER_NODE_INDEX; i < size; i++) { List hostInfos = (List) slotInfo.get(i); if (hostInfos.isEmpty()) { continue; } HostAndPort targetNode = generateHostAndPort(hostInfos); hostAndPortKeys.add(getNodeKey(targetNode)); setupNodeIfNotExist(targetNode); if (i == MASTER_NODE_INDEX) { assignSlotsToNode(slotNums, targetNode); } else if (clientConfig.isReadOnlyForRedisClusterReplicas()) { assignSlotsToReplicaNode(slotNums, targetNode); } } } // Remove dead nodes according to the latest query Iterator> entryIt = nodes.entrySet().iterator(); while (entryIt.hasNext()) { Entry entry = entryIt.next(); if (!hostAndPortKeys.contains(entry.getKey())) { ConnectionPool pool = entry.getValue(); try { if (pool != null) { pool.destroy(); } } catch (Exception e) { // pass, may be this node dead } entryIt.remove(); } } } finally { w.unlock(); } } private HostAndPort generateHostAndPort(List hostInfos) { String host = SafeEncoder.encode((byte[]) hostInfos.get(0)); int port = ((Long) hostInfos.get(1)).intValue(); return new HostAndPort(host, port); } public ConnectionPool setupNodeIfNotExist(final HostAndPort node) { w.lock(); try { String nodeKey = getNodeKey(node); ConnectionPool existingPool = nodes.get(nodeKey); if (existingPool != null) return existingPool; ConnectionPool nodePool = createNodePool(node); nodes.put(nodeKey, nodePool); return nodePool; } finally { w.unlock(); } } private ConnectionPool createNodePool(HostAndPort node) { if (poolConfig == null) { if (clientSideCache == null) { return new ConnectionPool(node, clientConfig); } else { return new ConnectionPool(node, clientConfig, clientSideCache); } } else { if (clientSideCache == null) { return new ConnectionPool(node, clientConfig, poolConfig); } else { return new ConnectionPool(node, clientConfig, clientSideCache, poolConfig); } } } public void assignSlotToNode(int slot, HostAndPort targetNode) { w.lock(); try { ConnectionPool targetPool = setupNodeIfNotExist(targetNode); slots[slot] = targetPool; slotNodes[slot] = targetNode; } finally { w.unlock(); } } public void assignSlotsToNode(List targetSlots, HostAndPort targetNode) { w.lock(); try { ConnectionPool targetPool = setupNodeIfNotExist(targetNode); for (Integer slot : targetSlots) { slots[slot] = targetPool; slotNodes[slot] = targetNode; } } finally { w.unlock(); } } public void assignSlotsToReplicaNode(List targetSlots, HostAndPort targetNode) { w.lock(); try { ConnectionPool targetPool = setupNodeIfNotExist(targetNode); for (Integer slot : targetSlots) { if (replicaSlots[slot] == null) { replicaSlots[slot] = new ArrayList<>(); } replicaSlots[slot].add(targetPool); } } finally { w.unlock(); } } public ConnectionPool getNode(String nodeKey) { r.lock(); try { return nodes.get(nodeKey); } finally { r.unlock(); } } public ConnectionPool getNode(HostAndPort node) { return getNode(getNodeKey(node)); } public ConnectionPool getSlotPool(int slot) { r.lock(); try { return slots[slot]; } finally { r.unlock(); } } public HostAndPort getSlotNode(int slot) { r.lock(); try { return slotNodes[slot]; } finally { r.unlock(); } } public List getSlotReplicaPools(int slot) { r.lock(); try { return replicaSlots[slot]; } finally { r.unlock(); } } public Map getNodes() { r.lock(); try { return new HashMap<>(nodes); } finally { r.unlock(); } } public Map getPrimaryNodes() { r.lock(); try { return new HashMap<>(primaryNodesCache); } finally { r.unlock(); } } public List getShuffledPrimaryNodesPool() { r.lock(); try { List pools = new ArrayList<>(primaryNodesCache.values()); Collections.shuffle(pools); return pools; } finally { r.unlock(); } } public List getShuffledNodesPool() { r.lock(); try { List pools = new ArrayList<>(nodes.values()); Collections.shuffle(pools); return pools; } finally { r.unlock(); } } /** * Clear discovered nodes collections and gently release allocated resources */ public void reset() { w.lock(); try { resetNodes(); resetSlots(); } finally { w.unlock(); } } private void resetSlots() { Arrays.fill(slots, null); Arrays.fill(slotNodes, null); resetReplicaSlots(); } private void resetReplicaSlots() { if (replicaSlots == null) { return; } Arrays.stream(replicaSlots).filter(Objects::nonNull).forEach(List::clear); Arrays.fill(replicaSlots, null); } private void resetNodes() { for (ConnectionPool pool : nodes.values()) { try { if (pool != null) { pool.destroy(); } } catch (RuntimeException e) { // pass } } nodes.clear(); primaryNodesCache.clear(); } public void close() { reset(); if (topologyRefreshExecutor != null) { logger.info("Cluster topology refresh shutdown, startNodes: {}", startNodes); topologyRefreshExecutor.shutdownNow(); } } public static String getNodeKey(HostAndPort hnp) { return hnp.toString(); } @SuppressWarnings("unchecked") private List executeClusterSlots(Connection jedis) { CommandArguments clusterSlotsCmd = new CommandArguments(Protocol.Command.CLUSTER).add( "SLOTS"); return (List) jedis.executeCommand(clusterSlotsCmd); } private List getAssignedSlotArray(List slotInfo) { List slotNums = new ArrayList<>(); for (int slot = ((Long) slotInfo.get(0)).intValue(); slot <= ((Long) slotInfo.get(1)) .intValue(); slot++) { slotNums.add(slot); } return slotNums; } } ================================================ FILE: src/main/java/redis/clients/jedis/JedisFactory.java ================================================ package redis.clients.jedis; import java.net.URI; import javax.net.ssl.HostnameVerifier; import javax.net.ssl.SSLParameters; import javax.net.ssl.SSLSocketFactory; import org.apache.commons.pool2.PooledObject; import org.apache.commons.pool2.PooledObjectFactory; import org.apache.commons.pool2.impl.DefaultPooledObject; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import redis.clients.jedis.exceptions.InvalidURIException; import redis.clients.jedis.exceptions.JedisException; import redis.clients.jedis.util.JedisURIHelper; /** * PooledObjectFactory implementation for creating and managing {@link Jedis} instances in connection pools. *

* This factory is used internally by {@link JedisPool} and {@link JedisSentinelPool} to create, validate, * and destroy pooled Jedis connections. *

* * @deprecated JedisFactory is used exclusively with the deprecated {@link JedisPool} and {@link JedisSentinelPool} * classes. For modern Redis clients ({@link RedisClient}, {@link RedisSentinelClient}), the framework * uses {@link ConnectionFactory} internally, which manages {@link Connection} objects instead of * {@link Jedis} instances. There is no direct replacement for JedisFactory as connection management * is handled automatically by the new client architecture. */ @Deprecated public class JedisFactory implements PooledObjectFactory { private static final Logger logger = LoggerFactory.getLogger(JedisFactory.class); private final JedisSocketFactory jedisSocketFactory; private final JedisClientConfig clientConfig; protected JedisFactory(final String host, final int port, final int connectionTimeout, final int soTimeout, final String password, final int database, final String clientName) { this(host, port, connectionTimeout, soTimeout, password, database, clientName, false, null, null, null); } protected JedisFactory(final String host, final int port, final int connectionTimeout, final int soTimeout, final String user, final String password, final int database, final String clientName) { this(host, port, connectionTimeout, soTimeout, 0, user, password, database, clientName); } protected JedisFactory(final String host, final int port, final int connectionTimeout, final int soTimeout, final int infiniteSoTimeout, final String user, final String password, final int database, final String clientName) { this(host, port, connectionTimeout, soTimeout, infiniteSoTimeout, user, password, database, clientName, false, null, null, null); } /** * {@link #setHostAndPort(redis.clients.jedis.HostAndPort) setHostAndPort} must be called later. */ JedisFactory(final int connectionTimeout, final int soTimeout, final int infiniteSoTimeout, final String user, final String password, final int database, final String clientName) { this(connectionTimeout, soTimeout, infiniteSoTimeout, user, password, database, clientName, false, null, null, null); } protected JedisFactory(final String host, final int port, final int connectionTimeout, final int soTimeout, final String password, final int database, final String clientName, final boolean ssl, final SSLSocketFactory sslSocketFactory, final SSLParameters sslParameters, final HostnameVerifier hostnameVerifier) { this(host, port, connectionTimeout, soTimeout, null, password, database, clientName, ssl, sslSocketFactory, sslParameters, hostnameVerifier); } protected JedisFactory(final String host, final int port, final int connectionTimeout, final int soTimeout, final String user, final String password, final int database, final String clientName, final boolean ssl, final SSLSocketFactory sslSocketFactory, final SSLParameters sslParameters, final HostnameVerifier hostnameVerifier) { this(host, port, connectionTimeout, soTimeout, 0, user, password, database, clientName, ssl, sslSocketFactory, sslParameters, hostnameVerifier); } protected JedisFactory(final HostAndPort hostAndPort, final JedisClientConfig clientConfig) { this.clientConfig = clientConfig; this.jedisSocketFactory = new DefaultJedisSocketFactory(hostAndPort, this.clientConfig); } protected JedisFactory(final String host, final int port, final int connectionTimeout, final int soTimeout, final int infiniteSoTimeout, final String user, final String password, final int database, final String clientName, final boolean ssl, final SSLSocketFactory sslSocketFactory, final SSLParameters sslParameters, final HostnameVerifier hostnameVerifier) { this.clientConfig = DefaultJedisClientConfig.builder().connectionTimeoutMillis(connectionTimeout) .socketTimeoutMillis(soTimeout).blockingSocketTimeoutMillis(infiniteSoTimeout).user(user) .password(password).database(database).clientName(clientName) .ssl(ssl).sslSocketFactory(sslSocketFactory) .sslParameters(sslParameters).hostnameVerifier(hostnameVerifier).build(); this.jedisSocketFactory = new DefaultJedisSocketFactory(new HostAndPort(host, port), this.clientConfig); } protected JedisFactory(final JedisSocketFactory jedisSocketFactory, final JedisClientConfig clientConfig) { this.clientConfig = clientConfig; this.jedisSocketFactory = jedisSocketFactory; } /** * {@link #setHostAndPort(redis.clients.jedis.HostAndPort) setHostAndPort} must be called later. */ JedisFactory(final int connectionTimeout, final int soTimeout, final int infiniteSoTimeout, final String user, final String password, final int database, final String clientName, final boolean ssl, final SSLSocketFactory sslSocketFactory, final SSLParameters sslParameters, final HostnameVerifier hostnameVerifier) { this(DefaultJedisClientConfig.builder().connectionTimeoutMillis(connectionTimeout) .socketTimeoutMillis(soTimeout).blockingSocketTimeoutMillis(infiniteSoTimeout).user(user) .password(password).database(database).clientName(clientName) .ssl(ssl).sslSocketFactory(sslSocketFactory) .sslParameters(sslParameters).hostnameVerifier(hostnameVerifier).build()); } /** * {@link JedisFactory#setHostAndPort(redis.clients.jedis.HostAndPort) setHostAndPort} must be called later. */ JedisFactory(final JedisClientConfig clientConfig) { this.clientConfig = clientConfig; this.jedisSocketFactory = new DefaultJedisSocketFactory(clientConfig); } protected JedisFactory(final URI uri, final int connectionTimeout, final int soTimeout, final String clientName) { this(uri, connectionTimeout, soTimeout, clientName, null, null, null); } protected JedisFactory(final URI uri, final int connectionTimeout, final int soTimeout, final String clientName, final SSLSocketFactory sslSocketFactory, final SSLParameters sslParameters, final HostnameVerifier hostnameVerifier) { this(uri, connectionTimeout, soTimeout, 0, clientName, sslSocketFactory, sslParameters, hostnameVerifier); } protected JedisFactory(final URI uri, final int connectionTimeout, final int soTimeout, final int infiniteSoTimeout, final String clientName, final SSLSocketFactory sslSocketFactory, final SSLParameters sslParameters, final HostnameVerifier hostnameVerifier) { if (!JedisURIHelper.isValid(uri)) { throw new InvalidURIException(String.format( "Cannot open Redis connection due invalid URI. %s", uri.toString())); } this.clientConfig = DefaultJedisClientConfig.builder().connectionTimeoutMillis(connectionTimeout) .socketTimeoutMillis(soTimeout).blockingSocketTimeoutMillis(infiniteSoTimeout) .user(JedisURIHelper.getUser(uri)).password(JedisURIHelper.getPassword(uri)) .database(JedisURIHelper.getDBIndex(uri)).clientName(clientName) .protocol(JedisURIHelper.getRedisProtocol(uri)) .ssl(JedisURIHelper.isRedisSSLScheme(uri)).sslSocketFactory(sslSocketFactory) .sslParameters(sslParameters).hostnameVerifier(hostnameVerifier).build(); this.jedisSocketFactory = new DefaultJedisSocketFactory(new HostAndPort(uri.getHost(), uri.getPort()), this.clientConfig); } void setHostAndPort(final HostAndPort hostAndPort) { if (!(jedisSocketFactory instanceof DefaultJedisSocketFactory)) { throw new IllegalStateException("setHostAndPort method has limited capability."); } ((DefaultJedisSocketFactory) jedisSocketFactory).updateHostAndPort(hostAndPort); } @Override public void activateObject(PooledObject pooledJedis) throws Exception { final Jedis jedis = pooledJedis.getObject(); if (jedis.getDB() != clientConfig.getDatabase()) { jedis.select(clientConfig.getDatabase()); } } @Override public void destroyObject(PooledObject pooledJedis) throws Exception { final Jedis jedis = pooledJedis.getObject(); if (jedis.isConnected()) { try { jedis.close(); } catch (RuntimeException e) { logger.debug("Error while close", e); } } } @Override public PooledObject makeObject() throws Exception { Jedis jedis = null; try { jedis = new Jedis(jedisSocketFactory, clientConfig); return new DefaultPooledObject<>(jedis); } catch (JedisException je) { logger.debug("Error while makeObject", je); throw je; } } @Override public void passivateObject(PooledObject pooledJedis) throws Exception { // TODO maybe should select db 0? Not sure right now. } @Override public boolean validateObject(PooledObject pooledJedis) { final Jedis jedis = pooledJedis.getObject(); try { boolean targetHasNotChanged = true; if (jedisSocketFactory instanceof DefaultJedisSocketFactory) { HostAndPort targetAddress = ((DefaultJedisSocketFactory) jedisSocketFactory).getHostAndPort(); HostAndPort objectAddress = jedis.getConnection().getHostAndPort(); targetHasNotChanged = targetAddress.getHost().equals(objectAddress.getHost()) && targetAddress.getPort() == objectAddress.getPort(); } return targetHasNotChanged && jedis.getConnection().isConnected() && jedis.ping().equals("PONG"); } catch (final Exception e) { logger.warn("Error while validating pooled Jedis object.", e); return false; } } } ================================================ FILE: src/main/java/redis/clients/jedis/JedisMetaInfo.java ================================================ package redis.clients.jedis; import java.io.InputStream; import java.util.Properties; import org.slf4j.LoggerFactory; /** * Jedis Meta info load version groupId */ class JedisMetaInfo { private static final String groupId; private static final String artifactId; private static final String version; static { Properties p = new Properties(); try (InputStream in = JedisMetaInfo.class.getClassLoader() .getResourceAsStream("redis/clients/jedis/pom.properties")) { p.load(in); } catch (Exception e) { LoggerFactory.getLogger(JedisMetaInfo.class) .error("Load Jedis meta info from pom.properties failed", e); } groupId = p.getProperty("groupId", null); artifactId = p.getProperty("artifactId", null); version = p.getProperty("version", null); } public static String getGroupId() { return groupId; } public static String getArtifactId() { return artifactId; } public static String getVersion() { return version; } } ================================================ FILE: src/main/java/redis/clients/jedis/JedisMonitor.java ================================================ package redis.clients.jedis; public abstract class JedisMonitor { protected Connection client; public void proceed(Connection client) { this.client = client; this.client.setTimeoutInfinite(); do { String command = client.getBulkReply(); onCommand(command); } while (client.isConnected()); } public abstract void onCommand(String command); } ================================================ FILE: src/main/java/redis/clients/jedis/JedisPool.java ================================================ package redis.clients.jedis; import java.net.URI; import java.util.function.Consumer; import java.util.function.Function; import javax.net.ssl.HostnameVerifier; import javax.net.ssl.SSLParameters; import javax.net.ssl.SSLSocketFactory; import org.apache.commons.pool2.PooledObjectFactory; import org.apache.commons.pool2.impl.GenericObjectPoolConfig; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import redis.clients.jedis.util.JedisURIHelper; import redis.clients.jedis.util.Pool; /** * JedisPool is a pooled connection client for standalone Redis servers. * * @deprecated Use {@link RedisClient} instead. RedisClient provides the same functionality * with a cleaner API and simplified constructor options. For basic usage, simple * constructors are available. For advanced configuration, use {@link RedisClient#builder()}. */ @Deprecated public class JedisPool extends Pool { private static final Logger log = LoggerFactory.getLogger(JedisPool.class); public JedisPool() { this(Protocol.DEFAULT_HOST, Protocol.DEFAULT_PORT); } /** * WARNING: This constructor only accepts a uri string as {@code url}. {@link JedisURIHelper#isValid(java.net.URI)} * can be used before this. *

* To use a host string, {@link #JedisPool(java.lang.String, int)} can be used with {@link Protocol#DEFAULT_PORT}. * * @param url */ public JedisPool(final String url) { this(URI.create(url)); } /** * WARNING: This constructor only accepts a uri string as {@code url}. {@link JedisURIHelper#isValid(java.net.URI)} * can be used before this. *

* To use a host string, * {@link #JedisPool(java.lang.String, int, boolean, javax.net.ssl.SSLSocketFactory, javax.net.ssl.SSLParameters, * javax.net.ssl.HostnameVerifier)} can be used with {@link Protocol#DEFAULT_PORT} and {@code ssl=true}. * * @param url * @param sslSocketFactory * @param sslParameters * @param hostnameVerifier */ public JedisPool(final String url, final SSLSocketFactory sslSocketFactory, final SSLParameters sslParameters, final HostnameVerifier hostnameVerifier) { this(new GenericObjectPoolConfig(), new JedisFactory(URI.create(url), Protocol.DEFAULT_TIMEOUT, Protocol.DEFAULT_TIMEOUT, null, sslSocketFactory, sslParameters, hostnameVerifier)); } public JedisPool(final String host, final int port) { this(new HostAndPort(host, port), DefaultJedisClientConfig.builder().build()); } public JedisPool(final String host, final int port, final boolean ssl) { this(new HostAndPort(host, port), DefaultJedisClientConfig.builder().ssl(ssl).build()); } public JedisPool(final String host, final int port, final boolean ssl, final SSLSocketFactory sslSocketFactory, final SSLParameters sslParameters, final HostnameVerifier hostnameVerifier) { this(new HostAndPort(host, port), DefaultJedisClientConfig.builder().ssl(ssl) .sslSocketFactory(sslSocketFactory).sslParameters(sslParameters) .hostnameVerifier(hostnameVerifier).build()); } public JedisPool(final String host, int port, String user, final String password) { this(new HostAndPort(host, port), DefaultJedisClientConfig.builder().user(user).password(password).build()); } public JedisPool(final HostAndPort hostAndPort, final JedisClientConfig clientConfig) { this(new JedisFactory(hostAndPort, clientConfig)); } public JedisPool(PooledObjectFactory factory) { super(factory); } public JedisPool(final GenericObjectPoolConfig poolConfig) { this(poolConfig, Protocol.DEFAULT_HOST, Protocol.DEFAULT_PORT); } /** * WARNING: This constructor only accepts a uri string as {@code url}. {@link JedisURIHelper#isValid(java.net.URI)} * can be used before this. *

* To use a host string, * {@link #JedisPool(org.apache.commons.pool2.impl.GenericObjectPoolConfig, java.lang.String, int)} can be used with * {@link Protocol#DEFAULT_PORT}. * * @param poolConfig * @param url */ public JedisPool(final GenericObjectPoolConfig poolConfig, final String url) { this(poolConfig, URI.create(url)); } public JedisPool(final GenericObjectPoolConfig poolConfig, final String host, final int port) { this(poolConfig, host, port, Protocol.DEFAULT_TIMEOUT); } public JedisPool(final GenericObjectPoolConfig poolConfig, final String host, final int port, final boolean ssl) { this(poolConfig, host, port, Protocol.DEFAULT_TIMEOUT, ssl); } public JedisPool(final GenericObjectPoolConfig poolConfig, final String host, final int port, final boolean ssl, final SSLSocketFactory sslSocketFactory, final SSLParameters sslParameters, final HostnameVerifier hostnameVerifier) { this(poolConfig, host, port, Protocol.DEFAULT_TIMEOUT, ssl, sslSocketFactory, sslParameters, hostnameVerifier); } public JedisPool(final GenericObjectPoolConfig poolConfig, final String host, int port, String user, final String password) { this(poolConfig, host, port, Protocol.DEFAULT_TIMEOUT, user, password, Protocol.DEFAULT_DATABASE); } public JedisPool(final GenericObjectPoolConfig poolConfig, final String host, final int port, final int timeout) { this(poolConfig, host, port, timeout, null); } public JedisPool(final GenericObjectPoolConfig poolConfig, final String host, final int port, final int timeout, final boolean ssl) { this(poolConfig, host, port, timeout, null, ssl); } public JedisPool(final GenericObjectPoolConfig poolConfig, final String host, final int port, final int timeout, final boolean ssl, final SSLSocketFactory sslSocketFactory, final SSLParameters sslParameters, final HostnameVerifier hostnameVerifier) { this(poolConfig, host, port, timeout, null, ssl, sslSocketFactory, sslParameters, hostnameVerifier); } public JedisPool(final GenericObjectPoolConfig poolConfig, final String host, int port, int timeout, final String password) { this(poolConfig, host, port, timeout, password, Protocol.DEFAULT_DATABASE); } public JedisPool(final GenericObjectPoolConfig poolConfig, final String host, int port, int timeout, final String password, final boolean ssl) { this(poolConfig, host, port, timeout, password, Protocol.DEFAULT_DATABASE, ssl); } public JedisPool(final GenericObjectPoolConfig poolConfig, final String host, int port, int timeout, final String password, final boolean ssl, final SSLSocketFactory sslSocketFactory, final SSLParameters sslParameters, final HostnameVerifier hostnameVerifier) { this(poolConfig, host, port, timeout, password, Protocol.DEFAULT_DATABASE, ssl, sslSocketFactory, sslParameters, hostnameVerifier); } public JedisPool(final GenericObjectPoolConfig poolConfig, final String host, int port, int timeout, final String user, final String password) { this(poolConfig, host, port, timeout, user, password, Protocol.DEFAULT_DATABASE); } public JedisPool(final GenericObjectPoolConfig poolConfig, final String host, int port, int timeout, final String user, final String password, final boolean ssl) { this(poolConfig, host, port, timeout, user, password, Protocol.DEFAULT_DATABASE, ssl); } public JedisPool(final GenericObjectPoolConfig poolConfig, final String host, int port, int timeout, final String password, final int database) { this(poolConfig, host, port, timeout, password, database, null); } public JedisPool(final GenericObjectPoolConfig poolConfig, final String host, int port, int timeout, final String password, final int database, final boolean ssl) { this(poolConfig, host, port, timeout, password, database, null, ssl); } public JedisPool(final GenericObjectPoolConfig poolConfig, final String host, int port, int timeout, final String password, final int database, final boolean ssl, final SSLSocketFactory sslSocketFactory, final SSLParameters sslParameters, final HostnameVerifier hostnameVerifier) { this(poolConfig, host, port, timeout, password, database, null, ssl, sslSocketFactory, sslParameters, hostnameVerifier); } public JedisPool(final GenericObjectPoolConfig poolConfig, final String host, int port, int timeout, final String user, final String password, final int database) { this(poolConfig, host, port, timeout, user, password, database, null); } public JedisPool(final GenericObjectPoolConfig poolConfig, final String host, int port, int timeout, final String user, final String password, final int database, final boolean ssl) { this(poolConfig, host, port, timeout, user, password, database, null, ssl); } public JedisPool(final GenericObjectPoolConfig poolConfig, final String host, int port, int timeout, final String password, final int database, final String clientName) { this(poolConfig, host, port, timeout, timeout, password, database, clientName); } public JedisPool(final GenericObjectPoolConfig poolConfig, final String host, int port, int timeout, final String password, final int database, final String clientName, final boolean ssl) { this(poolConfig, host, port, timeout, timeout, password, database, clientName, ssl); } public JedisPool(final GenericObjectPoolConfig poolConfig, final String host, int port, int timeout, final String password, final int database, final String clientName, final boolean ssl, final SSLSocketFactory sslSocketFactory, final SSLParameters sslParameters, final HostnameVerifier hostnameVerifier) { this(poolConfig, host, port, timeout, timeout, password, database, clientName, ssl, sslSocketFactory, sslParameters, hostnameVerifier); } public JedisPool(final GenericObjectPoolConfig poolConfig, final String host, int port, int timeout, final String user, final String password, final int database, final String clientName) { this(poolConfig, host, port, timeout, timeout, user, password, database, clientName); } public JedisPool(final GenericObjectPoolConfig poolConfig, final String host, int port, int timeout, final String user, final String password, final int database, final String clientName, final boolean ssl) { this(poolConfig, host, port, timeout, timeout, user, password, database, clientName, ssl); } public JedisPool(final GenericObjectPoolConfig poolConfig, final String host, int port, final int connectionTimeout, final int soTimeout, final String password, final int database, final String clientName) { this(poolConfig, new JedisFactory(host, port, connectionTimeout, soTimeout, password, database, clientName)); } public JedisPool(final GenericObjectPoolConfig poolConfig, final String host, final int port, final int connectionTimeout, final int soTimeout, final String password, final int database, final String clientName, final boolean ssl) { this(poolConfig, host, port, connectionTimeout, soTimeout, password, database, clientName, ssl, null, null, null); } public JedisPool(final GenericObjectPoolConfig poolConfig, final String host, int port, final int connectionTimeout, final int soTimeout, final String password, final int database, final String clientName, final boolean ssl, final SSLSocketFactory sslSocketFactory, final SSLParameters sslParameters, final HostnameVerifier hostnameVerifier) { this(poolConfig, new JedisFactory(host, port, connectionTimeout, soTimeout, password, database, clientName, ssl, sslSocketFactory, sslParameters, hostnameVerifier)); } public JedisPool(final GenericObjectPoolConfig poolConfig, final String host, int port, final int connectionTimeout, final int soTimeout, final String user, final String password, final int database, final String clientName) { this(poolConfig, new JedisFactory(host, port, connectionTimeout, soTimeout, user, password, database, clientName)); } public JedisPool(final GenericObjectPoolConfig poolConfig, final String host, final int port, final int connectionTimeout, final int soTimeout, final String user, final String password, final int database, final String clientName, final boolean ssl) { this(poolConfig, host, port, connectionTimeout, soTimeout, user, password, database, clientName, ssl, null, null, null); } public JedisPool(final GenericObjectPoolConfig poolConfig, final String host, int port, final int connectionTimeout, final int soTimeout, final String user, final String password, final int database, final String clientName, final boolean ssl, final SSLSocketFactory sslSocketFactory, final SSLParameters sslParameters, final HostnameVerifier hostnameVerifier) { this(poolConfig, host, port, connectionTimeout, soTimeout, 0, user, password, database, clientName, ssl, sslSocketFactory, sslParameters, hostnameVerifier); } public JedisPool(final GenericObjectPoolConfig poolConfig, final String host, int port, final int connectionTimeout, final int soTimeout, final int infiniteSoTimeout, final String password, final int database, final String clientName, final boolean ssl, final SSLSocketFactory sslSocketFactory, final SSLParameters sslParameters, final HostnameVerifier hostnameVerifier) { this(poolConfig, host, port, connectionTimeout, soTimeout, infiniteSoTimeout, null, password, database, clientName, ssl, sslSocketFactory, sslParameters, hostnameVerifier); } public JedisPool(final GenericObjectPoolConfig poolConfig, final String host, int port, final int connectionTimeout, final int soTimeout, final int infiniteSoTimeout, final String user, final String password, final int database, final String clientName) { this(poolConfig, new JedisFactory(host, port, connectionTimeout, soTimeout, infiniteSoTimeout, user, password, database, clientName)); } public JedisPool(final GenericObjectPoolConfig poolConfig, final String host, int port, final int connectionTimeout, final int soTimeout, final int infiniteSoTimeout, final String user, final String password, final int database, final String clientName, final boolean ssl, final SSLSocketFactory sslSocketFactory, final SSLParameters sslParameters, final HostnameVerifier hostnameVerifier) { this(poolConfig, new JedisFactory(host, port, connectionTimeout, soTimeout, infiniteSoTimeout, user, password, database, clientName, ssl, sslSocketFactory, sslParameters, hostnameVerifier)); } public JedisPool(final URI uri) { this(new GenericObjectPoolConfig(), uri); } public JedisPool(final URI uri, final SSLSocketFactory sslSocketFactory, final SSLParameters sslParameters, final HostnameVerifier hostnameVerifier) { this(new GenericObjectPoolConfig(), uri, sslSocketFactory, sslParameters, hostnameVerifier); } public JedisPool(final URI uri, final int timeout) { this(new GenericObjectPoolConfig(), uri, timeout); } public JedisPool(final URI uri, final int timeout, final SSLSocketFactory sslSocketFactory, final SSLParameters sslParameters, final HostnameVerifier hostnameVerifier) { this(new GenericObjectPoolConfig(), uri, timeout, sslSocketFactory, sslParameters, hostnameVerifier); } public JedisPool(final GenericObjectPoolConfig poolConfig, final URI uri) { this(poolConfig, uri, Protocol.DEFAULT_TIMEOUT); } public JedisPool(final GenericObjectPoolConfig poolConfig, final URI uri, final SSLSocketFactory sslSocketFactory, final SSLParameters sslParameters, final HostnameVerifier hostnameVerifier) { this(poolConfig, uri, Protocol.DEFAULT_TIMEOUT, sslSocketFactory, sslParameters, hostnameVerifier); } public JedisPool(final GenericObjectPoolConfig poolConfig, final URI uri, final int timeout) { this(poolConfig, uri, timeout, timeout); } public JedisPool(final GenericObjectPoolConfig poolConfig, final URI uri, final int timeout, final SSLSocketFactory sslSocketFactory, final SSLParameters sslParameters, final HostnameVerifier hostnameVerifier) { this(poolConfig, uri, timeout, timeout, sslSocketFactory, sslParameters, hostnameVerifier); } public JedisPool(final GenericObjectPoolConfig poolConfig, final URI uri, final int connectionTimeout, final int soTimeout) { this(poolConfig, uri, connectionTimeout, soTimeout, null, null, null); } public JedisPool(final GenericObjectPoolConfig poolConfig, final URI uri, final int connectionTimeout, final int soTimeout, final SSLSocketFactory sslSocketFactory, final SSLParameters sslParameters, final HostnameVerifier hostnameVerifier) { this(poolConfig, new JedisFactory(uri, connectionTimeout, soTimeout, null, sslSocketFactory, sslParameters, hostnameVerifier)); } public JedisPool(final GenericObjectPoolConfig poolConfig, final URI uri, final int connectionTimeout, final int soTimeout, final int infiniteSoTimeout, final SSLSocketFactory sslSocketFactory, final SSLParameters sslParameters, final HostnameVerifier hostnameVerifier) { this(poolConfig, new JedisFactory(uri, connectionTimeout, soTimeout, infiniteSoTimeout, null, sslSocketFactory, sslParameters, hostnameVerifier)); } public JedisPool(final GenericObjectPoolConfig poolConfig, final HostAndPort hostAndPort, final JedisClientConfig clientConfig) { this(poolConfig, new JedisFactory(hostAndPort, clientConfig)); } public JedisPool(final GenericObjectPoolConfig poolConfig, final JedisSocketFactory jedisSocketFactory, final JedisClientConfig clientConfig) { this(poolConfig, new JedisFactory(jedisSocketFactory, clientConfig)); } public JedisPool(GenericObjectPoolConfig poolConfig, PooledObjectFactory factory) { super(poolConfig, factory); } @Override public Jedis getResource() { Jedis jedis = super.getResource(); jedis.setDataSource(this); return jedis; } @Override public void returnResource(final Jedis resource) { if (resource != null) { try { resource.resetState(); super.returnResource(resource); } catch (RuntimeException e) { super.returnBrokenResource(resource); log.warn("Resource is returned to the pool as broken", e); } } } public void withResource(Consumer consumer) { try (Jedis jedis = this.getResource()) { consumer.accept(jedis); } } public K withResourceGet(Function function) { try (Jedis jedis = this.getResource()) { return function.apply(jedis); } } } ================================================ FILE: src/main/java/redis/clients/jedis/JedisPoolConfig.java ================================================ package redis.clients.jedis; import java.time.Duration; import org.apache.commons.pool2.impl.GenericObjectPoolConfig; /** * Configuration class for {@link JedisPool} connection pooling. * * @deprecated JedisPoolConfig is used with the deprecated {@link JedisPool} and {@link JedisSentinelPool} classes. * Use {@link ConnectionPoolConfig} instead, which is designed for the modern {@link RedisClient} * and {@link RedisSentinelClient} classes. ConnectionPoolConfig provides the same pooling configuration * options with better integration into the new client architecture. */ @Deprecated public class JedisPoolConfig extends GenericObjectPoolConfig { public JedisPoolConfig() { // defaults to make your life with connection pool easier :) setTestWhileIdle(true); setMinEvictableIdleTime(Duration.ofMillis(60000)); setTimeBetweenEvictionRuns(Duration.ofMillis(30000)); setNumTestsPerEvictionRun(-1); } } ================================================ FILE: src/main/java/redis/clients/jedis/JedisPooled.java ================================================ package redis.clients.jedis; import java.net.URI; import javax.net.ssl.HostnameVerifier; import javax.net.ssl.SSLParameters; import javax.net.ssl.SSLSocketFactory; import org.apache.commons.pool2.PooledObjectFactory; import org.apache.commons.pool2.impl.GenericObjectPoolConfig; import redis.clients.jedis.annots.Experimental; import redis.clients.jedis.builders.StandaloneClientBuilder; import redis.clients.jedis.csc.Cache; import redis.clients.jedis.csc.CacheConfig; import redis.clients.jedis.csc.CacheFactory; import redis.clients.jedis.executors.CommandExecutor; import redis.clients.jedis.providers.ConnectionProvider; import redis.clients.jedis.providers.PooledConnectionProvider; import redis.clients.jedis.util.JedisURIHelper; import redis.clients.jedis.util.Pool; /** * JedisPooled is a pooled connection client for standalone Redis servers. * * @deprecated Use {@link RedisClient} instead. RedisClient provides the same functionality * with a cleaner API and simplified constructor options. For basic usage, simple * constructors are available. For advanced configuration, use {@link RedisClient#builder()}. */ @Deprecated public class JedisPooled extends UnifiedJedis { public JedisPooled() { this(Protocol.DEFAULT_HOST, Protocol.DEFAULT_PORT); } /** * WARNING: This constructor only accepts a uri string as {@code url}. {@link JedisURIHelper#isValid(java.net.URI)} * can be used before this. *

* To use a host string, {@link #JedisPooled(java.lang.String, int)} can be used with {@link Protocol#DEFAULT_PORT}. * * @param url */ public JedisPooled(final String url) { super(url); } /** * WARNING: This constructor only accepts a uri string as {@code url}. {@link JedisURIHelper#isValid(java.net.URI)} * can be used before this. *

* To use a host string, {@link #JedisPooled(java.lang.String, int, boolean, javax.net.ssl.SSLSocketFactory, * javax.net.ssl.SSLParameters, javax.net.ssl.HostnameVerifier)} can be used with {@link Protocol#DEFAULT_PORT} and * {@code ssl=true}. * * @param url * @param sslSocketFactory * @param sslParameters * @param hostnameVerifier */ public JedisPooled(final String url, final SSLSocketFactory sslSocketFactory, final SSLParameters sslParameters, final HostnameVerifier hostnameVerifier) { this(URI.create(url), sslSocketFactory, sslParameters, hostnameVerifier); } public JedisPooled(final String host, final int port) { this(new HostAndPort(host, port)); } public JedisPooled(final HostAndPort hostAndPort) { super(hostAndPort); } public JedisPooled(final String host, final int port, final boolean ssl) { this(new HostAndPort(host, port), DefaultJedisClientConfig.builder().ssl(ssl).build()); } public JedisPooled(final String host, final int port, final boolean ssl, final SSLSocketFactory sslSocketFactory, final SSLParameters sslParameters, final HostnameVerifier hostnameVerifier) { this(new HostAndPort(host, port), DefaultJedisClientConfig.builder().ssl(ssl) .sslSocketFactory(sslSocketFactory).sslParameters(sslParameters) .hostnameVerifier(hostnameVerifier).build()); } public JedisPooled(final String host, final int port, final String user, final String password) { this(new HostAndPort(host, port), DefaultJedisClientConfig.builder().user(user).password(password).build()); } public JedisPooled(final HostAndPort hostAndPort, final JedisClientConfig clientConfig) { super(hostAndPort, clientConfig); } @Experimental public JedisPooled(final HostAndPort hostAndPort, final JedisClientConfig clientConfig, CacheConfig cacheConfig) { this(hostAndPort, clientConfig, CacheFactory.getCache(cacheConfig)); } @Experimental public JedisPooled(final HostAndPort hostAndPort, final JedisClientConfig clientConfig, Cache clientSideCache) { super(hostAndPort, clientConfig, clientSideCache); } public JedisPooled(PooledObjectFactory factory) { this(new PooledConnectionProvider(factory)); } public JedisPooled(final GenericObjectPoolConfig poolConfig) { this(poolConfig, Protocol.DEFAULT_HOST, Protocol.DEFAULT_PORT); } /** * WARNING: This constructor only accepts a uri string as {@code url}. {@link JedisURIHelper#isValid(java.net.URI)} * can be used before this. *

* To use a host string, * {@link #JedisPooled(org.apache.commons.pool2.impl.GenericObjectPoolConfig, java.lang.String, int)} can be used with * {@link Protocol#DEFAULT_PORT}. * * @param poolConfig * @param url */ public JedisPooled(final GenericObjectPoolConfig poolConfig, final String url) { this(poolConfig, URI.create(url)); } public JedisPooled(final GenericObjectPoolConfig poolConfig, final String host, final int port) { this(poolConfig, host, port, Protocol.DEFAULT_TIMEOUT); } public JedisPooled(final GenericObjectPoolConfig poolConfig, final String host, final int port, final boolean ssl) { this(poolConfig, host, port, Protocol.DEFAULT_TIMEOUT, ssl); } public JedisPooled(final GenericObjectPoolConfig poolConfig, final String host, final int port, final boolean ssl, final SSLSocketFactory sslSocketFactory, final SSLParameters sslParameters, final HostnameVerifier hostnameVerifier) { this(poolConfig, host, port, Protocol.DEFAULT_TIMEOUT, ssl, sslSocketFactory, sslParameters, hostnameVerifier); } public JedisPooled(final GenericObjectPoolConfig poolConfig, final String host, final int port, final String user, final String password) { this(poolConfig, host, port, Protocol.DEFAULT_TIMEOUT, user, password, Protocol.DEFAULT_DATABASE); } public JedisPooled(final GenericObjectPoolConfig poolConfig, final String host, final int port, final int timeout) { this(poolConfig, host, port, timeout, null); } public JedisPooled(final GenericObjectPoolConfig poolConfig, final String host, final int port, final int timeout, final boolean ssl) { this(poolConfig, host, port, timeout, null, ssl); } public JedisPooled(final GenericObjectPoolConfig poolConfig, final String host, final int port, final int timeout, final boolean ssl, final SSLSocketFactory sslSocketFactory, final SSLParameters sslParameters, final HostnameVerifier hostnameVerifier) { this(poolConfig, host, port, timeout, null, ssl, sslSocketFactory, sslParameters, hostnameVerifier); } public JedisPooled(final GenericObjectPoolConfig poolConfig, final String host, int port, int timeout, final String password) { this(poolConfig, host, port, timeout, password, Protocol.DEFAULT_DATABASE); } public JedisPooled(final GenericObjectPoolConfig poolConfig, final String host, int port, int timeout, final String password, final boolean ssl) { this(poolConfig, host, port, timeout, password, Protocol.DEFAULT_DATABASE, ssl); } public JedisPooled(final GenericObjectPoolConfig poolConfig, final String host, int port, int timeout, final String password, final boolean ssl, final SSLSocketFactory sslSocketFactory, final SSLParameters sslParameters, final HostnameVerifier hostnameVerifier) { this(poolConfig, host, port, timeout, password, Protocol.DEFAULT_DATABASE, ssl, sslSocketFactory, sslParameters, hostnameVerifier); } public JedisPooled(final GenericObjectPoolConfig poolConfig, final String host, int port, int timeout, final String user, final String password) { this(poolConfig, host, port, timeout, user, password, Protocol.DEFAULT_DATABASE); } public JedisPooled(final GenericObjectPoolConfig poolConfig, final String host, int port, int timeout, final String user, final String password, final boolean ssl) { this(poolConfig, host, port, timeout, user, password, Protocol.DEFAULT_DATABASE, ssl); } public JedisPooled(final GenericObjectPoolConfig poolConfig, final String host, int port, int timeout, final String password, final int database) { this(poolConfig, host, port, timeout, password, database, null); } public JedisPooled(final GenericObjectPoolConfig poolConfig, final String host, int port, int timeout, final String password, final int database, final boolean ssl) { this(poolConfig, host, port, timeout, password, database, null, ssl); } public JedisPooled(final GenericObjectPoolConfig poolConfig, final String host, int port, int timeout, final String password, final int database, final boolean ssl, final SSLSocketFactory sslSocketFactory, final SSLParameters sslParameters, final HostnameVerifier hostnameVerifier) { this(poolConfig, host, port, timeout, password, database, null, ssl, sslSocketFactory, sslParameters, hostnameVerifier); } public JedisPooled(final GenericObjectPoolConfig poolConfig, final String host, int port, int timeout, final String user, final String password, final int database) { this(poolConfig, host, port, timeout, user, password, database, null); } public JedisPooled(final GenericObjectPoolConfig poolConfig, final String host, int port, int timeout, final String user, final String password, final int database, final boolean ssl) { this(poolConfig, host, port, timeout, user, password, database, null, ssl); } public JedisPooled(final GenericObjectPoolConfig poolConfig, final String host, int port, int timeout, final String password, final int database, final String clientName) { this(poolConfig, host, port, timeout, timeout, password, database, clientName); } public JedisPooled(final GenericObjectPoolConfig poolConfig, final String host, int port, int timeout, final String password, final int database, final String clientName, final boolean ssl) { this(poolConfig, host, port, timeout, timeout, password, database, clientName, ssl); } public JedisPooled(final GenericObjectPoolConfig poolConfig, final String host, int port, int timeout, final String password, final int database, final String clientName, final boolean ssl, final SSLSocketFactory sslSocketFactory, final SSLParameters sslParameters, final HostnameVerifier hostnameVerifier) { this(poolConfig, host, port, timeout, timeout, password, database, clientName, ssl, sslSocketFactory, sslParameters, hostnameVerifier); } public JedisPooled(final GenericObjectPoolConfig poolConfig, final String host, int port, int timeout, final String user, final String password, final int database, final String clientName) { this(poolConfig, host, port, timeout, timeout, user, password, database, clientName); } public JedisPooled(final GenericObjectPoolConfig poolConfig, final String host, int port, int timeout, final String user, final String password, final int database, final String clientName, final boolean ssl) { this(poolConfig, host, port, timeout, timeout, user, password, database, clientName, ssl); } public JedisPooled(final GenericObjectPoolConfig poolConfig, final String host, int port, final int connectionTimeout, final int soTimeout, final String password, final int database, final String clientName) { this(poolConfig, host, port, connectionTimeout, soTimeout, null, password, database, clientName); } public JedisPooled(final GenericObjectPoolConfig poolConfig, final String host, int port, final int connectionTimeout, final int soTimeout, final String password, final int database, final String clientName, final boolean ssl) { this(poolConfig, host, port, connectionTimeout, soTimeout, password, database, clientName, ssl, null, null, null); } public JedisPooled(final GenericObjectPoolConfig poolConfig, final String host, int port, final int connectionTimeout, final int soTimeout, final String password, final int database, final String clientName, final boolean ssl, final SSLSocketFactory sslSocketFactory, final SSLParameters sslParameters, final HostnameVerifier hostnameVerifier) { this(poolConfig, host, port, connectionTimeout, soTimeout, null, password, database, clientName, ssl, sslSocketFactory, sslParameters, hostnameVerifier); } public JedisPooled(final GenericObjectPoolConfig poolConfig, final String host, int port, final int connectionTimeout, final int soTimeout, final String user, final String password, final int database, final String clientName) { this(poolConfig, host, port, connectionTimeout, soTimeout, 0, user, password, database, clientName); } public JedisPooled(final GenericObjectPoolConfig poolConfig, final String host, int port, final int connectionTimeout, final int soTimeout, final String user, final String password, final int database, final String clientName, final boolean ssl) { this(poolConfig, host, port, connectionTimeout, soTimeout, user, password, database, clientName, ssl, null, null, null); } public JedisPooled(final GenericObjectPoolConfig poolConfig, final String host, int port, final int connectionTimeout, final int soTimeout, final String user, final String password, final int database, final String clientName, final boolean ssl, final SSLSocketFactory sslSocketFactory, final SSLParameters sslParameters, final HostnameVerifier hostnameVerifier) { this(poolConfig, host, port, connectionTimeout, soTimeout, 0, user, password, database, clientName, ssl, sslSocketFactory, sslParameters, hostnameVerifier); } public JedisPooled(final GenericObjectPoolConfig poolConfig, final String host, int port, final int connectionTimeout, final int soTimeout, final int infiniteSoTimeout, final String password, final int database, final String clientName, final boolean ssl, final SSLSocketFactory sslSocketFactory, final SSLParameters sslParameters, final HostnameVerifier hostnameVerifier) { this(poolConfig, host, port, connectionTimeout, soTimeout, infiniteSoTimeout, null, password, database, clientName, ssl, sslSocketFactory, sslParameters, hostnameVerifier); } public JedisPooled(final GenericObjectPoolConfig poolConfig, final String host, int port, final int connectionTimeout, final int soTimeout, final int infiniteSoTimeout, final String user, final String password, final int database, final String clientName) { this(new HostAndPort(host, port), DefaultJedisClientConfig.builder().connectionTimeoutMillis(connectionTimeout).socketTimeoutMillis(soTimeout) .blockingSocketTimeoutMillis(infiniteSoTimeout).user(user).password(password).database(database) .clientName(clientName).build(), poolConfig); } public JedisPooled(final GenericObjectPoolConfig poolConfig, final String host, int port, final int connectionTimeout, final int soTimeout, final int infiniteSoTimeout, final String user, final String password, final int database, final String clientName, final boolean ssl, final SSLSocketFactory sslSocketFactory, final SSLParameters sslParameters, final HostnameVerifier hostnameVerifier) { this(new HostAndPort(host, port), DefaultJedisClientConfig.builder().connectionTimeoutMillis(connectionTimeout).socketTimeoutMillis(soTimeout) .blockingSocketTimeoutMillis(infiniteSoTimeout).user(user).password(password).database(database) .clientName(clientName).ssl(ssl).sslSocketFactory(sslSocketFactory).sslParameters(sslParameters) .hostnameVerifier(hostnameVerifier).build(), poolConfig); } public JedisPooled(final URI uri) { super(uri); } public JedisPooled(final URI uri, final SSLSocketFactory sslSocketFactory, final SSLParameters sslParameters, final HostnameVerifier hostnameVerifier) { this(new GenericObjectPoolConfig(), uri, sslSocketFactory, sslParameters, hostnameVerifier); } public JedisPooled(final URI uri, final int timeout) { this(new GenericObjectPoolConfig(), uri, timeout); } public JedisPooled(final URI uri, final int timeout, final SSLSocketFactory sslSocketFactory, final SSLParameters sslParameters, final HostnameVerifier hostnameVerifier) { this(new GenericObjectPoolConfig(), uri, timeout, sslSocketFactory, sslParameters, hostnameVerifier); } public JedisPooled(final GenericObjectPoolConfig poolConfig, final URI uri) { this(poolConfig, uri, Protocol.DEFAULT_TIMEOUT); } public JedisPooled(final GenericObjectPoolConfig poolConfig, final URI uri, final SSLSocketFactory sslSocketFactory, final SSLParameters sslParameters, final HostnameVerifier hostnameVerifier) { this(poolConfig, uri, Protocol.DEFAULT_TIMEOUT, sslSocketFactory, sslParameters, hostnameVerifier); } public JedisPooled(final GenericObjectPoolConfig poolConfig, final URI uri, final int timeout) { this(poolConfig, uri, timeout, timeout); } public JedisPooled(final GenericObjectPoolConfig poolConfig, final URI uri, final int timeout, final SSLSocketFactory sslSocketFactory, final SSLParameters sslParameters, final HostnameVerifier hostnameVerifier) { this(poolConfig, uri, timeout, timeout, sslSocketFactory, sslParameters, hostnameVerifier); } public JedisPooled(final GenericObjectPoolConfig poolConfig, final URI uri, final int connectionTimeout, final int soTimeout) { this(poolConfig, uri, connectionTimeout, soTimeout, null, null, null); } public JedisPooled(final GenericObjectPoolConfig poolConfig, final URI uri, final int connectionTimeout, final int soTimeout, final SSLSocketFactory sslSocketFactory, final SSLParameters sslParameters, final HostnameVerifier hostnameVerifier) { this(poolConfig, uri, connectionTimeout, soTimeout, 0, sslSocketFactory, sslParameters, hostnameVerifier); } public JedisPooled(final GenericObjectPoolConfig poolConfig, final URI uri, final int connectionTimeout, final int soTimeout, final int infiniteSoTimeout, final SSLSocketFactory sslSocketFactory, final SSLParameters sslParameters, final HostnameVerifier hostnameVerifier) { this(new HostAndPort(uri.getHost(), uri.getPort()), DefaultJedisClientConfig.builder() .connectionTimeoutMillis(connectionTimeout).socketTimeoutMillis(soTimeout) .blockingSocketTimeoutMillis(infiniteSoTimeout).user(JedisURIHelper.getUser(uri)) .password(JedisURIHelper.getPassword(uri)).database(JedisURIHelper.getDBIndex(uri)) .protocol(JedisURIHelper.getRedisProtocol(uri)).ssl(JedisURIHelper.isRedisSSLScheme(uri)) .sslSocketFactory(sslSocketFactory).sslParameters(sslParameters) .hostnameVerifier(hostnameVerifier).build(), poolConfig); } public JedisPooled(final HostAndPort hostAndPort, final GenericObjectPoolConfig poolConfig) { this(hostAndPort, DefaultJedisClientConfig.builder().build(), poolConfig); } public JedisPooled(final GenericObjectPoolConfig poolConfig, final HostAndPort hostAndPort, final JedisClientConfig clientConfig) { this(hostAndPort, clientConfig, poolConfig); } public JedisPooled(final HostAndPort hostAndPort, final JedisClientConfig clientConfig, final GenericObjectPoolConfig poolConfig) { super(new PooledConnectionProvider(hostAndPort, clientConfig, poolConfig), clientConfig.getRedisProtocol()); } @Experimental public JedisPooled(final HostAndPort hostAndPort, final JedisClientConfig clientConfig, CacheConfig cacheConfig, final GenericObjectPoolConfig poolConfig) { this(hostAndPort, clientConfig, CacheFactory.getCache(cacheConfig), poolConfig); } @Experimental public JedisPooled(final HostAndPort hostAndPort, final JedisClientConfig clientConfig, Cache clientSideCache, final GenericObjectPoolConfig poolConfig) { super(new PooledConnectionProvider(hostAndPort, clientConfig, clientSideCache, poolConfig), clientConfig.getRedisProtocol(), clientSideCache); } public JedisPooled(final GenericObjectPoolConfig poolConfig, final JedisSocketFactory jedisSocketFactory, final JedisClientConfig clientConfig) { super(new PooledConnectionProvider(new ConnectionFactory(jedisSocketFactory, clientConfig), poolConfig), clientConfig.getRedisProtocol()); } public JedisPooled(GenericObjectPoolConfig poolConfig, PooledObjectFactory factory) { this(factory, poolConfig); } public JedisPooled(PooledObjectFactory factory, GenericObjectPoolConfig poolConfig) { this(new PooledConnectionProvider(factory, poolConfig)); } public JedisPooled(PooledConnectionProvider provider) { super(provider); } private JedisPooled(CommandExecutor commandExecutor, ConnectionProvider connectionProvider, CommandObjects commandObjects, RedisProtocol redisProtocol, Cache cache) { super(commandExecutor, connectionProvider, commandObjects, redisProtocol, cache); } /** * Fluent builder for {@link JedisPooled} (standalone). *

* Obtain an instance via {@link #builder()}. *

*/ static public class Builder extends StandaloneClientBuilder { @Override protected JedisPooled createClient() { return new JedisPooled(commandExecutor, connectionProvider, commandObjects, clientConfig.getRedisProtocol(), cache); } } /** * Create a new builder for configuring JedisPooled instances. * @return a new {@link JedisPooled.Builder} instance */ public static Builder builder() { return new Builder(); } public final Pool getPool() { return ((PooledConnectionProvider) provider).getPool(); } @Override public Pipeline pipelined() { return (Pipeline) super.pipelined(); } } ================================================ FILE: src/main/java/redis/clients/jedis/JedisPubSub.java ================================================ package redis.clients.jedis; import redis.clients.jedis.util.SafeEncoder; public abstract class JedisPubSub extends JedisPubSubBase { @Override protected final String encode(byte[] raw) { return SafeEncoder.encode(raw); } } ================================================ FILE: src/main/java/redis/clients/jedis/JedisPubSubBase.java ================================================ package redis.clients.jedis; import static redis.clients.jedis.Protocol.ResponseKeyword.*; import java.util.Arrays; import java.util.List; import java.util.function.Consumer; import redis.clients.jedis.Protocol.Command; import redis.clients.jedis.exceptions.JedisException; import redis.clients.jedis.util.SafeEncoder; public abstract class JedisPubSubBase { private int subscribedChannels = 0; private final JedisSafeAuthenticator authenticator = new JedisSafeAuthenticator(); private final Consumer pingResultHandler = this::processPingReply; public void onMessage(T channel, T message) { } public void onPMessage(T pattern, T channel, T message) { } public void onSubscribe(T channel, int subscribedChannels) { } public void onUnsubscribe(T channel, int subscribedChannels) { } public void onPUnsubscribe(T pattern, int subscribedChannels) { } public void onPSubscribe(T pattern, int subscribedChannels) { } public void onPong(T pattern) { } private void sendAndFlushCommand(Command command, T... args) { authenticator.sendAndFlushCommand(command, args); } public final void unsubscribe() { sendAndFlushCommand(Command.UNSUBSCRIBE); } public final void unsubscribe(T... channels) { sendAndFlushCommand(Command.UNSUBSCRIBE, channels); } public final void subscribe(T... channels) { checkConnectionSuitableForPubSub(); sendAndFlushCommand(Command.SUBSCRIBE, channels); } public final void psubscribe(T... patterns) { checkConnectionSuitableForPubSub(); sendAndFlushCommand(Command.PSUBSCRIBE, patterns); } private void checkConnectionSuitableForPubSub() { if (authenticator.client.protocol != RedisProtocol.RESP3 && authenticator.client.isTokenBasedAuthenticationEnabled()) { throw new JedisException( "Blocking pub/sub operations are not supported on token-based authentication enabled connections with RESP2 protocol!"); } } public final void punsubscribe() { sendAndFlushCommand(Command.PUNSUBSCRIBE); } public final void punsubscribe(T... patterns) { sendAndFlushCommand(Command.PUNSUBSCRIBE, patterns); } public final void ping() { authenticator.commandSync.lock(); try { sendAndFlushCommand(Command.PING); authenticator.resultHandler.add(pingResultHandler); } finally { authenticator.commandSync.unlock(); } } public final void ping(T argument) { authenticator.commandSync.lock(); try { sendAndFlushCommand(Command.PING, argument); authenticator.resultHandler.add(pingResultHandler); } finally { authenticator.commandSync.unlock(); } } public final boolean isSubscribed() { return subscribedChannels > 0; } public final int getSubscribedChannels() { return subscribedChannels; } public final void proceed(Connection client, T... channels) { authenticator.registerForAuthentication(client); authenticator.client.setTimeoutInfinite(); try { subscribe(channels); process(); } finally { authenticator.client.rollbackTimeout(); } } public final void proceedWithPatterns(Connection client, T... patterns) { authenticator.registerForAuthentication(client); authenticator.client.setTimeoutInfinite(); try { psubscribe(patterns); process(); } finally { authenticator.client.rollbackTimeout(); } } protected abstract T encode(byte[] raw); // private void process(Client client) { private void process() { do { Object reply = authenticator.client.getUnflushedObject(); if (reply instanceof List) { List listReply = (List) reply; final Object firstObj = listReply.get(0); if (!(firstObj instanceof byte[])) { throw new JedisException("Unknown message type: " + firstObj); } final byte[] resp = (byte[]) firstObj; if (Arrays.equals(SUBSCRIBE.getRaw(), resp)) { subscribedChannels = ((Long) listReply.get(2)).intValue(); final byte[] bchannel = (byte[]) listReply.get(1); final T enchannel = (bchannel == null) ? null : encode(bchannel); onSubscribe(enchannel, subscribedChannels); } else if (Arrays.equals(UNSUBSCRIBE.getRaw(), resp)) { subscribedChannels = ((Long) listReply.get(2)).intValue(); final byte[] bchannel = (byte[]) listReply.get(1); final T enchannel = (bchannel == null) ? null : encode(bchannel); onUnsubscribe(enchannel, subscribedChannels); } else if (Arrays.equals(MESSAGE.getRaw(), resp)) { final byte[] bchannel = (byte[]) listReply.get(1); final Object mesg = listReply.get(2); final T enchannel = (bchannel == null) ? null : encode(bchannel); if (mesg instanceof List) { ((List) mesg).forEach(bmesg -> onMessage(enchannel, encode(bmesg))); } else { onMessage(enchannel, (mesg == null) ? null : encode((byte[]) mesg)); } } else if (Arrays.equals(PMESSAGE.getRaw(), resp)) { final byte[] bpattern = (byte[]) listReply.get(1); final byte[] bchannel = (byte[]) listReply.get(2); final byte[] bmesg = (byte[]) listReply.get(3); final T enpattern = (bpattern == null) ? null : encode(bpattern); final T enchannel = (bchannel == null) ? null : encode(bchannel); final T enmesg = (bmesg == null) ? null : encode(bmesg); onPMessage(enpattern, enchannel, enmesg); } else if (Arrays.equals(PSUBSCRIBE.getRaw(), resp)) { subscribedChannels = ((Long) listReply.get(2)).intValue(); final byte[] bpattern = (byte[]) listReply.get(1); final T enpattern = (bpattern == null) ? null : encode(bpattern); onPSubscribe(enpattern, subscribedChannels); } else if (Arrays.equals(PUNSUBSCRIBE.getRaw(), resp)) { subscribedChannels = ((Long) listReply.get(2)).intValue(); final byte[] bpattern = (byte[]) listReply.get(1); final T enpattern = (bpattern == null) ? null : encode(bpattern); onPUnsubscribe(enpattern, subscribedChannels); } else if (Arrays.equals(PONG.getRaw(), resp)) { final byte[] bpattern = (byte[]) listReply.get(1); final T enpattern = (bpattern == null) ? null : encode(bpattern); onPong(enpattern); } else { throw new JedisException("Unknown message type: " + firstObj); } } else if (reply instanceof byte[]) { Consumer resultHandler = authenticator.resultHandler.poll(); if (resultHandler == null) { throw new JedisException("Unexpected message : " + SafeEncoder.encode((byte[]) reply)); } resultHandler.accept(reply); } else { throw new JedisException("Unknown message type: " + reply); } } while (!Thread.currentThread().isInterrupted() && isSubscribed()); // /* Invalidate instance since this thread is no longer listening */ // this.client = null; } private void processPingReply(Object reply) { byte[] resp = (byte[]) reply; if ("PONG".equals(SafeEncoder.encode(resp))) { onPong(null); } else { onPong(encode(resp)); } } } ================================================ FILE: src/main/java/redis/clients/jedis/JedisSafeAuthenticator.java ================================================ package redis.clients.jedis; import java.util.Queue; import java.util.concurrent.ConcurrentLinkedQueue; import java.util.concurrent.atomic.AtomicReference; import java.util.concurrent.locks.ReentrantLock; import java.util.function.Consumer; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import redis.clients.authentication.core.SimpleToken; import redis.clients.authentication.core.Token; import redis.clients.jedis.Protocol.Command; import redis.clients.jedis.authentication.JedisAuthenticationException; import redis.clients.jedis.exceptions.JedisException; import redis.clients.jedis.util.SafeEncoder; class JedisSafeAuthenticator { private static final Token PLACEHOLDER_TOKEN = new SimpleToken(null, null, 0, 0, null); private static final Logger logger = LoggerFactory.getLogger(JedisSafeAuthenticator.class); protected volatile Connection client; protected final Consumer authResultHandler = this::processAuthReply; protected final Consumer authenticationHandler = this::safeReAuthenticate; protected final AtomicReference pendingTokenRef = new AtomicReference(null); protected final ReentrantLock commandSync = new ReentrantLock(); protected final Queue> resultHandler = new ConcurrentLinkedQueue>(); protected void sendAndFlushCommand(Command command, Object... args) { if (client == null) { throw new JedisException(getClass() + " is not connected to a Connection."); } CommandArguments cargs = new CommandArguments(command).addObjects(args); Token newToken = pendingTokenRef.getAndSet(PLACEHOLDER_TOKEN); // lets send the command without locking !!IF!! we know that pendingTokenRef is null replaced with PLACEHOLDER_TOKEN and no re-auth will go into action // !!ELSE!! we are locking since we already know a re-auth is still in progress in another thread and we need to wait for it to complete, we do nothing but wait on it! if (newToken != null) { commandSync.lock(); } try { client.sendCommand(cargs); client.flush(); } finally { Token newerToken = pendingTokenRef.getAndSet(null); // lets check if a newer token received since the beginning of this sendAndFlushCommand call if (newerToken != null && newerToken != PLACEHOLDER_TOKEN) { safeReAuthenticate(newerToken); } if (newToken != null) { commandSync.unlock(); } } } protected void registerForAuthentication(Connection newClient) { Connection oldClient = this.client; if (oldClient == newClient) return; if (oldClient != null && oldClient.getAuthXManager() != null) { oldClient.getAuthXManager().removePostAuthenticationHook(authenticationHandler); } if (newClient != null && newClient.getAuthXManager() != null) { newClient.getAuthXManager().addPostAuthenticationHook(authenticationHandler); } this.client = newClient; } private void safeReAuthenticate(Token token) { try { byte[] rawPass = client.encodeToBytes(token.getValue().toCharArray()); byte[] rawUser = client.encodeToBytes(token.getUser().toCharArray()); Token newToken = pendingTokenRef.getAndSet(token); if (newToken == null) { commandSync.lock(); try { sendAndFlushCommand(Command.AUTH, rawUser, rawPass); resultHandler.add(this.authResultHandler); } finally { pendingTokenRef.set(null); commandSync.unlock(); } } } catch (Exception e) { logger.error("Error while re-authenticating connection", e); client.getAuthXManager().getListener().onConnectionAuthenticationError(e); } } protected void processAuthReply(Object reply) { byte[] resp = (byte[]) reply; String response = SafeEncoder.encode(resp); if (!"OK".equals(response)) { String msg = "Re-authentication failed with server response: " + response; Exception failedAuth = new JedisAuthenticationException(msg); logger.error(failedAuth.getMessage(), failedAuth); client.getAuthXManager().getListener().onConnectionAuthenticationError(failedAuth); } } } ================================================ FILE: src/main/java/redis/clients/jedis/JedisSentinelPool.java ================================================ package redis.clients.jedis; import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; import java.util.List; import java.util.Set; import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.locks.Lock; import java.util.concurrent.locks.ReentrantLock; import java.util.stream.Collectors; import org.apache.commons.pool2.impl.GenericObjectPoolConfig; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import redis.clients.jedis.exceptions.JedisConnectionException; import redis.clients.jedis.exceptions.JedisException; import redis.clients.jedis.util.Pool; /** * JedisSentinelPool is a pooled connection client for Redis Sentinel deployments. * * @deprecated Use {@link RedisSentinelClient} instead. RedisSentinelClient provides the same functionality * with a cleaner API and simplified constructor options. For basic usage, simple * constructors are available. For advanced configuration, use {@link RedisSentinelClient#builder()}. */ @Deprecated public class JedisSentinelPool extends Pool { private static final Logger LOG = LoggerFactory.getLogger(JedisSentinelPool.class); private final JedisFactory factory; private final JedisClientConfig sentinelClientConfig; protected final Collection masterListeners = new ArrayList<>(); private volatile HostAndPort currentHostMaster; private final Lock initPoolLock = new ReentrantLock(true); public JedisSentinelPool(String masterName, Set sentinels, final JedisClientConfig masterClientConfig, final JedisClientConfig sentinelClientConfig) { this(masterName, sentinels, new JedisFactory(masterClientConfig), sentinelClientConfig); } public JedisSentinelPool(String masterName, Set sentinels, final GenericObjectPoolConfig poolConfig) { this(masterName, sentinels, poolConfig, Protocol.DEFAULT_TIMEOUT, null, Protocol.DEFAULT_DATABASE); } public JedisSentinelPool(String masterName, Set sentinels) { this(masterName, sentinels, new GenericObjectPoolConfig(), Protocol.DEFAULT_TIMEOUT, null, Protocol.DEFAULT_DATABASE); } public JedisSentinelPool(String masterName, Set sentinels, String password) { this(masterName, sentinels, new GenericObjectPoolConfig(), Protocol.DEFAULT_TIMEOUT, password); } public JedisSentinelPool(String masterName, Set sentinels, String password, String sentinelPassword) { this(masterName, sentinels, new GenericObjectPoolConfig(), Protocol.DEFAULT_TIMEOUT, Protocol.DEFAULT_TIMEOUT, password, Protocol.DEFAULT_DATABASE, null, Protocol.DEFAULT_TIMEOUT, Protocol.DEFAULT_TIMEOUT, sentinelPassword, null); } public JedisSentinelPool(String masterName, Set sentinels, final GenericObjectPoolConfig poolConfig, int timeout, final String password) { this(masterName, sentinels, poolConfig, timeout, password, Protocol.DEFAULT_DATABASE); } public JedisSentinelPool(String masterName, Set sentinels, final GenericObjectPoolConfig poolConfig, final int timeout) { this(masterName, sentinels, poolConfig, timeout, null, Protocol.DEFAULT_DATABASE); } public JedisSentinelPool(String masterName, Set sentinels, final GenericObjectPoolConfig poolConfig, final String password) { this(masterName, sentinels, poolConfig, Protocol.DEFAULT_TIMEOUT, password); } public JedisSentinelPool(String masterName, Set sentinels, final GenericObjectPoolConfig poolConfig, int timeout, final String password, final int database) { this(masterName, sentinels, poolConfig, timeout, timeout, null, password, database); } public JedisSentinelPool(String masterName, Set sentinels, final GenericObjectPoolConfig poolConfig, int timeout, final String user, final String password, final int database) { this(masterName, sentinels, poolConfig, timeout, timeout, user, password, database); } public JedisSentinelPool(String masterName, Set sentinels, final GenericObjectPoolConfig poolConfig, int timeout, final String password, final int database, final String clientName) { this(masterName, sentinels, poolConfig, timeout, timeout, password, database, clientName); } public JedisSentinelPool(String masterName, Set sentinels, final GenericObjectPoolConfig poolConfig, int timeout, final String user, final String password, final int database, final String clientName) { this(masterName, sentinels, poolConfig, timeout, timeout, user, password, database, clientName); } public JedisSentinelPool(String masterName, Set sentinels, final GenericObjectPoolConfig poolConfig, final int connectionTimeout, final int soTimeout, final String password, final int database) { this(masterName, sentinels, poolConfig, connectionTimeout, soTimeout, null, password, database, null); } public JedisSentinelPool(String masterName, Set sentinels, final GenericObjectPoolConfig poolConfig, final int connectionTimeout, final int soTimeout, final String user, final String password, final int database) { this(masterName, sentinels, poolConfig, connectionTimeout, soTimeout, user, password, database, null); } public JedisSentinelPool(String masterName, Set sentinels, final GenericObjectPoolConfig poolConfig, final int connectionTimeout, final int soTimeout, final String password, final int database, final String clientName) { this(masterName, sentinels, poolConfig, connectionTimeout, soTimeout, null, password, database, clientName); } public JedisSentinelPool(String masterName, Set sentinels, final GenericObjectPoolConfig poolConfig, final int connectionTimeout, final int soTimeout, final String user, final String password, final int database, final String clientName) { this(masterName, sentinels, poolConfig, connectionTimeout, soTimeout, user, password, database, clientName, Protocol.DEFAULT_TIMEOUT, Protocol.DEFAULT_TIMEOUT, null, null, null); } public JedisSentinelPool(String masterName, Set sentinels, final GenericObjectPoolConfig poolConfig, final int connectionTimeout, final int soTimeout, final int infiniteSoTimeout, final String user, final String password, final int database, final String clientName) { this(masterName, sentinels, poolConfig, connectionTimeout, soTimeout, infiniteSoTimeout, user, password, database, clientName, Protocol.DEFAULT_TIMEOUT, Protocol.DEFAULT_TIMEOUT, null, null, null); } public JedisSentinelPool(String masterName, Set sentinels, final GenericObjectPoolConfig poolConfig, final int connectionTimeout, final int soTimeout, final String password, final int database, final String clientName, final int sentinelConnectionTimeout, final int sentinelSoTimeout, final String sentinelPassword, final String sentinelClientName) { this(masterName, sentinels, poolConfig, connectionTimeout, soTimeout, null, password, database, clientName, sentinelConnectionTimeout, sentinelSoTimeout, null, sentinelPassword, sentinelClientName); } public JedisSentinelPool(String masterName, Set sentinels, final GenericObjectPoolConfig poolConfig, final int connectionTimeout, final int soTimeout, final String user, final String password, final int database, final String clientName, final int sentinelConnectionTimeout, final int sentinelSoTimeout, final String sentinelUser, final String sentinelPassword, final String sentinelClientName) { this(masterName, sentinels, poolConfig, connectionTimeout, soTimeout, 0, user, password, database, clientName, sentinelConnectionTimeout, sentinelSoTimeout, sentinelUser, sentinelPassword, sentinelClientName); } public JedisSentinelPool(String masterName, Set sentinels, final GenericObjectPoolConfig poolConfig, final int connectionTimeout, final int soTimeout, final int infiniteSoTimeout, final String user, final String password, final int database, final String clientName, final int sentinelConnectionTimeout, final int sentinelSoTimeout, final String sentinelUser, final String sentinelPassword, final String sentinelClientName) { this(masterName, parseHostAndPorts(sentinels), poolConfig, DefaultJedisClientConfig.builder().connectionTimeoutMillis(connectionTimeout) .socketTimeoutMillis(soTimeout).blockingSocketTimeoutMillis(infiniteSoTimeout) .user(user).password(password).database(database).clientName(clientName).build(), DefaultJedisClientConfig.builder().connectionTimeoutMillis(sentinelConnectionTimeout) .socketTimeoutMillis(sentinelSoTimeout).user(sentinelUser).password(sentinelPassword) .clientName(sentinelClientName).build() ); } public JedisSentinelPool(String masterName, Set sentinels, final GenericObjectPoolConfig poolConfig, final JedisFactory factory) { this(masterName, parseHostAndPorts(sentinels), poolConfig, factory, DefaultJedisClientConfig.builder().build()); } public JedisSentinelPool(String masterName, Set sentinels, final GenericObjectPoolConfig poolConfig, final JedisClientConfig masterClientConfig, final JedisClientConfig sentinelClientConfig) { this(masterName, sentinels, poolConfig, new JedisFactory(masterClientConfig), sentinelClientConfig); } public JedisSentinelPool(String masterName, Set sentinels, final JedisFactory factory, final JedisClientConfig sentinelClientConfig) { super(factory); this.factory = factory; this.sentinelClientConfig = sentinelClientConfig; HostAndPort master = initSentinels(sentinels, masterName); initMaster(master); } public JedisSentinelPool(String masterName, Set sentinels, final GenericObjectPoolConfig poolConfig, final JedisFactory factory, final JedisClientConfig sentinelClientConfig) { super(poolConfig, factory); this.factory = factory; this.sentinelClientConfig = sentinelClientConfig; HostAndPort master = initSentinels(sentinels, masterName); initMaster(master); } private static Set parseHostAndPorts(Set strings) { return strings.stream().map(HostAndPort::from).collect(Collectors.toSet()); } @Override public void destroy() { for (MasterListener m : masterListeners) { m.shutdown(); } super.destroy(); } public HostAndPort getCurrentHostMaster() { return currentHostMaster; } private void initMaster(HostAndPort master) { initPoolLock.lock(); try { if (!master.equals(currentHostMaster)) { currentHostMaster = master; factory.setHostAndPort(currentHostMaster); // although we clear the pool, we still have to check the returned object in getResource, // this call only clears idle instances, not borrowed instances super.clear(); LOG.info("Created JedisSentinelPool to master at {}", master); } } finally { initPoolLock.unlock(); } } private HostAndPort initSentinels(Set sentinels, final String masterName) { HostAndPort master = null; boolean sentinelAvailable = false; LOG.info("Trying to find master from available Sentinels..."); for (HostAndPort sentinel : sentinels) { LOG.debug("Connecting to Sentinel {}", sentinel); try (Jedis jedis = new Jedis(sentinel, sentinelClientConfig)) { List masterAddr = jedis.sentinelGetMasterAddrByName(masterName); // connected to sentinel... sentinelAvailable = true; if (masterAddr == null || masterAddr.size() != 2) { LOG.warn("Can not get master addr, master name: {}. Sentinel: {}", masterName, sentinel); continue; } master = toHostAndPort(masterAddr); LOG.debug("Found Redis master at {}", master); break; } catch (JedisException e) { // resolves #1036, it should handle JedisException there's another chance // of raising JedisDataException LOG.warn( "Cannot get master address from sentinel running @ {}. Reason: {}. Trying next one.", sentinel, e); } } if (master == null) { if (sentinelAvailable) { // can connect to sentinel, but master name seems to not monitored throw new JedisException("Can connect to sentinel, but " + masterName + " seems to be not monitored..."); } else { throw new JedisConnectionException("All sentinels down, cannot determine where is " + masterName + " master is running..."); } } LOG.info("Redis master running at {}, starting Sentinel listeners...", master); for (HostAndPort sentinel : sentinels) { MasterListener masterListener = new MasterListener(masterName, sentinel.getHost(), sentinel.getPort()); // whether MasterListener threads are alive or not, process can be stopped masterListener.setDaemon(true); masterListeners.add(masterListener); masterListener.start(); } return master; } private HostAndPort toHostAndPort(List getMasterAddrByNameResult) { String host = getMasterAddrByNameResult.get(0); int port = Integer.parseInt(getMasterAddrByNameResult.get(1)); return new HostAndPort(host, port); } @Override public Jedis getResource() { while (true) { Jedis jedis = super.getResource(); jedis.setDataSource(this); // get a reference because it can change concurrently final HostAndPort master = currentHostMaster; final HostAndPort connection = jedis.getClient().getHostAndPort(); if (master.equals(connection)) { // connected to the correct master return jedis; } else { returnBrokenResource(jedis); } } } @Override public void returnResource(final Jedis resource) { if (resource != null) { try { resource.resetState(); super.returnResource(resource); } catch (RuntimeException e) { returnBrokenResource(resource); LOG.debug("Resource is returned to the pool as broken", e); } } } protected class MasterListener extends Thread { protected String masterName; protected String host; protected int port; protected long subscribeRetryWaitTimeMillis = 5000; protected volatile Jedis j; protected AtomicBoolean running = new AtomicBoolean(false); protected MasterListener() { } public MasterListener(String masterName, String host, int port) { super(String.format("MasterListener-%s-[%s:%d]", masterName, host, port)); this.masterName = masterName; this.host = host; this.port = port; } public MasterListener(String masterName, String host, int port, long subscribeRetryWaitTimeMillis) { this(masterName, host, port); this.subscribeRetryWaitTimeMillis = subscribeRetryWaitTimeMillis; } @Override public void run() { running.set(true); while (running.get()) { try { // double check that it is not being shutdown if (!running.get()) { break; } final HostAndPort hostPort = new HostAndPort(host, port); j = new Jedis(hostPort, sentinelClientConfig); // code for active refresh List masterAddr = j.sentinelGetMasterAddrByName(masterName); if (masterAddr == null || masterAddr.size() != 2) { LOG.warn("Can not get master addr, master name: {}. Sentinel: {}.", masterName, hostPort); } else { initMaster(toHostAndPort(masterAddr)); } j.subscribe(new JedisPubSub() { @Override public void onMessage(String channel, String message) { LOG.debug("Sentinel {} published: {}.", hostPort, message); String[] switchMasterMsg = message.split(" "); if (switchMasterMsg.length > 3) { if (masterName.equals(switchMasterMsg[0])) { initMaster(toHostAndPort(Arrays.asList(switchMasterMsg[3], switchMasterMsg[4]))); } else { LOG.debug( "Ignoring message on +switch-master for master name {}, our master name is {}", switchMasterMsg[0], masterName); } } else { LOG.error("Invalid message received on Sentinel {} on channel +switch-master: {}", hostPort, message); } } }, "+switch-master"); } catch (JedisException e) { if (running.get()) { LOG.warn("Lost connection to Sentinel {}:{}. Sleeping {}ms and retrying.", host, port, subscribeRetryWaitTimeMillis, e); try { Thread.sleep(subscribeRetryWaitTimeMillis); } catch (InterruptedException e1) { LOG.error("Sleep interrupted: ", e1); } } else { LOG.debug("Unsubscribing from Sentinel at {}:{}", host, port); } } finally { if (j != null) { j.close(); } } } } public void shutdown() { try { LOG.debug("Shutting down listener on {}:{}", host, port); running.set(false); // This isn't good, the Jedis object is not thread safe if (j != null) { j.close(); } } catch (RuntimeException e) { LOG.error("Caught exception while shutting down: ", e); } } } } ================================================ FILE: src/main/java/redis/clients/jedis/JedisSentineled.java ================================================ package redis.clients.jedis; import java.util.Set; import org.apache.commons.pool2.impl.GenericObjectPoolConfig; import redis.clients.jedis.annots.Experimental; import redis.clients.jedis.builders.SentinelClientBuilder; import redis.clients.jedis.csc.Cache; import redis.clients.jedis.csc.CacheConfig; import redis.clients.jedis.csc.CacheFactory; import redis.clients.jedis.executors.CommandExecutor; import redis.clients.jedis.providers.ConnectionProvider; import redis.clients.jedis.providers.SentineledConnectionProvider; /** * JedisSentineled is a client for Redis Sentinel deployments. * * @deprecated Use {@link RedisSentinelClient} instead. RedisSentinelClient provides the same functionality * with a cleaner API. Use {@link RedisSentinelClient#builder()} to configure the client * with sentinel settings, master configuration, and connection pooling options. */ @Deprecated public class JedisSentineled extends UnifiedJedis { public JedisSentineled(String masterName, final JedisClientConfig masterClientConfig, Set sentinels, final JedisClientConfig sentinelClientConfig) { super(new SentineledConnectionProvider(masterName, masterClientConfig, sentinels, sentinelClientConfig), masterClientConfig.getRedisProtocol()); } @Experimental public JedisSentineled(String masterName, final JedisClientConfig masterClientConfig, CacheConfig cacheConfig, Set sentinels, final JedisClientConfig sentinelClientConfig) { this(masterName, masterClientConfig, CacheFactory.getCache(cacheConfig), sentinels, sentinelClientConfig); } @Experimental public JedisSentineled(String masterName, final JedisClientConfig masterClientConfig, Cache clientSideCache, Set sentinels, final JedisClientConfig sentinelClientConfig) { super(new SentineledConnectionProvider(masterName, masterClientConfig, clientSideCache, sentinels, sentinelClientConfig), masterClientConfig.getRedisProtocol(), clientSideCache); } public JedisSentineled(String masterName, final JedisClientConfig masterClientConfig, final GenericObjectPoolConfig poolConfig, Set sentinels, final JedisClientConfig sentinelClientConfig) { super(new SentineledConnectionProvider(masterName, masterClientConfig, poolConfig, sentinels, sentinelClientConfig), masterClientConfig.getRedisProtocol()); } @Experimental public JedisSentineled(String masterName, final JedisClientConfig masterClientConfig, Cache clientSideCache, final GenericObjectPoolConfig poolConfig, Set sentinels, final JedisClientConfig sentinelClientConfig) { super(new SentineledConnectionProvider(masterName, masterClientConfig, clientSideCache, poolConfig, sentinels, sentinelClientConfig), masterClientConfig.getRedisProtocol(), clientSideCache); } public JedisSentineled(SentineledConnectionProvider sentineledConnectionProvider) { super(sentineledConnectionProvider); } private JedisSentineled(CommandExecutor commandExecutor, ConnectionProvider connectionProvider, CommandObjects commandObjects, RedisProtocol redisProtocol, Cache cache) { super(commandExecutor, connectionProvider, commandObjects, redisProtocol, cache); } /** * Fluent builder for {@link JedisSentineled} (Redis Sentinel). *

* Obtain an instance via {@link #builder()}. *

*/ static public class Builder extends SentinelClientBuilder { @Override protected JedisSentineled createClient() { return new JedisSentineled(commandExecutor, connectionProvider, commandObjects, clientConfig.getRedisProtocol(), cache); } } /** * Create a new builder for configuring JedisSentineled instances. * * @return a new {@link JedisSentineled.Builder} instance */ public static Builder builder() { return new Builder(); } public HostAndPort getCurrentMaster() { return ((SentineledConnectionProvider) provider).getCurrentMaster(); } @Override public Pipeline pipelined() { return (Pipeline) super.pipelined(); } } ================================================ FILE: src/main/java/redis/clients/jedis/JedisShardedPubSub.java ================================================ package redis.clients.jedis; import redis.clients.jedis.util.SafeEncoder; public abstract class JedisShardedPubSub extends JedisShardedPubSubBase { @Override protected final String encode(byte[] raw) { return SafeEncoder.encode(raw); } } ================================================ FILE: src/main/java/redis/clients/jedis/JedisShardedPubSubBase.java ================================================ package redis.clients.jedis; import static redis.clients.jedis.Protocol.ResponseKeyword.*; import java.util.Arrays; import java.util.List; import java.util.function.Consumer; import redis.clients.jedis.Protocol.Command; import redis.clients.jedis.exceptions.JedisException; import redis.clients.jedis.util.SafeEncoder; public abstract class JedisShardedPubSubBase { private int subscribedChannels = 0; private final JedisSafeAuthenticator authenticator = new JedisSafeAuthenticator(); public void onSMessage(T channel, T message) { } public void onSSubscribe(T channel, int subscribedChannels) { } public void onSUnsubscribe(T channel, int subscribedChannels) { } private void sendAndFlushCommand(Command command, T... args) { authenticator.sendAndFlushCommand(command, args); } public final void sunsubscribe() { sendAndFlushCommand(Command.SUNSUBSCRIBE); } public final void sunsubscribe(T... channels) { sendAndFlushCommand(Command.SUNSUBSCRIBE, channels); } public final void ssubscribe(T... channels) { checkConnectionSuitableForPubSub(); sendAndFlushCommand(Command.SSUBSCRIBE, channels); } private void checkConnectionSuitableForPubSub() { if (authenticator.client.protocol != RedisProtocol.RESP3 && authenticator.client.isTokenBasedAuthenticationEnabled()) { throw new JedisException( "Blocking pub/sub operations are not supported on token-based authentication enabled connections with RESP2 protocol!"); } } public final boolean isSubscribed() { return subscribedChannels > 0; } public final int getSubscribedChannels() { return subscribedChannels; } public final void proceed(Connection client, T... channels) { authenticator.registerForAuthentication(client); authenticator.client.setTimeoutInfinite(); try { ssubscribe(channels); process(); } finally { authenticator.client.rollbackTimeout(); } } protected abstract T encode(byte[] raw); private void process() { do { Object reply = authenticator.client.getUnflushedObject(); if (reply instanceof List) { List listReply = (List) reply; final Object firstObj = listReply.get(0); if (!(firstObj instanceof byte[])) { throw new JedisException("Unknown message type: " + firstObj); } final byte[] resp = (byte[]) firstObj; if (Arrays.equals(SSUBSCRIBE.getRaw(), resp)) { subscribedChannels = ((Long) listReply.get(2)).intValue(); final byte[] bchannel = (byte[]) listReply.get(1); final T enchannel = (bchannel == null) ? null : encode(bchannel); onSSubscribe(enchannel, subscribedChannels); } else if (Arrays.equals(SUNSUBSCRIBE.getRaw(), resp)) { subscribedChannels = ((Long) listReply.get(2)).intValue(); final byte[] bchannel = (byte[]) listReply.get(1); final T enchannel = (bchannel == null) ? null : encode(bchannel); onSUnsubscribe(enchannel, subscribedChannels); } else if (Arrays.equals(SMESSAGE.getRaw(), resp)) { final byte[] bchannel = (byte[]) listReply.get(1); final byte[] bmesg = (byte[]) listReply.get(2); final T enchannel = (bchannel == null) ? null : encode(bchannel); final T enmesg = (bmesg == null) ? null : encode(bmesg); onSMessage(enchannel, enmesg); } else { throw new JedisException("Unknown message type: " + firstObj); } } else if (reply instanceof byte[]) { Consumer resultHandler = authenticator.resultHandler.poll(); if (resultHandler == null) { throw new JedisException("Unexpected message : " + SafeEncoder.encode((byte[]) reply)); } resultHandler.accept(reply); } else { throw new JedisException("Unknown message type: " + reply); } } while (!Thread.currentThread().isInterrupted() && isSubscribed()); // /* Invalidate instance since this thread is no longer listening */ // this.client = null; } } ================================================ FILE: src/main/java/redis/clients/jedis/JedisSocketFactory.java ================================================ package redis.clients.jedis; import java.net.Socket; import redis.clients.jedis.exceptions.JedisConnectionException; /** * JedisSocketFactory: responsible for creating socket connections * from the within the Jedis client, the default socket factory will * create TCP sockets with the recommended configuration. *

* You can use a custom JedisSocketFactory for many use cases, such as: * - a custom address resolver * - a unix domain socket * - a custom configuration for you TCP sockets */ public interface JedisSocketFactory { Socket createSocket() throws JedisConnectionException; } ================================================ FILE: src/main/java/redis/clients/jedis/Module.java ================================================ package redis.clients.jedis; // TODO: 'resps' package // TODO: remove public class Module { private final String name; private final int version; public Module(String name, int version) { this.name = name; this.version = version; } public String getName() { return name; } public int getVersion() { return version; } @Override public boolean equals(Object o) { if (o == null) return false; if (o == this) return true; if (!(o instanceof Module)) return false; Module module = (Module) o; if (version != module.version) return false; return !(name != null ? !name.equals(module.name) : module.name != null); } @Override public int hashCode() { int result = name != null ? name.hashCode() : 0; result = 31 * result + version; return result; } } ================================================ FILE: src/main/java/redis/clients/jedis/MultiDbClient.java ================================================ package redis.clients.jedis; import redis.clients.jedis.MultiDbConfig.DatabaseConfig; import redis.clients.jedis.annots.Experimental; import redis.clients.jedis.builders.MultiDbClientBuilder; import redis.clients.jedis.csc.Cache; import redis.clients.jedis.exceptions.JedisValidationException; import redis.clients.jedis.executors.CommandExecutor; import redis.clients.jedis.mcf.MultiDbCommandExecutor; import redis.clients.jedis.mcf.MultiDbPipeline; import redis.clients.jedis.mcf.MultiDbTransaction; import redis.clients.jedis.providers.ConnectionProvider; import redis.clients.jedis.mcf.MultiDbConnectionProvider; import redis.clients.jedis.mcf.MultiDbConnectionProvider.Database; import java.util.Set; /** * MultiDbClient provides high-availability Redis connectivity with automatic failover and failback * capabilities across multiple weighted endpoints. *

* This client extends UnifiedJedis to support resilient operations with: *

    *
  • Multi-Endpoint Support: Configure multiple Redis endpoints with individual * weights
  • *
  • Automatic Failover: Seamless switching to backup endpoints when primary * becomes unavailable
  • *
  • Circuit Breaker Pattern: Built-in circuit breaker to prevent cascading * failures
  • *
  • Weight-Based Selection: Intelligent endpoint selection based on configured * weights
  • *
  • Health Monitoring: Continuous health checks with automatic failback to * recovered endpoints
  • *
  • Retry Logic: Configurable retry mechanisms with exponential backoff
  • *
*

* Usage Example: *

* *
 * // Create multi-db client with multiple endpoints
 * HostAndPort primary = new HostAndPort("localhost", 29379);
 * HostAndPort secondary = new HostAndPort("localhost", 29380);
 *
 *
 * MultiDbClient client = MultiDbClient.builder()
 *                 .multiDbConfig(
 *                         MultiDbConfig.builder()
 *                                 .database(
 *                                         DatabaseConfig.builder(
 *                                                         primary,
 *                                                         DefaultJedisClientConfig.builder().build())
 *                                                 .weight(100.0f)
 *                                                 .build())
 *                                 .database(DatabaseConfig.builder(
 *                                                 secondary,
 *                                                 DefaultJedisClientConfig.builder().build())
 *                                         .weight(50.0f).build())
 *                                 .failureDetector(MultiDbConfig.CircuitBreakerConfig.builder()
 *                                         .failureRateThreshold(50.0f)
 *                                         .build())
 *                                 .commandRetry(MultiDbConfig.RetryConfig.builder()
 *                                         .maxAttempts(3)
 *                                         .build())
 *                                 .build()
 *                 )
 *                 .databaseSwitchListener(event ->
 *                    System.out.println("Switched to: " + event.getEndpoint()))
 *                 .build();
 * 
 * // Use like any other Jedis client
 * client.set("key", "value");
 * String value = client.get("key");
 * 
 * // Automatic failover happens transparently
 * client.close();
 * 
*

* The client automatically handles endpoint failures and recoveries, providing transparent high * availability for Redis operations. All standard Jedis operations are supported with the added * resilience features. *

* @author Ivo Gaydazhiev * @since 7.0.0 * @see MultiDbConnectionProvider * @see MultiDbCommandExecutor * @see MultiDbConfig */ @Experimental public class MultiDbClient extends UnifiedJedis { /** * Creates a MultiDbClient with custom components. *

* This constructor allows full customization of the client components and is primarily used by * the builder pattern for advanced configurations. For most use cases, prefer using * {@link #builder()} to create instances. *

* @param commandExecutor the command executor (typically MultiDbCommandExecutor) * @param connectionProvider the connection provider (typically MultiDbConnectionProvider) * @param commandObjects the command objects * @param redisProtocol the Redis protocol version * @param cache the client-side cache (may be null) */ MultiDbClient(CommandExecutor commandExecutor, ConnectionProvider connectionProvider, CommandObjects commandObjects, RedisProtocol redisProtocol, Cache cache) { super(commandExecutor, connectionProvider, commandObjects, redisProtocol, cache); } /** * Returns the underlying MultiDbConnectionProvider. *

* This provides access to multi-database specific operations like manual failover, health status * monitoring, and database switch event handling. *

* @return the multi-db connection provider * @throws ClassCastException if the provider is not a MultiDbConnectionProvider */ private MultiDbConnectionProvider getMultiDbConnectionProvider() { return (MultiDbConnectionProvider) this.provider; } /** * Manually switches to the specified endpoint. *

* This method allows manual failover to a specific endpoint, bypassing the automatic weight-based * selection. The switch will only succeed if the target endpoint is healthy. *

* @param endpoint the endpoint to switch to */ public void setActiveDatabase(Endpoint endpoint) { getMultiDbConnectionProvider().setActiveDatabase(endpoint); } /** * Adds a pre-configured database configuration. *

* This method allows adding a fully configured DatabaseConfig instance, providing maximum * flexibility for advanced configurations including custom health check strategies, connection * pool settings, etc. *

* @param databaseConfig the pre-configured database configuration */ public void addDatabase(DatabaseConfig databaseConfig) { getMultiDbConnectionProvider().add(databaseConfig); } /** * Dynamically adds a new database endpoint to the multi-database client. *

* This allows adding new database endpoints at runtime without recreating the client. The new * endpoint will be available for failover operations immediately after being added and passing * health checks (if configured). *

* @param endpoint the Redis server endpoint * @param weight the weight for this endpoint (higher values = higher priority) * @param clientConfig the client configuration for this endpoint * @throws redis.clients.jedis.exceptions.JedisValidationException if the endpoint already exists */ public void addDatabase(Endpoint endpoint, float weight, JedisClientConfig clientConfig) { DatabaseConfig databaseConfig = DatabaseConfig.builder(endpoint, clientConfig).weight(weight) .build(); getMultiDbConnectionProvider().add(databaseConfig); } /** * Returns the set of all configured database endpoints. *

* This method provides a view of all database endpoints currently configured in the * multi-database client. These are the endpoints that can be used for failover operations. *

* @return the set of all configured database endpoints */ public Set getDatabaseEndpoints() { return getMultiDbConnectionProvider().getEndpoints(); } /** * Returns the health status of the specified database. *

* This method provides the current health status of a specific endpoint. *

* @param endpoint the endpoint to check * @return the health status of the endpoint */ public boolean isHealthy(Endpoint endpoint) { return getMultiDbConnectionProvider().isHealthy(endpoint); } /** * Returns the weight of the specified database. *

* This method provides the current weight of a specific endpoint. *

* @param endpoint the endpoint to check * @throws redis.clients.jedis.exceptions.JedisValidationException if the endpoint doesn't exist * @return the weight of the endpoint */ public float getWeight(Endpoint endpoint) { Database db = getMultiDbConnectionProvider().getDatabase(endpoint); if (db == null) { throw new JedisValidationException("Endpoint " + endpoint + " does not exist."); } return db.getWeight(); } /** * Sets the weight of the specified database. *

* This method allows changing the weight of a specific endpoint at runtime. The weight determines * the priority of the endpoint during failover operations. *

* @param endpoint the endpoint to change * @param weight the new weight for the endpoint * @throws redis.clients.jedis.exceptions.JedisValidationException if the endpoint doesn't exist */ public void setWeight(Endpoint endpoint, float weight) { Database db = getMultiDbConnectionProvider().getDatabase(endpoint); if (db == null) { throw new JedisValidationException("Endpoint " + endpoint + " does not exist."); } db.setWeight(weight); } /** * Dynamically removes a database endpoint from the multi-database client. *

* This allows removing database endpoints at runtime. If the removed endpoint is currently * active, the client will automatically failover to the next available healthy endpoint based on * weight priority. *

* @param endpoint the endpoint to remove * @throws redis.clients.jedis.exceptions.JedisValidationException if the endpoint doesn't exist * @throws redis.clients.jedis.exceptions.JedisException if removing the endpoint would leave no * healthy databases available */ public void removeDatabase(Endpoint endpoint) { getMultiDbConnectionProvider().remove(endpoint); } /** * Forces the client to switch to a specific database for a duration. *

* This method forces the client to use the specified database endpoint and puts all other * endpoints in a grace period, preventing automatic failover for the specified duration. This is * useful for maintenance scenarios or testing specific database endpoints. *

* @param endpoint the endpoint to force as active * @param forcedActiveDurationMs the duration in milliseconds to keep this endpoint forced * @throws redis.clients.jedis.exceptions.JedisValidationException if the endpoint is not healthy * or doesn't exist */ public void forceActiveDatabase(Endpoint endpoint, long forcedActiveDurationMs) { getMultiDbConnectionProvider().forceActiveDatabase(endpoint, forcedActiveDurationMs); } /** * Creates a new pipeline for batch operations with multi-db support. *

* The returned pipeline supports the same resilience features as the main client, including * automatic failover during batch execution. *

* @return a new MultiDbPipeline instance */ @Override public MultiDbPipeline pipelined() { return new MultiDbPipeline(getMultiDbConnectionProvider(), commandObjects); } /** * Creates a new transaction with multi-database support. *

* The returned transaction supports the same resilience features as the main client, including * automatic failover during transaction execution. *

* @return a new MultiDbTransaction instance */ @Override public MultiDbTransaction multi() { return new MultiDbTransaction((MultiDbConnectionProvider) provider, true, commandObjects); } /** * @param doMulti {@code false} should be set to enable manual WATCH, UNWATCH and MULTI * @return transaction object */ @Override public MultiDbTransaction transaction(boolean doMulti) { if (provider == null) { throw new IllegalStateException( "It is not allowed to create Transaction from this " + getClass()); } return new MultiDbTransaction(getMultiDbConnectionProvider(), doMulti, commandObjects); } /** * Returns the currently active database endpoint. *

* The active endpoint is the one currently being used for all operations. It can change at any * time due to health checks, failover, failback, or manual switching. *

* @return the active database endpoint */ public Endpoint getActiveDatabaseEndpoint() { return getMultiDbConnectionProvider().getDatabase().getEndpoint(); } /** * Fluent builder for {@link MultiDbClient}. *

* Obtain an instance via {@link #builder()}. *

*/ public static class Builder extends MultiDbClientBuilder { @Override protected MultiDbClient createClient() { return new MultiDbClient(commandExecutor, connectionProvider, commandObjects, clientConfig.getRedisProtocol(), cache); } } /** * Create a new builder for configuring MultiDbClient instances. * @return a new {@link MultiDbClient.Builder} instance */ public static Builder builder() { return new Builder(); } } ================================================ FILE: src/main/java/redis/clients/jedis/MultiDbConfig.java ================================================ package redis.clients.jedis; import io.github.resilience4j.circuitbreaker.CallNotPermittedException; import java.time.Duration; import java.util.ArrayList; import java.util.Arrays; import java.util.List; import org.apache.commons.pool2.impl.GenericObjectPoolConfig; import redis.clients.jedis.annots.Experimental; import redis.clients.jedis.exceptions.JedisConnectionException; import redis.clients.jedis.exceptions.JedisValidationException; import redis.clients.jedis.mcf.ConnectionFailoverException; import redis.clients.jedis.mcf.PingStrategy; import redis.clients.jedis.util.JedisAsserts; import redis.clients.jedis.mcf.HealthCheckStrategy; import redis.clients.jedis.mcf.InitializationPolicy; /** * Configuration class for multi-database Redis deployments with automatic failover and failback * capabilities. *

* This configuration enables seamless failover between multiple Redis databases endpoints by * providing comprehensive settings for retry logic, circuit breaker behavior, health checks, and * failback mechanisms. It is designed to work with * {@link redis.clients.jedis.mcf.MultiDbConnectionProvider} to provide high availability and * disaster recovery capabilities. *

*

* Key Features: *

*
    *
  • Multi-Database Support: Configure multiple Redis endpoints with individual * weights and health checks
  • *
  • Circuit Breaker Pattern: Automatic failure detection and circuit opening * based on configurable thresholds
  • *
  • Retry Logic: Configurable retry attempts with exponential backoff for * transient failures
  • *
  • Health Check Integration: Pluggable health check strategies for proactive * monitoring
  • *
  • Automatic Failback: Intelligent failback to higher-priority databases when * they recover
  • *
  • Weight-Based Routing: Priority-based database selection using configurable * weights
  • *
*

* Usage Example: *

* *
 * {
 *   @code
 *   // Configure individual databases
 *   DatabaseConfig primary = DatabaseConfig.builder(primaryEndpoint, clientConfig).weight(1.0f)
 *       .build();
 *
 *   DatabaseConfig secondary = DatabaseConfig.builder(secondaryEndpoint, clientConfig).weight(0.5f)
 *       .healthCheckEnabled(true).build();
 *
 *   // Build multi-database configuration
 *   MultiDbConfig config = MultiDbConfig.builder(primary, secondary)
 *       .failureDetector(CircuitBreakerConfig.builder().failureRateThreshold(10.0f).build())
 *       .commandRetry(RetryConfig.builder().maxAttempts(3).build()).failbackSupported(true)
 *       .gracePeriod(10000).build();
 *
 *   // Use with connection provider
 *   MultiDbConnectionProvider provider = new MultiDbConnectionProvider(config);
 * }
 * 
*

* The configuration leverages Resilience4j for * circuit breaker and retry implementations, providing battle-tested fault tolerance patterns. *

* @see redis.clients.jedis.mcf.MultiDbConnectionProvider * @see redis.clients.jedis.mcf.HealthCheckStrategy * @see redis.clients.jedis.mcf.PingStrategy * @see redis.clients.jedis.mcf.LagAwareStrategy * @since 7.0 */ // TODO: move @Experimental public final class MultiDbConfig { /** * Functional interface for creating {@link HealthCheckStrategy} instances for specific Redis * endpoints. *

* This supplier pattern allows for flexible health check strategy creation, enabling different * strategies for different endpoints or dynamic configuration based on endpoint characteristics. *

*

* Common Implementations: *

*
    *
  • {@link redis.clients.jedis.mcf.PingStrategy#DEFAULT} - Uses Redis PING command for health * checks
  • *
  • Custom implementations for specific monitoring requirements
  • *
  • Redis Enterprise implementations using REST API monitoring
  • *
* @see redis.clients.jedis.mcf.HealthCheckStrategy * @see redis.clients.jedis.mcf.PingStrategy * @see redis.clients.jedis.mcf.LagAwareStrategy */ public static interface StrategySupplier { /** * Creates a {@link HealthCheckStrategy} instance for the specified Redis endpoint. * @param hostAndPort the Redis endpoint (host and port) to create a health check strategy for * @param jedisClientConfig the Jedis client configuration containing connection settings, * authentication, and other client parameters. May be null for implementations that * don't require client configuration * @return a configured {@link HealthCheckStrategy} instance for monitoring the specified * endpoint * @throws IllegalArgumentException if the hostAndPort is null or invalid */ HealthCheckStrategy get(HostAndPort hostAndPort, JedisClientConfig jedisClientConfig); } /** * Configuration for command retry behavior. *

* This class encapsulates all retry-related settings including maximum attempts, wait duration, * exponential backoff, and exception handling. It provides a clean separation of retry concerns * from other configuration aspects. *

* @since 7.0 */ public static final class RetryConfig { private final int maxAttempts; private final Duration waitDuration; private final int exponentialBackoffMultiplier; private final List includedExceptionList; private final List ignoreExceptionList; private RetryConfig(Builder builder) { this.maxAttempts = builder.maxAttempts; this.waitDuration = Duration.ofMillis(builder.waitDuration); this.exponentialBackoffMultiplier = builder.exponentialBackoffMultiplier; this.includedExceptionList = builder.includedExceptionList; this.ignoreExceptionList = builder.ignoreExceptionList; } /** * Returns the maximum number of retry attempts including the initial call. * @return maximum retry attempts */ public int getMaxAttempts() { return maxAttempts; } /** * Returns the base wait duration between retry attempts. * @return wait duration between retries */ public Duration getWaitDuration() { return waitDuration; } /** * Returns the exponential backoff multiplier for retry wait duration. * @return exponential backoff multiplier */ public int getExponentialBackoffMultiplier() { return exponentialBackoffMultiplier; } /** * Returns the list of exception classes that trigger retry attempts. * @return list of exception classes that are retried, never null */ public List getIncludedExceptionList() { return includedExceptionList; } /** * Returns the list of exception classes that are ignored for retry purposes. * @return list of exception classes to ignore for retries, may be null */ public List getIgnoreExceptionList() { return ignoreExceptionList; } /** * Creates a new Builder instance for configuring RetryConfig. * @return new Builder instance with default values */ public static Builder builder() { return new Builder(); } /** * Builder for {@link RetryConfig}. */ public static final class Builder { private int maxAttempts = RETRY_MAX_ATTEMPTS_DEFAULT; private int waitDuration = RETRY_WAIT_DURATION_DEFAULT; private int exponentialBackoffMultiplier = RETRY_WAIT_DURATION_EXPONENTIAL_BACKOFF_MULTIPLIER_DEFAULT; private List includedExceptionList = RETRY_INCLUDED_EXCEPTIONS_DEFAULT; private List ignoreExceptionList = null; /** * Sets the maximum number of retry attempts including the initial call. * @param maxAttempts maximum number of attempts (must be >= 1) * @return this builder instance for method chaining */ public Builder maxAttempts(int maxAttempts) { this.maxAttempts = maxAttempts; return this; } /** * Sets the base wait duration between retry attempts in milliseconds. * @param waitDuration wait duration in milliseconds (must be >= 0) * @return this builder instance for method chaining */ public Builder waitDuration(int waitDuration) { this.waitDuration = waitDuration; return this; } /** * Sets the exponential backoff multiplier for retry wait duration. * @param exponentialBackoffMultiplier exponential backoff multiplier (must be >= 1) * @return this builder instance for method chaining */ public Builder exponentialBackoffMultiplier(int exponentialBackoffMultiplier) { this.exponentialBackoffMultiplier = exponentialBackoffMultiplier; return this; } /** * Sets the list of exception classes that trigger retry attempts. * @param includedExceptionList list of exception classes that should be retried * @return this builder instance for method chaining */ public Builder includedExceptionList(List includedExceptionList) { this.includedExceptionList = includedExceptionList; return this; } /** * Sets the list of exception classes that are ignored for retry purposes. * @param ignoreExceptionList list of exception classes to ignore for retries * @return this builder instance for method chaining */ public Builder ignoreExceptionList(List ignoreExceptionList) { this.ignoreExceptionList = ignoreExceptionList; return this; } /** * Builds and returns a new RetryConfig instance. * @return new RetryConfig instance with configured settings */ public RetryConfig build() { return new RetryConfig(this); } } } /** * Configuration for circuit breaker failure detection. *

* This class encapsulates all circuit breaker-related settings including failure rate threshold, * sliding window size, minimum failures, and exception handling. *

* @since 7.0 */ public static final class CircuitBreakerConfig { private final float failureRateThreshold; private final int slidingWindowSize; private final int minNumOfFailures; private final List includedExceptionList; private final List ignoreExceptionList; private CircuitBreakerConfig(Builder builder) { this.failureRateThreshold = builder.failureRateThreshold; this.slidingWindowSize = builder.slidingWindowSize; this.minNumOfFailures = builder.minNumOfFailures; this.includedExceptionList = builder.includedExceptionList; this.ignoreExceptionList = builder.ignoreExceptionList; } /** * Returns the failure rate threshold percentage for circuit breaker activation. *

* 0.0f means failure rate is ignored, and only minimum number of failures is considered. *

*

* When the failure rate exceeds both this threshold and the minimum number of failures, the * circuit breaker transitions to the OPEN state and starts short-circuiting calls, immediately * failing them without attempting to reach the Redis database. This prevents cascading failures * and allows the system to fail over to the next available database. *

*

* Range: 0.0 to 100.0 (percentage) *

* @return failure rate threshold as a percentage (0.0 to 100.0) * @see #getMinNumOfFailures() */ public float getFailureRateThreshold() { return failureRateThreshold; } /** * Returns the size of the sliding window used to record call outcomes when the circuit breaker * is CLOSED. *

* Default: {@value #CIRCUIT_BREAKER_SLIDING_WINDOW_SIZE_DEFAULT} *

* @return sliding window size (calls or seconds depending on window type) */ public int getSlidingWindowSize() { return slidingWindowSize; } /** * Returns the minimum number of failures before circuit breaker is tripped. *

* 0 means minimum number of failures is ignored, and only failure rate is considered. *

*

* When the number of failures exceeds both this threshold and the failure rate threshold, the * circuit breaker will trip and prevent further requests from being sent to the database until * it has recovered. *

* @return minimum number of failures before circuit breaker is tripped * @see #getFailureRateThreshold() */ public int getMinNumOfFailures() { return minNumOfFailures; } /** * Returns the list of exception classes that are recorded as circuit breaker failures and * increase the failure rate. *

* Any exception that matches or inherits from the classes in this list counts as a failure for * circuit breaker calculations, unless explicitly ignored via * {@link #getIgnoreExceptionList()}. If you specify this list, all other exceptions count as * successes unless they are explicitly ignored. *

*

* Default: {@link JedisConnectionException} *

* @return list of exception classes that count as failures, never null * @see #getIgnoreExceptionList() */ public List getIncludedExceptionList() { return includedExceptionList; } /** * Returns the list of exception classes that are ignored by the circuit breaker and neither * count as failures nor successes. *

* Any exception that matches or inherits from the classes in this list will not affect circuit * breaker failure rate calculations, even if the exception is included in * {@link #getIncludedExceptionList()}. *

*

* Default: null (no exceptions ignored) *

* @return list of exception classes to ignore for circuit breaker calculations, may be null * @see #getIncludedExceptionList() */ public List getIgnoreExceptionList() { return ignoreExceptionList; } /** * Creates a new Builder instance for configuring CircuitBreakerConfig. * @return new Builder instance with default values */ public static Builder builder() { return new Builder(); } /** * Builder for {@link CircuitBreakerConfig}. */ public static final class Builder { private float failureRateThreshold = CIRCUIT_BREAKER_FAILURE_RATE_THRESHOLD_DEFAULT; private int slidingWindowSize = CIRCUIT_BREAKER_SLIDING_WINDOW_SIZE_DEFAULT; private int minNumOfFailures = CIRCUITBREAKER_THRESHOLD_MIN_NUM_OF_FAILURES_DEFAULT; private List includedExceptionList = CIRCUIT_BREAKER_INCLUDED_EXCEPTIONS_DEFAULT; private List ignoreExceptionList = null; /** * Sets the failure rate threshold percentage that triggers circuit breaker activation. * @param failureRateThreshold failure rate threshold as percentage (0.0 to 100.0) * @return this builder instance for method chaining */ public Builder failureRateThreshold(float failureRateThreshold) { this.failureRateThreshold = failureRateThreshold; return this; } /** * Sets the size of the sliding window for circuit breaker calculations. * @param slidingWindowSize sliding window size * @return this builder instance for method chaining */ public Builder slidingWindowSize(int slidingWindowSize) { this.slidingWindowSize = slidingWindowSize; return this; } /** * Sets the minimum number of failures before circuit breaker is tripped. * @param minNumOfFailures minimum number of failures * @return this builder instance for method chaining */ public Builder minNumOfFailures(int minNumOfFailures) { this.minNumOfFailures = minNumOfFailures; return this; } /** * Sets the list of exception classes that are recorded as circuit breaker failures. * @param includedExceptionList list of exception classes that count as failures * @return this builder instance for method chaining */ public Builder includedExceptionList(List includedExceptionList) { this.includedExceptionList = includedExceptionList; return this; } /** * Sets the list of exception classes that are ignored by the circuit breaker. * @param ignoreExceptionList list of exception classes to ignore * @return this builder instance for method chaining */ public Builder ignoreExceptionList(List ignoreExceptionList) { this.ignoreExceptionList = ignoreExceptionList; return this; } /** * Builds and returns a new CircuitBreakerConfig instance. * @return new CircuitBreakerConfig instance with configured settings */ public CircuitBreakerConfig build() { return new CircuitBreakerConfig(this); } } } // ============ Default Configuration Constants ============ /** Default maximum number of retry attempts including the initial call. */ private static final int RETRY_MAX_ATTEMPTS_DEFAULT = 3; /** Default wait duration between retry attempts in milliseconds. */ private static final int RETRY_WAIT_DURATION_DEFAULT = 500; /** Default exponential backoff multiplier for retry wait duration. */ private static final int RETRY_WAIT_DURATION_EXPONENTIAL_BACKOFF_MULTIPLIER_DEFAULT = 2; /** Default list of exceptions that trigger retry attempts. */ private static final List RETRY_INCLUDED_EXCEPTIONS_DEFAULT = Arrays .asList(JedisConnectionException.class); /** Default failure rate threshold percentage for circuit breaker activation. */ private static final float CIRCUIT_BREAKER_FAILURE_RATE_THRESHOLD_DEFAULT = 10.0f; /** Minimum number of failures before circuit breaker is tripped. */ private static final int CIRCUITBREAKER_THRESHOLD_MIN_NUM_OF_FAILURES_DEFAULT = 1000; /** Default sliding window size for circuit breaker failure tracking. */ private static final int CIRCUIT_BREAKER_SLIDING_WINDOW_SIZE_DEFAULT = 2; /** Default list of exceptions that are recorded as circuit breaker failures. */ private static final List CIRCUIT_BREAKER_INCLUDED_EXCEPTIONS_DEFAULT = Arrays .asList(JedisConnectionException.class); /** Default list of exceptions that trigger fallback to next available database. */ private static final List> FALLBACK_EXCEPTIONS_DEFAULT = Arrays .asList(CallNotPermittedException.class, ConnectionFailoverException.class); /** Default interval in milliseconds for checking if failed databases have recovered. */ private static final long FAILBACK_CHECK_INTERVAL_DEFAULT = 120000; /** * Default grace period in milliseconds to keep databases disabled after they become unhealthy. */ private static final long GRACE_PERIOD_DEFAULT = 60000; /** Default maximum number of failover attempts. */ private static final int MAX_NUM_FAILOVER_ATTEMPTS_DEFAULT = 10; /** Default delay in milliseconds between failover attempts. */ private static final int DELAY_IN_BETWEEN_FAILOVER_ATTEMPTS_DEFAULT = 12000; /** Array of database configurations defining the available Redis endpoints and their settings. */ private final DatabaseConfig[] databaseConfigs; // ============ Retry Configuration ============ // Based on Resilience4j Retry: https://resilience4j.readme.io/docs/retry /** * Encapsulated retry configuration for command execution. *

* This provides a cleaner API for configuring retry behavior by grouping all retry-related * settings into a single configuration object. *

* @see RetryConfig */ private RetryConfig commandRetry; // ============ Circuit Breaker Configuration ============ /** * Encapsulated circuit breaker configuration for failure detection. *

* This provides a cleaner API for configuring circuit breaker behavior by grouping all circuit * breaker-related settings into a single configuration object. *

* @see CircuitBreakerConfig */ private CircuitBreakerConfig failureDetector; /** * List of exception classes that trigger fallback to the next available database. *

* When these exceptions occur, the system will attempt to failover to the next available database * based on weight priority. This enables immediate failover for specific error conditions without * waiting for circuit breaker thresholds. *

*

* Default: {@link CallNotPermittedException}, * {@link ConnectionFailoverException} *

* @see #getFallbackExceptionList() */ private List> fallbackExceptionList; // ============ Failover Configuration ============ /** * Whether to retry failed commands during the failover process. *

* When enabled, commands that fail during failover will be retried according to the configured * retry settings. When disabled, failed commands during failover will immediately return the * failure to the caller. *

*

* Default: false *

* @see #isRetryOnFailover() * @see #commandRetry */ private boolean retryOnFailover; /** * Whether automatic failback to higher-priority databases is supported. *

* When enabled, the system will automatically monitor failed databases using health checks and * failback to higher-priority (higher weight) databases when they recover. When disabled, manual * intervention is required to failback. *

*

* Default: true *

* @see #isFailbackSupported() * @see #failbackCheckInterval * @see #gracePeriod */ private boolean isFailbackSupported; /** * Interval in milliseconds between checks for failback opportunities to recovered databases. *

* This setting controls how frequently the system checks if a higher-priority database has * recovered and is available for failback. Lower values provide faster failback but increase * monitoring overhead. *

*

* Default: {@value #FAILBACK_CHECK_INTERVAL_DEFAULT} milliseconds (5 seconds) *

* @see #getFailbackCheckInterval() * @see #isFailbackSupported * @see #gracePeriod */ private long failbackCheckInterval; /** * Grace period in milliseconds to keep databases disabled after they become unhealthy. *

* After a database is marked as unhealthy, it remains disabled for this grace period before being * eligible for failback, even if health checks indicate recovery. This prevents rapid oscillation * between databases during intermittent failures. *

*

* Default: {@value #GRACE_PERIOD_DEFAULT} milliseconds (10 seconds) *

* @see #getGracePeriod() * @see #isFailbackSupported * @see #failbackCheckInterval */ private long gracePeriod; /** * Whether to forcefully terminate connections during failover for faster database switching. *

* When enabled, existing connections to the failed database are immediately closed during * failover, potentially reducing failover time but may cause some in-flight operations to fail. * When disabled, connections are closed gracefully. *

*

* Default: false *

* @see #isFastFailover() */ private boolean fastFailover; /** * Maximum number of failover attempts. *

* This setting controls how many times the system will attempt to failover to a different * database before giving up. For example, if set to 3, the system will make 1 initial attempt * plus 2 failover attempts for a total of 3 attempts. *

*

* Default: {@value #MAX_NUM_FAILOVER_ATTEMPTS_DEFAULT} *

* @see #getMaxNumFailoverAttempts() */ private int maxNumFailoverAttempts; /** * Delay in milliseconds between failover attempts. *

* This setting controls how long the system will wait before attempting to failover to a * different database. For example, if set to 1000, the system will wait 1 second before * attempting to failover to a different database. *

*

* Default: {@value #DELAY_IN_BETWEEN_FAILOVER_ATTEMPTS_DEFAULT} milliseconds *

* @see #getDelayInBetweenFailoverAttempts() */ private int delayInBetweenFailoverAttempts; /** * Initialization policy that determines when the multi-database connection is ready to be * returned based on the availability of individual database connections. *

* The policy is evaluated based on the completion status of database health checks, and the * decision to continue waiting, succeed, or fail is based on the number of available, pending, * and failed connections. *

*

* Default: {@link InitializationPolicy.BuiltIn#MAJORITY_AVAILABLE} *

* @see InitializationPolicy * @see #getInitializationPolicy() */ private InitializationPolicy initializationPolicy; /** * Constructs a new MultiDbConfig with the specified database configurations. *

* This constructor validates that at least one database configuration is provided and that all * configurations are non-null. Use the {@link Builder} class for more convenient configuration * with default values. *

* @param databaseConfigs array of database configurations defining the available Redis endpoints * @throws JedisValidationException if databaseConfigs is null or empty * @throws IllegalArgumentException if any database configuration is null * @see Builder#Builder(DatabaseConfig[]) */ public MultiDbConfig(DatabaseConfig[] databaseConfigs) { if (databaseConfigs == null || databaseConfigs.length < 1) throw new JedisValidationException( "DatabaseClientConfigs are required for MultiDbConnectionProvider"); for (DatabaseConfig databaseConfig : databaseConfigs) { if (databaseConfig == null) throw new IllegalArgumentException("DatabaseClientConfigs must not contain null elements"); } this.databaseConfigs = databaseConfigs; } /** * Returns the array of database configurations defining available Redis endpoints. * @return array of database configurations, never null or empty */ public DatabaseConfig[] getDatabaseConfigs() { return databaseConfigs; } /** * Returns the encapsulated retry configuration for command execution. *

* This provides access to all retry-related settings through a single configuration object. *

* @return retry configuration, never null * @see RetryConfig */ public RetryConfig getCommandRetry() { return commandRetry; } /** * Returns the encapsulated circuit breaker configuration for failure detection. *

* This provides access to all circuit breaker-related settings through a single configuration * object. *

* @return circuit breaker configuration, never null * @see CircuitBreakerConfig */ public CircuitBreakerConfig getFailureDetector() { return failureDetector; } /** * Returns the list of exception classes that trigger immediate fallback to next database. * @return list of exception classes that trigger fallback, never null * @see #fallbackExceptionList */ public List> getFallbackExceptionList() { return fallbackExceptionList; } /** * Returns whether failed commands are retried during failover. * @return true if commands are retried during failover, false otherwise * @see #retryOnFailover */ public boolean isRetryOnFailover() { return retryOnFailover; } /** * Returns whether automatic failback to higher-priority databases is supported. * @return true if automatic failback is enabled, false if manual failback is required * @see #isFailbackSupported */ public boolean isFailbackSupported() { return isFailbackSupported; } /** * Returns the interval between checks for failback opportunities. * @return failback check interval in milliseconds * @see #failbackCheckInterval */ public long getFailbackCheckInterval() { return failbackCheckInterval; } /** * Returns the grace period to keep databases disabled after they become unhealthy. * @return grace period in milliseconds * @see #gracePeriod */ public long getGracePeriod() { return gracePeriod; } /** * Returns the maximum number of failover attempts. * @return maximum number of failover attempts * @see #maxNumFailoverAttempts */ public int getMaxNumFailoverAttempts() { return maxNumFailoverAttempts; } /** * Returns the delay in milliseconds between failover attempts. * @return delay in milliseconds between failover attempts * @see #delayInBetweenFailoverAttempts */ public int getDelayInBetweenFailoverAttempts() { return delayInBetweenFailoverAttempts; } /** * Returns whether connections are forcefully terminated during failover. * @return true if fast failover is enabled, false for graceful failover * @see #fastFailover */ public boolean isFastFailover() { return fastFailover; } /** * Returns the initialization policy that determines when the multi-database connection is ready. *

* The policy is evaluated based on the completion status of database health checks, and the * decision to continue waiting, succeed, or fail is based on the number of available, pending, * and failed connections. *

* @return the initialization policy, defaults to * {@link InitializationPolicy.BuiltIn#MAJORITY_AVAILABLE} * @see InitializationPolicy * @see #initializationPolicy */ public InitializationPolicy getInitializationPolicy() { return initializationPolicy; } /** * Creates a new Builder instance for configuring MultiDbConfig. *

* At least one database configuration must be added to the builder before calling build(). Use * the endpoint() methods to add database configurations. *

* @return new Builder instance * @throws JedisValidationException if databaseConfigs is null or empty * @see Builder#Builder(DatabaseConfig[]) */ public static Builder builder() { return new Builder(); } /** * Creates a new Builder instance for configuring MultiDbConfig. * @param databaseConfigs array of database configurations defining available Redis endpoints * @return new Builder instance * @throws JedisValidationException if databaseConfigs is null or empty * @see Builder#Builder(DatabaseConfig[]) */ public static Builder builder(DatabaseConfig[] databaseConfigs) { return new Builder(databaseConfigs); } /** * Creates a new Builder instance for configuring MultiDbConfig. * @param databaseConfigs list of database configurations defining available Redis endpoints * @return new Builder instance * @throws JedisValidationException if databaseConfigs is null or empty * @see Builder#Builder(List) */ public static Builder builder(List databaseConfigs) { return new Builder(databaseConfigs); } /** * Configuration class for individual Redis database endpoints within a multi-database setup. *

* Each DatabaseConfig represents a single Redis endpoint that can participate in the * multi-database failover system. It encapsulates the connection details, weight for * priority-based selection, and health check configuration for that endpoint. *

* @see Builder * @see StrategySupplier * @see redis.clients.jedis.mcf.HealthCheckStrategy */ public static class DatabaseConfig { /** The Redis endpoint (host and port) for this database. */ private final Endpoint endpoint; /** Jedis client configuration containing connection settings and authentication. */ private final JedisClientConfig jedisClientConfig; /** Optional connection pool configuration for managing connections to this database. */ private GenericObjectPoolConfig connectionPoolConfig; /** * Weight value for database selection priority. Higher weights indicate higher priority. * Default value is 1.0f. */ private float weight = 1.0f; /** Health check enabled. Default: true */ private boolean healthCheckEnabled = true; /** * Strategy supplier for creating health check instances for this database. Default is * PingStrategy.DEFAULT. */ private StrategySupplier healthCheckStrategySupplier = PingStrategy.DEFAULT; /** * Constructs a DatabaseConfig with basic endpoint and client configuration. *

* This constructor creates a database configuration with default settings: weight of 1.0f and * PingStrategy for health checks. Use the {@link Builder} for more advanced configuration * options. *

* @param endpoint the Redis endpoint (host and port) * @param clientConfig the Jedis client configuration * @throws IllegalArgumentException if endpoint or clientConfig is null */ public DatabaseConfig(Endpoint endpoint, JedisClientConfig clientConfig) { this.endpoint = endpoint; this.jedisClientConfig = clientConfig; } /** * Private constructor used by the Builder to create configured instances. * @param builder the builder containing configuration values */ private DatabaseConfig(Builder builder) { this.endpoint = builder.endpoint; this.jedisClientConfig = builder.jedisClientConfig; this.connectionPoolConfig = builder.connectionPoolConfig; this.weight = builder.weight; this.healthCheckEnabled = builder.healthCheckEnabled; this.healthCheckStrategySupplier = builder.healthCheckStrategySupplier; } /** * Returns the Redis endpoint (host and port) for this database. * @return the host and port information */ public Endpoint getEndpoint() { return endpoint; } /** * Creates a new Builder instance for configuring a DatabaseConfig. * @param endpoint the Redis endpoint (host and port) * @param clientConfig the Jedis client configuration * @return new Builder instance * @throws IllegalArgumentException if endpoint or clientConfig is null */ public static Builder builder(Endpoint endpoint, JedisClientConfig clientConfig) { return new Builder(endpoint, clientConfig); } /** * Returns the Jedis client configuration for this database. * @return the client configuration containing connection settings and authentication */ public JedisClientConfig getJedisClientConfig() { return jedisClientConfig; } /** * Returns the connection pool configuration for this database. * @return the connection pool configuration, may be null if not specified */ public GenericObjectPoolConfig getConnectionPoolConfig() { return connectionPoolConfig; } /** * Returns the weight value used for database selection priority. *

* Higher weight values indicate higher priority. During failover, databases are selected in * descending order of weight (highest weight first). *

* @return the weight value, default is 1.0f */ public float getWeight() { return weight; } /** * Returns the health check strategy supplier for this database. *

* The strategy supplier is used to create health check instances that monitor this database's * availability. Returns null if health checks are disabled. *

* @return the health check strategy supplier, or null if health checks are disabled * @see StrategySupplier * @see redis.clients.jedis.mcf.HealthCheckStrategy */ public StrategySupplier getHealthCheckStrategySupplier() { if (!healthCheckEnabled) { return null; } return healthCheckStrategySupplier; } /** * Returns whether health checks are enabled for this database. * @return true if health checks are enabled, false otherwise */ public boolean isHealthCheckEnabled() { return healthCheckEnabled; } /** * Builder class for creating DatabaseConfig instances with fluent configuration API. *

* The Builder provides a convenient way to configure database settings including connection * pooling, weight-based priority, and health check strategies. All configuration methods return * the builder instance for method chaining. *

*

* Default Values: *

*
    *
  • Weight: 1.0f (standard priority)
  • *
  • Health Check: {@link redis.clients.jedis.mcf.PingStrategy#DEFAULT}
  • *
  • Connection Pool: null (uses default pooling)
  • *
*/ public static class Builder { /** The Redis endpoint for this database configuration. */ private Endpoint endpoint; /** The Jedis client configuration. */ private JedisClientConfig jedisClientConfig; /** Optional connection pool configuration. */ private GenericObjectPoolConfig connectionPoolConfig; /** Weight for database selection priority. Default: 1.0f */ private float weight = 1.0f; /** Health check enabled. Default: true */ private boolean healthCheckEnabled = true; /** Health check strategy supplier. Default: PingStrategy.DEFAULT */ private StrategySupplier healthCheckStrategySupplier = PingStrategy.DEFAULT; /** * Constructs a new Builder with required endpoint and client configuration. * @param endpoint the Redis endpoint (host and port) * @param clientConfig the Jedis client configuration * @throws IllegalArgumentException if endpoint or clientConfig is null */ public Builder(Endpoint endpoint, JedisClientConfig clientConfig) { this.endpoint = endpoint; this.jedisClientConfig = clientConfig; } /** * Sets the connection pool configuration for this database. *

* Connection pooling helps manage connections efficiently and provides better performance * under load. If not specified, default pooling behavior will be used. *

* @param connectionPoolConfig the connection pool configuration * @return this builder instance for method chaining */ public Builder connectionPoolConfig( GenericObjectPoolConfig connectionPoolConfig) { this.connectionPoolConfig = connectionPoolConfig; return this; } /** * Sets the weight value for database selection priority. *

* Weight determines the priority order for database selection during failover. Databases with * higher weights are preferred over those with lower weights. The system will attempt to use * the highest-weight healthy database available. *

*

* Examples: *

*
    *
  • 1.0f: Standard priority (default)
  • *
  • 0.8f: Lower priority (secondary database)
  • *
  • 0.1f: Lowest priority (backup database)
  • *
* @param weight the weight value for priority-based selection * @return this builder instance for method chaining */ public Builder weight(float weight) { JedisAsserts.isTrue(weight > 0, "Database weight must be greater than 0"); this.weight = weight; return this; } /** * Sets a custom health check strategy supplier for this database. *

* The strategy supplier creates health check instances that monitor this database's * availability. Different databases can use different health check strategies based on their * specific requirements. *

* @param healthCheckStrategySupplier the health check strategy supplier * @return this builder instance for method chaining * @throws IllegalArgumentException if healthCheckStrategySupplier is null * @see StrategySupplier * @see redis.clients.jedis.mcf.HealthCheckStrategy */ public Builder healthCheckStrategySupplier(StrategySupplier healthCheckStrategySupplier) { if (healthCheckStrategySupplier == null) { throw new IllegalArgumentException("healthCheckStrategySupplier must not be null"); } this.healthCheckStrategySupplier = healthCheckStrategySupplier; return this; } /** * Sets a specific health check strategy instance for this database. *

* This is a convenience method that wraps the provided strategy in a supplier that always * returns the same instance. Use this when you have a pre-configured strategy instance. *

*

* Note: The same strategy instance will be reused, so ensure it's * thread-safe if multiple databases might use it. *

* @param healthCheckStrategy the health check strategy instance * @return this builder instance for method chaining * @throws IllegalArgumentException if healthCheckStrategy is null * @see #healthCheckStrategySupplier(StrategySupplier) */ public Builder healthCheckStrategy(HealthCheckStrategy healthCheckStrategy) { if (healthCheckStrategy == null) { throw new IllegalArgumentException("healthCheckStrategy must not be null"); } this.healthCheckStrategySupplier = (hostAndPort, jedisClientConfig) -> healthCheckStrategy; return this; } /** * Enables or disables health checks for this database. *

* When health checks are disabled (false), the database will not be proactively monitored for * availability. This means: *

*
    *
  • No background health check threads will be created
  • *
  • Failback to this database must be triggered manually
  • *
  • The database is assumed to be healthy unless circuit breaker opens
  • *
*

* When health checks are enabled (true) and no strategy supplier was previously set, the * default {@link redis.clients.jedis.mcf.PingStrategy#DEFAULT} will be used. *

* @param healthCheckEnabled true to enable health checks, false to disable * @return this builder instance for method chaining */ public Builder healthCheckEnabled(boolean healthCheckEnabled) { this.healthCheckEnabled = healthCheckEnabled; return this; } /** * Builds and returns a new DatabaseConfig instance with the configured settings. * @return a new DatabaseConfig instance */ public DatabaseConfig build() { return new DatabaseConfig(this); } } } /** * Builder class for creating MultiDbConfig instances with comprehensive configuration options. *

* The Builder provides a fluent API for configuring all aspects of multi-database failover * behavior, including retry logic, circuit breaker settings, and failback mechanisms. It uses * sensible defaults based on production best practices while allowing fine-tuning for specific * requirements. *

* @see MultiDbConfig * @see DatabaseConfig */ public static class Builder { /** Array of database configurations defining available Redis endpoints. */ private final List databaseConfigs = new ArrayList<>(); // ============ Retry Configuration Fields ============ /** Encapsulated retry configuration for command execution. */ private RetryConfig commandRetry = RetryConfig.builder().build(); // ============ Circuit Breaker Configuration Fields ============ /** Encapsulated circuit breaker configuration for failure detection. */ private CircuitBreakerConfig failureDetector = CircuitBreakerConfig.builder().build(); /** List of exception classes that trigger immediate fallback to next database. */ private List> fallbackExceptionList = FALLBACK_EXCEPTIONS_DEFAULT; // ============ Failover Configuration Fields ============ /** Whether to retry failed commands during failover. */ private boolean retryOnFailover = false; /** Whether automatic failback to higher-priority databases is supported. */ private boolean isFailbackSupported = true; /** Interval between checks for failback opportunities in milliseconds. */ private long failbackCheckInterval = FAILBACK_CHECK_INTERVAL_DEFAULT; /** Grace period to keep databases disabled after they become unhealthy in milliseconds. */ private long gracePeriod = GRACE_PERIOD_DEFAULT; /** Whether to forcefully terminate connections during failover. */ private boolean fastFailover = false; /** Maximum number of failover attempts. */ private int maxNumFailoverAttempts = MAX_NUM_FAILOVER_ATTEMPTS_DEFAULT; /** Delay in milliseconds between failover attempts. */ private int delayInBetweenFailoverAttempts = DELAY_IN_BETWEEN_FAILOVER_ATTEMPTS_DEFAULT; /** Initialization policy for determining when the multi-database connection is ready. */ private InitializationPolicy initializationPolicy = InitializationPolicy.BuiltIn.MAJORITY_AVAILABLE; /** * Constructs a new Builder with the specified database configurations. */ public Builder() { } /** * Constructs a new Builder with the specified database configurations. * @param databaseConfigs array of database configurations defining available Redis endpoints * @throws JedisValidationException if databaseConfigs is null or empty */ public Builder(DatabaseConfig[] databaseConfigs) { this(Arrays.asList(databaseConfigs)); } /** * Constructs a new Builder with the specified database configurations. * @param databaseConfigs list of database configurations defining available Redis endpoints * @throws JedisValidationException if databaseConfigs is null or empty */ public Builder(List databaseConfigs) { this.databaseConfigs.addAll(databaseConfigs); } /** * Adds a pre-configured database configuration. *

* This method allows adding a fully configured DatabaseConfig instance, providing maximum * flexibility for advanced configurations including custom health check strategies, connection * pool settings, etc. *

* @param databaseConfig the pre-configured database configuration * @return this builder */ public Builder database(DatabaseConfig databaseConfig) { this.databaseConfigs.add(databaseConfig); return this; } /** * Adds a database endpoint with custom client configuration. *

* This method allows specifying database-specific configuration such as authentication, SSL * settings, timeouts, etc. This configuration will override the default client configuration * for this specific database endpoint. *

* @param endpoint the Redis server endpoint * @param weight the weight for this endpoint (higher values = higher priority) * @param clientConfig the client configuration for this endpoint * @return this builder */ public Builder database(Endpoint endpoint, float weight, JedisClientConfig clientConfig) { DatabaseConfig databaseConfig = DatabaseConfig.builder(endpoint, clientConfig).weight(weight) .build(); this.databaseConfigs.add(databaseConfig); return this; } // ============ Retry Configuration Methods ============ /** * Sets the encapsulated retry configuration for command execution. *

* This provides a cleaner API for configuring retry behavior by using a dedicated * {@link RetryConfig} object. *

* @param commandRetry the retry configuration * @return this builder instance for method chaining * @see RetryConfig */ public Builder commandRetry(RetryConfig commandRetry) { this.commandRetry = commandRetry; return this; } // ============ Circuit Breaker Configuration Methods ============ /** * Sets the encapsulated circuit breaker configuration for failure detection. *

* This provides a cleaner API for configuring circuit breaker behavior by using a dedicated * {@link CircuitBreakerConfig} object. *

* @param failureDetector the circuit breaker configuration * @return this builder instance for method chaining * @see CircuitBreakerConfig */ public Builder failureDetector(CircuitBreakerConfig failureDetector) { this.failureDetector = failureDetector; return this; } /** * Sets the list of exception classes that trigger immediate fallback to the next available * database. *

* When these exceptions occur, the system will immediately attempt to failover to the next * available database without waiting for circuit breaker thresholds. This enables fast failover * for specific error conditions. *

*

* Default exceptions: *

*
    *
  • {@link CallNotPermittedException} - Circuit breaker is open
  • *
  • {@link redis.clients.jedis.mcf.ConnectionFailoverException} - Connection-level failover * required
  • *
* @param fallbackExceptionList list of exception classes that trigger immediate fallback * @return this builder instance for method chaining */ public Builder fallbackExceptionList(List> fallbackExceptionList) { this.fallbackExceptionList = fallbackExceptionList; return this; } // ============ Failover Configuration Methods ============ /** * Sets whether failed commands should be retried during the failover process. *

* When enabled, commands that fail during failover will be retried according to the configured * retry settings on the new database. When disabled, failed commands during failover will * immediately return the failure to the caller. *

*

* Trade-offs: *

*
    *
  • Enabled: Better resilience, potentially longer response times
  • *
  • Disabled: Faster failover, some operations may fail (default)
  • *
* @param retryOnFailover true to retry failed commands during failover, false otherwise * @return this builder instance for method chaining */ public Builder retryOnFailover(boolean retryOnFailover) { this.retryOnFailover = retryOnFailover; return this; } /** * Sets whether automatic failback to higher-priority databases is supported. *

* When enabled, the system will automatically monitor failed da using health checks and * failback to higher-priority (higher weight) databases when they recover. When disabled, * failback must be triggered manually. *

*

* Requirements for automatic failback: *

*
    *
  • Health checks must be enabled on database configurations
  • *
  • Grace period must elapse after database becomes unhealthy
  • *
  • Higher-priority database must pass health checks
  • *
* @param supported true to enable automatic failback, false for manual failback only * @return this builder instance for method chaining */ public Builder failbackSupported(boolean supported) { this.isFailbackSupported = supported; return this; } /** * Sets the interval between checks for failback opportunities to recovered databases. *

* This controls how frequently the system checks if a higher-priority database has recovered * and is available for failback. Lower values provide faster failback response but increase * monitoring overhead. *

*

* Typical Values: *

*
    *
  • 1-2 seconds: Fast failback for critical applications
  • *
  • 5 seconds: Balanced approach (default)
  • *
  • 10-30 seconds: Conservative monitoring for stable environments
  • *
* @param failbackCheckInterval interval in milliseconds between failback checks * @return this builder instance for method chaining */ public Builder failbackCheckInterval(long failbackCheckInterval) { this.failbackCheckInterval = failbackCheckInterval; return this; } /** * Sets the grace period to keep databases disabled after they become unhealthy. *

* After a database is marked as unhealthy, it remains disabled for this grace period before * being eligible for failback, even if health checks indicate recovery. This prevents rapid * oscillation between databases during intermittent failures. *

*

* Considerations: *

*
    *
  • Short periods (5-10s): Faster recovery, risk of oscillation
  • *
  • Medium periods (10-30s): Balanced stability (default: 10s)
  • *
  • Long periods (60s+): Maximum stability, slower recovery
  • *
* @param gracePeriod grace period in milliseconds * @return this builder instance for method chaining */ public Builder gracePeriod(long gracePeriod) { this.gracePeriod = gracePeriod; return this; } /** * Sets whether to forcefully terminate connections during failover for faster database * switching. *

* When enabled, existing connections to the failed database are immediately closed during * failover, potentially reducing failover time but may cause some in-flight operations to fail. * When disabled, connections are closed gracefully. *

*

* Trade-offs: *

*
    *
  • Enabled: Faster failover, potential operation failures
  • *
  • Disabled: Graceful failover, potentially slower (default)
  • *
* @param fastFailover true for fast failover, false for graceful failover * @return this builder instance for method chaining */ public Builder fastFailover(boolean fastFailover) { this.fastFailover = fastFailover; return this; } /** * Sets the maximum number of failover attempts. *

* This setting controls how many times the system will attempt to failover to a different * database before giving up. For example, if set to 3, the system will make 1 initial attempt * plus 2 failover attempts for a total of 3 attempts. *

*

* Default: {@value #MAX_NUM_FAILOVER_ATTEMPTS_DEFAULT} *

* @param maxNumFailoverAttempts maximum number of failover attempts * @return this builder instance for method chaining */ public Builder maxNumFailoverAttempts(int maxNumFailoverAttempts) { this.maxNumFailoverAttempts = maxNumFailoverAttempts; return this; } /** * Sets the delay in milliseconds between failover attempts. *

* This setting controls how long the system will wait before attempting to failover to a * different database. For example, if set to 1000, the system will wait 1 second before * attempting to failover to a different database. *

*

* Default: {@value #DELAY_IN_BETWEEN_FAILOVER_ATTEMPTS_DEFAULT} milliseconds *

* @param delayInBetweenFailoverAttempts delay in milliseconds between failover attempts * @return this builder instance for method chaining */ public Builder delayInBetweenFailoverAttempts(int delayInBetweenFailoverAttempts) { this.delayInBetweenFailoverAttempts = delayInBetweenFailoverAttempts; return this; } /** * Sets the initialization policy that determines when the multi-database connection is ready. *

* The policy is evaluated based on the completion status of database health checks, and the * decision to continue waiting, succeed, or fail is based on the number of available, pending, * and failed connections. *

*

* Built-in policies: *

*
    *
  • {@link InitializationPolicy.BuiltIn#ALL_AVAILABLE} - All databases need to be * available
  • *
  • {@link InitializationPolicy.BuiltIn#MAJORITY_AVAILABLE} - Majority of databases need to * be available (default)
  • *
  • {@link InitializationPolicy.BuiltIn#ONE_AVAILABLE} - At least one database needs to be * available
  • *
* @param initializationPolicy the initialization policy to use * @return this builder instance for method chaining */ public Builder initializationPolicy(InitializationPolicy initializationPolicy) { JedisAsserts.notNull(initializationPolicy, "initializationPolicy must not be null"); this.initializationPolicy = initializationPolicy; return this; } /** * Builds and returns a new MultiDbConfig instance with all configured settings. *

* This method creates the final configuration object by copying all builder settings to the * configuration instance. The builder can be reused after calling build() to create additional * configurations with different settings. *

* @return a new MultiDbConfig instance with the configured settings */ public MultiDbConfig build() { MultiDbConfig config = new MultiDbConfig(this.databaseConfigs.toArray(new DatabaseConfig[0])); // Copy retry configuration config.commandRetry = this.commandRetry; // Copy circuit breaker configuration config.failureDetector = this.failureDetector; // Copy fallback and failover configuration config.fallbackExceptionList = this.fallbackExceptionList; config.retryOnFailover = this.retryOnFailover; config.isFailbackSupported = this.isFailbackSupported; config.failbackCheckInterval = this.failbackCheckInterval; config.gracePeriod = this.gracePeriod; config.fastFailover = this.fastFailover; config.maxNumFailoverAttempts = this.maxNumFailoverAttempts; config.delayInBetweenFailoverAttempts = this.delayInBetweenFailoverAttempts; config.initializationPolicy = this.initializationPolicy; return config; } } } ================================================ FILE: src/main/java/redis/clients/jedis/MultiNodePipelineBase.java ================================================ package redis.clients.jedis; import java.util.Iterator; import java.util.LinkedHashMap; import java.util.LinkedList; import java.util.List; import java.util.Map; import java.util.Queue; import java.util.Set; import java.util.concurrent.CountDownLatch; import java.util.concurrent.Executor; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import redis.clients.jedis.exceptions.JedisConnectionException; import redis.clients.jedis.util.IOUtils; public abstract class MultiNodePipelineBase extends AbstractPipeline { private final Logger log = LoggerFactory.getLogger(getClass()); /** * The number of processes for {@code sync()}. If you have enough cores for client (and you have * more than 3 cluster nodes), you may increase this number of workers. * Suggestion: ≤ cluster nodes. */ public static volatile int MULTI_NODE_PIPELINE_SYNC_WORKERS = 3; private final Map>> pipelinedResponses; private final Map connections; private volatile boolean syncing = false; protected final CommandFlagsRegistry commandFlagsRegistry; public MultiNodePipelineBase(CommandObjects commandObjects) { this(commandObjects, StaticCommandFlagsRegistry.registry()); } protected MultiNodePipelineBase(CommandObjects commandObjects, CommandFlagsRegistry commandFlagsRegistry) { super(commandObjects); this.commandFlagsRegistry = commandFlagsRegistry; pipelinedResponses = new LinkedHashMap<>(); connections = new LinkedHashMap<>(); } protected abstract HostAndPort getNodeKey(CommandArguments args); protected abstract Connection getConnection(HostAndPort nodeKey); @Override protected final Response appendCommand(CommandObject commandObject) { // Validate that the command is supported in pipeline mode validatePipelineCommand(commandObject.getArguments()); HostAndPort nodeKey = getNodeKey(commandObject.getArguments()); Queue> queue; Connection connection; if (pipelinedResponses.containsKey(nodeKey)) { queue = pipelinedResponses.get(nodeKey); connection = connections.get(nodeKey); } else { Connection newOne = getConnection(nodeKey); connections.putIfAbsent(nodeKey, newOne); connection = connections.get(nodeKey); if (connection != newOne) { log.debug("Duplicate connection to {}, closing it.", nodeKey); IOUtils.closeQuietly(newOne); } pipelinedResponses.putIfAbsent(nodeKey, new LinkedList<>()); queue = pipelinedResponses.get(nodeKey); } connection.sendCommand(commandObject.getArguments()); Response response = new Response<>(commandObject.getBuilder()); queue.add(response); return response; } @Override public void close() { try { sync(); } finally { connections.values().forEach(IOUtils::closeQuietly); } } @Override public final void sync() { if (syncing) { return; } syncing = true; boolean multiNode = pipelinedResponses.size() > 1; Executor executor; ExecutorService executorService = null; if (multiNode) { executorService = Executors.newFixedThreadPool(MULTI_NODE_PIPELINE_SYNC_WORKERS); executor = executorService; } else { executor = Runnable::run; } CountDownLatch countDownLatch = multiNode ? new CountDownLatch(pipelinedResponses.size()) : null; Iterator>>> pipelinedResponsesIterator = pipelinedResponses.entrySet() .iterator(); while (pipelinedResponsesIterator.hasNext()) { Map.Entry>> entry = pipelinedResponsesIterator.next(); HostAndPort nodeKey = entry.getKey(); Queue> queue = entry.getValue(); Connection connection = connections.get(nodeKey); executor.execute(() -> { try { List unformatted = connection.getMany(queue.size()); for (Object o : unformatted) { queue.poll().set(o); } } catch (JedisConnectionException jce) { log.error("Error with connection to " + nodeKey, jce); // cleanup the connection // TODO these operations not thread-safe and when executed here, the iter may moved pipelinedResponsesIterator.remove(); connections.remove(nodeKey); IOUtils.closeQuietly(connection); } finally { if (multiNode) { countDownLatch.countDown(); } } }); } if (multiNode) { try { countDownLatch.await(); } catch (InterruptedException e) { log.error("Thread is interrupted during sync.", e); } executorService.shutdownNow(); } syncing = false; } /** * Validates that a command can be executed in a multi-node pipeline. *

* Commands with multi-node request policies (ALL_SHARDS, MULTI_SHARD, ALL_NODES, SPECIAL) * are rejected UNLESS they have keys that route to a single slot, in which case they can * be executed on that single node. *

* * @param args the command arguments * @throws UnsupportedOperationException if the command requires multi-node execution */ private void validatePipelineCommand(CommandArguments args) { CommandFlagsRegistry.RequestPolicy policy = commandFlagsRegistry.getRequestPolicy(args); // For multi-node policies, check if the command can be routed to a single slot switch (policy) { case ALL_SHARDS: case MULTI_SHARD: case ALL_NODES: case SPECIAL: // If the command has keys that route to a single slot, allow it Set slots = args.getKeyHashSlots(); if (slots.size() == 1) { // Command can be routed to a single slot - allow it return; } // Command cannot be routed to a single slot - reject it String policyName = policy.name(); throw new UnsupportedOperationException( "Command '" + args.getCommand() + "' with " + policyName + " request policy " + "cannot be executed in pipeline mode because it cannot be routed to a single slot. " + (slots.isEmpty() ? "This command has no keys to determine routing. " : "This command's keys map to multiple slots (" + slots.size() + " slots). ") + "Use non-pipeline cluster client for this command."); case DEFAULT: default: // DEFAULT policy and unknown policies - allow standard command execution // Routes to single node based on key hash break; } } @Deprecated public Response waitReplicas(int replicas, long timeout) { return appendCommand(commandObjects.waitReplicas(replicas, timeout)); } } ================================================ FILE: src/main/java/redis/clients/jedis/Pipeline.java ================================================ package redis.clients.jedis; import java.io.Closeable; import java.util.ArrayList; import java.util.LinkedList; import java.util.List; import java.util.Queue; import redis.clients.jedis.commands.DatabasePipelineCommands; import redis.clients.jedis.exceptions.JedisDataException; import redis.clients.jedis.params.*; import redis.clients.jedis.util.IOUtils; import redis.clients.jedis.util.KeyValue; public class Pipeline extends AbstractPipeline implements DatabasePipelineCommands, Closeable { private final Queue> pipelinedResponses = new LinkedList<>(); protected final Connection connection; private final boolean closeConnection; //private final CommandObjects commandObjects; public Pipeline(Jedis jedis) { this(jedis.getConnection(), false); } public Pipeline(Connection connection) { this(connection, false); } public Pipeline(Connection connection, boolean closeConnection) { this(connection, closeConnection, createCommandObjects(connection)); } private static CommandObjects createCommandObjects(Connection connection) { CommandObjects commandObjects = new CommandObjects(); RedisProtocol proto = connection.getRedisProtocol(); if (proto != null) commandObjects.setProtocol(proto); return commandObjects; } Pipeline(Connection connection, boolean closeConnection, CommandObjects commandObjects) { super(commandObjects); this.connection = connection; this.closeConnection = closeConnection; } @Override public final Response appendCommand(CommandObject commandObject) { connection.sendCommand(commandObject.getArguments()); Response response = new Response<>(commandObject.getBuilder()); pipelinedResponses.add(response); return response; } @Override public void close() { try { sync(); } finally { if (closeConnection) { IOUtils.closeQuietly(connection); } } } /** * Synchronize pipeline by reading all responses. This operation close the pipeline. In order to * get return values from pipelined commands, capture the different Response<?> of the * commands you execute. */ @Override public void sync() { if (!hasPipelinedResponse()) return; List unformatted = connection.getMany(pipelinedResponses.size()); for (Object rawReply : unformatted) { pipelinedResponses.poll().set(rawReply); } } /** * Synchronize pipeline by reading all responses. This operation close the pipeline. Whenever * possible try to avoid using this version and use Pipeline.sync() as it won't go through all the * responses and generate the right response type (usually it is a waste of time). * @return A list of all the responses in the order you executed them. */ public List syncAndReturnAll() { if (hasPipelinedResponse()) { List unformatted = connection.getMany(pipelinedResponses.size()); List formatted = new ArrayList<>(); for (Object rawReply : unformatted) { try { Response response = pipelinedResponses.poll(); response.set(rawReply); formatted.add(response.get()); } catch (JedisDataException e) { formatted.add(e); } } return formatted; } else { return java.util.Collections. emptyList(); } } public final boolean hasPipelinedResponse() { return pipelinedResponses.size() > 0; } public Response waitReplicas(int replicas, long timeout) { return appendCommand(commandObjects.waitReplicas(replicas, timeout)); } public Response> waitAOF(long numLocal, long numReplicas, long timeout) { return appendCommand(commandObjects.waitAOF(numLocal, numReplicas, timeout)); } public Response> time() { return appendCommand(new CommandObject<>(commandObjects.commandArguments(Protocol.Command.TIME), BuilderFactory.STRING_LIST)); } @Override public Response select(final int index) { return appendCommand(new CommandObject<>(commandObjects.commandArguments(Protocol.Command.SELECT).add(index), BuilderFactory.STRING)); } @Override public Response dbSize() { return appendCommand(new CommandObject<>(commandObjects.commandArguments(Protocol.Command.DBSIZE), BuilderFactory.LONG)); } @Override public Response swapDB(final int index1, final int index2) { return appendCommand(new CommandObject<>(commandObjects.commandArguments(Protocol.Command.SWAPDB) .add(index1).add(index2), BuilderFactory.STRING)); } @Override public Response move(String key, int dbIndex) { return appendCommand(new CommandObject<>(commandObjects.commandArguments(Protocol.Command.MOVE) .key(key).add(dbIndex), BuilderFactory.LONG)); } @Override public Response move(final byte[] key, final int dbIndex) { return appendCommand(new CommandObject<>(commandObjects.commandArguments(Protocol.Command.MOVE) .key(key).add(dbIndex), BuilderFactory.LONG)); } @Override public Response copy(String srcKey, String dstKey, int db, boolean replace) { return appendCommand(commandObjects.copy(srcKey, dstKey, db, replace)); } @Override public Response copy(byte[] srcKey, byte[] dstKey, int db, boolean replace) { return appendCommand(commandObjects.copy(srcKey, dstKey, db, replace)); } @Override public Response migrate(String host, int port, byte[] key, int destinationDB, int timeout) { return appendCommand(commandObjects.migrate(host, port, key, destinationDB, timeout)); } @Override public Response migrate(String host, int port, String key, int destinationDB, int timeout) { return appendCommand(commandObjects.migrate(host, port, key, destinationDB, timeout)); } @Override public Response migrate(String host, int port, int destinationDB, int timeout, MigrateParams params, byte[]... keys) { return appendCommand(commandObjects.migrate(host, port, destinationDB, timeout, params, keys)); } @Override public Response migrate(String host, int port, int destinationDB, int timeout, MigrateParams params, String... keys) { return appendCommand(commandObjects.migrate(host, port, destinationDB, timeout, params, keys)); } } ================================================ FILE: src/main/java/redis/clients/jedis/PipeliningBase.java ================================================ package redis.clients.jedis; import java.util.List; import java.util.Map; import java.util.Set; import org.json.JSONArray; import redis.clients.jedis.annots.Experimental; import redis.clients.jedis.args.*; import redis.clients.jedis.bloom.*; import redis.clients.jedis.commands.PipelineBinaryCommands; import redis.clients.jedis.commands.PipelineCommands; import redis.clients.jedis.commands.ProtocolCommand; import redis.clients.jedis.commands.RedisModulePipelineCommands; import redis.clients.jedis.json.JsonSetParams; import redis.clients.jedis.json.Path; import redis.clients.jedis.json.Path2; import redis.clients.jedis.json.JsonObjectMapper; import redis.clients.jedis.params.*; import redis.clients.jedis.resps.*; import redis.clients.jedis.search.*; import redis.clients.jedis.search.aggr.AggregationBuilder; import redis.clients.jedis.search.aggr.AggregationResult; import redis.clients.jedis.search.hybrid.FTHybridParams; import redis.clients.jedis.search.hybrid.HybridResult; import redis.clients.jedis.search.schemafields.SchemaField; import redis.clients.jedis.timeseries.*; import redis.clients.jedis.util.CompareCondition; import redis.clients.jedis.util.KeyValue; public abstract class PipeliningBase implements PipelineCommands, PipelineBinaryCommands, RedisModulePipelineCommands { protected final CommandObjects commandObjects; protected PipeliningBase(CommandObjects commandObjects) { this.commandObjects = commandObjects; } protected abstract Response appendCommand(CommandObject commandObject); @Override public Response exists(String key) { return appendCommand(commandObjects.exists(key)); } @Override public Response exists(String... keys) { return appendCommand(commandObjects.exists(keys)); } @Override public Response persist(String key) { return appendCommand(commandObjects.persist(key)); } @Override public Response type(String key) { return appendCommand(commandObjects.type(key)); } @Override public Response dump(String key) { return appendCommand(commandObjects.dump(key)); } @Override public Response restore(String key, long ttl, byte[] serializedValue) { return appendCommand(commandObjects.restore(key, ttl, serializedValue)); } @Override public Response restore(String key, long ttl, byte[] serializedValue, RestoreParams params) { return appendCommand(commandObjects.restore(key, ttl, serializedValue, params)); } @Override public Response expire(String key, long seconds) { return appendCommand(commandObjects.expire(key, seconds)); } @Override public Response expire(String key, long seconds, ExpiryOption expiryOption) { return appendCommand(commandObjects.expire(key, seconds, expiryOption)); } @Override public Response pexpire(String key, long milliseconds) { return appendCommand(commandObjects.pexpire(key, milliseconds)); } @Override public Response pexpire(String key, long milliseconds, ExpiryOption expiryOption) { return appendCommand(commandObjects.pexpire(key, milliseconds, expiryOption)); } @Override public Response expireTime(String key) { return appendCommand(commandObjects.expireTime(key)); } @Override public Response pexpireTime(String key) { return appendCommand(commandObjects.pexpireTime(key)); } @Override public Response expireAt(String key, long unixTime) { return appendCommand(commandObjects.expireAt(key, unixTime)); } @Override public Response expireAt(String key, long unixTime, ExpiryOption expiryOption) { return appendCommand(commandObjects.expireAt(key, unixTime, expiryOption)); } @Override public Response pexpireAt(String key, long millisecondsTimestamp) { return appendCommand(commandObjects.pexpireAt(key, millisecondsTimestamp)); } @Override public Response pexpireAt(String key, long millisecondsTimestamp, ExpiryOption expiryOption) { return appendCommand(commandObjects.pexpireAt(key, millisecondsTimestamp, expiryOption)); } @Override public Response ttl(String key) { return appendCommand(commandObjects.ttl(key)); } @Override public Response pttl(String key) { return appendCommand(commandObjects.pttl(key)); } @Override public Response touch(String key) { return appendCommand(commandObjects.touch(key)); } @Override public Response touch(String... keys) { return appendCommand(commandObjects.touch(keys)); } @Override public Response> sort(String key) { return appendCommand(commandObjects.sort(key)); } @Override public Response sort(String key, String dstKey) { return appendCommand(commandObjects.sort(key, dstKey)); } @Override public Response> sort(String key, SortingParams sortingParams) { return appendCommand(commandObjects.sort(key, sortingParams)); } @Override public Response sort(String key, SortingParams sortingParams, String dstKey) { return appendCommand(commandObjects.sort(key, sortingParams, dstKey)); } @Override public Response> sortReadonly(String key, SortingParams sortingParams) { return appendCommand(commandObjects.sortReadonly(key, sortingParams)); } @Override public Response del(String key) { return appendCommand(commandObjects.del(key)); } @Override public Response del(String... keys) { return appendCommand(commandObjects.del(keys)); } @Override public Response unlink(String key) { return appendCommand(commandObjects.unlink(key)); } @Override public Response unlink(String... keys) { return appendCommand(commandObjects.unlink(keys)); } @Override public Response copy(String srcKey, String dstKey, boolean replace) { return appendCommand(commandObjects.copy(srcKey, dstKey, replace)); } @Override public Response rename(String oldkey, String newkey) { return appendCommand(commandObjects.rename(oldkey, newkey)); } @Override public Response renamenx(String oldkey, String newkey) { return appendCommand(commandObjects.renamenx(oldkey, newkey)); } @Override public Response memoryUsage(String key) { return appendCommand(commandObjects.memoryUsage(key)); } @Override public Response memoryUsage(String key, int samples) { return appendCommand(commandObjects.memoryUsage(key, samples)); } @Override public Response objectRefcount(String key) { return appendCommand(commandObjects.objectRefcount(key)); } @Override public Response objectEncoding(String key) { return appendCommand(commandObjects.objectEncoding(key)); } @Override public Response objectIdletime(String key) { return appendCommand(commandObjects.objectIdletime(key)); } @Override public Response objectFreq(String key) { return appendCommand(commandObjects.objectFreq(key)); } @Override public Response migrate(String host, int port, String key, int timeout) { return appendCommand(commandObjects.migrate(host, port, key, timeout)); } @Override public Response migrate(String host, int port, int timeout, MigrateParams params, String... keys) { return appendCommand(commandObjects.migrate(host, port, timeout, params, keys)); } @Override public Response> keys(String pattern) { return appendCommand(commandObjects.keys(pattern)); } @Override public Response> scan(String cursor) { return appendCommand(commandObjects.scan(cursor)); } @Override public Response> scan(String cursor, ScanParams params) { return appendCommand(commandObjects.scan(cursor, params)); } @Override public Response> scan(String cursor, ScanParams params, String type) { return appendCommand(commandObjects.scan(cursor, params, type)); } @Override public Response randomKey() { return appendCommand(commandObjects.randomKey()); } @Override public Response get(String key) { return appendCommand(commandObjects.get(key)); } @Override public Response setGet(String key, String value) { return appendCommand(commandObjects.setGet(key, value)); } @Override public Response setGet(String key, String value, SetParams params) { return appendCommand(commandObjects.setGet(key, value, params)); } @Override public Response getDel(String key) { return appendCommand(commandObjects.getDel(key)); } @Override public Response getEx(String key, GetExParams params) { return appendCommand(commandObjects.getEx(key, params)); } @Override public Response setbit(String key, long offset, boolean value) { return appendCommand(commandObjects.setbit(key, offset, value)); } @Override public Response getbit(String key, long offset) { return appendCommand(commandObjects.getbit(key, offset)); } @Override public Response setrange(String key, long offset, String value) { return appendCommand(commandObjects.setrange(key, offset, value)); } @Override public Response getrange(String key, long startOffset, long endOffset) { return appendCommand(commandObjects.getrange(key, startOffset, endOffset)); } /** * @deprecated Use {@link PipeliningBase#setGet(java.lang.String, java.lang.String)}. */ @Deprecated @Override public Response getSet(String key, String value) { return appendCommand(commandObjects.getSet(key, value)); } /** * @deprecated Use {@link PipeliningBase#set(String, String, redis.clients.jedis.params.SetParams)} with {@link redis.clients.jedis.params.SetParams#nx()}. * Deprecated in Jedis 7.3.0. Mirrors Redis deprecation since 2.6.12. */ @Deprecated @Override public Response setnx(String key, String value) { return appendCommand(commandObjects.setnx(key, value)); } /** * @deprecated Use {@link PipeliningBase#set(String, String, redis.clients.jedis.params.SetParams)} with {@link redis.clients.jedis.params.SetParams#ex(long)}. * Deprecated in Jedis 7.3.0. Mirrors Redis deprecation since 2.6.12. */ @Deprecated @Override public Response setex(String key, long seconds, String value) { return appendCommand(commandObjects.setex(key, seconds, value)); } /** * @deprecated Use {@link PipeliningBase#set(String, String, redis.clients.jedis.params.SetParams)} with {@link redis.clients.jedis.params.SetParams#px(long)}. * Deprecated in Jedis 7.3.0. Mirrors Redis deprecation since 2.6.12. */ @Deprecated @Override public Response psetex(String key, long milliseconds, String value) { return appendCommand(commandObjects.psetex(key, milliseconds, value)); } @Override public Response> mget(String... keys) { return appendCommand(commandObjects.mget(keys)); } @Override public Response mset(String... keysvalues) { return appendCommand(commandObjects.mset(keysvalues)); } @Override public Response msetnx(String... keysvalues) { return appendCommand(commandObjects.msetnx(keysvalues)); } @Override public Response msetex(MSetExParams params, String... keysvalues) { return appendCommand(commandObjects.msetex(params, keysvalues)); } @Override public Response incr(String key) { return appendCommand(commandObjects.incr(key)); } @Override public Response incrBy(String key, long increment) { return appendCommand(commandObjects.incrBy(key, increment)); } @Override public Response incrByFloat(String key, double increment) { return appendCommand(commandObjects.incrByFloat(key, increment)); } @Override public Response decr(String key) { return appendCommand(commandObjects.decr(key)); } @Override public Response decrBy(String key, long decrement) { return appendCommand(commandObjects.decrBy(key, decrement)); } @Override public Response append(String key, String value) { return appendCommand(commandObjects.append(key, value)); } /** * @deprecated Use {@link PipeliningBase#getrange(String, long, long)} instead. * Deprecated in Jedis 7.3.0. Mirrors Redis deprecation since 2.0.0. */ @Deprecated @Override public Response substr(String key, int start, int end) { return appendCommand(commandObjects.substr(key, start, end)); } @Override public Response strlen(String key) { return appendCommand(commandObjects.strlen(key)); } @Override public Response bitcount(String key) { return appendCommand(commandObjects.bitcount(key)); } @Override public Response bitcount(String key, long start, long end) { return appendCommand(commandObjects.bitcount(key, start, end)); } @Override public Response bitcount(String key, long start, long end, BitCountOption option) { return appendCommand(commandObjects.bitcount(key, start, end, option)); } @Override public Response bitpos(String key, boolean value) { return appendCommand(commandObjects.bitpos(key, value)); } @Override public Response bitpos(String key, boolean value, BitPosParams params) { return appendCommand(commandObjects.bitpos(key, value, params)); } @Override public Response> bitfield(String key, String... arguments) { return appendCommand(commandObjects.bitfield(key, arguments)); } @Override public Response> bitfieldReadonly(String key, String... arguments) { return appendCommand(commandObjects.bitfieldReadonly(key, arguments)); } @Override public Response bitop(BitOP op, String destKey, String... srcKeys) { return appendCommand(commandObjects.bitop(op, destKey, srcKeys)); } @Override public Response lcs(String keyA, String keyB, LCSParams params) { return appendCommand(commandObjects.lcs(keyA, keyB, params)); } @Override public Response set(String key, String value) { return appendCommand(commandObjects.set(key, value)); } @Override public Response set(String key, String value, SetParams params) { return appendCommand(commandObjects.set(key, value, params)); } @Override public Response rpush(String key, String... string) { return appendCommand(commandObjects.rpush(key, string)); } @Override public Response lpush(String key, String... string) { return appendCommand(commandObjects.lpush(key, string)); } @Override public Response llen(String key) { return appendCommand(commandObjects.llen(key)); } @Override public Response> lrange(String key, long start, long stop) { return appendCommand(commandObjects.lrange(key, start, stop)); } @Override public Response ltrim(String key, long start, long stop) { return appendCommand(commandObjects.ltrim(key, start, stop)); } @Override public Response lindex(String key, long index) { return appendCommand(commandObjects.lindex(key, index)); } @Override public Response lset(String key, long index, String value) { return appendCommand(commandObjects.lset(key, index, value)); } @Override public Response lrem(String key, long count, String value) { return appendCommand(commandObjects.lrem(key, count, value)); } @Override public Response lpop(String key) { return appendCommand(commandObjects.lpop(key)); } @Override public Response> lpop(String key, int count) { return appendCommand(commandObjects.lpop(key, count)); } @Override public Response lpos(String key, String element) { return appendCommand(commandObjects.lpos(key, element)); } @Override public Response lpos(String key, String element, LPosParams params) { return appendCommand(commandObjects.lpos(key, element, params)); } @Override public Response> lpos(String key, String element, LPosParams params, long count) { return appendCommand(commandObjects.lpos(key, element, params, count)); } @Override public Response rpop(String key) { return appendCommand(commandObjects.rpop(key)); } @Override public Response> rpop(String key, int count) { return appendCommand(commandObjects.rpop(key, count)); } @Override public Response linsert(String key, ListPosition where, String pivot, String value) { return appendCommand(commandObjects.linsert(key, where, pivot, value)); } @Override public Response lpushx(String key, String... strings) { return appendCommand(commandObjects.lpushx(key, strings)); } @Override public Response rpushx(String key, String... strings) { return appendCommand(commandObjects.rpushx(key, strings)); } @Override public Response> blpop(int timeout, String key) { return appendCommand(commandObjects.blpop(timeout, key)); } @Override public Response> blpop(double timeout, String key) { return appendCommand(commandObjects.blpop(timeout, key)); } @Override public Response> brpop(int timeout, String key) { return appendCommand(commandObjects.brpop(timeout, key)); } @Override public Response> brpop(double timeout, String key) { return appendCommand(commandObjects.brpop(timeout, key)); } @Override public Response> blpop(int timeout, String... keys) { return appendCommand(commandObjects.blpop(timeout, keys)); } @Override public Response> blpop(double timeout, String... keys) { return appendCommand(commandObjects.blpop(timeout, keys)); } @Override public Response> brpop(int timeout, String... keys) { return appendCommand(commandObjects.brpop(timeout, keys)); } @Override public Response> brpop(double timeout, String... keys) { return appendCommand(commandObjects.brpop(timeout, keys)); } /** * @deprecated Use {@link PipeliningBase#lmove(String, String, ListDirection, ListDirection)} with * {@link ListDirection#RIGHT} and {@link ListDirection#LEFT}. * Deprecated in Jedis 7.3.0. Mirrors Redis deprecation since 6.2.0. */ @Deprecated @Override public Response rpoplpush(String srcKey, String dstKey) { return appendCommand(commandObjects.rpoplpush(srcKey, dstKey)); } /** * @deprecated Use {@link PipeliningBase#blmove(String, String, ListDirection, ListDirection, double)} with * {@link ListDirection#RIGHT} and {@link ListDirection#LEFT}. * Deprecated in Jedis 7.3.0. Mirrors Redis deprecation since 6.2.0. */ @Deprecated @Override public Response brpoplpush(String source, String destination, int timeout) { return appendCommand(commandObjects.brpoplpush(source, destination, timeout)); } @Override public Response lmove(String srcKey, String dstKey, ListDirection from, ListDirection to) { return appendCommand(commandObjects.lmove(srcKey, dstKey, from, to)); } @Override public Response blmove(String srcKey, String dstKey, ListDirection from, ListDirection to, double timeout) { return appendCommand(commandObjects.blmove(srcKey, dstKey, from, to, timeout)); } @Override public Response>> lmpop(ListDirection direction, String... keys) { return appendCommand(commandObjects.lmpop(direction, keys)); } @Override public Response>> lmpop(ListDirection direction, int count, String... keys) { return appendCommand(commandObjects.lmpop(direction, count, keys)); } @Override public Response>> blmpop(double timeout, ListDirection direction, String... keys) { return appendCommand(commandObjects.blmpop(timeout, direction, keys)); } @Override public Response>> blmpop(double timeout, ListDirection direction, int count, String... keys) { return appendCommand(commandObjects.blmpop(timeout, direction, count, keys)); } @Override public Response hset(String key, String field, String value) { return appendCommand(commandObjects.hset(key, field, value)); } @Override public Response hset(String key, Map hash) { return appendCommand(commandObjects.hset(key, hash)); } /** * Sets the specified field in the hash stored at key to the specified value with additional parameters, * and optionally set their expiration. Use `HSetExParams` object to specify expiration parameters. * This command can overwrite any existing fields in the hash. * If key does not exist, a new key holding a hash is created. * * @param key the key of the hash * @param params additional parameters for the HSETEX command * @param field the field in the hash to set * @param value the value to set in the specified field * @return 0 if no fields were set, 1 if all the fields were set * * @see HSetExParams */ @Override public Response hsetex(String key, HSetExParams params, String field, String value) { return appendCommand(commandObjects.hsetex(key, params, field, value)); } /** * Sets the specified fields in the hash stored at key to the specified values with additional parameters, * and optionally set their expiration. Use `HSetExParams` object to specify expiration parameters. * This command can overwrite any existing fields in the hash. * If key does not exist, a new key holding a hash is created. * * @param key the key of the hash * @param params the parameters for the HSetEx command * @param hash the map containing field-value pairs to set in the hash * @return 0 if no fields were set, 1 if all the fields were set * * @see HSetExParams */ @Override public Response hsetex(String key, HSetExParams params, Map hash) { return appendCommand(commandObjects.hsetex(key, params, hash)); } @Override public Response hget(String key, String field) { return appendCommand(commandObjects.hget(key, field)); } /** * Retrieves the values associated with the specified fields in a hash stored at the given key * and optionally sets their expiration. Use `HGetExParams` object to specify expiration parameters. * * @param key the key of the hash * @param params additional parameters for the HGETEX command * @param fields the fields whose values are to be retrieved * @return a list of the value associated with each field or nil if the field doesn’t exist. * * @see HGetExParams */ @Override public Response> hgetex(String key, HGetExParams params, String... fields) { return appendCommand(commandObjects.hgetex(key, params, fields)); } /** * Retrieves the values associated with the specified fields in the hash stored at the given key * and then deletes those fields from the hash. * * @param key the key of the hash * @param fields the fields whose values are to be retrieved and then deleted * @return a list of values associated with the specified fields before they were deleted */ @Override public Response> hgetdel(String key, String... fields) { return appendCommand(commandObjects.hgetdel(key, fields)); } @Override public Response hsetnx(String key, String field, String value) { return appendCommand(commandObjects.hsetnx(key, field, value)); } /** * @deprecated Use {@link PipeliningBase#hset(String, Map)} instead. * Deprecated in Jedis 7.3.0. Mirrors Redis deprecation since 4.0.0. */ @Deprecated @Override public Response hmset(String key, Map hash) { return appendCommand(commandObjects.hmset(key, hash)); } @Override public Response> hmget(String key, String... fields) { return appendCommand(commandObjects.hmget(key, fields)); } @Override public Response hincrBy(String key, String field, long value) { return appendCommand(commandObjects.hincrBy(key, field, value)); } @Override public Response hincrByFloat(String key, String field, double value) { return appendCommand(commandObjects.hincrByFloat(key, field, value)); } @Override public Response hexists(String key, String field) { return appendCommand(commandObjects.hexists(key, field)); } @Override public Response hdel(String key, String... field) { return appendCommand(commandObjects.hdel(key, field)); } @Override public Response hlen(String key) { return appendCommand(commandObjects.hlen(key)); } @Override public Response> hkeys(String key) { return appendCommand(commandObjects.hkeys(key)); } @Override public Response> hvals(String key) { return appendCommand(commandObjects.hvals(key)); } @Override public Response> hgetAll(String key) { return appendCommand(commandObjects.hgetAll(key)); } @Override public Response hrandfield(String key) { return appendCommand(commandObjects.hrandfield(key)); } @Override public Response> hrandfield(String key, long count) { return appendCommand(commandObjects.hrandfield(key, count)); } @Override public Response>> hrandfieldWithValues(String key, long count) { return appendCommand(commandObjects.hrandfieldWithValues(key, count)); } @Override public Response>> hscan(String key, String cursor, ScanParams params) { return appendCommand(commandObjects.hscan(key, cursor, params)); } @Override public Response> hscanNoValues(String key, String cursor, ScanParams params) { return appendCommand(commandObjects.hscanNoValues(key, cursor, params)); } @Override public Response hstrlen(String key, String field) { return appendCommand(commandObjects.hstrlen(key, field)); } @Override public Response> hexpire(String key, long seconds, String... fields) { return appendCommand(commandObjects.hexpire(key, seconds, fields)); } @Override public Response> hexpire(String key, long seconds, ExpiryOption condition, String... fields) { return appendCommand(commandObjects.hexpire(key, seconds, condition, fields)); } @Override public Response> hpexpire(String key, long milliseconds, String... fields) { return appendCommand(commandObjects.hpexpire(key, milliseconds, fields)); } @Override public Response> hpexpire(String key, long milliseconds, ExpiryOption condition, String... fields) { return appendCommand(commandObjects.hpexpire(key, milliseconds, condition, fields)); } @Override public Response> hexpireAt(String key, long unixTimeSeconds, String... fields) { return appendCommand(commandObjects.hexpireAt(key, unixTimeSeconds, fields)); } @Override public Response> hexpireAt(String key, long unixTimeSeconds, ExpiryOption condition, String... fields) { return appendCommand(commandObjects.hexpireAt(key, unixTimeSeconds, condition, fields)); } @Override public Response> hpexpireAt(String key, long unixTimeMillis, String... fields) { return appendCommand(commandObjects.hpexpireAt(key, unixTimeMillis, fields)); } @Override public Response> hpexpireAt(String key, long unixTimeMillis, ExpiryOption condition, String... fields) { return appendCommand(commandObjects.hpexpireAt(key, unixTimeMillis, condition, fields)); } @Override public Response> hexpireTime(String key, String... fields) { return appendCommand(commandObjects.hexpireTime(key, fields)); } @Override public Response> hpexpireTime(String key, String... fields) { return appendCommand(commandObjects.hpexpireTime(key, fields)); } @Override public Response> httl(String key, String... fields) { return appendCommand(commandObjects.httl(key, fields)); } @Override public Response> hpttl(String key, String... fields) { return appendCommand(commandObjects.hpttl(key, fields)); } @Override public Response> hpersist(String key, String... fields) { return appendCommand(commandObjects.hpersist(key, fields)); } @Override public Response sadd(String key, String... members) { return appendCommand(commandObjects.sadd(key, members)); } @Override public Response> smembers(String key) { return appendCommand(commandObjects.smembers(key)); } @Override public Response srem(String key, String... members) { return appendCommand(commandObjects.srem(key, members)); } @Override public Response spop(String key) { return appendCommand(commandObjects.spop(key)); } @Override public Response> spop(String key, long count) { return appendCommand(commandObjects.spop(key, count)); } @Override public Response scard(String key) { return appendCommand(commandObjects.scard(key)); } @Override public Response sismember(String key, String member) { return appendCommand(commandObjects.sismember(key, member)); } @Override public Response> smismember(String key, String... members) { return appendCommand(commandObjects.smismember(key, members)); } @Override public Response srandmember(String key) { return appendCommand(commandObjects.srandmember(key)); } @Override public Response> srandmember(String key, int count) { return appendCommand(commandObjects.srandmember(key, count)); } @Override public Response> sscan(String key, String cursor, ScanParams params) { return appendCommand(commandObjects.sscan(key, cursor, params)); } @Override public Response> sdiff(String... keys) { return appendCommand(commandObjects.sdiff(keys)); } @Override public Response sdiffstore(String dstKey, String... keys) { return appendCommand(commandObjects.sdiffstore(dstKey, keys)); } @Override public Response> sinter(String... keys) { return appendCommand(commandObjects.sinter(keys)); } @Override public Response sinterstore(String dstKey, String... keys) { return appendCommand(commandObjects.sinterstore(dstKey, keys)); } @Override public Response sintercard(String... keys) { return appendCommand(commandObjects.sintercard(keys)); } @Override public Response sintercard(int limit, String... keys) { return appendCommand(commandObjects.sintercard(limit, keys)); } @Override public Response> sunion(String... keys) { return appendCommand(commandObjects.sunion(keys)); } @Override public Response sunionstore(String dstKey, String... keys) { return appendCommand(commandObjects.sunionstore(dstKey, keys)); } @Override public Response smove(String srcKey, String dstKey, String member) { return appendCommand(commandObjects.smove(srcKey, dstKey, member)); } @Override public Response zadd(String key, double score, String member) { return appendCommand(commandObjects.zadd(key, score, member)); } @Override public Response zadd(String key, double score, String member, ZAddParams params) { return appendCommand(commandObjects.zadd(key, score, member, params)); } @Override public Response zadd(String key, Map scoreMembers) { return appendCommand(commandObjects.zadd(key, scoreMembers)); } @Override public Response zadd(String key, Map scoreMembers, ZAddParams params) { return appendCommand(commandObjects.zadd(key, scoreMembers, params)); } @Override public Response zaddIncr(String key, double score, String member, ZAddParams params) { return appendCommand(commandObjects.zaddIncr(key, score, member, params)); } @Override public Response zrem(String key, String... members) { return appendCommand(commandObjects.zrem(key, members)); } @Override public Response zincrby(String key, double increment, String member) { return appendCommand(commandObjects.zincrby(key, increment, member)); } @Override public Response zincrby(String key, double increment, String member, ZIncrByParams params) { return appendCommand(commandObjects.zincrby(key, increment, member, params)); } @Override public Response zrank(String key, String member) { return appendCommand(commandObjects.zrank(key, member)); } @Override public Response zrevrank(String key, String member) { return appendCommand(commandObjects.zrevrank(key, member)); } @Override public Response> zrankWithScore(String key, String member) { return appendCommand(commandObjects.zrankWithScore(key, member)); } @Override public Response> zrevrankWithScore(String key, String member) { return appendCommand(commandObjects.zrevrankWithScore(key, member)); } @Override public Response> zrange(String key, long start, long stop) { return appendCommand(commandObjects.zrange(key, start, stop)); } @Override public Response> zrevrange(String key, long start, long stop) { return appendCommand(commandObjects.zrevrange(key, start, stop)); } @Override public Response> zrangeWithScores(String key, long start, long stop) { return appendCommand(commandObjects.zrangeWithScores(key, start, stop)); } @Override public Response> zrevrangeWithScores(String key, long start, long stop) { return appendCommand(commandObjects.zrevrangeWithScores(key, start, stop)); } @Override public Response zrandmember(String key) { return appendCommand(commandObjects.zrandmember(key)); } @Override public Response> zrandmember(String key, long count) { return appendCommand(commandObjects.zrandmember(key, count)); } @Override public Response> zrandmemberWithScores(String key, long count) { return appendCommand(commandObjects.zrandmemberWithScores(key, count)); } @Override public Response zcard(String key) { return appendCommand(commandObjects.zcard(key)); } @Override public Response zscore(String key, String member) { return appendCommand(commandObjects.zscore(key, member)); } @Override public Response> zmscore(String key, String... members) { return appendCommand(commandObjects.zmscore(key, members)); } @Override public Response zpopmax(String key) { return appendCommand(commandObjects.zpopmax(key)); } @Override public Response> zpopmax(String key, int count) { return appendCommand(commandObjects.zpopmax(key, count)); } @Override public Response zpopmin(String key) { return appendCommand(commandObjects.zpopmin(key)); } @Override public Response> zpopmin(String key, int count) { return appendCommand(commandObjects.zpopmin(key, count)); } @Override public Response zcount(String key, double min, double max) { return appendCommand(commandObjects.zcount(key, min, max)); } @Override public Response zcount(String key, String min, String max) { return appendCommand(commandObjects.zcount(key, min, max)); } @Override public Response> zrangeByScore(String key, double min, double max) { return appendCommand(commandObjects.zrangeByScore(key, min, max)); } @Override public Response> zrangeByScore(String key, String min, String max) { return appendCommand(commandObjects.zrangeByScore(key, min, max)); } @Override public Response> zrevrangeByScore(String key, double max, double min) { return appendCommand(commandObjects.zrevrangeByScore(key, max, min)); } @Override public Response> zrangeByScore(String key, double min, double max, int offset, int count) { return appendCommand(commandObjects.zrangeByScore(key, min, max, offset, count)); } @Override public Response> zrevrangeByScore(String key, String max, String min) { return appendCommand(commandObjects.zrevrangeByScore(key, max, min)); } @Override public Response> zrangeByScore(String key, String min, String max, int offset, int count) { return appendCommand(commandObjects.zrangeByScore(key, min, max, offset, count)); } @Override public Response> zrevrangeByScore(String key, double max, double min, int offset, int count) { return appendCommand(commandObjects.zrevrangeByScore(key, max, min, offset, count)); } @Override public Response> zrangeByScoreWithScores(String key, double min, double max) { return appendCommand(commandObjects.zrangeByScoreWithScores(key, min, max)); } @Override public Response> zrevrangeByScoreWithScores(String key, double max, double min) { return appendCommand(commandObjects.zrevrangeByScoreWithScores(key, max, min)); } @Override public Response> zrangeByScoreWithScores(String key, double min, double max, int offset, int count) { return appendCommand(commandObjects.zrangeByScoreWithScores(key, min, max, offset, count)); } @Override public Response> zrevrangeByScore(String key, String max, String min, int offset, int count) { return appendCommand(commandObjects.zrevrangeByScore(key, max, min, offset, count)); } @Override public Response> zrangeByScoreWithScores(String key, String min, String max) { return appendCommand(commandObjects.zrangeByScoreWithScores(key, min, max)); } @Override public Response> zrevrangeByScoreWithScores(String key, String max, String min) { return appendCommand(commandObjects.zrevrangeByScoreWithScores(key, max, min)); } @Override public Response> zrangeByScoreWithScores(String key, String min, String max, int offset, int count) { return appendCommand(commandObjects.zrangeByScoreWithScores(key, min, max, offset, count)); } @Override public Response> zrevrangeByScoreWithScores(String key, double max, double min, int offset, int count) { return appendCommand(commandObjects.zrevrangeByScoreWithScores(key, max, min, offset, count)); } @Override public Response> zrevrangeByScoreWithScores(String key, String max, String min, int offset, int count) { return appendCommand(commandObjects.zrevrangeByScoreWithScores(key, max, min, offset, count)); } @Override public Response> zrange(String key, ZRangeParams zRangeParams) { return appendCommand(commandObjects.zrange(key, zRangeParams)); } @Override public Response> zrangeWithScores(String key, ZRangeParams zRangeParams) { return appendCommand(commandObjects.zrangeWithScores(key, zRangeParams)); } @Override public Response zrangestore(String dest, String src, ZRangeParams zRangeParams) { return appendCommand(commandObjects.zrangestore(dest, src, zRangeParams)); } @Override public Response zremrangeByRank(String key, long start, long stop) { return appendCommand(commandObjects.zremrangeByRank(key, start, stop)); } @Override public Response zremrangeByScore(String key, double min, double max) { return appendCommand(commandObjects.zremrangeByScore(key, min, max)); } @Override public Response zremrangeByScore(String key, String min, String max) { return appendCommand(commandObjects.zremrangeByScore(key, min, max)); } @Override public Response zlexcount(String key, String min, String max) { return appendCommand(commandObjects.zlexcount(key, min, max)); } @Override public Response> zrangeByLex(String key, String min, String max) { return appendCommand(commandObjects.zrangeByLex(key, min, max)); } @Override public Response> zrangeByLex(String key, String min, String max, int offset, int count) { return appendCommand(commandObjects.zrangeByLex(key, min, max, offset, count)); } @Override public Response> zrevrangeByLex(String key, String max, String min) { return appendCommand(commandObjects.zrevrangeByLex(key, max, min)); } @Override public Response> zrevrangeByLex(String key, String max, String min, int offset, int count) { return appendCommand(commandObjects.zrevrangeByLex(key, max, min, offset, count)); } @Override public Response zremrangeByLex(String key, String min, String max) { return appendCommand(commandObjects.zremrangeByLex(key, min, max)); } @Override public Response> zscan(String key, String cursor, ScanParams params) { return appendCommand(commandObjects.zscan(key, cursor, params)); } @Override public Response> bzpopmax(double timeout, String... keys) { return appendCommand(commandObjects.bzpopmax(timeout, keys)); } @Override public Response> bzpopmin(double timeout, String... keys) { return appendCommand(commandObjects.bzpopmin(timeout, keys)); } @Override public Response>> zmpop(SortedSetOption option, String... keys) { return appendCommand(commandObjects.zmpop(option, keys)); } @Override public Response>> zmpop(SortedSetOption option, int count, String... keys) { return appendCommand(commandObjects.zmpop(option, count, keys)); } @Override public Response>> bzmpop(double timeout, SortedSetOption option, String... keys) { return appendCommand(commandObjects.bzmpop(timeout, option, keys)); } @Override public Response>> bzmpop(double timeout, SortedSetOption option, int count, String... keys) { return appendCommand(commandObjects.bzmpop(timeout, option, count, keys)); } @Override public Response> zdiff(String... keys) { return appendCommand(commandObjects.zdiff(keys)); } @Override public Response> zdiffWithScores(String... keys) { return appendCommand(commandObjects.zdiffWithScores(keys)); } @Override @Deprecated public Response zdiffStore(String dstKey, String... keys) { return appendCommand(commandObjects.zdiffStore(dstKey, keys)); } @Override public Response zdiffstore(String dstKey, String... keys) { return appendCommand(commandObjects.zdiffstore(dstKey, keys)); } @Override public Response zinterstore(String dstKey, String... sets) { return appendCommand(commandObjects.zinterstore(dstKey, sets)); } @Override public Response zinterstore(String dstKey, ZParams params, String... sets) { return appendCommand(commandObjects.zinterstore(dstKey, params, sets)); } @Override public Response> zinter(ZParams params, String... keys) { return appendCommand(commandObjects.zinter(params, keys)); } @Override public Response> zinterWithScores(ZParams params, String... keys) { return appendCommand(commandObjects.zinterWithScores(params, keys)); } @Override public Response zintercard(String... keys) { return appendCommand(commandObjects.zintercard(keys)); } @Override public Response zintercard(long limit, String... keys) { return appendCommand(commandObjects.zintercard(limit, keys)); } @Override public Response> zunion(ZParams params, String... keys) { return appendCommand(commandObjects.zunion(params, keys)); } @Override public Response> zunionWithScores(ZParams params, String... keys) { return appendCommand(commandObjects.zunionWithScores(params, keys)); } @Override public Response zunionstore(String dstKey, String... sets) { return appendCommand(commandObjects.zunionstore(dstKey, sets)); } @Override public Response zunionstore(String dstKey, ZParams params, String... sets) { return appendCommand(commandObjects.zunionstore(dstKey, params, sets)); } @Override public Response geoadd(String key, double longitude, double latitude, String member) { return appendCommand(commandObjects.geoadd(key, longitude, latitude, member)); } @Override public Response geoadd(String key, Map memberCoordinateMap) { return appendCommand(commandObjects.geoadd(key, memberCoordinateMap)); } @Override public Response geoadd(String key, GeoAddParams params, Map memberCoordinateMap) { return appendCommand(commandObjects.geoadd(key, params, memberCoordinateMap)); } @Override public Response geodist(String key, String member1, String member2) { return appendCommand(commandObjects.geodist(key, member1, member2)); } @Override public Response geodist(String key, String member1, String member2, GeoUnit unit) { return appendCommand(commandObjects.geodist(key, member1, member2, unit)); } @Override public Response> geohash(String key, String... members) { return appendCommand(commandObjects.geohash(key, members)); } @Override public Response> geopos(String key, String... members) { return appendCommand(commandObjects.geopos(key, members)); } @Override public Response> georadius(String key, double longitude, double latitude, double radius, GeoUnit unit) { return appendCommand(commandObjects.georadius(key, longitude, latitude, radius, unit)); } @Override public Response> georadiusReadonly(String key, double longitude, double latitude, double radius, GeoUnit unit) { return appendCommand(commandObjects.georadiusReadonly(key, longitude, latitude, radius, unit)); } @Override public Response> georadius(String key, double longitude, double latitude, double radius, GeoUnit unit, GeoRadiusParam param) { return appendCommand(commandObjects.georadius(key, longitude, latitude, radius, unit, param)); } @Override public Response> georadiusReadonly(String key, double longitude, double latitude, double radius, GeoUnit unit, GeoRadiusParam param) { return appendCommand(commandObjects.georadiusReadonly(key, longitude, latitude, radius, unit, param)); } @Override public Response> georadiusByMember(String key, String member, double radius, GeoUnit unit) { return appendCommand(commandObjects.georadiusByMember(key, member, radius, unit)); } @Override public Response> georadiusByMemberReadonly(String key, String member, double radius, GeoUnit unit) { return appendCommand(commandObjects.georadiusByMemberReadonly(key, member, radius, unit)); } @Override public Response> georadiusByMember(String key, String member, double radius, GeoUnit unit, GeoRadiusParam param) { return appendCommand(commandObjects.georadiusByMember(key, member, radius, unit, param)); } @Override public Response> georadiusByMemberReadonly(String key, String member, double radius, GeoUnit unit, GeoRadiusParam param) { return appendCommand(commandObjects.georadiusByMemberReadonly(key, member, radius, unit, param)); } @Override public Response georadiusStore(String key, double longitude, double latitude, double radius, GeoUnit unit, GeoRadiusParam param, GeoRadiusStoreParam storeParam) { return appendCommand(commandObjects.georadiusStore(key, longitude, latitude, radius, unit, param, storeParam)); } @Override public Response georadiusByMemberStore(String key, String member, double radius, GeoUnit unit, GeoRadiusParam param, GeoRadiusStoreParam storeParam) { return appendCommand(commandObjects.georadiusByMemberStore(key, member, radius, unit, param, storeParam)); } @Override public Response> geosearch(String key, String member, double radius, GeoUnit unit) { return appendCommand(commandObjects.geosearch(key, member, radius, unit)); } @Override public Response> geosearch(String key, GeoCoordinate coord, double radius, GeoUnit unit) { return appendCommand(commandObjects.geosearch(key, coord, radius, unit)); } @Override public Response> geosearch(String key, String member, double width, double height, GeoUnit unit) { return appendCommand(commandObjects.geosearch(key, member, width, height, unit)); } @Override public Response> geosearch(String key, GeoCoordinate coord, double width, double height, GeoUnit unit) { return appendCommand(commandObjects.geosearch(key, coord, width, height, unit)); } @Override public Response> geosearch(String key, GeoSearchParam params) { return appendCommand(commandObjects.geosearch(key, params)); } @Override public Response geosearchStore(String dest, String src, String member, double radius, GeoUnit unit) { return appendCommand(commandObjects.geosearchStore(dest, src, member, radius, unit)); } @Override public Response geosearchStore(String dest, String src, GeoCoordinate coord, double radius, GeoUnit unit) { return appendCommand(commandObjects.geosearchStore(dest, src, coord, radius, unit)); } @Override public Response geosearchStore(String dest, String src, String member, double width, double height, GeoUnit unit) { return appendCommand(commandObjects.geosearchStore(dest, src, member, width, height, unit)); } @Override public Response geosearchStore(String dest, String src, GeoCoordinate coord, double width, double height, GeoUnit unit) { return appendCommand(commandObjects.geosearchStore(dest, src, coord, width, height, unit)); } @Override public Response geosearchStore(String dest, String src, GeoSearchParam params) { return appendCommand(commandObjects.geosearchStore(dest, src, params)); } @Override public Response geosearchStoreStoreDist(String dest, String src, GeoSearchParam params) { return appendCommand(commandObjects.geosearchStoreStoreDist(dest, src, params)); } @Override public Response pfadd(String key, String... elements) { return appendCommand(commandObjects.pfadd(key, elements)); } @Override public Response pfmerge(String destkey, String... sourcekeys) { return appendCommand(commandObjects.pfmerge(destkey, sourcekeys)); } @Override public Response pfcount(String key) { return appendCommand(commandObjects.pfcount(key)); } @Override public Response pfcount(String... keys) { return appendCommand(commandObjects.pfcount(keys)); } @Override public Response xadd(String key, StreamEntryID id, Map hash) { return appendCommand(commandObjects.xadd(key, id, hash)); } @Override public Response xadd(String key, XAddParams params, Map hash) { return appendCommand(commandObjects.xadd(key, params, hash)); } @Override public Response xlen(String key) { return appendCommand(commandObjects.xlen(key)); } @Override public Response> xrange(String key, StreamEntryID start, StreamEntryID end) { return appendCommand(commandObjects.xrange(key, start, end)); } @Override public Response> xrange(String key, StreamEntryID start, StreamEntryID end, int count) { return appendCommand(commandObjects.xrange(key, start, end, count)); } @Override public Response> xrevrange(String key, StreamEntryID end, StreamEntryID start) { return appendCommand(commandObjects.xrevrange(key, end, start)); } @Override public Response> xrevrange(String key, StreamEntryID end, StreamEntryID start, int count) { return appendCommand(commandObjects.xrevrange(key, end, start, count)); } @Override public Response> xrange(String key, String start, String end) { return appendCommand(commandObjects.xrange(key, start, end)); } @Override public Response> xrange(String key, String start, String end, int count) { return appendCommand(commandObjects.xrange(key, start, end, count)); } @Override public Response> xrevrange(String key, String end, String start) { return appendCommand(commandObjects.xrevrange(key, end, start)); } @Override public Response> xrevrange(String key, String end, String start, int count) { return appendCommand(commandObjects.xrevrange(key, end, start, count)); } @Override public Response xack(String key, String group, StreamEntryID... ids) { return appendCommand(commandObjects.xack(key, group, ids)); } @Override public Response> xackdel(String key, String group, StreamEntryID... ids) { return appendCommand(commandObjects.xackdel(key, group, ids)); } @Override public Response> xackdel(String key, String group, StreamDeletionPolicy trimMode, StreamEntryID... ids) { return appendCommand(commandObjects.xackdel(key, group, trimMode, ids)); } @Override public Response xgroupCreate(String key, String groupName, StreamEntryID id, boolean makeStream) { return appendCommand(commandObjects.xgroupCreate(key, groupName, id, makeStream)); } @Override public Response xgroupSetID(String key, String groupName, StreamEntryID id) { return appendCommand(commandObjects.xgroupSetID(key, groupName, id)); } @Override public Response xgroupDestroy(String key, String groupName) { return appendCommand(commandObjects.xgroupDestroy(key, groupName)); } @Override public Response xgroupCreateConsumer(String key, String groupName, String consumerName) { return appendCommand(commandObjects.xgroupCreateConsumer(key, groupName, consumerName)); } @Override public Response xgroupDelConsumer(String key, String groupName, String consumerName) { return appendCommand(commandObjects.xgroupDelConsumer(key, groupName, consumerName)); } @Override public Response xpending(String key, String groupName) { return appendCommand(commandObjects.xpending(key, groupName)); } @Override public Response> xpending(String key, String groupName, XPendingParams params) { return appendCommand(commandObjects.xpending(key, groupName, params)); } @Override public Response xdel(String key, StreamEntryID... ids) { return appendCommand(commandObjects.xdel(key, ids)); } @Override public Response> xdelex(String key, StreamEntryID... ids) { return appendCommand(commandObjects.xdelex(key, ids)); } @Override public Response> xdelex(String key, StreamDeletionPolicy trimMode, StreamEntryID... ids) { return appendCommand(commandObjects.xdelex(key, trimMode, ids)); } @Override public Response xtrim(String key, long maxLen, boolean approximate) { return appendCommand(commandObjects.xtrim(key, maxLen, approximate)); } @Override public Response xtrim(String key, XTrimParams params) { return appendCommand(commandObjects.xtrim(key, params)); } @Override public Response> xclaim(String key, String group, String consumerName, long minIdleTime, XClaimParams params, StreamEntryID... ids) { return appendCommand(commandObjects.xclaim(key, group, consumerName, minIdleTime, params, ids)); } @Override public Response> xclaimJustId(String key, String group, String consumerName, long minIdleTime, XClaimParams params, StreamEntryID... ids) { return appendCommand(commandObjects.xclaimJustId(key, group, consumerName, minIdleTime, params, ids)); } @Override public Response>> xautoclaim(String key, String group, String consumerName, long minIdleTime, StreamEntryID start, XAutoClaimParams params) { return appendCommand(commandObjects.xautoclaim(key, group, consumerName, minIdleTime, start, params)); } @Override public Response>> xautoclaimJustId(String key, String group, String consumerName, long minIdleTime, StreamEntryID start, XAutoClaimParams params) { return appendCommand(commandObjects.xautoclaimJustId(key, group, consumerName, minIdleTime, start, params)); } @Override public Response xinfoStream(String key) { return appendCommand(commandObjects.xinfoStream(key)); } @Override public Response xinfoStreamFull(String key) { return appendCommand(commandObjects.xinfoStreamFull(key)); } @Override public Response xinfoStreamFull(String key, int count) { return appendCommand(commandObjects.xinfoStreamFull(key, count)); } @Override public Response> xinfoGroups(String key) { return appendCommand(commandObjects.xinfoGroups(key)); } @Override public Response> xinfoConsumers(String key, String group) { return appendCommand(commandObjects.xinfoConsumers(key, group)); } @Override public Response> xinfoConsumers2(String key, String group) { return appendCommand(commandObjects.xinfoConsumers2(key, group)); } @Override public Response>>> xread(XReadParams xReadParams, Map streams) { return appendCommand(commandObjects.xread(xReadParams, streams)); } @Override public Response>> xreadAsMap(XReadParams xReadParams, Map streams) { return appendCommand(commandObjects.xreadAsMap(xReadParams, streams)); } @Override public Response>>> xreadGroup(String groupName, String consumer, XReadGroupParams xReadGroupParams, Map streams) { return appendCommand(commandObjects.xreadGroup(groupName, consumer, xReadGroupParams, streams)); } @Override public Response>> xreadGroupAsMap(String groupName, String consumer, XReadGroupParams xReadGroupParams, Map streams) { return appendCommand(commandObjects.xreadGroupAsMap(groupName, consumer, xReadGroupParams, streams)); } @Override public Response xcfgset(String key, XCfgSetParams params) { return appendCommand(commandObjects.xcfgset(key, params)); } @Override public Response eval(String script) { return appendCommand(commandObjects.eval(script)); } @Override public Response eval(String script, int keyCount, String... params) { return appendCommand(commandObjects.eval(script, keyCount, params)); } @Override public Response eval(String script, List keys, List args) { return appendCommand(commandObjects.eval(script, keys, args)); } @Override public Response evalReadonly(String script, List keys, List args) { return appendCommand(commandObjects.evalReadonly(script, keys, args)); } @Override public Response evalsha(String sha1) { return appendCommand(commandObjects.evalsha(sha1)); } @Override public Response evalsha(String sha1, int keyCount, String... params) { return appendCommand(commandObjects.evalsha(sha1, keyCount, params)); } @Override public Response evalsha(String sha1, List keys, List args) { return appendCommand(commandObjects.evalsha(sha1, keys, args)); } @Override public Response evalshaReadonly(String sha1, List keys, List args) { return appendCommand(commandObjects.evalshaReadonly(sha1, keys, args)); } @Override public Response waitReplicas(String sampleKey, int replicas, long timeout) { return appendCommand(commandObjects.waitReplicas(sampleKey, replicas, timeout)); } @Override public Response> waitAOF(String sampleKey, long numLocal, long numReplicas, long timeout) { return appendCommand(commandObjects.waitAOF(sampleKey, numLocal, numReplicas, timeout)); } @Override public Response eval(String script, String sampleKey) { return appendCommand(commandObjects.eval(script, sampleKey)); } @Override public Response evalsha(String sha1, String sampleKey) { return appendCommand(commandObjects.evalsha(sha1, sampleKey)); } @Override public Response> scriptExists(String sampleKey, String... sha1) { return appendCommand(commandObjects.scriptExists(sampleKey, sha1)); } @Override public Response scriptLoad(String script, String sampleKey) { return appendCommand(commandObjects.scriptLoad(script, sampleKey)); } @Override public Response scriptFlush(String sampleKey) { return appendCommand(commandObjects.scriptFlush(sampleKey)); } @Override public Response scriptFlush(String sampleKey, FlushMode flushMode) { return appendCommand(commandObjects.scriptFlush(sampleKey, flushMode)); } @Override public Response scriptKill(String sampleKey) { return appendCommand(commandObjects.scriptKill(sampleKey)); } @Override public Response fcall(byte[] name, List keys, List args) { return appendCommand(commandObjects.fcall(name, keys, args)); } @Override public Response fcall(String name, List keys, List args) { return appendCommand(commandObjects.fcall(name, keys, args)); } @Override public Response fcallReadonly(byte[] name, List keys, List args) { return appendCommand(commandObjects.fcallReadonly(name, keys, args)); } @Override public Response fcallReadonly(String name, List keys, List args) { return appendCommand(commandObjects.fcallReadonly(name, keys, args)); } @Override public Response functionDelete(byte[] libraryName) { return appendCommand(commandObjects.functionDelete(libraryName)); } @Override public Response functionDelete(String libraryName) { return appendCommand(commandObjects.functionDelete(libraryName)); } @Override public Response functionDump() { return appendCommand(commandObjects.functionDump()); } @Override public Response> functionList(String libraryNamePattern) { return appendCommand(commandObjects.functionList(libraryNamePattern)); } @Override public Response> functionList() { return appendCommand(commandObjects.functionList()); } @Override public Response> functionListWithCode(String libraryNamePattern) { return appendCommand(commandObjects.functionListWithCode(libraryNamePattern)); } @Override public Response> functionListWithCode() { return appendCommand(commandObjects.functionListWithCode()); } @Override public Response> functionListBinary() { return appendCommand(commandObjects.functionListBinary()); } @Override public Response> functionList(final byte[] libraryNamePattern) { return appendCommand(commandObjects.functionList(libraryNamePattern)); } @Override public Response> functionListWithCodeBinary() { return appendCommand(commandObjects.functionListWithCodeBinary()); } @Override public Response> functionListWithCode(final byte[] libraryNamePattern) { return appendCommand(commandObjects.functionListWithCode(libraryNamePattern)); } @Override public Response functionLoad(byte[] functionCode) { return appendCommand(commandObjects.functionLoad(functionCode)); } @Override public Response functionLoad(String functionCode) { return appendCommand(commandObjects.functionLoad(functionCode)); } @Override public Response functionLoadReplace(byte[] functionCode) { return appendCommand(commandObjects.functionLoadReplace(functionCode)); } @Override public Response functionLoadReplace(String functionCode) { return appendCommand(commandObjects.functionLoadReplace(functionCode)); } @Override public Response functionRestore(byte[] serializedValue) { return appendCommand(commandObjects.functionRestore(serializedValue)); } @Override public Response functionRestore(byte[] serializedValue, FunctionRestorePolicy policy) { return appendCommand(commandObjects.functionRestore(serializedValue, policy)); } @Override public Response functionFlush() { return appendCommand(commandObjects.functionFlush()); } @Override public Response functionFlush(FlushMode mode) { return appendCommand(commandObjects.functionFlush(mode)); } @Override public Response functionKill() { return appendCommand(commandObjects.functionKill()); } @Override public Response functionStats() { return appendCommand(commandObjects.functionStats()); } @Override public Response functionStatsBinary() { return appendCommand(commandObjects.functionStatsBinary()); } @Override public Response geoadd(byte[] key, double longitude, double latitude, byte[] member) { return appendCommand(commandObjects.geoadd(key, longitude, latitude, member)); } @Override public Response geoadd(byte[] key, Map memberCoordinateMap) { return appendCommand(commandObjects.geoadd(key, memberCoordinateMap)); } @Override public Response geoadd(byte[] key, GeoAddParams params, Map memberCoordinateMap) { return appendCommand(commandObjects.geoadd(key, params, memberCoordinateMap)); } @Override public Response geodist(byte[] key, byte[] member1, byte[] member2) { return appendCommand(commandObjects.geodist(key, member1, member2)); } @Override public Response geodist(byte[] key, byte[] member1, byte[] member2, GeoUnit unit) { return appendCommand(commandObjects.geodist(key, member1, member2, unit)); } @Override public Response> geohash(byte[] key, byte[]... members) { return appendCommand(commandObjects.geohash(key, members)); } @Override public Response> geopos(byte[] key, byte[]... members) { return appendCommand(commandObjects.geopos(key, members)); } @Override public Response> georadius(byte[] key, double longitude, double latitude, double radius, GeoUnit unit) { return appendCommand(commandObjects.georadius(key, longitude, latitude, radius, unit)); } @Override public Response> georadiusReadonly(byte[] key, double longitude, double latitude, double radius, GeoUnit unit) { return appendCommand(commandObjects.georadiusReadonly(key, longitude, latitude, radius, unit)); } @Override public Response> georadius(byte[] key, double longitude, double latitude, double radius, GeoUnit unit, GeoRadiusParam param) { return appendCommand(commandObjects.georadius(key, longitude, latitude, radius, unit, param)); } @Override public Response> georadiusReadonly(byte[] key, double longitude, double latitude, double radius, GeoUnit unit, GeoRadiusParam param) { return appendCommand(commandObjects.georadiusReadonly(key, longitude, latitude, radius, unit, param)); } @Override public Response> georadiusByMember(byte[] key, byte[] member, double radius, GeoUnit unit) { return appendCommand(commandObjects.georadiusByMember(key, member, radius, unit)); } @Override public Response> georadiusByMemberReadonly(byte[] key, byte[] member, double radius, GeoUnit unit) { return appendCommand(commandObjects.georadiusByMemberReadonly(key, member, radius, unit)); } @Override public Response> georadiusByMember(byte[] key, byte[] member, double radius, GeoUnit unit, GeoRadiusParam param) { return appendCommand(commandObjects.georadiusByMember(key, member, radius, unit, param)); } @Override public Response> georadiusByMemberReadonly(byte[] key, byte[] member, double radius, GeoUnit unit, GeoRadiusParam param) { return appendCommand(commandObjects.georadiusByMemberReadonly(key, member, radius, unit, param)); } @Override public Response georadiusStore(byte[] key, double longitude, double latitude, double radius, GeoUnit unit, GeoRadiusParam param, GeoRadiusStoreParam storeParam) { return appendCommand(commandObjects.georadiusStore(key, longitude, latitude, radius, unit, param, storeParam)); } @Override public Response georadiusByMemberStore(byte[] key, byte[] member, double radius, GeoUnit unit, GeoRadiusParam param, GeoRadiusStoreParam storeParam) { return appendCommand(commandObjects.georadiusByMemberStore(key, member, radius, unit, param, storeParam)); } @Override public Response> geosearch(byte[] key, byte[] member, double radius, GeoUnit unit) { return appendCommand(commandObjects.geosearch(key, member, radius, unit)); } @Override public Response> geosearch(byte[] key, GeoCoordinate coord, double radius, GeoUnit unit) { return appendCommand(commandObjects.geosearch(key, coord, radius, unit)); } @Override public Response> geosearch(byte[] key, byte[] member, double width, double height, GeoUnit unit) { return appendCommand(commandObjects.geosearch(key, member, width, height, unit)); } @Override public Response> geosearch(byte[] key, GeoCoordinate coord, double width, double height, GeoUnit unit) { return appendCommand(commandObjects.geosearch(key, coord, width, height, unit)); } @Override public Response> geosearch(byte[] key, GeoSearchParam params) { return appendCommand(commandObjects.geosearch(key, params)); } @Override public Response geosearchStore(byte[] dest, byte[] src, byte[] member, double radius, GeoUnit unit) { return appendCommand(commandObjects.geosearchStore(dest, src, member, radius, unit)); } @Override public Response geosearchStore(byte[] dest, byte[] src, GeoCoordinate coord, double radius, GeoUnit unit) { return appendCommand(commandObjects.geosearchStore(dest, src, coord, radius, unit)); } @Override public Response geosearchStore(byte[] dest, byte[] src, byte[] member, double width, double height, GeoUnit unit) { return appendCommand(commandObjects.geosearchStore(dest, src, member, width, height, unit)); } @Override public Response geosearchStore(byte[] dest, byte[] src, GeoCoordinate coord, double width, double height, GeoUnit unit) { return appendCommand(commandObjects.geosearchStore(dest, src, coord, width, height, unit)); } @Override public Response geosearchStore(byte[] dest, byte[] src, GeoSearchParam params) { return appendCommand(commandObjects.geosearchStore(dest, src, params)); } @Override public Response geosearchStoreStoreDist(byte[] dest, byte[] src, GeoSearchParam params) { return appendCommand(commandObjects.geosearchStoreStoreDist(dest, src, params)); } @Override public Response hset(byte[] key, byte[] field, byte[] value) { return appendCommand(commandObjects.hset(key, field, value)); } @Override public Response hset(byte[] key, Map hash) { return appendCommand(commandObjects.hset(key, hash)); } /** * Sets the specified field in the hash stored at key to the specified value with additional parameters, * and optionally set their expiration. Use `HSetExParams` object to specify expiration parameters. * This command can overwrite any existing fields in the hash. * If key does not exist, a new key holding a hash is created. * * @param key the key of the hash * @param params the parameters for the HSetEx command * @param field the field in the hash to set * @param value the value to set in the specified field * @return 0 if no fields were set, 1 if all the fields were set * * @see HSetExParams */ @Override public Response hsetex(byte[] key, HSetExParams params, byte[] field, byte[] value) { return appendCommand(commandObjects.hsetex(key, params, field, value)); } /** * Sets the specified fields in the hash stored at key to the specified values with additional parameters, * and optionally set their expiration. Use `HSetExParams` object to specify expiration parameters. * This command can overwrite any existing fields in the hash. * If key does not exist, a new key holding a hash is created. * * @param key the key of the hash * @param params the parameters for the HSetEx command * @param hash the map containing field-value pairs to set in the hash * @return 0 if no fields were set, 1 if all the fields were set * * @see HSetExParams */ @Override public Response hsetex(byte[] key, HSetExParams params, Map hash) { return appendCommand(commandObjects.hsetex(key, params, hash)); } @Override public Response hget(byte[] key, byte[] field) { return appendCommand(commandObjects.hget(key, field)); } /** * Retrieves the values associated with the specified fields in a hash stored at the given key * and optionally sets their expiration. Use `HGetExParams` object to specify expiration parameters. * * @param key the key of the hash * @param params additional parameters for the HGETEX command * @param fields the fields whose values are to be retrieved * @return a list of the value associated with each field or nil if the field doesn’t exist. * * @see HGetExParams */ @Override public Response> hgetex(byte[] key, HGetExParams params, byte[]... fields) { return appendCommand(commandObjects.hgetex(key, params, fields)); } /** * Retrieves the values associated with the specified fields in the hash stored at the given key * and then deletes those fields from the hash. * * @param key the key of the hash * @param fields the fields whose values are to be retrieved and then deleted * @return a list of values associated with the specified fields before they were deleted */ @Override public Response> hgetdel(byte[] key, byte[]... fields) { return appendCommand(commandObjects.hgetdel(key, fields)); } @Override public Response hsetnx(byte[] key, byte[] field, byte[] value) { return appendCommand(commandObjects.hsetnx(key, field, value)); } /** * @deprecated Use {@link PipeliningBase#hset(byte[], Map)} instead. * Deprecated in Jedis 7.3.0. Mirrors Redis deprecation since 4.0.0. */ @Deprecated @Override public Response hmset(byte[] key, Map hash) { return appendCommand(commandObjects.hmset(key, hash)); } @Override public Response> hmget(byte[] key, byte[]... fields) { return appendCommand(commandObjects.hmget(key, fields)); } @Override public Response hincrBy(byte[] key, byte[] field, long value) { return appendCommand(commandObjects.hincrBy(key, field, value)); } @Override public Response hincrByFloat(byte[] key, byte[] field, double value) { return appendCommand(commandObjects.hincrByFloat(key, field, value)); } @Override public Response hexists(byte[] key, byte[] field) { return appendCommand(commandObjects.hexists(key, field)); } @Override public Response hdel(byte[] key, byte[]... field) { return appendCommand(commandObjects.hdel(key, field)); } @Override public Response hlen(byte[] key) { return appendCommand(commandObjects.hlen(key)); } @Override public Response> hkeys(byte[] key) { return appendCommand(commandObjects.hkeys(key)); } @Override public Response> hvals(byte[] key) { return appendCommand(commandObjects.hvals(key)); } @Override public Response> hgetAll(byte[] key) { return appendCommand(commandObjects.hgetAll(key)); } @Override public Response hrandfield(byte[] key) { return appendCommand(commandObjects.hrandfield(key)); } @Override public Response> hrandfield(byte[] key, long count) { return appendCommand(commandObjects.hrandfield(key, count)); } @Override public Response>> hrandfieldWithValues(byte[] key, long count) { return appendCommand(commandObjects.hrandfieldWithValues(key, count)); } @Override public Response>> hscan(byte[] key, byte[] cursor, ScanParams params) { return appendCommand(commandObjects.hscan(key, cursor, params)); } @Override public Response> hscanNoValues(byte[] key, byte[] cursor, ScanParams params) { return appendCommand(commandObjects.hscanNoValues(key, cursor, params)); } @Override public Response hstrlen(byte[] key, byte[] field) { return appendCommand(commandObjects.hstrlen(key, field)); } @Override public Response> hexpire(byte[] key, long seconds, byte[]... fields) { return appendCommand(commandObjects.hexpire(key, seconds, fields)); } @Override public Response> hexpire(byte[] key, long seconds, ExpiryOption condition, byte[]... fields) { return appendCommand(commandObjects.hexpire(key, seconds, condition, fields)); } @Override public Response> hpexpire(byte[] key, long milliseconds, byte[]... fields) { return appendCommand(commandObjects.hpexpire(key, milliseconds, fields)); } @Override public Response> hpexpire(byte[] key, long milliseconds, ExpiryOption condition, byte[]... fields) { return appendCommand(commandObjects.hpexpire(key, milliseconds, condition, fields)); } @Override public Response> hexpireAt(byte[] key, long unixTimeSeconds, byte[]... fields) { return appendCommand(commandObjects.hexpireAt(key, unixTimeSeconds, fields)); } @Override public Response> hexpireAt(byte[] key, long unixTimeSeconds, ExpiryOption condition, byte[]... fields) { return appendCommand(commandObjects.hexpireAt(key, unixTimeSeconds, condition, fields)); } @Override public Response> hpexpireAt(byte[] key, long unixTimeMillis, byte[]... fields) { return appendCommand(commandObjects.hpexpireAt(key, unixTimeMillis, fields)); } @Override public Response> hpexpireAt(byte[] key, long unixTimeMillis, ExpiryOption condition, byte[]... fields) { return appendCommand(commandObjects.hpexpireAt(key, unixTimeMillis, condition, fields)); } @Override public Response> hexpireTime(byte[] key, byte[]... fields) { return appendCommand(commandObjects.hexpireTime(key, fields)); } @Override public Response> hpexpireTime(byte[] key, byte[]... fields) { return appendCommand(commandObjects.hpexpireTime(key, fields)); } @Override public Response> httl(byte[] key, byte[]... fields) { return appendCommand(commandObjects.httl(key, fields)); } @Override public Response> hpttl(byte[] key, byte[]... fields) { return appendCommand(commandObjects.hpttl(key, fields)); } @Override public Response> hpersist(byte[] key, byte[]... fields) { return appendCommand(commandObjects.hpersist(key, fields)); } @Override public Response pfadd(byte[] key, byte[]... elements) { return appendCommand(commandObjects.pfadd(key, elements)); } @Override public Response pfmerge(byte[] destkey, byte[]... sourcekeys) { return appendCommand(commandObjects.pfmerge(destkey, sourcekeys)); } @Override public Response pfcount(byte[] key) { return appendCommand(commandObjects.pfcount(key)); } @Override public Response pfcount(byte[]... keys) { return appendCommand(commandObjects.pfcount(keys)); } @Override public Response exists(byte[] key) { return appendCommand(commandObjects.exists(key)); } @Override public Response exists(byte[]... keys) { return appendCommand(commandObjects.exists(keys)); } @Override public Response persist(byte[] key) { return appendCommand(commandObjects.persist(key)); } @Override public Response type(byte[] key) { return appendCommand(commandObjects.type(key)); } @Override public Response dump(byte[] key) { return appendCommand(commandObjects.dump(key)); } @Override public Response restore(byte[] key, long ttl, byte[] serializedValue) { return appendCommand(commandObjects.restore(key, ttl, serializedValue)); } @Override public Response restore(byte[] key, long ttl, byte[] serializedValue, RestoreParams params) { return appendCommand(commandObjects.restore(key, ttl, serializedValue, params)); } @Override public Response expire(byte[] key, long seconds) { return appendCommand(commandObjects.expire(key, seconds)); } @Override public Response expire(byte[] key, long seconds, ExpiryOption expiryOption) { return appendCommand(commandObjects.expire(key, seconds, expiryOption)); } @Override public Response pexpire(byte[] key, long milliseconds) { return appendCommand(commandObjects.pexpire(key, milliseconds)); } @Override public Response pexpire(byte[] key, long milliseconds, ExpiryOption expiryOption) { return appendCommand(commandObjects.pexpire(key, milliseconds, expiryOption)); } @Override public Response expireTime(byte[] key) { return appendCommand(commandObjects.expireTime(key)); } @Override public Response pexpireTime(byte[] key) { return appendCommand(commandObjects.pexpireTime(key)); } @Override public Response expireAt(byte[] key, long unixTime) { return appendCommand(commandObjects.expireAt(key, unixTime)); } @Override public Response expireAt(byte[] key, long unixTime, ExpiryOption expiryOption) { return appendCommand(commandObjects.expireAt(key, unixTime, expiryOption)); } @Override public Response pexpireAt(byte[] key, long millisecondsTimestamp) { return appendCommand(commandObjects.pexpireAt(key, millisecondsTimestamp)); } @Override public Response pexpireAt(byte[] key, long millisecondsTimestamp, ExpiryOption expiryOption) { return appendCommand(commandObjects.pexpireAt(key, millisecondsTimestamp, expiryOption)); } @Override public Response ttl(byte[] key) { return appendCommand(commandObjects.ttl(key)); } @Override public Response pttl(byte[] key) { return appendCommand(commandObjects.pttl(key)); } @Override public Response touch(byte[] key) { return appendCommand(commandObjects.touch(key)); } @Override public Response touch(byte[]... keys) { return appendCommand(commandObjects.touch(keys)); } @Override public Response> sort(byte[] key) { return appendCommand(commandObjects.sort(key)); } @Override public Response> sort(byte[] key, SortingParams sortingParams) { return appendCommand(commandObjects.sort(key, sortingParams)); } @Override public Response> sortReadonly(byte[] key, SortingParams sortingParams) { return appendCommand(commandObjects.sortReadonly(key, sortingParams)); } @Override public Response del(byte[] key) { return appendCommand(commandObjects.del(key)); } @Override public Response del(byte[]... keys) { return appendCommand(commandObjects.del(keys)); } @Override public Response delex(byte[] key, CompareCondition condition) { return appendCommand(commandObjects.delex(key, condition)); } @Override public Response delex(String key, CompareCondition condition) { return appendCommand(commandObjects.delex(key, condition)); } @Override public Response digestKey(byte[] key) { return appendCommand(commandObjects.digestKey(key)); } @Override public Response digestKey(String key) { return appendCommand(commandObjects.digestKey(key)); } @Override public Response unlink(byte[] key) { return appendCommand(commandObjects.unlink(key)); } @Override public Response unlink(byte[]... keys) { return appendCommand(commandObjects.unlink(keys)); } @Override public Response copy(byte[] srcKey, byte[] dstKey, boolean replace) { return appendCommand(commandObjects.copy(srcKey, dstKey, replace)); } @Override public Response rename(byte[] oldkey, byte[] newkey) { return appendCommand(commandObjects.rename(oldkey, newkey)); } @Override public Response renamenx(byte[] oldkey, byte[] newkey) { return appendCommand(commandObjects.renamenx(oldkey, newkey)); } @Override public Response sort(byte[] key, SortingParams sortingParams, byte[] dstkey) { return appendCommand(commandObjects.sort(key, sortingParams, dstkey)); } @Override public Response sort(byte[] key, byte[] dstkey) { return appendCommand(commandObjects.sort(key, dstkey)); } @Override public Response memoryUsage(byte[] key) { return appendCommand(commandObjects.memoryUsage(key)); } @Override public Response memoryUsage(byte[] key, int samples) { return appendCommand(commandObjects.memoryUsage(key, samples)); } @Override public Response objectRefcount(byte[] key) { return appendCommand(commandObjects.objectRefcount(key)); } @Override public Response objectEncoding(byte[] key) { return appendCommand(commandObjects.objectEncoding(key)); } @Override public Response objectIdletime(byte[] key) { return appendCommand(commandObjects.objectIdletime(key)); } @Override public Response objectFreq(byte[] key) { return appendCommand(commandObjects.objectFreq(key)); } @Override public Response migrate(String host, int port, byte[] key, int timeout) { return appendCommand(commandObjects.migrate(host, port, key, timeout)); } @Override public Response migrate(String host, int port, int timeout, MigrateParams params, byte[]... keys) { return appendCommand(commandObjects.migrate(host, port, timeout, params, keys)); } @Override public Response> keys(byte[] pattern) { return appendCommand(commandObjects.keys(pattern)); } @Override public Response> scan(byte[] cursor) { return appendCommand(commandObjects.scan(cursor)); } @Override public Response> scan(byte[] cursor, ScanParams params) { return appendCommand(commandObjects.scan(cursor, params)); } @Override public Response> scan(byte[] cursor, ScanParams params, byte[] type) { return appendCommand(commandObjects.scan(cursor, params, type)); } @Override public Response randomBinaryKey() { return appendCommand(commandObjects.randomBinaryKey()); } @Override public Response rpush(byte[] key, byte[]... args) { return appendCommand(commandObjects.rpush(key, args)); } @Override public Response lpush(byte[] key, byte[]... args) { return appendCommand(commandObjects.lpush(key, args)); } @Override public Response llen(byte[] key) { return appendCommand(commandObjects.llen(key)); } @Override public Response> lrange(byte[] key, long start, long stop) { return appendCommand(commandObjects.lrange(key, start, stop)); } @Override public Response ltrim(byte[] key, long start, long stop) { return appendCommand(commandObjects.ltrim(key, start, stop)); } @Override public Response lindex(byte[] key, long index) { return appendCommand(commandObjects.lindex(key, index)); } @Override public Response lset(byte[] key, long index, byte[] value) { return appendCommand(commandObjects.lset(key, index, value)); } @Override public Response lrem(byte[] key, long count, byte[] value) { return appendCommand(commandObjects.lrem(key, count, value)); } @Override public Response lpop(byte[] key) { return appendCommand(commandObjects.lpop(key)); } @Override public Response> lpop(byte[] key, int count) { return appendCommand(commandObjects.lpop(key, count)); } @Override public Response lpos(byte[] key, byte[] element) { return appendCommand(commandObjects.lpos(key, element)); } @Override public Response lpos(byte[] key, byte[] element, LPosParams params) { return appendCommand(commandObjects.lpos(key, element, params)); } @Override public Response> lpos(byte[] key, byte[] element, LPosParams params, long count) { return appendCommand(commandObjects.lpos(key, element, params, count)); } @Override public Response rpop(byte[] key) { return appendCommand(commandObjects.rpop(key)); } @Override public Response> rpop(byte[] key, int count) { return appendCommand(commandObjects.rpop(key, count)); } @Override public Response linsert(byte[] key, ListPosition where, byte[] pivot, byte[] value) { return appendCommand(commandObjects.linsert(key, where, pivot, value)); } @Override public Response lpushx(byte[] key, byte[]... args) { return appendCommand(commandObjects.lpushx(key, args)); } @Override public Response rpushx(byte[] key, byte[]... args) { return appendCommand(commandObjects.rpushx(key, args)); } @Override public Response> blpop(int timeout, byte[]... keys) { return appendCommand(commandObjects.blpop(timeout, keys)); } @Override public Response> blpop(double timeout, byte[]... keys) { return appendCommand(commandObjects.blpop(timeout, keys)); } @Override public Response> brpop(int timeout, byte[]... keys) { return appendCommand(commandObjects.brpop(timeout, keys)); } @Override public Response> brpop(double timeout, byte[]... keys) { return appendCommand(commandObjects.brpop(timeout, keys)); } /** * @deprecated Use {@link PipeliningBase#lmove(byte[], byte[], ListDirection, ListDirection)} with * {@link ListDirection#RIGHT} and {@link ListDirection#LEFT}. * Deprecated in Jedis 7.3.0. Mirrors Redis deprecation since 6.2.0. */ @Deprecated @Override public Response rpoplpush(byte[] srckey, byte[] dstkey) { return appendCommand(commandObjects.rpoplpush(srckey, dstkey)); } /** * @deprecated Use {@link PipeliningBase#blmove(byte[], byte[], ListDirection, ListDirection, double)} with * {@link ListDirection#RIGHT} and {@link ListDirection#LEFT}. * Deprecated in Jedis 7.3.0. Mirrors Redis deprecation since 6.2.0. */ @Deprecated @Override public Response brpoplpush(byte[] source, byte[] destination, int timeout) { return appendCommand(commandObjects.brpoplpush(source, destination, timeout)); } @Override public Response lmove(byte[] srcKey, byte[] dstKey, ListDirection from, ListDirection to) { return appendCommand(commandObjects.lmove(srcKey, dstKey, from, to)); } @Override public Response blmove(byte[] srcKey, byte[] dstKey, ListDirection from, ListDirection to, double timeout) { return appendCommand(commandObjects.blmove(srcKey, dstKey, from, to, timeout)); } @Override public Response>> lmpop(ListDirection direction, byte[]... keys) { return appendCommand(commandObjects.lmpop(direction, keys)); } @Override public Response>> lmpop(ListDirection direction, int count, byte[]... keys) { return appendCommand(commandObjects.lmpop(direction, count, keys)); } @Override public Response>> blmpop(double timeout, ListDirection direction, byte[]... keys) { return appendCommand(commandObjects.blmpop(timeout, direction, keys)); } @Override public Response>> blmpop(double timeout, ListDirection direction, int count, byte[]... keys) { return appendCommand(commandObjects.blmpop(timeout, direction, count, keys)); } @Override public Response waitReplicas(byte[] sampleKey, int replicas, long timeout) { return appendCommand(commandObjects.waitReplicas(sampleKey, replicas, timeout)); } @Override public Response> waitAOF(byte[] sampleKey, long numLocal, long numReplicas, long timeout) { return appendCommand(commandObjects.waitAOF(sampleKey, numLocal, numReplicas, timeout)); } @Override public Response eval(byte[] script, byte[] sampleKey) { return appendCommand(commandObjects.eval(script, sampleKey)); } @Override public Response evalsha(byte[] sha1, byte[] sampleKey) { return appendCommand(commandObjects.evalsha(sha1, sampleKey)); } @Override public Response> scriptExists(byte[] sampleKey, byte[]... sha1s) { return appendCommand(commandObjects.scriptExists(sampleKey, sha1s)); } @Override public Response scriptLoad(byte[] script, byte[] sampleKey) { return appendCommand(commandObjects.scriptLoad(script, sampleKey)); } @Override public Response scriptFlush(byte[] sampleKey) { return appendCommand(commandObjects.scriptFlush(sampleKey)); } @Override public Response scriptFlush(byte[] sampleKey, FlushMode flushMode) { return appendCommand(commandObjects.scriptFlush(sampleKey, flushMode)); } @Override public Response scriptKill(byte[] sampleKey) { return appendCommand(commandObjects.scriptKill(sampleKey)); } @Override public Response eval(byte[] script) { return appendCommand(commandObjects.eval(script)); } @Override public Response eval(byte[] script, int keyCount, byte[]... params) { return appendCommand(commandObjects.eval(script, keyCount, params)); } @Override public Response eval(byte[] script, List keys, List args) { return appendCommand(commandObjects.eval(script, keys, args)); } @Override public Response evalReadonly(byte[] script, List keys, List args) { return appendCommand(commandObjects.evalReadonly(script, keys, args)); } @Override public Response evalsha(byte[] sha1) { return appendCommand(commandObjects.evalsha(sha1)); } @Override public Response evalsha(byte[] sha1, int keyCount, byte[]... params) { return appendCommand(commandObjects.evalsha(sha1, keyCount, params)); } @Override public Response evalsha(byte[] sha1, List keys, List args) { return appendCommand(commandObjects.evalsha(sha1, keys, args)); } @Override public Response evalshaReadonly(byte[] sha1, List keys, List args) { return appendCommand(commandObjects.evalshaReadonly(sha1, keys, args)); } @Override public Response sadd(byte[] key, byte[]... members) { return appendCommand(commandObjects.sadd(key, members)); } @Override public Response> smembers(byte[] key) { return appendCommand(commandObjects.smembers(key)); } @Override public Response srem(byte[] key, byte[]... members) { return appendCommand(commandObjects.srem(key, members)); } @Override public Response spop(byte[] key) { return appendCommand(commandObjects.spop(key)); } @Override public Response> spop(byte[] key, long count) { return appendCommand(commandObjects.spop(key, count)); } @Override public Response scard(byte[] key) { return appendCommand(commandObjects.scard(key)); } @Override public Response sismember(byte[] key, byte[] member) { return appendCommand(commandObjects.sismember(key, member)); } @Override public Response> smismember(byte[] key, byte[]... members) { return appendCommand(commandObjects.smismember(key, members)); } @Override public Response srandmember(byte[] key) { return appendCommand(commandObjects.srandmember(key)); } @Override public Response> srandmember(byte[] key, int count) { return appendCommand(commandObjects.srandmember(key, count)); } @Override public Response> sscan(byte[] key, byte[] cursor, ScanParams params) { return appendCommand(commandObjects.sscan(key, cursor, params)); } @Override public Response> sdiff(byte[]... keys) { return appendCommand(commandObjects.sdiff(keys)); } @Override public Response sdiffstore(byte[] dstkey, byte[]... keys) { return appendCommand(commandObjects.sdiffstore(dstkey, keys)); } @Override public Response> sinter(byte[]... keys) { return appendCommand(commandObjects.sinter(keys)); } @Override public Response sinterstore(byte[] dstkey, byte[]... keys) { return appendCommand(commandObjects.sinterstore(dstkey, keys)); } @Override public Response sintercard(byte[]... keys) { return appendCommand(commandObjects.sintercard(keys)); } @Override public Response sintercard(int limit, byte[]... keys) { return appendCommand(commandObjects.sintercard(limit, keys)); } @Override public Response> sunion(byte[]... keys) { return appendCommand(commandObjects.sunion(keys)); } @Override public Response sunionstore(byte[] dstkey, byte[]... keys) { return appendCommand(commandObjects.sunionstore(dstkey, keys)); } @Override public Response smove(byte[] srckey, byte[] dstkey, byte[] member) { return appendCommand(commandObjects.smove(srckey, dstkey, member)); } @Override public Response zadd(byte[] key, double score, byte[] member) { return appendCommand(commandObjects.zadd(key, score, member)); } @Override public Response zadd(byte[] key, double score, byte[] member, ZAddParams params) { return appendCommand(commandObjects.zadd(key, score, member, params)); } @Override public Response zadd(byte[] key, Map scoreMembers) { return appendCommand(commandObjects.zadd(key, scoreMembers)); } @Override public Response zadd(byte[] key, Map scoreMembers, ZAddParams params) { return appendCommand(commandObjects.zadd(key, scoreMembers, params)); } @Override public Response zaddIncr(byte[] key, double score, byte[] member, ZAddParams params) { return appendCommand(commandObjects.zaddIncr(key, score, member, params)); } @Override public Response zrem(byte[] key, byte[]... members) { return appendCommand(commandObjects.zrem(key, members)); } @Override public Response zincrby(byte[] key, double increment, byte[] member) { return appendCommand(commandObjects.zincrby(key, increment, member)); } @Override public Response zincrby(byte[] key, double increment, byte[] member, ZIncrByParams params) { return appendCommand(commandObjects.zincrby(key, increment, member, params)); } @Override public Response zrank(byte[] key, byte[] member) { return appendCommand(commandObjects.zrank(key, member)); } @Override public Response zrevrank(byte[] key, byte[] member) { return appendCommand(commandObjects.zrevrank(key, member)); } @Override public Response> zrankWithScore(byte[] key, byte[] member) { return appendCommand(commandObjects.zrankWithScore(key, member)); } @Override public Response> zrevrankWithScore(byte[] key, byte[] member) { return appendCommand(commandObjects.zrevrankWithScore(key, member)); } @Override public Response> zrange(byte[] key, long start, long stop) { return appendCommand(commandObjects.zrange(key, start, stop)); } @Override public Response> zrevrange(byte[] key, long start, long stop) { return appendCommand(commandObjects.zrevrange(key, start, stop)); } @Override public Response> zrangeWithScores(byte[] key, long start, long stop) { return appendCommand(commandObjects.zrangeWithScores(key, start, stop)); } @Override public Response> zrevrangeWithScores(byte[] key, long start, long stop) { return appendCommand(commandObjects.zrevrangeWithScores(key, start, stop)); } @Override public Response zrandmember(byte[] key) { return appendCommand(commandObjects.zrandmember(key)); } @Override public Response> zrandmember(byte[] key, long count) { return appendCommand(commandObjects.zrandmember(key, count)); } @Override public Response> zrandmemberWithScores(byte[] key, long count) { return appendCommand(commandObjects.zrandmemberWithScores(key, count)); } @Override public Response zcard(byte[] key) { return appendCommand(commandObjects.zcard(key)); } @Override public Response zscore(byte[] key, byte[] member) { return appendCommand(commandObjects.zscore(key, member)); } @Override public Response> zmscore(byte[] key, byte[]... members) { return appendCommand(commandObjects.zmscore(key, members)); } @Override public Response zpopmax(byte[] key) { return appendCommand(commandObjects.zpopmax(key)); } @Override public Response> zpopmax(byte[] key, int count) { return appendCommand(commandObjects.zpopmax(key, count)); } @Override public Response zpopmin(byte[] key) { return appendCommand(commandObjects.zpopmin(key)); } @Override public Response> zpopmin(byte[] key, int count) { return appendCommand(commandObjects.zpopmin(key, count)); } @Override public Response zcount(byte[] key, double min, double max) { return appendCommand(commandObjects.zcount(key, min, max)); } @Override public Response zcount(byte[] key, byte[] min, byte[] max) { return appendCommand(commandObjects.zcount(key, min, max)); } @Override public Response> zrangeByScore(byte[] key, double min, double max) { return appendCommand(commandObjects.zrangeByScore(key, min, max)); } @Override public Response> zrangeByScore(byte[] key, byte[] min, byte[] max) { return appendCommand(commandObjects.zrangeByScore(key, min, max)); } @Override public Response> zrevrangeByScore(byte[] key, double max, double min) { return appendCommand(commandObjects.zrevrangeByScore(key, max, min)); } @Override public Response> zrangeByScore(byte[] key, double min, double max, int offset, int count) { return appendCommand(commandObjects.zrangeByScore(key, min, max, offset, count)); } @Override public Response> zrevrangeByScore(byte[] key, byte[] max, byte[] min) { return appendCommand(commandObjects.zrevrangeByScore(key, max, min)); } @Override public Response> zrangeByScore(byte[] key, byte[] min, byte[] max, int offset, int count) { return appendCommand(commandObjects.zrangeByScore(key, min, max, offset, count)); } @Override public Response> zrevrangeByScore(byte[] key, double max, double min, int offset, int count) { return appendCommand(commandObjects.zrevrangeByScore(key, max, min, offset, count)); } @Override public Response> zrangeByScoreWithScores(byte[] key, double min, double max) { return appendCommand(commandObjects.zrangeByScoreWithScores(key, min, max)); } @Override public Response> zrevrangeByScoreWithScores(byte[] key, double max, double min) { return appendCommand(commandObjects.zrevrangeByScoreWithScores(key, max, min)); } @Override public Response> zrangeByScoreWithScores(byte[] key, double min, double max, int offset, int count) { return appendCommand(commandObjects.zrangeByScoreWithScores(key, min, max, offset, count)); } @Override public Response> zrevrangeByScore(byte[] key, byte[] max, byte[] min, int offset, int count) { return appendCommand(commandObjects.zrevrangeByScore(key, max, min, offset, count)); } @Override public Response> zrangeByScoreWithScores(byte[] key, byte[] min, byte[] max) { return appendCommand(commandObjects.zrangeByScoreWithScores(key, min, max)); } @Override public Response> zrevrangeByScoreWithScores(byte[] key, byte[] max, byte[] min) { return appendCommand(commandObjects.zrevrangeByScoreWithScores(key, max, min)); } @Override public Response> zrangeByScoreWithScores(byte[] key, byte[] min, byte[] max, int offset, int count) { return appendCommand(commandObjects.zrangeByScoreWithScores(key, min, max, offset, count)); } @Override public Response> zrevrangeByScoreWithScores(byte[] key, double max, double min, int offset, int count) { return appendCommand(commandObjects.zrevrangeByScoreWithScores(key, max, min, offset, count)); } @Override public Response> zrevrangeByScoreWithScores(byte[] key, byte[] max, byte[] min, int offset, int count) { return appendCommand(commandObjects.zrevrangeByScoreWithScores(key, max, min, offset, count)); } @Override public Response zremrangeByRank(byte[] key, long start, long stop) { return appendCommand(commandObjects.zremrangeByRank(key, start, stop)); } @Override public Response zremrangeByScore(byte[] key, double min, double max) { return appendCommand(commandObjects.zremrangeByScore(key, min, max)); } @Override public Response zremrangeByScore(byte[] key, byte[] min, byte[] max) { return appendCommand(commandObjects.zremrangeByScore(key, min, max)); } @Override public Response zlexcount(byte[] key, byte[] min, byte[] max) { return appendCommand(commandObjects.zlexcount(key, min, max)); } @Override public Response> zrangeByLex(byte[] key, byte[] min, byte[] max) { return appendCommand(commandObjects.zrangeByLex(key, min, max)); } @Override public Response> zrangeByLex(byte[] key, byte[] min, byte[] max, int offset, int count) { return appendCommand(commandObjects.zrangeByLex(key, min, max, offset, count)); } @Override public Response> zrevrangeByLex(byte[] key, byte[] max, byte[] min) { return appendCommand(commandObjects.zrevrangeByLex(key, max, min)); } @Override public Response> zrevrangeByLex(byte[] key, byte[] max, byte[] min, int offset, int count) { return appendCommand(commandObjects.zrevrangeByLex(key, max, min, offset, count)); } @Override public Response> zrange(byte[] key, ZRangeParams zRangeParams) { return appendCommand(commandObjects.zrange(key, zRangeParams)); } @Override public Response> zrangeWithScores(byte[] key, ZRangeParams zRangeParams) { return appendCommand(commandObjects.zrangeWithScores(key, zRangeParams)); } @Override public Response zrangestore(byte[] dest, byte[] src, ZRangeParams zRangeParams) { return appendCommand(commandObjects.zrangestore(dest, src, zRangeParams)); } @Override public Response zremrangeByLex(byte[] key, byte[] min, byte[] max) { return appendCommand(commandObjects.zremrangeByLex(key, min, max)); } @Override public Response> zscan(byte[] key, byte[] cursor, ScanParams params) { return appendCommand(commandObjects.zscan(key, cursor, params)); } @Override public Response> bzpopmax(double timeout, byte[]... keys) { return appendCommand(commandObjects.bzpopmax(timeout, keys)); } @Override public Response> bzpopmin(double timeout, byte[]... keys) { return appendCommand(commandObjects.bzpopmin(timeout, keys)); } @Override public Response>> zmpop(SortedSetOption option, byte[]... keys) { return appendCommand(commandObjects.zmpop(option, keys)); } @Override public Response>> zmpop(SortedSetOption option, int count, byte[]... keys) { return appendCommand(commandObjects.zmpop(option, count, keys)); } @Override public Response>> bzmpop(double timeout, SortedSetOption option, byte[]... keys) { return appendCommand(commandObjects.bzmpop(timeout, option, keys)); } @Override public Response>> bzmpop(double timeout, SortedSetOption option, int count, byte[]... keys) { return appendCommand(commandObjects.bzmpop(timeout, option, count, keys)); } @Override public Response> zdiff(byte[]... keys) { return appendCommand(commandObjects.zdiff(keys)); } @Override public Response> zdiffWithScores(byte[]... keys) { return appendCommand(commandObjects.zdiffWithScores(keys)); } @Override @Deprecated public Response zdiffStore(byte[] dstkey, byte[]... keys) { return appendCommand(commandObjects.zdiffStore(dstkey, keys)); } @Override public Response zdiffstore(byte[] dstkey, byte[]... keys) { return appendCommand(commandObjects.zdiffstore(dstkey, keys)); } @Override public Response> zinter(ZParams params, byte[]... keys) { return appendCommand(commandObjects.zinter(params, keys)); } @Override public Response> zinterWithScores(ZParams params, byte[]... keys) { return appendCommand(commandObjects.zinterWithScores(params, keys)); } @Override public Response zinterstore(byte[] dstkey, byte[]... sets) { return appendCommand(commandObjects.zinterstore(dstkey, sets)); } @Override public Response zinterstore(byte[] dstkey, ZParams params, byte[]... sets) { return appendCommand(commandObjects.zinterstore(dstkey, params, sets)); } @Override public Response zintercard(byte[]... keys) { return appendCommand(commandObjects.zintercard(keys)); } @Override public Response zintercard(long limit, byte[]... keys) { return appendCommand(commandObjects.zintercard(limit, keys)); } @Override public Response> zunion(ZParams params, byte[]... keys) { return appendCommand(commandObjects.zunion(params, keys)); } @Override public Response> zunionWithScores(ZParams params, byte[]... keys) { return appendCommand(commandObjects.zunionWithScores(params, keys)); } @Override public Response zunionstore(byte[] dstkey, byte[]... sets) { return appendCommand(commandObjects.zunionstore(dstkey, sets)); } @Override public Response zunionstore(byte[] dstkey, ZParams params, byte[]... sets) { return appendCommand(commandObjects.zunionstore(dstkey, params, sets)); } @Override public Response xadd(byte[] key, XAddParams params, Map hash) { return appendCommand(commandObjects.xadd(key, params, hash)); } @Override public Response xlen(byte[] key) { return appendCommand(commandObjects.xlen(key)); } @Override public Response> xrange(byte[] key, byte[] start, byte[] end) { return appendCommand(commandObjects.xrange(key, start, end)); } @Override public Response> xrange(byte[] key, byte[] start, byte[] end, int count) { return appendCommand(commandObjects.xrange(key, start, end, count)); } @Override public Response> xrevrange(byte[] key, byte[] end, byte[] start) { return appendCommand(commandObjects.xrevrange(key, end, start)); } @Override public Response> xrevrange(byte[] key, byte[] end, byte[] start, int count) { return appendCommand(commandObjects.xrevrange(key, end, start, count)); } @Override public Response xack(byte[] key, byte[] group, byte[]... ids) { return appendCommand(commandObjects.xack(key, group, ids)); } @Override public Response> xackdel(byte[] key, byte[] group, byte[]... ids) { return appendCommand(commandObjects.xackdel(key, group, ids)); } @Override public Response> xackdel(byte[] key, byte[] group, StreamDeletionPolicy trimMode, byte[]... ids) { return appendCommand(commandObjects.xackdel(key, group, trimMode, ids)); } @Override public Response xgroupCreate(byte[] key, byte[] groupName, byte[] id, boolean makeStream) { return appendCommand(commandObjects.xgroupCreate(key, groupName, id, makeStream)); } @Override public Response xgroupSetID(byte[] key, byte[] groupName, byte[] id) { return appendCommand(commandObjects.xgroupSetID(key, groupName, id)); } @Override public Response xgroupDestroy(byte[] key, byte[] groupName) { return appendCommand(commandObjects.xgroupDestroy(key, groupName)); } @Override public Response xgroupCreateConsumer(byte[] key, byte[] groupName, byte[] consumerName) { return appendCommand(commandObjects.xgroupCreateConsumer(key, groupName, consumerName)); } @Override public Response xgroupDelConsumer(byte[] key, byte[] groupName, byte[] consumerName) { return appendCommand(commandObjects.xgroupDelConsumer(key, groupName, consumerName)); } @Override public Response xdel(byte[] key, byte[]... ids) { return appendCommand(commandObjects.xdel(key, ids)); } @Override public Response> xdelex(byte[] key, byte[]... ids) { return appendCommand(commandObjects.xdelex(key, ids)); } @Override public Response> xdelex(byte[] key, StreamDeletionPolicy trimMode, byte[]... ids) { return appendCommand(commandObjects.xdelex(key, trimMode, ids)); } @Override public Response xtrim(byte[] key, long maxLen, boolean approximateLength) { return appendCommand(commandObjects.xtrim(key, maxLen, approximateLength)); } @Override public Response xtrim(byte[] key, XTrimParams params) { return appendCommand(commandObjects.xtrim(key, params)); } @Override public Response xpending(byte[] key, byte[] groupName) { return appendCommand(commandObjects.xpending(key, groupName)); } @Override public Response> xpending(byte[] key, byte[] groupName, XPendingParams params) { return appendCommand(commandObjects.xpending(key, groupName, params)); } @Override public Response> xclaim(byte[] key, byte[] group, byte[] consumerName, long minIdleTime, XClaimParams params, byte[]... ids) { return appendCommand(commandObjects.xclaim(key, group, consumerName, minIdleTime, params, ids)); } @Override public Response> xclaimJustId(byte[] key, byte[] group, byte[] consumerName, long minIdleTime, XClaimParams params, byte[]... ids) { return appendCommand(commandObjects.xclaimJustId(key, group, consumerName, minIdleTime, params, ids)); } @Override public Response> xautoclaim(byte[] key, byte[] groupName, byte[] consumerName, long minIdleTime, byte[] start, XAutoClaimParams params) { return appendCommand(commandObjects.xautoclaim(key, groupName, consumerName, minIdleTime, start, params)); } @Override public Response> xautoclaimJustId(byte[] key, byte[] groupName, byte[] consumerName, long minIdleTime, byte[] start, XAutoClaimParams params) { return appendCommand(commandObjects.xautoclaimJustId(key, groupName, consumerName, minIdleTime, start, params)); } @Override public Response xinfoStream(byte[] key) { return appendCommand(commandObjects.xinfoStream(key)); } @Override public Response xinfoStreamFull(byte[] key) { return appendCommand(commandObjects.xinfoStreamFull(key)); } @Override public Response xinfoStreamFull(byte[] key, int count) { return appendCommand(commandObjects.xinfoStreamFull(key, count)); } @Override public Response> xinfoGroups(byte[] key) { return appendCommand(commandObjects.xinfoGroups(key)); } @Override public Response> xinfoConsumers(byte[] key, byte[] group) { return appendCommand(commandObjects.xinfoConsumers(key, group)); } /** * @deprecated As of Jedis 6.1.0, use {@link #xreadBinary(XReadParams, Map)} or * {@link #xreadBinaryAsMap(XReadParams, Map)} for type safety and better stream entry * parsing. */ @Deprecated @Override public Response> xread(XReadParams xReadParams, Map.Entry... streams) { return appendCommand(commandObjects.xread(xReadParams, streams)); } /** * @deprecated As of Jedis 6.1.0, use * {@link #xreadGroupBinary(byte[], byte[], XReadGroupParams, Map)} or * {@link #xreadGroupBinaryAsMap(byte[], byte[], XReadGroupParams, Map)} instead. */ @Deprecated @Override public Response> xreadGroup(byte[] groupName, byte[] consumer, XReadGroupParams xReadGroupParams, Map.Entry... streams) { return appendCommand(commandObjects.xreadGroup(groupName, consumer, xReadGroupParams, streams)); } @Override public Response>>> xreadBinary(XReadParams xReadParams, Map streams) { return appendCommand(commandObjects.xreadBinary(xReadParams, streams)); } @Override public Response>> xreadBinaryAsMap(XReadParams xReadParams, Map streams) { return appendCommand(commandObjects.xreadBinaryAsMap(xReadParams, streams)); } @Override public Response>>> xreadGroupBinary(byte[] groupName, byte[] consumer, XReadGroupParams xReadGroupParams, Map streams) { return appendCommand( commandObjects.xreadGroupBinary(groupName, consumer, xReadGroupParams, streams)); } @Override public Response>> xreadGroupBinaryAsMap(byte[] groupName, byte[] consumer, XReadGroupParams xReadGroupParams, Map streams) { return appendCommand( commandObjects.xreadGroupBinaryAsMap(groupName, consumer, xReadGroupParams, streams)); } @Override public Response xcfgset(byte[] key, XCfgSetParams params) { return appendCommand(commandObjects.xcfgset(key, params)); } @Override public Response set(byte[] key, byte[] value) { return appendCommand(commandObjects.set(key, value)); } @Override public Response set(byte[] key, byte[] value, SetParams params) { return appendCommand(commandObjects.set(key, value, params)); } @Override public Response get(byte[] key) { return appendCommand(commandObjects.get(key)); } @Override public Response setGet(byte[] key, byte[] value) { return appendCommand(commandObjects.setGet(key, value)); } @Override public Response setGet(byte[] key, byte[] value, SetParams params) { return appendCommand(commandObjects.setGet(key, value, params)); } @Override public Response getDel(byte[] key) { return appendCommand(commandObjects.getDel(key)); } @Override public Response getEx(byte[] key, GetExParams params) { return appendCommand(commandObjects.getEx(key, params)); } @Override public Response setbit(byte[] key, long offset, boolean value) { return appendCommand(commandObjects.setbit(key, offset, value)); } @Override public Response getbit(byte[] key, long offset) { return appendCommand(commandObjects.getbit(key, offset)); } @Override public Response setrange(byte[] key, long offset, byte[] value) { return appendCommand(commandObjects.setrange(key, offset, value)); } @Override public Response getrange(byte[] key, long startOffset, long endOffset) { return appendCommand(commandObjects.getrange(key, startOffset, endOffset)); } /** * @deprecated Use {@link PipeliningBase#setGet(byte[], byte[])}. */ @Deprecated @Override public Response getSet(byte[] key, byte[] value) { return appendCommand(commandObjects.getSet(key, value)); } /** * @deprecated Use {@link PipeliningBase#set(byte[], byte[], redis.clients.jedis.params.SetParams)} with {@link redis.clients.jedis.params.SetParams#nx()}. * Deprecated in Jedis 7.3.0. Mirrors Redis deprecation since 2.6.12. */ @Deprecated @Override public Response setnx(byte[] key, byte[] value) { return appendCommand(commandObjects.setnx(key, value)); } /** * @deprecated Use {@link PipeliningBase#set(byte[], byte[], redis.clients.jedis.params.SetParams)} with {@link redis.clients.jedis.params.SetParams#ex(long)}. * Deprecated in Jedis 7.3.0. Mirrors Redis deprecation since 2.6.12. */ @Deprecated @Override public Response setex(byte[] key, long seconds, byte[] value) { return appendCommand(commandObjects.setex(key, seconds, value)); } /** * @deprecated Use {@link PipeliningBase#set(byte[], byte[], redis.clients.jedis.params.SetParams)} with {@link redis.clients.jedis.params.SetParams#px(long)}. * Deprecated in Jedis 7.3.0. Mirrors Redis deprecation since 2.6.12. */ @Deprecated @Override public Response psetex(byte[] key, long milliseconds, byte[] value) { return appendCommand(commandObjects.psetex(key, milliseconds, value)); } @Override public Response> mget(byte[]... keys) { return appendCommand(commandObjects.mget(keys)); } @Override public Response mset(byte[]... keysvalues) { return appendCommand(commandObjects.mset(keysvalues)); } @Override public Response msetex(MSetExParams params, byte[]... keysvalues) { return appendCommand(commandObjects.msetex(params, keysvalues)); } @Override public Response msetnx(byte[]... keysvalues) { return appendCommand(commandObjects.msetnx(keysvalues)); } @Override public Response incr(byte[] key) { return appendCommand(commandObjects.incr(key)); } @Override public Response incrBy(byte[] key, long increment) { return appendCommand(commandObjects.incrBy(key, increment)); } @Override public Response incrByFloat(byte[] key, double increment) { return appendCommand(commandObjects.incrByFloat(key, increment)); } @Override public Response decr(byte[] key) { return appendCommand(commandObjects.decr(key)); } @Override public Response decrBy(byte[] key, long decrement) { return appendCommand(commandObjects.decrBy(key, decrement)); } @Override public Response append(byte[] key, byte[] value) { return appendCommand(commandObjects.append(key, value)); } /** * @deprecated Use {@link PipeliningBase#getrange(byte[], long, long)} instead. * Deprecated in Jedis 7.3.0. Mirrors Redis deprecation since 2.0.0. */ @Deprecated @Override public Response substr(byte[] key, int start, int end) { return appendCommand(commandObjects.substr(key, start, end)); } @Override public Response strlen(byte[] key) { return appendCommand(commandObjects.strlen(key)); } @Override public Response bitcount(byte[] key) { return appendCommand(commandObjects.bitcount(key)); } @Override public Response bitcount(byte[] key, long start, long end) { return appendCommand(commandObjects.bitcount(key, start, end)); } @Override public Response bitcount(byte[] key, long start, long end, BitCountOption option) { return appendCommand(commandObjects.bitcount(key, start, end, option)); } @Override public Response bitpos(byte[] key, boolean value) { return appendCommand(commandObjects.bitpos(key, value)); } @Override public Response bitpos(byte[] key, boolean value, BitPosParams params) { return appendCommand(commandObjects.bitpos(key, value, params)); } @Override public Response> bitfield(byte[] key, byte[]... arguments) { return appendCommand(commandObjects.bitfield(key, arguments)); } @Override public Response> bitfieldReadonly(byte[] key, byte[]... arguments) { return appendCommand(commandObjects.bitfieldReadonly(key, arguments)); } @Override public Response bitop(BitOP op, byte[] destKey, byte[]... srcKeys) { return appendCommand(commandObjects.bitop(op, destKey, srcKeys)); } // RediSearch commands @Override public Response ftCreate(String indexName, IndexOptions indexOptions, Schema schema) { return appendCommand(commandObjects.ftCreate(indexName, indexOptions, schema)); } @Override public Response ftCreate(String indexName, FTCreateParams createParams, Iterable schemaFields) { return appendCommand(commandObjects.ftCreate(indexName, createParams, schemaFields)); } @Override public Response ftAlter(String indexName, Schema schema) { return appendCommand(commandObjects.ftAlter(indexName, schema)); } @Override public Response ftAlter(String indexName, Iterable schemaFields) { return appendCommand(commandObjects.ftAlter(indexName, schemaFields)); } @Override public Response ftAliasAdd(String aliasName, String indexName) { return appendCommand(commandObjects.ftAliasAdd(aliasName, indexName)); } @Override public Response ftAliasUpdate(String aliasName, String indexName) { return appendCommand(commandObjects.ftAliasUpdate(aliasName, indexName)); } @Override public Response ftAliasDel(String aliasName) { return appendCommand(commandObjects.ftAliasDel(aliasName)); } @Override public Response ftDropIndex(String indexName) { return appendCommand(commandObjects.ftDropIndex(indexName)); } @Override public Response ftDropIndexDD(String indexName) { return appendCommand(commandObjects.ftDropIndexDD(indexName)); } @Override public Response ftSearch(String indexName, String query) { return appendCommand(commandObjects.ftSearch(indexName, query)); } @Override public Response ftSearch(String indexName, String query, FTSearchParams searchParams) { return appendCommand(commandObjects.ftSearch(indexName, query, searchParams)); } @Override public Response ftSearch(String indexName, Query query) { return appendCommand(commandObjects.ftSearch(indexName, query)); } @Override @Deprecated public Response ftSearch(byte[] indexName, Query query) { return appendCommand(commandObjects.ftSearch(indexName, query)); } @Override public Response ftExplain(String indexName, Query query) { return appendCommand(commandObjects.ftExplain(indexName, query)); } @Override public Response> ftExplainCLI(String indexName, Query query) { return appendCommand(commandObjects.ftExplainCLI(indexName, query)); } @Override public Response ftAggregate(String indexName, AggregationBuilder aggr) { return appendCommand(commandObjects.ftAggregate(indexName, aggr)); } @Override @Experimental public Response ftHybrid(String indexName, FTHybridParams hybridParams) { return appendCommand(commandObjects.ftHybrid(indexName, hybridParams)); } @Override public Response ftSynUpdate(String indexName, String synonymGroupId, String... terms) { return appendCommand(commandObjects.ftSynUpdate(indexName, synonymGroupId, terms)); } @Override public Response>> ftSynDump(String indexName) { return appendCommand(commandObjects.ftSynDump(indexName)); } @Override public Response ftDictAdd(String dictionary, String... terms) { return appendCommand(commandObjects.ftDictAdd(dictionary, terms)); } @Override public Response ftDictDel(String dictionary, String... terms) { return appendCommand(commandObjects.ftDictDel(dictionary, terms)); } @Override public Response> ftDictDump(String dictionary) { return appendCommand(commandObjects.ftDictDump(dictionary)); } @Override public Response ftDictAddBySampleKey(String indexName, String dictionary, String... terms) { return appendCommand(commandObjects.ftDictAddBySampleKey(indexName, dictionary, terms)); } @Override public Response ftDictDelBySampleKey(String indexName, String dictionary, String... terms) { return appendCommand(commandObjects.ftDictDelBySampleKey(indexName, dictionary, terms)); } @Override public Response> ftDictDumpBySampleKey(String indexName, String dictionary) { return appendCommand(commandObjects.ftDictDumpBySampleKey(indexName, dictionary)); } @Override public Response>> ftSpellCheck(String index, String query) { return appendCommand(commandObjects.ftSpellCheck(index, query)); } @Override public Response>> ftSpellCheck(String index, String query, FTSpellCheckParams spellCheckParams) { return appendCommand(commandObjects.ftSpellCheck(index, query, spellCheckParams)); } @Override public Response> ftInfo(String indexName) { return appendCommand(commandObjects.ftInfo(indexName)); } @Override public Response> ftTagVals(String indexName, String fieldName) { return appendCommand(commandObjects.ftTagVals(indexName, fieldName)); } @Override @Deprecated public Response> ftConfigGet(String option) { return appendCommand(commandObjects.ftConfigGet(option)); } @Override @Deprecated public Response> ftConfigGet(String indexName, String option) { return appendCommand(commandObjects.ftConfigGet(indexName, option)); } @Override @Deprecated public Response ftConfigSet(String option, String value) { return appendCommand(commandObjects.ftConfigSet(option, value)); } @Override @Deprecated public Response ftConfigSet(String indexName, String option, String value) { return appendCommand(commandObjects.ftConfigSet(indexName, option, value)); } @Override public Response ftSugAdd(String key, String string, double score) { return appendCommand(commandObjects.ftSugAdd(key, string, score)); } @Override public Response ftSugAddIncr(String key, String string, double score) { return appendCommand(commandObjects.ftSugAddIncr(key, string, score)); } @Override public Response> ftSugGet(String key, String prefix) { return appendCommand(commandObjects.ftSugGet(key, prefix)); } @Override public Response> ftSugGet(String key, String prefix, boolean fuzzy, int max) { return appendCommand(commandObjects.ftSugGet(key, prefix, fuzzy, max)); } @Override public Response> ftSugGetWithScores(String key, String prefix) { return appendCommand(commandObjects.ftSugGetWithScores(key, prefix)); } @Override public Response> ftSugGetWithScores(String key, String prefix, boolean fuzzy, int max) { return appendCommand(commandObjects.ftSugGetWithScores(key, prefix, fuzzy, max)); } @Override public Response ftSugDel(String key, String string) { return appendCommand(commandObjects.ftSugDel(key, string)); } @Override public Response ftSugLen(String key) { return appendCommand(commandObjects.ftSugLen(key)); } // RediSearch commands // RedisJSON commands @Override public Response lcs(byte[] keyA, byte[] keyB, LCSParams params) { return appendCommand(commandObjects.lcs(keyA, keyB, params)); } @Override public Response jsonSet(String key, Path2 path, Object object) { return appendCommand(commandObjects.jsonSet(key, path, object)); } @Override public Response jsonSetWithEscape(String key, Path2 path, Object object) { return appendCommand(commandObjects.jsonSetWithEscape(key, path, object)); } @Override public Response jsonSet(String key, Path path, Object object) { return appendCommand(commandObjects.jsonSet(key, path, object)); } @Override public Response jsonSet(String key, Path2 path, Object object, JsonSetParams params) { return appendCommand(commandObjects.jsonSet(key, path, object, params)); } @Override public Response jsonSetWithEscape(String key, Path2 path, Object object, JsonSetParams params) { return appendCommand(commandObjects.jsonSetWithEscape(key, path, object, params)); } @Override public Response jsonSet(String key, Path path, Object object, JsonSetParams params) { return appendCommand(commandObjects.jsonSet(key, path, object, params)); } @Override public Response jsonMerge(String key, Path2 path, Object object) { return appendCommand(commandObjects.jsonMerge(key, path, object)); } @Override public Response jsonMerge(String key, Path path, Object object) { return appendCommand(commandObjects.jsonMerge(key, path, object)); } @Override public Response jsonGet(String key) { return appendCommand(commandObjects.jsonGet(key)); } @Override public Response jsonGet(String key, Class clazz) { return appendCommand(commandObjects.jsonGet(key, clazz)); } @Override public Response jsonGet(String key, Path2... paths) { return appendCommand(commandObjects.jsonGet(key, paths)); } @Override public Response jsonGet(String key, Path... paths) { return appendCommand(commandObjects.jsonGet(key, paths)); } @Override public Response jsonGet(String key, Class clazz, Path... paths) { return appendCommand(commandObjects.jsonGet(key, clazz, paths)); } @Override public Response> jsonMGet(Path2 path, String... keys) { return appendCommand(commandObjects.jsonMGet(path, keys)); } @Override public Response> jsonMGet(Path path, Class clazz, String... keys) { return appendCommand(commandObjects.jsonMGet(path, clazz, keys)); } @Override public Response jsonDel(String key) { return appendCommand(commandObjects.jsonDel(key)); } @Override public Response jsonDel(String key, Path2 path) { return appendCommand(commandObjects.jsonDel(key, path)); } @Override public Response jsonDel(String key, Path path) { return appendCommand(commandObjects.jsonDel(key, path)); } @Override public Response jsonClear(String key) { return appendCommand(commandObjects.jsonClear(key)); } @Override public Response jsonClear(String key, Path2 path) { return appendCommand(commandObjects.jsonClear(key, path)); } @Override public Response jsonClear(String key, Path path) { return appendCommand(commandObjects.jsonClear(key, path)); } @Override public Response> jsonToggle(String key, Path2 path) { return appendCommand(commandObjects.jsonToggle(key, path)); } @Override public Response jsonToggle(String key, Path path) { return appendCommand(commandObjects.jsonToggle(key, path)); } @Override public Response> jsonType(String key) { return appendCommand(commandObjects.jsonType(key)); } @Override public Response>> jsonType(String key, Path2 path) { return appendCommand(commandObjects.jsonType(key, path)); } @Override public Response> jsonType(String key, Path path) { return appendCommand(commandObjects.jsonType(key, path)); } @Override public Response jsonStrAppend(String key, Object string) { return appendCommand(commandObjects.jsonStrAppend(key, string)); } @Override public Response> jsonStrAppend(String key, Path2 path, Object string) { return appendCommand(commandObjects.jsonStrAppend(key, path, string)); } @Override public Response jsonStrAppend(String key, Path path, Object string) { return appendCommand(commandObjects.jsonStrAppend(key, path, string)); } @Override public Response jsonStrLen(String key) { return appendCommand(commandObjects.jsonStrLen(key)); } @Override public Response> jsonStrLen(String key, Path2 path) { return appendCommand(commandObjects.jsonStrLen(key, path)); } @Override public Response jsonStrLen(String key, Path path) { return appendCommand(commandObjects.jsonStrLen(key, path)); } @Override public Response jsonNumIncrBy(String key, Path2 path, double value) { return appendCommand(commandObjects.jsonNumIncrBy(key, path, value)); } @Override public Response jsonNumIncrBy(String key, Path path, double value) { return appendCommand(commandObjects.jsonNumIncrBy(key, path, value)); } @Override public Response> jsonArrAppend(String key, Path2 path, Object... objects) { return appendCommand(commandObjects.jsonArrAppend(key, path, objects)); } @Override public Response> jsonArrAppendWithEscape(String key, Path2 path, Object... objects) { return appendCommand(commandObjects.jsonArrAppendWithEscape(key, path, objects)); } @Override public Response jsonArrAppend(String key, Path path, Object... objects) { return appendCommand(commandObjects.jsonArrAppend(key, path, objects)); } @Override public Response> jsonArrIndex(String key, Path2 path, Object scalar) { return appendCommand(commandObjects.jsonArrIndex(key, path, scalar)); } @Override public Response> jsonArrIndexWithEscape(String key, Path2 path, Object scalar) { return appendCommand(commandObjects.jsonArrIndexWithEscape(key, path, scalar)); } @Override public Response jsonArrIndex(String key, Path path, Object scalar) { return appendCommand(commandObjects.jsonArrIndex(key, path, scalar)); } @Override public Response> jsonArrInsert(String key, Path2 path, int index, Object... objects) { return appendCommand(commandObjects.jsonArrInsert(key, path, index, objects)); } @Override public Response> jsonArrInsertWithEscape(String key, Path2 path, int index, Object... objects) { return appendCommand(commandObjects.jsonArrInsertWithEscape(key, path, index, objects)); } @Override public Response jsonArrInsert(String key, Path path, int index, Object... pojos) { return appendCommand(commandObjects.jsonArrInsert(key, path, index, pojos)); } @Override public Response jsonArrPop(String key) { return appendCommand(commandObjects.jsonArrPop(key)); } @Override public Response jsonArrLen(String key, Path path) { return appendCommand(commandObjects.jsonArrLen(key, path)); } @Override public Response> jsonArrTrim(String key, Path2 path, int start, int stop) { return appendCommand(commandObjects.jsonArrTrim(key, path, start, stop)); } @Override public Response jsonArrTrim(String key, Path path, int start, int stop) { return appendCommand(commandObjects.jsonArrTrim(key, path, start, stop)); } @Override public Response jsonArrPop(String key, Class clazz, Path path) { return appendCommand(commandObjects.jsonArrPop(key, clazz, path)); } @Override public Response> jsonArrPop(String key, Path2 path, int index) { return appendCommand(commandObjects.jsonArrPop(key, path, index)); } @Override public Response jsonArrPop(String key, Path path, int index) { return appendCommand(commandObjects.jsonArrPop(key, path, index)); } @Override public Response jsonArrPop(String key, Class clazz, Path path, int index) { return appendCommand(commandObjects.jsonArrPop(key, clazz, path, index)); } @Override public Response jsonArrLen(String key) { return appendCommand(commandObjects.jsonArrLen(key)); } @Override public Response> jsonArrLen(String key, Path2 path) { return appendCommand(commandObjects.jsonArrLen(key, path)); } @Override public Response jsonArrPop(String key, Class clazz) { return appendCommand(commandObjects.jsonArrPop(key, clazz)); } @Override public Response> jsonArrPop(String key, Path2 path) { return appendCommand(commandObjects.jsonArrPop(key, path)); } @Override public Response jsonArrPop(String key, Path path) { return appendCommand(commandObjects.jsonArrPop(key, path)); } // RedisJSON commands // RedisTimeSeries commands @Override public Response tsCreate(String key) { return appendCommand(commandObjects.tsCreate(key)); } @Override public Response tsCreate(String key, TSCreateParams createParams) { return appendCommand(commandObjects.tsCreate(key, createParams)); } @Override public Response tsDel(String key, long fromTimestamp, long toTimestamp) { return appendCommand(commandObjects.tsDel(key, fromTimestamp, toTimestamp)); } @Override public Response tsAlter(String key, TSAlterParams alterParams) { return appendCommand(commandObjects.tsAlter(key, alterParams)); } @Override public Response tsAdd(String key, double value) { return appendCommand(commandObjects.tsAdd(key, value)); } @Override public Response tsAdd(String key, long timestamp, double value) { return appendCommand(commandObjects.tsAdd(key, timestamp, value)); } @Override public Response tsAdd(String key, long timestamp, double value, TSCreateParams createParams) { return appendCommand(commandObjects.tsAdd(key, timestamp, value, createParams)); } @Override public Response tsAdd(String key, long timestamp, double value, TSAddParams addParams) { return appendCommand(commandObjects.tsAdd(key, timestamp, value, addParams)); } @Override public Response> tsMAdd(Map.Entry... entries) { return appendCommand(commandObjects.tsMAdd(entries)); } @Override public Response tsIncrBy(String key, double value) { return appendCommand(commandObjects.tsIncrBy(key, value)); } @Override public Response tsIncrBy(String key, double value, long timestamp) { return appendCommand(commandObjects.tsIncrBy(key, value, timestamp)); } @Override public Response tsIncrBy(String key, double addend, TSIncrByParams incrByParams) { return appendCommand(commandObjects.tsIncrBy(key, addend, incrByParams)); } @Override public Response tsDecrBy(String key, double value) { return appendCommand(commandObjects.tsDecrBy(key, value)); } @Override public Response tsDecrBy(String key, double value, long timestamp) { return appendCommand(commandObjects.tsDecrBy(key, value, timestamp)); } @Override public Response tsDecrBy(String key, double subtrahend, TSDecrByParams decrByParams) { return appendCommand(commandObjects.tsDecrBy(key, subtrahend, decrByParams)); } @Override public Response> tsRange(String key, long fromTimestamp, long toTimestamp) { return appendCommand(commandObjects.tsRange(key, fromTimestamp, toTimestamp)); } @Override public Response> tsRange(String key, TSRangeParams rangeParams) { return appendCommand(commandObjects.tsRange(key, rangeParams)); } @Override public Response> tsRevRange(String key, long fromTimestamp, long toTimestamp) { return appendCommand(commandObjects.tsRevRange(key, fromTimestamp, toTimestamp)); } @Override public Response> tsRevRange(String key, TSRangeParams rangeParams) { return appendCommand(commandObjects.tsRevRange(key, rangeParams)); } @Override public Response> tsMRange(long fromTimestamp, long toTimestamp, String... filters) { return appendCommand(commandObjects.tsMRange(fromTimestamp, toTimestamp, filters)); } @Override public Response> tsMRange(TSMRangeParams multiRangeParams) { return appendCommand(commandObjects.tsMRange(multiRangeParams)); } @Override public Response> tsMRevRange(long fromTimestamp, long toTimestamp, String... filters) { return appendCommand(commandObjects.tsMRevRange(fromTimestamp, toTimestamp, filters)); } @Override public Response> tsMRevRange(TSMRangeParams multiRangeParams) { return appendCommand(commandObjects.tsMRevRange(multiRangeParams)); } @Override public Response tsGet(String key) { return appendCommand(commandObjects.tsGet(key)); } @Override public Response tsGet(String key, TSGetParams getParams) { return appendCommand(commandObjects.tsGet(key, getParams)); } @Override public Response> tsMGet(TSMGetParams multiGetParams, String... filters) { return appendCommand(commandObjects.tsMGet(multiGetParams, filters)); } @Override public Response tsCreateRule(String sourceKey, String destKey, AggregationType aggregationType, long timeBucket) { return appendCommand(commandObjects.tsCreateRule(sourceKey, destKey, aggregationType, timeBucket)); } @Override public Response tsCreateRule(String sourceKey, String destKey, AggregationType aggregationType, long bucketDuration, long alignTimestamp) { return appendCommand(commandObjects.tsCreateRule(sourceKey, destKey, aggregationType, bucketDuration, alignTimestamp)); } @Override public Response tsDeleteRule(String sourceKey, String destKey) { return appendCommand(commandObjects.tsDeleteRule(sourceKey, destKey)); } @Override public Response> tsQueryIndex(String... filters) { return appendCommand(commandObjects.tsQueryIndex(filters)); } @Override public Response tsInfo(String key) { return appendCommand(commandObjects.tsInfo(key)); } @Override public Response tsInfoDebug(String key) { return appendCommand(commandObjects.tsInfoDebug(key)); } // RedisTimeSeries commands // RedisBloom commands @Override public Response bfReserve(String key, double errorRate, long capacity) { return appendCommand(commandObjects.bfReserve(key, errorRate, capacity)); } @Override public Response bfReserve(String key, double errorRate, long capacity, BFReserveParams reserveParams) { return appendCommand(commandObjects.bfReserve(key, errorRate, capacity, reserveParams)); } @Override public Response bfAdd(String key, String item) { return appendCommand(commandObjects.bfAdd(key, item)); } @Override public Response> bfMAdd(String key, String... items) { return appendCommand(commandObjects.bfMAdd(key, items)); } @Override public Response> bfInsert(String key, String... items) { return appendCommand(commandObjects.bfInsert(key, items)); } @Override public Response> bfInsert(String key, BFInsertParams insertParams, String... items) { return appendCommand(commandObjects.bfInsert(key, insertParams, items)); } @Override public Response bfExists(String key, String item) { return appendCommand(commandObjects.bfExists(key, item)); } @Override public Response> bfMExists(String key, String... items) { return appendCommand(commandObjects.bfMExists(key, items)); } @Override public Response> bfScanDump(String key, long iterator) { return appendCommand(commandObjects.bfScanDump(key, iterator)); } @Override public Response bfLoadChunk(String key, long iterator, byte[] data) { return appendCommand(commandObjects.bfLoadChunk(key, iterator, data)); } @Override public Response bfCard(String key) { return appendCommand(commandObjects.bfCard(key)); } @Override public Response> bfInfo(String key) { return appendCommand(commandObjects.bfInfo(key)); } @Override public Response cfReserve(String key, long capacity) { return appendCommand(commandObjects.cfReserve(key, capacity)); } @Override public Response cfReserve(String key, long capacity, CFReserveParams reserveParams) { return appendCommand(commandObjects.cfReserve(key, capacity, reserveParams)); } @Override public Response cfAdd(String key, String item) { return appendCommand(commandObjects.cfAdd(key, item)); } @Override public Response cfAddNx(String key, String item) { return appendCommand(commandObjects.cfAddNx(key, item)); } @Override public Response> cfInsert(String key, String... items) { return appendCommand(commandObjects.cfInsert(key, items)); } @Override public Response> cfInsert(String key, CFInsertParams insertParams, String... items) { return appendCommand(commandObjects.cfInsert(key, insertParams, items)); } @Override public Response> cfInsertNx(String key, String... items) { return appendCommand(commandObjects.cfInsertNx(key, items)); } @Override public Response> cfInsertNx(String key, CFInsertParams insertParams, String... items) { return appendCommand(commandObjects.cfInsertNx(key, insertParams, items)); } @Override public Response cfExists(String key, String item) { return appendCommand(commandObjects.cfExists(key, item)); } @Override public Response> cfMExists(String key, String... items) { return appendCommand(commandObjects.cfMExists(key, items)); } @Override public Response cfDel(String key, String item) { return appendCommand(commandObjects.cfDel(key, item)); } @Override public Response cfCount(String key, String item) { return appendCommand(commandObjects.cfCount(key, item)); } @Override public Response> cfScanDump(String key, long iterator) { return appendCommand(commandObjects.cfScanDump(key, iterator)); } @Override public Response cfLoadChunk(String key, long iterator, byte[] data) { return appendCommand(commandObjects.cfLoadChunk(key, iterator, data)); } @Override public Response> cfInfo(String key) { return appendCommand(commandObjects.cfInfo(key)); } @Override public Response cmsInitByDim(String key, long width, long depth) { return appendCommand(commandObjects.cmsInitByDim(key, width, depth)); } @Override public Response cmsInitByProb(String key, double error, double probability) { return appendCommand(commandObjects.cmsInitByProb(key, error, probability)); } @Override public Response> cmsIncrBy(String key, Map itemIncrements) { return appendCommand(commandObjects.cmsIncrBy(key, itemIncrements)); } @Override public Response> cmsQuery(String key, String... items) { return appendCommand(commandObjects.cmsQuery(key, items)); } @Override public Response cmsMerge(String destKey, String... keys) { return appendCommand(commandObjects.cmsMerge(destKey, keys)); } @Override public Response cmsMerge(String destKey, Map keysAndWeights) { return appendCommand(commandObjects.cmsMerge(destKey, keysAndWeights)); } @Override public Response> cmsInfo(String key) { return appendCommand(commandObjects.cmsInfo(key)); } @Override public Response topkReserve(String key, long topk) { return appendCommand(commandObjects.topkReserve(key, topk)); } @Override public Response topkReserve(String key, long topk, long width, long depth, double decay) { return appendCommand(commandObjects.topkReserve(key, topk, width, depth, decay)); } @Override public Response> topkAdd(String key, String... items) { return appendCommand(commandObjects.topkAdd(key, items)); } @Override public Response> topkIncrBy(String key, Map itemIncrements) { return appendCommand(commandObjects.topkIncrBy(key, itemIncrements)); } @Override public Response> topkQuery(String key, String... items) { return appendCommand(commandObjects.topkQuery(key, items)); } @Override public Response> topkList(String key) { return appendCommand(commandObjects.topkList(key)); } @Override public Response> topkListWithCount(String key) { return appendCommand(commandObjects.topkListWithCount(key)); } @Override public Response> topkInfo(String key) { return appendCommand(commandObjects.topkInfo(key)); } @Override public Response tdigestCreate(String key) { return appendCommand(commandObjects.tdigestCreate(key)); } @Override public Response tdigestCreate(String key, int compression) { return appendCommand(commandObjects.tdigestCreate(key, compression)); } @Override public Response tdigestReset(String key) { return appendCommand(commandObjects.tdigestReset(key)); } @Override public Response tdigestMerge(String destinationKey, String... sourceKeys) { return appendCommand(commandObjects.tdigestMerge(destinationKey, sourceKeys)); } @Override public Response tdigestMerge(TDigestMergeParams mergeParams, String destinationKey, String... sourceKeys) { return appendCommand(commandObjects.tdigestMerge(mergeParams, destinationKey, sourceKeys)); } @Override public Response> tdigestInfo(String key) { return appendCommand(commandObjects.tdigestInfo(key)); } @Override public Response tdigestAdd(String key, double... values) { return appendCommand(commandObjects.tdigestAdd(key, values)); } @Override public Response> tdigestCDF(String key, double... values) { return appendCommand(commandObjects.tdigestCDF(key, values)); } @Override public Response> tdigestQuantile(String key, double... quantiles) { return appendCommand(commandObjects.tdigestQuantile(key, quantiles)); } @Override public Response tdigestMin(String key) { return appendCommand(commandObjects.tdigestMin(key)); } @Override public Response tdigestMax(String key) { return appendCommand(commandObjects.tdigestMax(key)); } @Override public Response tdigestTrimmedMean(String key, double lowCutQuantile, double highCutQuantile) { return appendCommand(commandObjects.tdigestTrimmedMean(key, lowCutQuantile, highCutQuantile)); } @Override public Response> tdigestRank(String key, double... values) { return appendCommand(commandObjects.tdigestRank(key, values)); } @Override public Response> tdigestRevRank(String key, double... values) { return appendCommand(commandObjects.tdigestRevRank(key, values)); } @Override public Response> tdigestByRank(String key, long... ranks) { return appendCommand(commandObjects.tdigestByRank(key, ranks)); } @Override public Response> tdigestByRevRank(String key, long... ranks) { return appendCommand(commandObjects.tdigestByRevRank(key, ranks)); } // RedisBloom commands // Vector Set commands @Override public Response vadd(String key, float[] vector, String element) { return appendCommand(commandObjects.vadd(key, vector, element)); } @Override public Response vadd(String key, float[] vector, String element, VAddParams params) { return appendCommand(commandObjects.vadd(key, vector, element, params)); } @Override public Response vaddFP32(String key, byte[] vectorBlob, String element) { return appendCommand(commandObjects.vaddFP32(key, vectorBlob, element)); } @Override public Response vaddFP32(String key, byte[] vectorBlob, String element, VAddParams params) { return appendCommand(commandObjects.vaddFP32(key, vectorBlob, element, params)); } @Override public Response vadd(String key, float[] vector, String element, int reduceDim, VAddParams params) { return appendCommand(commandObjects.vadd(key, vector, element, reduceDim, params)); } @Override public Response vaddFP32(String key, byte[] vectorBlob, String element, int reduceDim, VAddParams params) { return appendCommand(commandObjects.vaddFP32(key, vectorBlob, element, reduceDim, params)); } @Override public Response> vsim(String key, float[] vector) { return appendCommand(commandObjects.vsim(key, vector)); } @Override public Response> vsim(String key, float[] vector, VSimParams params) { return appendCommand(commandObjects.vsim(key, vector, params)); } @Override public Response> vsimWithScores(String key, float[] vector, VSimParams params) { return appendCommand(commandObjects.vsimWithScores(key, vector, params)); } @Override public Response> vsimByElement(String key, String element) { return appendCommand(commandObjects.vsimByElement(key, element)); } @Override public Response> vsimByElement(String key, String element, VSimParams params) { return appendCommand(commandObjects.vsimByElement(key, element, params)); } @Override public Response> vsimByElementWithScores(String key, String element, VSimParams params) { return appendCommand(commandObjects.vsimByElementWithScores(key, element, params)); } @Override public Response vdim(String key) { return appendCommand(commandObjects.vdim(key)); } @Override public Response vcard(String key) { return appendCommand(commandObjects.vcard(key)); } @Override public Response> vemb(String key, String element) { return appendCommand(commandObjects.vemb(key, element)); } @Override public Response vembRaw(String key, String element) { return appendCommand(commandObjects.vembRaw(key, element)); } @Override public Response vrem(String key, String element) { return appendCommand(commandObjects.vrem(key, element)); } @Override public Response>> vlinks(String key, String element) { return appendCommand(commandObjects.vlinks(key, element)); } @Override public Response>> vlinksWithScores(String key, String element) { return appendCommand(commandObjects.vlinksWithScores(key, element)); } @Override public Response vrandmember(String key) { return appendCommand(commandObjects.vrandmember(key)); } @Override public Response> vrandmember(String key, int count) { return appendCommand(commandObjects.vrandmember(key, count)); } @Override public Response vgetattr(String key, String element) { return appendCommand(commandObjects.vgetattr(key, element)); } @Override public Response vsetattr(String key, String element, String attributes) { return appendCommand(commandObjects.vsetattr(key, element, attributes)); } @Override public Response vinfo(String key) { return appendCommand(commandObjects.vinfo(key)); } // Binary vector set pipeline commands @Override public Response vadd(byte[] key, float[] vector, byte[] element) { return appendCommand(commandObjects.vadd(key, vector, element)); } @Override public Response vadd(byte[] key, float[] vector, byte[] element, VAddParams params) { return appendCommand(commandObjects.vadd(key, vector, element, params)); } @Override public Response vaddFP32(byte[] key, byte[] vectorBlob, byte[] element) { return appendCommand(commandObjects.vaddFP32(key, vectorBlob, element)); } @Override public Response vaddFP32(byte[] key, byte[] vectorBlob, byte[] element, VAddParams params) { return appendCommand(commandObjects.vaddFP32(key, vectorBlob, element, params)); } @Override public Response vadd(byte[] key, float[] vector, byte[] element, int reduceDim, VAddParams params) { return appendCommand(commandObjects.vadd(key, vector, element, reduceDim, params)); } @Override public Response vaddFP32(byte[] key, byte[] vectorBlob, byte[] element, int reduceDim, VAddParams params) { return appendCommand(commandObjects.vaddFP32(key, vectorBlob, element, reduceDim, params)); } @Override public Response> vsim(byte[] key, float[] vector) { return appendCommand(commandObjects.vsim(key, vector)); } @Override public Response> vsim(byte[] key, float[] vector, VSimParams params) { return appendCommand(commandObjects.vsim(key, vector, params)); } @Override public Response> vsimWithScores(byte[] key, float[] vector, VSimParams params) { return appendCommand(commandObjects.vsimWithScores(key, vector, params)); } @Override public Response> vsimByElement(byte[] key, byte[] element) { return appendCommand(commandObjects.vsimByElement(key, element)); } @Override public Response> vsimByElement(byte[] key, byte[] element, VSimParams params) { return appendCommand(commandObjects.vsimByElement(key, element, params)); } @Override public Response> vsimByElementWithScores(byte[] key, byte[] element, VSimParams params) { return appendCommand(commandObjects.vsimByElementWithScores(key, element, params)); } @Override public Response vdim(byte[] key) { return appendCommand(commandObjects.vdim(key)); } @Override public Response vcard(byte[] key) { return appendCommand(commandObjects.vcard(key)); } @Override public Response> vemb(byte[] key, byte[] element) { return appendCommand(commandObjects.vemb(key, element)); } @Override public Response vembRaw(byte[] key, byte[] element) { return appendCommand(commandObjects.vembRaw(key, element)); } @Override public Response vrem(byte[] key, byte[] element) { return appendCommand(commandObjects.vrem(key, element)); } @Override public Response>> vlinks(byte[] key, byte[] element) { return appendCommand(commandObjects.vlinks(key, element)); } @Override public Response>> vlinksWithScores(byte[] key, byte[] element) { return appendCommand(commandObjects.vlinksWithScores(key, element)); } @Override public Response vrandmember(byte[] key) { return appendCommand(commandObjects.vrandmember(key)); } @Override public Response> vrandmember(byte[] key, int count) { return appendCommand(commandObjects.vrandmember(key, count)); } @Override public Response vgetattr(byte[] key, byte[] element) { return appendCommand(commandObjects.vgetattr(key, element)); } @Override public Response vsetattr(byte[] key, byte[] element, byte[] attributes) { return appendCommand(commandObjects.vsetattr(key, element, attributes)); } // Vector Set pipeline commands end // Hotkeys pipeline commands public Response hotkeysStart(HotkeysParams params) { return appendCommand(commandObjects.hotkeysStart(params)); } public Response hotkeysStop() { return appendCommand(commandObjects.hotkeysStop()); } public Response hotkeysReset() { return appendCommand(commandObjects.hotkeysReset()); } public Response hotkeysGet() { return appendCommand(commandObjects.hotkeysGet()); } // Hotkeys pipeline commands end public Response sendCommand(ProtocolCommand cmd, String... args) { return sendCommand(new CommandArguments(cmd).addObjects((Object[]) args)); } public Response sendCommand(ProtocolCommand cmd, byte[]... args) { return sendCommand(new CommandArguments(cmd).addObjects((Object[]) args)); } public Response sendCommand(CommandArguments args) { return executeCommand(new CommandObject<>(args, BuilderFactory.RAW_OBJECT)); } public Response executeCommand(CommandObject command) { return appendCommand(command); } public void setJsonObjectMapper(JsonObjectMapper jsonObjectMapper) { this.commandObjects.setJsonObjectMapper(jsonObjectMapper); } } ================================================ FILE: src/main/java/redis/clients/jedis/Protocol.java ================================================ package redis.clients.jedis; import java.io.IOException; import java.nio.charset.Charset; import java.nio.charset.StandardCharsets; import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; import java.util.List; import java.util.Locale; import redis.clients.jedis.annots.Experimental; import redis.clients.jedis.exceptions.*; import redis.clients.jedis.args.Rawable; import redis.clients.jedis.commands.ProtocolCommand; import redis.clients.jedis.csc.Cache; import redis.clients.jedis.util.KeyValue; import redis.clients.jedis.util.RedisInputStream; import redis.clients.jedis.util.RedisOutputStream; import redis.clients.jedis.util.SafeEncoder; public final class Protocol { public static final String DEFAULT_HOST = "127.0.0.1"; public static final int DEFAULT_PORT = 6379; public static final int DEFAULT_SENTINEL_PORT = 26379; public static final int DEFAULT_TIMEOUT = 2000; public static final int DEFAULT_DATABASE = 0; public static final int CLUSTER_HASHSLOTS = 16384; public static final Charset CHARSET = StandardCharsets.UTF_8; public static final byte ASTERISK_BYTE = '*'; public static final byte COLON_BYTE = ':'; public static final byte COMMA_BYTE = ','; public static final byte DOLLAR_BYTE = '$'; public static final byte EQUAL_BYTE = '='; public static final byte GREATER_THAN_BYTE = '>'; public static final byte HASH_BYTE = '#'; public static final byte LEFT_BRACE_BYTE = '('; public static final byte MINUS_BYTE = '-'; public static final byte PERCENT_BYTE = '%'; public static final byte PLUS_BYTE = '+'; public static final byte TILDE_BYTE = '~'; public static final byte UNDERSCORE_BYTE = '_'; public static final byte[] BYTES_TRUE = toByteArray(1); public static final byte[] BYTES_FALSE = toByteArray(0); public static final byte[] BYTES_TILDE = SafeEncoder.encode("~"); public static final byte[] BYTES_EQUAL = SafeEncoder.encode("="); public static final byte[] BYTES_ASTERISK = SafeEncoder.encode("*"); public static final byte[] POSITIVE_INFINITY_BYTES = "+inf".getBytes(); public static final byte[] NEGATIVE_INFINITY_BYTES = "-inf".getBytes(); static final List PROTOCOL_EMPTY_MAP = Collections.unmodifiableList(new ArrayList<>(0)); private static final String ASK_PREFIX = "ASK "; private static final String MOVED_PREFIX = "MOVED "; private static final String CLUSTERDOWN_PREFIX = "CLUSTERDOWN "; private static final String BUSY_PREFIX = "BUSY "; private static final String NOSCRIPT_PREFIX = "NOSCRIPT "; private static final String NOAUTH_PREFIX = "NOAUTH"; private static final String WRONGPASS_PREFIX = "WRONGPASS"; private static final String NOPERM_PREFIX = "NOPERM"; private static final byte[] INVALIDATE_BYTES = SafeEncoder.encode("invalidate"); private Protocol() { throw new InstantiationError("Must not instantiate this class"); } public static void sendCommand(final RedisOutputStream os, CommandArguments args) { try { os.write(ASTERISK_BYTE); os.writeIntCrLf(args.size()); for (Rawable arg : args) { os.write(DOLLAR_BYTE); final byte[] bin = arg.getRaw(); os.writeIntCrLf(bin.length); os.write(bin); os.writeCrLf(); } } catch (IOException e) { throw new JedisConnectionException(e); } } private static void processError(final RedisInputStream is) { String message = is.readLine(); // TODO: I'm not sure if this is the best way to do this. // Maybe Read only first 5 bytes instead? if (message.startsWith(MOVED_PREFIX)) { String[] movedInfo = parseTargetHostAndSlot(message); throw new JedisMovedDataException(message, HostAndPort.from(movedInfo[1]), Integer.parseInt(movedInfo[0])); } else if (message.startsWith(ASK_PREFIX)) { String[] askInfo = parseTargetHostAndSlot(message); throw new JedisAskDataException(message, HostAndPort.from(askInfo[1]), Integer.parseInt(askInfo[0])); } else if (message.startsWith(CLUSTERDOWN_PREFIX)) { throw new JedisClusterException(message); } else if (message.startsWith(BUSY_PREFIX)) { throw new JedisBusyException(message); } else if (message.startsWith(NOSCRIPT_PREFIX)) { throw new JedisNoScriptException(message); } else if (message.startsWith(NOAUTH_PREFIX) || message.startsWith(WRONGPASS_PREFIX) || message.startsWith(NOPERM_PREFIX)) { throw new JedisAccessControlException(message); } throw new JedisDataException(message); } public static String readErrorLineIfPossible(RedisInputStream is) { final byte b = is.readByte(); // if buffer contains other type of response, just ignore. if (b != MINUS_BYTE) { return null; } return is.readLine(); } private static String[] parseTargetHostAndSlot(String clusterRedirectResponse) { String[] response = new String[2]; String[] messageInfo = clusterRedirectResponse.split(" "); response[0] = messageInfo[1]; response[1] = messageInfo[2]; return response; } private static Object process(final RedisInputStream is) { final byte b = is.readByte(); // System.out.println("BYTE: " + (char) b); switch (b) { case PLUS_BYTE: return is.readLineBytes(); case DOLLAR_BYTE: case EQUAL_BYTE: return processBulkReply(is); case ASTERISK_BYTE: return processMultiBulkReply(is); case UNDERSCORE_BYTE: return is.readNullCrLf(); case HASH_BYTE: return is.readBooleanCrLf(); case COLON_BYTE: return is.readLongCrLf(); case COMMA_BYTE: return is.readDoubleCrLf(); case LEFT_BRACE_BYTE: return is.readBigIntegerCrLf(); case PERCENT_BYTE: // TODO: currently just to start working with HELLO return processMapKeyValueReply(is); case TILDE_BYTE: // TODO: return processMultiBulkReply(is); case GREATER_THAN_BYTE: return processMultiBulkReply(is); case MINUS_BYTE: processError(is); return null; // TODO: Blob error '!' default: throw new JedisConnectionException("Unknown reply: " + (char) b); } } private static byte[] processBulkReply(final RedisInputStream is) { final int len = is.readIntCrLf(); if (len == -1) { return null; } final byte[] read = new byte[len]; int offset = 0; while (offset < len) { final int size = is.read(read, offset, (len - offset)); if (size == -1) { throw new JedisConnectionException("It seems like server has closed the connection."); } offset += size; } // read 2 more bytes for the command delimiter is.readByte(); is.readByte(); return read; } private static List processMultiBulkReply(final RedisInputStream is) { final int num = is.readIntCrLf(); if (num == -1) return null; final List ret = new ArrayList<>(num); for (int i = 0; i < num; i++) { try { ret.add(process(is)); } catch (JedisDataException e) { ret.add(e); } } return ret; } private static List processMapKeyValueReply(final RedisInputStream is) { final int num = is.readIntCrLf(); switch (num) { case -1: return null; case 0: return PROTOCOL_EMPTY_MAP; default: final List ret = new ArrayList<>(num); for (int i = 0; i < num; i++) { ret.add(new KeyValue(process(is), process(is))); } return ret; } } public static Object read(final RedisInputStream is) { return process(is); } @Experimental public static Object read(final RedisInputStream is, final Cache cache) { Object unhandledPush = readPushes(is, cache, false); return unhandledPush == null ? process(is) : unhandledPush; } @Experimental public static Object readPushes(final RedisInputStream is, final Cache cache, boolean onlyPendingBuffer) { Object unhandledPush = null; if (onlyPendingBuffer) { try { while (unhandledPush == null && is.available() > 0 && is.peek(GREATER_THAN_BYTE)) { unhandledPush = processPush(is, cache); } } catch (IOException e) { throw new JedisConnectionException("Failed to read pending buffer for push messages!", e); } } else { while (unhandledPush == null && is.peek(GREATER_THAN_BYTE)) { unhandledPush = processPush(is, cache); } } return unhandledPush; } private static Object processPush(final RedisInputStream is, Cache cache) { is.readByte(); List list = processMultiBulkReply(is); if (list.size() == 2 && list.get(0) instanceof byte[] && Arrays.equals(INVALIDATE_BYTES, (byte[]) list.get(0))) { cache.deleteByRedisKeys((List) list.get(1)); return null; } else { return list; } } public static final byte[] toByteArray(final boolean value) { return value ? BYTES_TRUE : BYTES_FALSE; } public static final byte[] toByteArray(final int value) { return SafeEncoder.encode(String.valueOf(value)); } public static final byte[] toByteArray(final long value) { return SafeEncoder.encode(String.valueOf(value)); } public static final byte[] toByteArray(final double value) { if (value == Double.POSITIVE_INFINITY) { return POSITIVE_INFINITY_BYTES; } else if (value == Double.NEGATIVE_INFINITY) { return NEGATIVE_INFINITY_BYTES; } else { return SafeEncoder.encode(String.valueOf(value)); } } public static enum Command implements ProtocolCommand { PING, AUTH, HELLO, SET, GET, GETDEL, GETEX, DIGEST, EXISTS, DEL, DELEX, UNLINK, TYPE, FLUSHDB, FLUSHALL, MOVE, KEYS, RANDOMKEY, RENAME, RENAMENX, DUMP, RESTORE, DBSIZE, SELECT, SWAPDB, MIGRATE, ECHO, // EXPIRE, EXPIREAT, EXPIRETIME, PEXPIRE, PEXPIREAT, PEXPIRETIME, TTL, PTTL, // <-- key expiration MULTI, DISCARD, EXEC, WATCH, UNWATCH, SORT, SORT_RO, INFO, SHUTDOWN, MONITOR, CONFIG, LCS, // GETSET, MGET, SETNX, SETEX, PSETEX, MSETEX, MSET, MSETNX, DECR, DECRBY, INCR, INCRBY, INCRBYFLOAT, STRLEN, APPEND, SUBSTR, // <-- string SETBIT, GETBIT, BITPOS, SETRANGE, GETRANGE, BITCOUNT, BITOP, BITFIELD, BITFIELD_RO, // <-- bit (string) HSET, HGET, HSETNX, HMSET, HMGET, HINCRBY, HEXISTS, HDEL, HLEN, HKEYS, HVALS, HGETALL, HSTRLEN, HEXPIRE, HPEXPIRE, HEXPIREAT, HPEXPIREAT, HTTL, HPTTL, HEXPIRETIME, HPEXPIRETIME, HPERSIST, HRANDFIELD, HINCRBYFLOAT, HSETEX, HGETEX, HGETDEL, // <-- hash RPUSH, LPUSH, LLEN, LRANGE, LTRIM, LINDEX, LSET, LREM, LPOP, RPOP, BLPOP, BRPOP, LINSERT, LPOS, RPOPLPUSH, BRPOPLPUSH, BLMOVE, LMOVE, LMPOP, BLMPOP, LPUSHX, RPUSHX, // <-- list SADD, SMEMBERS, SREM, SPOP, SMOVE, SCARD, SRANDMEMBER, SINTER, SINTERSTORE, SUNION, SUNIONSTORE, SDIFF, SDIFFSTORE, SISMEMBER, SMISMEMBER, SINTERCARD, // <-- set ZADD, ZDIFF, ZDIFFSTORE, ZRANGE, ZREM, ZINCRBY, ZRANK, ZREVRANK, ZREVRANGE, ZRANDMEMBER, ZCARD, ZSCORE, ZPOPMAX, ZPOPMIN, ZCOUNT, ZUNION, ZUNIONSTORE, ZINTER, ZINTERSTORE, ZRANGEBYSCORE, ZREVRANGEBYSCORE, ZREMRANGEBYRANK, ZREMRANGEBYSCORE, ZLEXCOUNT, ZRANGEBYLEX, ZREVRANGEBYLEX, ZREMRANGEBYLEX, ZMSCORE, ZRANGESTORE, ZINTERCARD, ZMPOP, BZMPOP, BZPOPMIN, BZPOPMAX, // <-- zset GEOADD, GEODIST, GEOHASH, GEOPOS, GEORADIUS, GEORADIUS_RO, GEOSEARCH, GEOSEARCHSTORE, GEORADIUSBYMEMBER, GEORADIUSBYMEMBER_RO, // <-- geo PFADD, PFCOUNT, PFMERGE, // <-- hyper log log XADD, XLEN, XDEL, XTRIM, XRANGE, XREVRANGE, XREAD, XACK, XGROUP, XREADGROUP, XPENDING, XCLAIM, XAUTOCLAIM, XINFO, XDELEX, XACKDEL, XCFGSET, // <-- stream EVAL, EVALSHA, SCRIPT, EVAL_RO, EVALSHA_RO, FUNCTION, FCALL, FCALL_RO, // <-- program SUBSCRIBE, UNSUBSCRIBE, PSUBSCRIBE, PUNSUBSCRIBE, PUBLISH, PUBSUB, SSUBSCRIBE, SUNSUBSCRIBE, SPUBLISH, // <-- pub sub SAVE, BGSAVE, BGREWRITEAOF, LASTSAVE, PERSIST, ROLE, FAILOVER, SLOWLOG, OBJECT, CLIENT, TIME, SCAN, HSCAN, SSCAN, ZSCAN, WAIT, CLUSTER, ASKING, READONLY, READWRITE, SLAVEOF, REPLICAOF, COPY, SENTINEL, MODULE, ACL, TOUCH, MEMORY, LOLWUT, COMMAND, RESET, LATENCY, WAITAOF, HOTKEYS, VADD, VSIM, VDIM, VCARD, VEMB, VREM, VLINKS, VRANDMEMBER, VGETATTR, VSETATTR, VINFO; // <-- vector set private final byte[] raw; private Command() { raw = SafeEncoder.encode(name()); } @Override public byte[] getRaw() { return raw; } } public static enum Keyword implements Rawable { AGGREGATE, ALPHA, BY, GET, LIMIT, NO, NOSORT, ONE, SET, STORE, WEIGHTS, WITHSCORE, WITHSCORES, RESETSTAT, REWRITE, RESET, FLUSH, EXISTS, LOAD, LEN, HELP, SCHEDULE, MATCH, COUNT, TYPE, KEYS, REFCOUNT, ENCODING, IDLETIME, FREQ, REPLACE, GETNAME, SETNAME, SETINFO, LIST, ID, KILL, PERSIST, STREAMS, CREATE, MKSTREAM, SETID, DESTROY, DELCONSUMER, MAXLEN, GROUP, IDLE, TIME, BLOCK, NOACK, CLAIM, RETRYCOUNT, STREAM, GROUPS, CONSUMERS, JUSTID, WITHVALUES, NOMKSTREAM, MINID, CREATECONSUMER, IDMPAUTO, IDMP, DURATION, MAXSIZE, SETUSER, GETUSER, DELUSER, WHOAMI, USERS, CAT, GENPASS, LOG, SAVE, DRYRUN, COPY, AUTH, AUTH2, NX, XX, IFEQ, IFNE, IFDEQ, IFDNE, EX, PX, EXAT, PXAT, ABSTTL, KEEPTTL, INCR, LT, GT, CH, INFO, PAUSE, UNPAUSE, UNBLOCK, REV, WITHCOORD, WITHDIST, WITHHASH, ANY, FROMMEMBER, FROMLONLAT, BYRADIUS, BYBOX, BYLEX, BYSCORE, STOREDIST, TO, FORCE, TIMEOUT, DB, UNLOAD, ABORT, IDX, MINMATCHLEN, WITHMATCHLEN, FULL, DELETE, LIBRARYNAME, WITHCODE, DESCRIPTION, GETKEYS, GETKEYSANDFLAGS, DOCS, FILTERBY, DUMP, MODULE, ACLCAT, PATTERN, DOCTOR, LATEST, HISTORY, USAGE, SAMPLES, PURGE, STATS, LOADEX, CONFIG, ARGS, RANK, NOW, VERSION, ADDR, SKIPME, USER, LADDR, FIELDS, CHANNELS, NUMPAT, NUMSUB, SHARDCHANNELS, SHARDNUMSUB, NOVALUES, MAXAGE, FXX, FNX, // Vector set keywords REDUCE, CAS, NOQUANT, Q8, BIN, EF, SETATTR, M, VALUES, FP32, ELE, FILTER, FILTER_EF, TRUTH, NOTHREAD, RAW, EPSILON, WITHATTRIBS, // Hotkeys keywords METRICS, SAMPLE, SLOTS, START, STOP, CPU, NET; private final byte[] raw; private Keyword() { raw = SafeEncoder.encode(name()); } @Override public byte[] getRaw() { return raw; } } public static enum SentinelKeyword implements Rawable { MYID, MASTERS, MASTER, SENTINELS, SLAVES, REPLICAS, RESET, FAILOVER, REMOVE, SET, MONITOR, GET_MASTER_ADDR_BY_NAME("GET-MASTER-ADDR-BY-NAME"); private final byte[] raw; private SentinelKeyword() { raw = SafeEncoder.encode(name()); } private SentinelKeyword(String str) { raw = SafeEncoder.encode(str); } @Override public byte[] getRaw() { return raw; } } public static enum ResponseKeyword implements Rawable { SUBSCRIBE, PSUBSCRIBE, UNSUBSCRIBE, PUNSUBSCRIBE, MESSAGE, PMESSAGE, PONG, SSUBSCRIBE, SUNSUBSCRIBE, SMESSAGE; private final byte[] raw; private ResponseKeyword() { raw = SafeEncoder.encode(name().toLowerCase(Locale.ENGLISH)); } @Override public byte[] getRaw() { return raw; } } public static enum ClusterKeyword implements Rawable { MEET, RESET, INFO, FAILOVER, SLOTS, NODES, REPLICAS, SLAVES, MYID, ADDSLOTS, DELSLOTS, GETKEYSINSLOT, SETSLOT, NODE, MIGRATING, IMPORTING, STABLE, FORGET, FLUSHSLOTS, KEYSLOT, COUNTKEYSINSLOT, SAVECONFIG, REPLICATE, LINKS, ADDSLOTSRANGE, DELSLOTSRANGE, BUMPEPOCH, MYSHARDID, SHARDS; private final byte[] raw; private ClusterKeyword() { raw = SafeEncoder.encode(name()); } @Override public byte[] getRaw() { return raw; } } } ================================================ FILE: src/main/java/redis/clients/jedis/RedisClient.java ================================================ package redis.clients.jedis; import java.net.URI; import redis.clients.jedis.builders.StandaloneClientBuilder; import redis.clients.jedis.csc.Cache; import redis.clients.jedis.executors.CommandExecutor; import redis.clients.jedis.providers.ConnectionProvider; import redis.clients.jedis.providers.PooledConnectionProvider; import redis.clients.jedis.util.JedisAsserts; import redis.clients.jedis.util.JedisURIHelper; import redis.clients.jedis.util.Pool; /** * {@code RedisClient} is the recommended client for connecting to standalone Redis deployments. *

* This class provides a modern, unified interface for interacting with Redis, supporting connection * pooling, authentication, and configuration via a fluent builder API. *

*

* {@code RedisClient} supersedes the deprecated {@link JedisPooled} and {@link UnifiedJedis} * classes, offering improved usability and extensibility. For new applications, use * {@code RedisClient} instead of the older classes. *

*

* Example usage: *

* *
 * {
 *   @code
 *   RedisClient client = RedisClient.builder().host("localhost").port(6379).build();
 * }
 * 
*

* For advanced configuration, see the {@link RedisClient.Builder} class. *

*/ public class RedisClient extends UnifiedJedis { private RedisClient(CommandExecutor commandExecutor, ConnectionProvider connectionProvider, CommandObjects commandObjects, RedisProtocol redisProtocol, Cache cache) { super(commandExecutor, connectionProvider, commandObjects, redisProtocol, cache); } /** * Creates a RedisClient with default host and port (localhost:6379). *

* This is a convenience factory method that uses the builder pattern internally. * @return a new {@link RedisClient} instance */ public static RedisClient create() { return builder().build(); } /** * Creates a RedisClient with the specified host and port. *

* This is a convenience factory method that uses the builder pattern internally. * @param host the Redis server hostname * @param port the Redis server port * @return a new {@link RedisClient} instance */ public static RedisClient create(final String host, final int port) { return builder().hostAndPort(host, port).build(); } /** * Creates a RedisClient with the specified host and port. *

* This is a convenience factory method that uses the builder pattern internally. * @param hostAndPort the Redis server host and port * @return a new {@link RedisClient} instance */ public static RedisClient create(final HostAndPort hostAndPort) { return builder().hostAndPort(hostAndPort).build(); } /** * Creates a RedisClient with the specified host, port, user, and password. *

* This is a convenience factory method that uses the builder pattern internally. * @param host the Redis server hostname * @param port the Redis server port * @param user the username for authentication * @param password the password for authentication * @return a new {@link RedisClient} instance */ public static RedisClient create(final String host, final int port, final String user, final String password) { return builder().hostAndPort(host, port) .clientConfig(DefaultJedisClientConfig.builder().user(user).password(password).build()) .build(); } /** * Creates a RedisClient from a Redis URI. *

* The URI must be in the format: {@code redis[s]://[[user][:password]@]host[:port][/database]} *

* Examples: *

    *
  • {@code redis://localhost:6379}
  • *
  • {@code redis://user:password@localhost:6379/0}
  • *
  • {@code rediss://localhost:6380} (SSL)
  • *
*

* Note: To connect using just a hostname and port without a URI, use * {@link #create(String, int)} instead. *

* This is a convenience factory method that uses the builder pattern internally. * @param url Redis URI string (not just a hostname) * @return a new {@link RedisClient} instance * @throws IllegalArgumentException if the URI format is invalid * @see JedisURIHelper#isValid(java.net.URI) */ public static RedisClient create(final String url) { return create(URI.create(url)); } /** * Creates a RedisClient from a Redis URI. *

* This is a convenience factory method that uses the builder pattern internally. * @param uri the Redis server URI * @return a new {@link RedisClient} instance */ public static RedisClient create(final URI uri) { JedisAsserts.notNull(uri, "Redis URI must not be null"); JedisAsserts.isTrue(JedisURIHelper.isValid(uri), "Invalid Redis URI"); JedisClientConfig clientConfig = DefaultJedisClientConfig.builder(uri).build(); HostAndPort hostAndPort = JedisURIHelper.getHostAndPort(uri); return builder().hostAndPort(hostAndPort).clientConfig(clientConfig).build(); } /** * Fluent builder for {@link RedisClient} (standalone). *

* Obtain an instance via {@link #builder()}. *

*/ public static class Builder extends StandaloneClientBuilder { @Override protected RedisClient createClient() { return new RedisClient(commandExecutor, connectionProvider, commandObjects, clientConfig.getRedisProtocol(), cache); } } /** * Create a new builder for configuring RedisClient instances. * @return a new {@link RedisClient.Builder} instance */ public static Builder builder() { return new Builder(); } public final Pool getPool() { return ((PooledConnectionProvider) provider).getPool(); } @Override public Pipeline pipelined() { return (Pipeline) super.pipelined(); } } ================================================ FILE: src/main/java/redis/clients/jedis/RedisClusterClient.java ================================================ package redis.clients.jedis; import java.time.Duration; import java.util.Collections; import java.util.List; import java.util.Map; import java.util.Set; import redis.clients.jedis.builders.ClusterClientBuilder; import redis.clients.jedis.executors.ClusterCommandExecutor; import redis.clients.jedis.executors.CommandExecutor; import redis.clients.jedis.params.MSetExParams; import redis.clients.jedis.providers.ClusterConnectionProvider; import redis.clients.jedis.csc.Cache; import redis.clients.jedis.providers.ConnectionProvider; import redis.clients.jedis.util.JedisClusterCRC16; // @formatter:off /** * RedisClusterClient provides a high-level, unified interface for interacting with a Redis Cluster. *

* This class is intended as a modern replacement for the deprecated {@code JedisCluster} class. It * supports all cluster operations and is designed to work seamlessly with the {@link UnifiedJedis} * API, allowing for consistent usage patterns across standalone, sentinel, and cluster deployments. *

* Usage: * *

{@code
 *   Set clusterNodes = new HashSet<>();
 *   clusterNodes.add(new HostAndPort("127.0.0.1", 7000));
 *   RedisClusterClient client = RedisClusterClient.create(clusterNodes);
 *   client.set("key", "value");
 *   String value = client.get("key");
 * }
*

* Migration: Users of {@code JedisCluster} are encouraged to migrate to this class for * improved API consistency, better resource management, and enhanced support for future Redis * features. *

* Thread-safety: This client is thread-safe and can be shared across multiple threads. *

* Configuration: Use the {@link #builder()} method for advanced configuration, or the * {@link #create(HostAndPort)} and {@link #create(Set)} factory methods for simple use cases. */ // @formatter:on public class RedisClusterClient extends UnifiedJedis { public static final String INIT_NO_ERROR_PROPERTY = "jedis.cluster.initNoError"; /** * Default timeout in milliseconds. */ public static final int DEFAULT_TIMEOUT = 2000; /** * Default amount of attempts for executing a command */ public static final int DEFAULT_MAX_ATTEMPTS = 5; private final CommandFlagsRegistry commandFlagsRegistry; private RedisClusterClient(CommandExecutor commandExecutor, ConnectionProvider connectionProvider, CommandObjects commandObjects, RedisProtocol redisProtocol, Cache cache, CommandFlagsRegistry commandFlagsRegistry) { super(commandExecutor, connectionProvider, commandObjects, redisProtocol, cache); this.commandFlagsRegistry = commandFlagsRegistry; } /** * Creates a RedisClusterClient instance. The provided node is used to make the first contact with * the cluster. *

* Here, the default timeout of {@value redis.clients.jedis.RedisClusterClient#DEFAULT_TIMEOUT} ms * is being used with {@value redis.clients.jedis.RedisClusterClient#DEFAULT_MAX_ATTEMPTS} maximum * attempts. *

* This is a convenience factory method that uses the builder pattern internally. * @param node Node to first connect to. * @return a new {@link RedisClusterClient} instance */ public static RedisClusterClient create(HostAndPort node) { return builder().nodes(Collections.singleton(node)) .clientConfig(DefaultJedisClientConfig.builder().timeoutMillis(DEFAULT_TIMEOUT).build()) .maxAttempts(DEFAULT_MAX_ATTEMPTS) .maxTotalRetriesDuration(Duration.ofMillis((long) DEFAULT_TIMEOUT * DEFAULT_MAX_ATTEMPTS)) .build(); } /** * Creates a RedisClusterClient with multiple entry points. *

* Here, the default timeout of {@value redis.clients.jedis.RedisClusterClient#DEFAULT_TIMEOUT} ms * is being used with {@value redis.clients.jedis.RedisClusterClient#DEFAULT_MAX_ATTEMPTS} maximum * attempts. *

* This is a convenience factory method that uses the builder pattern internally. * @param nodes Nodes to connect to. * @return a new {@link RedisClusterClient} instance */ public static RedisClusterClient create(Set nodes) { return builder().nodes(nodes) .clientConfig(DefaultJedisClientConfig.builder().timeoutMillis(DEFAULT_TIMEOUT).build()) .maxAttempts(DEFAULT_MAX_ATTEMPTS) .maxTotalRetriesDuration(Duration.ofMillis((long) DEFAULT_TIMEOUT * DEFAULT_MAX_ATTEMPTS)) .build(); } /** * Creates a RedisClusterClient with multiple entry points and authentication. *

* Here, the default timeout of {@value redis.clients.jedis.Protocol#DEFAULT_TIMEOUT} ms is being * used with {@value redis.clients.jedis.RedisClusterClient#DEFAULT_MAX_ATTEMPTS} maximum * attempts. *

* This is a convenience factory method that uses the builder pattern internally. * @param nodes Nodes to connect to. * @param user Username for authentication. * @param password Password for authentication. * @return a new {@link RedisClusterClient} instance */ public static RedisClusterClient create(Set nodes, String user, String password) { return builder().nodes(nodes) .clientConfig(DefaultJedisClientConfig.builder().user(user).password(password).build()) .maxAttempts(DEFAULT_MAX_ATTEMPTS).maxTotalRetriesDuration( Duration.ofMillis((long) Protocol.DEFAULT_TIMEOUT * DEFAULT_MAX_ATTEMPTS)) .build(); } /** * Fluent builder for {@link RedisClusterClient} (Redis Cluster). *

* Obtain an instance via {@link #builder()}. *

*/ public static class Builder extends ClusterClientBuilder { @Override protected RedisClusterClient createClient() { return new RedisClusterClient(commandExecutor, connectionProvider, commandObjects, clientConfig.getRedisProtocol(), cache, getCommandFlags()); } } /** * Create a new builder for configuring RedisClusterClient instances. * @return a new {@link RedisClusterClient.Builder} instance */ public static Builder builder() { return new Builder(); } /** * Returns all nodes that were configured to connect to in key-value pairs ({@link Map}).
* Key is the HOST:PORT and the value is the connection pool. * @return the map of all connections. */ public Map getClusterNodes() { return ((ClusterConnectionProvider) provider).getNodes(); } /** * Returns the connection for one of the 16,384 slots. * @param slot the slot to retrieve the connection for. * @return connection of the provided slot. {@code close()} of this connection must be called * after use. */ public Connection getConnectionFromSlot(int slot) { return ((ClusterConnectionProvider) provider).getConnectionFromSlot(slot); } // commands public long spublish(String channel, String message) { return executeCommand(commandObjects.spublish(channel, message)); } public long spublish(byte[] channel, byte[] message) { return executeCommand(commandObjects.spublish(channel, message)); } public void ssubscribe(final JedisShardedPubSub jedisPubSub, final String... channels) { try (Connection connection = getConnectionFromSlot(JedisClusterCRC16.getSlot(channels[0]))) { jedisPubSub.proceed(connection, channels); } } public void ssubscribe(BinaryJedisShardedPubSub jedisPubSub, final byte[]... channels) { try (Connection connection = getConnectionFromSlot(JedisClusterCRC16.getSlot(channels[0]))) { jedisPubSub.proceed(connection, channels); } } // commands @Override public ClusterPipeline pipelined() { return new ClusterPipeline((ClusterConnectionProvider) provider, (ClusterCommandObjects) commandObjects, commandFlagsRegistry); } /** * @param doMulti param * @return nothing * @throws UnsupportedOperationException */ @Override public AbstractTransaction transaction(boolean doMulti) { throw new UnsupportedOperationException(); } public final T executeCommandToReplica(CommandObject commandObject) { if (!(executor instanceof ClusterCommandExecutor)) { throw new UnsupportedOperationException( "Support only execute to replica in ClusterCommandExecutor"); } return ((ClusterCommandExecutor) executor).executeCommandToReplica(commandObject); } /** * Broadcast a command to all primary nodes in the cluster. *

* This method is useful for administrative commands that need to be executed on all primary nodes, * such as {@code PING}, {@code CONFIG SET}, {@code FLUSHALL}, etc. *

* @param commandObject the command to broadcast * @param the return type of the command * @return the aggregated reply from all primary nodes * @throws UnsupportedOperationException if the executor is not a ClusterCommandExecutor */ public final T broadcastCommand(CommandObject commandObject) { if (!(executor instanceof ClusterCommandExecutor)) { throw new UnsupportedOperationException( "Broadcast command is only supported in ClusterCommandExecutor"); } return ((ClusterCommandExecutor) executor).broadcastCommand(commandObject, true); } // ==================== Multi-Shard Command Methods ==================== // These methods execute commands across multiple Redis cluster shards when keys // hash to different slots, aggregating the results appropriately. private T executeMultiShardCommand(List> commandObjects) { if (!(executor instanceof ClusterCommandExecutor)) { throw new UnsupportedOperationException( "Multi-shard command is only supported in ClusterCommandExecutor"); } return ((ClusterCommandExecutor) executor).executeMultiShardCommand(commandObjects); } /** * {@inheritDoc} *

* This override automatically splits the keys by hash slot and executes DEL on each shard, * aggregating the results (sum of deleted keys). *

*/ @Override public long del(String... keys) { return executeMultiShardCommand(getClusterCommandObjects().delMultiShard(keys)); } /** * {@inheritDoc} *

* This override automatically splits the keys by hash slot and executes DEL on each shard, * aggregating the results (sum of deleted keys). *

*/ @Override public long del(byte[]... keys) { return executeMultiShardCommand(getClusterCommandObjects().delMultiShard(keys)); } /** * {@inheritDoc} *

* This override automatically splits the keys by hash slot and executes EXISTS on each shard, * aggregating the results (sum of existing keys). *

*/ @Override public long exists(String... keys) { return executeMultiShardCommand(getClusterCommandObjects().existsMultiShard(keys)); } /** * {@inheritDoc} *

* This override automatically splits the keys by hash slot and executes EXISTS on each shard, * aggregating the results (sum of existing keys). *

*/ @Override public long exists(byte[]... keys) { return executeMultiShardCommand(getClusterCommandObjects().existsMultiShard(keys)); } /** * {@inheritDoc} *

* This override automatically splits the keys by hash slot and executes MGET on each shard, * concatenating the results. *

* *

Order Guarantee: The returned values are in the same order as the input keys. * Each {@code values.get(i)} corresponds to {@code keys[i]}.

* *

Performance Tip: For better performance, pre-sort keys by hash slot before calling * this method. This minimizes the number of separate Redis commands by allowing consecutive * keys with the same slot to be batched together. Example:

*
{@code
   * // Sort keys by hash slot for optimal batching
   * String[] sortedKeys = Arrays.stream(keys)
   *     .sorted(Comparator.comparingInt(JedisClusterCRC16::getSlot))
   *     .toArray(String[]::new);
   * List values = client.mget(sortedKeys);
   * }
*/ @Override public List mget(String... keys) { return executeMultiShardCommand(getClusterCommandObjects().mgetMultiShard(keys)); } /** * {@inheritDoc} *

* This override automatically splits the keys by hash slot and executes MGET on each shard, * concatenating the results. *

* *

Order Guarantee: The returned values are in the same order as the input keys. * Each {@code values.get(i)} corresponds to {@code keys[i]}.

* *

Performance Tip: For better performance, pre-sort keys by hash slot before calling * this method. This minimizes the number of separate Redis commands by allowing consecutive * keys with the same slot to be batched together. Example:

*
{@code
   * // Sort keys by hash slot for optimal batching
   * byte[][] sortedKeys = Arrays.stream(keys)
   *     .sorted(Comparator.comparingInt(JedisClusterCRC16::getSlot))
   *     .toArray(byte[][]::new);
   * List values = client.mget(sortedKeys);
   * }
*/ @Override public List mget(byte[]... keys) { return executeMultiShardCommand(getClusterCommandObjects().mgetMultiShard(keys)); } /** * {@inheritDoc} *

* This override automatically splits the key-value pairs by hash slot and executes MSET * on each shard. *

*/ @Override public String mset(String... keysvalues) { return executeMultiShardCommand(getClusterCommandObjects().msetMultiShard(keysvalues)); } /** * {@inheritDoc} *

* This override automatically splits the key-value pairs by hash slot and executes MSET * on each shard. *

*/ @Override public String mset(byte[]... keysvalues) { return executeMultiShardCommand(getClusterCommandObjects().msetMultiShard(keysvalues)); } /** * {@inheritDoc} *

* This override automatically splits the keys by hash slot and executes TOUCH on each shard, * aggregating the results (sum of touched keys). *

*/ @Override public long touch(String... keys) { return executeMultiShardCommand(getClusterCommandObjects().touchMultiShard(keys)); } /** * {@inheritDoc} *

* This override automatically splits the keys by hash slot and executes TOUCH on each shard, * aggregating the results (sum of touched keys). *

*/ @Override public long touch(byte[]... keys) { return executeMultiShardCommand(getClusterCommandObjects().touchMultiShard(keys)); } /** * {@inheritDoc} *

* This override automatically splits the keys by hash slot and executes UNLINK on each shard, * aggregating the results (sum of unlinked keys). *

*/ @Override public long unlink(String... keys) { return executeMultiShardCommand(getClusterCommandObjects().unlinkMultiShard(keys)); } /** * {@inheritDoc} *

* This override automatically splits the keys by hash slot and executes UNLINK on each shard, * aggregating the results (sum of unlinked keys). *

*/ @Override public long unlink(byte[]... keys) { return executeMultiShardCommand(getClusterCommandObjects().unlinkMultiShard(keys)); } /** * {@inheritDoc} *

* This override automatically splits the key-value pairs by hash slot and executes MSETEX * on each shard with the provided parameters. *

*/ @Override public boolean msetex(MSetExParams params, String... keysvalues) { return executeMultiShardCommand(getClusterCommandObjects().msetexMultiShard(params, keysvalues)); } /** * {@inheritDoc} *

* This override automatically splits the key-value pairs by hash slot and executes MSETEX * on each shard with the provided parameters. *

*/ @Override public boolean msetex(MSetExParams params, byte[]... keysvalues) { return executeMultiShardCommand(getClusterCommandObjects().msetexMultiShard(params, keysvalues)); } private ClusterCommandObjects getClusterCommandObjects() { return (ClusterCommandObjects) commandObjects; } } ================================================ FILE: src/main/java/redis/clients/jedis/RedisCredentials.java ================================================ package redis.clients.jedis; public interface RedisCredentials { /** * @return Redis ACL user */ default String getUser() { return null; } default char[] getPassword() { return null; } } ================================================ FILE: src/main/java/redis/clients/jedis/RedisCredentialsProvider.java ================================================ package redis.clients.jedis; import java.util.function.Supplier; public interface RedisCredentialsProvider extends Supplier { /** * Prepare {@link RedisCredentials} before {@link RedisCredentialsProvider#get()} is called. *

* An application may: *

    *
  • Load credentials from the credentials management system
  • *
  • Reload credentials when credentials are rotated.
  • *
  • Reload credentials after an authentication error (e.g. NOAUTH, WRONGPASS, etc).
  • *
  • Minimize the time that the password lives in the memory (in combination with * {@link RedisCredentialsProvider#cleanUp()}).
  • *
*/ default void prepare() { } /** * Clean up credentials (e.g. from memory). * * @see RedisCredentialsProvider#prepare() */ default void cleanUp() { } } ================================================ FILE: src/main/java/redis/clients/jedis/RedisProtocol.java ================================================ package redis.clients.jedis; public enum RedisProtocol { RESP2("2"), RESP3("3"); private final String version; private RedisProtocol(String ver) { this.version = ver; } public String version() { return version; } } ================================================ FILE: src/main/java/redis/clients/jedis/RedisSentinelClient.java ================================================ package redis.clients.jedis; import redis.clients.jedis.builders.SentinelClientBuilder; import redis.clients.jedis.csc.Cache; import redis.clients.jedis.executors.CommandExecutor; import redis.clients.jedis.providers.ConnectionProvider; import redis.clients.jedis.providers.SentineledConnectionProvider; // @formatter:off /** * A high-level client for interacting with Redis Sentinel-managed Redis deployments. *

* {@code RedisSentinelClient} provides robust support for automatic master failover, connection * management, and command execution in environments where Redis Sentinel is used to monitor and * manage Redis servers. *

*

* Usage: *

* *
 * RedisSentinelClient client = RedisSentinelClient.builder().sentinel("localhost", 26379)
 *     .masterName("mymaster").build();
 * 
*

* Relationship to {@code JedisSentineled}:

*
    *
  • {@code RedisSentinelClient} is the recommended replacement for the deprecated * {@code JedisSentineled} class.
  • *
  • It offers improved API consistency, better failover handling, and a fluent builder for * configuration.
  • *
  • Use {@code RedisSentinelClient} for new codebases and when migrating from * {@code JedisSentineled}.
  • *
*/ // @formatter:on public class RedisSentinelClient extends UnifiedJedis { private RedisSentinelClient(CommandExecutor commandExecutor, ConnectionProvider connectionProvider, CommandObjects commandObjects, RedisProtocol redisProtocol, Cache cache) { super(commandExecutor, connectionProvider, commandObjects, redisProtocol, cache); } /** * Fluent builder for {@link RedisSentinelClient} (Redis Sentinel). *

* Obtain an instance via {@link #builder()}. *

*/ public static class Builder extends SentinelClientBuilder { @Override protected RedisSentinelClient createClient() { return new RedisSentinelClient(commandExecutor, connectionProvider, commandObjects, clientConfig.getRedisProtocol(), cache); } } /** * Create a new builder for configuring RedisSentinelClient instances. * @return a new {@link RedisSentinelClient.Builder} instance */ public static Builder builder() { return new Builder(); } public HostAndPort getCurrentMaster() { return ((SentineledConnectionProvider) provider).getCurrentMaster(); } @Override public Pipeline pipelined() { return (Pipeline) super.pipelined(); } } ================================================ FILE: src/main/java/redis/clients/jedis/ReliableTransaction.java ================================================ package redis.clients.jedis; import static redis.clients.jedis.Protocol.Command.DISCARD; import static redis.clients.jedis.Protocol.Command.EXEC; import static redis.clients.jedis.Protocol.Command.MULTI; import static redis.clients.jedis.Protocol.Command.UNWATCH; import java.util.ArrayList; import java.util.LinkedList; import java.util.List; import java.util.Queue; import redis.clients.jedis.exceptions.JedisConnectionException; import redis.clients.jedis.exceptions.JedisDataException; import redis.clients.jedis.exceptions.JedisException; /** * A transaction where commands are immediately sent to Redis server and the {@code QUEUED} reply checked. */ public class ReliableTransaction extends AbstractTransaction { private static final String QUEUED_STR = "QUEUED"; private final Queue> pipelinedResponses = new LinkedList<>(); protected final Connection connection; private final boolean closeConnection; private boolean broken = false; private boolean inWatch = false; private boolean inMulti = false; /** * Creates a new transaction. * * A MULTI command will be executed. WATCH/UNWATCH/MULTI commands must not be called with this object. * @param connection connection */ public ReliableTransaction(Connection connection) { this(connection, true); } /** * Creates a new transaction. * * A user wanting to WATCH/UNWATCH keys followed by a call to MULTI ({@link #multi()}) it should * be {@code doMulti=false}. * * @param connection connection * @param doMulti {@code false} should be set to enable manual WATCH, UNWATCH and MULTI */ public ReliableTransaction(Connection connection, boolean doMulti) { this(connection, doMulti, false); } /** * Creates a new transaction. * * A user wanting to WATCH/UNWATCH keys followed by a call to MULTI ({@link #multi()}) it should * be {@code doMulti=false}. * * @param connection connection * @param doMulti {@code false} should be set to enable manual WATCH, UNWATCH and MULTI * @param closeConnection should the 'connection' be closed when 'close()' is called? */ public ReliableTransaction(Connection connection, boolean doMulti, boolean closeConnection) { this(connection, doMulti, closeConnection, createCommandObjects(connection)); } /** * Creates a new transaction. * * A user wanting to WATCH/UNWATCH keys followed by a call to MULTI ({@link #multi()}) it should * be {@code doMulti=false}. * * @param connection connection * @param commandObjects command objects * @param doMulti {@code false} should be set to enable manual WATCH, UNWATCH and MULTI * @param closeConnection should the 'connection' be closed when 'close()' is called? */ ReliableTransaction(Connection connection, boolean doMulti, boolean closeConnection, CommandObjects commandObjects) { super(commandObjects); this.connection = connection; this.closeConnection = closeConnection; if (doMulti) multi(); } private static CommandObjects createCommandObjects(Connection connection) { CommandObjects commandObjects = new CommandObjects(); RedisProtocol proto = connection.getRedisProtocol(); if (proto != null) commandObjects.setProtocol(proto); return commandObjects; } @Override public final void multi() { connection.sendCommand(MULTI); String status = connection.getStatusCodeReply(); if (!"OK".equals(status)) { throw new JedisException("MULTI command failed. Received response: " + status); } inMulti = true; } @Override public String watch(final String... keys) { String status = connection.executeCommand(commandObjects.watch(keys)); inWatch = true; return status; } @Override public String watch(final byte[]... keys) { String status = connection.executeCommand(commandObjects.watch(keys)); inWatch = true; return status; } @Override public String unwatch() { connection.sendCommand(UNWATCH); String status = connection.getStatusCodeReply(); inWatch = false; return status; } @Override protected final Response appendCommand(CommandObject commandObject) { connection.sendCommand(commandObject.getArguments()); String status = connection.getStatusCodeReply(); if (!QUEUED_STR.equals(status)) { throw new JedisException(status); } Response response = new Response<>(commandObject.getBuilder()); pipelinedResponses.add(response); return response; } @Override public final void close() { try { clear(); } finally { if (closeConnection) { connection.close(); } } } @Deprecated // TODO: private public final void clear() { if (broken) { return; } if (inMulti) { discard(); } else if (inWatch) { unwatch(); } } @Override public List exec() { if (!inMulti) { throw new IllegalStateException("EXEC without MULTI"); } try { // processPipelinedResponses(pipelinedResponses.size()); // do nothing connection.sendCommand(EXEC); List unformatted = connection.getObjectMultiBulkReply(); if (unformatted == null) { pipelinedResponses.clear(); return null; } List formatted = new ArrayList<>(unformatted.size()); for (Object o : unformatted) { try { Response response = pipelinedResponses.poll(); response.set(o); formatted.add(response.get()); } catch (JedisDataException e) { formatted.add(e); } } return formatted; } catch (JedisConnectionException jce) { broken = true; throw jce; } finally { inMulti = false; inWatch = false; pipelinedResponses.clear(); } } @Override public String discard() { if (!inMulti) { throw new IllegalStateException("DISCARD without MULTI"); } try { // processPipelinedResponses(pipelinedResponses.size()); // do nothing connection.sendCommand(DISCARD); String status = connection.getStatusCodeReply(); if (!"OK".equals(status)) { throw new JedisException("DISCARD command failed. Received response: " + status); } return status; } catch (JedisConnectionException jce) { broken = true; throw jce; } finally { inMulti = false; inWatch = false; pipelinedResponses.clear(); } } } ================================================ FILE: src/main/java/redis/clients/jedis/Response.java ================================================ package redis.clients.jedis; import java.util.function.Supplier; import redis.clients.jedis.exceptions.JedisDataException; public class Response implements Supplier { protected T response = null; protected JedisDataException exception = null; private boolean building = false; private boolean built = false; private boolean set = false; private Builder builder; private Object data; private Response dependency = null; public Response(Builder b) { this.builder = b; } public void set(Object data) { this.data = data; set = true; } @Override public T get() { // if response has dependency response and dependency is not built, build it first and no more!! if (dependency != null && dependency.set && !dependency.built) { dependency.build(); } if (!set) { throw new IllegalStateException( "Please close pipeline or multi block before calling this method."); } if (!built) { build(); } if (exception != null) { throw exception; } return response; } public void setDependency(Response dependency) { this.dependency = dependency; } private void build() { // check build state to prevent recursion if (building) { return; } building = true; try { if (data != null) { if (data instanceof JedisDataException) { exception = (JedisDataException) data; } else { response = builder.build(data); } } data = null; } finally { building = false; built = true; } } @Override public String toString() { return "Response " + builder.toString(); } static class DecodedResponse extends Response { private DecodedResponse(T response, JedisDataException exception) { super(null); this.exception = exception; this.response = response; } @Override public T get() { if (exception != null) { throw exception; } return response; } @Override public String toString() { return "Response"; } } static Response of(T response) { return new DecodedResponse<>(response, null); } static Response error(JedisDataException exception) { return new DecodedResponse<>(null, exception); } } ================================================ FILE: src/main/java/redis/clients/jedis/SSLSocketWrapper.java ================================================ package redis.clients.jedis; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.net.InetAddress; import java.net.Socket; import java.net.SocketAddress; import java.net.SocketException; import java.util.function.BiFunction; import java.util.List; import javax.net.ssl.HandshakeCompletedListener; import javax.net.ssl.SSLParameters; import javax.net.ssl.SSLSession; import javax.net.ssl.SSLSocket; public class SSLSocketWrapper extends SSLSocket { SSLSocket actual; Socket underlying; InputStream wrapper; private class InputStreamWrapper extends InputStream { private InputStream actual; private InputStream underlying; public InputStreamWrapper(InputStream actual, InputStream underlying) { this.actual = actual; this.underlying = underlying; } @Override public int read() throws IOException { return actual.read(); } @Override public int read(byte[] b) throws IOException { return actual.read(b); } @Override public int read(byte[] b, int off, int len) throws IOException { return actual.read(b, off, len); } @Override public long skip(long n) throws IOException { return actual.skip(n); } @Override public int available() throws IOException { return underlying.available(); } @Override public void close() throws IOException { actual.close(); } @Override public synchronized void mark(int readlimit) { actual.mark(readlimit); } @Override public synchronized void reset() throws IOException { actual.reset(); } @Override public boolean markSupported() { return actual.markSupported(); } } public SSLSocketWrapper(SSLSocket actual, Socket underlying) throws IOException { this.actual = actual; this.underlying = underlying; this.wrapper = new InputStreamWrapper(actual.getInputStream(), underlying.getInputStream()); } @Override public void connect(SocketAddress endpoint) throws IOException { actual.connect(endpoint); } @Override public void connect(SocketAddress endpoint, int timeout) throws IOException { actual.connect(endpoint, timeout); } @Override public void bind(SocketAddress bindpoint) throws IOException { actual.bind(bindpoint); } @Override public InetAddress getInetAddress() { return actual.getInetAddress(); } @Override public InetAddress getLocalAddress() { return actual.getLocalAddress(); } @Override public int getPort() { return actual.getPort(); } @Override public int getLocalPort() { return actual.getLocalPort(); } @Override public SocketAddress getRemoteSocketAddress() { return actual.getRemoteSocketAddress(); } @Override public SocketAddress getLocalSocketAddress() { return actual.getLocalSocketAddress(); } @Override public void setTcpNoDelay(boolean on) throws SocketException { actual.setTcpNoDelay(on); } @Override public boolean getTcpNoDelay() throws SocketException { return actual.getTcpNoDelay(); } @Override public void setSoLinger(boolean on, int linger) throws SocketException { actual.setSoLinger(on, linger); } @Override public int getSoLinger() throws SocketException { return actual.getSoLinger(); } @Override public void sendUrgentData(int data) throws IOException { actual.sendUrgentData(data); } @Override public void setOOBInline(boolean on) throws SocketException { actual.setOOBInline(on); } @Override public boolean getOOBInline() throws SocketException { return actual.getOOBInline(); } @Override public synchronized void setSoTimeout(int timeout) throws SocketException { actual.setSoTimeout(timeout); } @Override public synchronized int getSoTimeout() throws SocketException { return actual.getSoTimeout(); } @Override public synchronized void setSendBufferSize(int size) throws SocketException { actual.setSendBufferSize(size); } @Override public synchronized int getSendBufferSize() throws SocketException { return actual.getSendBufferSize(); } @Override public synchronized void setReceiveBufferSize(int size) throws SocketException { actual.setReceiveBufferSize(size); } @Override public synchronized int getReceiveBufferSize() throws SocketException { return actual.getReceiveBufferSize(); } @Override public void setKeepAlive(boolean on) throws SocketException { actual.setKeepAlive(on); } @Override public boolean getKeepAlive() throws SocketException { return actual.getKeepAlive(); } @Override public void setTrafficClass(int tc) throws SocketException { actual.setTrafficClass(tc); } @Override public int getTrafficClass() throws SocketException { return actual.getTrafficClass(); } @Override public void setReuseAddress(boolean on) throws SocketException { actual.setReuseAddress(on); } @Override public boolean getReuseAddress() throws SocketException { return actual.getReuseAddress(); } @Override public synchronized void close() throws IOException { actual.close(); } @Override public void shutdownInput() throws IOException { actual.shutdownInput(); } @Override public void shutdownOutput() throws IOException { actual.shutdownOutput(); } @Override public String toString() { return actual.toString(); } @Override public boolean isConnected() { return actual.isConnected(); } @Override public boolean isBound() { return actual.isBound(); } @Override public boolean isClosed() { return actual.isClosed(); } @Override public boolean isInputShutdown() { return actual.isInputShutdown(); } @Override public boolean isOutputShutdown() { return actual.isOutputShutdown(); } @Override public void setPerformancePreferences(int connectionTime, int latency, int bandwidth) { actual.setPerformancePreferences(connectionTime, latency, bandwidth); } @Override public InputStream getInputStream() throws IOException { return wrapper; } @Override public OutputStream getOutputStream() throws IOException { return actual.getOutputStream(); } @Override public String[] getSupportedCipherSuites() { return actual.getSupportedCipherSuites(); } @Override public String[] getEnabledCipherSuites() { return actual.getEnabledCipherSuites(); } @Override public void setEnabledCipherSuites(String[] var1) { actual.setEnabledCipherSuites(var1); } @Override public String[] getSupportedProtocols() { return actual.getSupportedProtocols(); } @Override public String[] getEnabledProtocols() { return actual.getEnabledProtocols(); } @Override public void setEnabledProtocols(String[] var1) { actual.setEnabledProtocols(var1); } @Override public SSLSession getSession() { return actual.getSession(); } @Override public SSLSession getHandshakeSession() { return actual.getHandshakeSession(); } @Override public void addHandshakeCompletedListener(HandshakeCompletedListener var1) { actual.addHandshakeCompletedListener(var1); } @Override public void removeHandshakeCompletedListener(HandshakeCompletedListener var1) { actual.removeHandshakeCompletedListener(var1); } @Override public void startHandshake() throws IOException { actual.startHandshake(); } @Override public void setUseClientMode(boolean var1) { actual.setUseClientMode(var1); } @Override public boolean getUseClientMode() { return actual.getUseClientMode(); } @Override public void setNeedClientAuth(boolean var1) { actual.setNeedClientAuth(var1); } @Override public boolean getNeedClientAuth() { return actual.getNeedClientAuth(); } @Override public void setWantClientAuth(boolean var1) { actual.setWantClientAuth(var1); } @Override public boolean getWantClientAuth() { return actual.getWantClientAuth(); } @Override public void setEnableSessionCreation(boolean var1) { actual.setEnableSessionCreation(var1); } @Override public boolean getEnableSessionCreation() { return actual.getEnableSessionCreation(); } @Override public SSLParameters getSSLParameters() { return actual.getSSLParameters(); } @Override public void setSSLParameters(SSLParameters var1) { actual.setSSLParameters(var1); } @Override public String getApplicationProtocol() { return actual.getApplicationProtocol(); } @Override public String getHandshakeApplicationProtocol() { return actual.getHandshakeApplicationProtocol(); } @Override public void setHandshakeApplicationProtocolSelector(BiFunction, String> var1) { actual.setHandshakeApplicationProtocolSelector(var1); } @Override public BiFunction, String> getHandshakeApplicationProtocolSelector() { return actual.getHandshakeApplicationProtocolSelector(); } } ================================================ FILE: src/main/java/redis/clients/jedis/ScanIteration.java ================================================ package redis.clients.jedis; import java.util.Collection; import java.util.function.Function; import redis.clients.jedis.params.ScanParams; import redis.clients.jedis.Protocol.Keyword; import redis.clients.jedis.providers.ConnectionProvider; import redis.clients.jedis.resps.ScanResult; import redis.clients.jedis.util.JedisCommandIterationBase; public class ScanIteration extends JedisCommandIterationBase, String> { private final int count; private final Function args; public ScanIteration(ConnectionProvider connectionProvider, int batchCount, String match) { super(connectionProvider, BuilderFactory.SCAN_RESPONSE); this.count = batchCount; this.args = (cursor) -> new CommandArguments(Protocol.Command.SCAN).add(cursor) .add(Keyword.MATCH).add(match).add(Keyword.COUNT).add(count); } public ScanIteration(ConnectionProvider connectionProvider, int batchCount, String match, String type) { super(connectionProvider, BuilderFactory.SCAN_RESPONSE); this.count = batchCount; this.args = (cursor) -> new CommandArguments(Protocol.Command.SCAN).add(cursor) .add(Keyword.MATCH).add(match).add(Keyword.COUNT).add(count).add(Keyword.TYPE).add(type); } @Override protected boolean isNodeCompleted(ScanResult reply) { return reply.isCompleteIteration(); } @Override protected CommandArguments initCommandArguments() { return args.apply(ScanParams.SCAN_POINTER_START); } @Override protected CommandArguments nextCommandArguments(ScanResult lastReply) { return args.apply(lastReply.getCursor()); } @Override protected Collection convertBatchToData(ScanResult batch) { return batch.getResult(); } } ================================================ FILE: src/main/java/redis/clients/jedis/SslOptions.java ================================================ package redis.clients.jedis; import java.io.File; import java.io.FileInputStream; import java.io.IOException; import java.io.InputStream; import java.net.Socket; import java.net.URL; import java.util.Arrays; import java.util.Objects; import java.security.GeneralSecurityException; import java.security.KeyStore; import java.security.cert.CertificateException; import java.security.cert.X509Certificate; import javax.net.ssl.KeyManager; import javax.net.ssl.KeyManagerFactory; import javax.net.ssl.SSLContext; import javax.net.ssl.SSLEngine; import javax.net.ssl.SSLParameters; import javax.net.ssl.TrustManager; import javax.net.ssl.TrustManagerFactory; import javax.net.ssl.X509ExtendedTrustManager; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** * Options to configure SSL options for the connections kept to Redis servers. * * @author Mark Paluch */ public class SslOptions { private static final Logger logger = LoggerFactory.getLogger(SslOptions.class); private final String keyManagerAlgorithm = KeyManagerFactory.getDefaultAlgorithm(); private final String trustManagerAlgorithm = TrustManagerFactory.getDefaultAlgorithm(); private final String keyStoreType; private final String trustStoreType; private final Resource keystoreResource; private final char[] keystorePassword; private final Resource truststoreResource; private final char[] truststorePassword; private final SSLParameters sslParameters; private final SslVerifyMode sslVerifyMode; private final String sslProtocol; // protocol for SSLContext private SslOptions(Builder builder) { this.keyStoreType = builder.keyStoreType; this.trustStoreType = builder.trustStoreType; this.keystoreResource = builder.keystoreResource; this.keystorePassword = builder.keystorePassword; this.truststoreResource = builder.truststoreResource; this.truststorePassword = builder.truststorePassword; this.sslParameters = builder.sslParameters; this.sslVerifyMode = builder.sslVerifyMode; this.sslProtocol = builder.sslProtocol; } /** * Returns a new {@link SslOptions.Builder} to construct {@link SslOptions}. * * @return a new {@link SslOptions.Builder} to construct {@link SslOptions}. */ public static SslOptions.Builder builder() { return new SslOptions.Builder(); } /** * Builder for {@link SslOptions}. */ public static class Builder { private String keyStoreType; private String trustStoreType; private Resource keystoreResource; private char[] keystorePassword = null; private Resource truststoreResource; private char[] truststorePassword = null; private SSLParameters sslParameters; private SslVerifyMode sslVerifyMode = SslVerifyMode.FULL; private String sslProtocol = "TLS"; // protocol for SSLContext private Builder() { } /** * Sets the KeyStore type. Defaults to {@link KeyStore#getDefaultType()} if not set. * * @param keyStoreType the keystore type to use, must not be {@code null}. * @return {@code this} */ public Builder keyStoreType(String keyStoreType) { this.keyStoreType = Objects.requireNonNull(keyStoreType, "KeyStoreType must not be null"); return this; } /** * Sets the KeyStore type. Defaults to {@link KeyStore#getDefaultType()} if not set. * * @param trustStoreType the keystore type to use, must not be {@code null}. * @return {@code this} */ public Builder trustStoreType(String trustStoreType) { this.trustStoreType = Objects.requireNonNull(trustStoreType, "TrustStoreType must not be null"); return this; } /** * Sets the Keystore file to load client certificates. The key store file must be supported by * {@link java.security.KeyStore} which is {@link KeyStore#getDefaultType()} by default. The keystore is reloaded on * each connection attempt that allows to replace certificates during runtime. * * @param keystore the keystore file, must not be {@code null}. * @return {@code this} */ public Builder keystore(File keystore) { return keystore(keystore, null); } /** * Sets the Keystore file to load client certificates. The keystore file must be supported by * {@link java.security.KeyStore} which is {@link KeyStore#getDefaultType()} by default. The keystore is reloaded on * each connection attempt that allows to replace certificates during runtime. * * @param keystore the keystore file, must not be {@code null}. * @param keystorePassword the keystore password. May be empty to omit password and the keystore integrity check. * @return {@code this} */ public Builder keystore(File keystore, char[] keystorePassword) { Objects.requireNonNull(keystore, "Keystore must not be null"); assertFile("Keystore", keystore); return keystore(Resource.from(keystore), keystorePassword); } /** * Sets the Keystore resource to load client certificates. The keystore file must be supported by * {@link java.security.KeyStore} which is {@link KeyStore#getDefaultType()} by default. The keystore is reloaded on * each connection attempt that allows to replace certificates during runtime. * * @param keystore the keystore URL, must not be {@code null}. * @return {@code this} */ public Builder keystore(URL keystore) { return keystore(keystore, null); } /** * Sets the Keystore resource to load client certificates. The keystore file must be supported by * {@link java.security.KeyStore} which is {@link KeyStore#getDefaultType()} by default. The keystore is reloaded on * each connection attempt that allows to replace certificates during runtime. * * @param keystore the keystore file, must not be {@code null}. * @param keystorePassword * @return {@code this} */ public Builder keystore(URL keystore, char[] keystorePassword) { Objects.requireNonNull(keystore, "Keystore must not be null"); return keystore(Resource.from(keystore), keystorePassword); } /** * Sets the Java Keystore resource to load client certificates. The keystore file must be supported by * {@link java.security.KeyStore} which is {@link KeyStore#getDefaultType()} by default. The keystore is reloaded on * each connection attempt that allows to replace certificates during runtime. * * @param resource the provider that opens a {@link InputStream} to the keystore file, must not be {@code null}. * @param keystorePassword the keystore password. May be empty to omit password and the keystore integrity check. * @return {@code this} */ public Builder keystore(Resource resource, char[] keystorePassword) { this.keystoreResource = Objects.requireNonNull(resource, "Keystore InputStreamProvider must not be null"); this.keystorePassword = getPassword(keystorePassword); return this; } /** * Sets the Truststore file to load trusted certificates. The truststore file must be supported by * {@link java.security.KeyStore} which is {@link KeyStore#getDefaultType()} by default. The truststore is reloaded on * each connection attempt that allows to replace certificates during runtime. * * @param truststore the truststore file, must not be {@code null}. * @return {@code this} */ public Builder truststore(File truststore) { return truststore(truststore, null); } /** * Sets the Truststore file to load trusted certificates. The truststore file must be supported by * {@link java.security.KeyStore} which is {@link KeyStore#getDefaultType()} by default. The truststore is reloaded on * each connection attempt that allows to replace certificates during runtime. * * @param truststore the truststore file, must not be {@code null}. * @param truststorePassword the truststore password. May be empty to omit password and the truststore integrity check. * @return {@code this} */ public Builder truststore(File truststore, char[] truststorePassword) { Objects.requireNonNull(truststore, "Truststore must not be null"); assertFile("Truststore", truststore); return truststore(Resource.from(truststore), truststorePassword); } /** * Sets the Truststore resource to load trusted certificates. The truststore resource must be supported by * {@link java.security.KeyStore} which is {@link KeyStore#getDefaultType()} by default. The truststore is reloaded on * each connection attempt that allows to replace certificates during runtime. * * @param truststore the truststore file, must not be {@code null}. * @return {@code this} */ public Builder truststore(URL truststore) { return truststore(truststore, null); } /** * Sets the Truststore resource to load trusted certificates. The truststore resource must be supported by * {@link java.security.KeyStore} which is {@link KeyStore#getDefaultType()} by default. The truststore is reloaded on * each connection attempt that allows to replace certificates during runtime. * * @param truststore the truststore file, must not be {@code null}. * @param truststorePassword the truststore password. May be empty to omit password and the truststore integrity check. * @return {@code this} */ public Builder truststore(URL truststore, char[] truststorePassword) { Objects.requireNonNull(truststore, "Truststore must not be null"); return truststore(Resource.from(truststore), truststorePassword); } /** * Sets the Truststore resource to load trusted certificates. The truststore resource must be supported by * {@link java.security.KeyStore} which is {@link KeyStore#getDefaultType()} by default. The truststore is reloaded on * each connection attempt that allows to replace certificates during runtime. * * @param resource the provider that opens a {@link InputStream} to the keystore file, must not be {@code null}. * @param truststorePassword the truststore password. May be empty to omit password and the truststore integrity check. * @return {@code this} */ public Builder truststore(Resource resource, char[] truststorePassword) { this.truststoreResource = Objects.requireNonNull(resource, "Truststore InputStreamProvider must not be null"); this.truststorePassword = getPassword(truststorePassword); return this; } /** * Sets a configured {@link SSLParameters}. * * @param sslParameters a {@link SSLParameters} object. * @return {@code this} */ public Builder sslParameters(SSLParameters sslParameters) { this.sslParameters = sslParameters; return this; } /** * Sets the {@link SslVerifyMode}. * * @param sslVerifyMode the {@link SslVerifyMode}. * @return {@code this} */ public Builder sslVerifyMode(SslVerifyMode sslVerifyMode) { this.sslVerifyMode = sslVerifyMode; return this; } /** * The SSL/TLS protocol to be used to initialize {@link SSLContext}. * @param protocol the ssl/tls protocol * @return {@code this} */ public Builder sslProtocol(String protocol) { this.sslProtocol = protocol; return this; } /** * Create a new instance of {@link SslOptions} * * @return new instance of {@link SslOptions} */ public SslOptions build() { if (this.sslParameters == null) { this.sslParameters = new SSLParameters(); } return new SslOptions(this); } } /** * A {@link SSLContext} object that is configured with values from this {@link SslOptions} object. * * @return {@link SSLContext} * @throws IOException thrown when loading the keystore or the truststore fails. * @throws GeneralSecurityException thrown when loading the keystore or the truststore fails. */ public SSLContext createSslContext() throws IOException, GeneralSecurityException { KeyManager[] keyManagers = null; TrustManager[] trustManagers = null; if (sslVerifyMode == SslVerifyMode.FULL) { this.sslParameters.setEndpointIdentificationAlgorithm("HTTPS"); } else if (sslVerifyMode == SslVerifyMode.CA) { this.sslParameters.setEndpointIdentificationAlgorithm(""); } else if (sslVerifyMode == SslVerifyMode.INSECURE) { trustManagers = new TrustManager[] { INSECURE_TRUST_MANAGER }; } if (keystoreResource != null) { KeyStore keyStore = KeyStore.getInstance(keyStoreType==null ? KeyStore.getDefaultType() : keyStoreType); try (InputStream keystoreStream = keystoreResource.get()) { keyStore.load(keystoreStream, keystorePassword); } KeyManagerFactory keyManagerFactory = KeyManagerFactory.getInstance(keyManagerAlgorithm); keyManagerFactory.init(keyStore, keystorePassword); keyManagers = keyManagerFactory.getKeyManagers(); } if (trustManagers == null && truststoreResource != null) { KeyStore trustStore = KeyStore.getInstance(trustStoreType == null ? KeyStore.getDefaultType() : trustStoreType); try (InputStream truststoreStream = truststoreResource.get()) { trustStore.load(truststoreStream, truststorePassword); } TrustManagerFactory trustManagerFactory = TrustManagerFactory.getInstance(trustManagerAlgorithm); trustManagerFactory.init(trustStore); trustManagers = trustManagerFactory.getTrustManagers(); } SSLContext sslContext = SSLContext.getInstance(sslProtocol); sslContext.init(keyManagers, trustManagers, null); return sslContext; } /** * {@link #createSslContext()} must be called before this. * @return {@link SSLParameters} */ public SSLParameters getSslParameters() { return sslParameters; } /** * Configured ssl verify mode. * @return {@link SslVerifyMode} */ public SslVerifyMode getSslVerifyMode() { return sslVerifyMode; } private static char[] getPassword(char[] chars) { return chars != null ? Arrays.copyOf(chars, chars.length) : null; } /** * Assert that {@code file} {@link File#exists() exists}. * * @param keyword file recognizer * @param file * @throws IllegalArgumentException if the file doesn't exist */ public static void assertFile(String keyword, File file) { if (!file.exists()) { throw new IllegalArgumentException(String.format("%s file %s does not exist", keyword, file)); } if (!file.isFile()) { throw new IllegalArgumentException(String.format("%s file %s is not a file", keyword, file)); } } /** * Supplier for a {@link InputStream} representing a resource. The resulting {@link InputStream} must be closed by * the calling code. */ @FunctionalInterface public interface Resource { /** * Create a {@link Resource} that obtains a {@link InputStream} from a {@link URL}. * * @param url the URL to obtain the {@link InputStream} from. * @return a {@link Resource} that opens a connection to the URL and obtains the {@link InputStream} for it. */ static Resource from(URL url) { Objects.requireNonNull(url, "URL must not be null"); return () -> url.openConnection().getInputStream(); } /** * Create a {@link Resource} that obtains a {@link InputStream} from a {@link File}. * * @param file the File to obtain the {@link InputStream} from. * @return a {@link Resource} that obtains the {@link FileInputStream} for the given {@link File}. */ static Resource from(File file) { Objects.requireNonNull(file, "File must not be null"); return () -> new FileInputStream(file); } /** * Obtains the {@link InputStream}. * * @return the {@link InputStream}. * @throws IOException */ InputStream get() throws IOException; } private static final X509Certificate[] EMPTY_X509_CERTIFICATES = {}; private static final TrustManager INSECURE_TRUST_MANAGER = new X509ExtendedTrustManager() { @Override public void checkClientTrusted(X509Certificate[] chain, String s) { if (logger.isDebugEnabled()) { logger.debug("Accepting a client certificate: " + chain[0].getSubjectDN()); } } @Override public void checkServerTrusted(X509Certificate[] chain, String s) { if (logger.isDebugEnabled()) { logger.debug("Accepting a server certificate: " + chain[0].getSubjectDN()); } } @Override public void checkClientTrusted(X509Certificate[] chain, String s, Socket socket) throws CertificateException { checkClientTrusted(chain, s); } @Override public void checkClientTrusted(X509Certificate[] chain, String s, SSLEngine sslEngine) throws CertificateException { checkClientTrusted(chain, s); } @Override public void checkServerTrusted(X509Certificate[] chain, String s, Socket socket) throws CertificateException { checkServerTrusted(chain, s); } @Override public void checkServerTrusted(X509Certificate[] chain, String s, SSLEngine sslEngine) throws CertificateException { checkServerTrusted(chain, s); } @Override public X509Certificate[] getAcceptedIssuers() { return EMPTY_X509_CERTIFICATES; } }; } ================================================ FILE: src/main/java/redis/clients/jedis/SslVerifyMode.java ================================================ package redis.clients.jedis; /** * Enumeration of SSL/TLS hostname verification modes. */ public enum SslVerifyMode { /** * DO NOT USE THIS IN PRODUCTION. *

* No verification at all. */ INSECURE, /** * Verify the CA and certificate without verifying that the hostname matches. */ CA, /** * Full certificate verification. */ FULL; } ================================================ FILE: src/main/java/redis/clients/jedis/StaticCommandFlagsRegistry.java ================================================ package redis.clients.jedis; import redis.clients.jedis.annots.Internal; import redis.clients.jedis.args.Rawable; import redis.clients.jedis.commands.ProtocolCommand; import redis.clients.jedis.util.JedisByteMap; import redis.clients.jedis.util.SafeEncoder; import java.util.EnumSet; import java.util.Map; /** * Static implementation of CommandFlagsRegistry. */ @Internal public class StaticCommandFlagsRegistry implements CommandFlagsRegistry { // Empty flags constant for commands with no flags public static final EnumSet EMPTY_FLAGS = EnumSet.noneOf(CommandFlag.class); // Default request policy for commands without a specific policy public static final RequestPolicy DEFAULT_REQUEST_POLICY = RequestPolicy.DEFAULT; // Default response policy for commands without a specific policy public static final ResponsePolicy DEFAULT_RESPONSE_POLICY = ResponsePolicy.DEFAULT; // Singleton instance private static final StaticCommandFlagsRegistry REGISTRY = createRegistry(); private final Commands commands; private StaticCommandFlagsRegistry(Commands commands) { this.commands = commands; } /** * Get the singleton instance of the static command flags registry. *

* DO NOT USE THIS METHOD UNLESS YOU KNOW WHAT YOU ARE DOING. *

* @return StaticCommandFlagsRegistry */ public static StaticCommandFlagsRegistry registry() { return REGISTRY; } private static StaticCommandFlagsRegistry createRegistry() { Builder builder = new Builder(); // Delegate population to generated class StaticCommandFlagsRegistryInitializer.initialize(builder); return builder.build(); } /** * Get the flags for a given command. Flags are looked up from a static registry based on the * command arguments. This approach significantly reduces memory usage by sharing flag instances * across all CommandObject instances. *

* Uses the same hierarchical lookup strategy as {@link #lookupCommandMeta(CommandArguments)}. * @param commandArguments the command arguments containing the command and its parameters * @return EnumSet of CommandFlag for this command, or empty set if command has no flags */ @Override public EnumSet getFlags(CommandArguments commandArguments) { CommandMeta commandMeta = lookupCommandMeta(commandArguments); if (commandMeta == null) { return EMPTY_FLAGS; } return commandMeta.getFlags(); } /** * Get the request policy for a given command. The request policy helps clients determine which * shards to send the command to in a clustered deployment. *

* Uses the same hierarchical lookup strategy as {@link #getFlags(CommandArguments)}. * @param commandArguments the command arguments containing the command and its parameters * @return RequestPolicy for this command, or DEFAULT if no specific policy is defined */ @Override public RequestPolicy getRequestPolicy(CommandArguments commandArguments) { CommandMeta commandMeta = lookupCommandMeta(commandArguments); if (commandMeta == null) { return DEFAULT_REQUEST_POLICY; } return commandMeta.getRequestPolicy(); } /** * Get the response policy for a given command. The response policy helps clients determine how to * aggregate replies from multiple shards in a cluster. *

* Uses the same hierarchical lookup strategy as {@link #getFlags(CommandArguments)}. * @param commandArguments the command arguments containing the command and its parameters * @return ResponsePolicy for this command, or DEFAULT if no specific policy is defined */ @Override public ResponsePolicy getResponsePolicy(CommandArguments commandArguments) { CommandMeta commandMeta = lookupCommandMeta(commandArguments); if (commandMeta == null) { return DEFAULT_RESPONSE_POLICY; } return commandMeta.getResponsePolicy(); } /** * Common lookup logic for finding the CommandMeta for a given command. Handles both simple * commands and commands with subcommands. */ private CommandMeta lookupCommandMeta(CommandArguments commandArguments) { ProtocolCommand cmd = commandArguments.getCommand(); byte[] raw = cmd.getRaw(); byte[] uppercaseBytes = SafeEncoder.toUpperCase(raw); CommandMeta commandMeta = commands.getCommand(uppercaseBytes); if (commandMeta == null) { return null; } if (commandMeta.hasSubcommands()) { byte[] subCommand = getSubCommand(commandArguments); if (subCommand != null) { CommandMeta subCommandMeta = commandMeta.getSubcommand(subCommand); if (subCommandMeta != null) { return subCommandMeta; } } } return commandMeta; } private byte[] getSubCommand(CommandArguments commandArguments) { if (commandArguments.size() > 1) { Rawable secondArg = commandArguments.get(1); byte[] subRaw = secondArg.getRaw(); // Convert to uppercase using SafeEncoder utility return SafeEncoder.toUpperCase(subRaw); } else { return null; } } // Internal class to hold subcommand mappings for parent commands. static class Commands { final JedisByteMap commands = new JedisByteMap<>(); boolean isEmpty() { return commands.isEmpty(); } public Commands register(byte[] cmd, CommandMeta command) { commands.put(cmd, command); return this; } public boolean containsKey(byte[] command) { return commands.containsKey(command); } public CommandMeta getCommand(byte[] command) { return commands.get(command); } public Map getCommands() { return commands; } } /** * Internal class to hold command metadata including flags and policies. */ static class CommandMeta { final EnumSet flags; final RequestPolicy requestPolicy; final ResponsePolicy responsePolicy; final Commands subcommands = new Commands(); CommandMeta(EnumSet flags) { this(flags, DEFAULT_REQUEST_POLICY, DEFAULT_RESPONSE_POLICY); } CommandMeta(EnumSet flags, RequestPolicy requestPolicy, ResponsePolicy responsePolicy) { this.flags = flags; this.requestPolicy = requestPolicy != null ? requestPolicy : DEFAULT_REQUEST_POLICY; this.responsePolicy = responsePolicy != null ? responsePolicy : DEFAULT_RESPONSE_POLICY; } void putSubCommand(byte[] subCommand, CommandMeta subCommandMeta) { this.subcommands.register(subCommand, subCommandMeta); } boolean hasSubcommands() { return !subcommands.isEmpty(); } EnumSet getFlags() { if (flags == null) { return EMPTY_FLAGS; } return flags; } RequestPolicy getRequestPolicy() { return requestPolicy; } ResponsePolicy getResponsePolicy() { return responsePolicy; } CommandMeta getSubcommand(byte[] subcommand) { return subcommands.getCommand(subcommand); } } /** * Builder for constructing StaticCommandFlagsRegistry instances. */ static public class Builder { private final Commands commands = new Commands(); public Builder register(String name, EnumSet flags) { commands.register(SafeEncoder.encode(name), new CommandMeta(flags)); return this; } public Builder register(String name, EnumSet flags, RequestPolicy requestPolicy, ResponsePolicy responsePolicy) { commands.register(SafeEncoder.encode(name), new CommandMeta(flags, requestPolicy, responsePolicy)); return this; } public Builder register(String name, String subcommand, EnumSet flags) { byte[] cmdName = SafeEncoder.encode(name); if (!commands.containsKey(cmdName)) { commands.register(SafeEncoder.encode(name), new CommandMeta(EMPTY_FLAGS)); } byte[] subCmdName = SafeEncoder.encode(subcommand); commands.getCommand(cmdName).putSubCommand(subCmdName, new CommandMeta(flags)); return this; } public Builder register(String name, String subcommand, EnumSet flags, RequestPolicy requestPolicy, ResponsePolicy responsePolicy) { byte[] cmdName = SafeEncoder.encode(name); if (!commands.containsKey(cmdName)) { commands.register(SafeEncoder.encode(name), new CommandMeta(EMPTY_FLAGS)); } byte[] subCmdName = SafeEncoder.encode(subcommand); commands.getCommand(cmdName).putSubCommand(subCmdName, new CommandMeta(flags, requestPolicy, responsePolicy)); return this; } public StaticCommandFlagsRegistry build() { return new StaticCommandFlagsRegistry(commands); } } } ================================================ FILE: src/main/java/redis/clients/jedis/StaticCommandFlagsRegistryInitializer.java ================================================ package redis.clients.jedis; import java.util.EnumSet; import static redis.clients.jedis.StaticCommandFlagsRegistry.EMPTY_FLAGS; import static redis.clients.jedis.CommandFlagsRegistry.CommandFlag; import static redis.clients.jedis.CommandFlagsRegistry.RequestPolicy; import static redis.clients.jedis.CommandFlagsRegistry.ResponsePolicy; /** * Static implementation of CommandFlagsRegistry. This class is auto-generated by * CommandFlagsRegistryGenerator. DO NOT EDIT MANUALLY. *

* Generated from Redis Server: *

    *
  • Version: 8.6.1
  • *
  • Mode: standalone
  • *
  • Loaded Modules: timeseries, search, vectorset, bf, ReJSON
  • *
  • Generated at: 2026-03-20 13:22:33 EET
  • *
*/ final class StaticCommandFlagsRegistryInitializer { static void initialize(StaticCommandFlagsRegistry.Builder builder) { builder.register("FT.CONFIG", EnumSet.of(CommandFlag.MODULE, CommandFlag.READONLY)); // FT.CONFIG subcommands builder.register("FT.CONFIG", "GET", EnumSet.of(CommandFlag.MODULE, CommandFlag.READONLY)); builder.register("FT.CONFIG", "SET", EnumSet.of(CommandFlag.MODULE, CommandFlag.WRITE)); builder.register("ACL", EMPTY_FLAGS); // ACL subcommands builder.register("ACL", "CAT", EnumSet.of(CommandFlag.LOADING, CommandFlag.NOSCRIPT, CommandFlag.STALE)); builder.register("ACL", "DELUSER", EnumSet.of(CommandFlag.ADMIN, CommandFlag.LOADING, CommandFlag.NOSCRIPT, CommandFlag.STALE), RequestPolicy.ALL_NODES, ResponsePolicy.ALL_SUCCEEDED); builder.register("ACL", "DRYRUN", EnumSet.of(CommandFlag.ADMIN, CommandFlag.LOADING, CommandFlag.NOSCRIPT, CommandFlag.STALE)); builder.register("ACL", "GENPASS", EnumSet.of(CommandFlag.LOADING, CommandFlag.NOSCRIPT, CommandFlag.STALE)); builder.register("ACL", "GETUSER", EnumSet.of(CommandFlag.ADMIN, CommandFlag.LOADING, CommandFlag.NOSCRIPT, CommandFlag.STALE)); builder.register("ACL", "LIST", EnumSet.of(CommandFlag.ADMIN, CommandFlag.LOADING, CommandFlag.NOSCRIPT, CommandFlag.STALE)); builder.register("ACL", "LOAD", EnumSet.of(CommandFlag.ADMIN, CommandFlag.LOADING, CommandFlag.NOSCRIPT, CommandFlag.STALE)); builder.register("ACL", "LOG", EnumSet.of(CommandFlag.ADMIN, CommandFlag.LOADING, CommandFlag.NOSCRIPT, CommandFlag.STALE)); builder.register("ACL", "SAVE", EnumSet.of(CommandFlag.ADMIN, CommandFlag.LOADING, CommandFlag.NOSCRIPT, CommandFlag.STALE), RequestPolicy.ALL_NODES, ResponsePolicy.ALL_SUCCEEDED); builder.register("ACL", "SETUSER", EnumSet.of(CommandFlag.ADMIN, CommandFlag.LOADING, CommandFlag.NOSCRIPT, CommandFlag.STALE), RequestPolicy.ALL_NODES, ResponsePolicy.ALL_SUCCEEDED); builder.register("ACL", "USERS", EnumSet.of(CommandFlag.ADMIN, CommandFlag.LOADING, CommandFlag.NOSCRIPT, CommandFlag.STALE)); builder.register("ACL", "WHOAMI", EnumSet.of(CommandFlag.LOADING, CommandFlag.NOSCRIPT, CommandFlag.STALE)); builder.register("OBJECT", EMPTY_FLAGS); // OBJECT subcommands builder.register("OBJECT", "ENCODING", EnumSet.of(CommandFlag.READONLY)); builder.register("OBJECT", "FREQ", EnumSet.of(CommandFlag.READONLY)); builder.register("OBJECT", "IDLETIME", EnumSet.of(CommandFlag.READONLY)); builder.register("OBJECT", "REFCOUNT", EnumSet.of(CommandFlag.READONLY)); builder.register("FUNCTION", EMPTY_FLAGS); // FUNCTION subcommands builder.register("FUNCTION", "DELETE", EnumSet.of(CommandFlag.NOSCRIPT, CommandFlag.WRITE), RequestPolicy.ALL_SHARDS, ResponsePolicy.ALL_SUCCEEDED); builder.register("FUNCTION", "DUMP", EnumSet.of(CommandFlag.NOSCRIPT)); builder.register("FUNCTION", "FLUSH", EnumSet.of(CommandFlag.NOSCRIPT, CommandFlag.WRITE), RequestPolicy.ALL_SHARDS, ResponsePolicy.ALL_SUCCEEDED); builder.register("FUNCTION", "KILL", EnumSet.of(CommandFlag.ALLOW_BUSY, CommandFlag.NOSCRIPT), RequestPolicy.ALL_SHARDS, ResponsePolicy.ONE_SUCCEEDED); builder.register("FUNCTION", "LIST", EnumSet.of(CommandFlag.NOSCRIPT)); builder.register("FUNCTION", "LOAD", EnumSet.of(CommandFlag.DENYOOM, CommandFlag.NOSCRIPT, CommandFlag.WRITE), RequestPolicy.ALL_SHARDS, ResponsePolicy.ALL_SUCCEEDED); builder.register("FUNCTION", "RESTORE", EnumSet.of(CommandFlag.DENYOOM, CommandFlag.NOSCRIPT, CommandFlag.WRITE), RequestPolicy.ALL_SHARDS, ResponsePolicy.ALL_SUCCEEDED); builder.register("FUNCTION", "STATS", EnumSet.of(CommandFlag.ALLOW_BUSY, CommandFlag.NOSCRIPT), RequestPolicy.DEFAULT, ResponsePolicy.SPECIAL); builder.register("CLIENT", EMPTY_FLAGS); // CLIENT subcommands builder.register("CLIENT", "CACHING", EnumSet.of(CommandFlag.LOADING, CommandFlag.NOSCRIPT, CommandFlag.STALE)); builder.register("CLIENT", "GETNAME", EnumSet.of(CommandFlag.LOADING, CommandFlag.NOSCRIPT, CommandFlag.STALE)); builder.register("CLIENT", "GETREDIR", EnumSet.of(CommandFlag.LOADING, CommandFlag.NOSCRIPT, CommandFlag.STALE)); builder.register("CLIENT", "ID", EnumSet.of(CommandFlag.LOADING, CommandFlag.NOSCRIPT, CommandFlag.STALE)); builder.register("CLIENT", "INFO", EnumSet.of(CommandFlag.LOADING, CommandFlag.NOSCRIPT, CommandFlag.STALE)); builder.register("CLIENT", "KILL", EnumSet.of(CommandFlag.ADMIN, CommandFlag.LOADING, CommandFlag.NOSCRIPT, CommandFlag.STALE)); builder.register("CLIENT", "LIST", EnumSet.of(CommandFlag.ADMIN, CommandFlag.LOADING, CommandFlag.NOSCRIPT, CommandFlag.STALE)); builder.register("CLIENT", "NO-EVICT", EnumSet.of(CommandFlag.ADMIN, CommandFlag.LOADING, CommandFlag.NOSCRIPT, CommandFlag.STALE)); builder.register("CLIENT", "NO-TOUCH", EnumSet.of(CommandFlag.LOADING, CommandFlag.NOSCRIPT, CommandFlag.STALE)); builder.register("CLIENT", "PAUSE", EnumSet.of(CommandFlag.ADMIN, CommandFlag.LOADING, CommandFlag.NOSCRIPT, CommandFlag.STALE)); builder.register("CLIENT", "REPLY", EnumSet.of(CommandFlag.LOADING, CommandFlag.NOSCRIPT, CommandFlag.STALE)); builder.register("CLIENT", "SETINFO", EnumSet.of(CommandFlag.LOADING, CommandFlag.NOSCRIPT, CommandFlag.STALE), RequestPolicy.ALL_NODES, ResponsePolicy.ALL_SUCCEEDED); builder.register("CLIENT", "SETNAME", EnumSet.of(CommandFlag.LOADING, CommandFlag.NOSCRIPT, CommandFlag.STALE), RequestPolicy.ALL_NODES, ResponsePolicy.ALL_SUCCEEDED); builder.register("CLIENT", "TRACKING", EnumSet.of(CommandFlag.LOADING, CommandFlag.NOSCRIPT, CommandFlag.STALE)); builder.register("CLIENT", "TRACKINGINFO", EnumSet.of(CommandFlag.LOADING, CommandFlag.NOSCRIPT, CommandFlag.STALE)); builder.register("CLIENT", "UNBLOCK", EnumSet.of(CommandFlag.ADMIN, CommandFlag.LOADING, CommandFlag.NOSCRIPT, CommandFlag.STALE)); builder.register("CLIENT", "UNPAUSE", EnumSet.of(CommandFlag.ADMIN, CommandFlag.LOADING, CommandFlag.NOSCRIPT, CommandFlag.STALE)); builder.register("CONFIG", EMPTY_FLAGS); // CONFIG subcommands builder.register("CONFIG", "GET", EnumSet.of(CommandFlag.ADMIN, CommandFlag.LOADING, CommandFlag.NOSCRIPT, CommandFlag.STALE)); builder.register("CONFIG", "RESETSTAT", EnumSet.of(CommandFlag.ADMIN, CommandFlag.LOADING, CommandFlag.NOSCRIPT, CommandFlag.STALE), RequestPolicy.ALL_NODES, ResponsePolicy.ALL_SUCCEEDED); builder.register("CONFIG", "REWRITE", EnumSet.of(CommandFlag.ADMIN, CommandFlag.LOADING, CommandFlag.NOSCRIPT, CommandFlag.STALE), RequestPolicy.ALL_NODES, ResponsePolicy.ALL_SUCCEEDED); builder.register("CONFIG", "SET", EnumSet.of(CommandFlag.ADMIN, CommandFlag.LOADING, CommandFlag.NOSCRIPT, CommandFlag.STALE), RequestPolicy.ALL_NODES, ResponsePolicy.ALL_SUCCEEDED); builder.register("MODULE", EMPTY_FLAGS); // MODULE subcommands builder.register("MODULE", "LIST", EnumSet.of(CommandFlag.ADMIN, CommandFlag.NOSCRIPT)); builder.register("MODULE", "LOAD", EnumSet.of(CommandFlag.ADMIN, CommandFlag.NOSCRIPT, CommandFlag.NO_ASYNC_LOADING)); builder.register("MODULE", "LOADEX", EnumSet.of(CommandFlag.ADMIN, CommandFlag.NOSCRIPT, CommandFlag.NO_ASYNC_LOADING)); builder.register("MODULE", "UNLOAD", EnumSet.of(CommandFlag.ADMIN, CommandFlag.NOSCRIPT, CommandFlag.NO_ASYNC_LOADING)); builder.register("HOTKEYS", EMPTY_FLAGS); // HOTKEYS subcommands builder.register("HOTKEYS", "GET", EnumSet.of(CommandFlag.ADMIN, CommandFlag.NOSCRIPT), RequestPolicy.SPECIAL, ResponsePolicy.SPECIAL); builder.register("HOTKEYS", "RESET", EnumSet.of(CommandFlag.ADMIN, CommandFlag.NOSCRIPT), RequestPolicy.SPECIAL, null); builder.register("HOTKEYS", "START", EnumSet.of(CommandFlag.ADMIN, CommandFlag.NOSCRIPT), RequestPolicy.SPECIAL, null); builder.register("HOTKEYS", "STOP", EnumSet.of(CommandFlag.ADMIN, CommandFlag.NOSCRIPT), RequestPolicy.SPECIAL, null); builder.register("PUBSUB", EMPTY_FLAGS); // PUBSUB subcommands builder.register("PUBSUB", "CHANNELS", EnumSet.of(CommandFlag.LOADING, CommandFlag.PUBSUB, CommandFlag.STALE)); builder.register("PUBSUB", "NUMPAT", EnumSet.of(CommandFlag.LOADING, CommandFlag.PUBSUB, CommandFlag.STALE)); builder.register("PUBSUB", "NUMSUB", EnumSet.of(CommandFlag.LOADING, CommandFlag.PUBSUB, CommandFlag.STALE)); builder.register("PUBSUB", "SHARDCHANNELS", EnumSet.of(CommandFlag.LOADING, CommandFlag.PUBSUB, CommandFlag.STALE)); builder.register("PUBSUB", "SHARDNUMSUB", EnumSet.of(CommandFlag.LOADING, CommandFlag.PUBSUB, CommandFlag.STALE)); builder.register("SCRIPT", EMPTY_FLAGS); // SCRIPT subcommands builder.register("SCRIPT", "DEBUG", EnumSet.of(CommandFlag.NOSCRIPT)); builder.register("SCRIPT", "EXISTS", EnumSet.of(CommandFlag.NOSCRIPT), RequestPolicy.ALL_SHARDS, ResponsePolicy.AGG_LOGICAL_AND); builder.register("SCRIPT", "FLUSH", EnumSet.of(CommandFlag.NOSCRIPT), RequestPolicy.ALL_NODES, ResponsePolicy.ALL_SUCCEEDED); builder.register("SCRIPT", "KILL", EnumSet.of(CommandFlag.ALLOW_BUSY, CommandFlag.NOSCRIPT), RequestPolicy.ALL_SHARDS, ResponsePolicy.ONE_SUCCEEDED); builder.register("SCRIPT", "LOAD", EnumSet.of(CommandFlag.NOSCRIPT, CommandFlag.STALE), RequestPolicy.ALL_NODES, ResponsePolicy.ALL_SUCCEEDED); builder.register("MEMORY", EMPTY_FLAGS); // MEMORY subcommands builder.register("MEMORY", "DOCTOR", EMPTY_FLAGS, RequestPolicy.ALL_SHARDS, ResponsePolicy.SPECIAL); builder.register("MEMORY", "MALLOC-STATS", EMPTY_FLAGS, RequestPolicy.ALL_SHARDS, ResponsePolicy.SPECIAL); builder.register("MEMORY", "PURGE", EMPTY_FLAGS, RequestPolicy.ALL_SHARDS, ResponsePolicy.ALL_SUCCEEDED); builder.register("MEMORY", "STATS", EMPTY_FLAGS, RequestPolicy.ALL_SHARDS, ResponsePolicy.SPECIAL); builder.register("MEMORY", "USAGE", EnumSet.of(CommandFlag.READONLY)); builder.register("SLOWLOG", EMPTY_FLAGS); // SLOWLOG subcommands builder.register("SLOWLOG", "GET", EnumSet.of(CommandFlag.ADMIN, CommandFlag.LOADING, CommandFlag.STALE), RequestPolicy.ALL_NODES, null); builder.register("SLOWLOG", "LEN", EnumSet.of(CommandFlag.ADMIN, CommandFlag.LOADING, CommandFlag.STALE), RequestPolicy.ALL_NODES, ResponsePolicy.AGG_SUM); builder.register("SLOWLOG", "RESET", EnumSet.of(CommandFlag.ADMIN, CommandFlag.LOADING, CommandFlag.STALE), RequestPolicy.ALL_NODES, ResponsePolicy.ALL_SUCCEEDED); builder.register("FT.CURSOR", EnumSet.of(CommandFlag.MODULE, CommandFlag.READONLY)); // FT.CURSOR subcommands builder.register("FT.CURSOR", "DEL", EnumSet.of(CommandFlag.MODULE, CommandFlag.READONLY), RequestPolicy.SPECIAL, null); builder.register("FT.CURSOR", "GC", EnumSet.of(CommandFlag.MODULE, CommandFlag.READONLY)); builder.register("FT.CURSOR", "READ", EnumSet.of(CommandFlag.MODULE, CommandFlag.READONLY), RequestPolicy.SPECIAL, null); builder.register("COMMAND", EnumSet.of(CommandFlag.LOADING, CommandFlag.STALE)); // COMMAND subcommands builder.register("COMMAND", "COUNT", EnumSet.of(CommandFlag.LOADING, CommandFlag.STALE)); builder.register("COMMAND", "DOCS", EnumSet.of(CommandFlag.LOADING, CommandFlag.STALE)); builder.register("COMMAND", "GETKEYS", EnumSet.of(CommandFlag.LOADING, CommandFlag.STALE)); builder.register("COMMAND", "GETKEYSANDFLAGS", EnumSet.of(CommandFlag.LOADING, CommandFlag.STALE)); builder.register("COMMAND", "INFO", EnumSet.of(CommandFlag.LOADING, CommandFlag.STALE)); builder.register("COMMAND", "LIST", EnumSet.of(CommandFlag.LOADING, CommandFlag.STALE)); builder.register("LATENCY", EMPTY_FLAGS); // LATENCY subcommands builder.register("LATENCY", "DOCTOR", EnumSet.of(CommandFlag.ADMIN, CommandFlag.LOADING, CommandFlag.NOSCRIPT, CommandFlag.STALE), RequestPolicy.ALL_NODES, ResponsePolicy.SPECIAL); builder.register("LATENCY", "GRAPH", EnumSet.of(CommandFlag.ADMIN, CommandFlag.LOADING, CommandFlag.NOSCRIPT, CommandFlag.STALE), RequestPolicy.ALL_NODES, ResponsePolicy.SPECIAL); builder.register("LATENCY", "HISTOGRAM", EnumSet.of(CommandFlag.ADMIN, CommandFlag.LOADING, CommandFlag.NOSCRIPT, CommandFlag.STALE), RequestPolicy.ALL_NODES, ResponsePolicy.SPECIAL); builder.register("LATENCY", "HISTORY", EnumSet.of(CommandFlag.ADMIN, CommandFlag.LOADING, CommandFlag.NOSCRIPT, CommandFlag.STALE), RequestPolicy.ALL_NODES, ResponsePolicy.SPECIAL); builder.register("LATENCY", "LATEST", EnumSet.of(CommandFlag.ADMIN, CommandFlag.LOADING, CommandFlag.NOSCRIPT, CommandFlag.STALE), RequestPolicy.ALL_NODES, ResponsePolicy.SPECIAL); builder.register("LATENCY", "RESET", EnumSet.of(CommandFlag.ADMIN, CommandFlag.LOADING, CommandFlag.NOSCRIPT, CommandFlag.STALE), RequestPolicy.ALL_NODES, ResponsePolicy.AGG_SUM); builder.register("CLUSTER", EMPTY_FLAGS); // CLUSTER subcommands builder.register("CLUSTER", "ADDSLOTS", EnumSet.of(CommandFlag.ADMIN, CommandFlag.NO_ASYNC_LOADING, CommandFlag.STALE)); builder.register("CLUSTER", "ADDSLOTSRANGE", EnumSet.of(CommandFlag.ADMIN, CommandFlag.NO_ASYNC_LOADING, CommandFlag.STALE)); builder.register("CLUSTER", "BUMPEPOCH", EnumSet.of(CommandFlag.ADMIN, CommandFlag.NO_ASYNC_LOADING, CommandFlag.STALE)); builder.register("CLUSTER", "COUNT-FAILURE-REPORTS", EnumSet.of(CommandFlag.ADMIN, CommandFlag.STALE)); builder.register("CLUSTER", "COUNTKEYSINSLOT", EnumSet.of(CommandFlag.STALE)); builder.register("CLUSTER", "DELSLOTS", EnumSet.of(CommandFlag.ADMIN, CommandFlag.NO_ASYNC_LOADING, CommandFlag.STALE)); builder.register("CLUSTER", "DELSLOTSRANGE", EnumSet.of(CommandFlag.ADMIN, CommandFlag.NO_ASYNC_LOADING, CommandFlag.STALE)); builder.register("CLUSTER", "FAILOVER", EnumSet.of(CommandFlag.ADMIN, CommandFlag.NO_ASYNC_LOADING, CommandFlag.STALE)); builder.register("CLUSTER", "FLUSHSLOTS", EnumSet.of(CommandFlag.ADMIN, CommandFlag.NO_ASYNC_LOADING, CommandFlag.STALE)); builder.register("CLUSTER", "FORGET", EnumSet.of(CommandFlag.ADMIN, CommandFlag.NO_ASYNC_LOADING, CommandFlag.STALE)); builder.register("CLUSTER", "GETKEYSINSLOT", EnumSet.of(CommandFlag.STALE)); builder.register("CLUSTER", "INFO", EnumSet.of(CommandFlag.STALE)); builder.register("CLUSTER", "KEYSLOT", EnumSet.of(CommandFlag.STALE)); builder.register("CLUSTER", "LINKS", EnumSet.of(CommandFlag.STALE)); builder.register("CLUSTER", "MEET", EnumSet.of(CommandFlag.ADMIN, CommandFlag.NO_ASYNC_LOADING, CommandFlag.STALE)); builder.register("CLUSTER", "MIGRATION", EnumSet.of(CommandFlag.ADMIN, CommandFlag.NO_ASYNC_LOADING, CommandFlag.STALE)); builder.register("CLUSTER", "MYID", EnumSet.of(CommandFlag.STALE)); builder.register("CLUSTER", "MYSHARDID", EnumSet.of(CommandFlag.STALE)); builder.register("CLUSTER", "NODES", EnumSet.of(CommandFlag.STALE)); builder.register("CLUSTER", "REPLICAS", EnumSet.of(CommandFlag.ADMIN, CommandFlag.STALE)); builder.register("CLUSTER", "REPLICATE", EnumSet.of(CommandFlag.ADMIN, CommandFlag.NO_ASYNC_LOADING, CommandFlag.STALE)); builder.register("CLUSTER", "RESET", EnumSet.of(CommandFlag.ADMIN, CommandFlag.NOSCRIPT, CommandFlag.STALE)); builder.register("CLUSTER", "SAVECONFIG", EnumSet.of(CommandFlag.ADMIN, CommandFlag.NO_ASYNC_LOADING, CommandFlag.STALE)); builder.register("CLUSTER", "SET-CONFIG-EPOCH", EnumSet.of(CommandFlag.ADMIN, CommandFlag.NO_ASYNC_LOADING, CommandFlag.STALE)); builder.register("CLUSTER", "SETSLOT", EnumSet.of(CommandFlag.ADMIN, CommandFlag.NO_ASYNC_LOADING, CommandFlag.STALE)); builder.register("CLUSTER", "SHARDS", EnumSet.of(CommandFlag.LOADING, CommandFlag.STALE)); builder.register("CLUSTER", "SLAVES", EnumSet.of(CommandFlag.ADMIN, CommandFlag.STALE)); builder.register("CLUSTER", "SLOT-STATS", EnumSet.of(CommandFlag.LOADING, CommandFlag.STALE), RequestPolicy.ALL_SHARDS, null); builder.register("CLUSTER", "SLOTS", EnumSet.of(CommandFlag.LOADING, CommandFlag.STALE)); builder.register("CLUSTER", "SYNCSLOTS", EnumSet.of(CommandFlag.ADMIN, CommandFlag.NO_ASYNC_LOADING, CommandFlag.STALE)); builder.register("XINFO", EMPTY_FLAGS); // XINFO subcommands builder.register("XINFO", "CONSUMERS", EnumSet.of(CommandFlag.READONLY)); builder.register("XINFO", "GROUPS", EnumSet.of(CommandFlag.READONLY)); builder.register("XINFO", "STREAM", EnumSet.of(CommandFlag.READONLY)); builder.register("XGROUP", EMPTY_FLAGS); // XGROUP subcommands builder.register("XGROUP", "CREATE", EnumSet.of(CommandFlag.DENYOOM, CommandFlag.WRITE)); builder.register("XGROUP", "CREATECONSUMER", EnumSet.of(CommandFlag.DENYOOM, CommandFlag.WRITE)); builder.register("XGROUP", "DELCONSUMER", EnumSet.of(CommandFlag.WRITE)); builder.register("XGROUP", "DESTROY", EnumSet.of(CommandFlag.WRITE)); builder.register("XGROUP", "SETID", EnumSet.of(CommandFlag.WRITE)); // 1 command(s) with: admin builder.register("PFSELFTEST", EnumSet.of(CommandFlag.ADMIN)); // 2 command(s) with: blocking; request_policy=all_shards; response_policy=agg_min builder.register("WAIT", EnumSet.of(CommandFlag.BLOCKING), RequestPolicy.ALL_SHARDS, ResponsePolicy.AGG_MIN); builder.register("WAITAOF", EnumSet.of(CommandFlag.BLOCKING), RequestPolicy.ALL_SHARDS, ResponsePolicy.AGG_MIN); // 1 command(s) with: fast; request_policy=all_shards; response_policy=all_succeeded builder.register("PING", EnumSet.of(CommandFlag.FAST), RequestPolicy.ALL_SHARDS, ResponsePolicy.ALL_SUCCEEDED); // 1 command(s) with: fast builder.register("ASKING", EnumSet.of(CommandFlag.FAST)); // 1 command(s) with: readonly; request_policy=all_shards; response_policy=special builder.register("RANDOMKEY", EnumSet.of(CommandFlag.READONLY), RequestPolicy.ALL_SHARDS, ResponsePolicy.SPECIAL); // 1 command(s) with: readonly; request_policy=all_shards builder.register("KEYS", EnumSet.of(CommandFlag.READONLY), RequestPolicy.ALL_SHARDS, null); // 1 command(s) with: readonly; request_policy=special; response_policy=special builder.register("SCAN", EnumSet.of(CommandFlag.READONLY), RequestPolicy.SPECIAL, ResponsePolicy.SPECIAL); // 38 command(s) with: readonly builder.register("BITCOUNT", EnumSet.of(CommandFlag.READONLY)); builder.register("BITPOS", EnumSet.of(CommandFlag.READONLY)); builder.register("DUMP", EnumSet.of(CommandFlag.READONLY)); builder.register("GEODIST", EnumSet.of(CommandFlag.READONLY)); builder.register("GEOHASH", EnumSet.of(CommandFlag.READONLY)); builder.register("GEOPOS", EnumSet.of(CommandFlag.READONLY)); builder.register("GEORADIUSBYMEMBER_RO", EnumSet.of(CommandFlag.READONLY)); builder.register("GEORADIUS_RO", EnumSet.of(CommandFlag.READONLY)); builder.register("GEOSEARCH", EnumSet.of(CommandFlag.READONLY)); builder.register("GETRANGE", EnumSet.of(CommandFlag.READONLY)); builder.register("HGETALL", EnumSet.of(CommandFlag.READONLY)); builder.register("HKEYS", EnumSet.of(CommandFlag.READONLY)); builder.register("HRANDFIELD", EnumSet.of(CommandFlag.READONLY)); builder.register("HSCAN", EnumSet.of(CommandFlag.READONLY)); builder.register("HVALS", EnumSet.of(CommandFlag.READONLY)); builder.register("LCS", EnumSet.of(CommandFlag.READONLY)); builder.register("LINDEX", EnumSet.of(CommandFlag.READONLY)); builder.register("LPOS", EnumSet.of(CommandFlag.READONLY)); builder.register("LRANGE", EnumSet.of(CommandFlag.READONLY)); builder.register("PFCOUNT", EnumSet.of(CommandFlag.READONLY)); builder.register("SDIFF", EnumSet.of(CommandFlag.READONLY)); builder.register("SINTER", EnumSet.of(CommandFlag.READONLY)); builder.register("SMEMBERS", EnumSet.of(CommandFlag.READONLY)); builder.register("SRANDMEMBER", EnumSet.of(CommandFlag.READONLY)); builder.register("SSCAN", EnumSet.of(CommandFlag.READONLY)); builder.register("SUBSTR", EnumSet.of(CommandFlag.READONLY)); builder.register("SUNION", EnumSet.of(CommandFlag.READONLY)); builder.register("XPENDING", EnumSet.of(CommandFlag.READONLY)); builder.register("XRANGE", EnumSet.of(CommandFlag.READONLY)); builder.register("XREVRANGE", EnumSet.of(CommandFlag.READONLY)); builder.register("ZRANDMEMBER", EnumSet.of(CommandFlag.READONLY)); builder.register("ZRANGE", EnumSet.of(CommandFlag.READONLY)); builder.register("ZRANGEBYLEX", EnumSet.of(CommandFlag.READONLY)); builder.register("ZRANGEBYSCORE", EnumSet.of(CommandFlag.READONLY)); builder.register("ZREVRANGE", EnumSet.of(CommandFlag.READONLY)); builder.register("ZREVRANGEBYLEX", EnumSet.of(CommandFlag.READONLY)); builder.register("ZREVRANGEBYSCORE", EnumSet.of(CommandFlag.READONLY)); builder.register("ZSCAN", EnumSet.of(CommandFlag.READONLY)); // 2 command(s) with: write; request_policy=all_shards; response_policy=all_succeeded builder.register("FLUSHALL", EnumSet.of(CommandFlag.WRITE), RequestPolicy.ALL_SHARDS, ResponsePolicy.ALL_SUCCEEDED); builder.register("FLUSHDB", EnumSet.of(CommandFlag.WRITE), RequestPolicy.ALL_SHARDS, ResponsePolicy.ALL_SUCCEEDED); // 1 command(s) with: write; request_policy=multi_shard; response_policy=agg_sum builder.register("DEL", EnumSet.of(CommandFlag.WRITE), RequestPolicy.MULTI_SHARD, ResponsePolicy.AGG_SUM); // 8 command(s) with: write builder.register("LREM", EnumSet.of(CommandFlag.WRITE)); builder.register("LTRIM", EnumSet.of(CommandFlag.WRITE)); builder.register("RENAME", EnumSet.of(CommandFlag.WRITE)); builder.register("TRIMSLOTS", EnumSet.of(CommandFlag.WRITE)); builder.register("XTRIM", EnumSet.of(CommandFlag.WRITE)); builder.register("ZREMRANGEBYLEX", EnumSet.of(CommandFlag.WRITE)); builder.register("ZREMRANGEBYRANK", EnumSet.of(CommandFlag.WRITE)); builder.register("ZREMRANGEBYSCORE", EnumSet.of(CommandFlag.WRITE)); // 2 command(s) with: blocking, write builder.register("BLPOP", EnumSet.of(CommandFlag.BLOCKING, CommandFlag.WRITE)); builder.register("BRPOP", EnumSet.of(CommandFlag.BLOCKING, CommandFlag.WRITE)); // 1 command(s) with: denyoom, write; request_policy=multi_shard; response_policy=all_succeeded builder.register("MSET", EnumSet.of(CommandFlag.DENYOOM, CommandFlag.WRITE), RequestPolicy.MULTI_SHARD, ResponsePolicy.ALL_SUCCEEDED); // 21 command(s) with: denyoom, write builder.register("BITFIELD", EnumSet.of(CommandFlag.DENYOOM, CommandFlag.WRITE)); builder.register("BITOP", EnumSet.of(CommandFlag.DENYOOM, CommandFlag.WRITE)); builder.register("COPY", EnumSet.of(CommandFlag.DENYOOM, CommandFlag.WRITE)); builder.register("GEOADD", EnumSet.of(CommandFlag.DENYOOM, CommandFlag.WRITE)); builder.register("GEOSEARCHSTORE", EnumSet.of(CommandFlag.DENYOOM, CommandFlag.WRITE)); builder.register("LINSERT", EnumSet.of(CommandFlag.DENYOOM, CommandFlag.WRITE)); builder.register("LMOVE", EnumSet.of(CommandFlag.DENYOOM, CommandFlag.WRITE)); builder.register("LSET", EnumSet.of(CommandFlag.DENYOOM, CommandFlag.WRITE)); builder.register("MSETNX", EnumSet.of(CommandFlag.DENYOOM, CommandFlag.WRITE)); builder.register("PFMERGE", EnumSet.of(CommandFlag.DENYOOM, CommandFlag.WRITE)); builder.register("PSETEX", EnumSet.of(CommandFlag.DENYOOM, CommandFlag.WRITE)); builder.register("RESTORE", EnumSet.of(CommandFlag.DENYOOM, CommandFlag.WRITE)); builder.register("RPOPLPUSH", EnumSet.of(CommandFlag.DENYOOM, CommandFlag.WRITE)); builder.register("SDIFFSTORE", EnumSet.of(CommandFlag.DENYOOM, CommandFlag.WRITE)); builder.register("SET", EnumSet.of(CommandFlag.DENYOOM, CommandFlag.WRITE)); builder.register("SETBIT", EnumSet.of(CommandFlag.DENYOOM, CommandFlag.WRITE)); builder.register("SETEX", EnumSet.of(CommandFlag.DENYOOM, CommandFlag.WRITE)); builder.register("SETRANGE", EnumSet.of(CommandFlag.DENYOOM, CommandFlag.WRITE)); builder.register("SINTERSTORE", EnumSet.of(CommandFlag.DENYOOM, CommandFlag.WRITE)); builder.register("SUNIONSTORE", EnumSet.of(CommandFlag.DENYOOM, CommandFlag.WRITE)); builder.register("ZRANGESTORE", EnumSet.of(CommandFlag.DENYOOM, CommandFlag.WRITE)); // 1 command(s) with: fast, readonly; request_policy=all_shards; response_policy=agg_sum builder.register("DBSIZE", EnumSet.of(CommandFlag.FAST, CommandFlag.READONLY), RequestPolicy.ALL_SHARDS, ResponsePolicy.AGG_SUM); // 2 command(s) with: fast, readonly; request_policy=multi_shard; response_policy=agg_sum builder.register("EXISTS", EnumSet.of(CommandFlag.FAST, CommandFlag.READONLY), RequestPolicy.MULTI_SHARD, ResponsePolicy.AGG_SUM); builder.register("TOUCH", EnumSet.of(CommandFlag.FAST, CommandFlag.READONLY), RequestPolicy.MULTI_SHARD, ResponsePolicy.AGG_SUM); // 1 command(s) with: fast, readonly; request_policy=multi_shard builder.register("MGET", EnumSet.of(CommandFlag.FAST, CommandFlag.READONLY), RequestPolicy.MULTI_SHARD, null); // 32 command(s) with: fast, readonly builder.register("BITFIELD_RO", EnumSet.of(CommandFlag.FAST, CommandFlag.READONLY)); builder.register("DIGEST", EnumSet.of(CommandFlag.FAST, CommandFlag.READONLY)); builder.register("EXPIRETIME", EnumSet.of(CommandFlag.FAST, CommandFlag.READONLY)); builder.register("GET", EnumSet.of(CommandFlag.FAST, CommandFlag.READONLY)); builder.register("GETBIT", EnumSet.of(CommandFlag.FAST, CommandFlag.READONLY)); builder.register("HEXISTS", EnumSet.of(CommandFlag.FAST, CommandFlag.READONLY)); builder.register("HEXPIRETIME", EnumSet.of(CommandFlag.FAST, CommandFlag.READONLY)); builder.register("HGET", EnumSet.of(CommandFlag.FAST, CommandFlag.READONLY)); builder.register("HLEN", EnumSet.of(CommandFlag.FAST, CommandFlag.READONLY)); builder.register("HMGET", EnumSet.of(CommandFlag.FAST, CommandFlag.READONLY)); builder.register("HPEXPIRETIME", EnumSet.of(CommandFlag.FAST, CommandFlag.READONLY)); builder.register("HPTTL", EnumSet.of(CommandFlag.FAST, CommandFlag.READONLY)); builder.register("HSTRLEN", EnumSet.of(CommandFlag.FAST, CommandFlag.READONLY)); builder.register("HTTL", EnumSet.of(CommandFlag.FAST, CommandFlag.READONLY)); builder.register("LLEN", EnumSet.of(CommandFlag.FAST, CommandFlag.READONLY)); builder.register("LOLWUT", EnumSet.of(CommandFlag.FAST, CommandFlag.READONLY)); builder.register("PEXPIRETIME", EnumSet.of(CommandFlag.FAST, CommandFlag.READONLY)); builder.register("PTTL", EnumSet.of(CommandFlag.FAST, CommandFlag.READONLY)); builder.register("SCARD", EnumSet.of(CommandFlag.FAST, CommandFlag.READONLY)); builder.register("SISMEMBER", EnumSet.of(CommandFlag.FAST, CommandFlag.READONLY)); builder.register("SMISMEMBER", EnumSet.of(CommandFlag.FAST, CommandFlag.READONLY)); builder.register("STRLEN", EnumSet.of(CommandFlag.FAST, CommandFlag.READONLY)); builder.register("TTL", EnumSet.of(CommandFlag.FAST, CommandFlag.READONLY)); builder.register("TYPE", EnumSet.of(CommandFlag.FAST, CommandFlag.READONLY)); builder.register("XLEN", EnumSet.of(CommandFlag.FAST, CommandFlag.READONLY)); builder.register("ZCARD", EnumSet.of(CommandFlag.FAST, CommandFlag.READONLY)); builder.register("ZCOUNT", EnumSet.of(CommandFlag.FAST, CommandFlag.READONLY)); builder.register("ZLEXCOUNT", EnumSet.of(CommandFlag.FAST, CommandFlag.READONLY)); builder.register("ZMSCORE", EnumSet.of(CommandFlag.FAST, CommandFlag.READONLY)); builder.register("ZRANK", EnumSet.of(CommandFlag.FAST, CommandFlag.READONLY)); builder.register("ZREVRANK", EnumSet.of(CommandFlag.FAST, CommandFlag.READONLY)); builder.register("ZSCORE", EnumSet.of(CommandFlag.FAST, CommandFlag.READONLY)); // 1 command(s) with: fast, write; request_policy=multi_shard; response_policy=agg_sum builder.register("UNLINK", EnumSet.of(CommandFlag.FAST, CommandFlag.WRITE), RequestPolicy.MULTI_SHARD, ResponsePolicy.AGG_SUM); // 34 command(s) with: fast, write builder.register("DELEX", EnumSet.of(CommandFlag.FAST, CommandFlag.WRITE)); builder.register("EXPIRE", EnumSet.of(CommandFlag.FAST, CommandFlag.WRITE)); builder.register("EXPIREAT", EnumSet.of(CommandFlag.FAST, CommandFlag.WRITE)); builder.register("GETDEL", EnumSet.of(CommandFlag.FAST, CommandFlag.WRITE)); builder.register("GETEX", EnumSet.of(CommandFlag.FAST, CommandFlag.WRITE)); builder.register("HDEL", EnumSet.of(CommandFlag.FAST, CommandFlag.WRITE)); builder.register("HEXPIRE", EnumSet.of(CommandFlag.FAST, CommandFlag.WRITE)); builder.register("HEXPIREAT", EnumSet.of(CommandFlag.FAST, CommandFlag.WRITE)); builder.register("HGETDEL", EnumSet.of(CommandFlag.FAST, CommandFlag.WRITE)); builder.register("HGETEX", EnumSet.of(CommandFlag.FAST, CommandFlag.WRITE)); builder.register("HPERSIST", EnumSet.of(CommandFlag.FAST, CommandFlag.WRITE)); builder.register("HPEXPIRE", EnumSet.of(CommandFlag.FAST, CommandFlag.WRITE)); builder.register("HPEXPIREAT", EnumSet.of(CommandFlag.FAST, CommandFlag.WRITE)); builder.register("LPOP", EnumSet.of(CommandFlag.FAST, CommandFlag.WRITE)); builder.register("MOVE", EnumSet.of(CommandFlag.FAST, CommandFlag.WRITE)); builder.register("PERSIST", EnumSet.of(CommandFlag.FAST, CommandFlag.WRITE)); builder.register("PEXPIRE", EnumSet.of(CommandFlag.FAST, CommandFlag.WRITE)); builder.register("PEXPIREAT", EnumSet.of(CommandFlag.FAST, CommandFlag.WRITE)); builder.register("RENAMENX", EnumSet.of(CommandFlag.FAST, CommandFlag.WRITE)); builder.register("RPOP", EnumSet.of(CommandFlag.FAST, CommandFlag.WRITE)); builder.register("SMOVE", EnumSet.of(CommandFlag.FAST, CommandFlag.WRITE)); builder.register("SPOP", EnumSet.of(CommandFlag.FAST, CommandFlag.WRITE)); builder.register("SREM", EnumSet.of(CommandFlag.FAST, CommandFlag.WRITE)); builder.register("SWAPDB", EnumSet.of(CommandFlag.FAST, CommandFlag.WRITE)); builder.register("XACK", EnumSet.of(CommandFlag.FAST, CommandFlag.WRITE)); builder.register("XACKDEL", EnumSet.of(CommandFlag.FAST, CommandFlag.WRITE)); builder.register("XAUTOCLAIM", EnumSet.of(CommandFlag.FAST, CommandFlag.WRITE)); builder.register("XCFGSET", EnumSet.of(CommandFlag.FAST, CommandFlag.WRITE)); builder.register("XCLAIM", EnumSet.of(CommandFlag.FAST, CommandFlag.WRITE)); builder.register("XDEL", EnumSet.of(CommandFlag.FAST, CommandFlag.WRITE)); builder.register("XDELEX", EnumSet.of(CommandFlag.FAST, CommandFlag.WRITE)); builder.register("ZPOPMAX", EnumSet.of(CommandFlag.FAST, CommandFlag.WRITE)); builder.register("ZPOPMIN", EnumSet.of(CommandFlag.FAST, CommandFlag.WRITE)); builder.register("ZREM", EnumSet.of(CommandFlag.FAST, CommandFlag.WRITE)); // 1 command(s) with: loading, stale; request_policy=default; response_policy=special builder.register("INFO", EnumSet.of(CommandFlag.LOADING, CommandFlag.STALE), RequestPolicy.DEFAULT, ResponsePolicy.SPECIAL); // 56 command(s) with: module, readonly builder.register("CMS.INFO", EnumSet.of(CommandFlag.MODULE, CommandFlag.READONLY)); builder.register("CMS.QUERY", EnumSet.of(CommandFlag.MODULE, CommandFlag.READONLY)); builder.register("FT.AGGREGATE", EnumSet.of(CommandFlag.MODULE, CommandFlag.READONLY)); builder.register("FT.DICTDUMP", EnumSet.of(CommandFlag.MODULE, CommandFlag.READONLY)); builder.register("FT.EXPLAIN", EnumSet.of(CommandFlag.MODULE, CommandFlag.READONLY)); builder.register("FT.EXPLAINCLI", EnumSet.of(CommandFlag.MODULE, CommandFlag.READONLY)); builder.register("FT.GET", EnumSet.of(CommandFlag.MODULE, CommandFlag.READONLY)); builder.register("FT.HYBRID", EnumSet.of(CommandFlag.MODULE, CommandFlag.READONLY)); builder.register("FT.INFO", EnumSet.of(CommandFlag.MODULE, CommandFlag.READONLY)); builder.register("FT.MGET", EnumSet.of(CommandFlag.MODULE, CommandFlag.READONLY)); builder.register("FT.PROFILE", EnumSet.of(CommandFlag.MODULE, CommandFlag.READONLY)); builder.register("FT.SEARCH", EnumSet.of(CommandFlag.MODULE, CommandFlag.READONLY)); builder.register("FT.SPELLCHECK", EnumSet.of(CommandFlag.MODULE, CommandFlag.READONLY)); builder.register("FT.SUGGET", EnumSet.of(CommandFlag.MODULE, CommandFlag.READONLY)); builder.register("FT.SUGLEN", EnumSet.of(CommandFlag.MODULE, CommandFlag.READONLY)); builder.register("FT.SYNDUMP", EnumSet.of(CommandFlag.MODULE, CommandFlag.READONLY)); builder.register("FT.TAGVALS", EnumSet.of(CommandFlag.MODULE, CommandFlag.READONLY)); builder.register("FT._LIST", EnumSet.of(CommandFlag.MODULE, CommandFlag.READONLY)); builder.register("JSON.ARRINDEX", EnumSet.of(CommandFlag.MODULE, CommandFlag.READONLY)); builder.register("JSON.ARRLEN", EnumSet.of(CommandFlag.MODULE, CommandFlag.READONLY)); builder.register("JSON.DEBUG", EnumSet.of(CommandFlag.MODULE, CommandFlag.READONLY)); builder.register("JSON.GET", EnumSet.of(CommandFlag.MODULE, CommandFlag.READONLY)); builder.register("JSON.MGET", EnumSet.of(CommandFlag.MODULE, CommandFlag.READONLY)); builder.register("JSON.OBJKEYS", EnumSet.of(CommandFlag.MODULE, CommandFlag.READONLY)); builder.register("JSON.OBJLEN", EnumSet.of(CommandFlag.MODULE, CommandFlag.READONLY)); builder.register("JSON.RESP", EnumSet.of(CommandFlag.MODULE, CommandFlag.READONLY)); builder.register("JSON.STRLEN", EnumSet.of(CommandFlag.MODULE, CommandFlag.READONLY)); builder.register("JSON.TYPE", EnumSet.of(CommandFlag.MODULE, CommandFlag.READONLY)); builder.register("TDIGEST.BYRANK", EnumSet.of(CommandFlag.MODULE, CommandFlag.READONLY)); builder.register("TDIGEST.BYREVRANK", EnumSet.of(CommandFlag.MODULE, CommandFlag.READONLY)); builder.register("TDIGEST.CDF", EnumSet.of(CommandFlag.MODULE, CommandFlag.READONLY)); builder.register("TDIGEST.INFO", EnumSet.of(CommandFlag.MODULE, CommandFlag.READONLY)); builder.register("TDIGEST.MAX", EnumSet.of(CommandFlag.MODULE, CommandFlag.READONLY)); builder.register("TDIGEST.MIN", EnumSet.of(CommandFlag.MODULE, CommandFlag.READONLY)); builder.register("TDIGEST.QUANTILE", EnumSet.of(CommandFlag.MODULE, CommandFlag.READONLY)); builder.register("TDIGEST.RANK", EnumSet.of(CommandFlag.MODULE, CommandFlag.READONLY)); builder.register("TDIGEST.REVRANK", EnumSet.of(CommandFlag.MODULE, CommandFlag.READONLY)); builder.register("TDIGEST.TRIMMED_MEAN", EnumSet.of(CommandFlag.MODULE, CommandFlag.READONLY)); builder.register("TOPK.COUNT", EnumSet.of(CommandFlag.MODULE, CommandFlag.READONLY)); builder.register("TOPK.INFO", EnumSet.of(CommandFlag.MODULE, CommandFlag.READONLY)); builder.register("TOPK.LIST", EnumSet.of(CommandFlag.MODULE, CommandFlag.READONLY)); builder.register("TOPK.QUERY", EnumSet.of(CommandFlag.MODULE, CommandFlag.READONLY)); builder.register("TS.GET", EnumSet.of(CommandFlag.MODULE, CommandFlag.READONLY)); builder.register("TS.INFO", EnumSet.of(CommandFlag.MODULE, CommandFlag.READONLY)); builder.register("TS.MGET", EnumSet.of(CommandFlag.MODULE, CommandFlag.READONLY)); builder.register("TS.MRANGE", EnumSet.of(CommandFlag.MODULE, CommandFlag.READONLY)); builder.register("TS.MREVRANGE", EnumSet.of(CommandFlag.MODULE, CommandFlag.READONLY)); builder.register("TS.QUERYINDEX", EnumSet.of(CommandFlag.MODULE, CommandFlag.READONLY)); builder.register("TS.RANGE", EnumSet.of(CommandFlag.MODULE, CommandFlag.READONLY)); builder.register("TS.REVRANGE", EnumSet.of(CommandFlag.MODULE, CommandFlag.READONLY)); builder.register("VISMEMBER", EnumSet.of(CommandFlag.MODULE, CommandFlag.READONLY)); builder.register("VRANDMEMBER", EnumSet.of(CommandFlag.MODULE, CommandFlag.READONLY)); builder.register("VRANGE", EnumSet.of(CommandFlag.MODULE, CommandFlag.READONLY)); builder.register("VSIM", EnumSet.of(CommandFlag.MODULE, CommandFlag.READONLY)); builder.register("_FT.CONFIG", EnumSet.of(CommandFlag.MODULE, CommandFlag.READONLY)); builder.register("_FT.DEBUG", EnumSet.of(CommandFlag.MODULE, CommandFlag.READONLY)); // 21 command(s) with: module, write builder.register("FT.ALIASDEL", EnumSet.of(CommandFlag.MODULE, CommandFlag.WRITE)); builder.register("FT.DEL", EnumSet.of(CommandFlag.MODULE, CommandFlag.WRITE)); builder.register("FT.DICTDEL", EnumSet.of(CommandFlag.MODULE, CommandFlag.WRITE)); builder.register("FT.DROP", EnumSet.of(CommandFlag.MODULE, CommandFlag.WRITE)); builder.register("FT.DROPINDEX", EnumSet.of(CommandFlag.MODULE, CommandFlag.WRITE)); builder.register("FT.SUGDEL", EnumSet.of(CommandFlag.MODULE, CommandFlag.WRITE)); builder.register("FT._ALIASDELIFX", EnumSet.of(CommandFlag.MODULE, CommandFlag.WRITE)); builder.register("FT._DROPIFX", EnumSet.of(CommandFlag.MODULE, CommandFlag.WRITE)); builder.register("FT._DROPINDEXIFX", EnumSet.of(CommandFlag.MODULE, CommandFlag.WRITE)); builder.register("JSON.ARRPOP", EnumSet.of(CommandFlag.MODULE, CommandFlag.WRITE)); builder.register("JSON.ARRTRIM", EnumSet.of(CommandFlag.MODULE, CommandFlag.WRITE)); builder.register("JSON.CLEAR", EnumSet.of(CommandFlag.MODULE, CommandFlag.WRITE)); builder.register("JSON.DEL", EnumSet.of(CommandFlag.MODULE, CommandFlag.WRITE)); builder.register("JSON.FORGET", EnumSet.of(CommandFlag.MODULE, CommandFlag.WRITE)); builder.register("JSON.NUMINCRBY", EnumSet.of(CommandFlag.MODULE, CommandFlag.WRITE)); builder.register("JSON.NUMMULTBY", EnumSet.of(CommandFlag.MODULE, CommandFlag.WRITE)); builder.register("JSON.NUMPOWBY", EnumSet.of(CommandFlag.MODULE, CommandFlag.WRITE)); builder.register("JSON.TOGGLE", EnumSet.of(CommandFlag.MODULE, CommandFlag.WRITE)); builder.register("TS.DEL", EnumSet.of(CommandFlag.MODULE, CommandFlag.WRITE)); builder.register("TS.DELETERULE", EnumSet.of(CommandFlag.MODULE, CommandFlag.WRITE)); builder.register("VREM", EnumSet.of(CommandFlag.MODULE, CommandFlag.WRITE)); // 6 command(s) with: movablekeys, readonly builder.register("SINTERCARD", EnumSet.of(CommandFlag.MOVABLEKEYS, CommandFlag.READONLY)); builder.register("SORT_RO", EnumSet.of(CommandFlag.MOVABLEKEYS, CommandFlag.READONLY)); builder.register("ZDIFF", EnumSet.of(CommandFlag.MOVABLEKEYS, CommandFlag.READONLY)); builder.register("ZINTER", EnumSet.of(CommandFlag.MOVABLEKEYS, CommandFlag.READONLY)); builder.register("ZINTERCARD", EnumSet.of(CommandFlag.MOVABLEKEYS, CommandFlag.READONLY)); builder.register("ZUNION", EnumSet.of(CommandFlag.MOVABLEKEYS, CommandFlag.READONLY)); // 3 command(s) with: movablekeys, write builder.register("LMPOP", EnumSet.of(CommandFlag.MOVABLEKEYS, CommandFlag.WRITE)); builder.register("MIGRATE", EnumSet.of(CommandFlag.MOVABLEKEYS, CommandFlag.WRITE)); builder.register("ZMPOP", EnumSet.of(CommandFlag.MOVABLEKEYS, CommandFlag.WRITE)); // 1 command(s) with: admin, denyoom, write builder.register("PFDEBUG", EnumSet.of(CommandFlag.ADMIN, CommandFlag.DENYOOM, CommandFlag.WRITE)); // 2 command(s) with: admin, noscript, no_async_loading builder.register("BGREWRITEAOF", EnumSet.of(CommandFlag.ADMIN, CommandFlag.NOSCRIPT, CommandFlag.NO_ASYNC_LOADING)); builder.register("BGSAVE", EnumSet.of(CommandFlag.ADMIN, CommandFlag.NOSCRIPT, CommandFlag.NO_ASYNC_LOADING)); // 1 command(s) with: admin, noscript, stale builder.register("FAILOVER", EnumSet.of(CommandFlag.ADMIN, CommandFlag.NOSCRIPT, CommandFlag.STALE)); // 1 command(s) with: asking, denyoom, write builder.register("RESTORE-ASKING", EnumSet.of(CommandFlag.ASKING, CommandFlag.DENYOOM, CommandFlag.WRITE)); // 2 command(s) with: blocking, denyoom, write builder.register("BLMOVE", EnumSet.of(CommandFlag.BLOCKING, CommandFlag.DENYOOM, CommandFlag.WRITE)); builder.register("BRPOPLPUSH", EnumSet.of(CommandFlag.BLOCKING, CommandFlag.DENYOOM, CommandFlag.WRITE)); // 2 command(s) with: blocking, fast, write builder.register("BZPOPMAX", EnumSet.of(CommandFlag.BLOCKING, CommandFlag.FAST, CommandFlag.WRITE)); builder.register("BZPOPMIN", EnumSet.of(CommandFlag.BLOCKING, CommandFlag.FAST, CommandFlag.WRITE)); // 1 command(s) with: blocking, movablekeys, readonly builder.register("XREAD", EnumSet.of(CommandFlag.BLOCKING, CommandFlag.MOVABLEKEYS, CommandFlag.READONLY)); // 3 command(s) with: blocking, movablekeys, write builder.register("BLMPOP", EnumSet.of(CommandFlag.BLOCKING, CommandFlag.MOVABLEKEYS, CommandFlag.WRITE)); builder.register("BZMPOP", EnumSet.of(CommandFlag.BLOCKING, CommandFlag.MOVABLEKEYS, CommandFlag.WRITE)); builder.register("XREADGROUP", EnumSet.of(CommandFlag.BLOCKING, CommandFlag.MOVABLEKEYS, CommandFlag.WRITE)); // 24 command(s) with: denyoom, fast, write builder.register("APPEND", EnumSet.of(CommandFlag.DENYOOM, CommandFlag.FAST, CommandFlag.WRITE)); builder.register("DECR", EnumSet.of(CommandFlag.DENYOOM, CommandFlag.FAST, CommandFlag.WRITE)); builder.register("DECRBY", EnumSet.of(CommandFlag.DENYOOM, CommandFlag.FAST, CommandFlag.WRITE)); builder.register("GETSET", EnumSet.of(CommandFlag.DENYOOM, CommandFlag.FAST, CommandFlag.WRITE)); builder.register("HINCRBY", EnumSet.of(CommandFlag.DENYOOM, CommandFlag.FAST, CommandFlag.WRITE)); builder.register("HINCRBYFLOAT", EnumSet.of(CommandFlag.DENYOOM, CommandFlag.FAST, CommandFlag.WRITE)); builder.register("HMSET", EnumSet.of(CommandFlag.DENYOOM, CommandFlag.FAST, CommandFlag.WRITE)); builder.register("HSET", EnumSet.of(CommandFlag.DENYOOM, CommandFlag.FAST, CommandFlag.WRITE)); builder.register("HSETEX", EnumSet.of(CommandFlag.DENYOOM, CommandFlag.FAST, CommandFlag.WRITE)); builder.register("HSETNX", EnumSet.of(CommandFlag.DENYOOM, CommandFlag.FAST, CommandFlag.WRITE)); builder.register("INCR", EnumSet.of(CommandFlag.DENYOOM, CommandFlag.FAST, CommandFlag.WRITE)); builder.register("INCRBY", EnumSet.of(CommandFlag.DENYOOM, CommandFlag.FAST, CommandFlag.WRITE)); builder.register("INCRBYFLOAT", EnumSet.of(CommandFlag.DENYOOM, CommandFlag.FAST, CommandFlag.WRITE)); builder.register("LPUSH", EnumSet.of(CommandFlag.DENYOOM, CommandFlag.FAST, CommandFlag.WRITE)); builder.register("LPUSHX", EnumSet.of(CommandFlag.DENYOOM, CommandFlag.FAST, CommandFlag.WRITE)); builder.register("PFADD", EnumSet.of(CommandFlag.DENYOOM, CommandFlag.FAST, CommandFlag.WRITE)); builder.register("RPUSH", EnumSet.of(CommandFlag.DENYOOM, CommandFlag.FAST, CommandFlag.WRITE)); builder.register("RPUSHX", EnumSet.of(CommandFlag.DENYOOM, CommandFlag.FAST, CommandFlag.WRITE)); builder.register("SADD", EnumSet.of(CommandFlag.DENYOOM, CommandFlag.FAST, CommandFlag.WRITE)); builder.register("SETNX", EnumSet.of(CommandFlag.DENYOOM, CommandFlag.FAST, CommandFlag.WRITE)); builder.register("XADD", EnumSet.of(CommandFlag.DENYOOM, CommandFlag.FAST, CommandFlag.WRITE)); builder.register("XSETID", EnumSet.of(CommandFlag.DENYOOM, CommandFlag.FAST, CommandFlag.WRITE)); builder.register("ZADD", EnumSet.of(CommandFlag.DENYOOM, CommandFlag.FAST, CommandFlag.WRITE)); builder.register("ZINCRBY", EnumSet.of(CommandFlag.DENYOOM, CommandFlag.FAST, CommandFlag.WRITE)); // 47 command(s) with: denyoom, module, write builder.register("BF.ADD", EnumSet.of(CommandFlag.DENYOOM, CommandFlag.MODULE, CommandFlag.WRITE)); builder.register("BF.INSERT", EnumSet.of(CommandFlag.DENYOOM, CommandFlag.MODULE, CommandFlag.WRITE)); builder.register("BF.LOADCHUNK", EnumSet.of(CommandFlag.DENYOOM, CommandFlag.MODULE, CommandFlag.WRITE)); builder.register("BF.MADD", EnumSet.of(CommandFlag.DENYOOM, CommandFlag.MODULE, CommandFlag.WRITE)); builder.register("BF.RESERVE", EnumSet.of(CommandFlag.DENYOOM, CommandFlag.MODULE, CommandFlag.WRITE)); builder.register("CF.ADD", EnumSet.of(CommandFlag.DENYOOM, CommandFlag.MODULE, CommandFlag.WRITE)); builder.register("CF.ADDNX", EnumSet.of(CommandFlag.DENYOOM, CommandFlag.MODULE, CommandFlag.WRITE)); builder.register("CF.INSERT", EnumSet.of(CommandFlag.DENYOOM, CommandFlag.MODULE, CommandFlag.WRITE)); builder.register("CF.INSERTNX", EnumSet.of(CommandFlag.DENYOOM, CommandFlag.MODULE, CommandFlag.WRITE)); builder.register("CF.LOADCHUNK", EnumSet.of(CommandFlag.DENYOOM, CommandFlag.MODULE, CommandFlag.WRITE)); builder.register("CF.RESERVE", EnumSet.of(CommandFlag.DENYOOM, CommandFlag.MODULE, CommandFlag.WRITE)); builder.register("CMS.INCRBY", EnumSet.of(CommandFlag.DENYOOM, CommandFlag.MODULE, CommandFlag.WRITE)); builder.register("CMS.INITBYDIM", EnumSet.of(CommandFlag.DENYOOM, CommandFlag.MODULE, CommandFlag.WRITE)); builder.register("CMS.INITBYPROB", EnumSet.of(CommandFlag.DENYOOM, CommandFlag.MODULE, CommandFlag.WRITE)); builder.register("CMS.MERGE", EnumSet.of(CommandFlag.DENYOOM, CommandFlag.MODULE, CommandFlag.WRITE)); builder.register("FT.ADD", EnumSet.of(CommandFlag.DENYOOM, CommandFlag.MODULE, CommandFlag.WRITE)); builder.register("FT.ALIASADD", EnumSet.of(CommandFlag.DENYOOM, CommandFlag.MODULE, CommandFlag.WRITE)); builder.register("FT.ALIASUPDATE", EnumSet.of(CommandFlag.DENYOOM, CommandFlag.MODULE, CommandFlag.WRITE)); builder.register("FT.ALTER", EnumSet.of(CommandFlag.DENYOOM, CommandFlag.MODULE, CommandFlag.WRITE)); builder.register("FT.CREATE", EnumSet.of(CommandFlag.DENYOOM, CommandFlag.MODULE, CommandFlag.WRITE)); builder.register("FT.DICTADD", EnumSet.of(CommandFlag.DENYOOM, CommandFlag.MODULE, CommandFlag.WRITE)); builder.register("FT.SAFEADD", EnumSet.of(CommandFlag.DENYOOM, CommandFlag.MODULE, CommandFlag.WRITE)); builder.register("FT.SUGADD", EnumSet.of(CommandFlag.DENYOOM, CommandFlag.MODULE, CommandFlag.WRITE)); builder.register("FT.SYNADD", EnumSet.of(CommandFlag.DENYOOM, CommandFlag.MODULE, CommandFlag.WRITE)); builder.register("FT.SYNUPDATE", EnumSet.of(CommandFlag.DENYOOM, CommandFlag.MODULE, CommandFlag.WRITE)); builder.register("FT._ALIASADDIFNX", EnumSet.of(CommandFlag.DENYOOM, CommandFlag.MODULE, CommandFlag.WRITE)); builder.register("FT._ALTERIFNX", EnumSet.of(CommandFlag.DENYOOM, CommandFlag.MODULE, CommandFlag.WRITE)); builder.register("FT._CREATEIFNX", EnumSet.of(CommandFlag.DENYOOM, CommandFlag.MODULE, CommandFlag.WRITE)); builder.register("JSON.ARRAPPEND", EnumSet.of(CommandFlag.DENYOOM, CommandFlag.MODULE, CommandFlag.WRITE)); builder.register("JSON.ARRINSERT", EnumSet.of(CommandFlag.DENYOOM, CommandFlag.MODULE, CommandFlag.WRITE)); builder.register("JSON.MERGE", EnumSet.of(CommandFlag.DENYOOM, CommandFlag.MODULE, CommandFlag.WRITE)); builder.register("JSON.MSET", EnumSet.of(CommandFlag.DENYOOM, CommandFlag.MODULE, CommandFlag.WRITE)); builder.register("JSON.SET", EnumSet.of(CommandFlag.DENYOOM, CommandFlag.MODULE, CommandFlag.WRITE)); builder.register("JSON.STRAPPEND", EnumSet.of(CommandFlag.DENYOOM, CommandFlag.MODULE, CommandFlag.WRITE)); builder.register("TDIGEST.ADD", EnumSet.of(CommandFlag.DENYOOM, CommandFlag.MODULE, CommandFlag.WRITE)); builder.register("TDIGEST.CREATE", EnumSet.of(CommandFlag.DENYOOM, CommandFlag.MODULE, CommandFlag.WRITE)); builder.register("TDIGEST.RESET", EnumSet.of(CommandFlag.DENYOOM, CommandFlag.MODULE, CommandFlag.WRITE)); builder.register("TOPK.ADD", EnumSet.of(CommandFlag.DENYOOM, CommandFlag.MODULE, CommandFlag.WRITE)); builder.register("TOPK.INCRBY", EnumSet.of(CommandFlag.DENYOOM, CommandFlag.MODULE, CommandFlag.WRITE)); builder.register("TOPK.RESERVE", EnumSet.of(CommandFlag.DENYOOM, CommandFlag.MODULE, CommandFlag.WRITE)); builder.register("TS.ADD", EnumSet.of(CommandFlag.DENYOOM, CommandFlag.MODULE, CommandFlag.WRITE)); builder.register("TS.ALTER", EnumSet.of(CommandFlag.DENYOOM, CommandFlag.MODULE, CommandFlag.WRITE)); builder.register("TS.CREATE", EnumSet.of(CommandFlag.DENYOOM, CommandFlag.MODULE, CommandFlag.WRITE)); builder.register("TS.DECRBY", EnumSet.of(CommandFlag.DENYOOM, CommandFlag.MODULE, CommandFlag.WRITE)); builder.register("TS.INCRBY", EnumSet.of(CommandFlag.DENYOOM, CommandFlag.MODULE, CommandFlag.WRITE)); builder.register("TS.MADD", EnumSet.of(CommandFlag.DENYOOM, CommandFlag.MODULE, CommandFlag.WRITE)); builder.register("VADD", EnumSet.of(CommandFlag.DENYOOM, CommandFlag.MODULE, CommandFlag.WRITE)); // 1 command(s) with: denyoom, movablekeys, write; request_policy=multi_shard; // response_policy=all_succeeded builder.register("MSETEX", EnumSet.of(CommandFlag.DENYOOM, CommandFlag.MOVABLEKEYS, CommandFlag.WRITE), RequestPolicy.MULTI_SHARD, ResponsePolicy.ALL_SUCCEEDED); // 6 command(s) with: denyoom, movablekeys, write builder.register("GEORADIUS", EnumSet.of(CommandFlag.DENYOOM, CommandFlag.MOVABLEKEYS, CommandFlag.WRITE)); builder.register("GEORADIUSBYMEMBER", EnumSet.of(CommandFlag.DENYOOM, CommandFlag.MOVABLEKEYS, CommandFlag.WRITE)); builder.register("SORT", EnumSet.of(CommandFlag.DENYOOM, CommandFlag.MOVABLEKEYS, CommandFlag.WRITE)); builder.register("ZDIFFSTORE", EnumSet.of(CommandFlag.DENYOOM, CommandFlag.MOVABLEKEYS, CommandFlag.WRITE)); builder.register("ZINTERSTORE", EnumSet.of(CommandFlag.DENYOOM, CommandFlag.MOVABLEKEYS, CommandFlag.WRITE)); builder.register("ZUNIONSTORE", EnumSet.of(CommandFlag.DENYOOM, CommandFlag.MOVABLEKEYS, CommandFlag.WRITE)); // 6 command(s) with: fast, loading, stale builder.register("ECHO", EnumSet.of(CommandFlag.FAST, CommandFlag.LOADING, CommandFlag.STALE)); builder.register("LASTSAVE", EnumSet.of(CommandFlag.FAST, CommandFlag.LOADING, CommandFlag.STALE)); builder.register("READONLY", EnumSet.of(CommandFlag.FAST, CommandFlag.LOADING, CommandFlag.STALE)); builder.register("READWRITE", EnumSet.of(CommandFlag.FAST, CommandFlag.LOADING, CommandFlag.STALE)); builder.register("SELECT", EnumSet.of(CommandFlag.FAST, CommandFlag.LOADING, CommandFlag.STALE)); builder.register("TIME", EnumSet.of(CommandFlag.FAST, CommandFlag.LOADING, CommandFlag.STALE)); // 19 command(s) with: fast, module, readonly builder.register("BF.CARD", EnumSet.of(CommandFlag.FAST, CommandFlag.MODULE, CommandFlag.READONLY)); builder.register("BF.DEBUG", EnumSet.of(CommandFlag.FAST, CommandFlag.MODULE, CommandFlag.READONLY)); builder.register("BF.EXISTS", EnumSet.of(CommandFlag.FAST, CommandFlag.MODULE, CommandFlag.READONLY)); builder.register("BF.INFO", EnumSet.of(CommandFlag.FAST, CommandFlag.MODULE, CommandFlag.READONLY)); builder.register("BF.MEXISTS", EnumSet.of(CommandFlag.FAST, CommandFlag.MODULE, CommandFlag.READONLY)); builder.register("BF.SCANDUMP", EnumSet.of(CommandFlag.FAST, CommandFlag.MODULE, CommandFlag.READONLY)); builder.register("CF.COMPACT", EnumSet.of(CommandFlag.FAST, CommandFlag.MODULE, CommandFlag.READONLY)); builder.register("CF.COUNT", EnumSet.of(CommandFlag.FAST, CommandFlag.MODULE, CommandFlag.READONLY)); builder.register("CF.DEBUG", EnumSet.of(CommandFlag.FAST, CommandFlag.MODULE, CommandFlag.READONLY)); builder.register("CF.EXISTS", EnumSet.of(CommandFlag.FAST, CommandFlag.MODULE, CommandFlag.READONLY)); builder.register("CF.INFO", EnumSet.of(CommandFlag.FAST, CommandFlag.MODULE, CommandFlag.READONLY)); builder.register("CF.MEXISTS", EnumSet.of(CommandFlag.FAST, CommandFlag.MODULE, CommandFlag.READONLY)); builder.register("CF.SCANDUMP", EnumSet.of(CommandFlag.FAST, CommandFlag.MODULE, CommandFlag.READONLY)); builder.register("VCARD", EnumSet.of(CommandFlag.FAST, CommandFlag.MODULE, CommandFlag.READONLY)); builder.register("VDIM", EnumSet.of(CommandFlag.FAST, CommandFlag.MODULE, CommandFlag.READONLY)); builder.register("VEMB", EnumSet.of(CommandFlag.FAST, CommandFlag.MODULE, CommandFlag.READONLY)); builder.register("VGETATTR", EnumSet.of(CommandFlag.FAST, CommandFlag.MODULE, CommandFlag.READONLY)); builder.register("VINFO", EnumSet.of(CommandFlag.FAST, CommandFlag.MODULE, CommandFlag.READONLY)); builder.register("VLINKS", EnumSet.of(CommandFlag.FAST, CommandFlag.MODULE, CommandFlag.READONLY)); // 3 command(s) with: fast, module, write builder.register("CF.DEL", EnumSet.of(CommandFlag.FAST, CommandFlag.MODULE, CommandFlag.WRITE)); builder.register("TS.CREATERULE", EnumSet.of(CommandFlag.FAST, CommandFlag.MODULE, CommandFlag.WRITE)); builder.register("VSETATTR", EnumSet.of(CommandFlag.FAST, CommandFlag.MODULE, CommandFlag.WRITE)); // 3 command(s) with: module, noscript, readonly builder.register("SEARCH.CLUSTERREFRESH", EnumSet.of(CommandFlag.MODULE, CommandFlag.NOSCRIPT, CommandFlag.READONLY)); builder.register("TIMESERIES.CLUSTERSET", EnumSet.of(CommandFlag.MODULE, CommandFlag.NOSCRIPT, CommandFlag.READONLY)); builder.register("TIMESERIES.REFRESHCLUSTER", EnumSet.of(CommandFlag.MODULE, CommandFlag.NOSCRIPT, CommandFlag.READONLY)); // 2 command(s) with: admin, loading, noscript, stale builder.register("DEBUG", EnumSet.of(CommandFlag.ADMIN, CommandFlag.LOADING, CommandFlag.NOSCRIPT, CommandFlag.STALE)); builder.register("MONITOR", EnumSet.of(CommandFlag.ADMIN, CommandFlag.LOADING, CommandFlag.NOSCRIPT, CommandFlag.STALE)); // 3 command(s) with: admin, noscript, no_async_loading, no_multi builder.register("PSYNC", EnumSet.of(CommandFlag.ADMIN, CommandFlag.NOSCRIPT, CommandFlag.NO_ASYNC_LOADING, CommandFlag.NO_MULTI)); builder.register("SAVE", EnumSet.of(CommandFlag.ADMIN, CommandFlag.NOSCRIPT, CommandFlag.NO_ASYNC_LOADING, CommandFlag.NO_MULTI)); builder.register("SYNC", EnumSet.of(CommandFlag.ADMIN, CommandFlag.NOSCRIPT, CommandFlag.NO_ASYNC_LOADING, CommandFlag.NO_MULTI)); // 2 command(s) with: admin, noscript, no_async_loading, stale builder.register("REPLICAOF", EnumSet.of(CommandFlag.ADMIN, CommandFlag.NOSCRIPT, CommandFlag.NO_ASYNC_LOADING, CommandFlag.STALE)); builder.register("SLAVEOF", EnumSet.of(CommandFlag.ADMIN, CommandFlag.NOSCRIPT, CommandFlag.NO_ASYNC_LOADING, CommandFlag.STALE)); // 1 command(s) with: denyoom, module, movablekeys, write builder.register("TDIGEST.MERGE", EnumSet.of(CommandFlag.DENYOOM, CommandFlag.MODULE, CommandFlag.MOVABLEKEYS, CommandFlag.WRITE)); // 1 command(s) with: fast, loading, noscript, stale builder.register("ROLE", EnumSet.of(CommandFlag.FAST, CommandFlag.LOADING, CommandFlag.NOSCRIPT, CommandFlag.STALE)); // 2 command(s) with: fast, loading, pubsub, stale builder.register("PUBLISH", EnumSet.of(CommandFlag.FAST, CommandFlag.LOADING, CommandFlag.PUBSUB, CommandFlag.STALE)); builder.register("SPUBLISH", EnumSet.of(CommandFlag.FAST, CommandFlag.LOADING, CommandFlag.PUBSUB, CommandFlag.STALE)); // 2 command(s) with: loading, module, noscript, readonly builder.register("SEARCH.CLUSTERINFO", EnumSet.of(CommandFlag.LOADING, CommandFlag.MODULE, CommandFlag.NOSCRIPT, CommandFlag.READONLY)); builder.register("SEARCH.CLUSTERSET", EnumSet.of(CommandFlag.LOADING, CommandFlag.MODULE, CommandFlag.NOSCRIPT, CommandFlag.READONLY)); // 6 command(s) with: loading, noscript, pubsub, stale builder.register("PSUBSCRIBE", EnumSet.of(CommandFlag.LOADING, CommandFlag.NOSCRIPT, CommandFlag.PUBSUB, CommandFlag.STALE)); builder.register("PUNSUBSCRIBE", EnumSet.of(CommandFlag.LOADING, CommandFlag.NOSCRIPT, CommandFlag.PUBSUB, CommandFlag.STALE)); builder.register("SSUBSCRIBE", EnumSet.of(CommandFlag.LOADING, CommandFlag.NOSCRIPT, CommandFlag.PUBSUB, CommandFlag.STALE)); builder.register("SUBSCRIBE", EnumSet.of(CommandFlag.LOADING, CommandFlag.NOSCRIPT, CommandFlag.PUBSUB, CommandFlag.STALE)); builder.register("SUNSUBSCRIBE", EnumSet.of(CommandFlag.LOADING, CommandFlag.NOSCRIPT, CommandFlag.PUBSUB, CommandFlag.STALE)); builder.register("UNSUBSCRIBE", EnumSet.of(CommandFlag.LOADING, CommandFlag.NOSCRIPT, CommandFlag.PUBSUB, CommandFlag.STALE)); // 1 command(s) with: loading, noscript, skip_slowlog, stale builder.register("EXEC", EnumSet.of(CommandFlag.LOADING, CommandFlag.NOSCRIPT, CommandFlag.SKIP_SLOWLOG, CommandFlag.STALE)); // 1 command(s) with: admin, allow_busy, loading, noscript, stale builder.register("REPLCONF", EnumSet.of(CommandFlag.ADMIN, CommandFlag.ALLOW_BUSY, CommandFlag.LOADING, CommandFlag.NOSCRIPT, CommandFlag.STALE)); // 4 command(s) with: allow_busy, fast, loading, noscript, stale builder.register("DISCARD", EnumSet.of(CommandFlag.ALLOW_BUSY, CommandFlag.FAST, CommandFlag.LOADING, CommandFlag.NOSCRIPT, CommandFlag.STALE)); builder.register("MULTI", EnumSet.of(CommandFlag.ALLOW_BUSY, CommandFlag.FAST, CommandFlag.LOADING, CommandFlag.NOSCRIPT, CommandFlag.STALE)); builder.register("UNWATCH", EnumSet.of(CommandFlag.ALLOW_BUSY, CommandFlag.FAST, CommandFlag.LOADING, CommandFlag.NOSCRIPT, CommandFlag.STALE)); builder.register("WATCH", EnumSet.of(CommandFlag.ALLOW_BUSY, CommandFlag.FAST, CommandFlag.LOADING, CommandFlag.NOSCRIPT, CommandFlag.STALE)); // 3 command(s) with: movablekeys, noscript, no_mandatory_keys, skip_monitor, stale builder.register("EVAL", EnumSet.of(CommandFlag.MOVABLEKEYS, CommandFlag.NOSCRIPT, CommandFlag.NO_MANDATORY_KEYS, CommandFlag.SKIP_MONITOR, CommandFlag.STALE)); builder.register("EVALSHA", EnumSet.of(CommandFlag.MOVABLEKEYS, CommandFlag.NOSCRIPT, CommandFlag.NO_MANDATORY_KEYS, CommandFlag.SKIP_MONITOR, CommandFlag.STALE)); builder.register("FCALL", EnumSet.of(CommandFlag.MOVABLEKEYS, CommandFlag.NOSCRIPT, CommandFlag.NO_MANDATORY_KEYS, CommandFlag.SKIP_MONITOR, CommandFlag.STALE)); // 1 command(s) with: admin, allow_busy, loading, noscript, no_multi, stale builder.register("SHUTDOWN", EnumSet.of(CommandFlag.ADMIN, CommandFlag.ALLOW_BUSY, CommandFlag.LOADING, CommandFlag.NOSCRIPT, CommandFlag.NO_MULTI, CommandFlag.STALE)); // 4 command(s) with: allow_busy, fast, loading, noscript, no_auth, stale builder.register("AUTH", EnumSet.of(CommandFlag.ALLOW_BUSY, CommandFlag.FAST, CommandFlag.LOADING, CommandFlag.NOSCRIPT, CommandFlag.NO_AUTH, CommandFlag.STALE)); builder.register("HELLO", EnumSet.of(CommandFlag.ALLOW_BUSY, CommandFlag.FAST, CommandFlag.LOADING, CommandFlag.NOSCRIPT, CommandFlag.NO_AUTH, CommandFlag.STALE)); builder.register("QUIT", EnumSet.of(CommandFlag.ALLOW_BUSY, CommandFlag.FAST, CommandFlag.LOADING, CommandFlag.NOSCRIPT, CommandFlag.NO_AUTH, CommandFlag.STALE)); builder.register("RESET", EnumSet.of(CommandFlag.ALLOW_BUSY, CommandFlag.FAST, CommandFlag.LOADING, CommandFlag.NOSCRIPT, CommandFlag.NO_AUTH, CommandFlag.STALE)); // 3 command(s) with: movablekeys, noscript, no_mandatory_keys, readonly, skip_monitor, stale builder.register("EVALSHA_RO", EnumSet.of(CommandFlag.MOVABLEKEYS, CommandFlag.NOSCRIPT, CommandFlag.NO_MANDATORY_KEYS, CommandFlag.READONLY, CommandFlag.SKIP_MONITOR, CommandFlag.STALE)); builder.register("EVAL_RO", EnumSet.of(CommandFlag.MOVABLEKEYS, CommandFlag.NOSCRIPT, CommandFlag.NO_MANDATORY_KEYS, CommandFlag.READONLY, CommandFlag.SKIP_MONITOR, CommandFlag.STALE)); builder.register("FCALL_RO", EnumSet.of(CommandFlag.MOVABLEKEYS, CommandFlag.NOSCRIPT, CommandFlag.NO_MANDATORY_KEYS, CommandFlag.READONLY, CommandFlag.SKIP_MONITOR, CommandFlag.STALE)); } } ================================================ FILE: src/main/java/redis/clients/jedis/StreamEntryID.java ================================================ package redis.clients.jedis; import java.io.IOException; import java.io.Serializable; import redis.clients.jedis.util.SafeEncoder; public class StreamEntryID implements Comparable, Serializable { private static final long serialVersionUID = 1L; private long time; private long sequence; public StreamEntryID() { this(0, 0L); } public StreamEntryID(byte[] id) { this(SafeEncoder.encode(id)); } public StreamEntryID(String id) { String[] split = id.split("-"); this.time = Long.parseLong(split[0]); this.sequence = Long.parseLong(split[1]); } public StreamEntryID(long time) { this(time, 0); } public StreamEntryID(long time, long sequence) { this.time = time; this.sequence = sequence; } @Override public String toString() { return time + "-" + sequence; } @Override public boolean equals(Object obj) { if (this == obj) return true; if (obj == null) return false; if (getClass() != obj.getClass()) return false; StreamEntryID other = (StreamEntryID) obj; return this.time == other.time && this.sequence == other.sequence; } @Override public int hashCode() { return this.toString().hashCode(); } @Override public int compareTo(StreamEntryID other) { int timeCompare = Long.compare(this.time, other.time); return timeCompare != 0 ? timeCompare : Long.compare(this.sequence, other.sequence); } public long getTime() { return time; } public long getSequence() { return sequence; } private void writeObject(java.io.ObjectOutputStream out) throws IOException { out.writeLong(this.time); out.writeLong(this.sequence); } private void readObject(java.io.ObjectInputStream in) throws IOException, ClassNotFoundException { this.time = in.readLong(); this.sequence = in.readLong(); } /** * Should be used only with XADD * * {@code XADD mystream * field1 value1} */ public static final StreamEntryID NEW_ENTRY = new StreamEntryID() { private static final long serialVersionUID = 1L; @Override public String toString() { return "*"; } }; /** * Should be used only with XGROUP CREATE * * {@code XGROUP CREATE mystream consumer-group-name $} */ public static final StreamEntryID XGROUP_LAST_ENTRY = new StreamEntryID() { private static final long serialVersionUID = 1L; @Override public String toString() { return "$"; } }; /** * @deprecated Use {@link StreamEntryID#XGROUP_LAST_ENTRY} for XGROUP CREATE command or * {@link StreamEntryID#XREAD_NEW_ENTRY} for XREAD command. */ @Deprecated public static final StreamEntryID LAST_ENTRY = XGROUP_LAST_ENTRY; /** * Should be used only with XREAD * * {@code XREAD BLOCK 5000 COUNT 100 STREAMS mystream $} */ public static final StreamEntryID XREAD_NEW_ENTRY = new StreamEntryID() { private static final long serialVersionUID = 1L; @Override public String toString() { return "$"; } }; /** * Should be used only with XREADGROUP *

* {@code XREADGROUP GROUP mygroup myconsumer STREAMS mystream >} */ public static final StreamEntryID XREADGROUP_UNDELIVERED_ENTRY = new StreamEntryID() { private static final long serialVersionUID = 1L; @Override public String toString() { return ">"; } }; /** * @deprecated Use {@link StreamEntryID#XREADGROUP_UNDELIVERED_ENTRY}. */ @Deprecated public static final StreamEntryID UNRECEIVED_ENTRY = XREADGROUP_UNDELIVERED_ENTRY; /** * Can be used in XRANGE, XREVRANGE and XPENDING commands. */ public static final StreamEntryID MINIMUM_ID = new StreamEntryID() { private static final long serialVersionUID = 1L; @Override public String toString() { return "-"; } }; /** * Can be used in XRANGE, XREVRANGE and XPENDING commands. */ public static final StreamEntryID MAXIMUM_ID = new StreamEntryID() { private static final long serialVersionUID = 1L; @Override public String toString() { return "+"; } }; /** * Should be used only with XREAD * * {@code XREAD STREAMS mystream +} */ public static final StreamEntryID XREAD_LAST_ENTRY = new StreamEntryID() { private static final long serialVersionUID = 1L; @Override public String toString() { return "+"; } }; } ================================================ FILE: src/main/java/redis/clients/jedis/Transaction.java ================================================ package redis.clients.jedis; import static redis.clients.jedis.Protocol.Command.DISCARD; import static redis.clients.jedis.Protocol.Command.EXEC; import static redis.clients.jedis.Protocol.Command.MULTI; import static redis.clients.jedis.Protocol.Command.UNWATCH; import java.util.ArrayList; import java.util.LinkedList; import java.util.List; import java.util.Queue; import redis.clients.jedis.exceptions.JedisConnectionException; import redis.clients.jedis.exceptions.JedisDataException; /** * A transaction based on pipelining. */ public class Transaction extends AbstractTransaction { private final Queue> pipelinedResponses = new LinkedList<>(); protected final Connection connection; private final boolean closeConnection; private boolean broken = false; private boolean inWatch = false; private boolean inMulti = false; /** * Creates a new transaction. * * A MULTI command will be added to be sent to server. WATCH/UNWATCH/MULTI commands must not be * called with this object. * @param connection connection */ public Transaction(Connection connection) { this(connection, true); } /** * Creates a new transaction. * * A user wanting to WATCH/UNWATCH keys followed by a call to MULTI ({@link #multi()}) it should * be {@code doMulti=false}. * * @param connection connection * @param doMulti {@code false} should be set to enable manual WATCH, UNWATCH and MULTI */ public Transaction(Connection connection, boolean doMulti) { this(connection, doMulti, false, createCommandObjects(connection)); } /** * Creates a new transaction. * * A user wanting to WATCH/UNWATCH keys followed by a call to MULTI ({@link #multi()}) it should * be {@code doMulti=false}. * * @param connection connection * @param doMulti {@code false} should be set to enable manual WATCH, UNWATCH and MULTI * @param closeConnection should the 'connection' be closed when 'close()' is called? */ public Transaction(Connection connection, boolean doMulti, boolean closeConnection) { this(connection, doMulti, closeConnection, createCommandObjects(connection)); } /** * Creates a new transaction. * * A user wanting to WATCH/UNWATCH keys followed by a call to MULTI ({@link #multi()}) it should * be {@code doMulti=false}. * * @param connection connection * @param commandObjects command objects * @param doMulti {@code false} should be set to enable manual WATCH, UNWATCH and MULTI * @param closeConnection should the 'connection' be closed when 'close()' is called? */ Transaction(Connection connection, boolean doMulti, boolean closeConnection, CommandObjects commandObjects) { super(commandObjects); this.connection = connection; this.closeConnection = closeConnection; if (doMulti) multi(); } private static CommandObjects createCommandObjects(Connection connection) { CommandObjects commandObjects = new CommandObjects(); RedisProtocol proto = connection.getRedisProtocol(); if (proto != null) commandObjects.setProtocol(proto); return commandObjects; } @Override public final void multi() { connection.sendCommand(MULTI); // processMultiResponse(); // do nothing inMulti = true; } @Override public String watch(final String... keys) { String status = connection.executeCommand(commandObjects.watch(keys)); inWatch = true; return status; } @Override public String watch(final byte[]... keys) { String status = connection.executeCommand(commandObjects.watch(keys)); inWatch = true; return status; } @Override public String unwatch() { connection.sendCommand(UNWATCH); String status = connection.getStatusCodeReply(); inWatch = false; return status; } @Override protected final Response appendCommand(CommandObject commandObject) { Response response = null; if (!inMulti) { response = execute(commandObject); } else { connection.sendCommand(commandObject.getArguments()); response = new Response<>(commandObject.getBuilder()); pipelinedResponses.add(response); } return response; } private Response execute(CommandObject commandObject) { Response response; try { T result = connection.executeCommand(commandObject); response = Response.of(result); } catch (JedisDataException e) { response = Response.error(e); } return response; } @Override public final void close() { try { clear(); } finally { if (closeConnection) { connection.close(); } } } @Deprecated // TODO: private public final void clear() { if (broken) { return; } if (inMulti) { discard(); } else if (inWatch) { unwatch(); } } @Override public List exec() { if (!inMulti) { throw new IllegalStateException("EXEC without MULTI"); } try { // ignore QUEUED (or ERROR) // processPipelinedResponses(pipelinedResponses.size()); List queuedCmdResponses = connection.getMany(1 + pipelinedResponses.size()); connection.sendCommand(EXEC); List unformatted; try { unformatted = connection.getObjectMultiBulkReply(); } catch (JedisDataException jce) { // A command may fail to be queued, so there may be an error before EXEC is called // In this case, the server will discard all commands in the transaction and return the EXECABORT error. // Enhance the final error with suppressed errors. queuedCmdResponses.stream() .filter(o -> o instanceof Exception) .map(o -> (Exception) o) .forEach(jce::addSuppressed); throw jce; } if (unformatted == null) { pipelinedResponses.clear(); return null; } List formatted = new ArrayList<>(unformatted.size()); for (Object o : unformatted) { try { Response response = pipelinedResponses.poll(); response.set(o); formatted.add(response.get()); } catch (JedisDataException e) { formatted.add(e); } } return formatted; } catch (JedisConnectionException jce) { broken = true; throw jce; } finally { inMulti = false; inWatch = false; pipelinedResponses.clear(); onAfterExec(); } } @Override public String discard() { if (!inMulti) { throw new IllegalStateException("DISCARD without MULTI"); } try { // ignore QUEUED (or ERROR) // processPipelinedResponses(pipelinedResponses.size()); connection.getMany(1 + pipelinedResponses.size()); connection.sendCommand(DISCARD); return connection.getStatusCodeReply(); } catch (JedisConnectionException jce) { broken = true; throw jce; } finally { inMulti = false; inWatch = false; pipelinedResponses.clear(); onAfterDiscard(); } } /** * Hook method called after exec() completes (successfully or with error). * Subclasses can override this to perform cleanup actions. */ protected void onAfterExec() { // Default implementation does nothing } /** * Hook method called after discard() completes (successfully or with error). * Subclasses can override this to perform cleanup actions. */ protected void onAfterDiscard() { // Default implementation does nothing } } ================================================ FILE: src/main/java/redis/clients/jedis/UnifiedJedis.java ================================================ package redis.clients.jedis; import java.net.URI; import java.time.Duration; import java.util.List; import java.util.Map; import java.util.Set; import org.json.JSONArray; import redis.clients.jedis.annots.Experimental; import redis.clients.jedis.annots.VisibleForTesting; import redis.clients.jedis.args.*; import redis.clients.jedis.bloom.*; import redis.clients.jedis.commands.JedisCommands; import redis.clients.jedis.commands.JedisBinaryCommands; import redis.clients.jedis.commands.ProtocolCommand; import redis.clients.jedis.commands.SampleBinaryKeyedCommands; import redis.clients.jedis.commands.SampleKeyedCommands; import redis.clients.jedis.commands.RedisModuleCommands; import redis.clients.jedis.search.hybrid.FTHybridParams; import redis.clients.jedis.search.hybrid.HybridResult; import redis.clients.jedis.util.CompareCondition; import redis.clients.jedis.csc.Cache; import redis.clients.jedis.csc.CacheConfig; import redis.clients.jedis.csc.CacheConnection; import redis.clients.jedis.csc.CacheFactory; import redis.clients.jedis.exceptions.JedisException; import redis.clients.jedis.executors.*; import redis.clients.jedis.json.JsonSetParams; import redis.clients.jedis.json.Path; import redis.clients.jedis.json.Path2; import redis.clients.jedis.params.VAddParams; import redis.clients.jedis.params.VSimParams; import redis.clients.jedis.resps.RawVector; import redis.clients.jedis.json.JsonObjectMapper; import redis.clients.jedis.mcf.MultiDbPipeline; import redis.clients.jedis.mcf.MultiDbConnectionProvider; import redis.clients.jedis.mcf.MultiDbTransaction; import redis.clients.jedis.params.*; import redis.clients.jedis.providers.*; import redis.clients.jedis.resps.*; import redis.clients.jedis.search.*; import redis.clients.jedis.search.aggr.AggregationBuilder; import redis.clients.jedis.search.aggr.AggregationResult; import redis.clients.jedis.search.aggr.FtAggregateIteration; import redis.clients.jedis.search.schemafields.SchemaField; import redis.clients.jedis.timeseries.*; import redis.clients.jedis.util.IOUtils; import redis.clients.jedis.util.JedisURIHelper; import redis.clients.jedis.util.KeyValue; public class UnifiedJedis implements JedisCommands, JedisBinaryCommands, SampleKeyedCommands, SampleBinaryKeyedCommands, RedisModuleCommands, AutoCloseable { @Deprecated protected RedisProtocol protocol = null; protected final ConnectionProvider provider; protected final CommandExecutor executor; protected final CommandObjects commandObjects; private final Cache cache; /** * @deprecated Use {@link RedisClient#create()} instead. */ @Deprecated public UnifiedJedis() { this(new HostAndPort(Protocol.DEFAULT_HOST, Protocol.DEFAULT_PORT)); } /** * @deprecated Use {@link RedisClient#create(HostAndPort)} instead. */ @Deprecated public UnifiedJedis(HostAndPort hostAndPort) { this(new PooledConnectionProvider(hostAndPort), (RedisProtocol) null); } /** * @deprecated Use {@link RedisClient#create(String)} instead. */ @Deprecated public UnifiedJedis(final String url) { this(URI.create(url)); } /** * @deprecated Use {@link RedisClient#create(URI)} instead. */ @Deprecated public UnifiedJedis(final URI uri) { this(JedisURIHelper.getHostAndPort(uri), DefaultJedisClientConfig.builder() .user(JedisURIHelper.getUser(uri)).password(JedisURIHelper.getPassword(uri)) .database(JedisURIHelper.getDBIndex(uri)).protocol(JedisURIHelper.getRedisProtocol(uri)) .ssl(JedisURIHelper.isRedisSSLScheme(uri)).build()); } /** * Create a new UnifiedJedis with the provided URI and JedisClientConfig object. Note that all fields * that can be parsed from the URI will be used instead of the corresponding configuration values. This includes * the following fields: user, password, database, protocol version, and whether to use SSL. * * For example, if the URI is "redis://user:password@localhost:6379/1", the user and password fields will be set * to "user" and "password" respectively, the database field will be set to 1. Those fields will be ignored * from the JedisClientConfig object. * * @param uri The URI to connect to * @param config The JedisClientConfig object to use * @deprecated Use {@link RedisClient#builder()} to configure the client with custom settings. */ @Deprecated public UnifiedJedis(final URI uri, JedisClientConfig config) { this(JedisURIHelper.getHostAndPort(uri), DefaultJedisClientConfig.builder() .connectionTimeoutMillis(config.getConnectionTimeoutMillis()) .socketTimeoutMillis(config.getSocketTimeoutMillis()) .blockingSocketTimeoutMillis(config.getBlockingSocketTimeoutMillis()) .user(JedisURIHelper.getUser(uri)).password(JedisURIHelper.getPassword(uri)) .database(JedisURIHelper.getDBIndex(uri)).clientName(config.getClientName()) .protocol(JedisURIHelper.getRedisProtocol(uri)) .ssl(JedisURIHelper.isRedisSSLScheme(uri)).sslSocketFactory(config.getSslSocketFactory()) .sslParameters(config.getSslParameters()).hostnameVerifier(config.getHostnameVerifier()).build()); } /** * @deprecated Use {@link RedisClient#builder()} to configure the client with custom settings. */ @Deprecated public UnifiedJedis(HostAndPort hostAndPort, JedisClientConfig clientConfig) { this(new PooledConnectionProvider(hostAndPort, clientConfig), clientConfig.getRedisProtocol()); } /** * @deprecated Use {@link RedisClient#builder()} to configure the client with client-side caching. */ @Experimental @Deprecated public UnifiedJedis(HostAndPort hostAndPort, JedisClientConfig clientConfig, CacheConfig cacheConfig) { this(hostAndPort, clientConfig, CacheFactory.getCache(cacheConfig)); } /** * @deprecated Use {@link RedisClient#builder()} to configure the client with client-side caching. */ @Experimental @Deprecated public UnifiedJedis(HostAndPort hostAndPort, JedisClientConfig clientConfig, Cache cache) { this(new PooledConnectionProvider(hostAndPort, clientConfig, cache), clientConfig.getRedisProtocol(), cache); } @Deprecated public UnifiedJedis(ConnectionProvider provider) { this(new DefaultCommandExecutor(provider), provider); } protected UnifiedJedis(ConnectionProvider provider, RedisProtocol protocol) { this(new DefaultCommandExecutor(provider), provider, new CommandObjects(), protocol); } @Experimental protected UnifiedJedis(ConnectionProvider provider, RedisProtocol protocol, Cache cache) { this(new DefaultCommandExecutor(provider), provider, new CommandObjects(), protocol, cache); } /** * The constructor to directly use a custom {@link JedisSocketFactory}. *

* WARNING: Using this constructor means a {@link NullPointerException} will be occurred if * {@link UnifiedJedis#provider} is accessed. * @deprecated Use {@link RedisClient#builder()} to configure the client with custom settings. */ @Deprecated public UnifiedJedis(JedisSocketFactory socketFactory) { this(new Connection(socketFactory)); } /** * The constructor to directly use a custom {@link JedisSocketFactory}. *

* WARNING: Using this constructor means a {@link NullPointerException} will be occurred if * {@link UnifiedJedis#provider} is accessed. * @deprecated Use {@link RedisClient#builder()} to configure the client with custom settings. */ @Deprecated public UnifiedJedis(JedisSocketFactory socketFactory, JedisClientConfig clientConfig) { this(new Connection(socketFactory, clientConfig)); } /** * The constructor to directly use a {@link Connection}. *

* WARNING: Using this constructor means a {@link NullPointerException} will be occurred if * {@link UnifiedJedis#provider} is accessed. * @deprecated */ @Deprecated public UnifiedJedis(Connection connection) { this.provider = null; this.executor = new SimpleCommandExecutor(connection); this.commandObjects = new CommandObjects(); RedisProtocol proto = connection.getRedisProtocol(); if (proto != null) { this.commandObjects.setProtocol(proto); } if (connection instanceof CacheConnection) { this.cache = ((CacheConnection) connection).getCache(); } else { this.cache = null; } } /** * @deprecated Use {@link RedisClusterClient#builder()} to configure the cluster client. */ @Deprecated public UnifiedJedis(ClusterConnectionProvider provider, int maxAttempts, Duration maxTotalRetriesDuration) { this(new ClusterCommandExecutor(provider, maxAttempts, maxTotalRetriesDuration, StaticCommandFlagsRegistry.registry()), provider, new ClusterCommandObjects()); } @Deprecated protected UnifiedJedis(ClusterConnectionProvider provider, int maxAttempts, Duration maxTotalRetriesDuration, RedisProtocol protocol) { this(new ClusterCommandExecutor(provider, maxAttempts, maxTotalRetriesDuration, StaticCommandFlagsRegistry.registry()), provider, new ClusterCommandObjects(), protocol); } @Deprecated protected UnifiedJedis(ClusterConnectionProvider provider, int maxAttempts, Duration maxTotalRetriesDuration, RedisProtocol protocol, Cache cache) { this(new ClusterCommandExecutor(provider, maxAttempts, maxTotalRetriesDuration, StaticCommandFlagsRegistry.registry()), provider, new ClusterCommandObjects(), protocol, cache); } /** * @deprecated Use {@link RedisClient#builder()} to configure the client with retry settings. */ @Deprecated public UnifiedJedis(ConnectionProvider provider, int maxAttempts, Duration maxTotalRetriesDuration) { this(new RetryableCommandExecutor(provider, maxAttempts, maxTotalRetriesDuration), provider); } /** * The constructor to use a custom {@link CommandExecutor}. *

* WARNING: Using this constructor means a {@link NullPointerException} will be occurred if * {@link UnifiedJedis#provider} is accessed. * @deprecated Use {@link RedisClient#builder()} to configure the client with custom settings. */ @Deprecated public UnifiedJedis(CommandExecutor executor) { this(executor, (ConnectionProvider) null); } private UnifiedJedis(CommandExecutor executor, ConnectionProvider provider) { this(executor, provider, new CommandObjects()); } /** * Uses a fetched connection to process protocol. Should be avoided if possible. * @deprecated */ @VisibleForTesting @Deprecated public UnifiedJedis(CommandExecutor executor, ConnectionProvider provider, CommandObjects commandObjects) { this(executor, provider, commandObjects, null, null); if (this.provider != null) { try (Connection conn = this.provider.getConnection()) { if (conn != null) { RedisProtocol proto = conn.getRedisProtocol(); if (proto != null) { this.commandObjects.setProtocol(proto); } } } catch (JedisException je) { } } } @Experimental private UnifiedJedis(CommandExecutor executor, ConnectionProvider provider, CommandObjects commandObjects, RedisProtocol protocol) { this(executor, provider, commandObjects, protocol, (Cache) null); } @Experimental UnifiedJedis(CommandExecutor executor, ConnectionProvider provider, CommandObjects commandObjects, RedisProtocol protocol, Cache cache) { if (cache != null && protocol != RedisProtocol.RESP3) { throw new IllegalArgumentException("Client-side caching is only supported with RESP3."); } this.provider = provider; this.executor = executor; this.commandObjects = commandObjects; if (protocol != null) { this.commandObjects.setProtocol(protocol); } this.cache = cache; } @Override public void close() { IOUtils.closeQuietly(this.executor); } @Deprecated protected final void setProtocol(RedisProtocol protocol) { this.protocol = protocol; this.commandObjects.setProtocol(this.protocol); } public final T executeCommand(CommandObject commandObject) { return executor.executeCommand(commandObject); } public Cache getCache() { return cache; } public String ping() { return executeCommand(commandObjects.ping()); } public String echo(String string) { return executeCommand(commandObjects.echo(string)); } public String flushDB() { return executeCommand(commandObjects.flushDB()); } public String flushAll() { return executeCommand(commandObjects.flushAll()); } public String configSet(String parameter, String value) { return executeCommand(commandObjects.configSet(parameter, value)); } public String info() { return executeCommand(commandObjects.info()); } public String info(String section) { return executeCommand(commandObjects.info(section)); } public String hotkeysStart(HotkeysParams params) { return executeCommand(commandObjects.hotkeysStart(params)); } public String hotkeysStop() { return executeCommand(commandObjects.hotkeysStop()); } public String hotkeysReset() { return executeCommand(commandObjects.hotkeysReset()); } public HotkeysInfo hotkeysGet() { return executeCommand(commandObjects.hotkeysGet()); } // Key commands @Override public boolean exists(String key) { return executeCommand(commandObjects.exists(key)); } @Override public long exists(String... keys) { return executeCommand(commandObjects.exists(keys)); } @Override public long persist(String key) { return executeCommand(commandObjects.persist(key)); } @Override public String type(String key) { return executeCommand(commandObjects.type(key)); } @Override public boolean exists(byte[] key) { return executeCommand(commandObjects.exists(key)); } @Override public long exists(byte[]... keys) { return executeCommand(commandObjects.exists(keys)); } @Override public long persist(byte[] key) { return executeCommand(commandObjects.persist(key)); } @Override public String type(byte[] key) { return executeCommand(commandObjects.type(key)); } @Override public byte[] dump(String key) { return executeCommand(commandObjects.dump(key)); } @Override public String restore(String key, long ttl, byte[] serializedValue) { return executeCommand(commandObjects.restore(key, ttl, serializedValue)); } @Override public String restore(String key, long ttl, byte[] serializedValue, RestoreParams params) { return executeCommand(commandObjects.restore(key, ttl, serializedValue, params)); } @Override public byte[] dump(byte[] key) { return executeCommand(commandObjects.dump(key)); } @Override public String restore(byte[] key, long ttl, byte[] serializedValue) { return executeCommand(commandObjects.restore(key, ttl, serializedValue)); } @Override public String restore(byte[] key, long ttl, byte[] serializedValue, RestoreParams params) { return executeCommand(commandObjects.restore(key, ttl, serializedValue, params)); } @Override public long expire(String key, long seconds) { return executeCommand(commandObjects.expire(key, seconds)); } @Override public long expire(String key, long seconds, ExpiryOption expiryOption) { return executeCommand(commandObjects.expire(key, seconds, expiryOption)); } @Override public long pexpire(String key, long milliseconds) { return executeCommand(commandObjects.pexpire(key, milliseconds)); } @Override public long pexpire(String key, long milliseconds, ExpiryOption expiryOption) { return executeCommand(commandObjects.pexpire(key, milliseconds, expiryOption)); } @Override public long expireTime(String key) { return executeCommand(commandObjects.expireTime(key)); } @Override public long pexpireTime(String key) { return executeCommand(commandObjects.pexpireTime(key)); } @Override public long expireAt(String key, long unixTime) { return executeCommand(commandObjects.expireAt(key, unixTime)); } @Override public long expireAt(String key, long unixTime, ExpiryOption expiryOption) { return executeCommand(commandObjects.expireAt(key, unixTime, expiryOption)); } @Override public long pexpireAt(String key, long millisecondsTimestamp) { return executeCommand(commandObjects.pexpireAt(key, millisecondsTimestamp)); } @Override public long pexpireAt(String key, long millisecondsTimestamp, ExpiryOption expiryOption) { return executeCommand(commandObjects.pexpireAt(key, millisecondsTimestamp, expiryOption)); } @Override public long expire(byte[] key, long seconds) { return executeCommand(commandObjects.expire(key, seconds)); } @Override public long expire(byte[] key, long seconds, ExpiryOption expiryOption) { return executeCommand(commandObjects.expire(key, seconds, expiryOption)); } @Override public long pexpire(byte[] key, long milliseconds) { return executeCommand(commandObjects.pexpire(key, milliseconds)); } @Override public long pexpire(byte[] key, long milliseconds, ExpiryOption expiryOption) { return executeCommand(commandObjects.pexpire(key, milliseconds, expiryOption)); } @Override public long expireTime(byte[] key) { return executeCommand(commandObjects.expireTime(key)); } @Override public long pexpireTime(byte[] key) { return executeCommand(commandObjects.pexpireTime(key)); } @Override public long expireAt(byte[] key, long unixTime) { return executeCommand(commandObjects.expireAt(key, unixTime)); } @Override public long expireAt(byte[] key, long unixTime, ExpiryOption expiryOption) { return executeCommand(commandObjects.expireAt(key, unixTime, expiryOption)); } @Override public long pexpireAt(byte[] key, long millisecondsTimestamp) { return executeCommand(commandObjects.pexpireAt(key, millisecondsTimestamp)); } @Override public long pexpireAt(byte[] key, long millisecondsTimestamp, ExpiryOption expiryOption) { return executeCommand(commandObjects.pexpireAt(key, millisecondsTimestamp, expiryOption)); } @Override public long ttl(String key) { return executeCommand(commandObjects.ttl(key)); } @Override public long pttl(String key) { return executeCommand(commandObjects.pttl(key)); } @Override public long touch(String key) { return executeCommand(commandObjects.touch(key)); } @Override public long touch(String... keys) { return executeCommand(commandObjects.touch(keys)); } @Override public long ttl(byte[] key) { return executeCommand(commandObjects.ttl(key)); } @Override public long pttl(byte[] key) { return executeCommand(commandObjects.pttl(key)); } @Override public long touch(byte[] key) { return executeCommand(commandObjects.touch(key)); } @Override public long touch(byte[]... keys) { return executeCommand(commandObjects.touch(keys)); } @Override public List sort(String key) { return executeCommand(commandObjects.sort(key)); } @Override public List sort(String key, SortingParams sortingParams) { return executeCommand(commandObjects.sort(key, sortingParams)); } @Override public long sort(String key, String dstkey) { return executeCommand(commandObjects.sort(key, dstkey)); } @Override public long sort(String key, SortingParams sortingParams, String dstkey) { return executeCommand(commandObjects.sort(key, sortingParams, dstkey)); } @Override public List sortReadonly(String key, SortingParams sortingParams) { return executeCommand(commandObjects.sortReadonly(key, sortingParams)); } @Override public List sort(byte[] key) { return executeCommand(commandObjects.sort(key)); } @Override public List sort(byte[] key, SortingParams sortingParams) { return executeCommand(commandObjects.sort(key, sortingParams)); } @Override public long sort(byte[] key, byte[] dstkey) { return executeCommand(commandObjects.sort(key, dstkey)); } @Override public List sortReadonly(byte[] key, SortingParams sortingParams) { return executeCommand(commandObjects.sortReadonly(key, sortingParams)); } @Override public long sort(byte[] key, SortingParams sortingParams, byte[] dstkey) { return executeCommand(commandObjects.sort(key, sortingParams, dstkey)); } @Override public long del(String key) { return executeCommand(commandObjects.del(key)); } @Override public long delex(String key, CompareCondition condition) { return executeCommand(commandObjects.delex(key, condition)); } @Override public long del(String... keys) { return executeCommand(commandObjects.del(keys)); } @Override public long unlink(String key) { return executeCommand(commandObjects.unlink(key)); } @Override public long delex(byte[] key, CompareCondition condition) { return executeCommand(commandObjects.delex(key, condition)); } @Override public long unlink(String... keys) { return executeCommand(commandObjects.unlink(keys)); } @Override public long del(byte[] key) { return executeCommand(commandObjects.del(key)); } @Override public long del(byte[]... keys) { return executeCommand(commandObjects.del(keys)); } @Override public long unlink(byte[] key) { return executeCommand(commandObjects.unlink(key)); } @Override public long unlink(byte[]... keys) { return executeCommand(commandObjects.unlink(keys)); } @Override public Long memoryUsage(String key) { return executeCommand(commandObjects.memoryUsage(key)); } @Override public Long memoryUsage(String key, int samples) { return executeCommand(commandObjects.memoryUsage(key, samples)); } @Override public Long memoryUsage(byte[] key) { return executeCommand(commandObjects.memoryUsage(key)); } @Override public Long memoryUsage(byte[] key, int samples) { return executeCommand(commandObjects.memoryUsage(key, samples)); } @Override public boolean copy(String srcKey, String dstKey, boolean replace) { return executeCommand(commandObjects.copy(srcKey, dstKey, replace)); } @Override public String rename(String oldkey, String newkey) { return executeCommand(commandObjects.rename(oldkey, newkey)); } @Override public long renamenx(String oldkey, String newkey) { return executeCommand(commandObjects.renamenx(oldkey, newkey)); } @Override public boolean copy(byte[] srcKey, byte[] dstKey, boolean replace) { return executeCommand(commandObjects.copy(srcKey, dstKey, replace)); } @Override public String rename(byte[] oldkey, byte[] newkey) { return executeCommand(commandObjects.rename(oldkey, newkey)); } @Override public long renamenx(byte[] oldkey, byte[] newkey) { return executeCommand(commandObjects.renamenx(oldkey, newkey)); } public long dbSize() { return executeCommand(commandObjects.dbSize()); } @Override public Set keys(String pattern) { return executeCommand(commandObjects.keys(pattern)); } @Override public ScanResult scan(String cursor) { return executeCommand(commandObjects.scan(cursor)); } @Override public ScanResult scan(String cursor, ScanParams params) { return executeCommand(commandObjects.scan(cursor, params)); } @Override public ScanResult scan(String cursor, ScanParams params, String type) { return executeCommand(commandObjects.scan(cursor, params, type)); } /** * @param batchCount COUNT for each batch execution * @param match pattern * @return scan iteration */ public ScanIteration scanIteration(int batchCount, String match) { return new ScanIteration(provider, batchCount, match); } /** * @param batchCount COUNT for each batch execution * @param match pattern * @param type key type * @return scan iteration */ public ScanIteration scanIteration(int batchCount, String match, String type) { return new ScanIteration(provider, batchCount, match, type); } @Override public Set keys(byte[] pattern) { return executeCommand(commandObjects.keys(pattern)); } @Override public ScanResult scan(byte[] cursor) { return executeCommand(commandObjects.scan(cursor)); } @Override public ScanResult scan(byte[] cursor, ScanParams params) { return executeCommand(commandObjects.scan(cursor, params)); } @Override public ScanResult scan(byte[] cursor, ScanParams params, byte[] type) { return executeCommand(commandObjects.scan(cursor, params, type)); } @Override public String randomKey() { return executeCommand(commandObjects.randomKey()); } @Override public byte[] randomBinaryKey() { return executeCommand(commandObjects.randomBinaryKey()); } // Key commands // String commands @Override public String set(String key, String value) { return executeCommand(commandObjects.set(key, value)); } @Override public String set(String key, String value, SetParams params) { return executeCommand(commandObjects.set(key, value, params)); } @Override public String get(String key) { return executeCommand(commandObjects.get(key)); } @Override public String digestKey(String key) { return executeCommand(commandObjects.digestKey(key)); } @Override public String setGet(String key, String value) { return executeCommand(commandObjects.setGet(key, value)); } @Override public String setGet(String key, String value, SetParams params) { return executeCommand(commandObjects.setGet(key, value, params)); } @Override public String getDel(String key) { return executeCommand(commandObjects.getDel(key)); } @Override public String getEx(String key, GetExParams params) { return executeCommand(commandObjects.getEx(key, params)); } @Override public String set(byte[] key, byte[] value) { return executeCommand(commandObjects.set(key, value)); } @Override public String set(byte[] key, byte[] value, SetParams params) { return executeCommand(commandObjects.set(key, value, params)); } @Override public byte[] get(byte[] key) { return executeCommand(commandObjects.get(key)); } @Override public byte[] digestKey(byte[] key) { return executeCommand(commandObjects.digestKey(key)); } @Override public byte[] setGet(byte[] key, byte[] value) { return executeCommand(commandObjects.setGet(key, value)); } @Override public byte[] setGet(byte[] key, byte[] value, SetParams params) { return executeCommand(commandObjects.setGet(key, value, params)); } @Override public byte[] getDel(byte[] key) { return executeCommand(commandObjects.getDel(key)); } @Override public byte[] getEx(byte[] key, GetExParams params) { return executeCommand(commandObjects.getEx(key, params)); } @Override public boolean setbit(String key, long offset, boolean value) { return executeCommand(commandObjects.setbit(key, offset, value)); } @Override public boolean getbit(String key, long offset) { return executeCommand(commandObjects.getbit(key, offset)); } @Override public long setrange(String key, long offset, String value) { return executeCommand(commandObjects.setrange(key, offset, value)); } @Override public String getrange(String key, long startOffset, long endOffset) { return executeCommand(commandObjects.getrange(key, startOffset, endOffset)); } @Override public boolean setbit(byte[] key, long offset, boolean value) { return executeCommand(commandObjects.setbit(key, offset, value)); } @Override public boolean getbit(byte[] key, long offset) { return executeCommand(commandObjects.getbit(key, offset)); } @Override public long setrange(byte[] key, long offset, byte[] value) { return executeCommand(commandObjects.setrange(key, offset, value)); } @Override public byte[] getrange(byte[] key, long startOffset, long endOffset) { return executeCommand(commandObjects.getrange(key, startOffset, endOffset)); } /** * @deprecated Use {@link UnifiedJedis#setGet(java.lang.String, java.lang.String)}. */ @Deprecated @Override public String getSet(String key, String value) { return executeCommand(commandObjects.getSet(key, value)); } /** * @deprecated Use {@link UnifiedJedis#set(String, String, redis.clients.jedis.params.SetParams)} with {@link redis.clients.jedis.params.SetParams#nx()}. * Deprecated in Jedis 7.3.0. Mirrors Redis deprecation since 2.6.12. */ @Deprecated @Override public long setnx(String key, String value) { return executeCommand(commandObjects.setnx(key, value)); } /** * @deprecated Use {@link UnifiedJedis#set(String, String, redis.clients.jedis.params.SetParams)} with {@link redis.clients.jedis.params.SetParams#ex(long)}. * Deprecated in Jedis 7.3.0. Mirrors Redis deprecation since 2.6.12. */ @Deprecated @Override public String setex(String key, long seconds, String value) { return executeCommand(commandObjects.setex(key, seconds, value)); } /** * @deprecated Use {@link UnifiedJedis#set(String, String, redis.clients.jedis.params.SetParams)} with {@link redis.clients.jedis.params.SetParams#px(long)}. * Deprecated in Jedis 7.3.0. Mirrors Redis deprecation since 2.6.12. */ @Deprecated @Override public String psetex(String key, long milliseconds, String value) { return executeCommand(commandObjects.psetex(key, milliseconds, value)); } /** * @deprecated Use {@link UnifiedJedis#setGet(byte[], byte[])}. */ @Deprecated @Override public byte[] getSet(byte[] key, byte[] value) { return executeCommand(commandObjects.getSet(key, value)); } /** * @deprecated Use {@link UnifiedJedis#set(byte[], byte[], redis.clients.jedis.params.SetParams)} with {@link redis.clients.jedis.params.SetParams#nx()}. * Deprecated in Jedis 7.3.0. Mirrors Redis deprecation since 2.6.12. */ @Deprecated @Override public long setnx(byte[] key, byte[] value) { return executeCommand(commandObjects.setnx(key, value)); } /** * @deprecated Use {@link UnifiedJedis#set(byte[], byte[], redis.clients.jedis.params.SetParams)} with {@link redis.clients.jedis.params.SetParams#ex(long)}. * Deprecated in Jedis 7.3.0. Mirrors Redis deprecation since 2.6.12. */ @Deprecated @Override public String setex(byte[] key, long seconds, byte[] value) { return executeCommand(commandObjects.setex(key, seconds, value)); } /** * @deprecated Use {@link UnifiedJedis#set(byte[], byte[], redis.clients.jedis.params.SetParams)} with {@link redis.clients.jedis.params.SetParams#px(long)}. * Deprecated in Jedis 7.3.0. Mirrors Redis deprecation since 2.6.12. */ @Deprecated @Override public String psetex(byte[] key, long milliseconds, byte[] value) { return executeCommand(commandObjects.psetex(key, milliseconds, value)); } @Override public long incr(String key) { return executeCommand(commandObjects.incr(key)); } @Override public long incrBy(String key, long increment) { return executeCommand(commandObjects.incrBy(key, increment)); } @Override public double incrByFloat(String key, double increment) { return executeCommand(commandObjects.incrByFloat(key, increment)); } @Override public long decr(String key) { return executeCommand(commandObjects.decr(key)); } @Override public long decrBy(String key, long decrement) { return executeCommand(commandObjects.decrBy(key, decrement)); } @Override public long incr(byte[] key) { return executeCommand(commandObjects.incr(key)); } @Override public long incrBy(byte[] key, long increment) { return executeCommand(commandObjects.incrBy(key, increment)); } @Override public double incrByFloat(byte[] key, double increment) { return executeCommand(commandObjects.incrByFloat(key, increment)); } @Override public long decr(byte[] key) { return executeCommand(commandObjects.decr(key)); } @Override public long decrBy(byte[] key, long decrement) { return executeCommand(commandObjects.decrBy(key, decrement)); } @Override public List mget(String... keys) { return executeCommand(commandObjects.mget(keys)); } @Override public String mset(String... keysvalues) { return executeCommand(commandObjects.mset(keysvalues)); } @Override public long msetnx(String... keysvalues) { return executeCommand(commandObjects.msetnx(keysvalues)); } @Override public boolean msetex(MSetExParams params, String... keysvalues) { return executeCommand(commandObjects.msetex(params, keysvalues)); } @Override public List mget(byte[]... keys) { return executeCommand(commandObjects.mget(keys)); } @Override public String mset(byte[]... keysvalues) { return executeCommand(commandObjects.mset(keysvalues)); } @Override public boolean msetex(MSetExParams params, byte[]... keysvalues) { return executeCommand(commandObjects.msetex(params, keysvalues)); } @Override public long msetnx(byte[]... keysvalues) { return executeCommand(commandObjects.msetnx(keysvalues)); } @Override public long append(String key, String value) { return executeCommand(commandObjects.append(key, value)); } /** * @deprecated Use {@link UnifiedJedis#getrange(String, long, long)} instead. * Deprecated in Jedis 7.3.0. Mirrors Redis deprecation since 2.0.0. */ @Deprecated @Override public String substr(String key, int start, int end) { return executeCommand(commandObjects.substr(key, start, end)); } @Override public long strlen(String key) { return executeCommand(commandObjects.strlen(key)); } @Override public long append(byte[] key, byte[] value) { return executeCommand(commandObjects.append(key, value)); } /** * @deprecated Use {@link UnifiedJedis#getrange(byte[], long, long)} instead. * Deprecated in Jedis 7.3.0. Mirrors Redis deprecation since 2.0.0. */ @Deprecated @Override public byte[] substr(byte[] key, int start, int end) { return executeCommand(commandObjects.substr(key, start, end)); } @Override public long strlen(byte[] key) { return executeCommand(commandObjects.strlen(key)); } @Override public long bitcount(String key) { return executeCommand(commandObjects.bitcount(key)); } @Override public long bitcount(String key, long start, long end) { return executeCommand(commandObjects.bitcount(key, start, end)); } @Override public long bitcount(String key, long start, long end, BitCountOption option) { return executeCommand(commandObjects.bitcount(key, start, end, option)); } @Override public long bitpos(String key, boolean value) { return executeCommand(commandObjects.bitpos(key, value)); } @Override public long bitpos(String key, boolean value, BitPosParams params) { return executeCommand(commandObjects.bitpos(key, value, params)); } @Override public long bitcount(byte[] key) { return executeCommand(commandObjects.bitcount(key)); } @Override public long bitcount(byte[] key, long start, long end) { return executeCommand(commandObjects.bitcount(key, start, end)); } @Override public long bitcount(byte[] key, long start, long end, BitCountOption option) { return executeCommand(commandObjects.bitcount(key, start, end, option)); } @Override public long bitpos(byte[] key, boolean value) { return executeCommand(commandObjects.bitpos(key, value)); } @Override public long bitpos(byte[] key, boolean value, BitPosParams params) { return executeCommand(commandObjects.bitpos(key, value, params)); } @Override public List bitfield(String key, String... arguments) { return executeCommand(commandObjects.bitfield(key, arguments)); } @Override public List bitfieldReadonly(String key, String... arguments) { return executeCommand(commandObjects.bitfieldReadonly(key, arguments)); } @Override public List bitfield(byte[] key, byte[]... arguments) { return executeCommand(commandObjects.bitfield(key, arguments)); } @Override public List bitfieldReadonly(byte[] key, byte[]... arguments) { return executeCommand(commandObjects.bitfieldReadonly(key, arguments)); } @Override public long bitop(BitOP op, String destKey, String... srcKeys) { return executeCommand(commandObjects.bitop(op, destKey, srcKeys)); } @Override public long bitop(BitOP op, byte[] destKey, byte[]... srcKeys) { return executeCommand(commandObjects.bitop(op, destKey, srcKeys)); } @Override public LCSMatchResult lcs(String keyA, String keyB, LCSParams params) { return executeCommand(commandObjects.lcs(keyA, keyB, params)); } @Override public LCSMatchResult lcs(byte[] keyA, byte[] keyB, LCSParams params) { return executeCommand(commandObjects.lcs(keyA, keyB, params)); } // String commands // List commands @Override public long rpush(String key, String... string) { return executeCommand(commandObjects.rpush(key, string)); } @Override public long lpush(String key, String... string) { return executeCommand(commandObjects.lpush(key, string)); } @Override public long llen(String key) { return executeCommand(commandObjects.llen(key)); } @Override public List lrange(String key, long start, long stop) { return executeCommand(commandObjects.lrange(key, start, stop)); } @Override public String ltrim(String key, long start, long stop) { return executeCommand(commandObjects.ltrim(key, start, stop)); } @Override public String lindex(String key, long index) { return executeCommand(commandObjects.lindex(key, index)); } @Override public long rpush(byte[] key, byte[]... args) { return executeCommand(commandObjects.rpush(key, args)); } @Override public long lpush(byte[] key, byte[]... args) { return executeCommand(commandObjects.lpush(key, args)); } @Override public long llen(byte[] key) { return executeCommand(commandObjects.llen(key)); } @Override public List lrange(byte[] key, long start, long stop) { return executeCommand(commandObjects.lrange(key, start, stop)); } @Override public String ltrim(byte[] key, long start, long stop) { return executeCommand(commandObjects.ltrim(key, start, stop)); } @Override public byte[] lindex(byte[] key, long index) { return executeCommand(commandObjects.lindex(key, index)); } @Override public String lset(String key, long index, String value) { return executeCommand(commandObjects.lset(key, index, value)); } @Override public long lrem(String key, long count, String value) { return executeCommand(commandObjects.lrem(key, count, value)); } @Override public String lpop(String key) { return executeCommand(commandObjects.lpop(key)); } @Override public List lpop(String key, int count) { return executeCommand(commandObjects.lpop(key, count)); } @Override public String lset(byte[] key, long index, byte[] value) { return executeCommand(commandObjects.lset(key, index, value)); } @Override public long lrem(byte[] key, long count, byte[] value) { return executeCommand(commandObjects.lrem(key, count, value)); } @Override public byte[] lpop(byte[] key) { return executeCommand(commandObjects.lpop(key)); } @Override public List lpop(byte[] key, int count) { return executeCommand(commandObjects.lpop(key, count)); } @Override public Long lpos(String key, String element) { return executeCommand(commandObjects.lpos(key, element)); } @Override public Long lpos(String key, String element, LPosParams params) { return executeCommand(commandObjects.lpos(key, element, params)); } @Override public List lpos(String key, String element, LPosParams params, long count) { return executeCommand(commandObjects.lpos(key, element, params, count)); } @Override public Long lpos(byte[] key, byte[] element) { return executeCommand(commandObjects.lpos(key, element)); } @Override public Long lpos(byte[] key, byte[] element, LPosParams params) { return executeCommand(commandObjects.lpos(key, element, params)); } @Override public List lpos(byte[] key, byte[] element, LPosParams params, long count) { return executeCommand(commandObjects.lpos(key, element, params, count)); } @Override public String rpop(String key) { return executeCommand(commandObjects.rpop(key)); } @Override public List rpop(String key, int count) { return executeCommand(commandObjects.rpop(key, count)); } @Override public byte[] rpop(byte[] key) { return executeCommand(commandObjects.rpop(key)); } @Override public List rpop(byte[] key, int count) { return executeCommand(commandObjects.rpop(key, count)); } @Override public long linsert(String key, ListPosition where, String pivot, String value) { return executeCommand(commandObjects.linsert(key, where, pivot, value)); } @Override public long lpushx(String key, String... strings) { return executeCommand(commandObjects.lpushx(key, strings)); } @Override public long rpushx(String key, String... strings) { return executeCommand(commandObjects.rpushx(key, strings)); } @Override public long linsert(byte[] key, ListPosition where, byte[] pivot, byte[] value) { return executeCommand(commandObjects.linsert(key, where, pivot, value)); } @Override public long lpushx(byte[] key, byte[]... args) { return executeCommand(commandObjects.lpushx(key, args)); } @Override public long rpushx(byte[] key, byte[]... args) { return executeCommand(commandObjects.rpushx(key, args)); } @Override public List blpop(int timeout, String key) { return executeCommand(commandObjects.blpop(timeout, key)); } @Override public KeyValue blpop(double timeout, String key) { return executeCommand(commandObjects.blpop(timeout, key)); } @Override public List brpop(int timeout, String key) { return executeCommand(commandObjects.brpop(timeout, key)); } @Override public KeyValue brpop(double timeout, String key) { return executeCommand(commandObjects.brpop(timeout, key)); } @Override public List blpop(int timeout, String... keys) { return executeCommand(commandObjects.blpop(timeout, keys)); } @Override public KeyValue blpop(double timeout, String... keys) { return executeCommand(commandObjects.blpop(timeout, keys)); } @Override public List brpop(int timeout, String... keys) { return executeCommand(commandObjects.brpop(timeout, keys)); } @Override public KeyValue brpop(double timeout, String... keys) { return executeCommand(commandObjects.brpop(timeout, keys)); } @Override public List blpop(int timeout, byte[]... keys) { return executeCommand(commandObjects.blpop(timeout, keys)); } @Override public KeyValue blpop(double timeout, byte[]... keys) { return executeCommand(commandObjects.blpop(timeout, keys)); } @Override public List brpop(int timeout, byte[]... keys) { return executeCommand(commandObjects.brpop(timeout, keys)); } @Override public KeyValue brpop(double timeout, byte[]... keys) { return executeCommand(commandObjects.brpop(timeout, keys)); } /** * @deprecated Use {@link UnifiedJedis#lmove(String, String, ListDirection, ListDirection)} with * {@link ListDirection#RIGHT} and {@link ListDirection#LEFT}. * Deprecated in Jedis 7.3.0. Mirrors Redis deprecation since 6.2.0. */ @Deprecated @Override public String rpoplpush(String srckey, String dstkey) { return executeCommand(commandObjects.rpoplpush(srckey, dstkey)); } /** * @deprecated Use {@link UnifiedJedis#blmove(String, String, ListDirection, ListDirection, double)} with * {@link ListDirection#RIGHT} and {@link ListDirection#LEFT}. * Deprecated in Jedis 7.3.0. Mirrors Redis deprecation since 6.2.0. */ @Deprecated @Override public String brpoplpush(String source, String destination, int timeout) { return executeCommand(commandObjects.brpoplpush(source, destination, timeout)); } /** * @deprecated Use {@link UnifiedJedis#lmove(byte[], byte[], ListDirection, ListDirection)} with * {@link ListDirection#RIGHT} and {@link ListDirection#LEFT}. * Deprecated in Jedis 7.3.0. Mirrors Redis deprecation since 6.2.0. */ @Deprecated @Override public byte[] rpoplpush(byte[] srckey, byte[] dstkey) { return executeCommand(commandObjects.rpoplpush(srckey, dstkey)); } /** * @deprecated Use {@link UnifiedJedis#blmove(byte[], byte[], ListDirection, ListDirection, double)} with * {@link ListDirection#RIGHT} and {@link ListDirection#LEFT}. * Deprecated in Jedis 7.3.0. Mirrors Redis deprecation since 6.2.0. */ @Deprecated @Override public byte[] brpoplpush(byte[] source, byte[] destination, int timeout) { return executeCommand(commandObjects.brpoplpush(source, destination, timeout)); } @Override public String lmove(String srcKey, String dstKey, ListDirection from, ListDirection to) { return executeCommand(commandObjects.lmove(srcKey, dstKey, from, to)); } @Override public String blmove(String srcKey, String dstKey, ListDirection from, ListDirection to, double timeout) { return executeCommand(commandObjects.blmove(srcKey, dstKey, from, to, timeout)); } @Override public byte[] lmove(byte[] srcKey, byte[] dstKey, ListDirection from, ListDirection to) { return executeCommand(commandObjects.lmove(srcKey, dstKey, from, to)); } @Override public byte[] blmove(byte[] srcKey, byte[] dstKey, ListDirection from, ListDirection to, double timeout) { return executeCommand(commandObjects.blmove(srcKey, dstKey, from, to, timeout)); } @Override public KeyValue> lmpop(ListDirection direction, String... keys) { return executeCommand(commandObjects.lmpop(direction, keys)); } @Override public KeyValue> lmpop(ListDirection direction, int count, String... keys) { return executeCommand(commandObjects.lmpop(direction, count, keys)); } @Override public KeyValue> blmpop(double timeout, ListDirection direction, String... keys) { return executeCommand(commandObjects.blmpop(timeout, direction, keys)); } @Override public KeyValue> blmpop(double timeout, ListDirection direction, int count, String... keys) { return executeCommand(commandObjects.blmpop(timeout, direction, count, keys)); } @Override public KeyValue> lmpop(ListDirection direction, byte[]... keys) { return executeCommand(commandObjects.lmpop(direction, keys)); } @Override public KeyValue> lmpop(ListDirection direction, int count, byte[]... keys) { return executeCommand(commandObjects.lmpop(direction, count, keys)); } @Override public KeyValue> blmpop(double timeout, ListDirection direction, byte[]... keys) { return executeCommand(commandObjects.blmpop(timeout, direction, keys)); } @Override public KeyValue> blmpop(double timeout, ListDirection direction, int count, byte[]... keys) { return executeCommand(commandObjects.blmpop(timeout, direction, count, keys)); } // List commands // Hash commands @Override public long hset(String key, String field, String value) { return executeCommand(commandObjects.hset(key, field, value)); } @Override public long hset(String key, Map hash) { return executeCommand(commandObjects.hset(key, hash)); } @Override public long hsetex(String key, HSetExParams params, String field, String value) { return executeCommand(commandObjects.hsetex(key, params, field, value)); } @Override public long hsetex(String key, HSetExParams params, Map hash) { return executeCommand(commandObjects.hsetex(key, params, hash)); } @Override public String hget(String key, String field) { return executeCommand(commandObjects.hget(key, field)); } @Override public List hgetex(String key, HGetExParams params, String... fields) { return executeCommand(commandObjects.hgetex(key, params, fields)); } @Override public List hgetdel(String key, String... fields) { return executeCommand(commandObjects.hgetdel(key, fields)); } @Override public long hsetnx(String key, String field, String value) { return executeCommand(commandObjects.hsetnx(key, field, value)); } /** * @deprecated Use {@link UnifiedJedis#hset(String, Map)} instead. * Deprecated in Jedis 7.3.0. Mirrors Redis deprecation since 4.0.0. */ @Deprecated @Override public String hmset(String key, Map hash) { return executeCommand(commandObjects.hmset(key, hash)); } @Override public List hmget(String key, String... fields) { return executeCommand(commandObjects.hmget(key, fields)); } @Override public long hset(byte[] key, byte[] field, byte[] value) { return executeCommand(commandObjects.hset(key, field, value)); } @Override public long hset(byte[] key, Map hash) { return executeCommand(commandObjects.hset(key, hash)); } @Override public long hsetex(byte[] key, HSetExParams params, byte[] field, byte[] value) { return executeCommand(commandObjects.hsetex(key, params, field, value)); } @Override public long hsetex(byte[] key, HSetExParams params, Map hash) { return executeCommand(commandObjects.hsetex(key, params, hash)); } @Override public byte[] hget(byte[] key, byte[] field) { return executeCommand(commandObjects.hget(key, field)); } @Override public List hgetex(byte[] key, HGetExParams params, byte[]... fields) { return executeCommand(commandObjects.hgetex(key, params, fields)); } @Override public List hgetdel(byte[] key, byte[]... fields) { return executeCommand(commandObjects.hgetdel(key, fields)); } @Override public long hsetnx(byte[] key, byte[] field, byte[] value) { return executeCommand(commandObjects.hsetnx(key, field, value)); } /** * @deprecated Use {@link UnifiedJedis#hset(byte[], Map)} instead. * Deprecated in Jedis 7.3.0. Mirrors Redis deprecation since 4.0.0. */ @Deprecated @Override public String hmset(byte[] key, Map hash) { return executeCommand(commandObjects.hmset(key, hash)); } @Override public List hmget(byte[] key, byte[]... fields) { return executeCommand(commandObjects.hmget(key, fields)); } @Override public long hincrBy(String key, String field, long value) { return executeCommand(commandObjects.hincrBy(key, field, value)); } @Override public double hincrByFloat(String key, String field, double value) { return executeCommand(commandObjects.hincrByFloat(key, field, value)); } @Override public boolean hexists(String key, String field) { return executeCommand(commandObjects.hexists(key, field)); } @Override public long hdel(String key, String... field) { return executeCommand(commandObjects.hdel(key, field)); } @Override public long hlen(String key) { return executeCommand(commandObjects.hlen(key)); } @Override public long hincrBy(byte[] key, byte[] field, long value) { return executeCommand(commandObjects.hincrBy(key, field, value)); } @Override public double hincrByFloat(byte[] key, byte[] field, double value) { return executeCommand(commandObjects.hincrByFloat(key, field, value)); } @Override public boolean hexists(byte[] key, byte[] field) { return executeCommand(commandObjects.hexists(key, field)); } @Override public long hdel(byte[] key, byte[]... field) { return executeCommand(commandObjects.hdel(key, field)); } @Override public long hlen(byte[] key) { return executeCommand(commandObjects.hlen(key)); } @Override public Set hkeys(String key) { return executeCommand(commandObjects.hkeys(key)); } @Override public List hvals(String key) { return executeCommand(commandObjects.hvals(key)); } @Override public Map hgetAll(String key) { return executeCommand(commandObjects.hgetAll(key)); } @Override public Set hkeys(byte[] key) { return executeCommand(commandObjects.hkeys(key)); } @Override public List hvals(byte[] key) { return executeCommand(commandObjects.hvals(key)); } @Override public Map hgetAll(byte[] key) { return executeCommand(commandObjects.hgetAll(key)); } @Override public String hrandfield(String key) { return executeCommand(commandObjects.hrandfield(key)); } @Override public List hrandfield(String key, long count) { return executeCommand(commandObjects.hrandfield(key, count)); } @Override public List> hrandfieldWithValues(String key, long count) { return executeCommand(commandObjects.hrandfieldWithValues(key, count)); } @Override public ScanResult> hscan(String key, String cursor, ScanParams params) { return executeCommand(commandObjects.hscan(key, cursor, params)); } @Override public ScanResult hscanNoValues(String key, String cursor, ScanParams params) { return executeCommand(commandObjects.hscanNoValues(key, cursor, params)); } @Override public long hstrlen(String key, String field) { return executeCommand(commandObjects.hstrlen(key, field)); } @Override public byte[] hrandfield(byte[] key) { return executeCommand(commandObjects.hrandfield(key)); } @Override public List hrandfield(byte[] key, long count) { return executeCommand(commandObjects.hrandfield(key, count)); } @Override public List> hrandfieldWithValues(byte[] key, long count) { return executeCommand(commandObjects.hrandfieldWithValues(key, count)); } @Override public ScanResult> hscan(byte[] key, byte[] cursor, ScanParams params) { return executeCommand(commandObjects.hscan(key, cursor, params)); } @Override public ScanResult hscanNoValues(byte[] key, byte[] cursor, ScanParams params) { return executeCommand(commandObjects.hscanNoValues(key, cursor, params)); } @Override public long hstrlen(byte[] key, byte[] field) { return executeCommand(commandObjects.hstrlen(key, field)); } @Override public List hexpire(String key, long seconds, String... fields) { return executeCommand(commandObjects.hexpire(key, seconds, fields)); } @Override public List hexpire(String key, long seconds, ExpiryOption condition, String... fields) { return executeCommand(commandObjects.hexpire(key, seconds, condition, fields)); } @Override public List hpexpire(String key, long milliseconds, String... fields) { return executeCommand(commandObjects.hpexpire(key, milliseconds, fields)); } @Override public List hpexpire(String key, long milliseconds, ExpiryOption condition, String... fields) { return executeCommand(commandObjects.hpexpire(key, milliseconds, condition, fields)); } @Override public List hexpireAt(String key, long unixTimeSeconds, String... fields) { return executeCommand(commandObjects.hexpireAt(key, unixTimeSeconds, fields)); } @Override public List hexpireAt(String key, long unixTimeSeconds, ExpiryOption condition, String... fields) { return executeCommand(commandObjects.hexpireAt(key, unixTimeSeconds, condition, fields)); } @Override public List hpexpireAt(String key, long unixTimeMillis, String... fields) { return executeCommand(commandObjects.hpexpireAt(key, unixTimeMillis, fields)); } @Override public List hpexpireAt(String key, long unixTimeMillis, ExpiryOption condition, String... fields) { return executeCommand(commandObjects.hpexpireAt(key, unixTimeMillis, condition, fields)); } @Override public List hexpire(byte[] key, long seconds, byte[]... fields) { return executeCommand(commandObjects.hexpire(key, seconds, fields)); } @Override public List hexpire(byte[] key, long seconds, ExpiryOption condition, byte[]... fields) { return executeCommand(commandObjects.hexpire(key, seconds, condition, fields)); } @Override public List hpexpire(byte[] key, long milliseconds, byte[]... fields) { return executeCommand(commandObjects.hpexpire(key, milliseconds, fields)); } @Override public List hpexpire(byte[] key, long milliseconds, ExpiryOption condition, byte[]... fields) { return executeCommand(commandObjects.hpexpire(key, milliseconds, condition, fields)); } @Override public List hexpireAt(byte[] key, long unixTimeSeconds, byte[]... fields) { return executeCommand(commandObjects.hexpireAt(key, unixTimeSeconds, fields)); } @Override public List hexpireAt(byte[] key, long unixTimeSeconds, ExpiryOption condition, byte[]... fields) { return executeCommand(commandObjects.hexpireAt(key, unixTimeSeconds, condition, fields)); } @Override public List hpexpireAt(byte[] key, long unixTimeMillis, byte[]... fields) { return executeCommand(commandObjects.hpexpireAt(key, unixTimeMillis, fields)); } @Override public List hpexpireAt(byte[] key, long unixTimeMillis, ExpiryOption condition, byte[]... fields) { return executeCommand(commandObjects.hpexpireAt(key, unixTimeMillis, condition, fields)); } @Override public List hexpireTime(String key, String... fields) { return executeCommand(commandObjects.hexpireTime(key, fields)); } @Override public List hpexpireTime(String key, String... fields) { return executeCommand(commandObjects.hpexpireTime(key, fields)); } @Override public List httl(String key, String... fields) { return executeCommand(commandObjects.httl(key, fields)); } @Override public List hpttl(String key, String... fields) { return executeCommand(commandObjects.hpttl(key, fields)); } @Override public List hexpireTime(byte[] key, byte[]... fields) { return executeCommand(commandObjects.hexpireTime(key, fields)); } @Override public List hpexpireTime(byte[] key, byte[]... fields) { return executeCommand(commandObjects.hpexpireTime(key, fields)); } @Override public List httl(byte[] key, byte[]... fields) { return executeCommand(commandObjects.httl(key, fields)); } @Override public List hpttl(byte[] key, byte[]... fields) { return executeCommand(commandObjects.hpttl(key, fields)); } @Override public List hpersist(String key, String... fields) { return executeCommand(commandObjects.hpersist(key, fields)); } @Override public List hpersist(byte[] key, byte[]... fields) { return executeCommand(commandObjects.hpersist(key, fields)); } // Hash commands // Set commands @Override public long sadd(String key, String... members) { return executeCommand(commandObjects.sadd(key, members)); } @Override public Set smembers(String key) { return executeCommand(commandObjects.smembers(key)); } @Override public long srem(String key, String... members) { return executeCommand(commandObjects.srem(key, members)); } @Override public String spop(String key) { return executeCommand(commandObjects.spop(key)); } @Override public Set spop(String key, long count) { return executeCommand(commandObjects.spop(key, count)); } @Override public long scard(String key) { return executeCommand(commandObjects.scard(key)); } @Override public boolean sismember(String key, String member) { return executeCommand(commandObjects.sismember(key, member)); } @Override public List smismember(String key, String... members) { return executeCommand(commandObjects.smismember(key, members)); } @Override public long sadd(byte[] key, byte[]... members) { return executeCommand(commandObjects.sadd(key, members)); } @Override public Set smembers(byte[] key) { return executeCommand(commandObjects.smembers(key)); } @Override public long srem(byte[] key, byte[]... members) { return executeCommand(commandObjects.srem(key, members)); } @Override public byte[] spop(byte[] key) { return executeCommand(commandObjects.spop(key)); } @Override public Set spop(byte[] key, long count) { return executeCommand(commandObjects.spop(key, count)); } @Override public long scard(byte[] key) { return executeCommand(commandObjects.scard(key)); } @Override public boolean sismember(byte[] key, byte[] member) { return executeCommand(commandObjects.sismember(key, member)); } @Override public List smismember(byte[] key, byte[]... members) { return executeCommand(commandObjects.smismember(key, members)); } @Override public String srandmember(String key) { return executeCommand(commandObjects.srandmember(key)); } @Override public List srandmember(String key, int count) { return executeCommand(commandObjects.srandmember(key, count)); } @Override public ScanResult sscan(String key, String cursor, ScanParams params) { return executeCommand(commandObjects.sscan(key, cursor, params)); } @Override public byte[] srandmember(byte[] key) { return executeCommand(commandObjects.srandmember(key)); } @Override public List srandmember(byte[] key, int count) { return executeCommand(commandObjects.srandmember(key, count)); } @Override public ScanResult sscan(byte[] key, byte[] cursor, ScanParams params) { return executeCommand(commandObjects.sscan(key, cursor, params)); } @Override public Set sdiff(String... keys) { return executeCommand(commandObjects.sdiff(keys)); } @Override public long sdiffstore(String dstkey, String... keys) { return executeCommand(commandObjects.sdiffstore(dstkey, keys)); } @Override public Set sinter(String... keys) { return executeCommand(commandObjects.sinter(keys)); } @Override public long sinterstore(String dstkey, String... keys) { return executeCommand(commandObjects.sinterstore(dstkey, keys)); } @Override public long sintercard(String... keys) { return executeCommand(commandObjects.sintercard(keys)); } @Override public long sintercard(int limit, String... keys) { return executeCommand(commandObjects.sintercard(limit, keys)); } @Override public Set sunion(String... keys) { return executeCommand(commandObjects.sunion(keys)); } @Override public long sunionstore(String dstkey, String... keys) { return executeCommand(commandObjects.sunionstore(dstkey, keys)); } @Override public long smove(String srckey, String dstkey, String member) { return executeCommand(commandObjects.smove(srckey, dstkey, member)); } @Override public Set sdiff(byte[]... keys) { return executeCommand(commandObjects.sdiff(keys)); } @Override public long sdiffstore(byte[] dstkey, byte[]... keys) { return executeCommand(commandObjects.sdiffstore(dstkey, keys)); } @Override public Set sinter(byte[]... keys) { return executeCommand(commandObjects.sinter(keys)); } @Override public long sinterstore(byte[] dstkey, byte[]... keys) { return executeCommand(commandObjects.sinterstore(dstkey, keys)); } @Override public long sintercard(byte[]... keys) { return executeCommand(commandObjects.sintercard(keys)); } @Override public long sintercard(int limit, byte[]... keys) { return executeCommand(commandObjects.sintercard(limit, keys)); } @Override public Set sunion(byte[]... keys) { return executeCommand(commandObjects.sunion(keys)); } @Override public long sunionstore(byte[] dstkey, byte[]... keys) { return executeCommand(commandObjects.sunionstore(dstkey, keys)); } @Override public long smove(byte[] srckey, byte[] dstkey, byte[] member) { return executeCommand(commandObjects.smove(srckey, dstkey, member)); } // Set commands // Sorted Set commands @Override public long zadd(String key, double score, String member) { return executeCommand(commandObjects.zadd(key, score, member)); } @Override public long zadd(String key, double score, String member, ZAddParams params) { return executeCommand(commandObjects.zadd(key, score, member, params)); } @Override public long zadd(String key, Map scoreMembers) { return executeCommand(commandObjects.zadd(key, scoreMembers)); } @Override public long zadd(String key, Map scoreMembers, ZAddParams params) { return executeCommand(commandObjects.zadd(key, scoreMembers, params)); } @Override public Double zaddIncr(String key, double score, String member, ZAddParams params) { return executeCommand(commandObjects.zaddIncr(key, score, member, params)); } @Override public long zadd(byte[] key, double score, byte[] member) { return executeCommand(commandObjects.zadd(key, score, member)); } @Override public long zadd(byte[] key, double score, byte[] member, ZAddParams params) { return executeCommand(commandObjects.zadd(key, score, member, params)); } @Override public long zadd(byte[] key, Map scoreMembers) { return executeCommand(commandObjects.zadd(key, scoreMembers)); } @Override public long zadd(byte[] key, Map scoreMembers, ZAddParams params) { return executeCommand(commandObjects.zadd(key, scoreMembers, params)); } @Override public Double zaddIncr(byte[] key, double score, byte[] member, ZAddParams params) { return executeCommand(commandObjects.zaddIncr(key, score, member, params)); } @Override public long zrem(String key, String... members) { return executeCommand(commandObjects.zrem(key, members)); } @Override public double zincrby(String key, double increment, String member) { return executeCommand(commandObjects.zincrby(key, increment, member)); } @Override public Double zincrby(String key, double increment, String member, ZIncrByParams params) { return executeCommand(commandObjects.zincrby(key, increment, member, params)); } @Override public Long zrank(String key, String member) { return executeCommand(commandObjects.zrank(key, member)); } @Override public Long zrevrank(String key, String member) { return executeCommand(commandObjects.zrevrank(key, member)); } @Override public KeyValue zrankWithScore(String key, String member) { return executeCommand(commandObjects.zrankWithScore(key, member)); } @Override public KeyValue zrevrankWithScore(String key, String member) { return executeCommand(commandObjects.zrevrankWithScore(key, member)); } @Override public long zrem(byte[] key, byte[]... members) { return executeCommand(commandObjects.zrem(key, members)); } @Override public double zincrby(byte[] key, double increment, byte[] member) { return executeCommand(commandObjects.zincrby(key, increment, member)); } @Override public Double zincrby(byte[] key, double increment, byte[] member, ZIncrByParams params) { return executeCommand(commandObjects.zincrby(key, increment, member, params)); } @Override public Long zrank(byte[] key, byte[] member) { return executeCommand(commandObjects.zrank(key, member)); } @Override public Long zrevrank(byte[] key, byte[] member) { return executeCommand(commandObjects.zrevrank(key, member)); } @Override public KeyValue zrankWithScore(byte[] key, byte[] member) { return executeCommand(commandObjects.zrankWithScore(key, member)); } @Override public KeyValue zrevrankWithScore(byte[] key, byte[] member) { return executeCommand(commandObjects.zrevrankWithScore(key, member)); } @Override public String zrandmember(String key) { return executeCommand(commandObjects.zrandmember(key)); } @Override public List zrandmember(String key, long count) { return executeCommand(commandObjects.zrandmember(key, count)); } @Override public List zrandmemberWithScores(String key, long count) { return executeCommand(commandObjects.zrandmemberWithScores(key, count)); } @Override public long zcard(String key) { return executeCommand(commandObjects.zcard(key)); } @Override public Double zscore(String key, String member) { return executeCommand(commandObjects.zscore(key, member)); } @Override public List zmscore(String key, String... members) { return executeCommand(commandObjects.zmscore(key, members)); } @Override public byte[] zrandmember(byte[] key) { return executeCommand(commandObjects.zrandmember(key)); } @Override public List zrandmember(byte[] key, long count) { return executeCommand(commandObjects.zrandmember(key, count)); } @Override public List zrandmemberWithScores(byte[] key, long count) { return executeCommand(commandObjects.zrandmemberWithScores(key, count)); } @Override public long zcard(byte[] key) { return executeCommand(commandObjects.zcard(key)); } @Override public Double zscore(byte[] key, byte[] member) { return executeCommand(commandObjects.zscore(key, member)); } @Override public List zmscore(byte[] key, byte[]... members) { return executeCommand(commandObjects.zmscore(key, members)); } @Override public Tuple zpopmax(String key) { return executeCommand(commandObjects.zpopmax(key)); } @Override public List zpopmax(String key, int count) { return executeCommand(commandObjects.zpopmax(key, count)); } @Override public Tuple zpopmin(String key) { return executeCommand(commandObjects.zpopmin(key)); } @Override public List zpopmin(String key, int count) { return executeCommand(commandObjects.zpopmin(key, count)); } @Override public long zcount(String key, double min, double max) { return executeCommand(commandObjects.zcount(key, min, max)); } @Override public long zcount(String key, String min, String max) { return executeCommand(commandObjects.zcount(key, min, max)); } @Override public Tuple zpopmax(byte[] key) { return executeCommand(commandObjects.zpopmax(key)); } @Override public List zpopmax(byte[] key, int count) { return executeCommand(commandObjects.zpopmax(key, count)); } @Override public Tuple zpopmin(byte[] key) { return executeCommand(commandObjects.zpopmin(key)); } @Override public List zpopmin(byte[] key, int count) { return executeCommand(commandObjects.zpopmin(key, count)); } @Override public long zcount(byte[] key, double min, double max) { return executeCommand(commandObjects.zcount(key, min, max)); } @Override public long zcount(byte[] key, byte[] min, byte[] max) { return executeCommand(commandObjects.zcount(key, min, max)); } @Override public List zrange(String key, long start, long stop) { return executeCommand(commandObjects.zrange(key, start, stop)); } /** * @deprecated Use {@link UnifiedJedis#zrange(String, ZRangeParams)} with {@link ZRangeParams#rev()}. * Deprecated in Jedis 7.3.0. Mirrors Redis deprecation since 6.2.0. */ @Deprecated @Override public List zrevrange(String key, long start, long stop) { return executeCommand(commandObjects.zrevrange(key, start, stop)); } @Override public List zrangeWithScores(String key, long start, long stop) { return executeCommand(commandObjects.zrangeWithScores(key, start, stop)); } /** * @deprecated Use {@link UnifiedJedis#zrangeWithScores(String, ZRangeParams)} with {@link ZRangeParams#rev()}. * Deprecated in Jedis 7.3.0. Mirrors Redis deprecation since 6.2.0. */ @Deprecated @Override public List zrevrangeWithScores(String key, long start, long stop) { return executeCommand(commandObjects.zrevrangeWithScores(key, start, stop)); } @Override public List zrange(String key, ZRangeParams zRangeParams) { return executeCommand(commandObjects.zrange(key, zRangeParams)); } @Override public List zrangeWithScores(String key, ZRangeParams zRangeParams) { return executeCommand(commandObjects.zrangeWithScores(key, zRangeParams)); } @Override public long zrangestore(String dest, String src, ZRangeParams zRangeParams) { return executeCommand(commandObjects.zrangestore(dest, src, zRangeParams)); } /** * @deprecated Use {@link UnifiedJedis#zrange(String, ZRangeParams)} with {@link ZRangeParams#zrangeByScoreParams(double, double)}. * Deprecated in Jedis 7.3.0. Mirrors Redis deprecation since 6.2.0. */ @Deprecated @Override public List zrangeByScore(String key, double min, double max) { return executeCommand(commandObjects.zrangeByScore(key, min, max)); } /** * @deprecated Use {@link UnifiedJedis#zrange(String, ZRangeParams)} with {@link ZRangeParams#zrangeByScoreParams(double, double)}. * Deprecated in Jedis 7.3.0. Mirrors Redis deprecation since 6.2.0. */ @Deprecated @Override public List zrangeByScore(String key, String min, String max) { return executeCommand(commandObjects.zrangeByScore(key, min, max)); } /** * @deprecated Use {@link UnifiedJedis#zrange(String, ZRangeParams)} with {@link ZRangeParams#zrangeByScoreParams(double, double)} and {@link ZRangeParams#rev()}. * Deprecated in Jedis 7.3.0. Mirrors Redis deprecation since 6.2.0. */ @Deprecated @Override public List zrevrangeByScore(String key, double max, double min) { return executeCommand(commandObjects.zrevrangeByScore(key, max, min)); } /** * @deprecated Use {@link UnifiedJedis#zrange(String, ZRangeParams)} with {@link ZRangeParams#zrangeByScoreParams(double, double)}. * Deprecated in Jedis 7.3.0. Mirrors Redis deprecation since 6.2.0. */ @Deprecated @Override public List zrangeByScore(String key, double min, double max, int offset, int count) { return executeCommand(commandObjects.zrangeByScore(key, min, max, offset, count)); } /** * @deprecated Use {@link UnifiedJedis#zrange(String, ZRangeParams)} with {@link ZRangeParams#zrangeByScoreParams(double, double)} and {@link ZRangeParams#rev()}. * Deprecated in Jedis 7.3.0. Mirrors Redis deprecation since 6.2.0. */ @Deprecated @Override public List zrevrangeByScore(String key, String max, String min) { return executeCommand(commandObjects.zrevrangeByScore(key, max, min)); } /** * @deprecated Use {@link UnifiedJedis#zrange(String, ZRangeParams)} with {@link ZRangeParams#zrangeByScoreParams(double, double)}. * Deprecated in Jedis 7.3.0. Mirrors Redis deprecation since 6.2.0. */ @Deprecated @Override public List zrangeByScore(String key, String min, String max, int offset, int count) { return executeCommand(commandObjects.zrangeByScore(key, min, max, offset, count)); } /** * @deprecated Use {@link UnifiedJedis#zrange(String, ZRangeParams)} with {@link ZRangeParams#zrangeByScoreParams(double, double)} and {@link ZRangeParams#rev()}. * Deprecated in Jedis 7.3.0. Mirrors Redis deprecation since 6.2.0. */ @Deprecated @Override public List zrevrangeByScore(String key, double max, double min, int offset, int count) { return executeCommand(commandObjects.zrevrangeByScore(key, max, min, offset, count)); } /** * @deprecated Use {@link UnifiedJedis#zrangeWithScores(String, ZRangeParams)} with {@link ZRangeParams#zrangeByScoreParams(double, double)}. * Deprecated in Jedis 7.3.0. Mirrors Redis deprecation since 6.2.0. */ @Deprecated @Override public List zrangeByScoreWithScores(String key, double min, double max) { return executeCommand(commandObjects.zrangeByScoreWithScores(key, min, max)); } /** * @deprecated Use {@link UnifiedJedis#zrangeWithScores(String, ZRangeParams)} with {@link ZRangeParams#zrangeByScoreParams(double, double)} and {@link ZRangeParams#rev()}. * Deprecated in Jedis 7.3.0. Mirrors Redis deprecation since 6.2.0. */ @Deprecated @Override public List zrevrangeByScoreWithScores(String key, double max, double min) { return executeCommand(commandObjects.zrevrangeByScoreWithScores(key, max, min)); } /** * @deprecated Use {@link UnifiedJedis#zrangeWithScores(String, ZRangeParams)} with {@link ZRangeParams#zrangeByScoreParams(double, double)}. * Deprecated in Jedis 7.3.0. Mirrors Redis deprecation since 6.2.0. */ @Deprecated @Override public List zrangeByScoreWithScores(String key, double min, double max, int offset, int count) { return executeCommand(commandObjects.zrangeByScoreWithScores(key, min, max, offset, count)); } /** * @deprecated Use {@link UnifiedJedis#zrange(String, ZRangeParams)} with {@link ZRangeParams#zrangeByScoreParams(double, double)} and {@link ZRangeParams#rev()}. * Deprecated in Jedis 7.3.0. Mirrors Redis deprecation since 6.2.0. */ @Deprecated @Override public List zrevrangeByScore(String key, String max, String min, int offset, int count) { return executeCommand(commandObjects.zrevrangeByScore(key, max, min, offset, count)); } /** * @deprecated Use {@link UnifiedJedis#zrangeWithScores(String, ZRangeParams)} with {@link ZRangeParams#zrangeByScoreParams(double, double)}. * Deprecated in Jedis 7.3.0. Mirrors Redis deprecation since 6.2.0. */ @Deprecated @Override public List zrangeByScoreWithScores(String key, String min, String max) { return executeCommand(commandObjects.zrangeByScoreWithScores(key, min, max)); } /** * @deprecated Use {@link UnifiedJedis#zrangeWithScores(String, ZRangeParams)} with {@link ZRangeParams#zrangeByScoreParams(double, double)} and {@link ZRangeParams#rev()}. * Deprecated in Jedis 7.3.0. Mirrors Redis deprecation since 6.2.0. */ @Deprecated @Override public List zrevrangeByScoreWithScores(String key, String max, String min) { return executeCommand(commandObjects.zrevrangeByScoreWithScores(key, max, min)); } /** * @deprecated Use {@link UnifiedJedis#zrangeWithScores(String, ZRangeParams)} with {@link ZRangeParams#zrangeByScoreParams(double, double)}. * Deprecated in Jedis 7.3.0. Mirrors Redis deprecation since 6.2.0. */ @Deprecated @Override public List zrangeByScoreWithScores(String key, String min, String max, int offset, int count) { return executeCommand(commandObjects.zrangeByScoreWithScores(key, min, max, offset, count)); } /** * @deprecated Use {@link UnifiedJedis#zrangeWithScores(String, ZRangeParams)} with {@link ZRangeParams#zrangeByScoreParams(double, double)} and {@link ZRangeParams#rev()}. * Deprecated in Jedis 7.3.0. Mirrors Redis deprecation since 6.2.0. */ @Deprecated @Override public List zrevrangeByScoreWithScores(String key, double max, double min, int offset, int count) { return executeCommand(commandObjects.zrevrangeByScoreWithScores(key, max, min, offset, count)); } /** * @deprecated Use {@link UnifiedJedis#zrangeWithScores(String, ZRangeParams)} with {@link ZRangeParams#zrangeByScoreParams(double, double)} and {@link ZRangeParams#rev()}. * Deprecated in Jedis 7.3.0. Mirrors Redis deprecation since 6.2.0. */ @Deprecated @Override public List zrevrangeByScoreWithScores(String key, String max, String min, int offset, int count) { return executeCommand(commandObjects.zrevrangeByScoreWithScores(key, max, min, offset, count)); } @Override public List zrange(byte[] key, long start, long stop) { return executeCommand(commandObjects.zrange(key, start, stop)); } /** * @deprecated Use {@link UnifiedJedis#zrange(byte[], ZRangeParams)} with {@link ZRangeParams#rev()}. * Deprecated in Jedis 7.3.0. Mirrors Redis deprecation since 6.2.0. */ @Deprecated @Override public List zrevrange(byte[] key, long start, long stop) { return executeCommand(commandObjects.zrevrange(key, start, stop)); } @Override public List zrangeWithScores(byte[] key, long start, long stop) { return executeCommand(commandObjects.zrangeWithScores(key, start, stop)); } /** * @deprecated Use {@link UnifiedJedis#zrangeWithScores(byte[], ZRangeParams)} with {@link ZRangeParams#rev()}. * Deprecated in Jedis 7.3.0. Mirrors Redis deprecation since 6.2.0. */ @Deprecated @Override public List zrevrangeWithScores(byte[] key, long start, long stop) { return executeCommand(commandObjects.zrevrangeWithScores(key, start, stop)); } @Override public List zrange(byte[] key, ZRangeParams zRangeParams) { return executeCommand(commandObjects.zrange(key, zRangeParams)); } @Override public List zrangeWithScores(byte[] key, ZRangeParams zRangeParams) { return executeCommand(commandObjects.zrangeWithScores(key, zRangeParams)); } @Override public long zrangestore(byte[] dest, byte[] src, ZRangeParams zRangeParams) { return executeCommand(commandObjects.zrangestore(dest, src, zRangeParams)); } /** * @deprecated Use {@link UnifiedJedis#zrange(byte[], ZRangeParams)} with {@link ZRangeParams#zrangeByScoreParams(double, double)}. * Deprecated in Jedis 7.3.0. Mirrors Redis deprecation since 6.2.0. */ @Deprecated @Override public List zrangeByScore(byte[] key, double min, double max) { return executeCommand(commandObjects.zrangeByScore(key, min, max)); } /** * @deprecated Use {@link UnifiedJedis#zrange(byte[], ZRangeParams)} with {@link ZRangeParams#zrangeByScoreParams(double, double)}. * Deprecated in Jedis 7.3.0. Mirrors Redis deprecation since 6.2.0. */ @Deprecated @Override public List zrangeByScore(byte[] key, byte[] min, byte[] max) { return executeCommand(commandObjects.zrangeByScore(key, min, max)); } /** * @deprecated Use {@link UnifiedJedis#zrange(byte[], ZRangeParams)} with {@link ZRangeParams#zrangeByScoreParams(double, double)} and {@link ZRangeParams#rev()}. * Deprecated in Jedis 7.3.0. Mirrors Redis deprecation since 6.2.0. */ @Deprecated @Override public List zrevrangeByScore(byte[] key, double max, double min) { return executeCommand(commandObjects.zrevrangeByScore(key, max, min)); } /** * @deprecated Use {@link UnifiedJedis#zrange(byte[], ZRangeParams)} with {@link ZRangeParams#zrangeByScoreParams(double, double)}. * Deprecated in Jedis 7.3.0. Mirrors Redis deprecation since 6.2.0. */ @Deprecated @Override public List zrangeByScore(byte[] key, double min, double max, int offset, int count) { return executeCommand(commandObjects.zrangeByScore(key, min, max, offset, count)); } /** * @deprecated Use {@link UnifiedJedis#zrange(byte[], ZRangeParams)} with {@link ZRangeParams#zrangeByScoreParams(double, double)} and {@link ZRangeParams#rev()}. * Deprecated in Jedis 7.3.0. Mirrors Redis deprecation since 6.2.0. */ @Deprecated @Override public List zrevrangeByScore(byte[] key, byte[] max, byte[] min) { return executeCommand(commandObjects.zrevrangeByScore(key, max, min)); } /** * @deprecated Use {@link UnifiedJedis#zrange(byte[], ZRangeParams)} with {@link ZRangeParams#zrangeByScoreParams(double, double)}. * Deprecated in Jedis 7.3.0. Mirrors Redis deprecation since 6.2.0. */ @Deprecated @Override public List zrangeByScore(byte[] key, byte[] min, byte[] max, int offset, int count) { return executeCommand(commandObjects.zrangeByScore(key, min, max, offset, count)); } /** * @deprecated Use {@link UnifiedJedis#zrange(byte[], ZRangeParams)} with {@link ZRangeParams#zrangeByScoreParams(double, double)} and {@link ZRangeParams#rev()}. * Deprecated in Jedis 7.3.0. Mirrors Redis deprecation since 6.2.0. */ @Deprecated @Override public List zrevrangeByScore(byte[] key, double max, double min, int offset, int count) { return executeCommand(commandObjects.zrevrangeByScore(key, max, min, offset, count)); } /** * @deprecated Use {@link UnifiedJedis#zrangeWithScores(byte[], ZRangeParams)} with {@link ZRangeParams#zrangeByScoreParams(double, double)}. * Deprecated in Jedis 7.3.0. Mirrors Redis deprecation since 6.2.0. */ @Deprecated @Override public List zrangeByScoreWithScores(byte[] key, double min, double max) { return executeCommand(commandObjects.zrangeByScoreWithScores(key, min, max)); } /** * @deprecated Use {@link UnifiedJedis#zrangeWithScores(byte[], ZRangeParams)} with {@link ZRangeParams#zrangeByScoreParams(double, double)} and {@link ZRangeParams#rev()}. * Deprecated in Jedis 7.3.0. Mirrors Redis deprecation since 6.2.0. */ @Deprecated @Override public List zrevrangeByScoreWithScores(byte[] key, double max, double min) { return executeCommand(commandObjects.zrevrangeByScoreWithScores(key, max, min)); } /** * @deprecated Use {@link UnifiedJedis#zrangeWithScores(byte[], ZRangeParams)} with {@link ZRangeParams#zrangeByScoreParams(double, double)}. * Deprecated in Jedis 7.3.0. Mirrors Redis deprecation since 6.2.0. */ @Deprecated @Override public List zrangeByScoreWithScores(byte[] key, double min, double max, int offset, int count) { return executeCommand(commandObjects.zrangeByScoreWithScores(key, min, max, offset, count)); } /** * @deprecated Use {@link UnifiedJedis#zrange(byte[], ZRangeParams)} with {@link ZRangeParams#zrangeByScoreParams(double, double)} and {@link ZRangeParams#rev()}. * Deprecated in Jedis 7.3.0. Mirrors Redis deprecation since 6.2.0. */ @Deprecated @Override public List zrevrangeByScore(byte[] key, byte[] max, byte[] min, int offset, int count) { return executeCommand(commandObjects.zrevrangeByScore(key, max, min, offset, count)); } /** * @deprecated Use {@link UnifiedJedis#zrangeWithScores(byte[], ZRangeParams)} with {@link ZRangeParams#zrangeByScoreParams(double, double)}. * Deprecated in Jedis 7.3.0. Mirrors Redis deprecation since 6.2.0. */ @Deprecated @Override public List zrangeByScoreWithScores(byte[] key, byte[] min, byte[] max) { return executeCommand(commandObjects.zrangeByScoreWithScores(key, min, max)); } /** * @deprecated Use {@link UnifiedJedis#zrangeWithScores(byte[], ZRangeParams)} with {@link ZRangeParams#zrangeByScoreParams(double, double)} and {@link ZRangeParams#rev()}. * Deprecated in Jedis 7.3.0. Mirrors Redis deprecation since 6.2.0. */ @Deprecated @Override public List zrevrangeByScoreWithScores(byte[] key, byte[] max, byte[] min) { return executeCommand(commandObjects.zrevrangeByScoreWithScores(key, max, min)); } /** * @deprecated Use {@link UnifiedJedis#zrangeWithScores(byte[], ZRangeParams)} with {@link ZRangeParams#zrangeByScoreParams(double, double)}. * Deprecated in Jedis 7.3.0. Mirrors Redis deprecation since 6.2.0. */ @Deprecated @Override public List zrangeByScoreWithScores(byte[] key, byte[] min, byte[] max, int offset, int count) { return executeCommand(commandObjects.zrangeByScoreWithScores(key, min, max, offset, count)); } /** * @deprecated Use {@link UnifiedJedis#zrangeWithScores(byte[], ZRangeParams)} with {@link ZRangeParams#zrangeByScoreParams(double, double)} and {@link ZRangeParams#rev()}. * Deprecated in Jedis 7.3.0. Mirrors Redis deprecation since 6.2.0. */ @Deprecated @Override public List zrevrangeByScoreWithScores(byte[] key, double max, double min, int offset, int count) { return executeCommand(commandObjects.zrevrangeByScoreWithScores(key, max, min, offset, count)); } /** * @deprecated Use {@link UnifiedJedis#zrangeWithScores(byte[], ZRangeParams)} with {@link ZRangeParams#zrangeByScoreParams(double, double)} and {@link ZRangeParams#rev()}. * Deprecated in Jedis 7.3.0. Mirrors Redis deprecation since 6.2.0. */ @Deprecated @Override public List zrevrangeByScoreWithScores(byte[] key, byte[] max, byte[] min, int offset, int count) { return executeCommand(commandObjects.zrevrangeByScoreWithScores(key, max, min, offset, count)); } @Override public long zremrangeByRank(String key, long start, long stop) { return executeCommand(commandObjects.zremrangeByRank(key, start, stop)); } @Override public long zremrangeByScore(String key, double min, double max) { return executeCommand(commandObjects.zremrangeByScore(key, min, max)); } @Override public long zremrangeByScore(String key, String min, String max) { return executeCommand(commandObjects.zremrangeByScore(key, min, max)); } @Override public long zremrangeByRank(byte[] key, long start, long stop) { return executeCommand(commandObjects.zremrangeByRank(key, start, stop)); } @Override public long zremrangeByScore(byte[] key, double min, double max) { return executeCommand(commandObjects.zremrangeByScore(key, min, max)); } @Override public long zremrangeByScore(byte[] key, byte[] min, byte[] max) { return executeCommand(commandObjects.zremrangeByScore(key, min, max)); } @Override public long zlexcount(String key, String min, String max) { return executeCommand(commandObjects.zlexcount(key, min, max)); } /** * @deprecated Use {@link UnifiedJedis#zrange(String, ZRangeParams)} with {@link ZRangeParams#zrangeByLexParams(String, String)}. * Deprecated in Jedis 7.3.0. Mirrors Redis deprecation since 6.2.0. */ @Deprecated @Override public List zrangeByLex(String key, String min, String max) { return executeCommand(commandObjects.zrangeByLex(key, min, max)); } /** * @deprecated Use {@link UnifiedJedis#zrange(String, ZRangeParams)} with {@link ZRangeParams#zrangeByLexParams(String, String)}. * Deprecated in Jedis 7.3.0. Mirrors Redis deprecation since 6.2.0. */ @Deprecated @Override public List zrangeByLex(String key, String min, String max, int offset, int count) { return executeCommand(commandObjects.zrangeByLex(key, min, max, offset, count)); } /** * @deprecated Use {@link UnifiedJedis#zrange(String, ZRangeParams)} with {@link ZRangeParams#zrangeByLexParams(String, String)} and {@link ZRangeParams#rev()}. * Deprecated in Jedis 7.3.0. Mirrors Redis deprecation since 6.2.0. */ @Deprecated @Override public List zrevrangeByLex(String key, String max, String min) { return executeCommand(commandObjects.zrevrangeByLex(key, max, min)); } /** * @deprecated Use {@link UnifiedJedis#zrange(String, ZRangeParams)} with {@link ZRangeParams#zrangeByLexParams(String, String)} and {@link ZRangeParams#rev()}. * Deprecated in Jedis 7.3.0. Mirrors Redis deprecation since 6.2.0. */ @Deprecated @Override public List zrevrangeByLex(String key, String max, String min, int offset, int count) { return executeCommand(commandObjects.zrevrangeByLex(key, max, min, offset, count)); } @Override public long zremrangeByLex(String key, String min, String max) { return executeCommand(commandObjects.zremrangeByLex(key, min, max)); } @Override public long zlexcount(byte[] key, byte[] min, byte[] max) { return executeCommand(commandObjects.zlexcount(key, min, max)); } /** * @deprecated Use {@link UnifiedJedis#zrange(byte[], ZRangeParams)} with {@link ZRangeParams#zrangeByLexParams(String, String)}. * Deprecated in Jedis 7.3.0. Mirrors Redis deprecation since 6.2.0. */ @Deprecated @Override public List zrangeByLex(byte[] key, byte[] min, byte[] max) { return executeCommand(commandObjects.zrangeByLex(key, min, max)); } /** * @deprecated Use {@link UnifiedJedis#zrange(byte[], ZRangeParams)} with {@link ZRangeParams#zrangeByLexParams(String, String)}. * Deprecated in Jedis 7.3.0. Mirrors Redis deprecation since 6.2.0. */ @Deprecated @Override public List zrangeByLex(byte[] key, byte[] min, byte[] max, int offset, int count) { return executeCommand(commandObjects.zrangeByLex(key, min, max, offset, count)); } /** * @deprecated Use {@link UnifiedJedis#zrange(byte[], ZRangeParams)} with {@link ZRangeParams#zrangeByLexParams(String, String)} and {@link ZRangeParams#rev()}. * Deprecated in Jedis 7.3.0. Mirrors Redis deprecation since 6.2.0. */ @Deprecated @Override public List zrevrangeByLex(byte[] key, byte[] max, byte[] min) { return executeCommand(commandObjects.zrevrangeByLex(key, max, min)); } /** * @deprecated Use {@link UnifiedJedis#zrange(byte[], ZRangeParams)} with {@link ZRangeParams#zrangeByLexParams(String, String)} and {@link ZRangeParams#rev()}. * Deprecated in Jedis 7.3.0. Mirrors Redis deprecation since 6.2.0. */ @Deprecated @Override public List zrevrangeByLex(byte[] key, byte[] max, byte[] min, int offset, int count) { return executeCommand(commandObjects.zrevrangeByLex(key, max, min, offset, count)); } @Override public long zremrangeByLex(byte[] key, byte[] min, byte[] max) { return executeCommand(commandObjects.zremrangeByLex(key, min, max)); } @Override public ScanResult zscan(String key, String cursor, ScanParams params) { return executeCommand(commandObjects.zscan(key, cursor, params)); } @Override public ScanResult zscan(byte[] key, byte[] cursor, ScanParams params) { return executeCommand(commandObjects.zscan(key, cursor, params)); } @Override public KeyValue bzpopmax(double timeout, String... keys) { return executeCommand(commandObjects.bzpopmax(timeout, keys)); } @Override public KeyValue bzpopmin(double timeout, String... keys) { return executeCommand(commandObjects.bzpopmin(timeout, keys)); } @Override public KeyValue bzpopmax(double timeout, byte[]... keys) { return executeCommand(commandObjects.bzpopmax(timeout, keys)); } @Override public KeyValue bzpopmin(double timeout, byte[]... keys) { return executeCommand(commandObjects.bzpopmin(timeout, keys)); } @Override public List zdiff(String... keys) { return executeCommand(commandObjects.zdiff(keys)); } @Override public List zdiffWithScores(String... keys) { return executeCommand(commandObjects.zdiffWithScores(keys)); } @Override @Deprecated public long zdiffStore(String dstkey, String... keys) { return executeCommand(commandObjects.zdiffStore(dstkey, keys)); } @Override public long zdiffstore(String dstkey, String... keys) { return executeCommand(commandObjects.zdiffstore(dstkey, keys)); } @Override public List zdiff(byte[]... keys) { return executeCommand(commandObjects.zdiff(keys)); } @Override public List zdiffWithScores(byte[]... keys) { return executeCommand(commandObjects.zdiffWithScores(keys)); } @Override @Deprecated public long zdiffStore(byte[] dstkey, byte[]... keys) { return executeCommand(commandObjects.zdiffStore(dstkey, keys)); } @Override public long zdiffstore(byte[] dstkey, byte[]... keys) { return executeCommand(commandObjects.zdiffstore(dstkey, keys)); } @Override public long zinterstore(String dstkey, String... sets) { return executeCommand(commandObjects.zinterstore(dstkey, sets)); } @Override public long zinterstore(String dstkey, ZParams params, String... sets) { return executeCommand(commandObjects.zinterstore(dstkey, params, sets)); } @Override public List zinter(ZParams params, String... keys) { return executeCommand(commandObjects.zinter(params, keys)); } @Override public List zinterWithScores(ZParams params, String... keys) { return executeCommand(commandObjects.zinterWithScores(params, keys)); } @Override public long zinterstore(byte[] dstkey, byte[]... sets) { return executeCommand(commandObjects.zinterstore(dstkey, sets)); } @Override public long zinterstore(byte[] dstkey, ZParams params, byte[]... sets) { return executeCommand(commandObjects.zinterstore(dstkey, params, sets)); } @Override public long zintercard(byte[]... keys) { return executeCommand(commandObjects.zintercard(keys)); } @Override public long zintercard(long limit, byte[]... keys) { return executeCommand(commandObjects.zintercard(limit, keys)); } @Override public long zintercard(String... keys) { return executeCommand(commandObjects.zintercard(keys)); } @Override public long zintercard(long limit, String... keys) { return executeCommand(commandObjects.zintercard(limit, keys)); } @Override public List zinter(ZParams params, byte[]... keys) { return executeCommand(commandObjects.zinter(params, keys)); } @Override public List zinterWithScores(ZParams params, byte[]... keys) { return executeCommand(commandObjects.zinterWithScores(params, keys)); } @Override public List zunion(ZParams params, String... keys) { return executeCommand(commandObjects.zunion(params, keys)); } @Override public List zunionWithScores(ZParams params, String... keys) { return executeCommand(commandObjects.zunionWithScores(params, keys)); } @Override public long zunionstore(String dstkey, String... sets) { return executeCommand(commandObjects.zunionstore(dstkey, sets)); } @Override public long zunionstore(String dstkey, ZParams params, String... sets) { return executeCommand(commandObjects.zunionstore(dstkey, params, sets)); } @Override public List zunion(ZParams params, byte[]... keys) { return executeCommand(commandObjects.zunion(params, keys)); } @Override public List zunionWithScores(ZParams params, byte[]... keys) { return executeCommand(commandObjects.zunionWithScores(params, keys)); } @Override public long zunionstore(byte[] dstkey, byte[]... sets) { return executeCommand(commandObjects.zunionstore(dstkey, sets)); } @Override public long zunionstore(byte[] dstkey, ZParams params, byte[]... sets) { return executeCommand(commandObjects.zunionstore(dstkey, params, sets)); } @Override public KeyValue> zmpop(SortedSetOption option, String... keys) { return executeCommand(commandObjects.zmpop(option, keys)); } @Override public KeyValue> zmpop(SortedSetOption option, int count, String... keys) { return executeCommand(commandObjects.zmpop(option, count, keys)); } @Override public KeyValue> bzmpop(double timeout, SortedSetOption option, String... keys) { return executeCommand(commandObjects.bzmpop(timeout, option, keys)); } @Override public KeyValue> bzmpop(double timeout, SortedSetOption option, int count, String... keys) { return executeCommand(commandObjects.bzmpop(timeout, option, count, keys)); } @Override public KeyValue> zmpop(SortedSetOption option, byte[]... keys) { return executeCommand(commandObjects.zmpop(option, keys)); } @Override public KeyValue> zmpop(SortedSetOption option, int count, byte[]... keys) { return executeCommand(commandObjects.zmpop(option, count, keys)); } @Override public KeyValue> bzmpop(double timeout, SortedSetOption option, byte[]... keys) { return executeCommand(commandObjects.bzmpop(timeout, option, keys)); } @Override public KeyValue> bzmpop(double timeout, SortedSetOption option, int count, byte[]... keys) { return executeCommand(commandObjects.bzmpop(timeout, option, count, keys)); } // Sorted Set commands // Geo commands @Override public long geoadd(String key, double longitude, double latitude, String member) { return executeCommand(commandObjects.geoadd(key, longitude, latitude, member)); } @Override public long geoadd(String key, Map memberCoordinateMap) { return executeCommand(commandObjects.geoadd(key, memberCoordinateMap)); } @Override public long geoadd(String key, GeoAddParams params, Map memberCoordinateMap) { return executeCommand(commandObjects.geoadd(key, params, memberCoordinateMap)); } @Override public Double geodist(String key, String member1, String member2) { return executeCommand(commandObjects.geodist(key, member1, member2)); } @Override public Double geodist(String key, String member1, String member2, GeoUnit unit) { return executeCommand(commandObjects.geodist(key, member1, member2, unit)); } @Override public List geohash(String key, String... members) { return executeCommand(commandObjects.geohash(key, members)); } @Override public List geopos(String key, String... members) { return executeCommand(commandObjects.geopos(key, members)); } @Override public long geoadd(byte[] key, double longitude, double latitude, byte[] member) { return executeCommand(commandObjects.geoadd(key, longitude, latitude, member)); } @Override public long geoadd(byte[] key, Map memberCoordinateMap) { return executeCommand(commandObjects.geoadd(key, memberCoordinateMap)); } @Override public long geoadd(byte[] key, GeoAddParams params, Map memberCoordinateMap) { return executeCommand(commandObjects.geoadd(key, params, memberCoordinateMap)); } @Override public Double geodist(byte[] key, byte[] member1, byte[] member2) { return executeCommand(commandObjects.geodist(key, member1, member2)); } @Override public Double geodist(byte[] key, byte[] member1, byte[] member2, GeoUnit unit) { return executeCommand(commandObjects.geodist(key, member1, member2, unit)); } @Override public List geohash(byte[] key, byte[]... members) { return executeCommand(commandObjects.geohash(key, members)); } @Override public List geopos(byte[] key, byte[]... members) { return executeCommand(commandObjects.geopos(key, members)); } /** * @deprecated Use {@link UnifiedJedis#geosearch(String, GeoSearchParam)} instead. * Deprecated in Jedis 7.3.0. Mirrors Redis deprecation since 6.2.0. */ @Deprecated @Override public List georadius(String key, double longitude, double latitude, double radius, GeoUnit unit) { return executeCommand(commandObjects.georadius(key, longitude, latitude, radius, unit)); } /** * @deprecated Use {@link UnifiedJedis#geosearch(String, GeoSearchParam)} instead. * Deprecated since Redis 6.2.0. */ @Deprecated @Override public List georadiusReadonly(String key, double longitude, double latitude, double radius, GeoUnit unit) { return executeCommand(commandObjects.georadiusReadonly(key, longitude, latitude, radius, unit)); } /** * @deprecated Use {@link UnifiedJedis#geosearch(String, GeoSearchParam)} instead. * Deprecated in Jedis 7.3.0. Mirrors Redis deprecation since 6.2.0. */ @Deprecated @Override public List georadius(String key, double longitude, double latitude, double radius, GeoUnit unit, GeoRadiusParam param) { return executeCommand(commandObjects.georadius(key, longitude, latitude, radius, unit, param)); } /** * @deprecated Use {@link UnifiedJedis#geosearch(String, GeoSearchParam)} instead. * Deprecated since Redis 6.2.0. */ @Deprecated @Override public List georadiusReadonly(String key, double longitude, double latitude, double radius, GeoUnit unit, GeoRadiusParam param) { return executeCommand(commandObjects.georadiusReadonly(key, longitude, latitude, radius, unit, param)); } /** * @deprecated Use {@link UnifiedJedis#geosearch(String, GeoSearchParam)} instead. * Deprecated since Redis 6.2.0. */ @Deprecated @Override public List georadiusByMember(String key, String member, double radius, GeoUnit unit) { return executeCommand(commandObjects.georadiusByMember(key, member, radius, unit)); } /** * @deprecated Use {@link UnifiedJedis#geosearch(String, GeoSearchParam)} instead. * Deprecated since Redis 6.2.0. */ @Deprecated @Override public List georadiusByMemberReadonly(String key, String member, double radius, GeoUnit unit) { return executeCommand(commandObjects.georadiusByMemberReadonly(key, member, radius, unit)); } /** * @deprecated Use {@link UnifiedJedis#geosearch(String, GeoSearchParam)} instead. * Deprecated since Redis 6.2.0. */ @Deprecated @Override public List georadiusByMember(String key, String member, double radius, GeoUnit unit, GeoRadiusParam param) { return executeCommand(commandObjects.georadiusByMember(key, member, radius, unit, param)); } /** * @deprecated Use {@link UnifiedJedis#geosearch(String, GeoSearchParam)} instead. * Deprecated since Redis 6.2.0. */ @Deprecated @Override public List georadiusByMemberReadonly(String key, String member, double radius, GeoUnit unit, GeoRadiusParam param) { return executeCommand(commandObjects.georadiusByMemberReadonly(key, member, radius, unit, param)); } /** * @deprecated Use {@link UnifiedJedis#geosearchStore(String, String, GeoSearchParam)} instead. * Deprecated since Redis 6.2.0. */ @Deprecated @Override public long georadiusStore(String key, double longitude, double latitude, double radius, GeoUnit unit, GeoRadiusParam param, GeoRadiusStoreParam storeParam) { return executeCommand(commandObjects.georadiusStore(key, longitude, latitude, radius, unit, param, storeParam)); } /** * @deprecated Use {@link UnifiedJedis#geosearchStore(String, String, GeoSearchParam)} instead. * Deprecated since Redis 6.2.0. */ @Deprecated @Override public long georadiusByMemberStore(String key, String member, double radius, GeoUnit unit, GeoRadiusParam param, GeoRadiusStoreParam storeParam) { return executeCommand(commandObjects.georadiusByMemberStore(key, member, radius, unit, param, storeParam)); } @Override public List geosearch(String key, String member, double radius, GeoUnit unit) { return executeCommand(commandObjects.geosearch(key, member, radius, unit)); } @Override public List geosearch(String key, GeoCoordinate coord, double radius, GeoUnit unit) { return executeCommand(commandObjects.geosearch(key, coord, radius, unit)); } @Override public List geosearch(String key, String member, double width, double height, GeoUnit unit) { return executeCommand(commandObjects.geosearch(key, member, width, height, unit)); } @Override public List geosearch(String key, GeoCoordinate coord, double width, double height, GeoUnit unit) { return executeCommand(commandObjects.geosearch(key, coord, width, height, unit)); } @Override public List geosearch(String key, GeoSearchParam params) { return executeCommand(commandObjects.geosearch(key, params)); } @Override public long geosearchStore(String dest, String src, String member, double radius, GeoUnit unit) { return executeCommand(commandObjects.geosearchStore(dest, src, member, radius, unit)); } @Override public long geosearchStore(String dest, String src, GeoCoordinate coord, double radius, GeoUnit unit) { return executeCommand(commandObjects.geosearchStore(dest, src, coord, radius, unit)); } @Override public long geosearchStore(String dest, String src, String member, double width, double height, GeoUnit unit) { return executeCommand(commandObjects.geosearchStore(dest, src, member, width, height, unit)); } @Override public long geosearchStore(String dest, String src, GeoCoordinate coord, double width, double height, GeoUnit unit) { return executeCommand(commandObjects.geosearchStore(dest, src, coord, width, height, unit)); } @Override public long geosearchStore(String dest, String src, GeoSearchParam params) { return executeCommand(commandObjects.geosearchStore(dest, src, params)); } @Override public long geosearchStoreStoreDist(String dest, String src, GeoSearchParam params) { return executeCommand(commandObjects.geosearchStoreStoreDist(dest, src, params)); } /** * @deprecated Use {@link UnifiedJedis#geosearch(byte[], GeoSearchParam)} instead. * Deprecated in Jedis 7.3.0. Mirrors Redis deprecation since 6.2.0. */ @Deprecated @Override public List georadius(byte[] key, double longitude, double latitude, double radius, GeoUnit unit) { return executeCommand(commandObjects.georadius(key, longitude, latitude, radius, unit)); } /** * @deprecated Use {@link UnifiedJedis#geosearch(byte[], GeoSearchParam)} instead. * Deprecated since Redis 6.2.0. */ @Deprecated @Override public List georadiusReadonly(byte[] key, double longitude, double latitude, double radius, GeoUnit unit) { return executeCommand(commandObjects.georadiusReadonly(key, longitude, latitude, radius, unit)); } /** * @deprecated Use {@link UnifiedJedis#geosearch(byte[], GeoSearchParam)} instead. * Deprecated in Jedis 7.3.0. Mirrors Redis deprecation since 6.2.0. */ @Deprecated @Override public List georadius(byte[] key, double longitude, double latitude, double radius, GeoUnit unit, GeoRadiusParam param) { return executeCommand(commandObjects.georadius(key, longitude, latitude, radius, unit, param)); } /** * @deprecated Use {@link UnifiedJedis#geosearch(byte[], GeoSearchParam)} instead. * Deprecated since Redis 6.2.0. */ @Deprecated @Override public List georadiusReadonly(byte[] key, double longitude, double latitude, double radius, GeoUnit unit, GeoRadiusParam param) { return executeCommand(commandObjects.georadiusReadonly(key, longitude, latitude, radius, unit, param)); } /** * @deprecated Use {@link UnifiedJedis#geosearch(byte[], GeoSearchParam)} instead. * Deprecated since Redis 6.2.0. */ @Deprecated @Override public List georadiusByMember(byte[] key, byte[] member, double radius, GeoUnit unit) { return executeCommand(commandObjects.georadiusByMember(key, member, radius, unit)); } /** * @deprecated Use {@link UnifiedJedis#geosearch(byte[], GeoSearchParam)} instead. * Deprecated since Redis 6.2.0. */ @Deprecated @Override public List georadiusByMemberReadonly(byte[] key, byte[] member, double radius, GeoUnit unit) { return executeCommand(commandObjects.georadiusByMemberReadonly(key, member, radius, unit)); } /** * @deprecated Use {@link UnifiedJedis#geosearch(byte[], GeoSearchParam)} instead. * Deprecated since Redis 6.2.0. */ @Deprecated @Override public List georadiusByMember(byte[] key, byte[] member, double radius, GeoUnit unit, GeoRadiusParam param) { return executeCommand(commandObjects.georadiusByMember(key, member, radius, unit, param)); } /** * @deprecated Use {@link UnifiedJedis#geosearch(byte[], GeoSearchParam)} instead. * Deprecated since Redis 6.2.0. */ @Deprecated @Override public List georadiusByMemberReadonly(byte[] key, byte[] member, double radius, GeoUnit unit, GeoRadiusParam param) { return executeCommand(commandObjects.georadiusByMemberReadonly(key, member, radius, unit, param)); } /** * @deprecated Use {@link UnifiedJedis#geosearchStore(byte[], byte[], GeoSearchParam)} instead. * Deprecated since Redis 6.2.0. */ @Deprecated @Override public long georadiusStore(byte[] key, double longitude, double latitude, double radius, GeoUnit unit, GeoRadiusParam param, GeoRadiusStoreParam storeParam) { return executeCommand(commandObjects.georadiusStore(key, longitude, latitude, radius, unit, param, storeParam)); } /** * @deprecated Use {@link UnifiedJedis#geosearchStore(byte[], byte[], GeoSearchParam)} instead. * Deprecated since Redis 6.2.0. */ @Deprecated @Override public long georadiusByMemberStore(byte[] key, byte[] member, double radius, GeoUnit unit, GeoRadiusParam param, GeoRadiusStoreParam storeParam) { return executeCommand(commandObjects.georadiusByMemberStore(key, member, radius, unit, param, storeParam)); } @Override public List geosearch(byte[] key, byte[] member, double radius, GeoUnit unit) { return executeCommand(commandObjects.geosearch(key, member, radius, unit)); } @Override public List geosearch(byte[] key, GeoCoordinate coord, double radius, GeoUnit unit) { return executeCommand(commandObjects.geosearch(key, coord, radius, unit)); } @Override public List geosearch(byte[] key, byte[] member, double width, double height, GeoUnit unit) { return executeCommand(commandObjects.geosearch(key, member, width, height, unit)); } @Override public List geosearch(byte[] key, GeoCoordinate coord, double width, double height, GeoUnit unit) { return executeCommand(commandObjects.geosearch(key, coord, width, height, unit)); } @Override public List geosearch(byte[] key, GeoSearchParam params) { return executeCommand(commandObjects.geosearch(key, params)); } @Override public long geosearchStore(byte[] dest, byte[] src, byte[] member, double radius, GeoUnit unit) { return executeCommand(commandObjects.geosearchStore(dest, src, member, radius, unit)); } @Override public long geosearchStore(byte[] dest, byte[] src, GeoCoordinate coord, double radius, GeoUnit unit) { return executeCommand(commandObjects.geosearchStore(dest, src, coord, radius, unit)); } @Override public long geosearchStore(byte[] dest, byte[] src, byte[] member, double width, double height, GeoUnit unit) { return executeCommand(commandObjects.geosearchStore(dest, src, member, width, height, unit)); } @Override public long geosearchStore(byte[] dest, byte[] src, GeoCoordinate coord, double width, double height, GeoUnit unit) { return executeCommand(commandObjects.geosearchStore(dest, src, coord, width, height, unit)); } @Override public long geosearchStore(byte[] dest, byte[] src, GeoSearchParam params) { return executeCommand(commandObjects.geosearchStore(dest, src, params)); } @Override public long geosearchStoreStoreDist(byte[] dest, byte[] src, GeoSearchParam params) { return executeCommand(commandObjects.geosearchStoreStoreDist(dest, src, params)); } // Geo commands // Hyper Log Log commands @Override public long pfadd(String key, String... elements) { return executeCommand(commandObjects.pfadd(key, elements)); } @Override public String pfmerge(String destkey, String... sourcekeys) { return executeCommand(commandObjects.pfmerge(destkey, sourcekeys)); } @Override public long pfcount(String key) { return executeCommand(commandObjects.pfcount(key)); } @Override public long pfcount(String... keys) { return executeCommand(commandObjects.pfcount(keys)); } @Override public long pfadd(byte[] key, byte[]... elements) { return executeCommand(commandObjects.pfadd(key, elements)); } @Override public String pfmerge(byte[] destkey, byte[]... sourcekeys) { return executeCommand(commandObjects.pfmerge(destkey, sourcekeys)); } @Override public long pfcount(byte[] key) { return executeCommand(commandObjects.pfcount(key)); } @Override public long pfcount(byte[]... keys) { return executeCommand(commandObjects.pfcount(keys)); } // Hyper Log Log commands // Stream commands @Override public StreamEntryID xadd(String key, StreamEntryID id, Map hash) { return executeCommand(commandObjects.xadd(key, id, hash)); } @Override public StreamEntryID xadd(String key, XAddParams params, Map hash) { return executeCommand(commandObjects.xadd(key, params, hash)); } @Override public long xlen(String key) { return executeCommand(commandObjects.xlen(key)); } @Override public List xrange(String key, StreamEntryID start, StreamEntryID end) { return executeCommand(commandObjects.xrange(key, start, end)); } @Override public List xrange(String key, StreamEntryID start, StreamEntryID end, int count) { return executeCommand(commandObjects.xrange(key, start, end, count)); } @Override public List xrevrange(String key, StreamEntryID end, StreamEntryID start) { return executeCommand(commandObjects.xrevrange(key, end, start)); } @Override public List xrevrange(String key, StreamEntryID end, StreamEntryID start, int count) { return executeCommand(commandObjects.xrevrange(key, end, start, count)); } @Override public List xrange(String key, String start, String end) { return executeCommand(commandObjects.xrange(key, start, end)); } @Override public List xrange(String key, String start, String end, int count) { return executeCommand(commandObjects.xrange(key, start, end, count)); } @Override public List xrevrange(String key, String end, String start) { return executeCommand(commandObjects.xrevrange(key, end, start)); } @Override public List xrevrange(String key, String end, String start, int count) { return executeCommand(commandObjects.xrevrange(key, end, start, count)); } @Override public long xack(String key, String group, StreamEntryID... ids) { return executeCommand(commandObjects.xack(key, group, ids)); } @Override public List xackdel(String key, String group, StreamEntryID... ids) { return executeCommand(commandObjects.xackdel(key, group, ids)); } @Override public List xackdel(String key, String group, StreamDeletionPolicy trimMode, StreamEntryID... ids) { return executeCommand(commandObjects.xackdel(key, group, trimMode, ids)); } @Override public String xgroupCreate(String key, String groupName, StreamEntryID id, boolean makeStream) { return executeCommand(commandObjects.xgroupCreate(key, groupName, id, makeStream)); } @Override public String xgroupSetID(String key, String groupName, StreamEntryID id) { return executeCommand(commandObjects.xgroupSetID(key, groupName, id)); } @Override public long xgroupDestroy(String key, String groupName) { return executeCommand(commandObjects.xgroupDestroy(key, groupName)); } @Override public boolean xgroupCreateConsumer(String key, String groupName, String consumerName) { return executeCommand(commandObjects.xgroupCreateConsumer(key, groupName, consumerName)); } @Override public long xgroupDelConsumer(String key, String groupName, String consumerName) { return executeCommand(commandObjects.xgroupDelConsumer(key, groupName, consumerName)); } @Override public StreamPendingSummary xpending(String key, String groupName) { return executeCommand(commandObjects.xpending(key, groupName)); } @Override public List xpending(String key, String groupName, XPendingParams params) { return executeCommand(commandObjects.xpending(key, groupName, params)); } @Override public long xdel(String key, StreamEntryID... ids) { return executeCommand(commandObjects.xdel(key, ids)); } @Override public List xdelex(String key, StreamEntryID... ids) { return executeCommand(commandObjects.xdelex(key, ids)); } @Override public List xdelex(String key, StreamDeletionPolicy trimMode, StreamEntryID... ids) { return executeCommand(commandObjects.xdelex(key, trimMode, ids)); } @Override public long xtrim(String key, long maxLen, boolean approximate) { return executeCommand(commandObjects.xtrim(key, maxLen, approximate)); } @Override public long xtrim(String key, XTrimParams params) { return executeCommand(commandObjects.xtrim(key, params)); } @Override public List xclaim(String key, String group, String consumerName, long minIdleTime, XClaimParams params, StreamEntryID... ids) { return executeCommand(commandObjects.xclaim(key, group, consumerName, minIdleTime, params, ids)); } @Override public List xclaimJustId(String key, String group, String consumerName, long minIdleTime, XClaimParams params, StreamEntryID... ids) { return executeCommand(commandObjects.xclaimJustId(key, group, consumerName, minIdleTime, params, ids)); } @Override public Map.Entry> xautoclaim(String key, String group, String consumerName, long minIdleTime, StreamEntryID start, XAutoClaimParams params) { return executeCommand(commandObjects.xautoclaim(key, group, consumerName, minIdleTime, start, params)); } @Override public Map.Entry> xautoclaimJustId(String key, String group, String consumerName, long minIdleTime, StreamEntryID start, XAutoClaimParams params) { return executeCommand(commandObjects.xautoclaimJustId(key, group, consumerName, minIdleTime, start, params)); } @Override public StreamInfo xinfoStream(String key) { return executeCommand(commandObjects.xinfoStream(key)); } @Override public StreamFullInfo xinfoStreamFull(String key) { return executeCommand(commandObjects.xinfoStreamFull(key)); } @Override public StreamFullInfo xinfoStreamFull(String key, int count) { return executeCommand(commandObjects.xinfoStreamFull(key, count)); } @Override public List xinfoGroups(String key) { return executeCommand(commandObjects.xinfoGroups(key)); } @Override public List xinfoConsumers(String key, String group) { return executeCommand(commandObjects.xinfoConsumers(key, group)); } @Override public List xinfoConsumers2(String key, String group) { return executeCommand(commandObjects.xinfoConsumers2(key, group)); } @Override public List>> xread(XReadParams xReadParams, Map streams) { return executeCommand(commandObjects.xread(xReadParams, streams)); } @Override public Map> xreadAsMap(XReadParams xReadParams, Map streams) { return executeCommand(commandObjects.xreadAsMap(xReadParams, streams)); } @Override public List>> xreadGroup(String groupName, String consumer, XReadGroupParams xReadGroupParams, Map streams) { return executeCommand(commandObjects.xreadGroup(groupName, consumer, xReadGroupParams, streams)); } @Override public Map> xreadGroupAsMap(String groupName, String consumer, XReadGroupParams xReadGroupParams, Map streams) { return executeCommand(commandObjects.xreadGroupAsMap(groupName, consumer, xReadGroupParams, streams)); } @Override public byte[] xadd(byte[] key, XAddParams params, Map hash) { return executeCommand(commandObjects.xadd(key, params, hash)); } @Override public long xlen(byte[] key) { return executeCommand(commandObjects.xlen(key)); } @Override public List xrange(byte[] key, byte[] start, byte[] end) { return executeCommand(commandObjects.xrange(key, start, end)); } @Override public List xrange(byte[] key, byte[] start, byte[] end, int count) { return executeCommand(commandObjects.xrange(key, start, end, count)); } @Override public List xrevrange(byte[] key, byte[] end, byte[] start) { return executeCommand(commandObjects.xrevrange(key, end, start)); } @Override public List xrevrange(byte[] key, byte[] end, byte[] start, int count) { return executeCommand(commandObjects.xrevrange(key, end, start, count)); } @Override public long xack(byte[] key, byte[] group, byte[]... ids) { return executeCommand(commandObjects.xack(key, group, ids)); } @Override public List xackdel(byte[] key, byte[] group, byte[]... ids) { return executeCommand(commandObjects.xackdel(key, group, ids)); } @Override public List xackdel(byte[] key, byte[] group, StreamDeletionPolicy trimMode, byte[]... ids) { return executeCommand(commandObjects.xackdel(key, group, trimMode, ids)); } @Override public String xgroupCreate(byte[] key, byte[] groupName, byte[] id, boolean makeStream) { return executeCommand(commandObjects.xgroupCreate(key, groupName, id, makeStream)); } @Override public String xgroupSetID(byte[] key, byte[] groupName, byte[] id) { return executeCommand(commandObjects.xgroupSetID(key, groupName, id)); } @Override public long xgroupDestroy(byte[] key, byte[] groupName) { return executeCommand(commandObjects.xgroupDestroy(key, groupName)); } @Override public boolean xgroupCreateConsumer(byte[] key, byte[] groupName, byte[] consumerName) { return executeCommand(commandObjects.xgroupCreateConsumer(key, groupName, consumerName)); } @Override public long xgroupDelConsumer(byte[] key, byte[] groupName, byte[] consumerName) { return executeCommand(commandObjects.xgroupDelConsumer(key, groupName, consumerName)); } @Override public long xdel(byte[] key, byte[]... ids) { return executeCommand(commandObjects.xdel(key, ids)); } @Override public List xdelex(byte[] key, byte[]... ids) { return executeCommand(commandObjects.xdelex(key, ids)); } @Override public List xdelex(byte[] key, StreamDeletionPolicy trimMode, byte[]... ids) { return executeCommand(commandObjects.xdelex(key, trimMode, ids)); } @Override public long xtrim(byte[] key, long maxLen, boolean approximateLength) { return executeCommand(commandObjects.xtrim(key, maxLen, approximateLength)); } @Override public long xtrim(byte[] key, XTrimParams params) { return executeCommand(commandObjects.xtrim(key, params)); } @Override public Object xpending(byte[] key, byte[] groupName) { return executeCommand(commandObjects.xpending(key, groupName)); } @Override public List xpending(byte[] key, byte[] groupName, XPendingParams params) { return executeCommand(commandObjects.xpending(key, groupName, params)); } @Override public List xclaim(byte[] key, byte[] group, byte[] consumerName, long minIdleTime, XClaimParams params, byte[]... ids) { return executeCommand(commandObjects.xclaim(key, group, consumerName, minIdleTime, params, ids)); } @Override public List xclaimJustId(byte[] key, byte[] group, byte[] consumerName, long minIdleTime, XClaimParams params, byte[]... ids) { return executeCommand(commandObjects.xclaimJustId(key, group, consumerName, minIdleTime, params, ids)); } @Override public List xautoclaim(byte[] key, byte[] groupName, byte[] consumerName, long minIdleTime, byte[] start, XAutoClaimParams params) { return executeCommand(commandObjects.xautoclaim(key, groupName, consumerName, minIdleTime, start, params)); } @Override public List xautoclaimJustId(byte[] key, byte[] groupName, byte[] consumerName, long minIdleTime, byte[] start, XAutoClaimParams params) { return executeCommand(commandObjects.xautoclaimJustId(key, groupName, consumerName, minIdleTime, start, params)); } @Override public Object xinfoStream(byte[] key) { return executeCommand(commandObjects.xinfoStream(key)); } @Override public Object xinfoStreamFull(byte[] key) { return executeCommand(commandObjects.xinfoStreamFull(key)); } @Override public Object xinfoStreamFull(byte[] key, int count) { return executeCommand(commandObjects.xinfoStreamFull(key, count)); } @Override public List xinfoGroups(byte[] key) { return executeCommand(commandObjects.xinfoGroups(key)); } @Override public List xinfoConsumers(byte[] key, byte[] group) { return executeCommand(commandObjects.xinfoConsumers(key, group)); } /** * @deprecated As of Jedis 6.1.0, use * {@link #xreadBinary(XReadParams, Map)} or * {@link #xreadBinaryAsMap(XReadParams, Map)} for type safety and better stream entry * parsing. */ @Deprecated @Override public List xread(XReadParams xReadParams, Map.Entry... streams) { return executeCommand(commandObjects.xread(xReadParams, streams)); } /** * @deprecated As of Jedis 6.1.0, use * {@link #xreadGroupBinary(byte[], byte[], XReadGroupParams, Map)} or * {@link #xreadGroupBinaryAsMap(byte[], byte[], XReadGroupParams, Map)} instead. */ @Deprecated @Override public List xreadGroup(byte[] groupName, byte[] consumer, XReadGroupParams xReadGroupParams, Map.Entry... streams) { return executeCommand( commandObjects.xreadGroup(groupName, consumer, xReadGroupParams, streams)); } @Override public List>> xreadBinary(XReadParams xReadParams, Map streams) { return executeCommand(commandObjects.xreadBinary(xReadParams, streams)); } @Override public Map> xreadBinaryAsMap(XReadParams xReadParams, Map streams) { return executeCommand(commandObjects.xreadBinaryAsMap(xReadParams, streams)); } @Override public List>> xreadGroupBinary(byte[] groupName, byte[] consumer, XReadGroupParams xReadGroupParams, Map streams) { return executeCommand( commandObjects.xreadGroupBinary(groupName, consumer, xReadGroupParams, streams)); } @Override public Map> xreadGroupBinaryAsMap(byte[] groupName, byte[] consumer, XReadGroupParams xReadGroupParams, Map streams) { return executeCommand( commandObjects.xreadGroupBinaryAsMap(groupName, consumer, xReadGroupParams, streams)); } @Override public String xcfgset(String key, XCfgSetParams params) { return executeCommand(commandObjects.xcfgset(key, params)); } @Override public byte[] xcfgset(byte[] key, XCfgSetParams params) { return executeCommand(commandObjects.xcfgset(key, params)); } // Stream commands // Scripting commands @Override public Object eval(String script) { return executeCommand(commandObjects.eval(script)); } @Override public Object eval(String script, int keyCount, String... params) { return executeCommand(commandObjects.eval(script, keyCount, params)); } @Override public Object eval(String script, List keys, List args) { return executeCommand(commandObjects.eval(script, keys, args)); } @Override public Object evalReadonly(String script, List keys, List args) { return executeCommand(commandObjects.evalReadonly(script, keys, args)); } @Override public Object evalsha(String sha1) { return executeCommand(commandObjects.evalsha(sha1)); } @Override public Object evalsha(String sha1, int keyCount, String... params) { return executeCommand(commandObjects.evalsha(sha1, keyCount, params)); } @Override public Object evalsha(String sha1, List keys, List args) { return executeCommand(commandObjects.evalsha(sha1, keys, args)); } @Override public Object evalshaReadonly(String sha1, List keys, List args) { return executeCommand(commandObjects.evalshaReadonly(sha1, keys, args)); } @Override public Object eval(byte[] script) { return executeCommand(commandObjects.eval(script)); } @Override public Object eval(byte[] script, int keyCount, byte[]... params) { return executeCommand(commandObjects.eval(script, keyCount, params)); } @Override public Object eval(byte[] script, List keys, List args) { return executeCommand(commandObjects.eval(script, keys, args)); } @Override public Object evalReadonly(byte[] script, List keys, List args) { return executeCommand(commandObjects.evalReadonly(script, keys, args)); } @Override public Object evalsha(byte[] sha1) { return executeCommand(commandObjects.evalsha(sha1)); } @Override public Object evalsha(byte[] sha1, int keyCount, byte[]... params) { return executeCommand(commandObjects.evalsha(sha1, keyCount, params)); } @Override public Object evalsha(byte[] sha1, List keys, List args) { return executeCommand(commandObjects.evalsha(sha1, keys, args)); } @Override public Object evalshaReadonly(byte[] sha1, List keys, List args) { return executeCommand(commandObjects.evalshaReadonly(sha1, keys, args)); } @Override public Object fcall(String name, List keys, List args) { return executeCommand(commandObjects.fcall(name, keys, args)); } @Override public Object fcallReadonly(String name, List keys, List args) { return executeCommand(commandObjects.fcallReadonly(name, keys, args)); } @Override public String functionDelete(String libraryName) { return executeCommand(commandObjects.functionDelete(libraryName)); } @Override public String functionFlush() { return executeCommand(commandObjects.functionFlush()); } @Override public String functionFlush(FlushMode mode) { return executeCommand(commandObjects.functionFlush(mode)); } @Override public String functionKill() { return executeCommand(commandObjects.functionKill()); } @Override public List functionList() { return executeCommand(commandObjects.functionList()); } @Override public List functionList(String libraryNamePattern) { return executeCommand(commandObjects.functionList(libraryNamePattern)); } @Override public List functionListWithCode() { return executeCommand(commandObjects.functionListWithCode()); } @Override public List functionListWithCode(String libraryNamePattern) { return executeCommand(commandObjects.functionListWithCode(libraryNamePattern)); } @Override public String functionLoad(String functionCode) { return executeCommand(commandObjects.functionLoad(functionCode)); } @Override public String functionLoadReplace(String functionCode) { return executeCommand(commandObjects.functionLoadReplace(functionCode)); } @Override public FunctionStats functionStats() { return executeCommand(commandObjects.functionStats()); } @Override public Object fcall(byte[] name, List keys, List args) { return executeCommand(commandObjects.fcall(name, keys, args)); } @Override public Object fcallReadonly(byte[] name, List keys, List args) { return executeCommand(commandObjects.fcallReadonly(name, keys, args)); } @Override public String functionDelete(byte[] libraryName) { return executeCommand(commandObjects.functionDelete(libraryName)); } @Override public byte[] functionDump() { return executeCommand(commandObjects.functionDump()); } @Override public List functionListBinary() { return executeCommand(commandObjects.functionListBinary()); } @Override public List functionList(final byte[] libraryNamePattern) { return executeCommand(commandObjects.functionList(libraryNamePattern)); } @Override public List functionListWithCodeBinary() { return executeCommand(commandObjects.functionListWithCodeBinary()); } @Override public List functionListWithCode(final byte[] libraryNamePattern) { return executeCommand(commandObjects.functionListWithCode(libraryNamePattern)); } @Override public String functionLoad(byte[] functionCode) { return executeCommand(commandObjects.functionLoad(functionCode)); } @Override public String functionLoadReplace(byte[] functionCode) { return executeCommand(commandObjects.functionLoadReplace(functionCode)); } @Override public String functionRestore(byte[] serializedValue) { return executeCommand(commandObjects.functionRestore(serializedValue)); } @Override public String functionRestore(byte[] serializedValue, FunctionRestorePolicy policy) { return executeCommand(commandObjects.functionRestore(serializedValue, policy)); } @Override public Object functionStatsBinary() { return executeCommand(commandObjects.functionStatsBinary()); } // Scripting commands // Other key commands @Override public Long objectRefcount(String key) { return executeCommand(commandObjects.objectRefcount(key)); } @Override public String objectEncoding(String key) { return executeCommand(commandObjects.objectEncoding(key)); } @Override public Long objectIdletime(String key) { return executeCommand(commandObjects.objectIdletime(key)); } @Override public Long objectFreq(String key) { return executeCommand(commandObjects.objectFreq(key)); } @Override public Long objectRefcount(byte[] key) { return executeCommand(commandObjects.objectRefcount(key)); } @Override public byte[] objectEncoding(byte[] key) { return executeCommand(commandObjects.objectEncoding(key)); } @Override public Long objectIdletime(byte[] key) { return executeCommand(commandObjects.objectIdletime(key)); } @Override public Long objectFreq(byte[] key) { return executeCommand(commandObjects.objectFreq(key)); } @Override public String migrate(String host, int port, String key, int timeout) { return executeCommand(commandObjects.migrate(host, port, key, timeout)); } @Override public String migrate(String host, int port, int timeout, MigrateParams params, String... keys) { return executeCommand(commandObjects.migrate(host, port, timeout, params, keys)); } @Override public String migrate(String host, int port, byte[] key, int timeout) { return executeCommand(commandObjects.migrate(host, port, key, timeout)); } @Override public String migrate(String host, int port, int timeout, MigrateParams params, byte[]... keys) { return executeCommand(commandObjects.migrate(host, port, timeout, params, keys)); } // Other key commands // Sample key commands @Override public long waitReplicas(String sampleKey, int replicas, long timeout) { return executeCommand(commandObjects.waitReplicas(sampleKey, replicas, timeout)); } @Override public long waitReplicas(byte[] sampleKey, int replicas, long timeout) { return executeCommand(commandObjects.waitReplicas(sampleKey, replicas, timeout)); } @Override public KeyValue waitAOF(String sampleKey, long numLocal, long numReplicas, long timeout) { return executeCommand(commandObjects.waitAOF(sampleKey, numLocal, numReplicas, timeout)); } @Override public KeyValue waitAOF(byte[] sampleKey, long numLocal, long numReplicas, long timeout) { return executeCommand(commandObjects.waitAOF(sampleKey, numLocal, numReplicas, timeout)); } @Override public Object eval(String script, String sampleKey) { return executeCommand(commandObjects.eval(script, sampleKey)); } @Override public Object evalsha(String sha1, String sampleKey) { return executeCommand(commandObjects.evalsha(sha1, sampleKey)); } @Override public Object eval(byte[] script, byte[] sampleKey) { return executeCommand(commandObjects.eval(script, sampleKey)); } @Override public Object evalsha(byte[] sha1, byte[] sampleKey) { return executeCommand(commandObjects.evalsha(sha1, sampleKey)); } public List scriptExists(List sha1s) { return executeCommand(commandObjects.scriptExists(sha1s)); } @Override public Boolean scriptExists(String sha1, String sampleKey) { return scriptExists(sampleKey, new String[] { sha1 }).get(0); } @Override public List scriptExists(String sampleKey, String... sha1s) { return executeCommand(commandObjects.scriptExists(sampleKey, sha1s)); } @Override public Boolean scriptExists(byte[] sha1, byte[] sampleKey) { return scriptExists(sampleKey, new byte[][] { sha1 }).get(0); } @Override public List scriptExists(byte[] sampleKey, byte[]... sha1s) { return executeCommand(commandObjects.scriptExists(sampleKey, sha1s)); } public String scriptLoad(String script) { return executeCommand(commandObjects.scriptLoad(script)); } @Override public String scriptLoad(String script, String sampleKey) { return executeCommand(commandObjects.scriptLoad(script, sampleKey)); } public String scriptFlush() { return executeCommand(commandObjects.scriptFlush()); } @Override public String scriptFlush(String sampleKey) { return executeCommand(commandObjects.scriptFlush(sampleKey)); } @Override public String scriptFlush(String sampleKey, FlushMode flushMode) { return executeCommand(commandObjects.scriptFlush(sampleKey, flushMode)); } public String scriptKill() { return executeCommand(commandObjects.scriptKill()); } @Override public String scriptKill(String sampleKey) { return executeCommand(commandObjects.scriptKill(sampleKey)); } @Override public byte[] scriptLoad(byte[] script, byte[] sampleKey) { return executeCommand(commandObjects.scriptLoad(script, sampleKey)); } @Override public String scriptFlush(byte[] sampleKey) { return executeCommand(commandObjects.scriptFlush(sampleKey)); } @Override public String scriptFlush(byte[] sampleKey, FlushMode flushMode) { return executeCommand(commandObjects.scriptFlush(sampleKey, flushMode)); } @Override public String scriptKill(byte[] sampleKey) { return executeCommand(commandObjects.scriptKill(sampleKey)); } public String slowlogReset() { return executeCommand(commandObjects.slowlogReset()); } // Sample key commands // Random node commands public long publish(String channel, String message) { return executeCommand(commandObjects.publish(channel, message)); } public long publish(byte[] channel, byte[] message) { return executeCommand(commandObjects.publish(channel, message)); } public void subscribe(final JedisPubSub jedisPubSub, final String... channels) { try (Connection connection = this.provider.getConnection()) { jedisPubSub.proceed(connection, channels); } } public void psubscribe(final JedisPubSub jedisPubSub, final String... patterns) { try (Connection connection = this.provider.getConnection()) { jedisPubSub.proceedWithPatterns(connection, patterns); } } public void subscribe(BinaryJedisPubSub jedisPubSub, final byte[]... channels) { try (Connection connection = this.provider.getConnection()) { jedisPubSub.proceed(connection, channels); } } public void psubscribe(BinaryJedisPubSub jedisPubSub, final byte[]... patterns) { try (Connection connection = this.provider.getConnection()) { jedisPubSub.proceedWithPatterns(connection, patterns); } } // Random node commands // RediSearch commands public long hsetObject(String key, String field, Object value) { return executeCommand(commandObjects.hsetObject(key, field, value)); } public long hsetObject(String key, Map hash) { return executeCommand(commandObjects.hsetObject(key, hash)); } @Override public String ftCreate(String indexName, IndexOptions indexOptions, Schema schema) { return executeCommand(commandObjects.ftCreate(indexName, indexOptions, schema)); } @Override public String ftCreate(String indexName, FTCreateParams createParams, Iterable schemaFields) { return executeCommand(commandObjects.ftCreate(indexName, createParams, schemaFields)); } @Override public String ftAlter(String indexName, Schema schema) { return executeCommand(commandObjects.ftAlter(indexName, schema)); } @Override public String ftAlter(String indexName, Iterable schemaFields) { return executeCommand(commandObjects.ftAlter(indexName, schemaFields)); } @Override public String ftAliasAdd(String aliasName, String indexName) { return executeCommand(commandObjects.ftAliasAdd(aliasName, indexName)); } @Override public String ftAliasUpdate(String aliasName, String indexName) { return executeCommand(commandObjects.ftAliasUpdate(aliasName, indexName)); } @Override public String ftAliasDel(String aliasName) { return executeCommand(commandObjects.ftAliasDel(aliasName)); } @Override public String ftDropIndex(String indexName) { return executeCommand(commandObjects.ftDropIndex(indexName)); } @Override public String ftDropIndexDD(String indexName) { return executeCommand(commandObjects.ftDropIndexDD(indexName)); } @Override public SearchResult ftSearch(String indexName, String query) { return executeCommand(commandObjects.ftSearch(indexName, query)); } @Override public SearchResult ftSearch(String indexName, String query, FTSearchParams params) { return executeCommand(commandObjects.ftSearch(indexName, query, params)); } /** * {@link FTSearchParams#limit(int, int)} will be ignored. * * @param batchSize batch size * @param indexName index name * @param query query * @param params limit will be ignored * @return search iteration */ public FtSearchIteration ftSearchIteration(int batchSize, String indexName, String query, FTSearchParams params) { return new FtSearchIteration(provider, commandObjects.getProtocol(), batchSize, indexName, query, params); } @Override public SearchResult ftSearch(String indexName, Query query) { return executeCommand(commandObjects.ftSearch(indexName, query)); } /** * {@link Query#limit(java.lang.Integer, java.lang.Integer)} will be ignored. * @param batchSize batch size * @param indexName index name * @param query limit will be ignored * @return search iteration */ public FtSearchIteration ftSearchIteration(int batchSize, String indexName, Query query) { return new FtSearchIteration(provider, commandObjects.getProtocol(), batchSize, indexName, query); } @Override @Deprecated public SearchResult ftSearch(byte[] indexName, Query query) { return executeCommand(commandObjects.ftSearch(indexName, query)); } @Override public String ftExplain(String indexName, Query query) { return executeCommand(commandObjects.ftExplain(indexName, query)); } @Override public List ftExplainCLI(String indexName, Query query) { return executeCommand(commandObjects.ftExplainCLI(indexName, query)); } @Override public AggregationResult ftAggregate(String indexName, AggregationBuilder aggr) { return executeCommand(commandObjects.ftAggregate(indexName, aggr)); } @Override public AggregationResult ftCursorRead(String indexName, long cursorId, int count) { return executeCommand(commandObjects.ftCursorRead(indexName, cursorId, count)); } @Override public String ftCursorDel(String indexName, long cursorId) { return executeCommand(commandObjects.ftCursorDel(indexName, cursorId)); } /** * {@link AggregationBuilder#cursor(int, long) CURSOR} must be set. * @param indexName index name * @param aggr cursor must be set * @return aggregate iteration */ public FtAggregateIteration ftAggregateIteration(String indexName, AggregationBuilder aggr) { return new FtAggregateIteration(provider, indexName, aggr); } @Override public Map.Entry ftProfileAggregate(String indexName, FTProfileParams profileParams, AggregationBuilder aggr) { return executeCommand(commandObjects.ftProfileAggregate(indexName, profileParams, aggr)); } @Override public Map.Entry ftProfileSearch(String indexName, FTProfileParams profileParams, Query query) { return executeCommand(commandObjects.ftProfileSearch(indexName, profileParams, query)); } @Override public Map.Entry ftProfileSearch(String indexName, FTProfileParams profileParams, String query, FTSearchParams searchParams) { return executeCommand(commandObjects.ftProfileSearch(indexName, profileParams, query, searchParams)); } @Override public String ftSynUpdate(String indexName, String synonymGroupId, String... terms) { return executeCommand(commandObjects.ftSynUpdate(indexName, synonymGroupId, terms)); } @Override public Map> ftSynDump(String indexName) { return executeCommand(commandObjects.ftSynDump(indexName)); } @Override public long ftDictAdd(String dictionary, String... terms) { return executeCommand(commandObjects.ftDictAdd(dictionary, terms)); } @Override public long ftDictDel(String dictionary, String... terms) { return executeCommand(commandObjects.ftDictDel(dictionary, terms)); } @Override public Set ftDictDump(String dictionary) { return executeCommand(commandObjects.ftDictDump(dictionary)); } @Override public long ftDictAddBySampleKey(String indexName, String dictionary, String... terms) { return executeCommand(commandObjects.ftDictAddBySampleKey(indexName, dictionary, terms)); } @Override public long ftDictDelBySampleKey(String indexName, String dictionary, String... terms) { return executeCommand(commandObjects.ftDictDelBySampleKey(indexName, dictionary, terms)); } @Override public Set ftDictDumpBySampleKey(String indexName, String dictionary) { return executeCommand(commandObjects.ftDictDumpBySampleKey(indexName, dictionary)); } @Override public Map> ftSpellCheck(String index, String query) { return executeCommand(commandObjects.ftSpellCheck(index, query)); } @Override public Map> ftSpellCheck(String index, String query, FTSpellCheckParams spellCheckParams) { return executeCommand(commandObjects.ftSpellCheck(index, query, spellCheckParams)); } @Override public Map ftInfo(String indexName) { return executeCommand(commandObjects.ftInfo(indexName)); } @Override public Set ftTagVals(String indexName, String fieldName) { return executeCommand(commandObjects.ftTagVals(indexName, fieldName)); } @Override @Experimental public HybridResult ftHybrid(String indexName, FTHybridParams hybridParams) { return executeCommand(commandObjects.ftHybrid(indexName, hybridParams)); } @Override @Deprecated public Map ftConfigGet(String option) { return executeCommand(commandObjects.ftConfigGet(option)); } @Override @Deprecated public Map ftConfigGet(String indexName, String option) { return executeCommand(commandObjects.ftConfigGet(indexName, option)); } @Override @Deprecated public String ftConfigSet(String option, String value) { return executeCommand(commandObjects.ftConfigSet(option, value)); } @Override @Deprecated public String ftConfigSet(String indexName, String option, String value) { return executeCommand(commandObjects.ftConfigSet(indexName, option, value)); } @Override public long ftSugAdd(String key, String string, double score) { return executeCommand(commandObjects.ftSugAdd(key, string, score)); } @Override public long ftSugAddIncr(String key, String string, double score) { return executeCommand(commandObjects.ftSugAddIncr(key, string, score)); } @Override public List ftSugGet(String key, String prefix) { return executeCommand(commandObjects.ftSugGet(key, prefix)); } @Override public List ftSugGet(String key, String prefix, boolean fuzzy, int max) { return executeCommand(commandObjects.ftSugGet(key, prefix, fuzzy, max)); } @Override public List ftSugGetWithScores(String key, String prefix) { return executeCommand(commandObjects.ftSugGetWithScores(key, prefix)); } @Override public List ftSugGetWithScores(String key, String prefix, boolean fuzzy, int max) { return executeCommand(commandObjects.ftSugGetWithScores(key, prefix, fuzzy, max)); } @Override public boolean ftSugDel(String key, String string) { return executeCommand(commandObjects.ftSugDel(key, string)); } @Override public long ftSugLen(String key) { return executeCommand(commandObjects.ftSugLen(key)); } @Override public Set ftList() { return executeCommand(commandObjects.ftList()); } // RediSearch commands // RedisJSON commands @Override public String jsonSet(String key, Path2 path, Object object) { return executeCommand(commandObjects.jsonSet(key, path, object)); } @Override public String jsonSetWithEscape(String key, Path2 path, Object object) { return executeCommand(commandObjects.jsonSetWithEscape(key, path, object)); } @Override @Deprecated public String jsonSet(String key, Path path, Object pojo) { return executeCommand(commandObjects.jsonSet(key, path, pojo)); } @Override @Deprecated public String jsonSetWithPlainString(String key, Path path, String string) { return executeCommand(commandObjects.jsonSetWithPlainString(key, path, string)); } @Override public String jsonSet(String key, Path2 path, Object pojo, JsonSetParams params) { return executeCommand(commandObjects.jsonSet(key, path, pojo, params)); } @Override public String jsonSetWithEscape(String key, Path2 path, Object pojo, JsonSetParams params) { return executeCommand(commandObjects.jsonSetWithEscape(key, path, pojo, params)); } @Override @Deprecated public String jsonSet(String key, Path path, Object pojo, JsonSetParams params) { return executeCommand(commandObjects.jsonSet(key, path, pojo, params)); } @Override public String jsonMerge(String key, Path2 path, Object object) { return executeCommand(commandObjects.jsonMerge(key, path, object)); } @Override @Deprecated public String jsonMerge(String key, Path path, Object pojo) { return executeCommand(commandObjects.jsonMerge(key, path, pojo)); } @Override public Object jsonGet(String key) { return executeCommand(commandObjects.jsonGet(key)); } @Override @Deprecated public T jsonGet(String key, Class clazz) { return executeCommand(commandObjects.jsonGet(key, clazz)); } @Override public Object jsonGet(String key, Path2... paths) { return executeCommand(commandObjects.jsonGet(key, paths)); } @Override @Deprecated public Object jsonGet(String key, Path... paths) { return executeCommand(commandObjects.jsonGet(key, paths)); } @Override @Deprecated public String jsonGetAsPlainString(String key, Path path) { return executeCommand(commandObjects.jsonGetAsPlainString(key, path)); } @Override @Deprecated public T jsonGet(String key, Class clazz, Path... paths) { return executeCommand(commandObjects.jsonGet(key, clazz, paths)); } @Override public List jsonMGet(Path2 path, String... keys) { return executeCommand(commandObjects.jsonMGet(path, keys)); } @Override @Deprecated public List jsonMGet(Path path, Class clazz, String... keys) { return executeCommand(commandObjects.jsonMGet(path, clazz, keys)); } @Override public long jsonDel(String key) { return executeCommand(commandObjects.jsonDel(key)); } @Override public long jsonDel(String key, Path2 path) { return executeCommand(commandObjects.jsonDel(key, path)); } @Override @Deprecated public long jsonDel(String key, Path path) { return executeCommand(commandObjects.jsonDel(key, path)); } @Override public long jsonClear(String key) { return executeCommand(commandObjects.jsonClear(key)); } @Override public long jsonClear(String key, Path2 path) { return executeCommand(commandObjects.jsonClear(key, path)); } @Override @Deprecated public long jsonClear(String key, Path path) { return executeCommand(commandObjects.jsonClear(key, path)); } @Override public List jsonToggle(String key, Path2 path) { return executeCommand(commandObjects.jsonToggle(key, path)); } @Override @Deprecated public String jsonToggle(String key, Path path) { return executeCommand(commandObjects.jsonToggle(key, path)); } @Override @Deprecated public Class jsonType(String key) { return executeCommand(commandObjects.jsonType(key)); } @Override public List> jsonType(String key, Path2 path) { return executeCommand(commandObjects.jsonType(key, path)); } @Override @Deprecated public Class jsonType(String key, Path path) { return executeCommand(commandObjects.jsonType(key, path)); } @Override @Deprecated public long jsonStrAppend(String key, Object string) { return executeCommand(commandObjects.jsonStrAppend(key, string)); } @Override public List jsonStrAppend(String key, Path2 path, Object string) { return executeCommand(commandObjects.jsonStrAppend(key, path, string)); } @Override @Deprecated public long jsonStrAppend(String key, Path path, Object string) { return executeCommand(commandObjects.jsonStrAppend(key, path, string)); } @Override @Deprecated public Long jsonStrLen(String key) { return executeCommand(commandObjects.jsonStrLen(key)); } @Override public List jsonStrLen(String key, Path2 path) { return executeCommand(commandObjects.jsonStrLen(key, path)); } @Override @Deprecated public Long jsonStrLen(String key, Path path) { return executeCommand(commandObjects.jsonStrLen(key, path)); } @Override public Object jsonNumIncrBy(String key, Path2 path, double value) { return executeCommand(commandObjects.jsonNumIncrBy(key, path, value)); } @Override @Deprecated public double jsonNumIncrBy(String key, Path path, double value) { return executeCommand(commandObjects.jsonNumIncrBy(key, path, value)); } @Override public List jsonArrAppend(String key, Path2 path, Object... objects) { return executeCommand(commandObjects.jsonArrAppend(key, path, objects)); } @Override public List jsonArrAppendWithEscape(String key, Path2 path, Object... objects) { return executeCommand(commandObjects.jsonArrAppendWithEscape(key, path, objects)); } @Override @Deprecated public Long jsonArrAppend(String key, Path path, Object... pojos) { return executeCommand(commandObjects.jsonArrAppend(key, path, pojos)); } @Override public List jsonArrIndex(String key, Path2 path, Object scalar) { return executeCommand(commandObjects.jsonArrIndex(key, path, scalar)); } @Override public List jsonArrIndexWithEscape(String key, Path2 path, Object scalar) { return executeCommand(commandObjects.jsonArrIndexWithEscape(key, path, scalar)); } @Override @Deprecated public long jsonArrIndex(String key, Path path, Object scalar) { return executeCommand(commandObjects.jsonArrIndex(key, path, scalar)); } @Override public List jsonArrInsert(String key, Path2 path, int index, Object... objects) { return executeCommand(commandObjects.jsonArrInsert(key, path, index, objects)); } @Override public List jsonArrInsertWithEscape(String key, Path2 path, int index, Object... objects) { return executeCommand(commandObjects.jsonArrInsertWithEscape(key, path, index, objects)); } @Override @Deprecated public long jsonArrInsert(String key, Path path, int index, Object... pojos) { return executeCommand(commandObjects.jsonArrInsert(key, path, index, pojos)); } @Override @Deprecated public Object jsonArrPop(String key) { return executeCommand(commandObjects.jsonArrPop(key)); } @Override @Deprecated public T jsonArrPop(String key, Class clazz) { return executeCommand(commandObjects.jsonArrPop(key, clazz)); } @Override public List jsonArrPop(String key, Path2 path) { return executeCommand(commandObjects.jsonArrPop(key, path)); } @Override @Deprecated public Object jsonArrPop(String key, Path path) { return executeCommand(commandObjects.jsonArrPop(key, path)); } @Override @Deprecated public T jsonArrPop(String key, Class clazz, Path path) { return executeCommand(commandObjects.jsonArrPop(key, clazz, path)); } @Override public List jsonArrPop(String key, Path2 path, int index) { return executeCommand(commandObjects.jsonArrPop(key, path, index)); } @Override @Deprecated public Object jsonArrPop(String key, Path path, int index) { return executeCommand(commandObjects.jsonArrPop(key, path, index)); } @Override @Deprecated public T jsonArrPop(String key, Class clazz, Path path, int index) { return executeCommand(commandObjects.jsonArrPop(key, clazz, path, index)); } @Override @Deprecated public Long jsonArrLen(String key) { return executeCommand(commandObjects.jsonArrLen(key)); } @Override public List jsonArrLen(String key, Path2 path) { return executeCommand(commandObjects.jsonArrLen(key, path)); } @Override @Deprecated public Long jsonArrLen(String key, Path path) { return executeCommand(commandObjects.jsonArrLen(key, path)); } @Override public List jsonArrTrim(String key, Path2 path, int start, int stop) { return executeCommand(commandObjects.jsonArrTrim(key, path, start, stop)); } @Override @Deprecated public Long jsonArrTrim(String key, Path path, int start, int stop) { return executeCommand(commandObjects.jsonArrTrim(key, path, start, stop)); } @Override @Deprecated public Long jsonObjLen(String key) { return executeCommand(commandObjects.jsonObjLen(key)); } @Override @Deprecated public Long jsonObjLen(String key, Path path) { return executeCommand(commandObjects.jsonObjLen(key, path)); } @Override public List jsonObjLen(String key, Path2 path) { return executeCommand(commandObjects.jsonObjLen(key, path)); } @Override @Deprecated public List jsonObjKeys(String key) { return executeCommand(commandObjects.jsonObjKeys(key)); } @Override @Deprecated public List jsonObjKeys(String key, Path path) { return executeCommand(commandObjects.jsonObjKeys(key, path)); } @Override public List> jsonObjKeys(String key, Path2 path) { return executeCommand(commandObjects.jsonObjKeys(key, path)); } @Override @Deprecated public long jsonDebugMemory(String key) { return executeCommand(commandObjects.jsonDebugMemory(key)); } @Override @Deprecated public long jsonDebugMemory(String key, Path path) { return executeCommand(commandObjects.jsonDebugMemory(key, path)); } @Override public List jsonDebugMemory(String key, Path2 path) { return executeCommand(commandObjects.jsonDebugMemory(key, path)); } // RedisJSON commands // RedisTimeSeries commands @Override public String tsCreate(String key) { return executeCommand(commandObjects.tsCreate(key)); } @Override public String tsCreate(String key, TSCreateParams createParams) { return executeCommand(commandObjects.tsCreate(key, createParams)); } @Override public long tsDel(String key, long fromTimestamp, long toTimestamp) { return executeCommand(commandObjects.tsDel(key, fromTimestamp, toTimestamp)); } @Override public String tsAlter(String key, TSAlterParams alterParams) { return executeCommand(commandObjects.tsAlter(key, alterParams)); } @Override public long tsAdd(String key, double value) { return executeCommand(commandObjects.tsAdd(key, value)); } @Override public long tsAdd(String key, long timestamp, double value) { return executeCommand(commandObjects.tsAdd(key, timestamp, value)); } @Override public long tsAdd(String key, long timestamp, double value, TSCreateParams createParams) { return executeCommand(commandObjects.tsAdd(key, timestamp, value, createParams)); } @Override public long tsAdd(String key, long timestamp, double value, TSAddParams addParams) { return executeCommand(commandObjects.tsAdd(key, timestamp, value, addParams)); } @Override public List tsMAdd(Map.Entry... entries) { return executeCommand(commandObjects.tsMAdd(entries)); } @Override public long tsIncrBy(String key, double value) { return executeCommand(commandObjects.tsIncrBy(key, value)); } @Override public long tsIncrBy(String key, double value, long timestamp) { return executeCommand(commandObjects.tsIncrBy(key, value, timestamp)); } @Override public long tsIncrBy(String key, double addend, TSIncrByParams incrByParams) { return executeCommand(commandObjects.tsIncrBy(key, addend, incrByParams)); } @Override public long tsDecrBy(String key, double value) { return executeCommand(commandObjects.tsDecrBy(key, value)); } @Override public long tsDecrBy(String key, double value, long timestamp) { return executeCommand(commandObjects.tsDecrBy(key, value, timestamp)); } @Override public long tsDecrBy(String key, double subtrahend, TSDecrByParams decrByParams) { return executeCommand(commandObjects.tsDecrBy(key, subtrahend, decrByParams)); } @Override public List tsRange(String key, long fromTimestamp, long toTimestamp) { return executeCommand(commandObjects.tsRange(key, fromTimestamp, toTimestamp)); } @Override public List tsRange(String key, TSRangeParams rangeParams) { return executeCommand(commandObjects.tsRange(key, rangeParams)); } @Override public List tsRevRange(String key, long fromTimestamp, long toTimestamp) { return executeCommand(commandObjects.tsRevRange(key, fromTimestamp, toTimestamp)); } @Override public List tsRevRange(String key, TSRangeParams rangeParams) { return executeCommand(commandObjects.tsRevRange(key, rangeParams)); } @Override public Map tsMRange(long fromTimestamp, long toTimestamp, String... filters) { return executeCommand(commandObjects.tsMRange(fromTimestamp, toTimestamp, filters)); } @Override public Map tsMRange(TSMRangeParams multiRangeParams) { return executeCommand(commandObjects.tsMRange(multiRangeParams)); } @Override public Map tsMRevRange(long fromTimestamp, long toTimestamp, String... filters) { return executeCommand(commandObjects.tsMRevRange(fromTimestamp, toTimestamp, filters)); } @Override public Map tsMRevRange(TSMRangeParams multiRangeParams) { return executeCommand(commandObjects.tsMRevRange(multiRangeParams)); } @Override public TSElement tsGet(String key) { return executeCommand(commandObjects.tsGet(key)); } @Override public TSElement tsGet(String key, TSGetParams getParams) { return executeCommand(commandObjects.tsGet(key, getParams)); } @Override public Map tsMGet(TSMGetParams multiGetParams, String... filters) { return executeCommand(commandObjects.tsMGet(multiGetParams, filters)); } @Override public String tsCreateRule(String sourceKey, String destKey, AggregationType aggregationType, long timeBucket) { return executeCommand(commandObjects.tsCreateRule(sourceKey, destKey, aggregationType, timeBucket)); } @Override public String tsCreateRule(String sourceKey, String destKey, AggregationType aggregationType, long bucketDuration, long alignTimestamp) { return executeCommand( commandObjects.tsCreateRule(sourceKey, destKey, aggregationType, bucketDuration, alignTimestamp)); } @Override public String tsDeleteRule(String sourceKey, String destKey) { return executeCommand(commandObjects.tsDeleteRule(sourceKey, destKey)); } @Override public List tsQueryIndex(String... filters) { return executeCommand(commandObjects.tsQueryIndex(filters)); } @Override public TSInfo tsInfo(String key) { return executeCommand(commandObjects.tsInfo(key)); } @Override public TSInfo tsInfoDebug(String key) { return executeCommand(commandObjects.tsInfoDebug(key)); } // RedisTimeSeries commands // RedisBloom commands @Override public String bfReserve(String key, double errorRate, long capacity) { return executeCommand(commandObjects.bfReserve(key, errorRate, capacity)); } @Override public String bfReserve(String key, double errorRate, long capacity, BFReserveParams reserveParams) { return executeCommand(commandObjects.bfReserve(key, errorRate, capacity, reserveParams)); } @Override public boolean bfAdd(String key, String item) { return executeCommand(commandObjects.bfAdd(key, item)); } @Override public List bfMAdd(String key, String... items) { return executeCommand(commandObjects.bfMAdd(key, items)); } @Override public List bfInsert(String key, String... items) { return executeCommand(commandObjects.bfInsert(key, items)); } @Override public List bfInsert(String key, BFInsertParams insertParams, String... items) { return executeCommand(commandObjects.bfInsert(key, insertParams, items)); } @Override public boolean bfExists(String key, String item) { return executeCommand(commandObjects.bfExists(key, item)); } @Override public List bfMExists(String key, String... items) { return executeCommand(commandObjects.bfMExists(key, items)); } @Override public Map.Entry bfScanDump(String key, long iterator) { return executeCommand(commandObjects.bfScanDump(key, iterator)); } @Override public String bfLoadChunk(String key, long iterator, byte[] data) { return executeCommand(commandObjects.bfLoadChunk(key, iterator, data)); } @Override public long bfCard(String key) { return executeCommand(commandObjects.bfCard(key)); } @Override public Map bfInfo(String key) { return executeCommand(commandObjects.bfInfo(key)); } @Override public String cfReserve(String key, long capacity) { return executeCommand(commandObjects.cfReserve(key, capacity)); } @Override public String cfReserve(String key, long capacity, CFReserveParams reserveParams) { return executeCommand(commandObjects.cfReserve(key, capacity, reserveParams)); } @Override public boolean cfAdd(String key, String item) { return executeCommand(commandObjects.cfAdd(key, item)); } @Override public boolean cfAddNx(String key, String item) { return executeCommand(commandObjects.cfAddNx(key, item)); } @Override public List cfInsert(String key, String... items) { return executeCommand(commandObjects.cfInsert(key, items)); } @Override public List cfInsert(String key, CFInsertParams insertParams, String... items) { return executeCommand(commandObjects.cfInsert(key, insertParams, items)); } @Override public List cfInsertNx(String key, String... items) { return executeCommand(commandObjects.cfInsertNx(key, items)); } @Override public List cfInsertNx(String key, CFInsertParams insertParams, String... items) { return executeCommand(commandObjects.cfInsertNx(key, insertParams, items)); } @Override public boolean cfExists(String key, String item) { return executeCommand(commandObjects.cfExists(key, item)); } @Override public List cfMExists(String key, String... items) { return executeCommand(commandObjects.cfMExists(key, items)); } @Override public boolean cfDel(String key, String item) { return executeCommand(commandObjects.cfDel(key, item)); } @Override public long cfCount(String key, String item) { return executeCommand(commandObjects.cfCount(key, item)); } @Override public Map.Entry cfScanDump(String key, long iterator) { return executeCommand(commandObjects.cfScanDump(key, iterator)); } @Override public String cfLoadChunk(String key, long iterator, byte[] data) { return executeCommand(commandObjects.cfLoadChunk(key, iterator, data)); } @Override public Map cfInfo(String key) { return executeCommand(commandObjects.cfInfo(key)); } @Override public String cmsInitByDim(String key, long width, long depth) { return executeCommand(commandObjects.cmsInitByDim(key, width, depth)); } @Override public String cmsInitByProb(String key, double error, double probability) { return executeCommand(commandObjects.cmsInitByProb(key, error, probability)); } @Override public List cmsIncrBy(String key, Map itemIncrements) { return executeCommand(commandObjects.cmsIncrBy(key, itemIncrements)); } @Override public List cmsQuery(String key, String... items) { return executeCommand(commandObjects.cmsQuery(key, items)); } @Override public String cmsMerge(String destKey, String... keys) { return executeCommand(commandObjects.cmsMerge(destKey, keys)); } @Override public String cmsMerge(String destKey, Map keysAndWeights) { return executeCommand(commandObjects.cmsMerge(destKey, keysAndWeights)); } @Override public Map cmsInfo(String key) { return executeCommand(commandObjects.cmsInfo(key)); } @Override public String topkReserve(String key, long topk) { return executeCommand(commandObjects.topkReserve(key, topk)); } @Override public String topkReserve(String key, long topk, long width, long depth, double decay) { return executeCommand(commandObjects.topkReserve(key, topk, width, depth, decay)); } @Override public List topkAdd(String key, String... items) { return executeCommand(commandObjects.topkAdd(key, items)); } @Override public List topkIncrBy(String key, Map itemIncrements) { return executeCommand(commandObjects.topkIncrBy(key, itemIncrements)); } @Override public List topkQuery(String key, String... items) { return executeCommand(commandObjects.topkQuery(key, items)); } @Override public List topkList(String key) { return executeCommand(commandObjects.topkList(key)); } @Override public Map topkListWithCount(String key) { return executeCommand(commandObjects.topkListWithCount(key)); } @Override public Map topkInfo(String key) { return executeCommand(commandObjects.topkInfo(key)); } @Override public String tdigestCreate(String key) { return executeCommand(commandObjects.tdigestCreate(key)); } @Override public String tdigestCreate(String key, int compression) { return executeCommand(commandObjects.tdigestCreate(key, compression)); } @Override public String tdigestReset(String key) { return executeCommand(commandObjects.tdigestReset(key)); } @Override public String tdigestMerge(String destinationKey, String... sourceKeys) { return executeCommand(commandObjects.tdigestMerge(destinationKey, sourceKeys)); } @Override public String tdigestMerge(TDigestMergeParams mergeParams, String destinationKey, String... sourceKeys) { return executeCommand(commandObjects.tdigestMerge(mergeParams, destinationKey, sourceKeys)); } @Override public Map tdigestInfo(String key) { return executeCommand(commandObjects.tdigestInfo(key)); } @Override public String tdigestAdd(String key, double... values) { return executeCommand(commandObjects.tdigestAdd(key, values)); } @Override public List tdigestCDF(String key, double... values) { return executeCommand(commandObjects.tdigestCDF(key, values)); } @Override public List tdigestQuantile(String key, double... quantiles) { return executeCommand(commandObjects.tdigestQuantile(key, quantiles)); } @Override public double tdigestMin(String key) { return executeCommand(commandObjects.tdigestMin(key)); } @Override public double tdigestMax(String key) { return executeCommand(commandObjects.tdigestMax(key)); } @Override public double tdigestTrimmedMean(String key, double lowCutQuantile, double highCutQuantile) { return executeCommand(commandObjects.tdigestTrimmedMean(key, lowCutQuantile, highCutQuantile)); } @Override public List tdigestRank(String key, double... values) { return executeCommand(commandObjects.tdigestRank(key, values)); } @Override public List tdigestRevRank(String key, double... values) { return executeCommand(commandObjects.tdigestRevRank(key, values)); } @Override public List tdigestByRank(String key, long... ranks) { return executeCommand(commandObjects.tdigestByRank(key, ranks)); } @Override public List tdigestByRevRank(String key, long... ranks) { return executeCommand(commandObjects.tdigestByRevRank(key, ranks)); } // RedisBloom commands /** * @return pipeline object */ public AbstractPipeline pipelined() { if (provider == null) { throw new IllegalStateException("It is not allowed to create Pipeline from this " + getClass()); } else if (provider instanceof MultiDbConnectionProvider) { return new MultiDbPipeline((MultiDbConnectionProvider) provider, commandObjects); } else { return new Pipeline(provider.getConnection(), true, commandObjects); } } /** * @return transaction object */ public AbstractTransaction multi() { return transaction(true); } /** * @param doMulti {@code false} should be set to enable manual WATCH, UNWATCH and MULTI * @return transaction object */ public AbstractTransaction transaction(boolean doMulti) { if (provider == null) { throw new IllegalStateException("It is not allowed to create Transaction from this " + getClass()); } else if (provider instanceof MultiDbConnectionProvider) { return new MultiDbTransaction((MultiDbConnectionProvider) provider, doMulti, commandObjects); } else { return new Transaction(provider.getConnection(), doMulti, true, commandObjects); } } /** * @deprecated Deprecated in Jedis 7.4.0. * Use {@link #executeCommand(CommandArguments)} with a {@link CommandArguments} object directly. *
{@code
   * jedis.executeCommand(new CommandArguments(PING));
   * }
*/ @Deprecated public Object sendCommand(ProtocolCommand cmd) { return executeCommand(commandObjects.commandArguments(cmd)); } /** * @deprecated Deprecated in Jedis 7.4.0. * Use {@link #executeCommand(CommandArguments)} with a {@link CommandArguments} object directly. *
{@code
   * jedis.executeCommand(new CommandArguments(GET).key("mykey".getBytes()));
   * }
*/ @Deprecated public Object sendCommand(ProtocolCommand cmd, byte[]... args) { return executeCommand(commandObjects.commandArguments(cmd).addObjects((Object[]) args)); } /** * @deprecated Deprecated in Jedis 7.4.0. * Use {@link #executeCommand(CommandArguments)} with a {@link CommandArguments} object directly. *
{@code
   * jedis.executeCommand(new CommandArguments(BLPOP).key("mykey".getBytes()).add("0".getBytes()).blocking());
   * }
*/ @Deprecated public Object sendBlockingCommand(ProtocolCommand cmd, byte[]... args) { return executeCommand(commandObjects.commandArguments(cmd).addObjects((Object[]) args).blocking()); } /** * @deprecated Deprecated in Jedis 7.4.0. * Use {@link #executeCommand(CommandArguments)} with a {@link CommandArguments} object directly. *
{@code
   * jedis.executeCommand(new CommandArguments(SET).key("x").add("1"));
   * }
*/ @Deprecated public Object sendCommand(ProtocolCommand cmd, String... args) { return executeCommand(commandObjects.commandArguments(cmd).addObjects((Object[]) args)); } /** * @deprecated Deprecated in Jedis 7.4.0. * Use {@link #executeCommand(CommandArguments)} with a {@link CommandArguments} object directly. *
{@code
   * jedis.executeCommand(new CommandArguments(BLPOP).key("mykey").add("0").blocking());
   * }
*/ @Deprecated public Object sendBlockingCommand(ProtocolCommand cmd, String... args) { return executeCommand(commandObjects.commandArguments(cmd).addObjects((Object[]) args).blocking()); } /** * @deprecated Deprecated in Jedis 7.4.0. * Use {@link #executeCommand(CommandArguments)} with a {@link CommandArguments} object directly. *
{@code
   * jedis.executeCommand(new CommandArguments(GET).key("mykey".getBytes()).processKey(sampleKey));
   * }
*/ @Deprecated public Object sendCommand(byte[] sampleKey, ProtocolCommand cmd, byte[]... args) { return executeCommand(commandObjects.commandArguments(cmd).addObjects((Object[]) args).addHashSlotKey(sampleKey)); } /** * @deprecated Deprecated in Jedis 7.4.0. * Use {@link #executeCommand(CommandArguments)} with a {@link CommandArguments} object directly. *
{@code
   * jedis.executeCommand(new CommandArguments(BLPOP).key("mykey".getBytes()).add("0".getBytes()).blocking().processKey(sampleKey));
   * }
*/ @Deprecated public Object sendBlockingCommand(byte[] sampleKey, ProtocolCommand cmd, byte[]... args) { return executeCommand( commandObjects.commandArguments(cmd).addObjects((Object[]) args).blocking().addHashSlotKey(sampleKey)); } /** * @deprecated Deprecated in Jedis 7.4.0. * Use {@link #executeCommand(CommandArguments)} with a {@link CommandArguments} object directly. *
{@code
   * jedis.executeCommand(new CommandArguments(GET).key("mykey").processKey(sampleKey));
   * }
*/ @Deprecated public Object sendCommand(String sampleKey, ProtocolCommand cmd, String... args) { return executeCommand(commandObjects.commandArguments(cmd).addObjects((Object[]) args).addHashSlotKey(sampleKey)); } /** * @deprecated Deprecated in Jedis 7.4.0. * Use {@link #executeCommand(CommandArguments)} with a {@link CommandArguments} object directly. *
{@code
   * jedis.executeCommand(new CommandArguments(BLPOP).key("mykey").add("0").blocking().processKey(sampleKey));
   * }
*/ @Deprecated public Object sendBlockingCommand(String sampleKey, ProtocolCommand cmd, String... args) { return executeCommand( commandObjects.commandArguments(cmd).addObjects((Object[]) args).blocking().addHashSlotKey(sampleKey)); } public Object executeCommand(CommandArguments args) { return executeCommand(new CommandObject<>(args, BuilderFactory.RAW_OBJECT)); } @Experimental public void setKeyArgumentPreProcessor(CommandKeyArgumentPreProcessor keyPreProcessor) { this.commandObjects.setKeyArgumentPreProcessor(keyPreProcessor); } public void setJsonObjectMapper(JsonObjectMapper jsonObjectMapper) { this.commandObjects.setJsonObjectMapper(jsonObjectMapper); } public void setDefaultSearchDialect(int dialect) { this.commandObjects.setDefaultSearchDialect(dialect); } // Vector Set commands @Override public boolean vadd(String key, float[] vector, String element) { return executeCommand(commandObjects.vadd(key, vector, element)); } @Override public boolean vadd(String key, float[] vector, String element, VAddParams params) { return executeCommand(commandObjects.vadd(key, vector, element, params)); } @Override public boolean vaddFP32(String key, byte[] vectorBlob, String element) { return executeCommand(commandObjects.vaddFP32(key, vectorBlob, element)); } @Override public boolean vaddFP32(String key, byte[] vectorBlob, String element, VAddParams params) { return executeCommand(commandObjects.vaddFP32(key, vectorBlob, element, params)); } @Override public boolean vadd(String key, float[] vector, String element, int reduceDim, VAddParams params) { return executeCommand(commandObjects.vadd(key, vector, element, reduceDim, params)); } @Override public boolean vaddFP32(String key, byte[] vectorBlob, String element, int reduceDim, VAddParams params) { return executeCommand(commandObjects.vaddFP32(key, vectorBlob, element, reduceDim, params)); } @Override public List vsim(String key, float[] vector) { return executeCommand(commandObjects.vsim(key, vector)); } @Override public List vsim(String key, float[] vector, VSimParams params) { return executeCommand(commandObjects.vsim(key, vector, params)); } @Override public Map vsimWithScores(String key, float[] vector, VSimParams params) { return executeCommand(commandObjects.vsimWithScores(key, vector, params)); } @Override public Map vsimWithScoresAndAttribs(String key, float[] vector, VSimParams params) { return executeCommand(commandObjects.vsimWithScoresAndAttribs(key, vector, params)); } @Override public List vsimByElement(String key, String element) { return executeCommand(commandObjects.vsimByElement(key, element)); } @Override public List vsimByElement(String key, String element, VSimParams params) { return executeCommand(commandObjects.vsimByElement(key, element, params)); } @Override public Map vsimByElementWithScores(String key, String element, VSimParams params) { return executeCommand(commandObjects.vsimByElementWithScores(key, element, params)); } @Override public Map vsimByElementWithScoresAndAttribs(String key, String element, VSimParams params) { return executeCommand(commandObjects.vsimByElementWithScoresAndAttribs(key, element, params)); } @Override public long vdim(String key) { return executeCommand(commandObjects.vdim(key)); } @Override public long vcard(String key) { return executeCommand(commandObjects.vcard(key)); } @Override public List vemb(String key, String element) { return executeCommand(commandObjects.vemb(key, element)); } @Override public RawVector vembRaw(String key, String element) { return executeCommand(commandObjects.vembRaw(key, element)); } @Override public boolean vrem(String key, String element) { return executeCommand(commandObjects.vrem(key, element)); } @Override public List> vlinks(String key, String element) { return executeCommand(commandObjects.vlinks(key, element)); } @Override public List> vlinksWithScores(String key, String element) { return executeCommand(commandObjects.vlinksWithScores(key, element)); } @Override public String vrandmember(String key) { return executeCommand(commandObjects.vrandmember(key)); } @Override public List vrandmember(String key, int count) { return executeCommand(commandObjects.vrandmember(key, count)); } @Override public String vgetattr(String key, String element) { return executeCommand(commandObjects.vgetattr(key, element)); } @Override public boolean vsetattr(String key, String element, String attributes) { return executeCommand(commandObjects.vsetattr(key, element, attributes)); } @Override public VectorInfo vinfo(String key) { return executeCommand(commandObjects.vinfo(key)); } // Binary vector set commands @Override public boolean vadd(byte[] key, float[] vector, byte[] element) { return executeCommand(commandObjects.vadd(key, vector, element)); } @Override public boolean vadd(byte[] key, float[] vector, byte[] element, VAddParams params) { return executeCommand(commandObjects.vadd(key, vector, element, params)); } @Override public boolean vaddFP32(byte[] key, byte[] vectorBlob, byte[] element) { return executeCommand(commandObjects.vaddFP32(key, vectorBlob, element)); } @Override public boolean vaddFP32(byte[] key, byte[] vectorBlob, byte[] element, VAddParams params) { return executeCommand(commandObjects.vaddFP32(key, vectorBlob, element, params)); } @Override public boolean vadd(byte[] key, float[] vector, byte[] element, int reduceDim, VAddParams params) { return executeCommand(commandObjects.vadd(key, vector, element, reduceDim, params)); } @Override public boolean vaddFP32(byte[] key, byte[] vectorBlob, byte[] element, int reduceDim, VAddParams params) { return executeCommand(commandObjects.vaddFP32(key, vectorBlob, element, reduceDim, params)); } @Override public List vsim(byte[] key, float[] vector) { return executeCommand(commandObjects.vsim(key, vector)); } @Override public List vsim(byte[] key, float[] vector, VSimParams params) { return executeCommand(commandObjects.vsim(key, vector, params)); } @Override public Map vsimWithScores(byte[] key, float[] vector, VSimParams params) { return executeCommand(commandObjects.vsimWithScores(key, vector, params)); } @Override public Map vsimWithScoresAndAttribs(byte[] key, float[] vector, VSimParams params) { return executeCommand(commandObjects.vsimWithScoresAndAttribs(key, vector, params)); } @Override public List vsimByElement(byte[] key, byte[] element) { return executeCommand(commandObjects.vsimByElement(key, element)); } @Override public List vsimByElement(byte[] key, byte[] element, VSimParams params) { return executeCommand(commandObjects.vsimByElement(key, element, params)); } @Override public Map vsimByElementWithScores(byte[] key, byte[] element, VSimParams params) { return executeCommand(commandObjects.vsimByElementWithScores(key, element, params)); } @Override public Map vsimByElementWithScoresAndAttribs(byte[] key, byte[] element, VSimParams params) { return executeCommand(commandObjects.vsimByElementWithScoresAndAttribs(key, element, params)); } @Override public long vdim(byte[] key) { return executeCommand(commandObjects.vdim(key)); } @Override public long vcard(byte[] key) { return executeCommand(commandObjects.vcard(key)); } @Override public List vemb(byte[] key, byte[] element) { return executeCommand(commandObjects.vemb(key, element)); } @Override public RawVector vembRaw(byte[] key, byte[] element) { return executeCommand(commandObjects.vembRaw(key, element)); } @Override public boolean vrem(byte[] key, byte[] element) { return executeCommand(commandObjects.vrem(key, element)); } @Override public List> vlinks(byte[] key, byte[] element) { return executeCommand(commandObjects.vlinks(key, element)); } @Override public List> vlinksWithScores(byte[] key, byte[] element) { return executeCommand(commandObjects.vlinksWithScores(key, element)); } @Override public byte[] vrandmember(byte[] key) { return executeCommand(commandObjects.vrandmember(key)); } @Override public List vrandmember(byte[] key, int count) { return executeCommand(commandObjects.vrandmember(key, count)); } @Override public byte[] vgetattr(byte[] key, byte[] element) { return executeCommand(commandObjects.vgetattr(key, element)); } @Override public boolean vsetattr(byte[] key, byte[] element, byte[] attributes) { return executeCommand(commandObjects.vsetattr(key, element, attributes)); } } ================================================ FILE: src/main/java/redis/clients/jedis/annots/Experimental.java ================================================ package redis.clients.jedis.annots; import java.lang.annotation.Documented; import java.lang.annotation.ElementType; import java.lang.annotation.Target; /** * Annotation to mark classes for experimental development. *

* Classes with this annotation may be renamed, changed or even removed in a future version. This * annotation doesn't mean that the implementation has an 'experimental' quality. *

* If a type is marked with this annotation, all its members are considered experimental. */ @Documented @Target({ ElementType.PACKAGE, ElementType.TYPE, ElementType.METHOD, ElementType.FIELD, ElementType.CONSTRUCTOR }) public @interface Experimental { } ================================================ FILE: src/main/java/redis/clients/jedis/annots/Internal.java ================================================ package redis.clients.jedis.annots; import java.lang.annotation.Documented; import java.lang.annotation.ElementType; import java.lang.annotation.Target; /** * Annotation to mark classes or methods as an internal development API. It indicates that the * annotated element must not be considered as a public API. *

* Classes or methods with this annotation may change across releases. *

* If a type is marked with this annotation, all its members are considered internal. */ @Documented @Target({ ElementType.TYPE, ElementType.METHOD, ElementType.CONSTRUCTOR, ElementType.FIELD }) public @interface Internal { } ================================================ FILE: src/main/java/redis/clients/jedis/annots/VisibleForTesting.java ================================================ package redis.clients.jedis.annots; import java.lang.annotation.Documented; import java.lang.annotation.ElementType; import java.lang.annotation.Target; /** * A member or type annotated with {@link VisibleForTesting} declares that it is only visible for * testing purposes. */ @Documented @Target({ ElementType.METHOD, ElementType.CONSTRUCTOR, ElementType.FIELD, ElementType.TYPE }) public @interface VisibleForTesting { } ================================================ FILE: src/main/java/redis/clients/jedis/args/BitCountOption.java ================================================ package redis.clients.jedis.args; import redis.clients.jedis.util.SafeEncoder; /** * The options for {@code BITCOUNT} command. */ public enum BitCountOption implements Rawable { BYTE, BIT; private final byte[] raw; private BitCountOption() { raw = SafeEncoder.encode(name()); } @Override public byte[] getRaw() { return raw; } } ================================================ FILE: src/main/java/redis/clients/jedis/args/BitOP.java ================================================ package redis.clients.jedis.args; import redis.clients.jedis.util.SafeEncoder; /** * Bit operations for {@code BITOP} command. */ public enum BitOP implements Rawable { AND, OR, XOR, NOT, DIFF, DIFF1, ANDOR, ONE; private final byte[] raw; private BitOP() { raw = SafeEncoder.encode(name()); } @Override public byte[] getRaw() { return raw; } } ================================================ FILE: src/main/java/redis/clients/jedis/args/ClientAttributeOption.java ================================================ package redis.clients.jedis.args; import redis.clients.jedis.util.SafeEncoder; /** * CLIENT SETINFO command attr option * since redis 7.2 */ public enum ClientAttributeOption implements Rawable { LIB_NAME("LIB-NAME"), LIB_VER("LIB-VER"); private final byte[] raw; private ClientAttributeOption(String str) { this.raw = SafeEncoder.encode(str); } @Override public byte[] getRaw() { return raw; } } ================================================ FILE: src/main/java/redis/clients/jedis/args/ClientPauseMode.java ================================================ package redis.clients.jedis.args; import redis.clients.jedis.util.SafeEncoder; /** * Client pause supported modes. */ public enum ClientPauseMode implements Rawable { ALL, WRITE; private final byte[] raw; private ClientPauseMode() { raw = SafeEncoder.encode(name()); } @Override public byte[] getRaw() { return raw; } } ================================================ FILE: src/main/java/redis/clients/jedis/args/ClientType.java ================================================ package redis.clients.jedis.args; import redis.clients.jedis.util.SafeEncoder; public enum ClientType implements Rawable { NORMAL, MASTER, SLAVE, REPLICA, PUBSUB; private final byte[] raw; private ClientType() { raw = SafeEncoder.encode(name().toLowerCase()); } @Override public byte[] getRaw() { return raw; } } ================================================ FILE: src/main/java/redis/clients/jedis/args/ClusterFailoverOption.java ================================================ package redis.clients.jedis.args; import redis.clients.jedis.util.SafeEncoder; /** * Enumeration of cluster failover options. *

* Used by {@link redis.clients.jedis.commands.ClusterCommands#clusterFailover(ClusterFailoverOption)}. */ public enum ClusterFailoverOption implements Rawable { FORCE, TAKEOVER; private final byte[] raw; private ClusterFailoverOption() { this.raw = SafeEncoder.encode(name()); } @Override public byte[] getRaw() { return raw; } } ================================================ FILE: src/main/java/redis/clients/jedis/args/ClusterResetType.java ================================================ package redis.clients.jedis.args; import redis.clients.jedis.util.SafeEncoder; /** * Reset type for command cluster reset. Before reset cluster status you should make true no data in Redis. * It is generally used to create/expand clusters. */ public enum ClusterResetType implements Rawable { /** * Soft reset: Reset only the cluster info. */ SOFT, /** * Hard reset: Reset the cluster info, set epochs to 0, change node ID. */ HARD; private final byte[] raw; private ClusterResetType() { this.raw = SafeEncoder.encode(name()); } @Override public byte[] getRaw() { return raw; } } ================================================ FILE: src/main/java/redis/clients/jedis/args/ExpiryOption.java ================================================ package redis.clients.jedis.args; import redis.clients.jedis.util.SafeEncoder; /** * Enumeration of setting expiration. */ public enum ExpiryOption implements Rawable { /** * Set expiry only when the key has no expiry. */ NX, /** * Set expiry only when the key has an existing expiry. */ XX, /** * Set expiry only when the new expiry is greater than the existing one. */ GT, /** * Set expiry only when the new expiry is less than the existing one. */ LT; private final byte[] raw; private ExpiryOption() { raw = SafeEncoder.encode(name()); } @Override public byte[] getRaw() { return raw; } } ================================================ FILE: src/main/java/redis/clients/jedis/args/FlushMode.java ================================================ package redis.clients.jedis.args; import redis.clients.jedis.util.SafeEncoder; /** * Enum object describing flushing mode. */ public enum FlushMode implements Rawable { /** * flushes synchronously */ SYNC, /** * flushes asynchronously */ ASYNC; private final byte[] raw; private FlushMode() { raw = SafeEncoder.encode(this.name()); } @Override public byte[] getRaw() { return raw; } } ================================================ FILE: src/main/java/redis/clients/jedis/args/FunctionRestorePolicy.java ================================================ package redis.clients.jedis.args; import redis.clients.jedis.util.SafeEncoder; public enum FunctionRestorePolicy implements Rawable { /** * Delete all existing libraries before restoring the payload */ FLUSH, /** * Append the restored libraries to the existing libraries and * aborts on collision. This is the default policy. */ APPEND, /** * Append the restored libraries to the existing libraries, replacing * any existing ones in case of name collisions. Note that this policy * doesn't prevent function name collisions, only libraries. */ REPLACE; private final byte[] raw; private FunctionRestorePolicy() { raw = SafeEncoder.encode(name()); } @Override public byte[] getRaw() { return raw; } } ================================================ FILE: src/main/java/redis/clients/jedis/args/GeoUnit.java ================================================ package redis.clients.jedis.args; import java.util.Locale; import redis.clients.jedis.util.SafeEncoder; public enum GeoUnit implements Rawable { M, KM, MI, FT; private final byte[] raw; private GeoUnit() { raw = SafeEncoder.encode(name().toLowerCase(Locale.ENGLISH)); } @Override public byte[] getRaw() { return raw; } } ================================================ FILE: src/main/java/redis/clients/jedis/args/HotkeysMetric.java ================================================ package redis.clients.jedis.args; import redis.clients.jedis.util.SafeEncoder; /** * Enum representing the metrics that can be tracked by the HOTKEYS command. */ public enum HotkeysMetric implements Rawable { /** * Track CPU time consumption per key. */ CPU, /** * Track network bytes transferred per key. */ NET; private final byte[] raw; private HotkeysMetric() { raw = SafeEncoder.encode(this.name()); } @Override public byte[] getRaw() { return raw; } } ================================================ FILE: src/main/java/redis/clients/jedis/args/LatencyEvent.java ================================================ package redis.clients.jedis.args; import redis.clients.jedis.util.SafeEncoder; public enum LatencyEvent implements Rawable { ACTIVE_DEFRAG_CYCLE("active-defrag-cycle"), AOF_FSYNC_ALWAYS("aof-fsync-always"), AOF_STAT("aof-stat"), AOF_REWRITE_DIFF_WRITE("aof-rewrite-diff-write"), AOF_RENAME("aof-rename"), AOF_WRITE("aof-write"), AOF_WRITE_ACTIVE_CHILD("aof-write-active-child"), AOF_WRITE_ALONE("aof-write-alone"), AOF_WRITE_PENDING_FSYNC("aof-write-pending-fsync"), COMMAND("command"), EXPIRE_CYCLE("expire-cycle"), EVICTION_CYCLE("eviction-cycle"), EVICTION_DEL("eviction-del"), FAST_COMMAND("fast-command"), FORK("fork"), RDB_UNLINK_TEMP_FILE("rdb-unlink-temp-file"); private final byte[] raw; private LatencyEvent(String s) { raw = SafeEncoder.encode(s); } @Override public byte[] getRaw() { return raw; } } ================================================ FILE: src/main/java/redis/clients/jedis/args/ListDirection.java ================================================ package redis.clients.jedis.args; import redis.clients.jedis.util.SafeEncoder; /** * Direction for {@code LMOVE} and {@code BLMOVE} command. */ public enum ListDirection implements Rawable { LEFT, RIGHT; private final byte[] raw; private ListDirection() { raw = SafeEncoder.encode(name()); } @Override public byte[] getRaw() { return raw; } } ================================================ FILE: src/main/java/redis/clients/jedis/args/ListPosition.java ================================================ package redis.clients.jedis.args; import redis.clients.jedis.util.SafeEncoder; public enum ListPosition implements Rawable { BEFORE, AFTER; private final byte[] raw; private ListPosition() { raw = SafeEncoder.encode(name()); } @Override public byte[] getRaw() { return raw; } } ================================================ FILE: src/main/java/redis/clients/jedis/args/Rawable.java ================================================ package redis.clients.jedis.args; /** * Byte array representation of arguments to write in socket input stream. */ public interface Rawable { /** * Get byte array. * @return binary */ byte[] getRaw(); @Override int hashCode(); @Override boolean equals(Object o); } ================================================ FILE: src/main/java/redis/clients/jedis/args/RawableFactory.java ================================================ package redis.clients.jedis.args; import static redis.clients.jedis.Protocol.toByteArray; import java.util.Arrays; import redis.clients.jedis.util.SafeEncoder; /** * Factory class to get {@link Rawable} objects. */ public final class RawableFactory { /** * Get a {@link Rawable} from a {@code boolean}. * @param b boolean value * @return raw */ public static Rawable from(boolean b) { return from(toByteArray(b)); } /** * Get a {@link Rawable} from an {@code int}. * @param i integer value * @return raw */ public static Rawable from(int i) { return from(toByteArray(i)); } /** * Get a {@link Rawable} from a {@code long}. * @param l long value * @return raw */ public static Rawable from(long l) { return from(toByteArray(l)); } /** * Get a {@link Rawable} from a {@code double}. * @param d numeric value * @return raw */ public static Rawable from(double d) { return from(toByteArray(d)); } /** * Get a {@link Rawable} from a byte array. * @param binary value * @return raw */ public static Rawable from(byte[] binary) { return new Raw(binary); } /** * Get a {@link Rawable} from a {@link String}. * @param string value * @return raw */ public static Rawable from(String string) { return new RawString(string); } /** * Default implementation of {@link Rawable}. */ public static class Raw implements Rawable { private final byte[] raw; public Raw(byte[] raw) { this.raw = Arrays.copyOf(raw, raw.length); } @Override public byte[] getRaw() { return raw; } @Override public boolean equals(Object o) { if (this == o) return true; if (o == null || getClass() != o.getClass()) return false; return Arrays.equals(raw, ((Raw) o).raw); } @Override public int hashCode() { return Arrays.hashCode(raw); } } /** * A {@link Rawable} wrapping a {@link String}. */ public static class RawString extends Raw { // TODO: private final String str; ^ implements Rawable public RawString(String str) { super(SafeEncoder.encode(str)); } } private RawableFactory() { throw new InstantiationError(); } } ================================================ FILE: src/main/java/redis/clients/jedis/args/SaveMode.java ================================================ package redis.clients.jedis.args; import redis.clients.jedis.util.SafeEncoder; public enum SaveMode implements Rawable { /** * Prevent a DB saving operation even if one or more save points are configured. */ NOSAVE, /** * Force a DB saving operation even if no save points are configured. */ SAVE; private final byte[] raw; private SaveMode() { raw = SafeEncoder.encode(name()); } @Override public byte[] getRaw() { return raw; } } ================================================ FILE: src/main/java/redis/clients/jedis/args/SortedSetOption.java ================================================ package redis.clients.jedis.args; import redis.clients.jedis.util.SafeEncoder; public enum SortedSetOption implements Rawable { MIN, MAX; private final byte[] raw; private SortedSetOption() { raw = SafeEncoder.encode(name()); } @Override public byte[] getRaw() { return raw; } } ================================================ FILE: src/main/java/redis/clients/jedis/args/SortingOrder.java ================================================ package redis.clients.jedis.args; import redis.clients.jedis.util.SafeEncoder; public enum SortingOrder implements Rawable { ASC, DESC; private final byte[] raw; private SortingOrder() { raw = SafeEncoder.encode(this.name()); } @Override public byte[] getRaw() { return raw; } } ================================================ FILE: src/main/java/redis/clients/jedis/args/StreamDeletionPolicy.java ================================================ package redis.clients.jedis.args; import redis.clients.jedis.util.SafeEncoder; /** * Deletion policy for stream commands that handle consumer group references. Used with XDELEX, * XACKDEL, and enhanced XADD/XTRIM commands. */ public enum StreamDeletionPolicy implements Rawable { /** * Preserves existing references to entries in all consumer groups' PEL. This is the default * behavior similar to XDEL. */ KEEP_REFERENCES("KEEPREF"), /** * Removes all references to entries from all consumer groups' pending entry lists, effectively * cleaning up all traces of the messages. */ DELETE_REFERENCES("DELREF"), /** * Only operates on entries that were read and acknowledged by all consumer groups. */ ACKNOWLEDGED("ACKED"); private final byte[] raw; StreamDeletionPolicy(String redisParamName) { raw = SafeEncoder.encode(redisParamName); } @Override public byte[] getRaw() { return raw; } } ================================================ FILE: src/main/java/redis/clients/jedis/args/UnblockType.java ================================================ package redis.clients.jedis.args; import redis.clients.jedis.util.SafeEncoder; /** * Unblock type for {@code CLIENT UNBLOCK} command. */ public enum UnblockType implements Rawable { TIMEOUT, ERROR; private final byte[] raw; private UnblockType() { raw = SafeEncoder.encode(this.name()); } @Override public byte[] getRaw() { return raw; } } ================================================ FILE: src/main/java/redis/clients/jedis/args/package-info.java ================================================ /** * This package contains the classes that represent arguments of Redis core commands. */ package redis.clients.jedis.args; ================================================ FILE: src/main/java/redis/clients/jedis/authentication/AuthXEventListener.java ================================================ package redis.clients.jedis.authentication; public interface AuthXEventListener { static AuthXEventListener NOOP_LISTENER = new AuthXEventListener() { @Override public void onIdentityProviderError(Exception reason) { } @Override public void onConnectionAuthenticationError(Exception reason) { } }; public void onIdentityProviderError(Exception reason); public void onConnectionAuthenticationError(Exception reason); } ================================================ FILE: src/main/java/redis/clients/jedis/authentication/AuthXManager.java ================================================ package redis.clients.jedis.authentication; import java.lang.ref.WeakReference; import java.util.ArrayList; import java.util.Collections; import java.util.List; import java.util.concurrent.CompletableFuture; import java.util.concurrent.ExecutionException; import java.util.concurrent.Future; import java.util.concurrent.atomic.AtomicReference; import java.util.function.Consumer; import java.util.function.Supplier; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import redis.clients.authentication.core.Token; import redis.clients.authentication.core.TokenAuthConfig; import redis.clients.authentication.core.TokenListener; import redis.clients.authentication.core.TokenManager; import redis.clients.jedis.Connection; import redis.clients.jedis.RedisCredentials; public final class AuthXManager implements Supplier { private static final Logger log = LoggerFactory.getLogger(AuthXManager.class); private TokenManager tokenManager; private List> connections = Collections .synchronizedList(new ArrayList<>()); private Token currentToken; private AuthXEventListener listener = AuthXEventListener.NOOP_LISTENER; private List> postAuthenticateHooks = new ArrayList<>(); private AtomicReference> uniqueStarterTask = new AtomicReference<>(); protected AuthXManager(TokenManager tokenManager) { this.tokenManager = tokenManager; } public AuthXManager(TokenAuthConfig tokenAuthConfig) { this(new TokenManager(tokenAuthConfig.getIdentityProviderConfig().getProvider(), tokenAuthConfig.getTokenManagerConfig())); } public void start() { Future safeStarter = safeStart(this::tokenManagerStart); try { safeStarter.get(); } catch (InterruptedException | ExecutionException e) { log.error("AuthXManager failed to start!", e); throw new JedisAuthenticationException("AuthXManager failed to start!", (e instanceof ExecutionException) ? e.getCause() : e); } } private Future safeStart(Runnable starter) { if (uniqueStarterTask.compareAndSet(null, new CompletableFuture())) { try { starter.run(); uniqueStarterTask.get().complete(null); } catch (Exception e) { uniqueStarterTask.get().completeExceptionally(e); } } return uniqueStarterTask.get(); } private void tokenManagerStart() { tokenManager.start(new TokenListener() { @Override public void onTokenRenewed(Token token) { currentToken = token; authenticateConnections(token); } @Override public void onError(Exception reason) { listener.onIdentityProviderError(reason); } }, true); } public void authenticateConnections(Token token) { RedisCredentials credentialsFromToken = new TokenCredentials(token); for (WeakReference connectionRef : connections) { Connection connection = connectionRef.get(); if (connection != null) { connection.setCredentials(credentialsFromToken); } else { connections.remove(connectionRef); } } postAuthenticateHooks.forEach(hook -> hook.accept(token)); } public Connection addConnection(Connection connection) { connections.add(new WeakReference<>(connection)); return connection; } public void stop() { tokenManager.stop(); } public void setListener(AuthXEventListener listener) { if (listener != null) { this.listener = listener; } } public void addPostAuthenticationHook(Consumer postAuthenticateHook) { postAuthenticateHooks.add(postAuthenticateHook); } public void removePostAuthenticationHook(Consumer postAuthenticateHook) { postAuthenticateHooks.remove(postAuthenticateHook); } public AuthXEventListener getListener() { return listener; } @Override public RedisCredentials get() { return new TokenCredentials(this.currentToken); } } ================================================ FILE: src/main/java/redis/clients/jedis/authentication/JedisAuthenticationException.java ================================================ package redis.clients.jedis.authentication; import redis.clients.jedis.exceptions.JedisException; public class JedisAuthenticationException extends JedisException { public JedisAuthenticationException(String message) { super(message); } public JedisAuthenticationException(String message, Throwable cause) { super(message, cause); } } ================================================ FILE: src/main/java/redis/clients/jedis/authentication/TokenCredentials.java ================================================ package redis.clients.jedis.authentication; import redis.clients.authentication.core.Token; import redis.clients.jedis.RedisCredentials; class TokenCredentials implements RedisCredentials { private final String user; private final char[] password; public TokenCredentials(Token token) { user = token.getUser(); password = token.getValue().toCharArray(); } @Override public String getUser() { return user; } @Override public char[] getPassword() { return password; } } ================================================ FILE: src/main/java/redis/clients/jedis/bloom/BFInsertParams.java ================================================ package redis.clients.jedis.bloom; import static redis.clients.jedis.Protocol.toByteArray; import static redis.clients.jedis.bloom.RedisBloomProtocol.RedisBloomKeyword.CAPACITY; import static redis.clients.jedis.bloom.RedisBloomProtocol.RedisBloomKeyword.ERROR; import static redis.clients.jedis.bloom.RedisBloomProtocol.RedisBloomKeyword.EXPANSION; import static redis.clients.jedis.bloom.RedisBloomProtocol.RedisBloomKeyword.NOCREATE; import static redis.clients.jedis.bloom.RedisBloomProtocol.RedisBloomKeyword.NONSCALING; import redis.clients.jedis.CommandArguments; import redis.clients.jedis.params.IParams; // [CAPACITY {cap}] [ERROR {error}] [EXPANSION {expansion}] [NOCREATE] [NONSCALING] public class BFInsertParams implements IParams { private Long capacity; private Double errorRate; private Integer expansion; private boolean noCreate = false; private boolean nonScaling = false; public static BFInsertParams insertParams() { return new BFInsertParams(); } public BFInsertParams capacity(long capacity) { this.capacity = capacity; return this; } public BFInsertParams error(double errorRate) { this.errorRate = errorRate; return this; } public BFInsertParams expansion(int expansion) { this.expansion = expansion; return this; } public BFInsertParams noCreate() { this.noCreate = true; return this; } public BFInsertParams nonScaling() { this.nonScaling = true; return this; } @Override public void addParams(CommandArguments args) { if (capacity != null) { args.add(CAPACITY).add(toByteArray(capacity)); } if (errorRate != null) { args.add(ERROR).add(toByteArray(errorRate)); } if (expansion != null) { args.add(EXPANSION).add(toByteArray(expansion)); } if (noCreate) { args.add(NOCREATE); } if (nonScaling) { args.add(NONSCALING); } } } ================================================ FILE: src/main/java/redis/clients/jedis/bloom/BFReserveParams.java ================================================ package redis.clients.jedis.bloom; import static redis.clients.jedis.Protocol.toByteArray; import static redis.clients.jedis.bloom.RedisBloomProtocol.RedisBloomKeyword.EXPANSION; import static redis.clients.jedis.bloom.RedisBloomProtocol.RedisBloomKeyword.NONSCALING; import redis.clients.jedis.CommandArguments; import redis.clients.jedis.params.IParams; public class BFReserveParams implements IParams { private Integer expansion; private boolean nonScaling = false; public static BFReserveParams reserveParams() { return new BFReserveParams(); } public BFReserveParams expansion(int expansion) { this.expansion = expansion; return this; } public BFReserveParams nonScaling() { this.nonScaling = true; return this; } @Override public void addParams(CommandArguments args) { if (expansion != null) { args.add(EXPANSION).add(toByteArray(expansion)); } if (nonScaling) { args.add(NONSCALING); } } } ================================================ FILE: src/main/java/redis/clients/jedis/bloom/CFInsertParams.java ================================================ package redis.clients.jedis.bloom; import static redis.clients.jedis.Protocol.toByteArray; import static redis.clients.jedis.bloom.RedisBloomProtocol.RedisBloomKeyword.CAPACITY; import static redis.clients.jedis.bloom.RedisBloomProtocol.RedisBloomKeyword.NOCREATE; import redis.clients.jedis.CommandArguments; import redis.clients.jedis.params.IParams; // [CAPACITY {capacity}] [NOCREATE] public class CFInsertParams implements IParams { private Long capacity; private boolean noCreate = false; public static CFInsertParams insertParams() { return new CFInsertParams(); } public CFInsertParams capacity(long capacity) { this.capacity = capacity; return this; } public CFInsertParams noCreate() { this.noCreate = true; return this; } @Override public void addParams(CommandArguments args) { if (capacity != null) { args.add(CAPACITY).add(toByteArray(capacity)); } if (noCreate) { args.add(NOCREATE); } } } ================================================ FILE: src/main/java/redis/clients/jedis/bloom/CFReserveParams.java ================================================ package redis.clients.jedis.bloom; import static redis.clients.jedis.Protocol.toByteArray; import static redis.clients.jedis.bloom.RedisBloomProtocol.RedisBloomKeyword.BUCKETSIZE; import static redis.clients.jedis.bloom.RedisBloomProtocol.RedisBloomKeyword.EXPANSION; import static redis.clients.jedis.bloom.RedisBloomProtocol.RedisBloomKeyword.MAXITERATIONS; import redis.clients.jedis.CommandArguments; import redis.clients.jedis.params.IParams; // [BUCKETSIZE {bucketsize}] [MAXITERATIONS {maxiterations}] [EXPANSION {expansion}] public class CFReserveParams implements IParams { private Long bucketSize; private Integer maxIterations; private Integer expansion; public static CFReserveParams reserveParams() { return new CFReserveParams(); } public CFReserveParams bucketSize(long bucketSize) { this.bucketSize = bucketSize; return this; } public CFReserveParams maxIterations(int maxIterations) { this.maxIterations = maxIterations; return this; } public CFReserveParams expansion(int expansion) { this.expansion = expansion; return this; } @Override public void addParams(CommandArguments args) { if (bucketSize != null) { args.add(BUCKETSIZE).add(toByteArray(bucketSize)); } if (maxIterations != null) { args.add(MAXITERATIONS).add(toByteArray(maxIterations)); } if (expansion != null) { args.add(EXPANSION).add(toByteArray(expansion)); } } } ================================================ FILE: src/main/java/redis/clients/jedis/bloom/RedisBloomProtocol.java ================================================ package redis.clients.jedis.bloom; import redis.clients.jedis.args.Rawable; import redis.clients.jedis.commands.ProtocolCommand; import redis.clients.jedis.util.SafeEncoder; public class RedisBloomProtocol { public enum BloomFilterCommand implements ProtocolCommand { RESERVE("BF.RESERVE"), ADD("BF.ADD"), MADD("BF.MADD"), EXISTS("BF.EXISTS"), MEXISTS("BF.MEXISTS"), INSERT("BF.INSERT"), SCANDUMP("BF.SCANDUMP"), LOADCHUNK("BF.LOADCHUNK"), CARD("BF.CARD"), INFO("BF.INFO"); private final byte[] raw; private BloomFilterCommand(String alt) { raw = SafeEncoder.encode(alt); } @Override public byte[] getRaw() { return raw; } } public enum CuckooFilterCommand implements ProtocolCommand { RESERVE("CF.RESERVE"), // ADD("CF.ADD"), // ADDNX("CF.ADDNX"), // INSERT("CF.INSERT"), // INSERTNX("CF.INSERTNX"), // EXISTS("CF.EXISTS"), // MEXISTS("CF.MEXISTS"), // DEL("CF.DEL"), // COUNT("CF.COUNT"), // SCANDUMP("CF.SCANDUMP"), // LOADCHUNK("CF.LOADCHUNK"), // INFO("CF.INFO"); private final byte[] raw; private CuckooFilterCommand(String alt) { raw = SafeEncoder.encode(alt); } @Override public byte[] getRaw() { return raw; } } public enum CountMinSketchCommand implements ProtocolCommand { INITBYDIM("CMS.INITBYDIM"), // INITBYPROB("CMS.INITBYPROB"), // INCRBY("CMS.INCRBY"), // QUERY("CMS.QUERY"), // MERGE("CMS.MERGE"), // INFO("CMS.INFO"); private final byte[] raw; private CountMinSketchCommand(String alt) { raw = SafeEncoder.encode(alt); } @Override public byte[] getRaw() { return raw; } } public enum TopKCommand implements ProtocolCommand { RESERVE("TOPK.RESERVE"), ADD("TOPK.ADD"), INCRBY("TOPK.INCRBY"), QUERY("TOPK.QUERY"), LIST("TOPK.LIST"), INFO("TOPK.INFO"); private final byte[] raw; private TopKCommand(String alt) { raw = SafeEncoder.encode(alt); } @Override public byte[] getRaw() { return raw; } } public enum TDigestCommand implements ProtocolCommand { CREATE, INFO, ADD, RESET, MERGE, CDF, QUANTILE, MIN, MAX, TRIMMED_MEAN, RANK, REVRANK, BYRANK, BYREVRANK; private final byte[] raw; private TDigestCommand() { raw = SafeEncoder.encode("TDIGEST." + name()); } @Override public byte[] getRaw() { return raw; } } public enum RedisBloomKeyword implements Rawable { CAPACITY, ERROR, NOCREATE, EXPANSION, NONSCALING, BUCKETSIZE, MAXITERATIONS, ITEMS, WEIGHTS, COMPRESSION, OVERRIDE, WITHCOUNT; private final byte[] raw; private RedisBloomKeyword() { raw = SafeEncoder.encode(name()); } @Override public byte[] getRaw() { return raw; } } } ================================================ FILE: src/main/java/redis/clients/jedis/bloom/TDigestMergeParams.java ================================================ package redis.clients.jedis.bloom; import static redis.clients.jedis.Protocol.toByteArray; import static redis.clients.jedis.bloom.RedisBloomProtocol.RedisBloomKeyword.COMPRESSION; import static redis.clients.jedis.bloom.RedisBloomProtocol.RedisBloomKeyword.OVERRIDE; import redis.clients.jedis.CommandArguments; import redis.clients.jedis.params.IParams; public class TDigestMergeParams implements IParams { private Integer compression; private boolean override = false; public static TDigestMergeParams mergeParams() { return new TDigestMergeParams(); } public TDigestMergeParams compression(int compression) { this.compression = compression; return this; } public TDigestMergeParams override() { this.override = true; return this; } @Override public void addParams(CommandArguments args) { if (compression != null) { args.add(COMPRESSION).add(toByteArray(compression)); } if (override) { args.add(OVERRIDE); } } } ================================================ FILE: src/main/java/redis/clients/jedis/bloom/commands/BloomFilterCommands.java ================================================ package redis.clients.jedis.bloom.commands; import java.util.List; import java.util.Map; import redis.clients.jedis.bloom.BFInsertParams; import redis.clients.jedis.bloom.BFReserveParams; public interface BloomFilterCommands { /** * {@code BF.RESERVE {key} {error_rate} {capacity}} * * @param key * @param errorRate * @param capacity * @return OK */ String bfReserve(String key, double errorRate, long capacity); /** * {@code BF.RESERVE {key} {error_rate} {capacity} [EXPANSION {expansion}] [NONSCALING]} * * @param key * @param errorRate * @param capacity * @param reserveParams * @return OK */ String bfReserve(String key, double errorRate, long capacity, BFReserveParams reserveParams); /** * {@code BF.ADD {key} {item}} * * @param key * @param item */ boolean bfAdd(String key, String item); /** * {@code BF.MADD {key} {item ...}} * * @param key * @param items */ List bfMAdd(String key, String... items); /** * {@code BF.INSERT {key} ITEMS {item ...}} * * @param key * @param items */ List bfInsert(String key, String... items); /** * {@code BF.INSERT {key} [CAPACITY {cap}] [ERROR {error}] [EXPANSION {expansion}] [NOCREATE] * [NONSCALING] ITEMS {item ...}} * * @param key * @param insertParams * @param items */ List bfInsert(String key, BFInsertParams insertParams, String... items); /** * {@code BF.EXISTS {key} {item}} * * @param key * @param item * @return if the item may exist */ boolean bfExists(String key, String item); /** * {@code BF.MEXISTS {key} {item ...}} * * @param key * @param items */ List bfMExists(String key, String... items); /** * {@code BF.SCANDUMP {key} {iterator}} * * @param key * @param iterator * @return Pair of next iterator and current data */ Map.Entry bfScanDump(String key, long iterator); /** * {@code BF.LOADCHUNK {key} {iterator} {data}} * * @param key * @param iterator * @param data * @return OK */ String bfLoadChunk(String key, long iterator, byte[] data); long bfCard(String key); Map bfInfo(String key); } ================================================ FILE: src/main/java/redis/clients/jedis/bloom/commands/BloomFilterPipelineCommands.java ================================================ package redis.clients.jedis.bloom.commands; import java.util.List; import java.util.Map; import redis.clients.jedis.Response; import redis.clients.jedis.bloom.BFInsertParams; import redis.clients.jedis.bloom.BFReserveParams; public interface BloomFilterPipelineCommands { Response bfReserve(String key, double errorRate, long capacity); Response bfReserve(String key, double errorRate, long capacity, BFReserveParams reserveParams); Response bfAdd(String key, String item); Response> bfMAdd(String key, String... items); Response> bfInsert(String key, String... items); Response> bfInsert(String key, BFInsertParams insertParams, String... items); Response bfExists(String key, String item); Response> bfMExists(String key, String... items); Response> bfScanDump(String key, long iterator); Response bfLoadChunk(String key, long iterator, byte[] data); Response bfCard(String key); Response> bfInfo(String key); } ================================================ FILE: src/main/java/redis/clients/jedis/bloom/commands/CountMinSketchCommands.java ================================================ package redis.clients.jedis.bloom.commands; import java.util.List; import java.util.Map; /** * Interface for RedisBloom Count-Min Sketch Commands * * @see RedisBloom * Count-Min Sketch Documentation */ public interface CountMinSketchCommands { /** * CMS.INITBYDIM Initializes a Count-Min Sketch to dimensions specified by user. * * @param key The name of the sketch * @param width Number of counter in each array. Reduces the error size * @param depth Number of counter-arrays. Reduces the probability for an error * of a certain size (percentage of total count * @return OK */ String cmsInitByDim(String key, long width, long depth); /** * CMS.INITBYPROB Initializes a Count-Min Sketch to accommodate requested * capacity. * * @param key The name of the sketch. * @param error Estimate size of error. The error is a percent of total * counted items. This effects the width of the sketch. * @param probability The desired probability for inflated count. This should be * a decimal value between 0 and 1. This effects the depth of * the sketch. For example, for a desired false positive rate * of 0.1% (1 in 1000), error_rate should be set to 0.001. * The closer this number is to zero, the greater the memory * consumption per item and the more CPU usage per operation. * @return OK */ String cmsInitByProb(String key, double error, double probability); /** * CMS.INCRBY Increases the count of item by increment * * @param key The name of the sketch * @param item The item which counter to be increased * @param increment Counter to be increased by this integer * @return Count for the item after increment */ // long cmsIncrBy(String key, String item, long increment); default long cmsIncrBy(String key, String item, long increment) { return cmsIncrBy(key, java.util.Collections.singletonMap(item, increment)).get(0); } /** * CMS.INCRBY Increases the count of one or more item. * * @param key The name of the sketch * @param itemIncrements a Map of the items to be increased and their integer * increment * @return Count of each item after increment */ List cmsIncrBy(String key, Map itemIncrements); /** * CMS.QUERY Returns count for item. Multiple items can be queried with one * call. * * @param key The name of the sketch * @param items The items for which to retrieve the counts * @return Count for one or more items */ List cmsQuery(String key, String... items); /** * CMS.MERGE Merges several sketches into one sketch. All sketches must have * identical width and depth. * * @param destKey The name of destination sketch. Must be initialized. * @param keys The sketches to be merged * @return OK */ String cmsMerge(String destKey, String... keys); /** * CMS.MERGE Merges several sketches into one sketch. All sketches must have * identical width and depth. Weights can be used to multiply certain sketches. * Default weight is 1. * * @param destKey The name of destination sketch. Must be initialized. * @param keysAndWeights A map of keys and weights used to multiply the sketch. * @return OK */ String cmsMerge(String destKey, Map keysAndWeights); /** * CMS.INFO Returns width, depth and total count of the sketch. * * @param key The name of the sketch * @return A Map with width, depth and total count. */ Map cmsInfo(String key); } ================================================ FILE: src/main/java/redis/clients/jedis/bloom/commands/CountMinSketchPipelineCommands.java ================================================ package redis.clients.jedis.bloom.commands; import java.util.List; import java.util.Map; import redis.clients.jedis.Response; public interface CountMinSketchPipelineCommands { Response cmsInitByDim(String key, long width, long depth); Response cmsInitByProb(String key, double error, double probability); Response> cmsIncrBy(String key, Map itemIncrements); Response> cmsQuery(String key, String... items); Response cmsMerge(String destKey, String... keys); Response cmsMerge(String destKey, Map keysAndWeights); Response> cmsInfo(String key); } ================================================ FILE: src/main/java/redis/clients/jedis/bloom/commands/CuckooFilterCommands.java ================================================ package redis.clients.jedis.bloom.commands; import java.util.List; import java.util.Map; import redis.clients.jedis.bloom.CFInsertParams; import redis.clients.jedis.bloom.CFReserveParams; /** * Interface for RedisBloom Cuckoo Filter Commands * * @see RedisBloom * Cuckoo Filter Documentation */ public interface CuckooFilterCommands { /** * CF.RESERVE Creates a Cuckoo Filter under key with the given parameters * * @param key The name of the filter * @param capacity * @return OK */ String cfReserve(String key, long capacity); /** * CF.RESERVE Creates a Cuckoo Filter under key with the given parameters * * @param key The name of the filter * @param capacity * @param reserveParams An instance of CFReserveParams containing the options * @return OK */ String cfReserve(String key, long capacity, CFReserveParams reserveParams); /** * CF.ADD Adds an item to the cuckoo filter, creating the filter if it does not * exist * * @param key The name of the filter * @param item The item to add * @return true on success, false otherwise */ boolean cfAdd(String key, String item); /** * CF.ADDNX Adds an item to the cuckoo filter, only if it does not exist yet * * @param key The name of the filter * @param item The item to add * @return true if the item was added to the filter, false if the item already * exists. */ boolean cfAddNx(String key, String item); /** * CF.INSERT Adds one or more items to a cuckoo filter, creating it if it does * not exist yet. * * @param key The name of the filter * @param items One or more items to add * @return true if the item was successfully inserted, false if an error * occurred */ List cfInsert(String key, String... items); /** * CF.INSERT Adds one or more items to a cuckoo filter, using the passed * options * * @param key The name of the filter * @param insertParams An instance of CFInsertParams containing the options * @param items One or more items to add * @return true if the item was successfully inserted, false if an error * occurred */ List cfInsert(String key, CFInsertParams insertParams, String... items); /** * CF.INSERTNX Adds one or more items to a cuckoo filter, only if it does not * exist yet * * @param key The name of the filter * @param items One or more items to add * @return true if the item was added to the filter, false if the item already * exists. */ List cfInsertNx(String key, String... items); /** * CF.INSERTNX Adds one or more items to a cuckoo filter, using the passed * options * * @param key The name of the filter * @param insertParams An instance of CFInsertParams containing the options * (CAPACITY/NOCREATE) * @param items One or more items to add * @return true if the item was added to the filter, false if the item already * exists. */ List cfInsertNx(String key, CFInsertParams insertParams, String... items); /** * CF.EXISTS Check if an item exists in a Cuckoo Filter * * @param key The name of the filter * @param item The item to check for * @return false if the item certainly does not exist, true if the item may * exist. */ boolean cfExists(String key, String item); /** * {@code CF.MEXISTS {key} {item ...}} * * @param key The name of the filter * @param items Items to check for (non empty sequence) * @return a list of booleans where false if the item certainly does not exist, * true if the item may exist. */ List cfMExists(String key, String... items); /** * CF.DEL Deletes an item once from the filter. If the item exists only once, it * will be removed from the filter. If the item was added multiple times, it * will still be present. * * @param key The name of the filter * @param item The item to delete from the filter * @return true if the item has been deleted, false if the item was not found. */ boolean cfDel(String key, String item); /** * CF.COUNT Returns the number of times an item may be in the filter. * * @param key The name of the filter * @param item The item to count * @return The number of times the item exists in the filter */ long cfCount(String key, String item); /** * CF.SCANDUMP Begins an incremental save of the cuckoo filter. This is useful * for large cuckoo filters which cannot fit into the normal SAVE and RESTORE * model. * * The Iterator is passed as input to the next invocation of SCANDUMP . If * Iterator is 0, the iteration has completed. * * @param key Name of the filter * @param iterator This is either 0, or the iterator from a previous invocation * of this command * @return a Map.Entry containing the Iterator and Data. */ Map.Entry cfScanDump(String key, long iterator); // // /** // * CF.SCANDUMP Begins an incremental save of the cuckoo filter. This is useful // * for large cuckoo filters which cannot fit into the normal SAVE and RESTORE // * model. // * // * Returns an iterator over the Map.Entry containing the Iterator and Data in // * proper sequence. // * // * @param key Name of the filter // * @return An iterator over the Pair containing the chunks of byte[] representing the filter // */ // Iterator> cfScanDumpIterator(String key); // // /** // * CF.SCANDUMP Begins an incremental save of the cuckoo filter. This is useful // * for large cuckoo filters which cannot fit into the normal SAVE and RESTORE // * model. // * // * Returns a sequential Stream with the Map.Entry containing the Iterator and // * Data as its source. // * // * @return A sequential Stream of Pair of iterator and data // */ // Stream> cfScanDumpStream(String key); /** * CF.LOADCHUNK Restores a filter previously saved using SCANDUMP. * * @param key Name of the filter to restore * @param iterator Iterator from CF.SCANDUMP * @param data Data from CF.SCANDUMP * @return OK */ String cfLoadChunk(String key, long iterator, byte[] data); // // /** // * CF.LOADCHUNK Restores a filter previously saved using SCANDUMP . See the // * SCANDUMP command for example usage. // * // * @param key Name of the filter to restore // * @param iterAndData Pair of iterator and data // */ // void cfLoadChunk(String key, Map.Entry iterAndData); /** * CF.INFO Return information about filter * * @param key Name of the filter to restore * @return A Map containing Size, Number of buckets, Number of filter, Number of * items inserted, Number of items deleted, Bucket size, Expansion rate, * Max iteration */ Map cfInfo(String key); } ================================================ FILE: src/main/java/redis/clients/jedis/bloom/commands/CuckooFilterPipelineCommands.java ================================================ package redis.clients.jedis.bloom.commands; import java.util.List; import java.util.Map; import redis.clients.jedis.Response; import redis.clients.jedis.bloom.CFInsertParams; import redis.clients.jedis.bloom.CFReserveParams; public interface CuckooFilterPipelineCommands { Response cfReserve(String key, long capacity); Response cfReserve(String key, long capacity, CFReserveParams reserveParams); Response cfAdd(String key, String item); Response cfAddNx(String key, String item); Response> cfInsert(String key, String... items); Response> cfInsert(String key, CFInsertParams insertParams, String... items); Response> cfInsertNx(String key, String... items); Response> cfInsertNx(String key, CFInsertParams insertParams, String... items); Response cfExists(String key, String item); Response> cfMExists(String key, String... items); Response cfDel(String key, String item); Response cfCount(String key, String item); Response> cfScanDump(String key, long iterator); Response cfLoadChunk(String key, long iterator, byte[] data); Response> cfInfo(String key); } ================================================ FILE: src/main/java/redis/clients/jedis/bloom/commands/RedisBloomCommands.java ================================================ package redis.clients.jedis.bloom.commands; public interface RedisBloomCommands extends BloomFilterCommands, CuckooFilterCommands, CountMinSketchCommands, TopKFilterCommands, TDigestSketchCommands { } ================================================ FILE: src/main/java/redis/clients/jedis/bloom/commands/RedisBloomPipelineCommands.java ================================================ package redis.clients.jedis.bloom.commands; public interface RedisBloomPipelineCommands extends BloomFilterPipelineCommands, CuckooFilterPipelineCommands, CountMinSketchPipelineCommands, TopKFilterPipelineCommands, TDigestSketchPipelineCommands { } ================================================ FILE: src/main/java/redis/clients/jedis/bloom/commands/TDigestSketchCommands.java ================================================ package redis.clients.jedis.bloom.commands; import java.util.List; import java.util.Map; import redis.clients.jedis.bloom.TDigestMergeParams; public interface TDigestSketchCommands { /** * {@code TDIGEST.CREATE key} * * @param key The name of the sketch (a t-digest data structure) * @return OK */ String tdigestCreate(String key); /** * {@code TDIGEST.CREATE key [compression]} * * @param key The name of the sketch (a t-digest data structure) * @param compression The compression parameter. 100 is a common value for normal uses. 1000 is extremely large. * @return OK */ String tdigestCreate(String key, int compression); /** * {@code TDIGEST.RESET key} * * @param key The name of the sketch (a t-digest data structure) * @return OK */ String tdigestReset(String key); /** * {@code TDIGEST.MERGE destination-key numkeys source-key [source-key ...]} * * @param destinationKey Sketch to copy observation values to (a t-digest data structure) * @param sourceKeys Sketch(es) to copy observation values from (a t-digest data structure) * @return OK */ String tdigestMerge(String destinationKey, String... sourceKeys); /** * {@code TDIGEST.MERGE destination-key numkeys source-key [source-key ...] * [COMPRESSION compression] [OVERRIDE]} * * @param mergeParams compression and override options * @param destinationKey Sketch to copy observation values to (a t-digest data structure) * @param sourceKeys Sketch(es) to copy observation values from (a t-digest data structure) * @return OK */ String tdigestMerge(TDigestMergeParams mergeParams, String destinationKey, String... sourceKeys); /** * {@code TDIGEST.INFO key} * * @param key The name of the sketch (a t-digest data structure) * @return information about the sketch */ Map tdigestInfo(String key); /** * {@code TDIGEST.ADD key value weight [ value weight ...]} * * @param key The name of the sketch (a t-digest data structure) * @param values The value of the observation (floating-point) * @return OK */ String tdigestAdd(String key, double... values); /** * {@code TDIGEST.CDF key value [value ...]} * * @param key The name of the sketch (a t-digest data structure) * @param values upper limit of observation value, for which the fraction of all observations added which are ≤ value * @return estimation of the fraction of all observations added which are ≤ value */ List tdigestCDF(String key, double... values); /** * {@code TDIGEST.QUANTILE key quantile [quantile ...]} * * @param key The name of the sketch (a t-digest data structure) * @param quantiles The desired fraction(s) (between 0 and 1 inclusively) * @return results */ List tdigestQuantile(String key, double... quantiles); /** * {@code TDIGEST.MIN key} * * @param key The name of the sketch (a t-digest data structure) * @return minimum observation value from the sketch */ double tdigestMin(String key); /** * {@code TDIGEST.MAX key} * * @param key The name of the sketch (a t-digest data structure) * @return maximum observation value from the sketch */ double tdigestMax(String key); /** * {@code TDIGEST.TRIMMED_MEAN key low_cut_quantile high_cut_quantile} * * @param key The name of the sketch (a t-digest data structure) * @param lowCutQuantile Exclude observation values lower than this quantile * @param highCutQuantile Exclude observation values higher than this quantile * @return estimation of the mean value */ double tdigestTrimmedMean(String key, double lowCutQuantile, double highCutQuantile); List tdigestRank(String key, double... values); List tdigestRevRank(String key, double... values); List tdigestByRank(String key, long... ranks); List tdigestByRevRank(String key, long... ranks); } ================================================ FILE: src/main/java/redis/clients/jedis/bloom/commands/TDigestSketchPipelineCommands.java ================================================ package redis.clients.jedis.bloom.commands; import java.util.List; import java.util.Map; import redis.clients.jedis.Response; import redis.clients.jedis.bloom.TDigestMergeParams; public interface TDigestSketchPipelineCommands { Response tdigestCreate(String key); Response tdigestCreate(String key, int compression); Response tdigestReset(String key); Response tdigestMerge(String destinationKey, String... sourceKeys); Response tdigestMerge(TDigestMergeParams mergeParams, String destinationKey, String... sourceKeys); Response> tdigestInfo(String key); Response tdigestAdd(String key, double... values); Response> tdigestCDF(String key, double... values); Response> tdigestQuantile(String key, double... quantiles); Response tdigestMin(String key); Response tdigestMax(String key); Response tdigestTrimmedMean(String key, double lowCutQuantile, double highCutQuantile); Response> tdigestRank(String key, double... values); Response> tdigestRevRank(String key, double... values); Response> tdigestByRank(String key, long... ranks); Response> tdigestByRevRank(String key, long... ranks); } ================================================ FILE: src/main/java/redis/clients/jedis/bloom/commands/TopKFilterCommands.java ================================================ package redis.clients.jedis.bloom.commands; import java.util.Collections; import java.util.List; import java.util.Map; public interface TopKFilterCommands { /** * {@code TOPK.RESERVE {key} {topk}} * * @param key * @param topk * @return OK */ String topkReserve(String key, long topk); /** * {@code TOPK.RESERVE {key} {topk} [{width} {depth} {decay}]} * * @param key * @param topk * @param width * @param depth * @param decay * @return OK */ String topkReserve(String key, long topk, long width, long depth, double decay); /** * {@code TOPK.ADD {key} {item ...}} * * @param key * @param items * @return items dropped from list */ List topkAdd(String key, String... items); /** * {@code TOPK.INCRBY {key} {item} {increment}} * * @param key * @param item * @param increment * @return item dropped from list */ default String topkIncrBy(String key, String item, long increment) { return topkIncrBy(key, Collections.singletonMap(item, increment)).get(0); } /** * {@code TOPK.INCRBY {key} {item} {increment} [{item} {increment} ...]} * * @param key * @param itemIncrements item and increment pairs * @return item dropped from list */ List topkIncrBy(String key, Map itemIncrements); /** * {@code TOPK.QUERY {key} {item ...}} * * @param key * @param items * @return if item is in Top-K */ List topkQuery(String key, String... items); /** * {@code TOPK.LIST {key}} * * @param key * @return k (or less) items in Top K list */ List topkList(String key); /** * {@code TOPK.LIST {key} WITHCOUNT} * * @param key * @return k (or less) items in Top K list */ Map topkListWithCount(String key); /** * {@code TOPK.INFO {key}} * * @param key * @return information */ Map topkInfo(String key); } ================================================ FILE: src/main/java/redis/clients/jedis/bloom/commands/TopKFilterPipelineCommands.java ================================================ package redis.clients.jedis.bloom.commands; import java.util.List; import java.util.Map; import redis.clients.jedis.Response; public interface TopKFilterPipelineCommands { Response topkReserve(String key, long topk); Response topkReserve(String key, long topk, long width, long depth, double decay); Response> topkAdd(String key, String... items); Response> topkIncrBy(String key, Map itemIncrements); Response> topkQuery(String key, String... items); Response> topkList(String key); Response> topkListWithCount(String key); Response> topkInfo(String key); } ================================================ FILE: src/main/java/redis/clients/jedis/bloom/commands/package-info.java ================================================ /** * This package contains the interfaces that contain methods representing RedisBloom commands. */ package redis.clients.jedis.bloom.commands; ================================================ FILE: src/main/java/redis/clients/jedis/bloom/package-info.java ================================================ /** * This package contains the classes related to RedisBloom module. */ package redis.clients.jedis.bloom; ================================================ FILE: src/main/java/redis/clients/jedis/builders/AbstractClientBuilder.java ================================================ package redis.clients.jedis.builders; import org.apache.commons.pool2.impl.GenericObjectPoolConfig; import redis.clients.jedis.*; import redis.clients.jedis.csc.Cache; import redis.clients.jedis.csc.CacheConfig; import redis.clients.jedis.csc.CacheFactory; import redis.clients.jedis.executors.CommandExecutor; import redis.clients.jedis.executors.DefaultCommandExecutor; import redis.clients.jedis.json.JsonObjectMapper; import redis.clients.jedis.providers.ConnectionProvider; import redis.clients.jedis.search.SearchProtocol; /** * Abstract base class for Redis client builders that provides common configuration options. *

* This class contains shared configuration fields and methods that are common across different * Redis client builders (RedisClient.Builder, RedisSentinelClient.Builder, etc.). It helps * eliminate code duplication and provides a consistent API for common features. *

* Common features provided: *

    *
  • Connection pool configuration
  • *
  • Client-side caching
  • *
  • Custom connection providers
  • *
  • Key preprocessing
  • *
  • JSON object mapping
  • *
  • Search dialect configuration
  • *
* @param the concrete builder type for method chaining * @param the client type that this builder creates */ public abstract class AbstractClientBuilder, C> { // Common configuration fields protected GenericObjectPoolConfig poolConfig = new ConnectionPoolConfig(); protected Cache cache = null; protected CacheConfig cacheConfig = null; protected CommandExecutor commandExecutor = null; protected CommandObjects commandObjects = null; protected ConnectionProvider connectionProvider = null; protected CommandKeyArgumentPreProcessor keyPreProcessor = null; protected JsonObjectMapper jsonObjectMapper = null; protected int searchDialect = SearchProtocol.DEFAULT_DIALECT; protected JedisClientConfig clientConfig = null; /** * Sets the client configuration for Redis connections. *

* The client configuration includes authentication, timeouts, SSL settings, and other * connection-specific parameters. * @param clientConfig the client configuration * @return this builder */ public T clientConfig(JedisClientConfig clientConfig) { this.clientConfig = clientConfig; return self(); } /** * Returns the concrete builder instance for method chaining. This method must be implemented by * subclasses to return their own type. * @return the concrete builder instance */ protected abstract T self(); /** * Creates a default connection provider based on the current configuration. * @return ConnectionProvider */ protected abstract ConnectionProvider createDefaultConnectionProvider(); /** * Creates a default command executor based on the current configuration. * @return CommandExecutor */ protected CommandExecutor createDefaultCommandExecutor() { return new DefaultCommandExecutor(this.connectionProvider); } /** * Creates a default client configuration based on the current configuration. * @return JedisClientConfig */ protected JedisClientConfig createDefaultClientConfig() { return DefaultJedisClientConfig.builder().build(); } /** * Factory method for creating CommandObjects. Subclasses may override to provide specialized * CommandObjects implementations (e.g., ClusterCommandObjects). */ protected CommandObjects createDefaultCommandObjects() { return new CommandObjects(); } /** * Creates the specific client instance with the provided components. *

* This method is called by the generic build() method to instantiate the concrete client type. * Each builder implementation should create their specific client type (JedisPooled, * JedisCluster, etc.) using the parameters provided to the builder. * @return the configured Redis client instance */ protected abstract C createClient(); /** * Validates the builder-specific configuration. *

* This method is called by the generic build() method to validate configuration specific to each * builder type. Implementations should call validateCommonConfiguration() and then perform their * own specific validation. * @throws IllegalArgumentException if the configuration is invalid */ protected abstract void validateSpecificConfiguration(); /** * Builds the Redis client instance using the common build pattern. *

* This method implements the common build pattern shared across all builder types: *

    *
  1. Validates configuration (both common and builder-specific)
  2. *
  3. Creates cache from cacheConfig if provided
  4. *
  5. Creates default connection provider if not already set
  6. *
  7. Creates default command executor if not already set
  8. *
  9. Applies common configuration to command objects
  10. *
  11. Creates and returns the specific client instance
  12. *
* @return the configured Redis client instance */ public C build() { // Validate configuration validateSpecificConfiguration(); // Create cache from config if provided if (this.cacheConfig != null) { this.cache = CacheFactory.getCache(this.cacheConfig); } if (this.clientConfig == null) { this.clientConfig = createDefaultClientConfig(); } // Create default connection provider if not set if (this.connectionProvider == null) { this.connectionProvider = createDefaultConnectionProvider(); } // Create default command executor if not set if (this.commandExecutor == null) { this.commandExecutor = createDefaultCommandExecutor(); } // Ensure CommandObjects are created (and allow subclasses to override the type) if (this.commandObjects == null) { this.commandObjects = createDefaultCommandObjects(); } // Apply common configuration this.applyCommandObjectsConfiguration(commandObjects); // Create and return the specific client instance return createClient(); } /** * Sets the connection pool configuration. *

* The pool configuration controls how connections are managed, including maximum number of * connections, idle timeout, and other pooling parameters. * @param poolConfig the pool configuration * @return this builder */ public T poolConfig(GenericObjectPoolConfig poolConfig) { this.poolConfig = poolConfig; return self(); } /** * Sets the client-side cache for caching Redis responses. *

* Client-side caching can improve performance by storing frequently accessed data locally, * reducing the number of round trips to the Redis server. * @param cache the cache instance * @return this builder */ public T cache(Cache cache) { this.cache = cache; return self(); } /** * Sets the cache configuration for client-side caching. *

* Client-side caching can improve performance by storing frequently accessed data locally. The * cache will be created from this configuration during the build process. * @param cacheConfig the cache configuration * @return this builder */ public T cacheConfig(CacheConfig cacheConfig) { this.cacheConfig = cacheConfig; return self(); } /** * Sets a custom connection provider. *

* When a custom connection provider is set, other connection-related configuration may be ignored * as the provider is responsible for managing connections. The specific behavior depends on the * concrete builder implementation. * @param connectionProvider the connection provider * @return this builder */ public T connectionProvider(ConnectionProvider connectionProvider) { this.connectionProvider = connectionProvider; return self(); } /** * Sets a custom command executor for executing Redis commands. *

* The command executor is responsible for sending commands to Redis and processing the responses. * @param commandExecutor the command executor * @return this builder */ public T commandExecutor(CommandExecutor commandExecutor) { this.commandExecutor = commandExecutor; return self(); } /** * Sets a key preprocessor for transforming Redis keys before sending commands. *

* The key preprocessor allows you to modify keys before they are sent to Redis, for example to * add prefixes, apply transformations, or implement key routing logic. *

* Example usage: * *

   * {@code
   * CommandKeyArgumentPreProcessor keyProcessor = key -> "myapp:" + key;
   * builder.keyPreProcessor(keyProcessor);
   * }
   * 
* * @param keyPreProcessor the key preprocessor * @return this builder */ public T keyPreProcessor(CommandKeyArgumentPreProcessor keyPreProcessor) { this.keyPreProcessor = keyPreProcessor; return self(); } /** * Sets a custom JSON object mapper for JSON operations. *

* The JSON object mapper is used for serializing and deserializing objects in JSON commands * (RedisJSON module). If not set, a default Gson-based mapper will be used. *

* Example usage: * *

   * {
   *   @code
   *   JsonObjectMapper customMapper = new MyCustomJsonMapper();
   *   builder.jsonObjectMapper(customMapper);
   * }
   * 
* * @param jsonObjectMapper the JSON object mapper * @return this builder */ public T jsonObjectMapper(JsonObjectMapper jsonObjectMapper) { this.jsonObjectMapper = jsonObjectMapper; return self(); } /** * Sets the default search dialect for RediSearch operations. *

* The search dialect determines the query syntax and features available for RediSearch commands. * Different dialects support different query features and syntax variations. *

* Default is {@value redis.clients.jedis.search.SearchProtocol#DEFAULT_DIALECT}. * @param searchDialect the search dialect version * @return this builder * @throws IllegalArgumentException if dialect is 0 (not allowed) */ public T searchDialect(int searchDialect) { if (searchDialect == 0) { throw new IllegalArgumentException("DIALECT=0 cannot be set."); } this.searchDialect = searchDialect; return self(); } /** * Applies common configuration to the CommandObjects instance. *

* This method is called by concrete builders to configure the CommandObjects with the common * settings like key preprocessor, JSON mapper, and search dialect. * @param commandObjects the CommandObjects instance to configure */ public void applyCommandObjectsConfiguration(CommandObjects commandObjects) { if (keyPreProcessor != null) { commandObjects.setKeyArgumentPreProcessor(keyPreProcessor); } if (jsonObjectMapper != null) { commandObjects.setJsonObjectMapper(jsonObjectMapper); } if (searchDialect != SearchProtocol.DEFAULT_DIALECT) { commandObjects.setDefaultSearchDialect(searchDialect); } } /** * Validates common configuration parameters. *

* This method can be called by concrete builders to validate the common configuration before * building the client. * @throws IllegalArgumentException if any common configuration is invalid */ protected void validateCommonConfiguration() { if (cache != null || cacheConfig != null) { if (clientConfig != null && clientConfig.getRedisProtocol() != RedisProtocol.RESP3) { throw new IllegalArgumentException("Client-side caching is only supported with RESP3."); } } } } ================================================ FILE: src/main/java/redis/clients/jedis/builders/ClusterClientBuilder.java ================================================ package redis.clients.jedis.builders; import java.time.Duration; import java.util.Set; import redis.clients.jedis.*; import redis.clients.jedis.executors.ClusterCommandExecutor; import redis.clients.jedis.executors.CommandExecutor; import redis.clients.jedis.providers.ClusterConnectionProvider; import redis.clients.jedis.providers.ConnectionProvider; /** * Builder for creating JedisCluster instances (Redis Cluster connections). *

* This builder provides methods specific to Redis Cluster deployments, including cluster nodes * configuration, retry settings, and topology refresh configuration. *

*/ public abstract class ClusterClientBuilder extends AbstractClientBuilder, C> { // Cluster-specific configuration fields private Set nodes = null; private int maxAttempts = JedisCluster.DEFAULT_MAX_ATTEMPTS; private Duration maxTotalRetriesDuration; private Duration topologyRefreshPeriod = null; private CommandFlagsRegistry commandFlags = null; /** * Sets the cluster nodes to connect to. *

* At least one node must be specified. The client will discover other nodes in the cluster * automatically. * @param nodes the set of cluster nodes * @return this builder */ public ClusterClientBuilder nodes(Set nodes) { this.nodes = nodes; return this; } /** * Sets the maximum number of attempts for cluster operations. *

* When a cluster operation fails (e.g., due to node failure or slot migration), the client will * retry up to this many times before giving up. * @param maxAttempts the maximum number of attempts (must be positive) * @return this builder */ public ClusterClientBuilder maxAttempts(int maxAttempts) { this.maxAttempts = maxAttempts; return this; } /** * Sets the maximum total duration for retries across all attempts. *

* This provides a time-based limit on retries in addition to the attempt-based limit. If not set, * it will be calculated as socketTimeout * maxAttempts. * @param maxTotalRetriesDuration the maximum total retry duration * @return this builder */ public ClusterClientBuilder maxTotalRetriesDuration(Duration maxTotalRetriesDuration) { this.maxTotalRetriesDuration = maxTotalRetriesDuration; return this; } /** * Sets the topology refresh period for cluster slot mapping updates. *

* The client will periodically refresh its view of the cluster topology to handle slot migrations * and node changes. A shorter period provides faster adaptation to cluster changes but increases * overhead. * @param topologyRefreshPeriod the topology refresh period * @return this builder */ public ClusterClientBuilder topologyRefreshPeriod(Duration topologyRefreshPeriod) { this.topologyRefreshPeriod = topologyRefreshPeriod; return this; } /** * Overrides the default command flags registry. * @param commandFlags custom command flags registry * @return this builder */ public ClusterClientBuilder commandFlags(CommandFlagsRegistry commandFlags) { this.commandFlags = commandFlags; return this; } /** * Gets the command flags registry, initializing it if necessary. * @return the command flags registry */ protected CommandFlagsRegistry getCommandFlags() { if (this.commandFlags == null) { this.commandFlags = createDefaultCommandFlagsRegistry(); } return this.commandFlags; } @Override protected ClusterClientBuilder self() { return this; } @Override protected ConnectionProvider createDefaultConnectionProvider() { return new ClusterConnectionProvider(this.nodes, this.clientConfig, this.cache, this.poolConfig, this.topologyRefreshPeriod); } /** * Creates a default command flags registry based on the current configuration. * @return CommandFlagsRegistry */ protected CommandFlagsRegistry createDefaultCommandFlagsRegistry() { return StaticCommandFlagsRegistry.registry(); } @Override protected CommandExecutor createDefaultCommandExecutor() { if (this.commandFlags == null) { this.commandFlags = createDefaultCommandFlagsRegistry(); } Duration effectiveMaxTotalRetriesDuration = (this.maxTotalRetriesDuration == null) ? Duration.ofMillis((long) this.clientConfig.getSocketTimeoutMillis() * this.maxAttempts) : this.maxTotalRetriesDuration; return new ClusterCommandExecutor((ClusterConnectionProvider) this.connectionProvider, this.maxAttempts, effectiveMaxTotalRetriesDuration, this.commandFlags); } @Override protected CommandObjects createDefaultCommandObjects() { return new ClusterCommandObjects(); } @Override protected void validateSpecificConfiguration() { validateCommonConfiguration(); if (nodes == null || nodes.isEmpty()) { throw new IllegalArgumentException( "At least one cluster node must be specified for cluster mode"); } if (maxAttempts <= 0) { throw new IllegalArgumentException("Max attempts must be positive for cluster mode"); } if (maxTotalRetriesDuration != null && maxTotalRetriesDuration.isNegative()) { throw new IllegalArgumentException( "Max total retries duration cannot be negative for cluster mode"); } if (topologyRefreshPeriod != null && topologyRefreshPeriod.isNegative()) { throw new IllegalArgumentException( "Topology refresh period cannot be negative for cluster mode"); } } } ================================================ FILE: src/main/java/redis/clients/jedis/builders/MultiDbClientBuilder.java ================================================ package redis.clients.jedis.builders; import java.util.function.Consumer; import redis.clients.jedis.MultiDbConfig; import redis.clients.jedis.annots.Experimental; import redis.clients.jedis.executors.CommandExecutor; import redis.clients.jedis.mcf.MultiDbCommandExecutor; import redis.clients.jedis.mcf.DatabaseSwitchEvent; import redis.clients.jedis.mcf.MultiDbConnectionProvider; import redis.clients.jedis.providers.ConnectionProvider; /** * Builder for creating multi-db Redis clients with multi-endpoint support. *

* This builder provides methods specific to multi-db Redis deployments, including multiple weighted * endpoints, circuit breaker configuration, health checks, and automatic failover/failback * capabilities. *

*

* Key Features: *

*
    *
  • Multi-Endpoint Configuration: Add multiple Redis endpoints with individual * weights
  • *
  • Circuit Breaker Integration: Built-in circuit breaker with configurable * thresholds
  • *
  • Health Monitoring: Automatic health checks with configurable strategies
  • *
  • Event Handling: Listen to database switch events for monitoring and * alerting
  • *
  • Flexible Configuration: Support for both simple and advanced multi-database * configurations
  • *
*

* Usage Examples: *

* *
 * MultiDbClient client = MultiDbClient.builder()
 *                 .multiDbConfig(
 *                         MultiDbConfig.builder()
 *                                 .database(
 *                                         DatabaseConfig.builder(
 *                                                         east,
 *                                                         DefaultJedisClientConfig.builder().credentials(credentialsEast).build())
 *                                                 .weight(100.0f)
 *                                                 .build())
 *                                 .database(DatabaseConfig.builder(
 *                                                 west,
 *                                                 DefaultJedisClientConfig.builder().credentials(credentialsWest).build())
 *                                         .weight(50.0f).build())
 *                                 .failureDetector(MultiDbConfig.CircuitBreakerConfig.builder()
 *                                         .failureRateThreshold(50.0f)
 *                                         .build())
 *                                 .commandRetry(MultiDbConfig.RetryConfig.builder()
 *                                         .maxAttempts(3)
 *                                         .build())
 *                                 .build()
 *                 )
 *                 .databaseSwitchListener(event ->
 *                     System.out.println("Switched to: " + event.getEndpoint()))
 *                 .build();
 * 
* * @param the client type that this builder creates * @author Ivo Gaydazhiev * @since 7.0.0 */ @Experimental public abstract class MultiDbClientBuilder extends AbstractClientBuilder, C> { // Multi-db specific configuration fields private MultiDbConfig multiDbConfig = null; private Consumer databaseSwitchListener = null; /** * Sets the multi-database configuration. *

* This configuration controls circuit breaker behavior, retry logic, health checks, failback * settings, and other resilience features. If not provided, default configuration will be used. *

* @param config the multi-database configuration * @return this builder */ public MultiDbClientBuilder multiDbConfig(MultiDbConfig config) { this.multiDbConfig = config; return this; } /** * Sets a listener for database switch events. *

* The listener will be called whenever the client switches from one endpoint to another, * providing information about the switch reason and the new active endpoint. This is useful for * monitoring, alerting, and logging purposes. *

* @param listener the database switch event listener * @return this builder */ public MultiDbClientBuilder databaseSwitchListener(Consumer listener) { this.databaseSwitchListener = listener; return this; } @Override protected MultiDbClientBuilder self() { return this; } @Override protected ConnectionProvider createDefaultConnectionProvider() { if (this.multiDbConfig == null || this.multiDbConfig.getDatabaseConfigs() == null || this.multiDbConfig.getDatabaseConfigs().length < 1) { throw new IllegalArgumentException("At least one endpoint must be specified"); } // Create the multi-database connection provider MultiDbConnectionProvider provider = new MultiDbConnectionProvider(multiDbConfig); // Set database switch listener if provided if (this.databaseSwitchListener != null) { provider.setDatabaseSwitchListener(this.databaseSwitchListener); } return provider; } @Override protected CommandExecutor createDefaultCommandExecutor() { // For multi-db clients, we always use MultiDbCommandExecutor return new MultiDbCommandExecutor((MultiDbConnectionProvider) this.connectionProvider); } @Override protected void validateSpecificConfiguration() { } } ================================================ FILE: src/main/java/redis/clients/jedis/builders/SentinelClientBuilder.java ================================================ package redis.clients.jedis.builders; import java.time.Duration; import java.util.Set; import redis.clients.jedis.*; import redis.clients.jedis.providers.ConnectionProvider; import redis.clients.jedis.providers.SentineledConnectionProvider; import redis.clients.jedis.util.Delay; import redis.clients.jedis.util.JedisAsserts; /** * Builder for creating JedisSentineled instances (Redis Sentinel connections). *

* This builder provides methods specific to Redis Sentinel deployments, including master name * configuration, sentinel nodes configuration, and separate client configurations for master and * sentinel connections. *

*/ public abstract class SentinelClientBuilder extends AbstractClientBuilder, C> { // Sentinel-specific configuration fields private String masterName = null; private Set sentinels = null; private JedisClientConfig sentinelClientConfig = null; // delay between re-subscribing to sentinel nodes after a disconnection private Delay sentinelReconnectDelay = SentineledConnectionProvider.DEFAULT_RESUBSCRIBE_DELAY; /** * Sets the master name for the Redis Sentinel configuration. *

* This is the name of the Redis master as configured in the Sentinel instances. The Sentinel will * monitor this master and provide failover capabilities. * @param masterName the master name (must not be null or empty) * @return this builder */ public SentinelClientBuilder masterName(String masterName) { this.masterName = masterName; return this; } /** * Sets the sentinel nodes to connect to. *

* At least one sentinel must be specified. The client will use these sentinels to discover the * current master and monitor for failover events. * @param sentinels the set of sentinel nodes * @return this builder */ public SentinelClientBuilder sentinels(Set sentinels) { this.sentinels = sentinels; return this; } /** * Sets the client configuration for Sentinel connections. *

* This configuration is used for connections to the Sentinel instances. It may have different * authentication credentials and settings than the master connections. * @param sentinelClientConfig the client configuration for sentinel connections * @return this builder */ public SentinelClientBuilder sentinelClientConfig(JedisClientConfig sentinelClientConfig) { this.sentinelClientConfig = sentinelClientConfig; return this; } /** * Sets the delay between re-subscribing to sentinel node after a disconnection. *

* In case connection to sentinel nodes is lost, the client will try to reconnect to them. This * method sets the delay between re-subscribing to sentinel nodes after a disconnection. *

* @param reconnectDelay the delay between re-subscribing to sentinel nodes after a disconnection * @return this builder */ public SentinelClientBuilder sentinelReconnectDelay(Delay reconnectDelay) { JedisAsserts.notNull(reconnectDelay, "reconnectDelay must not be null"); this.sentinelReconnectDelay = reconnectDelay; return this; } @Override protected SentinelClientBuilder self() { return this; } @Override protected ConnectionProvider createDefaultConnectionProvider() { return new SentineledConnectionProvider(this.masterName, this.clientConfig, this.cache, this.poolConfig, this.sentinels, this.sentinelClientConfig, sentinelReconnectDelay); } @Override protected void validateSpecificConfiguration() { validateCommonConfiguration(); if (masterName == null || masterName.trim().isEmpty()) { throw new IllegalArgumentException("Master name is required for Sentinel mode"); } if (sentinels == null || sentinels.isEmpty()) { throw new IllegalArgumentException( "At least one sentinel must be specified for Sentinel mode"); } } @Override public C build() { if (sentinelClientConfig == null) { sentinelClientConfig = DefaultJedisClientConfig.builder().build(); } return super.build(); } } ================================================ FILE: src/main/java/redis/clients/jedis/builders/StandaloneClientBuilder.java ================================================ package redis.clients.jedis.builders; import java.net.URI; import redis.clients.jedis.*; import redis.clients.jedis.providers.ConnectionProvider; import redis.clients.jedis.providers.PooledConnectionProvider; import redis.clients.jedis.util.JedisAsserts; import redis.clients.jedis.util.JedisURIHelper; /** * Builder for creating RedisClient instances (standalone Redis connections). *

* This builder provides methods specific to standalone Redis deployments, including host/port * configuration, URI-based configuration, and client configuration options. *

*/ public abstract class StandaloneClientBuilder extends AbstractClientBuilder, C> { // Standalone-specific configuration fields private HostAndPort hostAndPort = new HostAndPort(Protocol.DEFAULT_HOST, Protocol.DEFAULT_PORT); /** * Sets the Redis server host and port. * @param host the Redis server hostname * @param port the Redis server port * @return this builder */ public StandaloneClientBuilder hostAndPort(String host, int port) { this.hostAndPort = new HostAndPort(host, port); return this; } /** * Sets the Redis server host and port. * @param hostAndPort the Redis server host and port * @return this builder */ public StandaloneClientBuilder hostAndPort(HostAndPort hostAndPort) { this.hostAndPort = hostAndPort; return this; } @Override protected StandaloneClientBuilder self() { return this; } @Override protected ConnectionProvider createDefaultConnectionProvider() { return new PooledConnectionProvider(this.hostAndPort, this.clientConfig, this.cache, this.poolConfig); } @Override protected void validateSpecificConfiguration() { validateCommonConfiguration(); if (hostAndPort == null) { throw new IllegalArgumentException("Either URI or host/port must be specified"); } } /** * Sets the Redis server URI from a string. *

* This method extracts connection parameters from the URI and merges them into the current client * configuration. If a client configuration was previously set via * {@link #clientConfig(JedisClientConfig)}, only the values explicitly provided in the URI will * override the existing configuration. Values not present in the URI will be preserved from the * existing configuration. *

* This method sets: *

    *
  • Host and port from the URI (always set)
  • *
  • Client configuration with URI-derived values (merged with existing config if present)
  • *
*

* URI values override existing config values when: *

    *
  • URI contains user/password - overrides existing credentials
  • *
  • URI contains database - overrides existing database
  • *
  • URI contains protocol parameter - overrides existing protocol
  • *
  • URI uses rediss:// scheme - enables SSL
  • *
*

* Examples: * *

   * // Credentials from config are preserved (URI has no credentials)
   * builder.clientConfig(configWithCredentials).fromURI("redis://localhost:6379")
   *
   * // URI credentials override config credentials
   * builder.clientConfig(configWithCredentials).fromURI("redis://user:pass@localhost:6379")
   *
   * // Config completely overrides URI (last wins)
   * builder.fromURI("redis://user:pass@localhost:6379").clientConfig(newConfig)
   * 
* * @param uriString the Redis server URI string * @return this builder * @deprecated Use {@link #hostAndPort(String, int)} combined with * {@link #clientConfig(JedisClientConfig)} for explicit configuration. This method * will be removed in Jedis 8.0.0. *

* Migration example: * *

   *             // Old (deprecated):
   *             builder.fromURI("redis://user:pass@localhost:6379/2")
   *
   *             // New (recommended):
   *             builder.hostAndPort("localhost", 6379)
   *                    .clientConfig(DefaultJedisClientConfig.builder()
   *                        .user("user")
   *                        .password("pass")
   *                        .database(2)
   *                        .build())
   *             
*/ @Deprecated public StandaloneClientBuilder fromURI(String uriString) { return fromURI(URI.create(uriString)); } /** * Sets the Redis server URI. *

* This method extracts connection parameters from the URI and merges them into the current client * configuration. If a client configuration was previously set via * {@link #clientConfig(JedisClientConfig)}, only the values explicitly provided in the URI will * override the existing configuration. Values not present in the URI will be preserved from the * existing configuration. *

* This method sets: *

    *
  • Host and port from the URI (always set)
  • *
  • Client configuration with URI-derived values (merged with existing config if present)
  • *
*

* URI values override existing config values when: *

    *
  • URI contains user/password - overrides existing credentials
  • *
  • URI contains database - overrides existing database
  • *
  • URI contains protocol parameter - overrides existing protocol
  • *
  • URI uses rediss:// scheme - enables SSL
  • *
*

* Examples: * *

   * // Credentials from config are preserved (URI has no credentials)
   * builder.clientConfig(configWithCredentials).fromURI(uri)
   *
   * // URI credentials override config credentials
   * builder.clientConfig(configWithCredentials).fromURI(uriWithCredentials)
   *
   * // Config completely overrides URI (last wins)
   * builder.fromURI(uriWithCredentials).clientConfig(newConfig)
   * 
* * @param uri the Redis server URI * @return this builder * @deprecated Use {@link #hostAndPort(HostAndPort)} combined with * {@link #clientConfig(JedisClientConfig)} for explicit configuration. This method * will be removed in Jedis 8.0.0. See {@link #fromURI(String)} for migration example. */ @Deprecated public StandaloneClientBuilder fromURI(URI uri) { JedisAsserts.notNull(uri, "Redis URI must not be null"); JedisAsserts.isTrue(JedisURIHelper.isValid(uri), "Invalid Redis URI"); // Start with existing config if present, otherwise create new builder DefaultJedisClientConfig.Builder configBuilder = (this.clientConfig != null) ? DefaultJedisClientConfig.builder().from(this.clientConfig) : DefaultJedisClientConfig.builder(); // Override with URI values only if URI provides them (non-null) String uriUser = JedisURIHelper.getUser(uri); String uriPassword = JedisURIHelper.getPassword(uri); // If URI provides credentials, we need to clear the credentialsProvider // so that the new user/password values are used instead if (uriUser != null || uriPassword != null) { configBuilder.credentials(new DefaultRedisCredentials(uriUser, uriPassword)); } if (JedisURIHelper.hasDbIndex(uri)) { configBuilder.database(JedisURIHelper.getDBIndex(uri)); } RedisProtocol uriProtocol = JedisURIHelper.getRedisProtocol(uri); if (uriProtocol != null) { configBuilder.protocol(uriProtocol); } if (JedisURIHelper.isRedisSSLScheme(uri)) { configBuilder.ssl(true); } else if (JedisURIHelper.isRedisScheme(uri)) { configBuilder.ssl(false); } this.clientConfig = configBuilder.build(); return hostAndPort(JedisURIHelper.getHostAndPort(uri)); } } ================================================ FILE: src/main/java/redis/clients/jedis/commands/AccessControlLogBinaryCommands.java ================================================ package redis.clients.jedis.commands; import java.util.List; import redis.clients.jedis.CommandArguments; import redis.clients.jedis.resps.AccessControlUser; /** * This class provides the interfaces necessary to interact with * Access Control Lists (ACLs) within redis. These are the interfaces * for binary (i.e. non-decoded) interactions. * * @see Redis ACL Guide */ public interface AccessControlLogBinaryCommands { /** * Returns the username used to authenticate the current connection. * * @see ACL WHOAMI * @return The username used for the current connection */ byte[] aclWhoAmIBinary(); /** * Generate a random password * * @see ACL GENPASS * @return A random password */ byte[] aclGenPassBinary(); /** * Generate a random password * * @param bits the number of output bits * @return A random password */ byte[] aclGenPassBinary(int bits); /** * Returns the currently active ACL rules on the Redis Server * * @see ACL LIST * @return An array of ACL rules */ List aclListBinary(); /** * Shows a list of all usernames currently configured with access control * lists (ACL). * * @see ACL USERS * @return list of users */ List aclUsersBinary(); /** * The command returns all the rules defined for an existing ACL user. * @param name username * @return a list of ACL rule definitions for the user. */ AccessControlUser aclGetUser(byte[] name); /** * Create an ACL for the specified user with the default rules. * * @param name user who receives an acl * @see ACL SETUSER * @return A string containing OK on success */ String aclSetUser(byte[] name); /** * Create an ACL for the specified user, while specifying the rules. * * @param name user who receives an acl * @param rules the acl rules for the specified user * @see ACL SETUSER * @return A string containing OK on success */ String aclSetUser(byte[] name, byte[]... rules); /** * Delete the specified user, from the ACL. * * @param names The username to delete * @see ACL DELUSER * @return The number of users delete */ long aclDelUser(byte[]... names); /** * Show the available ACL categories. * * @see ACL CAT * @return the available ACL categories */ List aclCatBinary(); /** * Show the available ACLs for a given category. * * @param category The category for which to list available ACLs * @see ACL CAT * @return the available ACL categories */ List aclCat(byte[] category); /** * Shows the recent ACL security events. * * @see ACL LOG * @return The list of recent security events */ List aclLogBinary(); /** * Shows the recent limit ACL security events. * * @param limit The number of results to return * @see ACL LOG * @return The list of recent security events */ List aclLogBinary(int limit); /** * Reset the script event log * * @see ACL LOG * @return The OK string */ String aclLogReset(); /** * This function tells Redis to reload its external ACL rules, * when Redis is configured with an external ACL file * * @see ACL LOAD * @return OK or error text */ String aclLoad(); /** * Save the currently defined in-memory ACL to disk. * * @see ACL SAVE * @return OK on success */ String aclSave(); byte[] aclDryRunBinary(byte[] username, byte[] command, byte[]... args); byte[] aclDryRunBinary(byte[] username, CommandArguments commandArgs); } ================================================ FILE: src/main/java/redis/clients/jedis/commands/AccessControlLogCommands.java ================================================ package redis.clients.jedis.commands; import java.util.List; import redis.clients.jedis.CommandArguments; import redis.clients.jedis.resps.AccessControlLogEntry; import redis.clients.jedis.resps.AccessControlUser; /** * This class provides the interfaces necessary to interact with * Access Control Lists (ACLs) within redis. These are the interfaces * for string-based (i.e. decoded) interactions. * * * @see Redis ACL Guide */ public interface AccessControlLogCommands { /** * Returns the username used to authenticate the current connection. * * @see ACL WHOAMI * @return The username used for the current connection */ String aclWhoAmI(); /** * Generate a random password * * @return A random password */ String aclGenPass(); /** * Generate a random password * * @param bits the number of output bits * @return A random password */ String aclGenPass(int bits); /** * Returns the currently active ACL rules on the Redis Server * * @see ACL LIST * @return An array of ACL rules */ List aclList(); /** * Shows a list of all usernames currently configured with access control * lists (ACL). * * @see ACL USERS * @return list of users */ List aclUsers(); /** * The command returns all the rules defined for an existing ACL user. * @param name username * @return a list of ACL rule definitions for the user. */ AccessControlUser aclGetUser(String name); /** * Create an ACL for the specified user with the default rules. * * @param name user who receives an acl * @see ACL SETUSER * @return A string containing OK on success */ String aclSetUser(String name); /** * Create an ACL for the specified user, while specifying the rules. * * @param name user who receives an acl * @param rules the acl rules for the specified user * @see ACL SETUSER * @return A string containing OK on success */ String aclSetUser(String name, String... rules); /** * Delete the specified user, from the ACL. * * @param names The usernames to delete * @see ACL DELUSER * @return The number of users delete */ long aclDelUser(String... names); /** * Show the available ACL categories. * * @see ACL CAT * @return the available ACL categories */ List aclCat(); /** * Show the available ACLs for a given category. * * @param category The category for which to list available ACLs * @see ACL CAT * @return the available ACL categories */ List aclCat(String category); /** * Shows the recent ACL security events. * * @see ACL LOG * @return The list of recent security events */ List aclLog(); /** * Shows the recent limit ACL security events. * * @param limit The number of results to return * @see ACL LOG * @return The list of recent security events */ List aclLog(int limit); /** * Reset the script event log * * @return The OK string */ String aclLogReset(); /** * This function tells Redis to reload its external ACL rules, * when Redis is configured with an external ACL file * * @see ACL LOAD * @return OK or error text */ String aclLoad(); /** * Save the currently defined in-memory ACL to disk. * * @see ACL SAVE * @return OK on success */ String aclSave(); String aclDryRun(String username, String command, String... args); String aclDryRun(String username, CommandArguments commandArgs); } ================================================ FILE: src/main/java/redis/clients/jedis/commands/BitBinaryCommands.java ================================================ package redis.clients.jedis.commands; import java.util.List; import redis.clients.jedis.args.BitCountOption; import redis.clients.jedis.args.BitOP; import redis.clients.jedis.params.BitPosParams; public interface BitBinaryCommands { boolean setbit(byte[] key, long offset, boolean value); boolean getbit(byte[] key, long offset); long bitcount(byte[] key); long bitcount(byte[] key, long start, long end); long bitcount(byte[] key, long start, long end, BitCountOption option); long bitpos(byte[] key, boolean value); long bitpos(byte[] key, boolean value, BitPosParams params); List bitfield(byte[] key, byte[]... arguments); List bitfieldReadonly(byte[] key, byte[]... arguments); /** * Bitop Command Perform a bitwise operation * between multiple keys and store the result in the destKey. * @param op can be AND, OR, XOR, NOT, DIFF, DIFF1, ANDOR and ONE * @param destKey * @param srcKeys * @return The size of the string stored in the destKey */ long bitop(BitOP op, byte[] destKey, byte[]... srcKeys); } ================================================ FILE: src/main/java/redis/clients/jedis/commands/BitCommands.java ================================================ package redis.clients.jedis.commands; import java.util.List; import redis.clients.jedis.args.BitCountOption; import redis.clients.jedis.args.BitOP; import redis.clients.jedis.params.BitPosParams; public interface BitCommands { /** * SetBit Command * Sets or clears the bit at offset in the string value stored at key. *

* Time complexity: O(1) * @param key * @param offset * @param value * @return The original bit value stored at offset */ boolean setbit(String key, long offset, boolean value); /** * GetBit Command * Returns the bit value at offset in the string value stored at key. *

* Time complexity: O(1) * @param key * @param offset * @return The bit value stored at offset */ boolean getbit(String key, long offset); /** * Bitcount Command * Count the number of set bits (population counting) in a string. * @param key * @return The number of bits set to 1 */ long bitcount(String key); /** * Bitcount Command * Count the number of set bits (population counting) in a string only in an interval start and end. *

* Like for the GETRANGE command start and end can contain negative values in order to index bytes * starting from the end of the string, where -1 is the last byte, -2 is the penultimate, and so forth. * @param key * @param start byte start index * @param end byte end index * @return The number of bits set to 1 */ long bitcount(String key, long start, long end); /** * @see StringCommands#bitcount(String, long, long) * @param key * @param start byte start index * @param end byte end index * @param option indicate BYTE or BIT * @return The number of bits set to 1 */ long bitcount(String key, long start, long end, BitCountOption option); /** * Bitpos Command * Return the position of the first bit set to 1 or 0 in a string. * @param key * @param value the bit value * @return The position of the first bit set to 1 or 0 according to the request */ long bitpos(String key, boolean value); /** * Bitpos Command * Return the position of the first bit set to 1 or 0 in a string. * @param key * @param value the bit value * @param params {@link BitPosParams} * @return The position of the first bit set to 1 or 0 according to the request */ long bitpos(String key, boolean value, BitPosParams params); /** * Bitfield Command * The command treats a Redis string as an array of bits, and is capable of addressing specific integer * fields of varying bit widths and arbitrary non (necessary) aligned offset. * @param key * @param arguments may be used with optional arguments * @return A List of results */ List bitfield(String key, String...arguments); /** * The readonly version of {@link StringCommands#bitfield(String, String...) BITFIELD} */ List bitfieldReadonly(String key, String...arguments); /** * Bitop Command * Perform a bitwise operation between multiple keys (containing string values) and store the result in the destKey. * @param op can be AND, OR, XOR, NOT, DIFF, DIFF1, ANDOR and ONE * @param destKey * @param srcKeys * @return The size of the string stored in the destKey */ long bitop(BitOP op, String destKey, String... srcKeys); } ================================================ FILE: src/main/java/redis/clients/jedis/commands/BitPipelineBinaryCommands.java ================================================ package redis.clients.jedis.commands; import java.util.List; import redis.clients.jedis.Response; import redis.clients.jedis.args.BitCountOption; import redis.clients.jedis.args.BitOP; import redis.clients.jedis.params.BitPosParams; public interface BitPipelineBinaryCommands { Response setbit(byte[] key, long offset, boolean value); Response getbit(byte[] key, long offset); Response bitcount(byte[] key); Response bitcount(byte[] key, long start, long end); Response bitcount(byte[] key, long start, long end, BitCountOption option); Response bitpos(byte[] key, boolean value); Response bitpos(byte[] key, boolean value, BitPosParams params); Response> bitfield(byte[] key, byte[]... arguments); Response> bitfieldReadonly(byte[] key, byte[]... arguments); Response bitop(BitOP op, byte[] destKey, byte[]... srcKeys); } ================================================ FILE: src/main/java/redis/clients/jedis/commands/BitPipelineCommands.java ================================================ package redis.clients.jedis.commands; import redis.clients.jedis.Response; import redis.clients.jedis.args.BitCountOption; import redis.clients.jedis.args.BitOP; import redis.clients.jedis.params.BitPosParams; import java.util.List; public interface BitPipelineCommands { Response setbit(String key, long offset, boolean value); Response getbit(String key, long offset); Response bitcount(String key); Response bitcount(String key, long start, long end); Response bitcount(String key, long start, long end, BitCountOption option); Response bitpos(String key, boolean value); Response bitpos(String key, boolean value, BitPosParams params); Response> bitfield(String key, String...arguments); Response> bitfieldReadonly(String key, String...arguments); Response bitop(BitOP op, String destKey, String... srcKeys); } ================================================ FILE: src/main/java/redis/clients/jedis/commands/ClientBinaryCommands.java ================================================ package redis.clients.jedis.commands; import redis.clients.jedis.args.ClientAttributeOption; import redis.clients.jedis.args.ClientPauseMode; import redis.clients.jedis.args.ClientType; import redis.clients.jedis.args.UnblockType; import redis.clients.jedis.params.ClientKillParams; /** * The interface contain all the commands about client. * The params is byte[] */ public interface ClientBinaryCommands { /** * Close a given client connection. * * @param ipPort The ip:port should match a line returned by the CLIENT LIST command (addr field). * @return close success return OK */ String clientKill(byte[] ipPort); /** * Close a given client connection. * * @param ip The ip should match a line returned by the CLIENT LIST command (addr field). * @param port The port should match a line returned by the CLIENT LIST command (addr field). * @return Close success return OK */ String clientKill(String ip, int port); /** * Close a given client connection. * * @param params connect info will be closed * @return Close success return OK */ long clientKill(ClientKillParams params); /** * Returns the name of the current connection as set by CLIENT SETNAME * * @return Current connect name */ byte[] clientGetnameBinary(); /** * Returns information and statistics about the client connections server * in a mostly human-readable format. * * @return All clients info connected to redis-server */ byte[] clientListBinary(); /** * Returns information and statistics about the client connections server * in a mostly human-readable format filter by client type. * * @return all clients info connected to redis-server */ byte[] clientListBinary(ClientType type); /** * Returns information and statistics about the client connections server * in a mostly human-readable format filter by client ids. * * @param clientIds Unique 64-bit client IDs * @return All clients info connected to redis-server */ byte[] clientListBinary(long... clientIds); /** * Returns information and statistics about the current client connection * in a mostly human-readable format. * * @return Information and statistics about the current client connection */ byte[] clientInfoBinary(); /** * client set info command * Since redis 7.2 * @param attr the attr option * @param value the value * @return OK or error */ String clientSetInfo(ClientAttributeOption attr, byte[] value); /** * Assigns a name to the current connection. * * @param name Current connection name * @return OK if the connection name was successfully set. */ String clientSetname(byte[] name); /** * Returns the ID of the current connection. * * @return The id of the client. */ long clientId(); /** * Unblock from a different connection, a client blocked in a * blocking operation, such as for instance BRPOP or XREAD or WAIT. * * @param clientId The id of the client * @return Specifically: * 1 if the client was unblocked successfully. * 0 if the client wasn't unblocked. */ long clientUnblock(long clientId); /** * Unblock from a different connection, a client blocked in a * blocking operation, such as for instance BRPOP or XREAD or WAIT. * * @param clientId The id of the client * @param unblockType TIMEOUT|ERROR * @return Specifically: * 1 if the client was unblocked successfully. * 0 if the client wasn't unblocked. */ long clientUnblock(long clientId, UnblockType unblockType); /** * A connections control command able to suspend all the * Redis clients for the specified amount of time (in milliseconds) * * @param timeout WRITE|ALL * @return The command returns OK or an error if the timeout is invalid. */ String clientPause(long timeout); /** * A connections control command able to suspend all the * Redis clients for the specified amount of time (in milliseconds) * * @param timeout Command timeout * @param mode WRITE|ALL * @return The command returns OK or an error if the timeout is invalid. */ String clientPause(long timeout, ClientPauseMode mode); /** * CLIENT UNPAUSE is used to resume command processing for all clients that were paused by CLIENT PAUSE. * @return OK */ String clientUnpause(); /** * Turn on the client eviction mode for the current connection. * * @return OK */ String clientNoEvictOn(); /** * Turn off the client eviction mode for the current connection. * * @return OK */ String clientNoEvictOff(); } ================================================ FILE: src/main/java/redis/clients/jedis/commands/ClientCommands.java ================================================ package redis.clients.jedis.commands; import redis.clients.jedis.args.ClientAttributeOption; import redis.clients.jedis.args.ClientPauseMode; import redis.clients.jedis.args.ClientType; import redis.clients.jedis.args.UnblockType; import redis.clients.jedis.params.ClientKillParams; import redis.clients.jedis.resps.TrackingInfo; /** * The interface contain all the commands about client. * The params is String encoded in uft-8 */ public interface ClientCommands { /** * Close a given client connection. * * @param ipPort The ip:port should match a line returned by the CLIENT LIST command (addr field). * @return Close success return OK */ String clientKill(String ipPort); /** * Close a given client connection. * * @param ip The ip should match a line returned by the CLIENT LIST command (addr field). * @param port The port should match a line returned by the CLIENT LIST command (addr field). * @return Close success return OK */ String clientKill(String ip, int port); /** * Close client connections based on certain selection parameters. * * @param params Parameters defining what client connections to close. * @return The number of client connections that were closed. */ long clientKill(ClientKillParams params); /** * Returns the name of the current connection as set by CLIENT SETNAME * * @return Current connect name */ String clientGetname(); /** * Returns information and statistics about the client connections server * in a mostly human-readable format. * * @return All clients info connected to redis-server */ String clientList(); /** * Returns information and statistics about the client connections server * in a mostly human-readable format filter by client type. * * @return All clients info connected to redis-server */ String clientList(ClientType type); /** * Returns information and statistics about the client connections server * in a mostly human-readable format filter by client ids. * * @param clientIds Unique 64-bit client IDs * @return All clients info connected to redis-server */ String clientList(long... clientIds); /** * Returns information and statistics about the current client connection * in a mostly human-readable format. * * @return Information and statistics about the current client connection */ String clientInfo(); /** * client set info command * Since redis 7.2 * @param attr the attr option * @param value the value * @return OK or error */ String clientSetInfo(ClientAttributeOption attr, String value); /** * Assigns a name to the current connection. * * @param name current connection name * @return OK if the connection name was successfully set. */ String clientSetname(String name); /** * Returns the ID of the current connection. * * @return The id of the client. */ long clientId(); /** * Unblock from a different connection, a client blocked in a * blocking operation, such as for instance BRPOP or XREAD or WAIT. * * @param clientId The id of the client * @return 1 if the client was unblocked successfully, 0 if the client wasn't unblocked. */ long clientUnblock(long clientId); /** * Unblock from a different connection, a client blocked in a * blocking operation, such as for instance BRPOP or XREAD or WAIT. * * @param clientId The id of the client * @param unblockType TIMEOUT|ERROR * @return 1 if the client was unblocked successfully, 0 if the client wasn't unblocked. */ long clientUnblock(long clientId, UnblockType unblockType); /** * A connections control command able to suspend all the * Redis clients for the specified amount of time (in milliseconds) * * @param timeout WRITE|ALL * @return The command returns OK or an error if the timeout is invalid. */ String clientPause(long timeout); /** * A connections control command able to suspend all the * Redis clients for the specified amount of time (in milliseconds) * * @param timeout Command timeout * @param mode WRITE|ALL * @return The command returns OK or an error if the timeout is invalid. */ String clientPause(long timeout, ClientPauseMode mode); /** * CLIENT UNPAUSE is used to resume command processing for all clients that were paused by CLIENT PAUSE. * @return OK */ String clientUnpause(); /** * Turn on the client eviction mode for the current connection. * * @return OK */ String clientNoEvictOn(); /** * Turn off the client eviction mode for the current connection. * * @return OK */ String clientNoEvictOff(); /** * Turn on CLIENT NO-TOUCH * @return OK */ String clientNoTouchOn(); /** * Turn off CLIENT NO-TOUCH * @return OK */ String clientNoTouchOff(); TrackingInfo clientTrackingInfo(); } ================================================ FILE: src/main/java/redis/clients/jedis/commands/ClusterCommands.java ================================================ package redis.clients.jedis.commands; import java.util.List; import java.util.Map; import redis.clients.jedis.args.ClusterResetType; import redis.clients.jedis.args.ClusterFailoverOption; import redis.clients.jedis.resps.ClusterShardInfo; public interface ClusterCommands { String asking(); String readonly(); String readwrite(); String clusterNodes(); String clusterMeet(String ip, int port); String clusterAddSlots(int... slots); String clusterDelSlots(int... slots); String clusterInfo(); List clusterGetKeysInSlot(int slot, int count); List clusterGetKeysInSlotBinary(int slot, int count); String clusterSetSlotNode(int slot, String nodeId); String clusterSetSlotMigrating(int slot, String nodeId); String clusterSetSlotImporting(int slot, String nodeId); String clusterSetSlotStable(int slot); String clusterForget(String nodeId); String clusterFlushSlots(); long clusterKeySlot(String key); long clusterCountFailureReports(String nodeId); long clusterCountKeysInSlot(int slot); String clusterSaveConfig(); /** * Set a specific config epoch in a fresh node. It only works when the nodes' table * of the node is empty or when the node current config epoch is zero. * @param configEpoch * @return OK */ String clusterSetConfigEpoch(long configEpoch); /** * Advance the cluster config epoch. * @return BUMPED if the epoch was incremented, or STILL if the node already has the * greatest config epoch in the cluster. */ String clusterBumpEpoch(); String clusterReplicate(String nodeId); /** * {@code CLUSTER SLAVES} command is deprecated since Redis 5. * * @deprecated Use {@link ClusterCommands#clusterReplicas(java.lang.String)}. */ @Deprecated List clusterSlaves(String nodeId); List clusterReplicas(String nodeId); String clusterFailover(); String clusterFailover(ClusterFailoverOption failoverOption); /** * {@code CLUSTER SLOTS} command is deprecated since Redis 7. * * @deprecated Use {@link ClusterCommands#clusterShards()}. */ @Deprecated List clusterSlots(); /** * {@code CLUSTER SHARDS} returns details about the shards of the cluster. * This command replaces the {@code CLUSTER SLOTS} command from Redis 7, * by providing a more efficient and extensible representation of the cluster. * * @return a list of shards, with each shard containing two objects, 'slots' and 'nodes'. * @see CLUSTER SHARDS */ List clusterShards(); String clusterReset(); /** * {@code resetType} can be null for default behavior. * * @param resetType * @return OK */ String clusterReset(ClusterResetType resetType); String clusterMyId(); String clusterMyShardId(); /** * return the information of all such peer links as an array, where each array element is a map that contains * attributes and their values for an individual link. * * @return the information of all such peer links as an array * @see CLUSTET LINKS */ List> clusterLinks(); /** * Takes a list of slot ranges (specified by start and end slots) to assign to the node * * @param ranges slots range * @return OK if the command was successful. Otherwise, an error is returned. */ String clusterAddSlotsRange(int... ranges); /** * Takes a list of slot ranges (specified by start and end slots) to remove to the node. * * @param ranges slots range * @return OK if the command was successful. Otherwise, an error is returned. */ String clusterDelSlotsRange(int... ranges); } ================================================ FILE: src/main/java/redis/clients/jedis/commands/CommandCommands.java ================================================ package redis.clients.jedis.commands; import redis.clients.jedis.params.CommandListFilterByParams; import redis.clients.jedis.resps.CommandDocument; import redis.clients.jedis.resps.CommandInfo; import redis.clients.jedis.util.KeyValue; import java.util.List; import java.util.Map; public interface CommandCommands { /** * The number of total commands in this Redis server * @return The number of total commands */ long commandCount(); /** * Return documentary information about commands. * If not specifying commands, the reply includes all the server's commands. * @param commands specify the names of one or more commands * @return list of {@link CommandDocument} */ Map commandDocs(String... commands); /** * Return list of keys from a full Redis command * @param command * @return list of keys */ List commandGetKeys(String... command); /** * Return list of keys from a full Redis command and their usage flags * @param command * @return list of {@link KeyValue} */ List>> commandGetKeysAndFlags(String... command); /** * Return details about multiple Redis commands * @param commands * @return list of {@link CommandInfo} */ Map commandInfo(String... commands); /** * Return an array with details about every Redis command. * @return list of {@link CommandInfo} */ Map command(); /** * Return a list of the server's command names * @return commands list */ List commandList(); /** * Return a list of the server's command names filtered by module's name, ACL category or pattern * @param filterByParams {@link CommandListFilterByParams} * @return commands list */ List commandListFilterBy(CommandListFilterByParams filterByParams); } ================================================ FILE: src/main/java/redis/clients/jedis/commands/ConfigCommands.java ================================================ package redis.clients.jedis.commands; import java.util.Map; /** * The interface about managing configuration parameters of Redis server. */ public interface ConfigCommands { /** * Used to read the configuration parameters of Redis server. * * @param pattern name of Redis server's configuration * @return config value of Redis server */ Map configGet(String pattern); /** * Used to read the configuration parameters of Redis server. * * @param patterns names of Redis server's configuration * @return values of Redis server's configuration */ Map configGet(String... patterns); /** * Used to read the configuration parameters of Redis server. * * @param pattern name of Redis server's configuration * @return value of Redis server's configuration */ Map configGet(byte[] pattern); /** * Used to read the configuration parameters of Redis server. * * @param patterns names of Redis server's configuration * @return values of Redis server's configuration */ Map configGet(byte[]... patterns); /** * Used in order to reconfigure the Redis server at run time without * the need to restart. * * @param parameter name of Redis server's configuration * @param value value of Redis server's configuration * @return OK when the configuration was set properly. * Otherwise, an error is returned. */ String configSet(String parameter, String value); String configSet(String... parameterValues); String configSet(Map parameterValues); /** * Used in order to reconfigure the Redis server at run time without * the need to restart. * * @param parameter name of Redis server's configuration * @param value value of Redis server's configuration * @return OK when the configuration was set properly. * Otherwise, an error is returned. */ String configSet(byte[] parameter, byte[] value); String configSet(byte[]... parameterValues); String configSetBinary(Map parameterValues); /** * Resets the statistics reported by Redis using the INFO command. *

* These are the counters that are reset: *

* 1) Keyspace hits * 2) Keyspace misses * 3) Number of commands processed * 4) Number of connections received * 5) Number of expired keys * 6) Number of rejected connections * 7) Latest fork(2) time * 8) The aof_delayed_fsync counter * * @return always OK. */ String configResetStat(); /** * Rewrites the redis.conf file the server was started with, applying * the minimal changes needed to make it reflect the configuration * currently used by the server, which may be different compared to the * original one because of the use of the CONFIG SET command. * * @return OK when the configuration was rewritten properly. * Otherwise, an error is returned. */ String configRewrite(); } ================================================ FILE: src/main/java/redis/clients/jedis/commands/ControlBinaryCommands.java ================================================ package redis.clients.jedis.commands; import java.util.List; /** * The interface about Redis management command */ public interface ControlBinaryCommands extends AccessControlLogBinaryCommands, ClientBinaryCommands { /** * Provide information on the role of a Redis instance in the context of replication, * by returning if the instance is currently a master, slave, or sentinel. The command * also returns additional information about the state of the replication * (if the role is master or slave) or the list of monitored master names (if the role is sentinel). * * @return The information on the role of a Redis instance */ List roleBinary(); /** * Returns the reference count of the stored at {@code key}. * * @param key The key in Redis server * @return The reference count of the stored at {@code key} */ Long objectRefcount(byte[] key); /** * Returns the internal encoding for the Redis object stored at {@code key}. *

* See for details: OBJECT ENCODING key * * @param key The key in Redis server * @return The number of references */ byte[] objectEncoding(byte[] key); /** * Returns the time in seconds since the last access to the value stored at {@code key}. * The command is only available when the maxmemory-policy configuration directive * is not set to one of the LFU policies. * * @param key The key in Redis server * @return The idle time in seconds */ Long objectIdletime(byte[] key); /** * Returns the object subcommands and usages. * * @return object subcommands and usages */ List objectHelpBinary(); /** * Returns the logarithmic access frequency counter of a Redis object stored at {@code key}. *

* The command is only available when the maxmemory-policy configuration directive is * set to one of the LFU policies. * * @param key The key in Redis server * @return The counter's value */ Long objectFreq(byte[] key); /** * Reports about different memory-related issues that the Redis server experiences, * and advises about possible remedies. */ byte[] memoryDoctorBinary(); /** * Reports the number of bytes that a key and its value require to be stored in RAM. * The reported usage is the total of memory allocations for data and administrative * overheads that a key its value require. *

* See for details: MEMORY USAGE key * * @param key The key in Redis server * @return The memory usage in bytes, or nil when the key does not exist */ Long memoryUsage(byte[] key); /** * Reports the number of bytes that a key and its value require to be stored in RAM. * The reported usage is the total of memory allocations for data and administrative * overheads that a key its value require. *

* See for details: MEMORY USAGE key SAMPLES count * * @param key The key in Redis server * @return The memory usage in bytes, or nil when the key does not exist */ Long memoryUsage(byte[] key, int samples); } ================================================ FILE: src/main/java/redis/clients/jedis/commands/ControlCommands.java ================================================ package redis.clients.jedis.commands; import java.util.List; import java.util.Map; /** * The interface about Redis management command */ public interface ControlCommands extends AccessControlLogCommands, ClientCommands { /** * Provide information on the role of a Redis instance in the context of replication, * by returning if the instance is currently a master, slave, or sentinel. The command * also returns additional information about the state of the replication * (if the role is master or slave) or the list of monitored master names (if the role is sentinel). * * @return The information on the role of a Redis instance */ List role(); /** * Returns the reference count of the stored at {@code key}. * * @param key The key in Redis server * @return The reference count of the stored at {@code key} */ Long objectRefcount(String key); /** * Returns the internal encoding for the Redis object stored at {@code key}. *

* See for details: OBJECT ENCODING key * * @param key The key in Redis server * @return The number of references */ String objectEncoding(String key); /** * Returns the time in seconds since the last access to the value stored at {@code key}. * The command is only available when the maxmemory-policy configuration directive * is not set to one of the LFU policies. * * @param key The key in Redis server * @return The idle time in seconds */ Long objectIdletime(String key); /** * Returns the object subcommands and usages. * * @return object subcommands and usages */ List objectHelp(); /** * Returns the logarithmic access frequency counter of a Redis object stored at {@code key}. *

* The command is only available when the maxmemory-policy configuration directive is * set to one of the LFU policies. * * @param key The key in Redis server * @return The counter's value */ Long objectFreq(String key); /** * Reports about different memory-related issues that the Redis server experiences, * and advises about possible remedies. */ String memoryDoctor(); /** * Reports the number of bytes that a key and its value require to be stored in RAM. * The reported usage is the total of memory allocations for data and administrative * overheads that a key its value require. *

* See for details: MEMORY USAGE key * * @param key The key in Redis server * @return The memory usage in bytes, or {@code nil} when the key does not exist */ Long memoryUsage(String key); /** * Reports the number of bytes that a key and its value require to be stored in RAM. * The reported usage is the total of memory allocations for data and administrative * overheads that a key its value require. *

* See for details: MEMORY USAGE key SAMPLES count * * @param key The key in Redis server * @return The memory usage in bytes, or {@code nil} when the key does not exist */ Long memoryUsage(String key, int samples); /** * Attempts to purge dirty pages so these can be reclaimed by the allocator. * * @return OK */ String memoryPurge(); /** * Returns an Array reply about the memory usage of the server. * * @return nested list of memory usage metrics and their values */ Map memoryStats(); } ================================================ FILE: src/main/java/redis/clients/jedis/commands/DatabaseCommands.java ================================================ package redis.clients.jedis.commands; import redis.clients.jedis.args.FlushMode; import redis.clients.jedis.params.MigrateParams; public interface DatabaseCommands { /** * Select the DB with having the specified zero-based numeric index. * @param index the index * @return OK */ String select(int index); /** * Return the number of keys in the currently-selected database. * @return The number of keys */ long dbSize(); /** * Delete all the keys of the currently selected DB. This command never fails. The time-complexity * for this operation is O(N), N being the number of keys in the database. * @return OK */ String flushDB(); /** * Delete all the keys of the currently selected DB. This command never fails. The time-complexity * for this operation is O(N), N being the number of keys in the database. * @param flushMode can be SYNC or ASYNC * @return OK */ String flushDB(FlushMode flushMode); /** * This command swaps two Redis databases, so that immediately all the clients connected to a * given database will see the data of the other database, and the other way around. * @param index1 * @param index2 * @return OK */ String swapDB(int index1, int index2); /** * Move the specified key from the currently selected DB to the specified destination DB. Note * that this command returns 1 only if the key was successfully moved, and 0 if the target key was * already there or if the source key was not found at all, so it is possible to use MOVE as a * locking primitive. * @param key The specified key * @param dbIndex Specified destination database * @return 1 if the key was moved, 0 if the key was not moved because already present on the target * DB or was not found in the current DB */ long move(String key, int dbIndex); /** * Binary version of {@link DatabaseCommands#move(String, int) MOVE}. * @see DatabaseCommands#move(String, int) */ long move(byte[] key, int dbIndex); /** * Copy the value stored at the source key to the destination key. * @param srcKey the source key. * @param dstKey the destination key. * @param db allows specifying an alternative logical database index for the destination key. * @param replace removes the destination key before copying the value to it, in order to avoid error. */ boolean copy(String srcKey, String dstKey, int db, boolean replace); /** * Binary version of {@link DatabaseCommands#copy(String, String, int, boolean) COPY}. * @see DatabaseCommands#copy(String, String, int, boolean) */ boolean copy(byte[] srcKey, byte[] dstKey, int db, boolean replace); /** * Migrate Command * Atomically transfer a key from a source Redis instance to a destination Redis instance. * On success the key is deleted from the original instance and is guaranteed to exist in * the target instance. * * @param host target host * @param port target port * @param key migrate key * @param destinationDB target db * @param timeout the maximum idle time in any moment of the communication with the * destination instance in milliseconds. * @return OK on success, or NOKEY if no keys were found in the source instance */ String migrate(String host, int port, String key, int destinationDB, int timeout); /** * Binary version of {@link DatabaseCommands#migrate(String, int, String, int, int) MIGRATE}. * @see DatabaseCommands#migrate(String, int, String, int, int) */ String migrate(String host, int port, byte[] key, int destinationDB, int timeout); /** * Migrate Command * Atomically transfer a key from a source Redis instance to a destination Redis instance. * On success the key is deleted from the original instance and is guaranteed to exist in * the target instance. * @param host target host * @param port target port * @param destinationDB target db * @param timeout the maximum idle time in any moment of the communication with the * destination instance in milliseconds. * @param params {@link MigrateParams} * @param keys to migrate * @return OK on success, or NOKEY if no keys were found in the source instance. */ String migrate(String host, int port, int destinationDB, int timeout, MigrateParams params, String... keys); /** * Binary version of {@link DatabaseCommands#migrate(String, int, int, int, MigrateParams, String...) MIGRATE}. * @see DatabaseCommands#migrate(String, int, int, int, MigrateParams, String...) */ String migrate(String host, int port, int destinationDB, int timeout, MigrateParams params, byte[]... keys); } ================================================ FILE: src/main/java/redis/clients/jedis/commands/DatabasePipelineCommands.java ================================================ package redis.clients.jedis.commands; import redis.clients.jedis.Response; import redis.clients.jedis.params.MigrateParams; public interface DatabasePipelineCommands { /** * Select the DB with having the specified zero-based numeric index. * * @param index the index of db * @return OK */ Response select(int index); /** * Return the number of keys in the currently-selected database. * * @return The number of keys */ Response dbSize(); /** * This command swaps two Redis databases, so that immediately all the clients connected to a * given database will see the data of the other database, and the other way around. * * @param index1 * @param index2 * @return OK */ Response swapDB(int index1, int index2); /** * Move the specified key from the currently selected DB to the specified destination DB. Note * that this command returns 1 only if the key was successfully moved, and 0 if the target key was * already there or if the source key was not found at all, so it is possible to use MOVE as a * locking primitive. * * @param key The specified key * @param dbIndex Specified destination database * @return 1 if the key was moved, 0 if the key was not moved because already present on the target * DB or was not found in the current DB */ Response move(String key, int dbIndex); /** * Binary version of {@link DatabaseCommands#move(String, int) MOVE}. * * @see DatabaseCommands#move(String, int) */ Response move(byte[] key, int dbIndex); /** * Copy the value stored at the source key to the destination key. * * @param srcKey The source key. * @param dstKey The destination key. * @param db Allows specifying an alternative logical database index for the destination key. * @param replace Removes the destination key before copying the value to it, in order to avoid error. */ Response copy(String srcKey, String dstKey, int db, boolean replace); /** * Binary version of {@link DatabasePipelineCommands#copy(String, String, int, boolean) COPY}. * * @see DatabasePipelineCommands#copy(String, String, int, boolean) */ Response copy(byte[] srcKey, byte[] dstKey, int db, boolean replace); /** * Binary version of {@link DatabasePipelineCommands#migrate(String, int, String, int, int) MIGRATE}. * * @see DatabasePipelineCommands#migrate(String, int, String, int, int) */ Response migrate(String host, int port, byte[] key, int destinationDB, int timeout); /** * Binary version of {@link DatabasePipelineCommands#migrate(String, int, int, int, MigrateParams, String...) MIGRATE}. * * @see DatabasePipelineCommands#migrate(String, int, int, int, MigrateParams, String...) */ Response migrate(String host, int port, int destinationDB, int timeout, MigrateParams params, byte[]... keys); /** * Migrate Command * Atomically transfer a key from a source Redis instance to a destination Redis instance. * On success the key is deleted from the original instance and is guaranteed to exist in * the target instance. * * @param host Target host * @param port Target port * @param key Migrate key * @param destinationDB Target db * @param timeout The maximum idle time in any moment of the communication with the * destination instance in milliseconds. * @return OK on success, or NOKEY if no keys were found in the source instance */ Response migrate(String host, int port, String key, int destinationDB, int timeout); /** * Migrate Command * Atomically transfer a key from a source Redis instance to a destination Redis instance. * On success the key is deleted from the original instance and is guaranteed to exist in * the target instance. * * @param host Target host * @param port Target port * @param destinationDB Target db * @param timeout The maximum idle time in any moment of the communication with the * destination instance in milliseconds. * @param params {@link MigrateParams} * @param keys The keys to migrate * @return OK on success, or NOKEY if no keys were found in the source instance. */ Response migrate(String host, int port, int destinationDB, int timeout, MigrateParams params, String... keys); } ================================================ FILE: src/main/java/redis/clients/jedis/commands/FunctionBinaryCommands.java ================================================ package redis.clients.jedis.commands; import redis.clients.jedis.args.FlushMode; import redis.clients.jedis.args.FunctionRestorePolicy; import redis.clients.jedis.resps.FunctionStats; import redis.clients.jedis.resps.LibraryInfo; import java.util.List; public interface FunctionBinaryCommands { /** * Invoke a function. * @param name * @param keys * @param args * @return value depends on the function that was executed */ Object fcall(byte[] name, List keys, List args); Object fcallReadonly(byte[] name, List keys, List args); /** * This command deletes the library called library-name and all functions in it. * If the library doesn't exist, the server returns an error. * @param libraryName * @return OK */ String functionDelete(byte[] libraryName); /** * Return the serialized payload of loaded libraries. You can restore the * serialized payload later with the {@link FunctionBinaryCommands#functionRestore(byte[], FunctionRestorePolicy) FUNCTION RESTORE} command. * @return the serialized payload */ byte[] functionDump(); /** * Deletes all the libraries, unless called with the optional mode argument, the * 'lazyfree-lazy-user-flush' configuration directive sets the effective behavior. * @return OK */ String functionFlush(); /** * Deletes all the libraries, unless called with the optional mode argument, the * 'lazyfree-lazy-user-flush' configuration directive sets the effective behavior. * @param mode ASYNC: Asynchronously flush the libraries, SYNC: Synchronously flush the libraries. * @return OK */ String functionFlush(FlushMode mode); /** * Kill a function that is currently executing. The command can be used only on functions * that did not modify the dataset during their execution. * @return OK */ String functionKill(); /** * Return information about the functions and libraries. * @return {@link LibraryInfo} */ List functionListBinary(); /** * Return information about the functions and libraries. * @param libraryNamePattern a pattern for matching library names * @return {@link LibraryInfo} */ List functionList(byte[] libraryNamePattern); /** * Similar to {@link FunctionCommands#functionList() FUNCTION LIST} but include the * libraries source implementation in the reply. * @see FunctionCommands#functionList() * @return {@link LibraryInfo} */ List functionListWithCodeBinary(); /** * Similar to {@link FunctionBinaryCommands#functionList(byte[]) FUNCTION LIST} but include the * libraries source implementation in the reply. * @see FunctionBinaryCommands#functionList(byte[]) * @param libraryNamePattern a pattern for matching library names * @return {@link LibraryInfo} */ List functionListWithCode(byte[] libraryNamePattern); /** * Load a library to Redis. *

* The library payload must start with Shebang statement that provides a metadata about the * library (like the engine to use and the library name). Shebang format: * {@code #! name=}. Currently engine name must be lua. * * @param functionCode the source code. * @return The library name that was loaded */ String functionLoad(byte[] functionCode); /** * Load a library to Redis. Will replace the current library if it already exists. * @param functionCode the source code * @return The library name that was loaded */ String functionLoadReplace(byte[] functionCode); /** * Restore libraries from the serialized payload. Default policy is APPEND. * @param serializedValue the serialized payload * @return OK */ String functionRestore(byte[] serializedValue); /** * Restore libraries from the serialized payload. * @param serializedValue the serialized payload * @param policy can be {@link FunctionRestorePolicy FLUSH, APPEND or REPLACE} * @return OK */ String functionRestore(byte[] serializedValue, FunctionRestorePolicy policy); /** * Return information about the function that's currently running and information * about the available execution engines. * @return {@link FunctionStats} */ Object functionStatsBinary(); } ================================================ FILE: src/main/java/redis/clients/jedis/commands/FunctionCommands.java ================================================ package redis.clients.jedis.commands; import redis.clients.jedis.args.FlushMode; import redis.clients.jedis.args.FunctionRestorePolicy; import redis.clients.jedis.resps.FunctionStats; import redis.clients.jedis.resps.LibraryInfo; import java.util.List; public interface FunctionCommands { /** * Invoke a function. * @param name * @param keys * @param args */ Object fcall(String name, List keys, List args); /** * This is a read-only variant of the {@link FunctionCommands#fcall(String, List, List) FCALL} * command that cannot execute commands that modify data. */ Object fcallReadonly(String name, List keys, List args); /** * This command deletes the library called library-name and all functions in it. * If the library doesn't exist, the server returns an error. * @param libraryName * @return OK */ String functionDelete(String libraryName); /** * Return the serialized payload of loaded libraries. You can restore the * serialized payload later with the {@link FunctionBinaryCommands#functionRestore(byte[], FunctionRestorePolicy) FUNCTION RESTORE} command. * @return the serialized payload */ byte[] functionDump(); /** * Deletes all the libraries, unless called with the optional mode argument, the * 'lazyfree-lazy-user-flush' configuration directive sets the effective behavior. * @return OK */ String functionFlush(); /** * Deletes all the libraries, unless called with the optional mode argument, the * 'lazyfree-lazy-user-flush' configuration directive sets the effective behavior. * @param mode ASYNC: Asynchronously flush the libraries, SYNC: Synchronously flush the libraries. * @return OK */ String functionFlush(FlushMode mode); /** * Kill a function that is currently executing. The command can be used only on functions * that did not modify the dataset during their execution. * @return OK */ String functionKill(); /** * Return information about the functions and libraries. * @return {@link LibraryInfo} */ List functionList(); /** * Return information about the functions and libraries. * @param libraryNamePattern a pattern for matching library names * @return {@link LibraryInfo} */ List functionList(String libraryNamePattern); /** * Similar to {@link FunctionCommands#functionList() FUNCTION LIST} but include the * libraries source implementation in the reply. * @see FunctionCommands#functionList() * @return {@link LibraryInfo} */ List functionListWithCode(); /** * Similar to {@link FunctionCommands#functionList(String) FUNCTION LIST} but include the * libraries source implementation in the reply. * @see FunctionCommands#functionList(String) * @param libraryNamePattern a pattern for matching library names * @return {@link LibraryInfo} */ List functionListWithCode(String libraryNamePattern); /** * Load a library to Redis. *

* The library payload must start with Shebang statement that provides a metadata about the * library (like the engine to use and the library name). Shebang format: * {@code #! name=}. Currently engine name must be lua. * * @param functionCode the source code. * @return The library name that was loaded */ String functionLoad(String functionCode); /** * Load a library to Redis. Will replace the current library if it already exists. * @param functionCode the source code * @return The library name that was loaded */ String functionLoadReplace(String functionCode); /** * Restore libraries from the serialized payload. Default policy is APPEND. * @param serializedValue the serialized payload * @return OK */ String functionRestore(byte[] serializedValue); /** * Restore libraries from the serialized payload. * @param serializedValue the serialized payload * @param policy can be {@link FunctionRestorePolicy FLUSH, APPEND or REPLACE} * @return OK */ String functionRestore(byte[] serializedValue, FunctionRestorePolicy policy); /** * Return information about the function that's currently running and information * about the available execution engines. * @return {@link FunctionStats} */ FunctionStats functionStats(); } ================================================ FILE: src/main/java/redis/clients/jedis/commands/FunctionPipelineBinaryCommands.java ================================================ package redis.clients.jedis.commands; import redis.clients.jedis.Response; import redis.clients.jedis.args.FlushMode; import redis.clients.jedis.args.FunctionRestorePolicy; import java.util.List; public interface FunctionPipelineBinaryCommands { Response fcall(byte[] name, List keys, List args); Response fcallReadonly(byte[] name, List keys, List args); Response functionDelete(byte[] libraryName); Response functionDump(); Response functionFlush(); Response functionFlush(FlushMode mode); Response functionKill(); Response> functionListBinary(); Response> functionList(byte[] libraryNamePattern); Response> functionListWithCodeBinary(); Response> functionListWithCode(byte[] libraryNamePattern); Response functionLoad(byte[] functionCode); Response functionLoadReplace(byte[] functionCode); Response functionRestore(byte[] serializedValue); Response functionRestore(byte[] serializedValue, FunctionRestorePolicy policy); Response functionStatsBinary(); } ================================================ FILE: src/main/java/redis/clients/jedis/commands/FunctionPipelineCommands.java ================================================ package redis.clients.jedis.commands; import redis.clients.jedis.Response; import redis.clients.jedis.args.FlushMode; import redis.clients.jedis.args.FunctionRestorePolicy; import redis.clients.jedis.resps.FunctionStats; import redis.clients.jedis.resps.LibraryInfo; import java.util.List; public interface FunctionPipelineCommands { Response fcall(String name, List keys, List args); Response fcallReadonly(String name, List keys, List args); Response functionDelete(String libraryName); Response functionDump(); Response functionFlush(); Response functionFlush(FlushMode mode); Response functionKill(); Response> functionList(); Response> functionList(String libraryNamePattern); Response> functionListWithCode(); Response> functionListWithCode(String libraryNamePattern); Response functionLoad(String functionCode); Response functionLoadReplace(String functionCode); Response functionRestore(byte[] serializedValue); Response functionRestore(byte[] serializedValue, FunctionRestorePolicy policy); Response functionStats(); } ================================================ FILE: src/main/java/redis/clients/jedis/commands/GenericControlCommands.java ================================================ package redis.clients.jedis.commands; import java.util.List; import redis.clients.jedis.Module; import redis.clients.jedis.params.FailoverParams; public interface GenericControlCommands extends ConfigCommands, ScriptingControlCommands, SlowlogCommands { String failover(); String failover(FailoverParams failoverParams); String failoverAbort(); List moduleList(); } ================================================ FILE: src/main/java/redis/clients/jedis/commands/GeoBinaryCommands.java ================================================ package redis.clients.jedis.commands; import java.util.List; import java.util.Map; import redis.clients.jedis.GeoCoordinate; import redis.clients.jedis.args.GeoUnit; import redis.clients.jedis.params.GeoAddParams; import redis.clients.jedis.params.GeoRadiusParam; import redis.clients.jedis.params.GeoRadiusStoreParam; import redis.clients.jedis.params.GeoSearchParam; import redis.clients.jedis.resps.GeoRadiusResponse; public interface GeoBinaryCommands { long geoadd(byte[] key, double longitude, double latitude, byte[] member); long geoadd(byte[] key, Map memberCoordinateMap); long geoadd(byte[] key, GeoAddParams params, Map memberCoordinateMap); Double geodist(byte[] key, byte[] member1, byte[] member2); Double geodist(byte[] key, byte[] member1, byte[] member2, GeoUnit unit); List geohash(byte[] key, byte[]... members); List geopos(byte[] key, byte[]... members); List georadius(byte[] key, double longitude, double latitude, double radius, GeoUnit unit); List georadiusReadonly(byte[] key, double longitude, double latitude, double radius, GeoUnit unit); List georadius(byte[] key, double longitude, double latitude, double radius, GeoUnit unit, GeoRadiusParam param); List georadiusReadonly(byte[] key, double longitude, double latitude, double radius, GeoUnit unit, GeoRadiusParam param); List georadiusByMember(byte[] key, byte[] member, double radius, GeoUnit unit); List georadiusByMemberReadonly(byte[] key, byte[] member, double radius, GeoUnit unit); List georadiusByMember(byte[] key, byte[] member, double radius, GeoUnit unit, GeoRadiusParam param); List georadiusByMemberReadonly(byte[] key, byte[] member, double radius, GeoUnit unit, GeoRadiusParam param); long georadiusStore(byte[] key, double longitude, double latitude, double radius, GeoUnit unit, GeoRadiusParam param, GeoRadiusStoreParam storeParam); long georadiusByMemberStore(byte[] key, byte[] member, double radius, GeoUnit unit, GeoRadiusParam param, GeoRadiusStoreParam storeParam); List geosearch(byte[] key, byte[] member, double radius, GeoUnit unit); List geosearch(byte[] key, GeoCoordinate coord, double radius, GeoUnit unit); List geosearch(byte[] key, byte[] member, double width, double height, GeoUnit unit); List geosearch(byte[] key, GeoCoordinate coord, double width, double height, GeoUnit unit); List geosearch(byte[] key, GeoSearchParam params); long geosearchStore(byte[] dest, byte[] src, byte[] member, double radius, GeoUnit unit); long geosearchStore(byte[] dest, byte[] src, GeoCoordinate coord, double radius, GeoUnit unit); long geosearchStore(byte[] dest, byte[] src, byte[] member, double width, double height, GeoUnit unit); long geosearchStore(byte[] dest, byte[] src, GeoCoordinate coord, double width, double height, GeoUnit unit); long geosearchStore(byte[] dest, byte[] src, GeoSearchParam params); long geosearchStoreStoreDist(byte[] dest, byte[] src, GeoSearchParam params); } ================================================ FILE: src/main/java/redis/clients/jedis/commands/GeoCommands.java ================================================ package redis.clients.jedis.commands; import java.util.List; import java.util.Map; import redis.clients.jedis.GeoCoordinate; import redis.clients.jedis.args.GeoUnit; import redis.clients.jedis.params.GeoAddParams; import redis.clients.jedis.params.GeoRadiusParam; import redis.clients.jedis.params.GeoRadiusStoreParam; import redis.clients.jedis.params.GeoSearchParam; import redis.clients.jedis.resps.GeoRadiusResponse; public interface GeoCommands { /** * Adds the specified geospatial item (longitude, latitude, member) to the specified key. *

* Time complexity: O(log(N)) where N is the number of elements in the sorted set. * @param key * @param longitude * @param latitude * @param member * @return The number of elements added */ long geoadd(String key, double longitude, double latitude, String member); /** * Adds the specified geospatial items (in memberCoordinateMap) to the specified key. *

* Time complexity: O(log(N)) for each item added, where N is the number of elements in * the sorted set. * @param key * @param memberCoordinateMap Members names with their geo coordinates * @return The number of elements added */ long geoadd(String key, Map memberCoordinateMap); /** * Adds the specified geospatial items (in memberCoordinateMap) to the specified key. * Can be used with the following options: * XX- Only update elements that already exist. Never add elements. * NX- Don't update already existing elements. Always add new elements. * CH- Modify the return value from the number of new elements added, to the total number of elements changed *

* Time complexity: O(log(N)) for each item added * @param key * @param params Additional options * @param memberCoordinateMap Members names with their geo coordinates * @return The number of elements added */ long geoadd(String key, GeoAddParams params, Map memberCoordinateMap); /** * Return the distance between two members in the geospatial index represented by the sorted set. *

* Time complexity: O(log(N)) * @param key * @param member1 * @param member2 * @return The distance as a double */ Double geodist(String key, String member1, String member2); /** * Return the distance between two members in the geospatial index represented by the sorted set. *

* Time complexity: O(log(N)) * @param key * @param member1 * @param member2 * @param unit can be M, KM, MI or FT can M, KM, MI or FT * @return The distance as a double */ Double geodist(String key, String member1, String member2, GeoUnit unit); /** * Return valid Geohash strings representing the position of the given members. *

* Time complexity: O(log(N)) for each member requested * @param key * @param members * @return A list of Geohash strings corresponding to each member name passed as * argument to the command. */ List geohash(String key, String... members); /** * Return the positions (longitude,latitude) of all the specified members. *

* Time complexity: O(N) where N is the number of members requested. * @param key * @param members * @return A list of GeoCoordinate representing longitude and latitude (x,y) * of each member name passed as argument to the command. */ List geopos(String key, String... members); /** * Return the members of a sorted set populated with geospatial information using GEOADD, * which are within the borders of the area specified with the center location and the radius. *

* Time complexity: O(N+log(M)) where N is the number of elements inside the bounding box of * the circular area delimited by center and radius and M is the number of items inside the index. * @param key * @param longitude of the center point * @param latitude of the center point * @param radius of the area * @param unit can be M, KM, MI or FT * @return List of GeoRadiusResponse */ List georadius(String key, double longitude, double latitude, double radius, GeoUnit unit); /** * Readonly version of {@link GeoCommands#georadius(String, double, double, double, GeoUnit) GEORADIUS}, *

* Time complexity: O(N+log(M)) where N is the number of elements inside the bounding box of * the circular area delimited by center and radius and M is the number of items inside the index. * @see GeoCommands#georadius(String, double, double, double, GeoUnit) * @param key * @param longitude of the center point * @param latitude of the center point * @param radius of the area * @param unit can be M, KM, MI or FT * @return List of GeoRadiusResponse */ List georadiusReadonly(String key, double longitude, double latitude, double radius, GeoUnit unit); /** * Return the members of a sorted set populated with geospatial information using GEOADD, * which are within the borders of the area specified with the center location and the radius. * Additional information can be reached using {@link GeoRadiusParam}: * WITHDIST: Also return the distance of the returned items from the specified center. * The distance is returned in the same unit as the unit specified as the radius argument of the command. * WITHCOORD: Also return the longitude,latitude coordinates of the matching items. * WITHHASH: Also return the raw geohash-encoded sorted set score of the item, in the form of a 52 * bit unsigned integer. This is only useful for low level hacks or debugging and is otherwise of * little interest for the general user. *

* Time complexity: O(N+log(M)) where N is the number of elements inside the bounding box of * the circular area delimited by center and radius and M is the number of items inside the index. * @param key * @param longitude of the center point * @param latitude of the center point * @param radius of the area * @param unit can be M, KM, MI or FT * @param param {@link GeoRadiusParam} * @return List of GeoRadiusResponse */ List georadius(String key, double longitude, double latitude, double radius, GeoUnit unit, GeoRadiusParam param); /** * Readonly version of {@link GeoCommands#georadius(String, double, double, double, GeoUnit, GeoRadiusParam) GEORADIUS}, *

* Time complexity: O(N+log(M)) where N is the number of elements inside the bounding box of * the circular area delimited by center and radius and M is the number of items inside the index. * @see GeoCommands#georadius(String, double, double, double, GeoUnit, GeoRadiusParam) * @param key * @param longitude of the center point * @param latitude of the center point * @param radius of the area * @param unit can be M, KM, MI or FT * @param param {@link GeoRadiusParam} * @return List of GeoRadiusResponse */ List georadiusReadonly(String key, double longitude, double latitude, double radius, GeoUnit unit, GeoRadiusParam param); /** * This command is exactly like {@link GeoCommands#georadius(String, double, double, double, GeoUnit) GEORADIUS} * with the sole difference that instead of taking, as the center of the area to query, a longitude * and latitude value, it takes the name of a member already existing inside the geospatial index * represented by the sorted set. *

* Time complexity: O(N+log(M)) where N is the number of elements inside the bounding box of * the circular area delimited by center and radius and M is the number of items inside the index. * @param key * @param member represents the center of the area * @param radius of the area * @param unit can be M, KM, MI or FT * @return List of GeoRadiusResponse */ List georadiusByMember(String key, String member, double radius, GeoUnit unit); /** * Readonly version of {@link GeoCommands#georadiusByMember(String, String, double, GeoUnit) GEORADIUSBYMEMBER} *

* Time complexity: O(N+log(M)) where N is the number of elements inside the bounding box of * the circular area delimited by center and radius and M is the number of items inside the index. * @param key * @param member represents the center of the area * @param radius of the area * @param unit can be M, KM, MI or FT * @return List of GeoRadiusResponse */ List georadiusByMemberReadonly(String key, String member, double radius, GeoUnit unit); /** * This command is exactly like {@link GeoCommands#georadius(String, double, double, double, GeoUnit, GeoRadiusParam) GEORADIUS} * with the sole difference that instead of taking, as the center of the area to query, a longitude * and latitude value, it takes the name of a member already existing inside the geospatial index * represented by the sorted set. *

* Time complexity: O(N+log(M)) where N is the number of elements inside the bounding box of * the circular area delimited by center and radius and M is the number of items inside the index. * @param key * @param member represents the center of the area * @param radius of the area * @param unit can be M, KM, MI or FT * @param param {@link GeoRadiusParam} * @return List of GeoRadiusResponse */ List georadiusByMember(String key, String member, double radius, GeoUnit unit, GeoRadiusParam param); /** * Readonly version of {@link GeoCommands#georadiusByMember(String, String, double, GeoUnit, GeoRadiusParam) GEORADIUSBYMEMBER} *

* Time complexity: O(N+log(M)) where N is the number of elements inside the bounding box of * the circular area delimited by center and radius and M is the number of items inside the index. * @param key * @param member represents the center of the area * @param radius of the area * @param unit can be M, KM, MI or FT * @param param {@link GeoRadiusParam} * @return List of GeoRadiusResponse */ List georadiusByMemberReadonly(String key, String member, double radius, GeoUnit unit, GeoRadiusParam param); /** * This command is exactly like {@link GeoCommands#georadius(String, double, double, double, GeoUnit, GeoRadiusParam) GEORADIUS} * but storing the results at the destination key (provided with {@link GeoRadiusStoreParam storeParam}). *

* Time complexity: O(N+log(M)) where N is the number of elements inside the bounding box of * the circular area delimited by center and radius and M is the number of items inside the index. * @param key * @param longitude of the center point * @param latitude of the center point * @param radius of the area * @param unit can be M, KM, MI or FT * @param param {@link GeoRadiusParam} * @param storeParam {@link GeoRadiusStoreParam} * @return The number of results being stored */ long georadiusStore(String key, double longitude, double latitude, double radius, GeoUnit unit, GeoRadiusParam param, GeoRadiusStoreParam storeParam); /** * This command is exactly like {@link GeoCommands#georadiusByMember(String, String, double, GeoUnit, GeoRadiusParam) GEORADIUSBYMEMBER} * but storing the results at the destination key (provided with {@link GeoRadiusStoreParam storeParam}). *

* Time complexity: O(N+log(M)) where N is the number of elements inside the bounding box of * the circular area delimited by center and radius and M is the number of items inside the index. * @param key * @param member represents the center of the area * @param radius of the area * @param unit can be M, KM, MI or FT * @param param {@link GeoRadiusParam} * @param storeParam {@link GeoRadiusStoreParam} * @return The number of results being stored */ long georadiusByMemberStore(String key, String member, double radius, GeoUnit unit, GeoRadiusParam param, GeoRadiusStoreParam storeParam); /** * Return the members of a sorted set populated with geospatial information using GEOADD, * which are within the borders of the area specified by a given shape. *

* This command can be used in place of the {@link GeoCommands#georadiusByMember(String, String, double, GeoUnit) GEORADIUSBYMEMBER} command. *

* Time complexity: O(N+log(M)) where N is the number of elements in the grid-aligned * bounding box area around the shape provided as the filter and M is the number of items * inside the shape * @param key * @param member represents the center of the area * @param radius of the area * @param unit can be M, KM, MI or FT * @return List of GeoRadiusResponse */ List geosearch(String key, String member, double radius, GeoUnit unit); /** * Return the members of a sorted set populated with geospatial information using GEOADD, * which are within the borders of the area specified by a given shape. *

* This command can be used in place of the {@link GeoCommands#georadius(String, double, double, double, GeoUnit) GEORADIUS} command. *

* Time complexity: O(N+log(M)) where N is the number of elements in the grid-aligned * bounding box area around the shape provided as the filter and M is the number of items * inside the shape * @param key * @param coord represents the center of the area * @param radius of the area * @param unit can be M, KM, MI or FT * @return List of GeoRadiusResponse */ List geosearch(String key, GeoCoordinate coord, double radius, GeoUnit unit); /** * Return the members of a sorted set populated with geospatial information using GEOADD, * which are within the borders of the area specified by a given shape. This command extends * the GEORADIUS command, so in addition to searching within circular areas, it supports * searching within rectangular areas. *

* The axis-aligned rectangle, determined by height and width, when the center point is * determined by the position of the given member. *

* Time complexity: O(N+log(M)) where N is the number of elements in the grid-aligned * bounding box area around the shape provided as the filter and M is the number of items * inside the shape * @param key * @param member represents the center of the area * @param width of the rectangular area * @param height of the rectangular area * @param unit can be M, KM, MI or FT * @return List of GeoRadiusResponse */ List geosearch(String key, String member, double width, double height, GeoUnit unit); /** * Return the members of a sorted set populated with geospatial information using GEOADD, * which are within the borders of the area specified by a given shape. This command extends * the GEORADIUS command, so in addition to searching within circular areas, it supports * searching within rectangular areas. *

* The axis-aligned rectangle, determined by height and width, when the center point is * determined by the given coordinate. *

* Time complexity: O(N+log(M)) where N is the number of elements in the grid-aligned * bounding box area around the shape provided as the filter and M is the number of items * inside the shape * @param key * @param coord represents the center point * @param width of the rectangular area * @param height of the rectangular area * @param unit can be M, KM, MI or FT * @return List of GeoRadiusResponse */ List geosearch(String key, GeoCoordinate coord, double width, double height, GeoUnit unit); /** * Return the members of a sorted set populated with geospatial information using GEOADD, * which are within the borders of the area specified by a given shape. This command extends * the GEORADIUS command, so in addition to searching within circular areas, it supports * searching within rectangular areas. *

* Time complexity: O(N+log(M)) where N is the number of elements in the grid-aligned * bounding box area around the shape provided as the filter and M is the number of items * inside the shape * @param key * @param params {@link GeoSearchParam} * @return List of GeoRadiusResponse */ List geosearch(String key, GeoSearchParam params); /** * This command is exactly like {@link GeoCommands#geosearch(String, String, double, GeoUnit) GEOSEARCH} * but storing the results at dest. *

* Time complexity: O(N+log(M)) where N is the number of elements in the grid-aligned * bounding box area around the shape provided as the filter and M is the number of items * inside the shape * @param dest * @param src the sorted set (key) * @param member represents the center of the area * @param radius of the circular area * @param unit can be M, KM, MI or FT * @return The number of results being stored */ long geosearchStore(String dest, String src, String member, double radius, GeoUnit unit); /** * This command is exactly like {@link GeoCommands#geosearch(String, GeoCoordinate, double, GeoUnit) GEOSEARCH} * but storing the results at dest. *

* Time complexity: O(N+log(M)) where N is the number of elements in the grid-aligned * bounding box area around the shape provided as the filter and M is the number of items * inside the shape * @param dest * @param src * @param coord represents the center point * @param radius of the circular area * @param unit can be M, KM, MI or FT * @return The number of results being stored */ long geosearchStore(String dest, String src, GeoCoordinate coord, double radius, GeoUnit unit); /** * This command is exactly like {@link GeoCommands#geosearch(String, String, double, double, GeoUnit) GEOSEARCH} * but storing the results at dest. *

* Time complexity: O(N+log(M)) where N is the number of elements in the grid-aligned * bounding box area around the shape provided as the filter and M is the number of items * inside the shape * @param dest * @param src * @param member represents the center of the area * @param width of the rectangular area * @param height of the rectangular area * @param unit can be M, KM, MI or FT * @return The number of results being stored */ long geosearchStore(String dest, String src, String member, double width, double height, GeoUnit unit); /** * This command is exactly like {@link GeoCommands#geosearch(String, GeoCoordinate, double, double, GeoUnit) GEOSEARCH} * but storing the results at dest. *

* Time complexity: O(N+log(M)) where N is the number of elements in the grid-aligned * bounding box area around the shape provided as the filter and M is the number of items * inside the shape * @param dest * @param src * @param coord represents the center point * @param width of the rectangular area * @param height of the rectangular area * @param unit can be M, KM, MI or FT * @return The number of results being stored */ long geosearchStore(String dest, String src, GeoCoordinate coord, double width, double height, GeoUnit unit); /** * This command is exactly like {@link GeoCommands#geosearch(String, GeoSearchParam) GEOSEARCH} * but storing the results at dest. *

* Time complexity: O(N+log(M)) where N is the number of elements in the grid-aligned * bounding box area around the shape provided as the filter and M is the number of items * inside the shape * @param dest * @param src * @param params {@link GeoSearchParam} * @return The number of results being stored */ long geosearchStore(String dest, String src, GeoSearchParam params); /** * This command is exactly like {@link GeoCommands#geosearchStore(String, String, GeoSearchParam) GEOSEARCHSTORE} * but storing the results with their destinations from the center point. *

* Time complexity: O(N+log(M)) where N is the number of elements in the grid-aligned * bounding box area around the shape provided as the filter and M is the number of items * inside the shape * @param dest * @param src * @param params {@link GeoSearchParam} * @return The number of results being stored */ long geosearchStoreStoreDist(String dest, String src, GeoSearchParam params); } ================================================ FILE: src/main/java/redis/clients/jedis/commands/GeoPipelineBinaryCommands.java ================================================ package redis.clients.jedis.commands; import java.util.List; import java.util.Map; import redis.clients.jedis.GeoCoordinate; import redis.clients.jedis.Response; import redis.clients.jedis.args.GeoUnit; import redis.clients.jedis.params.GeoAddParams; import redis.clients.jedis.params.GeoRadiusParam; import redis.clients.jedis.params.GeoRadiusStoreParam; import redis.clients.jedis.params.GeoSearchParam; import redis.clients.jedis.resps.GeoRadiusResponse; public interface GeoPipelineBinaryCommands { Response geoadd(byte[] key, double longitude, double latitude, byte[] member); Response geoadd(byte[] key, Map memberCoordinateMap); Response geoadd(byte[] key, GeoAddParams params, Map memberCoordinateMap); Response geodist(byte[] key, byte[] member1, byte[] member2); Response geodist(byte[] key, byte[] member1, byte[] member2, GeoUnit unit); Response> geohash(byte[] key, byte[]... members); Response> geopos(byte[] key, byte[]... members); Response> georadius(byte[] key, double longitude, double latitude, double radius, GeoUnit unit); Response> georadiusReadonly(byte[] key, double longitude, double latitude, double radius, GeoUnit unit); Response> georadius(byte[] key, double longitude, double latitude, double radius, GeoUnit unit, GeoRadiusParam param); Response> georadiusReadonly(byte[] key, double longitude, double latitude, double radius, GeoUnit unit, GeoRadiusParam param); Response> georadiusByMember(byte[] key, byte[] member, double radius, GeoUnit unit); Response> georadiusByMemberReadonly(byte[] key, byte[] member, double radius, GeoUnit unit); Response> georadiusByMember(byte[] key, byte[] member, double radius, GeoUnit unit, GeoRadiusParam param); Response> georadiusByMemberReadonly(byte[] key, byte[] member, double radius, GeoUnit unit, GeoRadiusParam param); Response georadiusStore(byte[] key, double longitude, double latitude, double radius, GeoUnit unit, GeoRadiusParam param, GeoRadiusStoreParam storeParam); Response georadiusByMemberStore(byte[] key, byte[] member, double radius, GeoUnit unit, GeoRadiusParam param, GeoRadiusStoreParam storeParam); Response> geosearch(byte[] key, byte[] member, double radius, GeoUnit unit); Response> geosearch(byte[] key, GeoCoordinate coord, double radius, GeoUnit unit); Response> geosearch(byte[] key, byte[] member, double width, double height, GeoUnit unit); Response> geosearch(byte[] key, GeoCoordinate coord, double width, double height, GeoUnit unit); Response> geosearch(byte[] key, GeoSearchParam params); Response geosearchStore(byte[] dest, byte[] src, byte[] member, double radius, GeoUnit unit); Response geosearchStore(byte[] dest, byte[] src, GeoCoordinate coord, double radius, GeoUnit unit); Response geosearchStore(byte[] dest, byte[] src, byte[] member, double width, double height, GeoUnit unit); Response geosearchStore(byte[] dest, byte[] src, GeoCoordinate coord, double width, double height, GeoUnit unit); Response geosearchStore(byte[] dest, byte[] src, GeoSearchParam params); Response geosearchStoreStoreDist(byte[] dest, byte[] src, GeoSearchParam params); } ================================================ FILE: src/main/java/redis/clients/jedis/commands/GeoPipelineCommands.java ================================================ package redis.clients.jedis.commands; import java.util.List; import java.util.Map; import redis.clients.jedis.GeoCoordinate; import redis.clients.jedis.Response; import redis.clients.jedis.args.GeoUnit; import redis.clients.jedis.params.GeoAddParams; import redis.clients.jedis.params.GeoRadiusParam; import redis.clients.jedis.params.GeoRadiusStoreParam; import redis.clients.jedis.params.GeoSearchParam; import redis.clients.jedis.resps.GeoRadiusResponse; public interface GeoPipelineCommands { Response geoadd(String key, double longitude, double latitude, String member); Response geoadd(String key, Map memberCoordinateMap); Response geoadd(String key, GeoAddParams params, Map memberCoordinateMap); Response geodist(String key, String member1, String member2); Response geodist(String key, String member1, String member2, GeoUnit unit); Response> geohash(String key, String... members); Response> geopos(String key, String... members); Response> georadius(String key, double longitude, double latitude, double radius, GeoUnit unit); Response> georadiusReadonly(String key, double longitude, double latitude, double radius, GeoUnit unit); Response> georadius(String key, double longitude, double latitude, double radius, GeoUnit unit, GeoRadiusParam param); Response> georadiusReadonly(String key, double longitude, double latitude, double radius, GeoUnit unit, GeoRadiusParam param); Response> georadiusByMember(String key, String member, double radius, GeoUnit unit); Response> georadiusByMemberReadonly(String key, String member, double radius, GeoUnit unit); Response> georadiusByMember(String key, String member, double radius, GeoUnit unit, GeoRadiusParam param); Response> georadiusByMemberReadonly(String key, String member, double radius, GeoUnit unit, GeoRadiusParam param); Response georadiusStore(String key, double longitude, double latitude, double radius, GeoUnit unit, GeoRadiusParam param, GeoRadiusStoreParam storeParam); Response georadiusByMemberStore(String key, String member, double radius, GeoUnit unit, GeoRadiusParam param, GeoRadiusStoreParam storeParam); Response> geosearch(String key, String member, double radius, GeoUnit unit); Response> geosearch(String key, GeoCoordinate coord, double radius, GeoUnit unit); Response> geosearch(String key, String member, double width, double height, GeoUnit unit); Response> geosearch(String key, GeoCoordinate coord, double width, double height, GeoUnit unit); Response> geosearch(String key, GeoSearchParam params); Response geosearchStore(String dest, String src, String member, double radius, GeoUnit unit); Response geosearchStore(String dest, String src, GeoCoordinate coord, double radius, GeoUnit unit); Response geosearchStore(String dest, String src, String member, double width, double height, GeoUnit unit); Response geosearchStore(String dest, String src, GeoCoordinate coord, double width, double height, GeoUnit unit); Response geosearchStore(String dest, String src, GeoSearchParam params); Response geosearchStoreStoreDist(String dest, String src, GeoSearchParam params); } ================================================ FILE: src/main/java/redis/clients/jedis/commands/HashBinaryCommands.java ================================================ package redis.clients.jedis.commands; import java.util.List; import java.util.Map; import java.util.Set; import redis.clients.jedis.args.ExpiryOption; import redis.clients.jedis.params.HGetExParams; import redis.clients.jedis.params.HSetExParams; import redis.clients.jedis.params.ScanParams; import redis.clients.jedis.resps.ScanResult; public interface HashBinaryCommands { long hset(byte[] key, byte[] field, byte[] value); long hset(byte[] key, Map hash); /** * Sets the specified fields in the hash stored at key to the specified values with additional parameters, * and optionally set their expiration. Use `HSetExParams` object to specify expiration parameters. * This command can overwrite any existing fields in the hash. * If key does not exist, a new key holding a hash is created. * * @param key the key of the hash * @param params the parameters for the HSETEX command * @param field the field in the hash * @param value the value to set * @return 0 if no fields were set, 1 if all the fields were set * * @see HSetExParams */ long hsetex(byte[] key, HSetExParams params, byte[] field, byte[] value); /** * Sets the specified fields in the hash stored at key to the specified values with additional parameters, * and optionally set their expiration. Use `HSetExParams` object to specify expiration parameters. * This command can overwrite any existing fields in the hash. * If key does not exist, a new key holding a hash is created. * * @param key the key of the hash * @param params the parameters for the HSETEX command * @param hash the map containing field-value pairs to set in the hash * @return 0 if no fields were set, 1 if all the fields were set * * @see HSetExParams */ long hsetex(byte[] key, HSetExParams params, Map hash); byte[] hget(byte[] key, byte[] field); /** * Retrieves the values associated with the specified fields in a hash stored at the given key * and optionally sets their expiration. Use `HGetExParams` object to specify expiration parameters. * * @param key the key of the hash * @param params additional parameters for the HGETEX command * @param fields the fields whose values are to be retrieved * @return a list of the value associated with each field or nil if the field doesn’t exist. * * @see HGetExParams */ List hgetex(byte[] key, HGetExParams params, byte[]... fields); /** * Retrieves the values associated with the specified fields in the hash stored at the given key * and then deletes those fields from the hash. * * @param key the key of the hash * @param fields the fields whose values are to be retrieved and then deleted * @return a list of values associated with the specified fields before they were deleted */ List hgetdel(byte[] key, byte[]... fields); long hsetnx(byte[] key, byte[] field, byte[] value); /** * @deprecated Use {@link HashBinaryCommands#hset(byte[], Map)}. * Deprecated in Jedis 7.3.0. Mirrors Redis deprecation since 4.0.0. */ @Deprecated String hmset(byte[] key, Map hash); List hmget(byte[] key, byte[]... fields); long hincrBy(byte[] key, byte[] field, long value); double hincrByFloat(byte[] key, byte[] field, double value); boolean hexists(byte[] key, byte[] field); long hdel(byte[] key, byte[]... field); long hlen(byte[] key); Set hkeys(byte[] key); List hvals(byte[] key); Map hgetAll(byte[] key); byte[] hrandfield(byte[] key); List hrandfield(byte[] key, long count); List> hrandfieldWithValues(byte[] key, long count); default ScanResult> hscan(byte[] key, byte[] cursor) { return hscan(key, cursor, new ScanParams()); } ScanResult> hscan(byte[] key, byte[] cursor, ScanParams params); default ScanResult hscanNoValues(byte[] key, byte[] cursor) { return hscanNoValues(key, cursor, new ScanParams()); } ScanResult hscanNoValues(byte[] key, byte[] cursor, ScanParams params); long hstrlen(byte[] key, byte[] field); /** * Set expiry for hash field using relative time to expire (seconds). * * @param key hash * @param seconds time to expire * @param fields * @return integer-reply: 1 if the timeout was set, 0 otherwise */ List hexpire(byte[] key, long seconds, byte[]... fields); /** * Set expiry for hash field using relative time to expire (seconds). * * @param key hash * @param seconds time to expire * @param condition can be NX, XX, GT or LT * @param fields * @return integer-reply: 1 if the timeout was set, 0 otherwise */ List hexpire(byte[] key, long seconds, ExpiryOption condition, byte[]... fields); /** * Set expiry for hash field using relative time to expire (milliseconds). * * @param key hash * @param milliseconds time to expire * @param fields * @return integer-reply: 1 if the timeout was set, 0 otherwise */ List hpexpire(byte[] key, long milliseconds, byte[]... fields); /** * Set expiry for hash field using relative time to expire (milliseconds). * * @param key hash * @param milliseconds time to expire * @param condition can be NX, XX, GT or LT * @param fields * @return integer-reply: 1 if the timeout was set, 0 otherwise */ List hpexpire(byte[] key, long milliseconds, ExpiryOption condition, byte[]... fields); /** * Set expiry for hash field using an absolute Unix timestamp (seconds). * * @param key hash * @param unixTimeSeconds time to expire * @param fields * @return integer-reply: 1 if the timeout was set, 0 otherwise */ List hexpireAt(byte[] key, long unixTimeSeconds, byte[]... fields); /** * Set expiry for hash field using an absolute Unix timestamp (seconds). * * @param key hash * @param unixTimeSeconds time to expire * @param condition can be NX, XX, GT or LT * @param fields * @return integer-reply: 1 if the timeout was set, 0 otherwise */ List hexpireAt(byte[] key, long unixTimeSeconds, ExpiryOption condition, byte[]... fields); /** * Set expiry for hash field using an absolute Unix timestamp (milliseconds). * * @param key hash * @param unixTimeMillis time to expire * @param fields * @return integer-reply: 1 if the timeout was set, 0 otherwise */ List hpexpireAt(byte[] key, long unixTimeMillis, byte[]... fields); /** * Set expiry for hash field using an absolute Unix timestamp (milliseconds). * * @param key hash * @param unixTimeMillis time to expire * @param condition can be NX, XX, GT or LT * @param fields * @return integer-reply: 1 if the timeout was set, 0 otherwise */ List hpexpireAt(byte[] key, long unixTimeMillis, ExpiryOption condition, byte[]... fields); /** * Returns the expiration time of a hash field as a Unix timestamp, in seconds. * * @param key hash * @param fields * @return Expiration Unix timestamp in seconds; * or -1 if the field exists but has no associated expire or -2 if the field does not exist. */ List hexpireTime(byte[] key, byte[]... fields); /** * Returns the expiration time of a hash field as a Unix timestamp, in milliseconds. * * @param key hash * @param fields * @return Expiration Unix timestamp in milliseconds; * or -1 if the field exists but has no associated expire or -2 if the field does not exist. */ List hpexpireTime(byte[] key, byte[]... fields); /** * Returns the TTL in seconds of a hash field. * * @param key hash * @param fields * @return TTL in seconds; * or -1 if the field exists but has no associated expire or -2 if the field does not exist. */ List httl(byte[] key, byte[]... fields); /** * Returns the TTL in milliseconds of a hash field. * * @param key hash * @param fields * @return TTL in milliseconds; * or -1 if the field exists but has no associated expire or -2 if the field does not exist. */ List hpttl(byte[] key, byte[]... fields); /** * Removes the expiration time for each specified field. * * @param key hash * @param fields * @return integer-reply: 1 if the expiration time was removed, * or -1 if the field exists but has no associated expire or -2 if the field does not exist. */ List hpersist(byte[] key, byte[]... fields); } ================================================ FILE: src/main/java/redis/clients/jedis/commands/HashCommands.java ================================================ package redis.clients.jedis.commands; import java.util.List; import java.util.Map; import java.util.Set; import redis.clients.jedis.args.ExpiryOption; import redis.clients.jedis.params.HGetExParams; import redis.clients.jedis.params.HSetExParams; import redis.clients.jedis.params.ScanParams; import redis.clients.jedis.resps.ScanResult; public interface HashCommands { long hset(String key, String field, String value); long hset(String key, Map hash); /** * Sets the specified fields in the hash stored at key to the specified values with additional parameters, * and optionally set their expiration. Use `HSetExParams` object to specify expiration parameters. * This command can overwrite any existing fields in the hash. * If key does not exist, a new key holding a hash is created. * * @param key the key of the hash * @param params the parameters for the HSETEX command * @param field the field in the hash * @param value the value to set * @return 0 if no fields were set, 1 if all the fields were set * * @see HSetExParams */ long hsetex(String key, HSetExParams params, String field, String value); /** * Sets the specified fields in the hash stored at key to the specified values with additional parameters, * and optionally set their expiration. Use `HSetExParams` object to specify expiration parameters. * This command can overwrite any existing fields in the hash. * If key does not exist, a new key holding a hash is created. * * @param key the key of the hash * @param params the parameters for the HSETEX command * @param hash the map containing field-value pairs to set in the hash * @return 0 if no fields were set, 1 if all the fields were set * * @see HSetExParams */ long hsetex(String key, HSetExParams params, Map hash); String hget(String key, String field); /** * Retrieves the values associated with the specified fields in a hash stored at the given key * and optionally sets their expiration. Use `HGetExParams` object to specify expiration parameters. * * @param key the key of the hash * @param params additional parameters for the HGETEX command * @param fields the fields whose values are to be retrieved * @return a list of the value associated with each field or nil if the field doesn’t exist. * * @see HGetExParams */ List hgetex(String key, HGetExParams params, String... fields); /** * Retrieves the values associated with the specified fields in the hash stored at the given key * and then deletes those fields from the hash. * * @param key the key of the hash * @param fields the fields whose values are to be retrieved and then deleted * @return a list of values associated with the specified fields before they were deleted */ List hgetdel(String key, String... fields); long hsetnx(String key, String field, String value); /** * @deprecated Use {@link HashCommands#hset(String, Map)}. * Deprecated in Jedis 7.3.0. Mirrors Redis deprecation since 4.0.0. */ @Deprecated String hmset(String key, Map hash); List hmget(String key, String... fields); long hincrBy(String key, String field, long value); double hincrByFloat(String key, String field, double value); boolean hexists(String key, String field); long hdel(String key, String... field); long hlen(String key); Set hkeys(String key); List hvals(String key); Map hgetAll(String key); String hrandfield(String key); List hrandfield(String key, long count); List> hrandfieldWithValues(String key, long count); default ScanResult> hscan(String key, String cursor) { return hscan(key, cursor, new ScanParams()); } ScanResult> hscan(String key, String cursor, ScanParams params); default ScanResult hscanNoValues(String key, String cursor) { return hscanNoValues(key, cursor, new ScanParams()); } ScanResult hscanNoValues(String key, String cursor, ScanParams params); long hstrlen(String key, String field); /** * Set expiry for hash field using relative time to expire (seconds). * * @param key hash * @param seconds time to expire * @param fields * @return integer-reply: 1 if the timeout was set, 0 otherwise */ List hexpire(String key, long seconds, String... fields); /** * Set expiry for hash field using relative time to expire (seconds). * * @param key hash * @param seconds time to expire * @param condition can be NX, XX, GT or LT * @param fields * @return integer-reply: 1 if the timeout was set, 0 otherwise */ List hexpire(String key, long seconds, ExpiryOption condition, String... fields); /** * Set expiry for hash field using relative time to expire (milliseconds). * * @param key hash * @param milliseconds time to expire * @param fields * @return integer-reply: 1 if the timeout was set, 0 otherwise */ List hpexpire(String key, long milliseconds, String... fields); /** * Set expiry for hash field using relative time to expire (milliseconds). * * @param key hash * @param milliseconds time to expire * @param condition can be NX, XX, GT or LT * @param fields * @return integer-reply: 1 if the timeout was set, 0 otherwise */ List hpexpire(String key, long milliseconds, ExpiryOption condition, String... fields); /** * Set expiry for hash field using an absolute Unix timestamp (seconds). * * @param key hash * @param unixTimeSeconds time to expire * @param fields * @return integer-reply: 1 if the timeout was set, 0 otherwise */ List hexpireAt(String key, long unixTimeSeconds, String... fields); /** * Set expiry for hash field using an absolute Unix timestamp (seconds). * * @param key hash * @param unixTimeSeconds time to expire * @param condition can be NX, XX, GT or LT * @param fields * @return integer-reply: 1 if the timeout was set, 0 otherwise */ List hexpireAt(String key, long unixTimeSeconds, ExpiryOption condition, String... fields); /** * Set expiry for hash field using an absolute Unix timestamp (milliseconds). * * @param key hash * @param unixTimeMillis time to expire * @param fields * @return integer-reply: 1 if the timeout was set, 0 otherwise */ List hpexpireAt(String key, long unixTimeMillis, String... fields); /** * Set expiry for hash field using an absolute Unix timestamp (milliseconds). * * @param key hash * @param unixTimeMillis time to expire * @param condition can be NX, XX, GT or LT * @param fields * @return integer-reply: 1 if the timeout was set, 0 otherwise */ List hpexpireAt(String key, long unixTimeMillis, ExpiryOption condition, String... fields); /** * Returns the expiration time of a hash field as a Unix timestamp, in seconds. * * @param key hash * @param fields * @return Expiration Unix timestamp in seconds; * or -1 if the field exists but has no associated expire or -2 if the field does not exist. */ List hexpireTime(String key, String... fields); /** * Returns the expiration time of a hash field as a Unix timestamp, in milliseconds. * * @param key hash * @param fields * @return Expiration Unix timestamp in milliseconds; * or -1 if the field exists but has no associated expire or -2 if the field does not exist. */ List hpexpireTime(String key, String... fields); /** * Returns the TTL in seconds of a hash field. * * @param key hash * @param fields * @return TTL in seconds; * or -1 if the field exists but has no associated expire or -2 if the field does not exist. */ List httl(String key, String... fields); /** * Returns the TTL in milliseconds of a hash field. * * @param key hash * @param fields * @return TTL in milliseconds; * or -1 if the field exists but has no associated expire or -2 if the field does not exist. */ List hpttl(String key, String... fields); /** * Removes the expiration time for each specified field. * * @param key hash * @param fields * @return integer-reply: 1 if the expiration time was removed, * or -1 if the field exists but has no associated expire or -2 if the field does not exist. */ List hpersist(String key, String... fields); } ================================================ FILE: src/main/java/redis/clients/jedis/commands/HashPipelineBinaryCommands.java ================================================ package redis.clients.jedis.commands; import java.util.List; import java.util.Map; import java.util.Set; import redis.clients.jedis.Response; import redis.clients.jedis.args.ExpiryOption; import redis.clients.jedis.params.HGetExParams; import redis.clients.jedis.params.HSetExParams; import redis.clients.jedis.params.ScanParams; import redis.clients.jedis.resps.ScanResult; public interface HashPipelineBinaryCommands { Response hset(byte[] key, byte[] field, byte[] value); Response hset(byte[] key, Map hash); Response hsetex(byte[] key, HSetExParams params, byte[] field, byte[] value); Response hsetex(byte[] key, HSetExParams params, Map hash); Response hget(byte[] key, byte[] field); Response> hgetex(byte[] key, HGetExParams params, byte[]... fields); Response> hgetdel(byte[] key, byte[]... fields); Response hsetnx(byte[] key, byte[] field, byte[] value); Response hmset(byte[] key, Map hash); Response> hmget(byte[] key, byte[]... fields); Response hincrBy(byte[] key, byte[] field, long value); Response hincrByFloat(byte[] key, byte[] field, double value); Response hexists(byte[] key, byte[] field); Response hdel(byte[] key, byte[]... field); Response hlen(byte[] key); Response> hkeys(byte[] key); Response> hvals(byte[] key); Response> hgetAll(byte[] key); Response hrandfield(byte[] key); Response> hrandfield(byte[] key, long count); Response>> hrandfieldWithValues(byte[] key, long count); default Response>> hscan(byte[] key, byte[] cursor) { return hscan(key, cursor, new ScanParams()); } Response>> hscan(byte[] key, byte[] cursor, ScanParams params); default Response> hscanNoValues(byte[] key, byte[] cursor) { return hscanNoValues(key, cursor, new ScanParams()); } Response> hscanNoValues(byte[] key, byte[] cursor, ScanParams params); Response hstrlen(byte[] key, byte[] field); Response> hexpire(byte[] key, long seconds, byte[]... fields); Response> hexpire(byte[] key, long seconds, ExpiryOption condition, byte[]... fields); Response> hpexpire(byte[] key, long milliseconds, byte[]... fields); Response> hpexpire(byte[] key, long milliseconds, ExpiryOption condition, byte[]... fields); Response> hexpireAt(byte[] key, long unixTimeSeconds, byte[]... fields); Response> hexpireAt(byte[] key, long unixTimeSeconds, ExpiryOption condition, byte[]... fields); Response> hpexpireAt(byte[] key, long unixTimeMillis, byte[]... fields); Response> hpexpireAt(byte[] key, long unixTimeMillis, ExpiryOption condition, byte[]... fields); Response> hexpireTime(byte[] key, byte[]... fields); Response> hpexpireTime(byte[] key, byte[]... fields); Response> httl(byte[] key, byte[]... fields); Response> hpttl(byte[] key, byte[]... fields); Response> hpersist(byte[] key, byte[]... fields); } ================================================ FILE: src/main/java/redis/clients/jedis/commands/HashPipelineCommands.java ================================================ package redis.clients.jedis.commands; import java.util.List; import java.util.Map; import java.util.Set; import redis.clients.jedis.Response; import redis.clients.jedis.args.ExpiryOption; import redis.clients.jedis.params.HGetExParams; import redis.clients.jedis.params.HSetExParams; import redis.clients.jedis.params.ScanParams; import redis.clients.jedis.resps.ScanResult; public interface HashPipelineCommands { Response hset(String key, String field, String value); Response hset(String key, Map hash); Response hsetex(String key, HSetExParams params, String field, String value); Response hsetex(String key, HSetExParams params, Map hash); Response hget(String key, String field); Response> hgetex(String key, HGetExParams params, String... fields); Response> hgetdel(String key, String... fields); Response hsetnx(String key, String field, String value); Response hmset(String key, Map hash); Response> hmget(String key, String... fields); Response hincrBy(String key, String field, long value); Response hincrByFloat(String key, String field, double value); Response hexists(String key, String field); Response hdel(String key, String... field); Response hlen(String key); Response> hkeys(String key); Response> hvals(String key); Response> hgetAll(String key); Response hrandfield(String key); Response> hrandfield(String key, long count); Response>> hrandfieldWithValues(String key, long count); default Response>> hscan(String key, String cursor) { return hscan(key, cursor, new ScanParams()); } Response>> hscan(String key, String cursor, ScanParams params); default Response> hscanNoValues(String key, String cursor) { return hscanNoValues(key, cursor, new ScanParams()); } Response> hscanNoValues(String key, String cursor, ScanParams params); Response hstrlen(String key, String field); Response> hexpire(String key, long seconds, String... fields); Response> hexpire(String key, long seconds, ExpiryOption condition, String... fields); Response> hpexpire(String key, long milliseconds, String... fields); Response> hpexpire(String key, long milliseconds, ExpiryOption condition, String... fields); Response> hexpireAt(String key, long unixTimeSeconds, String... fields); Response> hexpireAt(String key, long unixTimeSeconds, ExpiryOption condition, String... fields); Response> hpexpireAt(String key, long unixTimeMillis, String... fields); Response> hpexpireAt(String key, long unixTimeMillis, ExpiryOption condition, String... fields); Response> hexpireTime(String key, String... fields); Response> hpexpireTime(String key, String... fields); Response> httl(String key, String... fields); Response> hpttl(String key, String... fields); Response> hpersist(String key, String... fields); } ================================================ FILE: src/main/java/redis/clients/jedis/commands/HyperLogLogBinaryCommands.java ================================================ package redis.clients.jedis.commands; public interface HyperLogLogBinaryCommands { long pfadd(byte[] key, byte[]... elements); String pfmerge(byte[] destkey, byte[]... sourcekeys); long pfcount(byte[] key); long pfcount(byte[]... keys); } ================================================ FILE: src/main/java/redis/clients/jedis/commands/HyperLogLogCommands.java ================================================ package redis.clients.jedis.commands; public interface HyperLogLogCommands { long pfadd(String key, String... elements); String pfmerge(String destkey, String... sourcekeys); long pfcount(String key); long pfcount(String... keys); } ================================================ FILE: src/main/java/redis/clients/jedis/commands/HyperLogLogPipelineBinaryCommands.java ================================================ package redis.clients.jedis.commands; import redis.clients.jedis.Response; public interface HyperLogLogPipelineBinaryCommands { Response pfadd(byte[] key, byte[]... elements); Response pfmerge(byte[] destkey, byte[]... sourcekeys); Response pfcount(byte[] key); Response pfcount(byte[]... keys); } ================================================ FILE: src/main/java/redis/clients/jedis/commands/HyperLogLogPipelineCommands.java ================================================ package redis.clients.jedis.commands; import redis.clients.jedis.Response; public interface HyperLogLogPipelineCommands { Response pfadd(String key, String... elements); Response pfmerge(String destkey, String... sourcekeys); Response pfcount(String key); Response pfcount(String... keys); } ================================================ FILE: src/main/java/redis/clients/jedis/commands/JedisBinaryCommands.java ================================================ package redis.clients.jedis.commands; public interface JedisBinaryCommands extends KeyBinaryCommands, StringBinaryCommands, ListBinaryCommands, HashBinaryCommands, SetBinaryCommands, SortedSetBinaryCommands, GeoBinaryCommands, HyperLogLogBinaryCommands, StreamBinaryCommands, ScriptingKeyBinaryCommands, FunctionBinaryCommands, VectorSetBinaryCommands { } ================================================ FILE: src/main/java/redis/clients/jedis/commands/JedisCommands.java ================================================ package redis.clients.jedis.commands; public interface JedisCommands extends KeyCommands, StringCommands, ListCommands, HashCommands, SetCommands, SortedSetCommands, GeoCommands, HyperLogLogCommands, StreamCommands, ScriptingKeyCommands, FunctionCommands, VectorSetCommands { } ================================================ FILE: src/main/java/redis/clients/jedis/commands/KeyBinaryCommands.java ================================================ package redis.clients.jedis.commands; import java.util.List; import java.util.Set; import redis.clients.jedis.args.ExpiryOption; import redis.clients.jedis.params.MigrateParams; import redis.clients.jedis.params.RestoreParams; import redis.clients.jedis.annots.Experimental; import redis.clients.jedis.util.CompareCondition; import redis.clients.jedis.params.ScanParams; import redis.clients.jedis.params.SortingParams; import redis.clients.jedis.resps.ScanResult; public interface KeyBinaryCommands { boolean exists(byte[] key); long exists(byte[]... keys); long persist(byte[] key); String type(byte[] key); byte[] dump(byte[] key); String restore(byte[] key, long ttl, byte[] serializedValue); String restore(byte[] key, long ttl, byte[] serializedValue, RestoreParams params); long expire(byte[] key, long seconds); long expire(byte[] key, long seconds, ExpiryOption expiryOption); long pexpire(byte[] key, long milliseconds); long pexpire(byte[] key, long milliseconds, ExpiryOption expiryOption); long expireTime(byte[] key); long pexpireTime(byte[] key); long expireAt(byte[] key, long unixTime); long expireAt(byte[] key, long unixTime, ExpiryOption expiryOption); long pexpireAt(byte[] key, long millisecondsTimestamp); long pexpireAt(byte[] key, long millisecondsTimestamp, ExpiryOption expiryOption); long ttl(byte[] key); long pttl(byte[] key); long touch(byte[] key); long touch(byte[]... keys); List sort(byte[] key); List sort(byte[] key, SortingParams sortingParams); long del(byte[] key); /** * Experimental: Compare-and-delete guarded by value/digest condition. */ @Experimental long delex(byte[] key, CompareCondition condition); /** Returns the 64-bit XXH3 digest hex (ASCII bytes) of the string value stored at key, or null if missing. */ @Experimental byte[] digestKey(byte[] key); long del(byte[]... keys); long unlink(byte[] key); long unlink(byte[]... keys); boolean copy(byte[] srcKey, byte[] dstKey, boolean replace); String rename(byte[] oldkey, byte[] newkey); long renamenx(byte[] oldkey, byte[] newkey); long sort(byte[] key, SortingParams sortingParams, byte[] dstkey); long sort(byte[] key, byte[] dstkey); List sortReadonly(byte[] key, SortingParams sortingParams); Long memoryUsage(byte[] key); Long memoryUsage(byte[] key, int samples); Long objectRefcount(byte[] key); byte[] objectEncoding(byte[] key); Long objectIdletime(byte[] key); Long objectFreq(byte[] key); String migrate(String host, int port, byte[] key, int timeout); String migrate(String host, int port, int timeout, MigrateParams params, byte[]... keys); Set keys(byte[] pattern); ScanResult scan(byte[] cursor); ScanResult scan(byte[] cursor, ScanParams params); ScanResult scan(byte[] cursor, ScanParams params, byte[] type); byte[] randomBinaryKey(); } ================================================ FILE: src/main/java/redis/clients/jedis/commands/KeyCommands.java ================================================ package redis.clients.jedis.commands; import java.util.List; import java.util.Set; import redis.clients.jedis.args.ExpiryOption; import redis.clients.jedis.params.MigrateParams; import redis.clients.jedis.params.RestoreParams; import redis.clients.jedis.annots.Experimental; import redis.clients.jedis.util.CompareCondition; import redis.clients.jedis.params.ScanParams; import redis.clients.jedis.params.SortingParams; import redis.clients.jedis.resps.ScanResult; public interface KeyCommands { /** * Exists Command * Test if the specified key exist. *

* Time complexity: O(1) * @param key * @return {@code true} if the key exists, {@code false} otherwise */ boolean exists(String key); /** * Exists Command * Test if the specified keys exist. *

* Time complexity: O(N) * @param keys * @return The number of keys that exist from those specified as {@code keys}. */ long exists(String... keys); /** * Persist Command * Undo a {@link KeyCommands#expire(String, long) expire} at turning the expire key into a normal key. *

* Time complexity: O(1) * @param key * @return 1 if the key is now persist. 0 otherwise (only happens when key not set) */ long persist(String key); /** * Type Command * Return the type of the value stored at key in form of a string. The type can be one of "none", * "string", "list", "set". "none" is returned if the key does not exist. *

* Time complexity: O(1) * @param key * @return "none" if the key does not exist, "string" if the key contains a String value, "list" * if the key contains a List value, "set" if the key contains a Set value, "zset" if the key * contains a Sorted Set value, "hash" if the key contains a Hash value */ String type(String key); /** * Dump Command * Serialize the value stored at key in a Redis-specific format and return it to the user. *

* Time complexity: O(1) to access the key and additional O(N*M) to serialize it where N is * the number of Redis objects composing the value and M their average size. * @param key * @return The serialized value */ byte[] dump(String key); /** * Restore Command * Create a key associated with a value that is obtained by deserializing the provided serialized * value (obtained via {@link KeyCommands#dump(String) DUMP}). *

* Time complexity: O(1) to access the key and additional O(N*M) to serialize it where N is * the number of Redis objects composing the value and M their average size. * @param key * @param ttl If ttl is 0 the key is created without any expire, otherwise the specified expire * time (in milliseconds) is set. * @param serializedValue * @return OK */ String restore(String key, long ttl, byte[] serializedValue); /** * Restore Command * Create a key associated with a value that is obtained by deserializing the provided serialized * value (obtained via {@link KeyCommands#dump(String) DUMP}). *

* Time complexity: O(1) to access the key and additional O(N*M) to serialize it where N is * the number of Redis objects composing the value and M their average size. * @param key * @param ttl If ttl is 0 the key is created without any expire, otherwise the specified expire * time (in milliseconds) is set. * @param serializedValue * @param params {@link RestoreParams} * @return OK */ String restore(String key, long ttl, byte[] serializedValue, RestoreParams params); /** * Expire Command * Set a timeout on the specified key. After the timeout the key will be automatically deleted by * the server. A key with an associated timeout is said to be volatile in Redis terminology. *

* Volatile keys are stored on disk like the other keys, the timeout is persistent too like all * the other aspects of the dataset. Saving a dataset containing expires and stopping the server * does not stop the flow of time as Redis stores on disk the time when the key will no longer be * available as Unix time, and not the remaining seconds. *

* Since Redis 2.1.3 you can update the value of the timeout of a key already having an expire * set. It is also possible to undo the expire at all turning the key into a normal key using the * {@link KeyCommands#persist(String) PERSIST} command. *

* Time complexity: O(1) * @param key * @param seconds time to expire * @return 1 if the timeout was set, 0 otherwise. Since the key already has an associated timeout * (this may happen only in Redis versions < 2.1.3, Redis >= 2.1.3 will happily update the timeout), * or the key does not exist. */ long expire(String key, long seconds); /** * Similar to {@link KeyCommands#expire(String, long) EXPIRE} but with optional expiry setting. * @see KeyCommands#expire(String, long) * @param key * @param seconds time to expire * @param expiryOption can be NX, XX, GT or LT * @return 1 if the timeout was set, 0 otherwise. Since the key already has an associated timeout * (this may happen only in Redis versions < 2.1.3, Redis >= 2.1.3 will happily update the timeout), * or the key does not exist. */ long expire(String key, long seconds, ExpiryOption expiryOption); /** * PExpire Command * This command works exactly like {@link KeyCommands#expire(String, long) EXPIRE} but the time * to live of the key is specified in milliseconds instead of seconds. *

* Time complexity: O(1) * @param key * @param milliseconds time to expire * @return 1 if the timeout was set, 0 otherwise. * e.g. key doesn't exist, or operation skipped due to the provided arguments. */ long pexpire(String key, long milliseconds); /** * Similar to {@link KeyCommands#pexpire(String, long) EXPIRE} but with optional expiry setting. * @see KeyCommands#pexpire(String, long) * @param key * @param milliseconds time to expire * @param expiryOption can be NX, XX, GT or LT * @return 1 if the timeout was set, 0 otherwise. * e.g. key doesn't exist, or operation skipped due to the provided arguments. */ long pexpire(String key, long milliseconds, ExpiryOption expiryOption); /** * ExpireTime Command * Returns the absolute Unix timestamp (since January 1, 1970) in seconds at which the given key will expire. *

* The command returns -1 if the key exists but has no associated expiration time, and -2 if the key does not exist. *

* Time complexity: O(1) * @param key * @return Expiration Unix timestamp in seconds, or a negative value in order to signal an error: * -1 if the key exists but has no associated expiration time, and -2 if the key does not exist. */ long expireTime(String key); /** * PExpireTime Command * Similar to {@link KeyCommands#expireTime(String) EXPIRETIME} but returns the absolute Unix expiration * timestamp in milliseconds instead of seconds. *

* Time complexity: O(1) * @see KeyCommands#expireTime(String) * @param key * @return Expiration Unix timestamp in milliseconds, or a negative value in order to signal an error: * -1 if the key exists but has no associated expiration time, and -2 if the key does not exist. */ long pexpireTime(String key); /** * ExpireAt Command * EXPIREAT works exactly like {@link KeyCommands#expire(String, long) EXPIRE} but instead to get the * number of seconds representing the Time To Live of the key as a second argument (that is a * relative way of specifying the TTL), it takes an absolute one in the form of a UNIX timestamp * (Number of seconds elapsed since 1 Gen 1970). *

* EXPIREAT was introduced in order to implement the Append Only File persistence mode so that * EXPIRE commands are automatically translated into EXPIREAT commands for the append only file. * Of course EXPIREAT can also used by programmers that need a way to simply specify that a given * key should expire at a given time in the future. *

* Time complexity: O(1) * @param key * @param unixTime time to expire * @return 1 if the timeout was set, 0 otherwise. * e.g. key doesn't exist, or operation skipped due to the provided arguments. */ long expireAt(String key, long unixTime); /** * ExpireAt Command * Similar to {@link KeyCommands#expireAt(String, long) EXPIREAT} but with {@code ExpiryOption}. * @see KeyCommands#expireAt(String, long) * @param key * @param unixTime time to expire * @param expiryOption can be NX, XX, GT or LT * @return 1 if the timeout was set, 0 otherwise. * e.g. key doesn't exist, or operation skipped due to the provided arguments. */ long expireAt(String key, long unixTime, ExpiryOption expiryOption); /** * PExpireAt Command * This command works exactly like {@link KeyCommands#expireAt(String, long) EXPIREAT} but * Unix time at which the key will expire is specified in milliseconds instead of seconds. *

* Time complexity: O(1) * @param key * @param millisecondsTimestamp time to expire * @return 1 if the timeout was set, 0 otherwise. * e.g. key doesn't exist, or operation skipped due to the provided arguments. */ long pexpireAt(String key, long millisecondsTimestamp); /** * ExpireAt Command * Similar to {@link KeyCommands#pexpireAt(String, long) PEXPIREAT} but with {@code ExpiryOption}. * @see KeyCommands#pexpireAt(String, long) * @param key * @param millisecondsTimestamp time to expire * @param expiryOption can be NX, XX, GT or LT * @return 1 if the timeout was set, 0 otherwise. * e.g. key doesn't exist, or operation skipped due to the provided arguments. */ long pexpireAt(String key, long millisecondsTimestamp, ExpiryOption expiryOption); /** * TTL Command * The TTL command returns the remaining time to live in seconds of a key that has an * {@link KeyCommands#expire(String, long) EXPIRE} set. This introspection capability allows a Redis * connection to check how many seconds a given key will continue to be part of the dataset. *

* Time complexity: O(1) * @param key * @return TTL in seconds, or a negative value in order to signal an error */ long ttl(String key); /** * PTTL Command * The PTTL command returns the remaining time to live in milliseconds of a key that has an * {@link KeyCommands#expire(String, long) EXPIRE} set. *

* Time complexity: O(1) * @param key * @return TTL in milliseconds, or a negative value in order to signal an error */ long pttl(String key); /** * Touch Command * Alters the last access time of a key. A key is ignored if it does not exist. *

* Time complexity: O(N) where N is the number of keys that will be touched. * @param key * @return The number of keys that were touched */ long touch(String key); /** * Touch Command * Alters the last access time of a key(s). A key is ignored if it does not exist. *

* Time complexity: O(N) where N is the number of keys that will be touched. * @param keys * @return The number of keys that were touched */ long touch(String... keys); /** * Sort Command * Sort a Set or a List. *

* Sort the elements contained in the List, Set, or Sorted Set values at key. By default, sorting is * numeric with elements being compared as double precision floating point numbers. This is the * simplest form of SORT. * @see KeyCommands#sort(String, SortingParams) * @param key * @return Assuming the Set/List at key contains a list of numbers, the return value will be the * list of numbers ordered from the smallest to the biggest number. */ List sort(String key); /** * Similar to {@link KeyCommands#sort(String) SORT} but store the result in {@code dstkey}. * @see KeyCommands#sort(String) * @param key * @param dstkey * @return The number of elements stored at dstkey. */ long sort(String key, String dstkey); /** * Sort a Set or a List accordingly to the specified parameters. *

* examples: *

* Given are the following sets and key/values: * *

   * x = [1, 2, 3]
   * y = [a, b, c]
   *
   * k1 = z
   * k2 = y
   * k3 = x
   *
   * w1 = 9
   * w2 = 8
   * w3 = 7
   * 
* * Sort Order: * *
   * sort(x) or sort(x, sp.asc())
   * -> [1, 2, 3]
   *
   * sort(x, sp.desc())
   * -> [3, 2, 1]
   *
   * sort(y)
   * -> [c, a, b]
   *
   * sort(y, sp.alpha())
   * -> [a, b, c]
   *
   * sort(y, sp.alpha().desc())
   * -> [c, a, b]
   * 
* * Limit (e.g. for Pagination): * *
   * sort(x, sp.limit(0, 2))
   * -> [1, 2]
   *
   * sort(y, sp.alpha().desc().limit(1, 2))
   * -> [b, a]
   * 
* * Sorting by external keys: * *
   * sort(x, sb.by(w*))
   * -> [3, 2, 1]
   *
   * sort(x, sb.by(w*).desc())
   * -> [1, 2, 3]
   * 
* * Getting external keys: * *
   * sort(x, sp.by(w*).get(k*))
   * -> [x, y, z]
   *
   * sort(x, sp.by(w*).get(#).get(k*))
   * -> [3, x, 2, y, 1, z]
   * 
* @param key * @param sortingParameters {@link SortingParams} * @return A list of sorted elements */ List sort(String key, SortingParams sortingParameters); /** * Similar to {@link KeyCommands#sort(String, SortingParams) SORT} but store the result in {@code dstkey}. * @see KeyCommands#sort(String, SortingParams) * @param key * @param sortingParameters {@link SortingParams} * @param dstkey * @return The number of elements stored at dstkey */ long sort(String key, SortingParams sortingParameters, String dstkey); /** * Read-only variant of the {@link KeyCommands#sort(String, SortingParams) SORT} command. * It is exactly like the original SORT but refuses the STORE option and can safely be used in read-only replicas. * @param key the key to sort * @param sortingParams {@link SortingParams} * @return list of sorted elements. */ List sortReadonly(String key, SortingParams sortingParams); /** * Del Command * Remove the specified key. If a given key does not exist, no operation is performed. *

* Time complexity: O(1) * @param key * @return 1 if the key was removed, 0 if the key does not exist */ long del(String key); /** * Remove the specified keys. If a given key does not exist, no operation is performed. *

* Time complexity: O(N) * @param keys * @return An integer greater than 0 if one or more keys were removed, 0 if none of the specified keys existed */ long del(String... keys); /** * Experimental: Compare-and-delete guarded by value/digest condition. */ @Experimental long delex(String key, CompareCondition condition); /** * Compute and return the 64-bit XXH3 digest hex of the string value stored at key. Returns null * if key does not exist. */ @Experimental String digestKey(String key); /** * Unlink Command * This command is very similar to {@link KeyCommands#del(String) DEL}: it removes the specified key. * Just like DEL a key is ignored if it does not exist. However, the command performs the actual * memory reclaiming in a different thread, so it is not blocking, while DEL is. This is where the * command name comes from: the command just unlinks the keys from the keyspace. The actual removal * will happen later asynchronously. *

* Time complexity: O(1) for each key removed regardless of its size. Then the command does O(N) * work in a different thread in order to reclaim memory, where N is the number of allocations the * deleted objects where composed of. * @param key * @return The number of keys that were unlinked */ long unlink(String key); /** * Similar to {@link KeyCommands#unlink(String) SORT} but can be used with multiple keys. * @see KeyCommands#unlink(String) * @param keys * @return The number of keys that were unlinked */ long unlink(String... keys); /** * Copy Command * Copy the value stored at the source key to the destination key. * @param srcKey the source key. * @param dstKey the destination key. * @param replace removes the destination key before copying the value to it, in order to avoid error. * @return {@code true} if source was copied, {@code false} otherwise */ boolean copy(String srcKey, String dstKey, boolean replace); /** * Rename Command * Atomically renames the key {@code oldkey} to {@code newkey}. If the source and destination name are the same an * error is returned. If {@code newkey} already exists it is overwritten. *

* Time complexity: O(1) * @param oldkey * @param newkey * @return OK */ String rename(String oldkey, String newkey); /** * RenameNX Command * Rename oldkey into newkey but fails if the destination key newkey already exists. *

* Time complexity: O(1) * @param oldkey * @param newkey * @return 1 if the key was renamed, 0 if the target key already exist */ long renamenx(String oldkey, String newkey); /** * Memory Usage Command * Report the number of bytes that a key and its value require to be stored in RAM. *

* Time complexity: O(1) * @param key * @return The memory usage in bytes */ Long memoryUsage(String key); /** * Memory Usage Command * Report the number of bytes that a key and its value require to be stored in RAM. *

* Time complexity: O(1) * @param key * @param samples the number of sampled nested values. By default, this option is set to 5. * To sample the all the nested values, use 0. * @return The memory usage in bytes */ Long memoryUsage(String key, int samples); /** * Object Refcount Command * Return the reference count of the stored at key. *

* Time complexity: O(1) * @param key * @return The number of references */ Long objectRefcount(String key); /** * Object Encoding Command * Return the internal encoding for the Redis object stored at key. *

* Time complexity: O(1) * @param key * @return The encoding of the object */ String objectEncoding(String key); /** * Object IdleTime Command * Return the time in seconds since the last access to the value stored at key. *

* Time complexity: O(1) * @param key * @return The idle time in seconds */ Long objectIdletime(String key); /** * Object Freq Command * Return the logarithmic access frequency counter of a Redis object stored at key. *

* Time complexity: O(1) * @param key * @return The counter's value */ Long objectFreq(String key); /** * Migrate Command * Atomically transfer a key from a source Redis instance to a destination Redis instance. * On success the key is deleted from the original instance and is guaranteed to exist in * the target instance. * @param host * @param port * @param key * @param timeout the maximum idle time in any moment of the communication with the * destination instance in milliseconds. * @return OK on success, or NOKEY if no keys were found in the source instance. */ String migrate(String host, int port, String key, int timeout); /** * Migrate Command * Atomically transfer a key from a source Redis instance to a destination Redis instance. * On success the key is deleted from the original instance and is guaranteed to exist in * the target instance. * @param host * @param port * @param timeout the maximum idle time in any moment of the communication with the * destination instance in milliseconds. * @param params {@link MigrateParams} * @param keys * @return OK on success, or NOKEY if no keys were found in the source instance. */ String migrate(String host, int port, int timeout, MigrateParams params, String... keys); /** * Keys Command * Returns all the keys matching the glob-style pattern as space separated strings. For example if * you have in the database the keys "foo" and "foobar" the command "KEYS foo*" will return * "foo foobar". *

* Note that while the time complexity for this operation is O(n) the constant times are pretty * low. For example Redis running on an entry level laptop can scan a 1 million keys database in * 40 milliseconds. Still it's better to consider this one of the slow commands that may ruin * the DB performance if not used with care. *

* In other words this command is intended only for debugging and special operations like creating * a script to change the DB schema. Don't use it in your normal code. Use Redis Sets in order to * group together a subset of objects. *

* Glob style patterns examples: *

    *
  • h?llo will match hello hallo hhllo *
  • h*llo will match hllo heeeello *
  • h[ae]llo will match hello and hallo, but not hillo *
*

* Use \ to escape special chars if you want to match them verbatim. *

* Time complexity: O(n) (with n being the number of keys in the DB, and assuming keys and pattern * of limited length) * @param pattern * @return List of keys matching the pattern. */ Set keys(String pattern); ScanResult scan(String cursor); ScanResult scan(String cursor, ScanParams params); ScanResult scan(String cursor, ScanParams params, String type); /** * RandomKey Command * Return a randomly selected key from the currently selected DB. *

* Time complexity: O(1) * @return The random key, or {@code nil} when the database is empty */ String randomKey(); } ================================================ FILE: src/main/java/redis/clients/jedis/commands/KeyPipelineBinaryCommands.java ================================================ package redis.clients.jedis.commands; import java.util.List; import java.util.Set; import redis.clients.jedis.Response; import redis.clients.jedis.args.ExpiryOption; import redis.clients.jedis.params.MigrateParams; import redis.clients.jedis.params.RestoreParams; import redis.clients.jedis.params.ScanParams; import redis.clients.jedis.params.SortingParams; import redis.clients.jedis.resps.ScanResult; import redis.clients.jedis.util.CompareCondition; public interface KeyPipelineBinaryCommands { Response exists(byte[] key); Response exists(byte[]... keys); Response persist(byte[] key); Response type(byte[] key); Response dump(byte[] key); Response restore(byte[] key, long ttl, byte[] serializedValue); Response restore(byte[] key, long ttl, byte[] serializedValue, RestoreParams params); Response expire(byte[] key, long seconds); Response expire(byte[] key, long seconds, ExpiryOption expiryOption); Response pexpire(byte[] key, long milliseconds); Response pexpire(byte[] key, long milliseconds, ExpiryOption expiryOption); Response expireTime(byte[] key); Response pexpireTime(byte[] key); Response expireAt(byte[] key, long unixTime); Response expireAt(byte[] key, long unixTime, ExpiryOption expiryOption); Response pexpireAt(byte[] key, long millisecondsTimestamp); Response pexpireAt(byte[] key, long millisecondsTimestamp, ExpiryOption expiryOption); Response ttl(byte[] key); Response pttl(byte[] key); Response touch(byte[] key); Response touch(byte[]... keys); Response> sort(byte[] key); Response> sort(byte[] key, SortingParams sortingParams); Response> sortReadonly(byte[] key, SortingParams sortingParams); Response del(byte[] key); Response del(byte[]... keys); Response delex(byte[] key, CompareCondition condition); Response digestKey(byte[] key); Response unlink(byte[] key); Response unlink(byte[]... keys); Response copy(byte[] srcKey, byte[] dstKey, boolean replace); Response rename(byte[] oldkey, byte[] newkey); Response renamenx(byte[] oldkey, byte[] newkey); Response sort(byte[] key, SortingParams sortingParams, byte[] dstkey); Response sort(byte[] key, byte[] dstkey); Response memoryUsage(byte[] key); Response memoryUsage(byte[] key, int samples); Response objectRefcount(byte[] key); Response objectEncoding(byte[] key); Response objectIdletime(byte[] key); Response objectFreq(byte[] key); Response migrate(String host, int port, byte[] key, int timeout); Response migrate(String host, int port, int timeout, MigrateParams params, byte[]... keys); Response> keys(byte[] pattern); Response> scan(byte[] cursor); Response> scan(byte[] cursor, ScanParams params); Response> scan(byte[] cursor, ScanParams params, byte[] type); Response randomBinaryKey(); } ================================================ FILE: src/main/java/redis/clients/jedis/commands/KeyPipelineCommands.java ================================================ package redis.clients.jedis.commands; import java.util.List; import java.util.Set; import redis.clients.jedis.args.ExpiryOption; import redis.clients.jedis.params.MigrateParams; import redis.clients.jedis.params.RestoreParams; import redis.clients.jedis.params.ScanParams; import redis.clients.jedis.params.SortingParams; import redis.clients.jedis.resps.ScanResult; import redis.clients.jedis.Response; import redis.clients.jedis.util.CompareCondition; public interface KeyPipelineCommands { Response exists(String key); Response exists(String... keys); Response persist(String key); Response type(String key); Response dump(String key); Response restore(String key, long ttl, byte[] serializedValue); Response restore(String key, long ttl, byte[] serializedValue, RestoreParams params); Response expire(String key, long seconds); Response expire(String key, long seconds, ExpiryOption expiryOption); Response pexpire(String key, long milliseconds); Response pexpire(String key, long milliseconds, ExpiryOption expiryOption); Response expireTime(String key); Response pexpireTime(String key); Response expireAt(String key, long unixTime); Response expireAt(String key, long unixTime, ExpiryOption expiryOption); Response pexpireAt(String key, long millisecondsTimestamp); Response pexpireAt(String key, long millisecondsTimestamp, ExpiryOption expiryOption); Response ttl(String key); Response pttl(String key); Response touch(String key); Response touch(String... keys); Response> sort(String key); Response sort(String key, String dstkey); Response> sort(String key, SortingParams sortingParams); Response sort(String key, SortingParams sortingParams, String dstkey); Response> sortReadonly(String key, SortingParams sortingParams); Response del(String key); Response delex(String key, CompareCondition condition); Response digestKey(String key); Response del(String... keys); Response unlink(String key); Response unlink(String... keys); Response copy(String srcKey, String dstKey, boolean replace); Response rename(String oldkey, String newkey); Response renamenx(String oldkey, String newkey); Response memoryUsage(String key); Response memoryUsage(String key, int samples); Response objectRefcount(String key); Response objectEncoding(String key); Response objectIdletime(String key); Response objectFreq(String key); Response migrate(String host, int port, String key, int timeout); Response migrate(String host, int port, int timeout, MigrateParams params, String... keys); Response> keys(String pattern); Response> scan(String cursor); Response> scan(String cursor, ScanParams params); Response> scan(String cursor, ScanParams params, String type); Response randomKey(); } ================================================ FILE: src/main/java/redis/clients/jedis/commands/ListBinaryCommands.java ================================================ package redis.clients.jedis.commands; import java.util.List; import redis.clients.jedis.args.ListDirection; import redis.clients.jedis.args.ListPosition; import redis.clients.jedis.params.LPosParams; import redis.clients.jedis.util.KeyValue; public interface ListBinaryCommands { long rpush(byte[] key, byte[]... args); long lpush(byte[] key, byte[]... args); long llen(byte[] key); List lrange(byte[] key, long start, long stop); String ltrim(byte[] key, long start, long stop); byte[] lindex(byte[] key, long index); String lset(byte[] key, long index, byte[] value); long lrem(byte[] key, long count, byte[] value); byte[] lpop(byte[] key); List lpop(byte[] key, int count); Long lpos(byte[] key, byte[] element); Long lpos(byte[] key, byte[] element, LPosParams params); List lpos(byte[] key, byte[] element, LPosParams params, long count); byte[] rpop(byte[] key); List rpop(byte[] key, int count); long linsert(byte[] key, ListPosition where, byte[] pivot, byte[] value); long lpushx(byte[] key, byte[]... args); long rpushx(byte[] key, byte[]... args); List blpop(int timeout, byte[]... keys); KeyValue blpop(double timeout, byte[]... keys); List brpop(int timeout, byte[]... keys); KeyValue brpop(double timeout, byte[]... keys); /** * @deprecated Use {@link ListBinaryCommands#lmove(byte[], byte[], ListDirection, ListDirection)} with * {@link ListDirection#RIGHT} and {@link ListDirection#LEFT}. * Deprecated in Jedis 7.3.0. Mirrors Redis deprecation since 6.2.0. */ @Deprecated byte[] rpoplpush(byte[] srckey, byte[] dstkey); /** * @deprecated Use {@link ListBinaryCommands#blmove(byte[], byte[], ListDirection, ListDirection, double)} with * {@link ListDirection#RIGHT} and {@link ListDirection#LEFT}. * Deprecated in Jedis 7.3.0. Mirrors Redis deprecation since 6.2.0. */ @Deprecated byte[] brpoplpush(byte[] source, byte[] destination, int timeout); byte[] lmove(byte[] srcKey, byte[] dstKey, ListDirection from, ListDirection to); byte[] blmove(byte[] srcKey, byte[] dstKey, ListDirection from, ListDirection to, double timeout); KeyValue> lmpop(ListDirection direction, byte[]... keys); KeyValue> lmpop(ListDirection direction, int count, byte[]... keys); KeyValue> blmpop(double timeout, ListDirection direction, byte[]... keys); KeyValue> blmpop(double timeout, ListDirection direction, int count, byte[]... keys); } ================================================ FILE: src/main/java/redis/clients/jedis/commands/ListCommands.java ================================================ package redis.clients.jedis.commands; import java.util.List; import redis.clients.jedis.args.ListDirection; import redis.clients.jedis.args.ListPosition; import redis.clients.jedis.params.LPosParams; import redis.clients.jedis.util.KeyValue; public interface ListCommands { /** * Add the string value to the head (LPUSH) or tail (RPUSH) of the list stored at key. If the key * does not exist an empty list is created just before the append operation. If the key exists but * is not a List an error is returned. *

* Time complexity: O(1) * @param key * @param strings data to push * @return The number of elements inside the list after the push operation */ long rpush(String key, String... strings); /** * Add the string value to the head (LPUSH) or tail (RPUSH) of the list stored at key. If the key * does not exist an empty list is created just before the append operation. If the key exists but * is not a List an error is returned. *

* Time complexity: O(1) * @param key * @param strings data to push * @return The number of elements inside the list after the push operation */ long lpush(String key, String... strings); /** * Return the length of the list stored at the specified key. If the key does not exist zero is * returned (the same behaviour as for empty lists). If the value stored at key is not a list an * error is returned. *

* Time complexity: O(1) * @param key * @return The length of the list */ long llen(String key); /** * Return the specified elements of the list stored at the specified key. Start and end are * zero-based indexes. 0 is the first element of the list (the list head), 1 the next element and * so on. *

* For example LRANGE foobar 0 2 will return the first three elements of the list. *

* start and end can also be negative numbers indicating offsets from the end of the list. For * example -1 is the last element of the list, -2 the penultimate element and so on. *

* Consistency with range functions in various programming languages *

* Note that if you have a list of numbers from 0 to 100, LRANGE 0 10 will return 11 elements, * that is, rightmost item is included. This may or may not be consistent with behavior of * range-related functions in your programming language of choice (think Ruby's Range.new, * Array#slice or Python's range() function). *

* LRANGE behavior is consistent with one of Tcl. *

* Out-of-range indexes *

* Indexes out of range will not produce an error: if start is over the end of the list, or start * > end, an empty list is returned. If end is over the end of the list Redis will threat it * just like the last element of the list. *

* Time complexity: O(start+n) (with n being the length of the range and start being the start * offset) * @param key * @param start * @param stop * @return A list of elements in the specified range */ List lrange(String key, long start, long stop); /** * Trim an existing list so that it will contain only the specified range of elements specified. * Start and end are zero-based indexes. 0 is the first element of the list (the list head), 1 the * next element and so on. *

* For example LTRIM foobar 0 2 will modify the list stored at foobar key so that only the first * three elements of the list will remain. *

* start and end can also be negative numbers indicating offsets from the end of the list. For * example -1 is the last element of the list, -2 the penultimate element and so on. *

* Indexes out of range will not produce an error: if start is over the end of the list, or start * > end, an empty list is left as value. If end over the end of the list Redis will threat it * just like the last element of the list. *

* Hint: the obvious use of LTRIM is together with LPUSH/RPUSH. For example: *

* {@code lpush("mylist", "someelement"); ltrim("mylist", 0, 99); * } *

* The above two commands will push elements in the list taking care that the list will not grow * without limits. This is very useful when using Redis to store logs for example. It is important * to note that when used in this way LTRIM is an O(1) operation because in the average case just * one element is removed from the tail of the list. *

* Time complexity: O(n) (with n being len of list - len of range) * @param key * @param start * @param stop * @return OK */ String ltrim(String key, long start, long stop); /** * Returns the element at index in the list stored at key. 0 is the first element, 1 the second * and so on. Negative indexes are supported, for example -1 is the last element, -2 the penultimate * and so on. *

* If the value stored at key is not of list type an error is returned. If the index is out of * range a 'nil' reply is returned. *

* Note that even if the average time complexity is O(n) asking for the first or the last element * of the list is O(1). *

* Time complexity: O(n) (with n being the length of the list) * @param key * @param index * @return The requested element */ String lindex(String key, long index); /** * Set a new value as the element at index position of the List at key. *

* Out of range indexes will generate an error. *

* Similarly to other list commands accepting indexes, the index can be negative to access * elements starting from the end of the list. So -1 is the last element, -2 is the penultimate, * and so forth. *

* Time Complexity O(N) when N being the length of the list. For the first or last elements of * the list is O(1) * @param key * @param index * @param value * @return OK */ String lset(String key, long index, String value); /** * Remove the first count occurrences of the value element from the list. If count is zero all the * elements are removed. If count is negative elements are removed from tail to head, instead to * go from head to tail that is the normal behaviour. So for example LREM with count -2 and hello * as value to remove against the list (a,b,c,hello,x,hello,hello) will leave the list * (a,b,c,hello,x). The number of removed elements is returned as an integer, see below for more * information about the returned value. Note that non existing keys are considered like empty * lists by LREM, so LREM against non existing keys will always return 0. *

* Time complexity: O(N) (with N being the length of the list) * @param key * @param count * @param value * @return The number of removed elements if the operation succeeded */ long lrem(String key, long count, String value); /** * Atomically return and remove the first (LPOP) or last (RPOP) element of the list. For example * if the list contains the elements "a","b","c" LPOP will return "a" and the list will become * "b","c". *

* If the key does not exist or the list is already empty the special value 'nil' is returned. * @param key * @return The popped element */ String lpop(String key); /** * Atomically return and remove the first (LPOP) or last (RPOP) element of the list. For example * if the list contains the elements "a","b","c" LPOP will return "a" and the list will become * "b","c". * @param key * @param count * @return A list of popped elements, or 'nil' when key does not exist */ List lpop(String key, int count); /** * Returns the index of the first matching element inside a redis list. If the element is found, * its index (the zero-based position in the list) is returned. Otherwise, if no match is found, * 'nil' is returned. *

* Time complexity: O(N) where N is the number of elements in the list * @param key * @param element * @return The index of first matching element in the list. Value will be 'nil' when the element * is not present in the list */ Long lpos(String key, String element); /** * In case there are multiple matches Rank option specifies the "rank" of the element to return. * A rank of 1 returns the first match, 2 to return the second match, and so forth. * If list `foo` has elements ("a","b","c","1","2","3","c","c"), The function call to get the * index of second occurrence of "c" will be as follows lpos("foo","c", LPosParams.lPosParams().rank(2)). *

* Maxlen option compares the element provided only with a given maximum number of list items. * A value of 1000 will make sure that the command performs only 1000 comparisons. The * comparison is made for the first part or the last part depending on the fact we use a positive or * negative rank. * Following is how we could use the Maxlen option lpos("foo", "b", LPosParams.lPosParams().rank(1).maxlen(2)). * @param key * @param element * @param params {@link LPosParams} * @return The integer representing the matching element, or 'nil' if there is no match */ Long lpos(String key, String element, LPosParams params); /** * Returns the index of matching elements inside a Redis list. If the element is found, its index * (the zero-based position in the list) is returned. Otherwise, if no match is found, nil is returned. *

* Time complexity: O(N) where N is the number of elements in the list * @param key * @param element * @param params {@link LPosParams} * @param count * @return A list containing position of the matching elements inside the list */ List lpos(String key, String element, LPosParams params, long count); /** * Atomically return and remove the first (LPOP) or last (RPOP) element of the list. For example * if the list contains the elements "a","b","c" LPOP will return "a" and the list will become * "b","c". * @param key * @return The popped element */ String rpop(String key); /** * Atomically return and remove the first (LPOP) or last (RPOP) element of the list. For example * if the list contains the elements "a","b","c" LPOP will return "a" and the list will become * "b","c". * @param key * @param count return up to count elements * @return A list of count popped elements, or 'nil' when key does not exist. */ List rpop(String key, int count); /** * Inserts element in the list stored at key either before or after the reference value pivot. *

* When key does not exist, it is considered an empty list and no operation is performed. * @param key * @param where can be BEFORE or AFTER * @param pivot reference value * @param value the value * @return The length of the list after the insert operation, or -1 when the value pivot was not found */ long linsert(String key, ListPosition where, String pivot, String value); /** * Inserts specified values at the head of the list stored at key. In contrary to * {@link ListCommands#lpush(String, String...) LPUSH}, no operation will be performed when key * does not yet exist. * @param key * @param strings the strings to push * @return The length of the list after the push operation */ long lpushx(String key, String... strings); /** * Inserts specified values at the tail of the list stored at key. In contrary to * {@link ListCommands#rpush(String, String...) RPUSH}, no operation will be performed when key * does not yet exist. * @param key * @param strings the strings to push * @return The length of the list after the push operation */ long rpushx(String key, String... strings); /** * The blocking version of {@link ListCommands#lpop(String)} LPOP} because it blocks the connection * when there are no elements to pop from any of the given lists. An element is popped from the head of * the first list that is non-empty, with the given keys being checked in the order that they are given. * @param timeout the timeout argument is interpreted as a double value specifying the maximum number of * seconds to block. A timeout of zero can be used to block indefinitely. * @param keys */ List blpop(int timeout, String... keys); /** * @see ListCommands#blpop(int, String...) */ List blpop(int timeout, String key); /** * The blocking version of {@link ListCommands#lpop(String)} LPOP} because it blocks the connection * when there are no elements to pop from any of the given lists. An element is popped from the head of * the first list that is non-empty, with the given keys being checked in the order that they are given. * @param timeout the timeout argument is interpreted as a double value specifying the maximum number of * seconds to block. A timeout of zero can be used to block indefinitely. * @param keys */ KeyValue blpop(double timeout, String... keys); /** * @see ListCommands#blpop(double, String...) */ KeyValue blpop(double timeout, String key); /** * The blocking version of {@link ListCommands#rpop(String)} RPOP} because it blocks the connection * when there are no elements to pop from any of the given lists. An element is popped from the tail of * the first list that is non-empty, with the given keys being checked in the order that they are given. * @param timeout the timeout argument is interpreted as a double value specifying the maximum number of * seconds to block. A timeout of zero can be used to block indefinitely. * @param keys */ List brpop(int timeout, String... keys); /** * @see ListCommands#brpop(int, String...) */ List brpop(int timeout, String key); /** * The blocking version of {@link ListCommands#rpop(String)} RPOP} because it blocks the connection * when there are no elements to pop from any of the given lists. An element is popped from the tail of * the first list that is non-empty, with the given keys being checked in the order that they are given. * @param timeout the timeout argument is interpreted as a double value specifying the maximum number of * seconds to block. A timeout of zero can be used to block indefinitely. * @param keys */ KeyValue brpop(double timeout, String... keys); /** * @see ListCommands#brpop(double, String...) */ KeyValue brpop(double timeout, String key); /** * Atomically return and remove the last (tail) element of the srckey list, and push the element * as the first (head) element of the dstkey list. For example if the source list contains the * elements "a","b","c" and the destination list contains the elements "foo","bar" after an * RPOPLPUSH command the content of the two lists will be "a","b" and "c","foo","bar". *

* If the key does not exist or the list is already empty the special value 'nil' is returned. If * the srckey and dstkey are the same the operation is equivalent to removing the last element * from the list and pushing it as first element of the list, so it's a "list rotation" command. *

* Time complexity: O(1) * @param srckey * @param dstkey * @return Bulk reply * @deprecated Use {@link ListCommands#lmove(String, String, ListDirection, ListDirection)} with * {@link ListDirection#RIGHT} and {@link ListDirection#LEFT}. * Deprecated in Jedis 7.3.0. Mirrors Redis deprecation since 6.2.0. */ @Deprecated String rpoplpush(String srckey, String dstkey); /** * The blocking variant of {@link ListCommands#rpoplpush(String, String)}. When source is * empty, Redis will block the connection until another client pushes to it or until timeout is * reached. A timeout of zero can be used to block indefinitely. *

* Time complexity: O(1) * @param source * @param destination * @param timeout the timeout argument is interpreted as a double value specifying the maximum number of * seconds to block. A timeout of zero can be used to block indefinitely. * @return The element being popped from source and pushed to destination * @deprecated Use {@link ListCommands#blmove(String, String, ListDirection, ListDirection, double)} with * {@link ListDirection#RIGHT} and {@link ListDirection#LEFT}. * Deprecated in Jedis 7.3.0. Mirrors Redis deprecation since 6.2.0. */ @Deprecated String brpoplpush(String source, String destination, int timeout); /** * Pop an element from a list, push it to another list and return it * @param srcKey * @param dstKey * @param from can be LEFT or RIGHT * @param to can be LEFT or RIGHT * @return The element being popped and pushed */ String lmove(String srcKey, String dstKey, ListDirection from, ListDirection to); /** * Pop an element from a list, push it to another list and return it; or block until one is available * @param srcKey * @param dstKey * @param from can be LEFT or RIGHT * @param to can be LEFT or RIGHT * @param timeout the timeout argument is interpreted as a double value specifying the maximum number of * seconds to block. A timeout of zero can be used to block indefinitely. * @return The element being popped and pushed */ String blmove(String srcKey, String dstKey, ListDirection from, ListDirection to, double timeout); KeyValue> lmpop(ListDirection direction, String... keys); KeyValue> lmpop(ListDirection direction, int count, String... keys); KeyValue> blmpop(double timeout, ListDirection direction, String... keys); KeyValue> blmpop(double timeout, ListDirection direction, int count, String... keys); } ================================================ FILE: src/main/java/redis/clients/jedis/commands/ListPipelineBinaryCommands.java ================================================ package redis.clients.jedis.commands; import java.util.List; import redis.clients.jedis.Response; import redis.clients.jedis.args.ListDirection; import redis.clients.jedis.args.ListPosition; import redis.clients.jedis.params.LPosParams; import redis.clients.jedis.util.KeyValue; public interface ListPipelineBinaryCommands { Response rpush(byte[] key, byte[]... args); Response lpush(byte[] key, byte[]... args); Response llen(byte[] key); Response> lrange(byte[] key, long start, long stop); Response ltrim(byte[] key, long start, long stop); Response lindex(byte[] key, long index); Response lset(byte[] key, long index, byte[] value); Response lrem(byte[] key, long count, byte[] value); Response lpop(byte[] key); Response> lpop(byte[] key, int count); Response lpos(byte[] key, byte[] element); Response lpos(byte[] key, byte[] element, LPosParams params); Response> lpos(byte[] key, byte[] element, LPosParams params, long count); Response rpop(byte[] key); Response> rpop(byte[] key, int count); Response linsert(byte[] key, ListPosition where, byte[] pivot, byte[] value); Response lpushx(byte[] key, byte[]... args); Response rpushx(byte[] key, byte[]... args); Response> blpop(int timeout, byte[]... keys); Response> blpop(double timeout, byte[]... keys); Response> brpop(int timeout, byte[]... keys); Response> brpop(double timeout, byte[]... keys); Response rpoplpush(byte[] srckey, byte[] dstkey); Response brpoplpush(byte[] source, byte[] destination, int timeout); Response lmove(byte[] srcKey, byte[] dstKey, ListDirection from, ListDirection to); Response blmove(byte[] srcKey, byte[] dstKey, ListDirection from, ListDirection to, double timeout); Response>> lmpop(ListDirection direction, byte[]... keys); Response>> lmpop(ListDirection direction, int count, byte[]... keys); Response>> blmpop(double timeout, ListDirection direction, byte[]... keys); Response>> blmpop(double timeout, ListDirection direction, int count, byte[]... keys); } ================================================ FILE: src/main/java/redis/clients/jedis/commands/ListPipelineCommands.java ================================================ package redis.clients.jedis.commands; import java.util.List; import redis.clients.jedis.Response; import redis.clients.jedis.args.ListDirection; import redis.clients.jedis.args.ListPosition; import redis.clients.jedis.params.LPosParams; import redis.clients.jedis.util.KeyValue; public interface ListPipelineCommands { Response rpush(String key, String... string); Response lpush(String key, String... string); Response llen(String key); Response> lrange(String key, long start, long stop); Response ltrim(String key, long start, long stop); Response lindex(String key, long index); Response lset(String key, long index, String value); Response lrem(String key, long count, String value); Response lpop(String key); Response> lpop(String key, int count); Response lpos(String key, String element); Response lpos(String key, String element, LPosParams params); Response> lpos(String key, String element, LPosParams params, long count); Response rpop(String key); Response> rpop(String key, int count); Response linsert(String key, ListPosition where, String pivot, String value); Response lpushx(String key, String... strings); Response rpushx(String key, String... strings); Response> blpop(int timeout, String key); Response> blpop(double timeout, String key); Response> brpop(int timeout, String key); Response> brpop(double timeout, String key); Response> blpop(int timeout, String... keys); Response> blpop(double timeout, String... keys); Response> brpop(int timeout, String... keys); Response> brpop(double timeout, String... keys); Response rpoplpush(String srckey, String dstkey); Response brpoplpush(String source, String destination, int timeout); Response lmove(String srcKey, String dstKey, ListDirection from, ListDirection to); Response blmove(String srcKey, String dstKey, ListDirection from, ListDirection to, double timeout); Response>> lmpop(ListDirection direction, String... keys); Response>> lmpop(ListDirection direction, int count, String... keys); Response>> blmpop(double timeout, ListDirection direction, String... keys); Response>> blmpop(double timeout, ListDirection direction, int count, String... keys); } ================================================ FILE: src/main/java/redis/clients/jedis/commands/ModuleCommands.java ================================================ package redis.clients.jedis.commands; import java.util.List; import redis.clients.jedis.Module; import redis.clients.jedis.params.ModuleLoadExParams; public interface ModuleCommands { /** * Load and initialize the Redis module from the dynamic library specified by the path argument. * * @param path should be the absolute path of the library, including the full filename * @return OK */ String moduleLoad(String path); /** * Load and initialize the Redis module from the dynamic library specified by the path argument. * * @param path should be the absolute path of the library, including the full filename * @param args additional arguments are passed unmodified to the module * @return OK */ String moduleLoad(String path, String... args); /** * Loads a module from a dynamic library at runtime with configuration directives. *

* This is an extended version of the MODULE LOAD command. *

* It loads and initializes the Redis module from the dynamic library specified by the path * argument. The path should be the absolute path of the library, including the full filename. *

* You can use the optional CONFIG argument to provide the module with configuration directives. * Any additional arguments that follow the ARGS keyword are passed unmodified to the module. * * @param path should be the absolute path of the library, including the full filename * @param params as in description * @return OK */ String moduleLoadEx(String path, ModuleLoadExParams params); /** * Unload the module specified by name. Note that the module's name is reported by the * {@link ModuleCommands#moduleList() MODULE LIST} command, and may differ from the dynamic * library's filename. * * @param name * @return OK */ String moduleUnload(String name); /** * Return information about the modules loaded to the server. * * @return list of {@link Module} */ List moduleList(); } ================================================ FILE: src/main/java/redis/clients/jedis/commands/PipelineBinaryCommands.java ================================================ package redis.clients.jedis.commands; public interface PipelineBinaryCommands extends KeyPipelineBinaryCommands, StringPipelineBinaryCommands, ListPipelineBinaryCommands, HashPipelineBinaryCommands, SetPipelineBinaryCommands, SortedSetPipelineBinaryCommands, GeoPipelineBinaryCommands, HyperLogLogPipelineBinaryCommands, StreamPipelineBinaryCommands, ScriptingKeyPipelineBinaryCommands, SampleBinaryKeyedPipelineCommands, FunctionPipelineBinaryCommands, VectorSetPipelineBinaryCommands { } ================================================ FILE: src/main/java/redis/clients/jedis/commands/PipelineCommands.java ================================================ package redis.clients.jedis.commands; public interface PipelineCommands extends KeyPipelineCommands, StringPipelineCommands, ListPipelineCommands, HashPipelineCommands, SetPipelineCommands, SortedSetPipelineCommands, GeoPipelineCommands, HyperLogLogPipelineCommands, StreamPipelineCommands, ScriptingKeyPipelineCommands, SampleKeyedPipelineCommands, FunctionPipelineCommands, VectorSetPipelineCommands { } ================================================ FILE: src/main/java/redis/clients/jedis/commands/ProtocolCommand.java ================================================ package redis.clients.jedis.commands; import redis.clients.jedis.args.Rawable; public interface ProtocolCommand extends Rawable { } ================================================ FILE: src/main/java/redis/clients/jedis/commands/RedisModuleCommands.java ================================================ package redis.clients.jedis.commands; import redis.clients.jedis.bloom.commands.RedisBloomCommands; import redis.clients.jedis.json.commands.RedisJsonCommands; import redis.clients.jedis.search.RediSearchCommands; import redis.clients.jedis.timeseries.RedisTimeSeriesCommands; public interface RedisModuleCommands extends RediSearchCommands, RedisJsonCommands, RedisTimeSeriesCommands, RedisBloomCommands { } ================================================ FILE: src/main/java/redis/clients/jedis/commands/RedisModulePipelineCommands.java ================================================ package redis.clients.jedis.commands; import redis.clients.jedis.bloom.commands.RedisBloomPipelineCommands; import redis.clients.jedis.json.commands.RedisJsonPipelineCommands; import redis.clients.jedis.search.RediSearchPipelineCommands; import redis.clients.jedis.timeseries.RedisTimeSeriesPipelineCommands; public interface RedisModulePipelineCommands extends RediSearchPipelineCommands, RedisJsonPipelineCommands, RedisTimeSeriesPipelineCommands, RedisBloomPipelineCommands { } ================================================ FILE: src/main/java/redis/clients/jedis/commands/SampleBinaryKeyedCommands.java ================================================ package redis.clients.jedis.commands; import java.util.List; import redis.clients.jedis.args.FlushMode; import redis.clients.jedis.util.KeyValue; public interface SampleBinaryKeyedCommands { long waitReplicas(byte[] sampleKey, int replicas, long timeout); KeyValue waitAOF(byte[] sampleKey, long numLocal, long numReplicas, long timeout); Object eval(byte[] script, byte[] sampleKey); Object evalsha(byte[] sha1, byte[] sampleKey); Boolean scriptExists(byte[] sha1, byte[] sampleKey); List scriptExists(byte[] sampleKey, byte[]... sha1s); byte[] scriptLoad(byte[] script, byte[] sampleKey); String scriptFlush(byte[] sampleKey); String scriptFlush(byte[] sampleKey, FlushMode flushMode); String scriptKill(byte[] sampleKey); } ================================================ FILE: src/main/java/redis/clients/jedis/commands/SampleBinaryKeyedPipelineCommands.java ================================================ package redis.clients.jedis.commands; import java.util.List; import redis.clients.jedis.Response; import redis.clients.jedis.args.FlushMode; import redis.clients.jedis.util.KeyValue; public interface SampleBinaryKeyedPipelineCommands { Response waitReplicas(byte[] sampleKey, int replicas, long timeout); Response> waitAOF(byte[] sampleKey, long numLocal, long numReplicas, long timeout); Response eval(byte[] script, byte[] sampleKey); Response evalsha(byte[] sha1, byte[] sampleKey); // // Response scriptExists(byte[] sha1, byte[] sampleKey); Response> scriptExists(byte[] sampleKey, byte[]... sha1s); Response scriptLoad(byte[] script, byte[] sampleKey); Response scriptFlush(byte[] sampleKey); Response scriptFlush(byte[] sampleKey, FlushMode flushMode); Response scriptKill(byte[] sampleKey); } ================================================ FILE: src/main/java/redis/clients/jedis/commands/SampleKeyedCommands.java ================================================ package redis.clients.jedis.commands; import java.util.List; import redis.clients.jedis.args.FlushMode; import redis.clients.jedis.util.KeyValue; public interface SampleKeyedCommands { long waitReplicas(String sampleKey, int replicas, long timeout); KeyValue waitAOF(String sampleKey, long numLocal, long numReplicas, long timeout); Object eval(String script, String sampleKey); Object evalsha(String sha1, String sampleKey); Boolean scriptExists(String sha1, String sampleKey); List scriptExists(String sampleKey, String... sha1s); String scriptLoad(String script, String sampleKey); String scriptFlush(String sampleKey); String scriptFlush(String sampleKey, FlushMode flushMode); String scriptKill(String sampleKey); } ================================================ FILE: src/main/java/redis/clients/jedis/commands/SampleKeyedPipelineCommands.java ================================================ package redis.clients.jedis.commands; import java.util.List; import redis.clients.jedis.Response; import redis.clients.jedis.args.FlushMode; import redis.clients.jedis.util.KeyValue; public interface SampleKeyedPipelineCommands { Response waitReplicas(String sampleKey, int replicas, long timeout); Response> waitAOF(String sampleKey, long numLocal, long numReplicas, long timeout); Response eval(String script, String sampleKey); Response evalsha(String sha1, String sampleKey); // // Response scriptExists(String sha1, String sampleKey); Response> scriptExists(String sampleKey, String... sha1); Response scriptLoad(String script, String sampleKey); Response scriptFlush(String sampleKey); Response scriptFlush(String sampleKey, FlushMode flushMode); Response scriptKill(String sampleKey); } ================================================ FILE: src/main/java/redis/clients/jedis/commands/ScriptingControlCommands.java ================================================ package redis.clients.jedis.commands; import java.util.List; import redis.clients.jedis.args.FlushMode; public interface ScriptingControlCommands { Boolean scriptExists(String sha1); List scriptExists(String... sha1); Boolean scriptExists(byte[] sha1); List scriptExists(byte[]... sha1); String scriptLoad(String script); byte[] scriptLoad(byte[] script); String scriptFlush(); String scriptFlush(FlushMode flushMode); String scriptKill(); } ================================================ FILE: src/main/java/redis/clients/jedis/commands/ScriptingKeyBinaryCommands.java ================================================ package redis.clients.jedis.commands; import java.util.List; public interface ScriptingKeyBinaryCommands { Object eval(byte[] script); Object eval(byte[] script, int keyCount, byte[]... params); Object eval(byte[] script, List keys, List args); Object evalReadonly(byte[] script, List keys, List args); Object evalsha(byte[] sha1); Object evalsha(byte[] sha1, int keyCount, byte[]... params); Object evalsha(byte[] sha1, List keys, List args); Object evalshaReadonly(byte[] sha1, List keys, List args); } ================================================ FILE: src/main/java/redis/clients/jedis/commands/ScriptingKeyCommands.java ================================================ package redis.clients.jedis.commands; import java.util.List; public interface ScriptingKeyCommands { /** * Eval Command * Use to evaluate scripts using the Lua interpreter built into Redis starting from version 2.6.0. * @param script Lua 5.1 script. The script does not need to define a Lua function (and should not). * It is just a Lua program that will run in the context of the Redis server. * @return The result of the evaluated script */ Object eval(String script); /** * Eval Command * Use to evaluate scripts using the Lua interpreter built into Redis starting from version 2.6.0. * @param script Lua 5.1 script. The script does not need to define a Lua function (and should not). * It is just a Lua program that will run in the context of the Redis server. * @param keyCount the count of the provided keys * @param params arguments that can be accessed from the script * @return The result of the evaluated script */ Object eval(String script, int keyCount, String... params); /** * Eval Command * Use to evaluate scripts using the Lua interpreter built into Redis starting from version 2.6.0. * @param script Lua 5.1 script. The script does not need to define a Lua function (and should not). * It is just a Lua program that will run in the context of the Redis server. * @param keys arguments that can be accessed by the script * @param args additional arguments should not represent key names and can be accessed by the script * @return The result of the evaluated script */ Object eval(String script, List keys, List args); /** * Readonly version of {@link ScriptingKeyCommands#eval(String, List, List) EVAL} * @see ScriptingKeyCommands#eval(String, List, List) * @param script Lua 5.1 script. The script does not need to define a Lua function (and should not). * It is just a Lua program that will run in the context of the Redis server. * @param keys arguments that can be accessed by the script * @param args additional arguments should not represent key names and can be accessed by the script * @return The result of the evaluated script */ Object evalReadonly(String script, List keys, List args); /** * EvalSha Command * Similar to {@link ScriptingKeyCommands#eval(String) EVAL}, but the script cached on the server * side by its SHA1 digest. Scripts are cached on the server side using the SCRIPT LOAD command. * @see ScriptingKeyCommands#eval(String) * @param sha1 the script * @return The result of the evaluated script */ Object evalsha(String sha1); /** * EvalSha Command * Similar to {@link ScriptingKeyCommands#eval(String, int, String...)} EVAL}, but the script cached on the server * side by its SHA1 digest. Scripts are cached on the server side using the SCRIPT LOAD command. * @see ScriptingKeyCommands#eval(String, int, String...) * @param sha1 the script * @return The result of the evaluated script */ Object evalsha(String sha1, int keyCount, String... params); /** * EvalSha Command * Similar to {@link ScriptingKeyCommands#eval(String, List, List)} EVAL}, but the script cached on the server * side by its SHA1 digest. Scripts are cached on the server side using the SCRIPT LOAD command. * @see ScriptingKeyCommands#eval(String, List, List) * @param sha1 the script * @return The result of the evaluated script */ Object evalsha(String sha1, List keys, List args); /** * Readonly version of {@link ScriptingKeyCommands#evalsha(String, List, List) EVAL} * @see ScriptingKeyCommands#evalsha(String, List, List) * @param sha1 the script * @return The result of the evaluated script */ Object evalshaReadonly(String sha1, List keys, List args); } ================================================ FILE: src/main/java/redis/clients/jedis/commands/ScriptingKeyPipelineBinaryCommands.java ================================================ package redis.clients.jedis.commands; import java.util.List; import redis.clients.jedis.Response; public interface ScriptingKeyPipelineBinaryCommands { Response eval(byte[] script); Response eval(byte[] script, int keyCount, byte[]... params); Response eval(byte[] script, List keys, List args); Response evalReadonly(byte[] script, List keys, List args); Response evalsha(byte[] sha1); Response evalsha(byte[] sha1, int keyCount, byte[]... params); Response evalsha(byte[] sha1, List keys, List args); Response evalshaReadonly(byte[] sha1, List keys, List args); } ================================================ FILE: src/main/java/redis/clients/jedis/commands/ScriptingKeyPipelineCommands.java ================================================ package redis.clients.jedis.commands; import java.util.List; import redis.clients.jedis.Response; public interface ScriptingKeyPipelineCommands { Response eval(String script); Response eval(String script, int keyCount, String... params); Response eval(String script, List keys, List args); Response evalReadonly(String script, List keys, List args); Response evalsha(String sha1); Response evalsha(String sha1, int keyCount, String... params); Response evalsha(String sha1, List keys, List args); Response evalshaReadonly(String sha1, List keys, List args); } ================================================ FILE: src/main/java/redis/clients/jedis/commands/SentinelCommands.java ================================================ package redis.clients.jedis.commands; import java.util.List; import java.util.Map; //Legacy public interface SentinelCommands { String sentinelMyId(); List> sentinelMasters(); Map sentinelMaster(String masterName); List> sentinelSentinels(String masterName); List sentinelGetMasterAddrByName(String masterName); Long sentinelReset(String pattern); /** * @deprecated Use {@link SentinelCommands#sentinelReplicas(java.lang.String)}. */ @Deprecated List> sentinelSlaves(String masterName); List> sentinelReplicas(String masterName); String sentinelFailover(String masterName); String sentinelMonitor(String masterName, String ip, int port, int quorum); String sentinelRemove(String masterName); String sentinelSet(String masterName, Map parameterMap); } ================================================ FILE: src/main/java/redis/clients/jedis/commands/ServerCommands.java ================================================ package redis.clients.jedis.commands; import redis.clients.jedis.args.FlushMode; import redis.clients.jedis.args.LatencyEvent; import redis.clients.jedis.args.SaveMode; import redis.clients.jedis.exceptions.JedisException; import redis.clients.jedis.params.HotkeysParams; import redis.clients.jedis.params.LolwutParams; import redis.clients.jedis.params.ShutdownParams; import redis.clients.jedis.resps.HotkeysInfo; import redis.clients.jedis.resps.LatencyHistoryInfo; import redis.clients.jedis.resps.LatencyLatestInfo; import redis.clients.jedis.util.KeyValue; import java.util.List; import java.util.Map; public interface ServerCommands { /** * This command is often used to test if a connection is still alive, or to measure latency. * @return PONG */ String ping(); String ping(String message); String echo(String string); byte[] echo(byte[] arg); /** * Delete all the keys of the currently selected DB. This command never fails. The time-complexity * for this operation is O(N), N being the number of keys in the database. * @return OK */ String flushDB(); /** * Delete all the keys of the currently selected DB. This command never fails. The time-complexity * for this operation is O(N), N being the number of keys in the database. * @param flushMode can be SYNC or ASYNC * @return OK */ String flushDB(FlushMode flushMode); /** * Delete all the keys of all the existing databases, not just the currently selected one. * @return a simple string reply (OK) */ String flushAll(); /** * Delete all the keys of all the existing databases, not just the currently selected one. * @param flushMode SYNC or ASYNC * @return a simple string reply (OK) */ String flushAll(FlushMode flushMode); /** * Request for authentication in a password-protected Redis server. Redis can be instructed to * require a password before allowing clients to execute commands. This is done using the * requirepass directive in the configuration file. If password matches the password in the * configuration file, the server replies with the OK status code and starts accepting commands. * Otherwise, an error is returned and the clients needs to try a new password. * @return the result of the auth */ String auth(String password); /** * Request for authentication with username and password, based on the ACL feature introduced in * Redis 6.0 see https://redis.io/topics/acl * @return OK */ String auth(String user, String password); /** * The SAVE commands performs a synchronous save of the dataset producing a point in time snapshot * of all the data inside the Redis instance, in the form of an RDB file. You almost never want to * call SAVE in production environments where it will block all the other clients. Instead usually * BGSAVE is used. However, in case of issues preventing Redis to create the background saving * child (for instance errors in the fork(2) system call), the SAVE command can be a good last * resort to perform the dump of the latest dataset. * @return result of the save */ String save(); /** * Save the DB in background. The OK code is immediately returned. Redis forks, the parent * continues to serve the clients, the child saves the DB on disk then exits. A client may be able * to check if the operation succeeded using the LASTSAVE command. * @return ok */ String bgsave(); String bgsaveSchedule(); /** * Instruct Redis to start an Append Only File rewrite process. The rewrite will create a small * optimized version of the current Append Only File If BGREWRITEAOF fails, no data gets lost as * the old AOF will be untouched. The rewrite will be only triggered by Redis if there is not * already a background process doing persistence. Specifically: If a Redis child is creating a * snapshot on disk, the AOF rewrite is scheduled but not started until the saving child producing * the RDB file terminates. In this case the BGREWRITEAOF will still return an OK code, but with * an appropriate message. You can check if an AOF rewrite is scheduled looking at the INFO * command as of Redis 2.6. If an AOF rewrite is already in progress the command returns an error * and no AOF rewrite will be scheduled for a later time. Since Redis 2.4 the AOF rewrite is * automatically triggered by Redis, however the BGREWRITEAOF command can be used to trigger a * rewrite at any time. * @return the response of the command */ String bgrewriteaof(); /** * Return the UNIX TIME of the last DB save executed with success. * @return the unix latest save */ long lastsave(); /** * Stop all the client. Perform a SAVE (if one save point is configured). Flush the append only * file if AOF is enabled quit the server * @throws JedisException only in case of error. */ void shutdown() throws JedisException; default void shutdown(SaveMode saveMode) throws JedisException { shutdown(ShutdownParams.shutdownParams().saveMode(saveMode)); } /** * @see SaveMode * @param shutdownParams set commands parameters * @throws JedisException */ void shutdown(ShutdownParams shutdownParams) throws JedisException; String shutdownAbort(); /** * The INFO command returns information and statistics about the server in a format that is simple * to parse by computers and easy to read by humans. * @return information on the server */ String info(); /** * The INFO command returns information and statistics about the server in a format that is simple * to parse by computers and easy to read by humans. * @param section (all: Return all sections, default: Return only the default set of sections, * server: General information about the Redis server, clients: Client connections * section, memory: Memory consumption related information, persistence: RDB and AOF * related information, stats: General statistics, replication: Master/slave replication * information, cpu: CPU consumption statistics, commandstats: Redis command statistics, * cluster: Redis Cluster section, keyspace: Database related statistics) * @return info */ String info(String section); /** * The SLAVEOF command can change the replication settings of a slave on the fly. In the proper * form SLAVEOF hostname port will make the server a slave of another server listening at the * specified hostname and port. If a server is already a slave of some master, SLAVEOF hostname * port will stop the replication against the old server and start the synchronization against the * new one, discarding the old dataset. * @param host listening at the specified hostname * @param port server listening at the specified port * @return result of the command. * @deprecated Use {@link ServerCommands#replicaof(java.lang.String, int)}. */ @Deprecated String slaveof(String host, int port); /** * SLAVEOF NO ONE will stop replication, turning the server into a MASTER, but will not discard * the replication. So, if the old master stops working, it is possible to turn the slave into a * master and set the application to use this new master in read/write. Later when the other Redis * server is fixed, it can be reconfigured to work as a slave. * @return result of the command * @deprecated Use {@link ServerCommands#replicaofNoOne()}. */ @Deprecated String slaveofNoOne(); /** * The REPLICAOF command can change the replication settings of a replica on the fly. In the * proper form REPLICAOF hostname port will make the server a replica of another server * listening at the specified hostname and port. If a server is already a replica of some master, * REPLICAOF hostname port will stop the replication against the old server and start the * synchronization against the new one, discarding the old dataset. * @param host listening at the specified hostname * @param port server listening at the specified port * @return result of the command. */ String replicaof(String host, int port); /** * REPLICAOF NO ONE will stop replication, turning the server into a MASTER, but will not discard * the replication. So, if the old master stops working, it is possible to turn the replica into * a master and set the application to use this new master in read/write. Later when the other * Redis server is fixed, it can be reconfigured to work as a replica. * @return result of the command */ String replicaofNoOne(); /** * Synchronous replication of Redis as described here: http://antirez.com/news/66. *

* Blocks until all the previous write commands are successfully transferred and acknowledged by * at least the specified number of replicas. If the timeout, specified in milliseconds, is * reached, the command returns even if the specified number of replicas were not yet reached. *

* Since Java Object class has implemented {@code wait} method, we cannot use it. * @param replicas successfully transferred and acknowledged by at least the specified number of * replicas * @param timeout the time to block in milliseconds, a timeout of 0 means to block forever * @return the number of replicas reached by all the writes performed in the context of the * current connection */ long waitReplicas(int replicas, long timeout); /** * Blocks the current client until all the previous write commands are acknowledged as having been * fsynced to the AOF of the local Redis and/or at least the specified number of replicas. * Redis Documentation * @param numLocal Number of local instances that are required to acknowledge the sync (0 or 1), * cannot be non-zero if the local Redis does not have AOF enabled * @param numReplicas Number of replicas that are required to acknowledge the sync * @param timeout Timeout in millis of the operation - if 0 timeout is unlimited. If the timeout is reached, * the command returns even if the specified number of acknowledgments has not been met. * @return KeyValue where Key is number of local Redises (0 or 1) that have fsynced to AOF all writes * performed in the context of the current connection, and the value is the number of replicas that have acknowledged doing the same. */ KeyValue waitAOF(long numLocal, long numReplicas, long timeout); String lolwut(); String lolwut(LolwutParams lolwutParams); String reset(); /** * The LATENCY DOCTOR command reports about different latency-related issues and advises about * possible remedies. *

* This command is the most powerful analysis tool in the latency monitoring framework, and is * able to provide additional statistical data like the average period between latency spikes, the * median deviation, and a human-readable analysis of the event. For certain events, like fork, * additional information is provided, like the rate at which the system forks processes. *

* This is the output you should post in the Redis mailing list if you are looking for help about * Latency related issues. * * @return the report */ String latencyDoctor(); Map latencyLatest(); List latencyHistory(LatencyEvent events); long latencyReset(LatencyEvent... events); /** * Start hotkey tracking with the specified parameters. *

* The HOTKEYS command is used to identify hot keys in your Redis instance by tracking * keys by CPU time consumption and network bytes transferred. *

* Note: This command is not supported in Redis Cluster mode. HOTKEYS is a node-local * operation and there is no built-in mechanism to aggregate hotkeys data across cluster nodes. * Calling this method on a cluster client will throw {@link UnsupportedOperationException}. * To use HOTKEYS in a cluster, connect directly to individual nodes. * * @param params the parameters for hotkey tracking (metrics, count, duration, sample) * @return OK * @throws UnsupportedOperationException if called on a cluster client */ String hotkeysStart(HotkeysParams params); /** * Stop hotkey tracking. Data is preserved until RESET or next START. *

* Note: This command is not supported in Redis Cluster mode. * See {@link #hotkeysStart(HotkeysParams)} for details. * * @return OK * @throws UnsupportedOperationException if called on a cluster client */ String hotkeysStop(); /** * Clear all collected hotkey data and return to empty state. *

* Note: This command is not supported in Redis Cluster mode. * See {@link #hotkeysStart(HotkeysParams)} for details. * * @return OK * @throws UnsupportedOperationException if called on a cluster client */ String hotkeysReset(); /** * Retrieve collected hotkey statistics. *

* Returns null if hotkey tracking has never been started. *

* Note: This command is not supported in Redis Cluster mode. * See {@link #hotkeysStart(HotkeysParams)} for details. * * @return HotkeysInfo containing tracking statistics, or null if never started * @throws UnsupportedOperationException if called on a cluster client */ HotkeysInfo hotkeysGet(); } ================================================ FILE: src/main/java/redis/clients/jedis/commands/SetBinaryCommands.java ================================================ package redis.clients.jedis.commands; import java.util.List; import java.util.Set; import redis.clients.jedis.params.ScanParams; import redis.clients.jedis.resps.ScanResult; public interface SetBinaryCommands { long sadd(byte[] key, byte[]... members); Set smembers(byte[] key); long srem(byte[] key, byte[]... members); byte[] spop(byte[] key); Set spop(byte[] key, long count); long scard(byte[] key); boolean sismember(byte[] key, byte[] member); List smismember(byte[] key, byte[]... members); byte[] srandmember(byte[] key); List srandmember(byte[] key, int count); default ScanResult sscan(byte[] key, byte[] cursor) { return sscan(key, cursor, new ScanParams()); } ScanResult sscan(byte[] key, byte[] cursor, ScanParams params); Set sdiff(byte[]... keys); long sdiffstore(byte[] dstkey, byte[]... keys); Set sinter(byte[]... keys); long sinterstore(byte[] dstkey, byte[]... keys); /** * This command works exactly like {@link SetBinaryCommands#sinter(byte[][]) SINTER} but instead of returning * the result set, it returns just the cardinality of the result. LIMIT defaults to 0 and means unlimited *

* Time complexity O(N*M) worst case where N is the cardinality of the smallest * @param keys * @return The cardinality of the set which would result from the intersection of all the given sets */ long sintercard(byte[]... keys); /** * This command works exactly like {@link SetBinaryCommands#sinter(byte[][]) SINTER} but instead of returning * the result set, it returns just the cardinality of the result. *

* Time complexity O(N*M) worst case where N is the cardinality of the smallest * @param limit If the intersection cardinality reaches limit partway through the computation, * the algorithm will exit and yield limit as the cardinality. * @param keys * @return The cardinality of the set which would result from the intersection of all the given sets */ long sintercard(int limit, byte[]... keys); Set sunion(byte[]... keys); long sunionstore(byte[] dstkey, byte[]... keys); long smove(byte[] srckey, byte[] dstkey, byte[] member); } ================================================ FILE: src/main/java/redis/clients/jedis/commands/SetCommands.java ================================================ package redis.clients.jedis.commands; import java.util.List; import java.util.Set; import redis.clients.jedis.params.ScanParams; import redis.clients.jedis.resps.ScanResult; public interface SetCommands { /** * Add the specified member to the set value stored at key. If member is already a member of the * set no operation is performed. If key does not exist a new set with the specified member as * sole member is created. If the key exists but does not hold a set value an error is returned. *

* Time complexity O(1) * @param key * @param members * @return The number of elements that were added to the set, not including all the elements * already present in the set */ long sadd(String key, String... members); /** * Return all the members (elements) of the set value stored at key. This is just syntax glue for * {@link SetCommands#sinter(String...) SINTER}. *

* Time complexity O(N) * @param key * @return All elements of the set */ Set smembers(String key); /** * Remove the specified member from the set value stored at key. If member was not a member of the * set no operation is performed. If key does not hold a set value an error is returned. *

* Time complexity O(1) * @param key * @param members * @return The number of members that were removed from the set, not including non-existing members */ long srem(String key, String... members); /** * Remove a random element from a Set returning it as return value. If the Set is empty or the key * does not exist, a nil object is returned. *

* The {@link SetCommands#srandmember(String)} command does a similar work but the returned element is * not removed from the Set. *

* Time complexity O(1) * @param key * @return The removed member, or nil when key does not exist */ String spop(String key); /** * By default, the command {@link SetCommands#spop(String)} pops a single member from the set. * In this command, the reply will consist of up to count members, depending on the set's cardinality. *

* The {@link SetCommands#srandmember(String)} command does a similar work but the returned element is * not removed from the Set. *

* Time complexity O(N), where N is the value of the passed count * @param key * @param count * @return The removed members */ Set spop(String key, long count); /** * Return the set cardinality (number of elements). If the key does not exist 0 is returned, like * for empty sets. * @param key * @return The cardinality (number of elements) of the set */ long scard(String key); /** * Return true if member is a member of the set stored at key, otherwise false is returned. *

* Time complexity O(1) * @param key * @param member * @return {@code true} if the element is a member of the set, {@code false} otherwise */ boolean sismember(String key, String member); /** * Returns whether each member is a member of the set stored at key. *

* Time complexity O(N) where N is the number of elements being checked for membership * @param key * @param members * @return List representing the membership of the given elements, in the same order as they are requested */ List smismember(String key, String... members); /** * Return a random element from a Set, without removing the element. If the Set is empty or the * key does not exist, a nil object is returned. *

* The {@link SetCommands#spop(String) SPOP} command does a similar work but the returned element * is popped (removed) from the Set. *

* Time complexity O(1) * @param key * @return The randomly selected element */ String srandmember(String key); /** * Return a random elements from a Set, without removing the elements. If the Set is empty or the * key does not exist, an empty list is returned. *

* The {@link SetCommands#spop(String) SPOP} command does a similar work but the returned element * is popped (removed) from the Set. *

* Time complexity O(1) * @param key * @param count if positive, return an array of distinct elements. * If negative the behavior changes and the command is allowed to * return the same element multiple times * @return A list of randomly selected elements */ List srandmember(String key, int count); default ScanResult sscan(String key, String cursor) { return sscan(key, cursor, new ScanParams()); } ScanResult sscan(String key, String cursor, ScanParams params); /** * Return the difference between the Sets stored at {@code keys} *

* Example: * *

   * key1 = [x, a, b, c]
   * key2 = [c]
   * key3 = [a, d]
   * SDIFF key1,key2,key3 => [x, b]
   * 
* * Non existing keys are considered like empty sets. *

* Time complexity O(N) with N being the total number of elements of all the sets * @param keys group of sets * @return The members of a set resulting from the difference between the sets */ Set sdiff(String... keys); /** * This command works exactly like {@link SetCommands#sdiff(String...) SDIFF} but instead of being * returned the resulting set is stored in dstkey. * @param dstkey * @param keys group of sets * @return The number of elements in the resulting set */ long sdiffstore(String dstkey, String... keys); /** * Return the members of a set resulting from the intersection of all the sets hold at the * specified keys. Like in {@link ListCommands#lrange(String, long, long) LRANGE} the result is sent to * the connection as a multi-bulk reply (see the protocol specification for more information). If * just a single key is specified, then this command produces the same result as * {@link SetCommands#smembers(String) SMEMBERS}. Actually SMEMBERS is just syntax sugar for SINTER. *

* Non existing keys are considered like empty sets, so if one of the keys is missing an empty set * is returned (since the intersection with an empty set always is an empty set). *

* Time complexity O(N*M) worst case where N is the cardinality of the smallest set and M the * number of sets * @param keys group of sets * @return A set with members of the resulting set */ Set sinter(String... keys); /** * This command works exactly like {@link SetCommands#sinter(String...) SINTER} but instead of being * returned the resulting set is stored as dstkey. *

* Time complexity O(N*M) worst case where N is the cardinality of the smallest set and M the * number of sets * @param dstkey * @param keys group of sets * @return The number of elements in the resulting set */ long sinterstore(String dstkey, String... keys); /** * This command works exactly like {@link SetCommands#sinter(String[]) SINTER} but instead of returning * the result set, it returns just the cardinality of the result. LIMIT defaults to 0 and means unlimited *

* Time complexity O(N*M) worst case where N is the cardinality of the smallest * @param keys group of sets * @return The cardinality of the set which would result from the intersection of all the given sets */ long sintercard(String... keys); /** * This command works exactly like {@link SetCommands#sinter(String[]) SINTER} but instead of returning * the result set, it returns just the cardinality of the result. *

* Time complexity O(N*M) worst case where N is the cardinality of the smallest * @param limit If the intersection cardinality reaches limit partway through the computation, * the algorithm will exit and yield limit as the cardinality. * @param keys group of sets * @return The cardinality of the set which would result from the intersection of all the given sets */ long sintercard(int limit, String... keys); /** * Return the members of a set resulting from the union of all the sets hold at the specified * keys. Like in {@link ListCommands#lrange(String, long, long) LRANGE} the result is sent to the * connection as a multi-bulk reply (see the protocol specification for more information). If just * a single key is specified, then this command produces the same result as * {@link SetCommands#smembers(String) SMEMBERS}. *

* Non existing keys are considered like empty sets. *

* Time complexity O(N) where N is the total number of elements in all the provided sets * @param keys group of sets * @return A set with members of the resulting set */ Set sunion(String... keys); /** * This command works exactly like {@link SetCommands#sunion(String...) SUNION} but instead of being * returned the resulting set is stored as dstkey. Any existing value in dstkey will be * over-written. *

* Time complexity O(N) where N is the total number of elements in all the provided sets * @param dstkey * @param keys group of sets * @return The number of elements in the resulting set */ long sunionstore(String dstkey, String... keys); /** * Move the specified member from the set at srckey to the set at dstkey. This operation is * atomic, in every given moment the element will appear to be in the source or destination set * for accessing clients. *

* If the source set does not exist or does not contain the specified element no operation is * performed and zero is returned, otherwise the element is removed from the source set and added * to the destination set. On success one is returned, even if the element was already present in * the destination set. *

* An error is raised if the source or destination keys contain a non Set value. *

* Time complexity O(1) * @param srckey * @param dstkey * @param member * @return 1 if the element was moved, 0 if no operation was performed */ long smove(String srckey, String dstkey, String member); } ================================================ FILE: src/main/java/redis/clients/jedis/commands/SetPipelineBinaryCommands.java ================================================ package redis.clients.jedis.commands; import java.util.List; import java.util.Set; import redis.clients.jedis.Response; import redis.clients.jedis.params.ScanParams; import redis.clients.jedis.resps.ScanResult; public interface SetPipelineBinaryCommands { Response sadd(byte[] key, byte[]... members); Response> smembers(byte[] key); Response srem(byte[] key, byte[]... members); Response spop(byte[] key); Response> spop(byte[] key, long count); Response scard(byte[] key); Response sismember(byte[] key, byte[] member); Response> smismember(byte[] key, byte[]... members); Response srandmember(byte[] key); Response> srandmember(byte[] key, int count); default Response> sscan(byte[] key, byte[] cursor) { return sscan(key, cursor, new ScanParams()); } Response> sscan(byte[] key, byte[] cursor, ScanParams params); Response> sdiff(byte[]... keys); Response sdiffstore(byte[] dstkey, byte[]... keys); Response> sinter(byte[]... keys); Response sinterstore(byte[] dstkey, byte[]... keys); Response sintercard(byte[]... keys); Response sintercard(int limit, byte[]... keys); Response> sunion(byte[]... keys); Response sunionstore(byte[] dstkey, byte[]... keys); Response smove(byte[] srckey, byte[] dstkey, byte[] member); } ================================================ FILE: src/main/java/redis/clients/jedis/commands/SetPipelineCommands.java ================================================ package redis.clients.jedis.commands; import java.util.List; import java.util.Set; import redis.clients.jedis.Response; import redis.clients.jedis.params.ScanParams; import redis.clients.jedis.resps.ScanResult; public interface SetPipelineCommands { Response sadd(String key, String... members); Response> smembers(String key); Response srem(String key, String... members); Response spop(String key); Response> spop(String key, long count); Response scard(String key); Response sismember(String key, String member); Response> smismember(String key, String... members); Response srandmember(String key); Response> srandmember(String key, int count); default Response> sscan(String key, String cursor) { return sscan(key, cursor, new ScanParams()); } Response> sscan(String key, String cursor, ScanParams params); Response> sdiff(String... keys); Response sdiffstore(String dstKey, String... keys); /** * @deprecated Use {@link SetPipelineCommands#sdiffstore(java.lang.String, java.lang.String...)}. */ @Deprecated default Response sdiffStore(String dstKey, String... keys) { return sdiffstore(dstKey, keys); } Response> sinter(String... keys); Response sinterstore(String dstKey, String... keys); Response sintercard(String... keys); Response sintercard(int limit, String... keys); Response> sunion(String... keys); Response sunionstore(String dstKey, String... keys); Response smove(String srckey, String dstKey, String member); } ================================================ FILE: src/main/java/redis/clients/jedis/commands/SlowlogCommands.java ================================================ package redis.clients.jedis.commands; import java.util.List; import redis.clients.jedis.resps.Slowlog; public interface SlowlogCommands { String slowlogReset(); long slowlogLen(); List slowlogGet(); List slowlogGetBinary(); List slowlogGet(long entries); List slowlogGetBinary(long entries); } ================================================ FILE: src/main/java/redis/clients/jedis/commands/SortedSetBinaryCommands.java ================================================ package redis.clients.jedis.commands; import java.util.List; import java.util.Map; import redis.clients.jedis.args.SortedSetOption; import redis.clients.jedis.params.*; import redis.clients.jedis.resps.ScanResult; import redis.clients.jedis.resps.Tuple; import redis.clients.jedis.util.KeyValue; public interface SortedSetBinaryCommands { long zadd(byte[] key, double score, byte[] member); long zadd(byte[] key, double score, byte[] member, ZAddParams params); long zadd(byte[] key, Map scoreMembers); long zadd(byte[] key, Map scoreMembers, ZAddParams params); Double zaddIncr(byte[] key, double score, byte[] member, ZAddParams params); long zrem(byte[] key, byte[]... members); double zincrby(byte[] key, double increment, byte[] member); Double zincrby(byte[] key, double increment, byte[] member, ZIncrByParams params); Long zrank(byte[] key, byte[] member); Long zrevrank(byte[] key, byte[] member); KeyValue zrankWithScore(byte[] key, byte[] member); KeyValue zrevrankWithScore(byte[] key, byte[] member); List zrange(byte[] key, long start, long stop); /** * @deprecated Use {@link SortedSetBinaryCommands#zrange(byte[], ZRangeParams)} with {@link ZRangeParams#rev()}. * Deprecated in Jedis 7.3.0. Mirrors Redis deprecation since 6.2.0. */ @Deprecated List zrevrange(byte[] key, long start, long stop); List zrangeWithScores(byte[] key, long start, long stop); /** * @deprecated Use {@link SortedSetBinaryCommands#zrangeWithScores(byte[], ZRangeParams)} with {@link ZRangeParams#rev()}. * Deprecated in Jedis 7.3.0. Mirrors Redis deprecation since 6.2.0. */ @Deprecated List zrevrangeWithScores(byte[] key, long start, long stop); List zrange(byte[] key, ZRangeParams zRangeParams); List zrangeWithScores(byte[] key, ZRangeParams zRangeParams); long zrangestore(byte[] dest, byte[] src, ZRangeParams zRangeParams); byte[] zrandmember(byte[] key); List zrandmember(byte[] key, long count); List zrandmemberWithScores(byte[] key, long count); long zcard(byte[] key); Double zscore(byte[] key, byte[] member); List zmscore(byte[] key, byte[]... members); Tuple zpopmax(byte[] key); List zpopmax(byte[] key, int count); Tuple zpopmin(byte[] key); List zpopmin(byte[] key, int count); long zcount(byte[] key, double min, double max); long zcount(byte[] key, byte[] min, byte[] max); /** * @deprecated Use {@link SortedSetBinaryCommands#zrange(byte[], ZRangeParams)} with {@link ZRangeParams#zrangeByScoreParams(double, double)}. * Deprecated in Jedis 7.3.0. Mirrors Redis deprecation since 6.2.0. */ @Deprecated List zrangeByScore(byte[] key, double min, double max); /** * @deprecated Use {@link SortedSetBinaryCommands#zrange(byte[], ZRangeParams)} with {@link ZRangeParams#zrangeByScoreParams(double, double)}. * Deprecated in Jedis 7.3.0. Mirrors Redis deprecation since 6.2.0. */ @Deprecated List zrangeByScore(byte[] key, byte[] min, byte[] max); /** * @deprecated Use {@link SortedSetBinaryCommands#zrange(byte[], ZRangeParams)} with {@link ZRangeParams#zrangeByScoreParams(double, double)} and {@link ZRangeParams#rev()}. * Deprecated in Jedis 7.3.0. Mirrors Redis deprecation since 6.2.0. */ @Deprecated List zrevrangeByScore(byte[] key, double max, double min); /** * @deprecated Use {@link SortedSetBinaryCommands#zrange(byte[], ZRangeParams)} with {@link ZRangeParams#zrangeByScoreParams(double, double)}. * Deprecated in Jedis 7.3.0. Mirrors Redis deprecation since 6.2.0. */ @Deprecated List zrangeByScore(byte[] key, double min, double max, int offset, int count); /** * @deprecated Use {@link SortedSetBinaryCommands#zrange(byte[], ZRangeParams)} with {@link ZRangeParams#zrangeByScoreParams(double, double)} and {@link ZRangeParams#rev()}. * Deprecated in Jedis 7.3.0. Mirrors Redis deprecation since 6.2.0. */ @Deprecated List zrevrangeByScore(byte[] key, byte[] max, byte[] min); /** * @deprecated Use {@link SortedSetBinaryCommands#zrange(byte[], ZRangeParams)} with {@link ZRangeParams#zrangeByScoreParams(double, double)}. * Deprecated in Jedis 7.3.0. Mirrors Redis deprecation since 6.2.0. */ @Deprecated List zrangeByScore(byte[] key, byte[] min, byte[] max, int offset, int count); /** * @deprecated Use {@link SortedSetBinaryCommands#zrange(byte[], ZRangeParams)} with {@link ZRangeParams#zrangeByScoreParams(double, double)} and {@link ZRangeParams#rev()}. * Deprecated in Jedis 7.3.0. Mirrors Redis deprecation since 6.2.0. */ @Deprecated List zrevrangeByScore(byte[] key, double max, double min, int offset, int count); /** * @deprecated Use {@link SortedSetBinaryCommands#zrangeWithScores(byte[], ZRangeParams)} with {@link ZRangeParams#zrangeByScoreParams(double, double)}. * Deprecated in Jedis 7.3.0. Mirrors Redis deprecation since 6.2.0. */ @Deprecated List zrangeByScoreWithScores(byte[] key, double min, double max); /** * @deprecated Use {@link SortedSetBinaryCommands#zrangeWithScores(byte[], ZRangeParams)} with {@link ZRangeParams#zrangeByScoreParams(double, double)} and {@link ZRangeParams#rev()}. * Deprecated in Jedis 7.3.0. Mirrors Redis deprecation since 6.2.0. */ @Deprecated List zrevrangeByScoreWithScores(byte[] key, double max, double min); /** * @deprecated Use {@link SortedSetBinaryCommands#zrangeWithScores(byte[], ZRangeParams)} with {@link ZRangeParams#zrangeByScoreParams(double, double)}. * Deprecated in Jedis 7.3.0. Mirrors Redis deprecation since 6.2.0. */ @Deprecated List zrangeByScoreWithScores(byte[] key, double min, double max, int offset, int count); /** * @deprecated Use {@link SortedSetBinaryCommands#zrange(byte[], ZRangeParams)} with {@link ZRangeParams#zrangeByScoreParams(double, double)} and {@link ZRangeParams#rev()}. * Deprecated in Jedis 7.3.0. Mirrors Redis deprecation since 6.2.0. */ @Deprecated List zrevrangeByScore(byte[] key, byte[] max, byte[] min, int offset, int count); /** * @deprecated Use {@link SortedSetBinaryCommands#zrangeWithScores(byte[], ZRangeParams)} with {@link ZRangeParams#zrangeByScoreParams(double, double)}. * Deprecated in Jedis 7.3.0. Mirrors Redis deprecation since 6.2.0. */ @Deprecated List zrangeByScoreWithScores(byte[] key, byte[] min, byte[] max); /** * @deprecated Use {@link SortedSetBinaryCommands#zrangeWithScores(byte[], ZRangeParams)} with {@link ZRangeParams#zrangeByScoreParams(double, double)} and {@link ZRangeParams#rev()}. * Deprecated in Jedis 7.3.0. Mirrors Redis deprecation since 6.2.0. */ @Deprecated List zrevrangeByScoreWithScores(byte[] key, byte[] max, byte[] min); /** * @deprecated Use {@link SortedSetBinaryCommands#zrangeWithScores(byte[], ZRangeParams)} with {@link ZRangeParams#zrangeByScoreParams(double, double)}. * Deprecated in Jedis 7.3.0. Mirrors Redis deprecation since 6.2.0. */ @Deprecated List zrangeByScoreWithScores(byte[] key, byte[] min, byte[] max, int offset, int count); /** * @deprecated Use {@link SortedSetBinaryCommands#zrangeWithScores(byte[], ZRangeParams)} with {@link ZRangeParams#zrangeByScoreParams(double, double)} and {@link ZRangeParams#rev()}. * Deprecated in Jedis 7.3.0. Mirrors Redis deprecation since 6.2.0. */ @Deprecated List zrevrangeByScoreWithScores(byte[] key, double max, double min, int offset, int count); /** * @deprecated Use {@link SortedSetBinaryCommands#zrangeWithScores(byte[], ZRangeParams)} with {@link ZRangeParams#zrangeByScoreParams(double, double)} and {@link ZRangeParams#rev()}. * Deprecated in Jedis 7.3.0. Mirrors Redis deprecation since 6.2.0. */ @Deprecated List zrevrangeByScoreWithScores(byte[] key, byte[] max, byte[] min, int offset, int count); long zremrangeByRank(byte[] key, long start, long stop); long zremrangeByScore(byte[] key, double min, double max); long zremrangeByScore(byte[] key, byte[] min, byte[] max); long zlexcount(byte[] key, byte[] min, byte[] max); /** * @deprecated Use {@link SortedSetBinaryCommands#zrange(byte[], ZRangeParams)} with {@link ZRangeParams#zrangeByLexParams(byte[], byte[])}. * Deprecated in Jedis 7.3.0. Mirrors Redis deprecation since 6.2.0. */ @Deprecated List zrangeByLex(byte[] key, byte[] min, byte[] max); /** * @deprecated Use {@link SortedSetBinaryCommands#zrange(byte[], ZRangeParams)} with {@link ZRangeParams#zrangeByLexParams(byte[], byte[])}. * Deprecated in Jedis 7.3.0. Mirrors Redis deprecation since 6.2.0. */ @Deprecated List zrangeByLex(byte[] key, byte[] min, byte[] max, int offset, int count); /** * @deprecated Use {@link SortedSetBinaryCommands#zrange(byte[], ZRangeParams)} with {@link ZRangeParams#zrangeByLexParams(byte[], byte[])} and {@link ZRangeParams#rev()}. * Deprecated in Jedis 7.3.0. Mirrors Redis deprecation since 6.2.0. */ @Deprecated List zrevrangeByLex(byte[] key, byte[] max, byte[] min); /** * @deprecated Use {@link SortedSetBinaryCommands#zrange(byte[], ZRangeParams)} with {@link ZRangeParams#zrangeByLexParams(byte[], byte[])} and {@link ZRangeParams#rev()}. * Deprecated in Jedis 7.3.0. Mirrors Redis deprecation since 6.2.0. */ @Deprecated List zrevrangeByLex(byte[] key, byte[] max, byte[] min, int offset, int count); long zremrangeByLex(byte[] key, byte[] min, byte[] max); default ScanResult zscan(byte[] key, byte[] cursor) { return zscan(key, cursor, new ScanParams()); } ScanResult zscan(byte[] key, byte[] cursor, ScanParams params); KeyValue bzpopmax(double timeout, byte[]... keys); KeyValue bzpopmin(double timeout, byte[]... keys); List zdiff(byte[]... keys); List zdiffWithScores(byte[]... keys); /** * @deprecated Use {@link #zdiffstore(byte[], byte[][])}. */ @Deprecated long zdiffStore(byte[] dstkey, byte[]... keys); long zdiffstore(byte[] dstkey, byte[]... keys); List zinter(ZParams params, byte[]... keys); List zinterWithScores(ZParams params, byte[]... keys); long zinterstore(byte[] dstkey, byte[]... sets); long zinterstore(byte[] dstkey, ZParams params, byte[]... sets); /** * Similar to {@link #zinter(ZParams, byte[][]) ZINTER}, but instead of returning the result set, * it returns just the cardinality of the result. *

* Time complexity O(N*K) worst case with N being the smallest input sorted set, K * being the number of input sorted sets * @see #zinter(ZParams, byte[][]) * @param keys group of sets * @return The number of elements in the resulting intersection */ long zintercard(byte[]... keys); /** * Similar to {@link #zinter(ZParams, byte[][]) ZINTER}, but instead of returning the result set, * it returns just the cardinality of the result. *

* Time complexity O(N*K) worst case with N being the smallest input sorted set, K * being the number of input sorted sets * @see #zinter(ZParams, byte[][]) * @param limit If the intersection cardinality reaches limit partway through the computation, * the algorithm will exit and yield limit as the cardinality * @param keys group of sets * @return The number of elements in the resulting intersection */ long zintercard(long limit, byte[]... keys); List zunion(ZParams params, byte[]... keys); List zunionWithScores(ZParams params, byte[]... keys); long zunionstore(byte[] dstkey, byte[]... sets); long zunionstore(byte[] dstkey, ZParams params, byte[]... sets); KeyValue> zmpop(SortedSetOption option, byte[]... keys); KeyValue> zmpop(SortedSetOption option, int count, byte[]... keys); KeyValue> bzmpop(double timeout, SortedSetOption option, byte[]... keys); KeyValue> bzmpop(double timeout, SortedSetOption option, int count, byte[]... keys); } ================================================ FILE: src/main/java/redis/clients/jedis/commands/SortedSetCommands.java ================================================ package redis.clients.jedis.commands; import java.util.List; import java.util.Map; import java.util.Set; import redis.clients.jedis.args.SortedSetOption; import redis.clients.jedis.params.*; import redis.clients.jedis.resps.ScanResult; import redis.clients.jedis.resps.Tuple; import redis.clients.jedis.util.KeyValue; public interface SortedSetCommands { /** * Add the specified member having the specified score to the sorted set stored at key. If member * is already a member of the sorted set the score is updated, and the element reinserted in the * right position to ensure sorting. If key does not exist a new sorted set with the specified * member as sole member is created. If the key exists but does not hold a sorted set value an * error is returned. *

* The score value can be the string representation of a double precision floating point number. *

* Time complexity O(log(N)) with N being the number of elements in the sorted set * @param key * @param score * @param member * @return 1 if the new element was added, 0 if the element was already a member of the sorted * set and the score was updated */ long zadd(String key, double score, String member); /** * Similar to {@link SortedSetCommands#zadd(String, double, String) ZADD} but can be used with optional params. * @see SortedSetCommands#zadd(String, double, String) * @param key * @param score * @param member * @param params {@link ZAddParams} * @return 1 if the new element was added, 0 if the element was already a member of the sorted * set and the score was updated */ long zadd(String key, double score, String member, ZAddParams params); /** * Similar to {@link SortedSetCommands#zadd(String, double, String) ZADD} but for multiple members. * @see SortedSetCommands#zadd(String, double, String) * @param key * @param scoreMembers * @return The number of elements added to the sorted set (excluding score updates). */ long zadd(String key, Map scoreMembers); /** * Similar to {@link SortedSetCommands#zadd(String, double, String) ZADD} but can be used with optional params, * and fits for multiple members. * @see SortedSetCommands#zadd(String, double, String) * @param key * @param scoreMembers * @param params {@link ZAddParams} * @return The number of elements added to the sorted set (excluding score updates). */ long zadd(String key, Map scoreMembers, ZAddParams params); /** * Increments the score of member in the sorted set stored at key by increment. If member does not * exist in the sorted set, it is added with increment as its score (as if its previous score was 0.0). * If key does not exist, a new sorted set with the specified member as its sole member is created. *

* The score value should be the string representation of a numeric value, and accepts double precision * floating point numbers. It is possible to provide a negative value to decrement the score. *

* Time complexity O(log(N)) with N being the number of elements in the sorted set * @param key * @param score * @param member * @param params {@link ZAddParams} * @return 1 if the new element was added, 0 if the element was already a member of the sorted * set and the score was updated */ Double zaddIncr(String key, double score, String member, ZAddParams params); /** * Remove the specified member from the sorted set value stored at key. If member was not a member * of the set no operation is performed. If key does not hold a set value an error is returned. *

* Time complexity O(log(N)) with N being the number of elements in the sorted set * @param key * @param members * @return 1 if the new element was removed, 0 if the new element was not a member of the set */ long zrem(String key, String... members); /** * If member already exists in the sorted set adds the increment to its score and updates the * position of the element in the sorted set accordingly. If member does not already exist in the * sorted set it is added with increment as score (that is, like if the previous score was * virtually zero). If key does not exist a new sorted set with the specified member as sole * member is created. If the key exists but does not hold a sorted set value an error is returned. *

* The score value can be the string representation of a double precision floating point number. * It's possible to provide a negative value to perform a decrement. *

* For an introduction to sorted sets check the Introduction to Redis data types page. *

* Time complexity O(log(N)) with N being the number of elements in the sorted set * @param key * @param increment * @param member * @return The new score */ double zincrby(String key, double increment, String member); /** * Similar to {@link SortedSetCommands#zincrby(String, double, String) ZINCRBY} but can be used with optionals params. * @see SortedSetCommands#zincrby(String, double, String) * @param key * @param increment * @param member * @param params {@link ZIncrByParams} * @return The new score for key */ Double zincrby(String key, double increment, String member, ZIncrByParams params); /** * Return the rank (or index) of member in the sorted set at key, with scores being ordered from * low to high. *

* When the given member does not exist in the sorted set, the special value 'nil' is returned. * The returned rank (or index) of the member is 0-based for both commands. *

* Time complexity O(log(N)) * @param key * @param member * @return The rank of the element as an integer reply if the element exists. A nil bulk reply * if there is no such element */ Long zrank(String key, String member); /** * Return the rank (or index) of member in the sorted set at key, with scores being ordered from * high to low. *

* When the given member does not exist in the sorted set, the special value 'nil' is returned. * The returned rank (or index) of the member is 0-based for both commands. *

* Time complexity O(log(N)) * @param key * @param member * @return The rank of the element as an integer reply if the element exists. A nil bulk reply * if there is no such element */ Long zrevrank(String key, String member); /** * Returns the rank and the score of member in the sorted set stored at key, with the scores * ordered from low to high. * @param key the key * @param member the member * @return the KeyValue contains rank and score. */ KeyValue zrankWithScore(String key, String member); /** * Returns the rank and the score of member in the sorted set stored at key, with the scores * ordered from high to low. * @param key the key * @param member the member * @return the KeyValue contains rank and score. */ KeyValue zrevrankWithScore(String key, String member); /** * Returns the specified range of elements in the sorted set stored at key. *

* Time complexity O(log(N)+M) with N being the number of elements in the sorted set and M the * number of elements returned. * @param key the key to query * @param start the minimum index * @param stop the maximum index * @return A List of Strings in the specified range */ List zrange(String key, long start, long stop); /** * Returns the specified range of elements in the sorted set stored at key. The elements are * considered to be ordered from the highest to the lowest score. Descending lexicographical * order is used for elements with equal score. *

* Time complexity O(log(N)+M) with N being the number of elements in the sorted set and M the * number of elements returned. * @param key the key to query * @param start the minimum index * @param stop the maximum index * @return A List of Strings in the specified range * @deprecated Use {@link SortedSetCommands#zrange(String, ZRangeParams)} with {@link ZRangeParams#rev()}. * Deprecated in Jedis 7.3.0. Mirrors Redis deprecation since 6.2.0. */ @Deprecated List zrevrange(String key, long start, long stop); /** * Returns the specified range of elements in the sorted set stored at key with the scores. * @param key the key to query * @param start the minimum index * @param stop the maximum index * @return A List of Tuple in the specified range (elements names and their scores) */ List zrangeWithScores(String key, long start, long stop); /** * Similar to {@link SortedSetCommands#zrevrange(String, long, long) ZREVRANGE} but the reply will * include the scores of the returned elements. * @see SortedSetCommands#zrevrange(String, long, long) * @param key the key to query * @param start the minimum index * @param stop the maximum index * @return A List of Tuple in the specified range (elements names and their scores) * @deprecated Use {@link SortedSetCommands#zrangeWithScores(String, ZRangeParams)} with {@link ZRangeParams#rev()}. * Deprecated in Jedis 7.3.0. Mirrors Redis deprecation since 6.2.0. */ @Deprecated List zrevrangeWithScores(String key, long start, long stop); /** * Similar to {@link SortedSetCommands#zrange(String, long, long) ZRANGE} but can be used with additional params. * @see SortedSetCommands#zrange(String, long, long) * @param key the key to query * @param zRangeParams {@link ZRangeParams} * @return A List of Strings in the specified range */ List zrange(String key, ZRangeParams zRangeParams); /** * Similar to {@link SortedSetCommands#zrangeWithScores(String, long, long) ZRANGE} but can be used with additional params. * @see SortedSetCommands#zrangeWithScores(String, long, long) * @param key the key to query * @param zRangeParams {@link ZRangeParams} * @return A List of Tuple in the specified range (elements names and their scores) */ List zrangeWithScores(String key, ZRangeParams zRangeParams); /** * Similar to {@link SortedSetCommands#zrange(String, ZRangeParams) ZRANGE} but stores the result in {@code dest}. * @see SortedSetCommands#zrange(String, ZRangeParams) * @param dest the storing key * @param src the key to query * @param zRangeParams {@link ZRangeParams} * @return The number of elements in the resulting sorted set */ long zrangestore(String dest, String src, ZRangeParams zRangeParams); /** * Return a random element from the sorted set value stored at key. *

* Time complexity O(N) where N is the number of elements returned * @param key * @return Random String from the set */ String zrandmember(String key); /** * Return an array of distinct elements. The array's length is either count or the sorted set's * cardinality ({@link SortedSetCommands#zcard(String) ZCARD}), whichever is lower. *

* Time complexity O(N) where N is the number of elements returned * @param key * @param count choose up to count elements * @return A list of distinct Strings from the set */ List zrandmember(String key, long count); /** * Similar to {@link SortedSetCommands#zrandmember(String, long) ZRANDMEMBER} but the replay will * include the scores with the result. * @see SortedSetCommands#zrandmember(String, long) * @param key * @param count choose up to count elements * @return A List of distinct Strings with their scores */ List zrandmemberWithScores(String key, long count); /** * Return the sorted set cardinality (number of elements). If the key does not exist 0 is * returned, like for empty sorted sets. *

* Time complexity O(1) * @param key * @return The cardinality (number of elements) of the set as an integer */ long zcard(String key); /** * Return the score of the specified element of the sorted set at key. If the specified element * does not exist in the sorted set, or the key does not exist at all, a special 'nil' value is * returned. *

* Time complexity O(1) * @param key * @param member * @return The score */ Double zscore(String key, String member); /** * Return the scores associated with the specified members in the sorted set stored at key. * For every member that does not exist in the sorted set, a nil value is returned. *

* Time complexity O(N) where N is the number of members being requested * @param key * @param members * @return The scores */ List zmscore(String key, String... members); /** * Remove and return the member with the highest score in the sorted set stored at key. *

* Time complexity O(log(N)) with N being the number of elements in the sorted set * @param key * @return The popped element and the score */ Tuple zpopmax(String key); /** * Remove and return up to count members with the highest scores in the sorted set stored at key. *

* Time complexity O(log(N)*M) with N being the number of elements in the sorted set, and M being * the number of elements popped. * @param key * @param count the number of elements to pop * @return A List of popped elements and scores */ List zpopmax(String key, int count); /** * Remove and return the member with the lowest score in the sorted set stored at key. *

* Time complexity O(log(N)) with N being the number of elements in the sorted set * @param key * @return The popped element and the score */ Tuple zpopmin(String key); /** * Remove and return up to count members with the lowest scores in the sorted set stored at key. *

* Time complexity O(log(N)*M) with N being the number of elements in the sorted set, and M being * the number of elements popped. * @param key * @param count the number of elements to pop * @return A List of popped elements and scores */ List zpopmin(String key, int count); /** * Return the number of elements in the sorted set at key with a score between min and max. *

* Time complexity O(log(N)) with N being the number of elements in the sorted set. * @param key the key to query * @param min minimum score * @param max maximum score * @return The number of elements in the specified score range. */ long zcount(String key, double min, double max); /** * Similar to {@link SortedSetCommands#zcount(String, double, double) ZCOUNT} but with exclusive range. * @see SortedSetCommands#zcount(String, double, double) */ long zcount(String key, String min, String max); /** * Return all the elements in the sorted set at key with a score between min and max * (including elements with score equal to min or max). The elements are considered to * be ordered from low to high scores. *

* Time complexity O(log(N)+M) with N being the number of elements in the sorted set * and M the number of elements being returned. * @param key the key to query * @param min minimum score * @param max maximum score * @return A List of elements in the specified score range * @deprecated Use {@link SortedSetCommands#zrange(String, ZRangeParams)} with {@link ZRangeParams#zrangeByScoreParams(double, double)}. * Deprecated in Jedis 7.3.0. Mirrors Redis deprecation since 6.2.0. */ @Deprecated List zrangeByScore(String key, double min, double max); /** * Similar to {@link SortedSetCommands#zrangeByScore(String, double, double) ZRANGE} but with exclusive range. * @see SortedSetCommands#zrangeByScore(String, double, double) * @deprecated Use {@link SortedSetCommands#zrange(String, ZRangeParams)} with {@link ZRangeParams#zrangeByScoreParams(double, double)}. * Deprecated in Jedis 7.3.0. Mirrors Redis deprecation since 6.2.0. */ @Deprecated List zrangeByScore(String key, String min, String max); /** * Return all the elements in the sorted set at key with a score between max and min * (including elements with score equal to max or min). In contrary to the default * ordering of sorted sets, for this command the elements are considered to be ordered * from high to low scores. *

* The elements having the same score are returned in reverse lexicographical order. *

* Time complexity O(log(N)+M) with N being the number of elements in the sorted set * and M the number of elements being returned. * @param key the key to query * @param max maximum score * @param min minimum score * @return A List of elements in the specified score range * @deprecated Use {@link SortedSetCommands#zrange(String, ZRangeParams)} with {@link ZRangeParams#zrangeByScoreParams(double, double)} and {@link ZRangeParams#rev()}. * Deprecated in Jedis 7.3.0. Mirrors Redis deprecation since 6.2.0. */ @Deprecated List zrevrangeByScore(String key, double max, double min); /** * Similar to {@link SortedSetCommands#zrangeByScore(String, double, double) ZRANGE} but with exclusive range. * @see SortedSetCommands#zrangeByScore(String, double, double) * @param key the key to query * @param min minimum score * @param max maximum score * @param offset the first index of the sub-range * @param count count of the sub-range. A negative count returns all elements from the offset * @return A List of elements in the specified score range * @deprecated Use {@link SortedSetCommands#zrange(String, ZRangeParams)} with {@link ZRangeParams#zrangeByScoreParams(double, double)}. * Deprecated in Jedis 7.3.0. Mirrors Redis deprecation since 6.2.0. */ @Deprecated List zrangeByScore(String key, double min, double max, int offset, int count); /** * Similar to {@link SortedSetCommands#zrevrangeByScore(String, double, double) ZREVRANGE} but with exclusive range. * @see SortedSetCommands#zrevrangeByScore(String, double, double) * @deprecated Use {@link SortedSetCommands#zrange(String, ZRangeParams)} with {@link ZRangeParams#zrangeByScoreParams(double, double)} and {@link ZRangeParams#rev()}. * Deprecated in Jedis 7.3.0. Mirrors Redis deprecation since 6.2.0. */ @Deprecated List zrevrangeByScore(String key, String max, String min); /** * Similar to {@link SortedSetCommands#zrangeByScore(String, double, double) ZRANGE} but with limit option, * @see SortedSetCommands#zrangeByScore(String, double, double) * and with exclusive range. * @param key the key to query * @param min minimum score * @param max maximum score * @param offset the first index of the sub-range * @param count count of the sub-range. A negative count returns all elements from the offset * @return A List of elements in the specified score range * @deprecated Use {@link SortedSetCommands#zrange(String, ZRangeParams)} with {@link ZRangeParams#zrangeByScoreParams(double, double)}. * Deprecated in Jedis 7.3.0. Mirrors Redis deprecation since 6.2.0. */ @Deprecated List zrangeByScore(String key, String min, String max, int offset, int count); /** * Similar to {@link SortedSetCommands#zrevrangeByScore(String, double, double) ZRANGE} but with limit option, * @see SortedSetCommands#zrevrangeByScore(String, double, double) * @param key the key to query * @param max maximum score * @param min minimum score * @param offset the first index of the sub-range * @param count count of the sub-range. A negative count returns all elements from the offset * @return A List of elements in the specified score range * @deprecated Use {@link SortedSetCommands#zrange(String, ZRangeParams)} with {@link ZRangeParams#zrangeByScoreParams(double, double)} and {@link ZRangeParams#rev()}. * Deprecated in Jedis 7.3.0. Mirrors Redis deprecation since 6.2.0. */ @Deprecated List zrevrangeByScore(String key, double max, double min, int offset, int count); /** * Similar to {@link SortedSetCommands#zrangeByScore(String, double, double) ZRANGE} but return with scores. * @see SortedSetCommands#zrangeByScore(String, double, double) * return both the element and its score, instead of the element alone. * @param key the key to query * @param min minimum score * @param max maximum score * @return A List of elements with scores in the specified score range * @deprecated Use {@link SortedSetCommands#zrangeWithScores(String, ZRangeParams)} with {@link ZRangeParams#zrangeByScoreParams(double, double)}. * Deprecated in Jedis 7.3.0. Mirrors Redis deprecation since 6.2.0. */ @Deprecated List zrangeByScoreWithScores(String key, double min, double max); /** * Similar to {@link SortedSetCommands#zrevrangeByScore(String, double, double) ZREVRANGE} but return with scores. * @see SortedSetCommands#zrevrangeByScore(String, double, double) * return both the element and its score, instead of the element alone. * @param key the key to query * @param max maximum score * @param min minimum score * @return A List of elements with scores in the specified score range * @deprecated Use {@link SortedSetCommands#zrangeWithScores(String, ZRangeParams)} with {@link ZRangeParams#zrangeByScoreParams(double, double)} and {@link ZRangeParams#rev()}. * Deprecated in Jedis 7.3.0. Mirrors Redis deprecation since 6.2.0. */ @Deprecated List zrevrangeByScoreWithScores(String key, double max, double min); /** * Similar to {@link SortedSetCommands#zrangeByScore(String, double, double) ZRANGE} but with limit option, * and return with scores. * @see SortedSetCommands#zrangeByScore(String, double, double) * @param key the key to query * @param min minimum score * @param max maximum score * @param offset the first index of the sub-range * @param count count of the sub-range. A negative count returns all elements from the offset * @return A List of elements in the specified score range * @deprecated Use {@link SortedSetCommands#zrangeWithScores(String, ZRangeParams)} with {@link ZRangeParams#zrangeByScoreParams(double, double)}. * Deprecated in Jedis 7.3.0. Mirrors Redis deprecation since 6.2.0. */ @Deprecated List zrangeByScoreWithScores(String key, double min, double max, int offset, int count); /** * Similar to {@link SortedSetCommands#zrevrangeByScore(String, double, double) ZREVRANGE} but with limit option, * @see SortedSetCommands#zrevrangeByScore(String, double, double) * and with exclusive range. * @param key the key to query * @param max maximum score * @param min minimum score * @param offset the first index of the sub-range * @param count count of the sub-range. A negative count returns all elements from the offset * @return A List of elements in the specified score range * @deprecated Use {@link SortedSetCommands#zrange(String, ZRangeParams)} with {@link ZRangeParams#zrangeByScoreParams(double, double)} and {@link ZRangeParams#rev()}. * Deprecated in Jedis 7.3.0. Mirrors Redis deprecation since 6.2.0. */ @Deprecated List zrevrangeByScore(String key, String max, String min, int offset, int count); /** * Similar to {@link SortedSetCommands#zrangeByScore(String, double, double) ZRANGE} but with exclusive range, * and return with scores. * @see SortedSetCommands#zrangeByScore(String, double, double) * @deprecated Use {@link SortedSetCommands#zrangeWithScores(String, ZRangeParams)} with {@link ZRangeParams#zrangeByScoreParams(double, double)}. * Deprecated in Jedis 7.3.0. Mirrors Redis deprecation since 6.2.0. */ @Deprecated List zrangeByScoreWithScores(String key, String min, String max); /** * Similar to {@link SortedSetCommands#zrevrangeByScore(String, double, double) ZREVRANGE} but with exclusive range, * and return with scores. * @see SortedSetCommands#zrevrangeByScore(String, double, double) * @deprecated Use {@link SortedSetCommands#zrangeWithScores(String, ZRangeParams)} with {@link ZRangeParams#zrangeByScoreParams(double, double)} and {@link ZRangeParams#rev()}. * Deprecated in Jedis 7.3.0. Mirrors Redis deprecation since 6.2.0. */ @Deprecated List zrevrangeByScoreWithScores(String key, String max, String min); /** * Similar to {@link SortedSetCommands#zrangeByScore(String, double, double) ZRANGE} but with exclusive range, * with limit options and return with scores. * @see SortedSetCommands#zrangeByScore(String, String, String) * @param key the key to query * @param min minimum score * @param max maximum score * @param offset the first index of the sub-range * @param count count of the sub-range. A negative count returns all elements from the offset * @return A List of elements in the specified score range * @deprecated Use {@link SortedSetCommands#zrangeWithScores(String, ZRangeParams)} with {@link ZRangeParams#zrangeByScoreParams(double, double)}. * Deprecated in Jedis 7.3.0. Mirrors Redis deprecation since 6.2.0. */ @Deprecated List zrangeByScoreWithScores(String key, String min, String max, int offset, int count); /** * Similar to {@link SortedSetCommands#zrevrangeByScore(String, double, double) ZREVRANGE} but with * limit options and return with scores. * @see SortedSetCommands#zrevrangeByScore(String, double, double) * @param key the key to query * @param max maximum score * @param min minimum score * @param offset the first index of the sub-range * @param count count of the sub-range. A negative count returns all elements from the offset * @return A List of elements in the specified score range * @deprecated Use {@link SortedSetCommands#zrangeWithScores(String, ZRangeParams)} with {@link ZRangeParams#zrangeByScoreParams(double, double)} and {@link ZRangeParams#rev()}. * Deprecated in Jedis 7.3.0. Mirrors Redis deprecation since 6.2.0. */ @Deprecated List zrevrangeByScoreWithScores(String key, double max, double min, int offset, int count); /** * Similar to {@link SortedSetCommands#zrevrangeByScore(String, double, double) ZREVRANGE} but with * exclusive range, with limit options and return with scores. * @see SortedSetCommands#zrevrangeByScore(String, double, double) * @param key the key to query * @param max maximum score * @param min minimum score * @param offset the first index of the sub-range * @param count count of the sub-range. A negative count returns all elements from the offset * @return A List of elements in the specified score range * @deprecated Use {@link SortedSetCommands#zrangeWithScores(String, ZRangeParams)} with {@link ZRangeParams#zrangeByScoreParams(double, double)} and {@link ZRangeParams#rev()}. * Deprecated in Jedis 7.3.0. Mirrors Redis deprecation since 6.2.0. */ @Deprecated List zrevrangeByScoreWithScores(String key, String max, String min, int offset, int count); /** * Remove all elements in the sorted set at key with rank between start and end. Start and end are * 0-based with rank 0 being the element with the lowest score. Both start and end can be negative * numbers, where they indicate offsets starting at the element with the highest rank. For * example: -1 is the element with the highest score, -2 the element with the second highest score * and so forth. *

* Time complexity O(log(N))+O(M) with N being the number of elements in the sorted set and M the * number of elements removed by the operation. * @param key * @param start * @param stop * @return The number of elements removed */ long zremrangeByRank(String key, long start, long stop); /** * Remove all the elements in the sorted set at key with a score between min and max (including * elements with score equal to min or max). *

* Time complexity O(log(N))+O(M) with N being the number of elements in the sorted set and M the * number of elements removed by the operation. * @param key * @param min minimum score to remove * @param max maximum score to remove * @return The number of elements removed */ long zremrangeByScore(String key, double min, double max); /** * Similar to {@link SortedSetCommands#zremrangeByScore(String, double, double) ZREMRANGE} but with limit option. * @see SortedSetCommands#zremrangeByScore(String, double, double) */ long zremrangeByScore(String key, String min, String max); /** * Return the number of elements in the sorted set at key with a value between min and max, when all * the elements in a sorted set are inserted with the same score, in order to force lexicographical ordering. *

* Time complexity O(log(N)) with N being the number of elements in the sorted set. * @param key * @param min minimum value * @param max maximum value * @return The number of elements in the specified score range */ long zlexcount(String key, String min, String max); /** * Return all the elements in the sorted set at key with a value between min and max, when all * the elements in a sorted set are inserted with the same score, in order to force lexicographical ordering. *

* Time complexity O(log(N)+M) with N being the number of elements in the sorted set and M the number of * elements being returned. * @param key * @param min minimum value * @param max maximum value * @return A List of elements in the specified score range * @deprecated Use {@link SortedSetCommands#zrange(String, ZRangeParams)} with {@link ZRangeParams#zrangeByLexParams(String, String)}. * Deprecated in Jedis 7.3.0. Mirrors Redis deprecation since 6.2.0. */ @Deprecated List zrangeByLex(String key, String min, String max); /** * Similar to {@link SortedSetCommands#zrangeByLex(String, String, String) ZRANGE} but with limit option. * @see SortedSetCommands#zrangeByLex(String, String, String) * @param key * @param min minimum value * @param max maximum value * @param offset the first index of the sub-range * @param count count of the sub-range. A negative count returns all elements from the offset * @return A List of elements in the specified score range * @deprecated Use {@link SortedSetCommands#zrange(String, ZRangeParams)} with {@link ZRangeParams#zrangeByLexParams(String, String)}. * Deprecated in Jedis 7.3.0. Mirrors Redis deprecation since 6.2.0. */ @Deprecated List zrangeByLex(String key, String min, String max, int offset, int count); /** * Return all the elements in the sorted set at key with a value between max and min, when all * the elements in a sorted set are inserted with the same score, in order to force lexicographical ordering. *

* Time complexity O(log(N)+M) with N being the number of elements in the sorted set and M the number of * elements being returned. * @param key * @param max maximum value * @param min minimum value * @return A List of elements in the specified score range * @deprecated Use {@link SortedSetCommands#zrange(String, ZRangeParams)} with {@link ZRangeParams#zrangeByLexParams(String, String)} and {@link ZRangeParams#rev()}. * Deprecated in Jedis 7.3.0. Mirrors Redis deprecation since 6.2.0. */ @Deprecated List zrevrangeByLex(String key, String max, String min); /** * Similar to {@link SortedSetCommands#zrevrangeByLex(String, String, String) ZRANGE} but with limit option. * @see SortedSetCommands#zrevrangeByLex(String, String, String) * @param key * @param max maximum value * @param min minimum value * @param offset the first index of the sub-range * @param count count of the sub-range. A negative count returns all elements from the offset * @return A List of elements in the specified score range * @deprecated Use {@link SortedSetCommands#zrange(String, ZRangeParams)} with {@link ZRangeParams#zrangeByLexParams(String, String)} and {@link ZRangeParams#rev()}. * Deprecated in Jedis 7.3.0. Mirrors Redis deprecation since 6.2.0. */ @Deprecated List zrevrangeByLex(String key, String max, String min, int offset, int count); /** * Remove all elements in the sorted set stored at key between the lexicographical range specified by min and max, * when all the elements in a sorted set are inserted with the same score, in order to force lexicographical ordering. *

* Time complexity O(log(N)+M) with N being the number of elements in the sorted set and M the number of elements * removed by the operation. * @param key * @param min minimum value to remove * @param max maximum value to remove * @return The number of elements removed */ long zremrangeByLex(String key, String min, String max); default ScanResult zscan(String key, String cursor) { return zscan(key, cursor, new ScanParams()); } ScanResult zscan(String key, String cursor, ScanParams params); /** * The blocking version of {@link SortedSetCommands#zpopmax(String) ZPOPMAX} * @param timeout specifying the maximum number of seconds to block. A timeout of zero can * be used to block indefinitely. * @param keys */ KeyValue bzpopmax(double timeout, String... keys); /** * The blocking version of {@link SortedSetCommands#zpopmin(String) ZPOPMIN} * @param timeout specifying the maximum number of seconds to block. A timeout of zero can * be used to block indefinitely. * @param keys */ KeyValue bzpopmin(double timeout, String... keys); /** * Compute the difference between all the sets in the given keys. *

* Time complexity O(L + (N-K)log(N)) worst case where L is the total number of elements in * all the sets, N is the size of the first set, and K is the size of the result set. * @param keys group of sets * @return The result of the difference */ List zdiff(String... keys); /** * Compute the difference between all the sets in the given keys. Return the result with scores. * @param keys group of sets * @return The result of the difference with their scores */ List zdiffWithScores(String... keys); /** * Compute the difference between all the sets in the given keys. Store the result in dstkey. * @param dstkey * @param keys group of sets * @return The number of elements in the resulting sorted set at dstkey. * @deprecated Use {@link #zdiffstore(java.lang.String, java.lang.String...)}. */ @Deprecated long zdiffStore(String dstkey, String... keys); /** * Compute the difference between all the sets in the given keys. Store the result in dstkey. * @param dstkey * @param keys group of sets * @return The number of elements in the resulting sorted set at dstkey. */ long zdiffstore(String dstkey, String... keys); /** * Compute the intersection between all the sets in the given keys. *

* Time complexity O(N*K)+O(M*log(M)) worst case with N being the smallest input sorted set, K being * the number of input sorted sets and M being the number of elements in the resulting sorted set. * @param params {@link ZParams} * @param keys group of sets * @return The result of the intersection */ List zinter(ZParams params, String... keys); /** * Compute the intersection between all the sets in the given keys. Return the result with scores. * @param params {@link ZParams} * @param keys group of sets * @return The result of the intersection with their scores */ List zinterWithScores(ZParams params, String... keys); /** * Compute the intersection between all the sets in the given keys. Store the result in dstkey. * @param dstkey * @param sets group of sets * @return The number of elements in the resulting sorted set at dstkey */ long zinterstore(String dstkey, String... sets); /** * Compute the intersection between all the sets in the given keys. Store the result in dstkey. * @param dstkey * @param params {@link ZParams} * @param sets group of sets * @return The number of elements in the resulting sorted set at dstkey */ long zinterstore(String dstkey, ZParams params, String... sets); /** * Similar to {@link SortedSetCommands#zinter(ZParams, String...) ZINTER}, but * instead of returning the result set, it returns just the cardinality of the result. *

* Time complexity O(N*K) worst case with N being the smallest input sorted set, K * being the number of input sorted sets * @see SortedSetCommands#zinter(ZParams, String...) * @param keys group of sets * @return The number of elements in the resulting intersection */ long zintercard(String... keys); /** * Similar to {@link SortedSetCommands#zinter(ZParams, String...) ZINTER}, but * instead of returning the result set, it returns just the cardinality of the result. *

* Time complexity O(N*K) worst case with N being the smallest input sorted set, K * being the number of input sorted sets * @see SortedSetCommands#zinter(ZParams, String...) * @param limit If the intersection cardinality reaches limit partway through the computation, * the algorithm will exit and yield limit as the cardinality * @param keys group of sets * @return The number of elements in the resulting intersection */ long zintercard(long limit, String... keys); /** * Compute the union between all the sets in the given keys. *

* Time complexity O(N)+O(M log(M)) with N being the sum of the sizes of the input sorted sets, * and M being the number of elements in the resulting sorted set. * @param params {@link ZParams} * @param keys group of sets * @return The result of the union */ List zunion(ZParams params, String... keys); /** * Compute the union between all the sets in the given keys. Return the result with scores. * @param params {@link ZParams} * @param keys group of sets * @return The result of the union with their scores */ List zunionWithScores(ZParams params, String... keys); /** * Compute the union between all the sets in the given keys. Store the result in dstkey. * @param dstkey * @param sets group of sets * @return The number of elements in the resulting sorted set at dstkey */ long zunionstore(String dstkey, String... sets); /** * Compute the union between all the sets in the given keys. Store the result in dstkey. * @param dstkey * @param params {@link ZParams} * @param sets group of sets * @return The number of elements in the resulting sorted set at dstkey */ long zunionstore(String dstkey, ZParams params, String... sets); KeyValue> zmpop(SortedSetOption option, String... keys); KeyValue> zmpop(SortedSetOption option, int count, String... keys); KeyValue> bzmpop(double timeout, SortedSetOption option, String... keys); KeyValue> bzmpop(double timeout, SortedSetOption option, int count, String... keys); } ================================================ FILE: src/main/java/redis/clients/jedis/commands/SortedSetPipelineBinaryCommands.java ================================================ package redis.clients.jedis.commands; import java.util.List; import java.util.Map; import redis.clients.jedis.Response; import redis.clients.jedis.args.SortedSetOption; import redis.clients.jedis.params.*; import redis.clients.jedis.resps.ScanResult; import redis.clients.jedis.resps.Tuple; import redis.clients.jedis.util.KeyValue; public interface SortedSetPipelineBinaryCommands { Response zadd(byte[] key, double score, byte[] member); Response zadd(byte[] key, double score, byte[] member, ZAddParams params); Response zadd(byte[] key, Map scoreMembers); Response zadd(byte[] key, Map scoreMembers, ZAddParams params); Response zaddIncr(byte[] key, double score, byte[] member, ZAddParams params); Response zrem(byte[] key, byte[]... members); Response zincrby(byte[] key, double increment, byte[] member); Response zincrby(byte[] key, double increment, byte[] member, ZIncrByParams params); Response zrank(byte[] key, byte[] member); Response zrevrank(byte[] key, byte[] member); Response> zrankWithScore(byte[] key, byte[] member); Response> zrevrankWithScore(byte[] key, byte[] member); Response> zrange(byte[] key, long start, long stop); Response> zrevrange(byte[] key, long start, long stop); Response> zrangeWithScores(byte[] key, long start, long stop); Response> zrevrangeWithScores(byte[] key, long start, long stop); Response zrandmember(byte[] key); Response> zrandmember(byte[] key, long count); Response> zrandmemberWithScores(byte[] key, long count); Response zcard(byte[] key); Response zscore(byte[] key, byte[] member); Response> zmscore(byte[] key, byte[]... members); Response zpopmax(byte[] key); Response> zpopmax(byte[] key, int count); Response zpopmin(byte[] key); Response> zpopmin(byte[] key, int count); Response zcount(byte[] key, double min, double max); Response zcount(byte[] key, byte[] min, byte[] max); Response> zrangeByScore(byte[] key, double min, double max); Response> zrangeByScore(byte[] key, byte[] min, byte[] max); Response> zrevrangeByScore(byte[] key, double max, double min); Response> zrangeByScore(byte[] key, double min, double max, int offset, int count); Response> zrevrangeByScore(byte[] key, byte[] max, byte[] min); Response> zrangeByScore(byte[] key, byte[] min, byte[] max, int offset, int count); Response> zrevrangeByScore(byte[] key, double max, double min, int offset, int count); Response> zrangeByScoreWithScores(byte[] key, double min, double max); Response> zrevrangeByScoreWithScores(byte[] key, double max, double min); Response> zrangeByScoreWithScores(byte[] key, double min, double max, int offset, int count); Response> zrevrangeByScore(byte[] key, byte[] max, byte[] min, int offset, int count); Response> zrangeByScoreWithScores(byte[] key, byte[] min, byte[] max); Response> zrevrangeByScoreWithScores(byte[] key, byte[] max, byte[] min); Response> zrangeByScoreWithScores(byte[] key, byte[] min, byte[] max, int offset, int count); Response> zrevrangeByScoreWithScores(byte[] key, double max, double min, int offset, int count); Response> zrevrangeByScoreWithScores(byte[] key, byte[] max, byte[] min, int offset, int count); Response zremrangeByRank(byte[] key, long start, long stop); Response zremrangeByScore(byte[] key, double min, double max); Response zremrangeByScore(byte[] key, byte[] min, byte[] max); Response zlexcount(byte[] key, byte[] min, byte[] max); Response> zrangeByLex(byte[] key, byte[] min, byte[] max); Response> zrangeByLex(byte[] key, byte[] min, byte[] max, int offset, int count); Response> zrevrangeByLex(byte[] key, byte[] max, byte[] min); Response> zrevrangeByLex(byte[] key, byte[] max, byte[] min, int offset, int count); Response> zrange(byte[] key, ZRangeParams zRangeParams); Response> zrangeWithScores(byte[] key, ZRangeParams zRangeParams); Response zrangestore(byte[] dest, byte[] src, ZRangeParams zRangeParams); Response zremrangeByLex(byte[] key, byte[] min, byte[] max); default Response> zscan(byte[] key, byte[] cursor) { return zscan(key, cursor, new ScanParams()); } Response> zscan(byte[] key, byte[] cursor, ScanParams params); Response> bzpopmax(double timeout, byte[]... keys); Response> bzpopmin(double timeout, byte[]... keys); Response> zdiff(byte[]... keys); Response> zdiffWithScores(byte[]... keys); /** * @deprecated Use {@link #zdiffstore(byte[], byte[][])}. */ @Deprecated Response zdiffStore(byte[] dstkey, byte[]... keys); Response zdiffstore(byte[] dstkey, byte[]... keys); Response> zinter(ZParams params, byte[]... keys); Response> zinterWithScores(ZParams params, byte[]... keys); Response zinterstore(byte[] dstkey, byte[]... sets); Response zinterstore(byte[] dstkey, ZParams params, byte[]... sets); Response zintercard(byte[]... keys); Response zintercard(long limit, byte[]... keys); Response> zunion(ZParams params, byte[]... keys); Response> zunionWithScores(ZParams params, byte[]... keys); Response zunionstore(byte[] dstkey, byte[]... sets); Response zunionstore(byte[] dstkey, ZParams params, byte[]... sets); Response>> zmpop(SortedSetOption option, byte[]... keys); Response>> zmpop(SortedSetOption option, int count, byte[]... keys); Response>> bzmpop(double timeout, SortedSetOption option, byte[]... keys); Response>> bzmpop(double timeout, SortedSetOption option, int count, byte[]... keys); } ================================================ FILE: src/main/java/redis/clients/jedis/commands/SortedSetPipelineCommands.java ================================================ package redis.clients.jedis.commands; import java.util.List; import java.util.Map; import java.util.Set; import redis.clients.jedis.Response; import redis.clients.jedis.args.SortedSetOption; import redis.clients.jedis.params.*; import redis.clients.jedis.resps.ScanResult; import redis.clients.jedis.resps.Tuple; import redis.clients.jedis.util.KeyValue; public interface SortedSetPipelineCommands { Response zadd(String key, double score, String member); Response zadd(String key, double score, String member, ZAddParams params); Response zadd(String key, Map scoreMembers); Response zadd(String key, Map scoreMembers, ZAddParams params); Response zaddIncr(String key, double score, String member, ZAddParams params); Response zrem(String key, String... members); Response zincrby(String key, double increment, String member); Response zincrby(String key, double increment, String member, ZIncrByParams params); Response zrank(String key, String member); Response zrevrank(String key, String member); Response> zrankWithScore(String key, String member); Response> zrevrankWithScore(String key, String member); Response> zrange(String key, long start, long stop); Response> zrevrange(String key, long start, long stop); Response> zrangeWithScores(String key, long start, long stop); Response> zrevrangeWithScores(String key, long start, long stop); Response zrandmember(String key); Response> zrandmember(String key, long count); Response> zrandmemberWithScores(String key, long count); Response zcard(String key); Response zscore(String key, String member); Response> zmscore(String key, String... members); Response zpopmax(String key); Response> zpopmax(String key, int count); Response zpopmin(String key); Response> zpopmin(String key, int count); Response zcount(String key, double min, double max); Response zcount(String key, String min, String max); Response> zrangeByScore(String key, double min, double max); Response> zrangeByScore(String key, String min, String max); Response> zrevrangeByScore(String key, double max, double min); Response> zrangeByScore(String key, double min, double max, int offset, int count); Response> zrevrangeByScore(String key, String max, String min); Response> zrangeByScore(String key, String min, String max, int offset, int count); Response> zrevrangeByScore(String key, double max, double min, int offset, int count); Response> zrangeByScoreWithScores(String key, double min, double max); Response> zrevrangeByScoreWithScores(String key, double max, double min); Response> zrangeByScoreWithScores(String key, double min, double max, int offset, int count); Response> zrevrangeByScore(String key, String max, String min, int offset, int count); Response> zrangeByScoreWithScores(String key, String min, String max); Response> zrevrangeByScoreWithScores(String key, String max, String min); Response> zrangeByScoreWithScores(String key, String min, String max, int offset, int count); Response> zrevrangeByScoreWithScores(String key, double max, double min, int offset, int count); Response> zrevrangeByScoreWithScores(String key, String max, String min, int offset, int count); Response> zrange(String key, ZRangeParams zRangeParams); Response> zrangeWithScores(String key, ZRangeParams zRangeParams); Response zrangestore(String dest, String src, ZRangeParams zRangeParams); Response zremrangeByRank(String key, long start, long stop); Response zremrangeByScore(String key, double min, double max); Response zremrangeByScore(String key, String min, String max); Response zlexcount(String key, String min, String max); Response> zrangeByLex(String key, String min, String max); Response> zrangeByLex(String key, String min, String max, int offset, int count); Response> zrevrangeByLex(String key, String max, String min); Response> zrevrangeByLex(String key, String max, String min, int offset, int count); Response zremrangeByLex(String key, String min, String max); default Response> zscan(String key, String cursor) { return zscan(key, cursor, new ScanParams()); } Response> zscan(String key, String cursor, ScanParams params); Response> bzpopmax(double timeout, String... keys); Response> bzpopmin(double timeout, String... keys); Response> zdiff(String... keys); Response> zdiffWithScores(String... keys); /** * @deprecated Use {@link #zdiffstore(java.lang.String, java.lang.String...)}. */ @Deprecated Response zdiffStore(String dstKey, String... keys); Response zdiffstore(String dstKey, String... keys); Response zinterstore(String dstKey, String... sets); Response zinterstore(String dstKey, ZParams params, String... sets); Response> zinter(ZParams params, String... keys); Response> zinterWithScores(ZParams params, String... keys); Response zintercard(String... keys); Response zintercard(long limit, String... keys); Response> zunion(ZParams params, String... keys); Response> zunionWithScores(ZParams params, String... keys); Response zunionstore(String dstKey, String... sets); Response zunionstore(String dstKey, ZParams params, String... sets); Response>> zmpop(SortedSetOption option, String... keys); Response>> zmpop(SortedSetOption option, int count, String... keys); Response>> bzmpop(double timeout, SortedSetOption option, String... keys); Response>> bzmpop(double timeout, SortedSetOption option, int count, String... keys); } ================================================ FILE: src/main/java/redis/clients/jedis/commands/StreamBinaryCommands.java ================================================ package redis.clients.jedis.commands; import java.util.List; import java.util.Map; import redis.clients.jedis.StreamEntryID; import redis.clients.jedis.args.StreamDeletionPolicy; import redis.clients.jedis.params.*; import redis.clients.jedis.resps.StreamEntryBinary; import redis.clients.jedis.resps.StreamEntryDeletionResult; public interface StreamBinaryCommands { default byte[] xadd(byte[] key, Map hash, XAddParams params) { return xadd(key, params, hash); } byte[] xadd(byte[] key, XAddParams params, Map hash); long xlen(byte[] key); List xrange(byte[] key, byte[] start, byte[] end); List xrange(byte[] key, byte[] start, byte[] end, int count); List xrevrange(byte[] key, byte[] end, byte[] start); List xrevrange(byte[] key, byte[] end, byte[] start, int count); long xack(byte[] key, byte[] group, byte[]... ids); /** * XACKDEL key group [KEEPREF | DELREF | ACKED] IDS numids id [id ...] */ List xackdel(byte[] key, byte[] group, byte[]... ids); /** * XACKDEL key group [KEEPREF | DELREF | ACKED] IDS numids id [id ...] */ List xackdel(byte[] key, byte[] group, StreamDeletionPolicy trimMode, byte[]... ids); String xgroupCreate(byte[] key, byte[] groupName, byte[] id, boolean makeStream); String xgroupSetID(byte[] key, byte[] groupName, byte[] id); long xgroupDestroy(byte[] key, byte[] groupName); boolean xgroupCreateConsumer(byte[] key, byte[] groupName, byte[] consumerName); long xgroupDelConsumer(byte[] key, byte[] groupName, byte[] consumerName); long xdel(byte[] key, byte[]... ids); /** * XDELEX key [KEEPREF | DELREF | ACKED] IDS numids id [id ...] */ List xdelex(byte[] key, byte[]... ids); /** * XDELEX key [KEEPREF | DELREF | ACKED] IDS numids id [id ...] */ List xdelex(byte[] key, StreamDeletionPolicy trimMode, byte[]... ids); long xtrim(byte[] key, long maxLen, boolean approximateLength); long xtrim(byte[] key, XTrimParams params); Object xpending(byte[] key, byte[] groupName); List xpending(byte[] key, byte[] groupName, XPendingParams params); List xclaim(byte[] key, byte[] group, byte[] consumerName, long minIdleTime, XClaimParams params, byte[]... ids); List xclaimJustId(byte[] key, byte[] group, byte[] consumerName, long minIdleTime, XClaimParams params, byte[]... ids); List xautoclaim(byte[] key, byte[] groupName, byte[] consumerName, long minIdleTime, byte[] start, XAutoClaimParams params); List xautoclaimJustId(byte[] key, byte[] groupName, byte[] consumerName, long minIdleTime, byte[] start, XAutoClaimParams params); Object xinfoStream(byte[] key); /** * Introspection command used in order to retrieve all information about the stream * @param key Stream name */ Object xinfoStreamFull(byte[] key); /** * Introspection command used in order to retrieve all information about the stream * @param key Stream name * @param count stream info count */ Object xinfoStreamFull(byte[] key, int count); List xinfoGroups(byte[] key); List xinfoConsumers(byte[] key, byte[] group); /** * @deprecated As of Jedis 6.1.0, replaced by {@link #xreadBinary(XReadParams, Map)} or * {@link #xreadBinaryAsMap(XReadParams, Map)} for type safety and better stream entry parsing. */ @Deprecated List xread(XReadParams xReadParams, Map.Entry... streams); /** * @deprecated As of Jedis 6.1.0, use {@link #xreadGroupBinary(byte[], byte[], XReadGroupParams, Map)} or * {@link #xreadGroupBinaryAsMap(byte[], byte[], XReadGroupParams, Map)} instead. */ @Deprecated List xreadGroup(byte[] groupName, byte[] consumer, XReadGroupParams xReadGroupParams, Map.Entry... streams); /** * Read from one or more streams. * @param xReadParams {@link XReadParams} * @param streams Map of stream name and ID to read from. * @return List of entries. Each entry in the list is a pair of stream name and the entries * reported for that key. */ List>> xreadBinary(XReadParams xReadParams, Map streams); /** * Read from one or more streams and return a map of stream name to list of entries. * @param xReadParams {@link XReadParams} * @param streams Map of stream name and ID to read from. * @return Map of stream name to list of entries. key is the stream name and value is the list of * entries reported for that key. */ Map> xreadBinaryAsMap(XReadParams xReadParams, Map streams); /** * Read from one or more streams as a consumer group. * @param groupName Consumer group name. * @param consumer Consumer name. * @param xReadGroupParams {@link XReadGroupParams} * @param streams Map of stream name and ID to read from. * @return List of entries. Each entry in the list is a pair of stream name and the entries * reported for that key. */ List>> xreadGroupBinary(byte[] groupName, byte[] consumer, XReadGroupParams xReadGroupParams, Map streams); /** * Read from one or more streams as a consumer group and return a map of stream name to list of * entries. * @param groupName Consumer group name. * @param consumer Consumer name. * @param xReadGroupParams {@link XReadGroupParams} * @param streams Map of stream name and ID to read from. * @return Map of stream name to list of entries. key is the stream name and value is the list of * entries reported for that key. */ Map> xreadGroupBinaryAsMap(byte[] groupName, byte[] consumer, XReadGroupParams xReadGroupParams, Map streams); /** * XCFGSET key [IDMP-DURATION duration] [IDMP-MAXSIZE maxsize] * Configure idempotent producer settings for a stream. * * @param key Stream name * @param params Configuration parameters * @return OK if successful */ byte[] xcfgset(byte[] key, XCfgSetParams params); } ================================================ FILE: src/main/java/redis/clients/jedis/commands/StreamCommands.java ================================================ package redis.clients.jedis.commands; import java.util.List; import java.util.Map; import redis.clients.jedis.StreamEntryID; import redis.clients.jedis.args.StreamDeletionPolicy; import redis.clients.jedis.params.*; import redis.clients.jedis.resps.*; public interface StreamCommands { /** * XADD key ID field string [field string ...] * * @return the ID of the added entry */ StreamEntryID xadd(String key, StreamEntryID id, Map hash); /** * XADD key [NOMKSTREAM] [MAXLEN|MINID [=|~] threshold [LIMIT count]] *|ID field value [field value ...] * * @return the ID of the added entry */ // Legacy default StreamEntryID xadd(String key, Map hash, XAddParams params) { return xadd(key, params, hash); } StreamEntryID xadd(String key, XAddParams params, Map hash); /** * XLEN key * * @return length of stream */ long xlen(String key); /** * XRANGE key start end * * @param key * @param start minimum {@link StreamEntryID} for the retrieved range, passing {@code null} will * indicate minimum ID possible in the stream * @param end maximum {@link StreamEntryID} for the retrieved range, passing {@code null} will * indicate maximum ID possible in the stream * @return The entries with IDs matching the specified range. */ List xrange(String key, StreamEntryID start, StreamEntryID end); /** * XRANGE key start end COUNT count * * @param key * @param start minimum {@link StreamEntryID} for the retrieved range, passing {@code null} will * indicate minimum ID possible in the stream * @param end maximum {@link StreamEntryID} for the retrieved range, passing {@code null} will * indicate maximum ID possible in the stream * @param count maximum number of entries returned * @return The entries with IDs matching the specified range. */ List xrange(String key, StreamEntryID start, StreamEntryID end, int count); /** * XREVRANGE key end start * * @param key * @param start minimum {@link StreamEntryID} for the retrieved range, passing {@code null} will * indicate minimum ID possible in the stream * @param end maximum {@link StreamEntryID} for the retrieved range, passing {@code null} will * indicate maximum ID possible in the stream * @return the entries with IDs matching the specified range, from the higher ID to the lower ID matching. */ List xrevrange(String key, StreamEntryID end, StreamEntryID start); /** * XREVRANGE key end start COUNT count * * @param key * @param start minimum {@link StreamEntryID} for the retrieved range, passing {@code null} will * indicate minimum ID possible in the stream * @param end maximum {@link StreamEntryID} for the retrieved range, passing {@code null} will * indicate maximum ID possible in the stream * @param count The entries with IDs matching the specified range. * @return the entries with IDs matching the specified range, from the higher ID to the lower ID matching. */ List xrevrange(String key, StreamEntryID end, StreamEntryID start, int count); List xrange(String key, String start, String end); List xrange(String key, String start, String end, int count); List xrevrange(String key, String end, String start); List xrevrange(String key, String end, String start, int count); /** * XACK key group ID [ID ...] */ long xack(String key, String group, StreamEntryID... ids); /** * XACKDEL key group [KEEPREF | DELREF | ACKED] IDS numids id [id ...] * Combines XACK and XDEL functionalities. Acknowledges specified message IDs * in the given consumer group and attempts to delete corresponding stream entries. */ List xackdel(String key, String group, StreamEntryID... ids); /** * XACKDEL key group [KEEPREF | DELREF | ACKED] IDS numids id [id ...] * Combines XACK and XDEL functionalities. Acknowledges specified message IDs * in the given consumer group and attempts to delete corresponding stream entries. */ List xackdel(String key, String group, StreamDeletionPolicy trimMode, StreamEntryID... ids); /** * {@code XGROUP CREATE key groupName } */ String xgroupCreate(String key, String groupName, StreamEntryID id, boolean makeStream); /** * {@code XGROUP SETID key groupName } */ String xgroupSetID(String key, String groupName, StreamEntryID id); /** * XGROUP DESTROY key groupName */ long xgroupDestroy(String key, String groupName); /** * XGROUP CREATECONSUMER key groupName consumerName */ boolean xgroupCreateConsumer(String key, String groupName, String consumerName); /** * XGROUP DELCONSUMER key groupName consumerName */ long xgroupDelConsumer(String key, String groupName, String consumerName); /** * XDEL key ID [ID ...] */ long xdel(String key, StreamEntryID... ids); /** * XDELEX key [KEEPREF | DELREF | ACKED] IDS numids id [id ...] * Extended XDEL command with enhanced control over message entry deletion * with respect to consumer groups. */ List xdelex(String key, StreamEntryID... ids); /** * XDELEX key [KEEPREF | DELREF | ACKED] IDS numids id [id ...] * Extended XDEL command with enhanced control over message entry deletion * with respect to consumer groups. */ List xdelex(String key, StreamDeletionPolicy trimMode, StreamEntryID... ids); /** * XTRIM key MAXLEN [~] count */ long xtrim(String key, long maxLen, boolean approximate); /** * XTRIM key MAXLEN|MINID [=|~] threshold [LIMIT count] */ long xtrim(String key, XTrimParams params); /** * XPENDING key group */ StreamPendingSummary xpending(String key, String groupName); /** * XPENDING key group [[IDLE min-idle-time] start end count [consumer]] */ List xpending(String key, String groupName, XPendingParams params); /** * {@code XCLAIM key group consumer min-idle-time ... * [IDLE ] [TIME ] [RETRYCOUNT ] * [FORCE]} */ List xclaim(String key, String group, String consumerName, long minIdleTime, XClaimParams params, StreamEntryID... ids); /** * {@code XCLAIM key group consumer min-idle-time ... * [IDLE ] [TIME ] [RETRYCOUNT ] * [FORCE] JUSTID} */ List xclaimJustId(String key, String group, String consumerName, long minIdleTime, XClaimParams params, StreamEntryID... ids); /** * XAUTOCLAIM key group consumer min-idle-time start [COUNT count] * * @param key Stream Key * @param group Consumer Group * @param consumerName Consumer name to transfer the auto claimed entries * @param minIdleTime Entries pending more than minIdleTime will be transferred ownership * @param start {@link StreamEntryID} - Entries ≥ start will be transferred ownership, passing * {@code null} will indicate '-' * @param params {@link XAutoClaimParams} */ Map.Entry> xautoclaim(String key, String group, String consumerName, long minIdleTime, StreamEntryID start, XAutoClaimParams params); /** * XAUTOCLAIM key group consumer min-idle-time start [COUNT count] JUSTID * * @param key Stream Key * @param group Consumer Group * @param consumerName Consumer name to transfer the auto claimed entries * @param minIdleTime Entries pending more than minIdleTime will be transferred ownership * @param start {@link StreamEntryID} - Entries ≥ start will be transferred ownership, passing * {@code null} will indicate '-' * @param params {@link XAutoClaimParams} */ Map.Entry> xautoclaimJustId(String key, String group, String consumerName, long minIdleTime, StreamEntryID start, XAutoClaimParams params); /** * Introspection command used in order to retrieve different information about the stream * @param key Stream name * @return {@link StreamInfo} that contains information about the stream */ StreamInfo xinfoStream(String key); /** * Introspection command used in order to retrieve all information about the stream * @param key Stream name * @return {@link StreamFullInfo} that contains information about the stream */ StreamFullInfo xinfoStreamFull(String key); /** * Introspection command used in order to retrieve all information about the stream * @param key Stream name * @param count stream info count * @return {@link StreamFullInfo} that contains information about the stream */ StreamFullInfo xinfoStreamFull(String key, int count); /** * Introspection command used in order to retrieve different information about groups in the stream * @param key Stream name * @return List of {@link StreamGroupInfo} containing information about groups */ List xinfoGroups(String key); /** * Introspection command used in order to retrieve different information about consumers in the group * @param key Stream name * @param group Group name * @return List of {@link StreamConsumersInfo} containing information about consumers that belong * to the group * @deprecated Use {@link #xinfoConsumers2(java.lang.String, java.lang.String)}. */ @Deprecated // keep it till at least Jedis 6/7 List xinfoConsumers(String key, String group); /** * Introspection command used in order to retrieve different information about consumers in the group * @param key Stream name * @param group Group name * @return List of {@link StreamConsumerInfo} containing information about consumers that belong * to the group */ List xinfoConsumers2(String key, String group); /** * XREAD [COUNT count] [BLOCK milliseconds] STREAMS key [key ...] ID [ID ...] */ List>> xread(XReadParams xReadParams, Map streams); /** * XREAD [COUNT count] [BLOCK milliseconds] STREAMS key [key ...] ID [ID ...] */ Map> xreadAsMap(XReadParams xReadParams, Map streams); /** * XREADGROUP GROUP group consumer [COUNT count] [BLOCK milliseconds] [NOACK] STREAMS key [key ...] id [id ...] */ List>> xreadGroup(String groupName, String consumer, XReadGroupParams xReadGroupParams, Map streams); /** * XREADGROUP GROUP group consumer [COUNT count] [BLOCK milliseconds] [NOACK] STREAMS key [key ...] id [id ...] */ Map> xreadGroupAsMap(String groupName, String consumer, XReadGroupParams xReadGroupParams, Map streams); /** * XCFGSET key [IDMP-DURATION duration] [IDMP-MAXSIZE maxsize] * Configure idempotent producer settings for a stream. * * @param key Stream name * @param params Configuration parameters * @return OK if successful */ String xcfgset(String key, XCfgSetParams params); } ================================================ FILE: src/main/java/redis/clients/jedis/commands/StreamPipelineBinaryCommands.java ================================================ package redis.clients.jedis.commands; import java.util.List; import java.util.Map; import redis.clients.jedis.Response; import redis.clients.jedis.StreamEntryID; import redis.clients.jedis.args.StreamDeletionPolicy; import redis.clients.jedis.params.*; import redis.clients.jedis.resps.StreamEntryBinary; import redis.clients.jedis.resps.StreamEntryDeletionResult; public interface StreamPipelineBinaryCommands { default Response xadd(byte[] key, Map hash, XAddParams params) { return xadd(key, params, hash); } Response xadd(byte[] key, XAddParams params, Map hash); Response xlen(byte[] key); Response> xrange(byte[] key, byte[] start, byte[] end); Response> xrange(byte[] key, byte[] start, byte[] end, int count); Response> xrevrange(byte[] key, byte[] end, byte[] start); Response> xrevrange(byte[] key, byte[] end, byte[] start, int count); Response xack(byte[] key, byte[] group, byte[]... ids); Response> xackdel(byte[] key, byte[] group, byte[]... ids); Response> xackdel(byte[] key, byte[] group, StreamDeletionPolicy trimMode, byte[]... ids); Response xgroupCreate(byte[] key, byte[] groupName, byte[] id, boolean makeStream); Response xgroupSetID(byte[] key, byte[] groupName, byte[] id); Response xgroupDestroy(byte[] key, byte[] groupName); Response xgroupCreateConsumer(byte[] key, byte[] groupName, byte[] consumerName); Response xgroupDelConsumer(byte[] key, byte[] groupName, byte[] consumerName); Response xdel(byte[] key, byte[]... ids); Response> xdelex(byte[] key, byte[]... ids); Response> xdelex(byte[] key, StreamDeletionPolicy trimMode, byte[]... ids); Response xtrim(byte[] key, long maxLen, boolean approximateLength); Response xtrim(byte[] key, XTrimParams params); Response xpending(byte[] key, byte[] groupName); Response> xpending(byte[] key, byte[] groupName, XPendingParams params); Response> xclaim(byte[] key, byte[] group, byte[] consumerName, long minIdleTime, XClaimParams params, byte[]... ids); Response> xclaimJustId(byte[] key, byte[] group, byte[] consumerName, long minIdleTime, XClaimParams params, byte[]... ids); Response> xautoclaim(byte[] key, byte[] groupName, byte[] consumerName, long minIdleTime, byte[] start, XAutoClaimParams params); Response> xautoclaimJustId(byte[] key, byte[] groupName, byte[] consumerName, long minIdleTime, byte[] start, XAutoClaimParams params); Response xinfoStream(byte[] key); /** * Introspection command used in order to retrieve all information about the stream * @param key Stream name */ Response xinfoStreamFull(byte[] key); /** * Introspection command used in order to retrieve all information about the stream * @param key Stream name * @param count stream info count */ Response xinfoStreamFull(byte[] key, int count); Response> xinfoGroups(byte[] key); Response> xinfoConsumers(byte[] key, byte[] group); /** * @deprecated As of Jedis 6.1.0, use {@link #xreadBinary(XReadParams, Map)} or * {@link #xreadBinaryAsMap(XReadParams, Map)} for type safety and better stream entry * parsing. */ @Deprecated Response> xread(XReadParams xReadParams, Map.Entry... streams); /** * @deprecated As of Jedis 6.1.0, use * {@link #xreadGroupBinary(byte[], byte[], XReadGroupParams, Map)} or * {@link #xreadGroupBinaryAsMap(byte[], byte[], XReadGroupParams, Map)} instead. */ @Deprecated Response> xreadGroup(byte[] groupName, byte[] consumer, XReadGroupParams xReadGroupParams, Map.Entry... streams); /** * Read from one or more streams. * * @param xReadParams {@link XReadParams} * @param streams Map of stream name and ID to read from. * @return List of entries. Each entry in the list is a pair of stream name and the entries reported for that key. */ Response>>> xreadBinary(XReadParams xReadParams, Map streams); /** * Read from one or more streams and return a map of stream name to list of entries. * * @param xReadParams {@link XReadParams} * @param streams Map of stream name and ID to read from. * @return Map of stream name to list of entries. */ Response>> xreadBinaryAsMap(XReadParams xReadParams, Map streams); /** * Read from one or more streams using a consumer group. * * @param groupName Consumer group name * @param consumer Consumer name * @param xReadGroupParams {@link XReadGroupParams} * @param streams Map of stream name and ID to read from. * @return List of entries. Each entry in the list is a pair of stream name and the entries reported for that key. */ Response>>> xreadGroupBinary(byte[] groupName, byte[] consumer, XReadGroupParams xReadGroupParams, Map streams); /** * Read from one or more streams using a consumer group and return a map of stream name to list of entries. * * @param groupName Consumer group name * @param consumer Consumer name * @param xReadGroupParams {@link XReadGroupParams} * @param streams Map of stream name and ID to read from. * @return Map of stream name to list of entries. */ Response>> xreadGroupBinaryAsMap(byte[] groupName, byte[] consumer, XReadGroupParams xReadGroupParams, Map streams); /** * XCFGSET key [IDMP-DURATION duration] [IDMP-MAXSIZE maxsize] * Configure idempotent producer settings for a stream. * * @param key Stream name * @param params Configuration parameters * @return OK if successful */ Response xcfgset(byte[] key, XCfgSetParams params); } ================================================ FILE: src/main/java/redis/clients/jedis/commands/StreamPipelineCommands.java ================================================ package redis.clients.jedis.commands; import java.util.List; import java.util.Map; import redis.clients.jedis.Response; import redis.clients.jedis.StreamEntryID; import redis.clients.jedis.args.StreamDeletionPolicy; import redis.clients.jedis.params.*; import redis.clients.jedis.resps.*; public interface StreamPipelineCommands { /** * XADD key ID field string [field string ...] * * @return the ID of the added entry */ Response xadd(String key, StreamEntryID id, Map hash); /** * XADD key [NOMKSTREAM] [MAXLEN|MINID [=|~] threshold [LIMIT count]] *|ID field value [field value ...] * * @return the ID of the added entry */ // Legacy default Response xadd(String key, Map hash, XAddParams params) { return xadd(key, params, hash); } Response xadd(String key, XAddParams params, Map hash); /** * XLEN key * * @return length of stream */ Response xlen(String key); /** * XRANGE key start end * * @param key key * @param start minimum {@link StreamEntryID} for the retrieved range, passing null will indicate minimum ID possible in the stream * @param end maximum {@link StreamEntryID} for the retrieved range, passing null will indicate maximum ID possible in the stream * @return The entries with IDs matching the specified range. */ Response> xrange(String key, StreamEntryID start, StreamEntryID end); /** * XRANGE key start end COUNT count * * @param key key * @param start minimum {@link StreamEntryID} for the retrieved range, passing null will indicate minimum ID possible in the stream * @param end maximum {@link StreamEntryID} for the retrieved range, passing null will indicate maximum ID possible in the stream * @param count maximum number of entries returned * @return The entries with IDs matching the specified range. */ Response> xrange(String key, StreamEntryID start, StreamEntryID end, int count); /** * XREVRANGE key end start * * @param key key * @param start minimum {@link StreamEntryID} for the retrieved range, passing null will indicate minimum ID possible in the stream * @param end maximum {@link StreamEntryID} for the retrieved range, passing null will indicate maximum ID possible in the stream * @return the entries with IDs matching the specified range, from the higher ID to the lower ID matching. */ Response> xrevrange(String key, StreamEntryID end, StreamEntryID start); /** * XREVRANGE key end start COUNT count * * @param key key * @param start minimum {@link StreamEntryID} for the retrieved range, passing null will indicate minimum ID possible in the stream * @param end maximum {@link StreamEntryID} for the retrieved range, passing null will indicate maximum ID possible in the stream * @param count The entries with IDs matching the specified range. * @return the entries with IDs matching the specified range, from the higher ID to the lower ID matching. */ Response> xrevrange(String key, StreamEntryID end, StreamEntryID start, int count); Response> xrange(String key, String start, String end); Response> xrange(String key, String start, String end, int count); Response> xrevrange(String key, String end, String start); Response> xrevrange(String key, String end, String start, int count); /** * XACK key group ID [ID ...] */ Response xack(String key, String group, StreamEntryID... ids); /** * XACKDEL key group [KEEPREF | DELREF | ACKED] IDS numids id [id ...] */ Response> xackdel(String key, String group, StreamEntryID... ids); /** * XACKDEL key group [KEEPREF | DELREF | ACKED] IDS numids id [id ...] */ Response> xackdel(String key, String group, StreamDeletionPolicy trimMode, StreamEntryID... ids); /** * {@code XGROUP CREATE key groupName } */ Response xgroupCreate( String key, String groupName, StreamEntryID id, boolean makeStream); /** * {@code XGROUP SETID key groupName } */ Response xgroupSetID( String key, String groupName, StreamEntryID id); /** * XGROUP DESTROY key groupName */ Response xgroupDestroy(String key, String groupName); /** * XGROUP CREATECONSUMER key groupName consumerName */ Response xgroupCreateConsumer( String key, String groupName, String consumerName); /** * XGROUP DELCONSUMER key groupName consumerName */ Response xgroupDelConsumer( String key, String groupName, String consumerName); /** * XPENDING key group */ Response xpending(String key, String groupName); /** * XPENDING key group [[IDLE min-idle-time] start end count [consumer]] */ Response> xpending(String key, String groupName, XPendingParams params); /** * XDEL key ID [ID ...] */ Response xdel(String key, StreamEntryID... ids); /** * XDELEX key [KEEPREF | DELREF | ACKED] IDS numids id [id ...] */ Response> xdelex(String key, StreamEntryID... ids); /** * XDELEX key [KEEPREF | DELREF | ACKED] IDS numids id [id ...] */ Response> xdelex(String key, StreamDeletionPolicy trimMode, StreamEntryID... ids); /** * XTRIM key MAXLEN [~] count */ Response xtrim(String key, long maxLen, boolean approximate); /** * XTRIM key MAXLEN|MINID [=|~] threshold [LIMIT count] */ Response xtrim(String key, XTrimParams params); /** * {@code XCLAIM key group consumer min-idle-time ... * [IDLE ] [TIME ] [RETRYCOUNT ] * [FORCE]} */ Response> xclaim(String key, String group, String consumerName, long minIdleTime, XClaimParams params, StreamEntryID... ids); /** * {@code XCLAIM key group consumer min-idle-time ... * [IDLE ] [TIME ] [RETRYCOUNT ] * [FORCE] JUSTID} */ Response> xclaimJustId(String key, String group, String consumerName, long minIdleTime, XClaimParams params, StreamEntryID... ids); /** * XAUTOCLAIM key group consumer min-idle-time start [COUNT count] * * @param key Stream Key * @param group Consumer Group * @param consumerName Consumer name to transfer the auto claimed entries * @param minIdleTime Entries pending more than minIdleTime will be transferred ownership * @param start {@link StreamEntryID} - Entries ≥ start will be transferred ownership, passing * {@code null} will indicate '-' * @param params {@link XAutoClaimParams} */ Response>> xautoclaim(String key, String group, String consumerName, long minIdleTime, StreamEntryID start, XAutoClaimParams params); /** * XAUTOCLAIM key group consumer min-idle-time start [COUNT count] JUSTID * * @param key Stream Key * @param group Consumer Group * @param consumerName Consumer name to transfer the auto claimed entries * @param minIdleTime Entries pending more than minIdleTime will be transferred ownership * @param start {@link StreamEntryID} - Entries ≥ start will be transferred ownership, passing * {@code null} will indicate '-' * @param params {@link XAutoClaimParams} */ Response>> xautoclaimJustId(String key, String group, String consumerName, long minIdleTime, StreamEntryID start, XAutoClaimParams params); /** * Introspection command used in order to retrieve different information about the stream * @param key Stream name * @return {@link StreamInfo} that contains information about the stream */ Response xinfoStream(String key); /** * Introspection command used in order to retrieve all information about the stream * @param key Stream name * @return {@link StreamFullInfo} that contains information about the stream */ Response xinfoStreamFull(String key); /** * Introspection command used in order to retrieve all information about the stream * @param key Stream name * @param count stream info count * @return {@link StreamFullInfo} that contains information about the stream */ Response xinfoStreamFull(String key, int count); /** * Introspection command used in order to retrieve different information about groups in the stream * @param key Stream name * @return List of {@link StreamGroupInfo} containing information about groups */ Response> xinfoGroups(String key); /** * Introspection command used in order to retrieve different information about consumers in the group * @param key Stream name * @param group Group name * @return List of {@link StreamConsumersInfo} containing information about consumers that belong * to the group * @deprecated Use {@link #xinfoConsumers2(java.lang.String, java.lang.String)}. */ @Deprecated // keep it till at least Jedis 6/7 Response> xinfoConsumers(String key, String group); /** * Introspection command used in order to retrieve different information about consumers in the group * @param key Stream name * @param group Group name * @return List of {@link StreamConsumerInfo} containing information about consumers that belong * to the group */ Response> xinfoConsumers2(String key, String group); /** * XREAD [COUNT count] [BLOCK milliseconds] STREAMS key [key ...] ID [ID ...] */ Response>>> xread(XReadParams xReadParams, Map streams); /** * XREAD [COUNT count] [BLOCK milliseconds] STREAMS key [key ...] ID [ID ...] */ Response>> xreadAsMap(XReadParams xReadParams, Map streams); /** * XREADGROUP GROUP group consumer [COUNT count] [BLOCK milliseconds] [NOACK] STREAMS key [key ...] id [id ...] */ Response>>> xreadGroup(String groupName, String consumer, XReadGroupParams xReadGroupParams, Map streams); /** * XREADGROUP GROUP group consumer [COUNT count] [BLOCK milliseconds] [NOACK] STREAMS key [key ...] id [id ...] */ Response>> xreadGroupAsMap(String groupName, String consumer, XReadGroupParams xReadGroupParams, Map streams); /** * XCFGSET key [IDMP-DURATION duration] [IDMP-MAXSIZE maxsize] * Configure idempotent producer settings for a stream. * * @param key Stream name * @param params Configuration parameters * @return OK if successful */ Response xcfgset(String key, XCfgSetParams params); } ================================================ FILE: src/main/java/redis/clients/jedis/commands/StringBinaryCommands.java ================================================ package redis.clients.jedis.commands; import java.util.List; import redis.clients.jedis.params.GetExParams; import redis.clients.jedis.params.SetParams; import redis.clients.jedis.params.MSetExParams; import redis.clients.jedis.params.LCSParams; import redis.clients.jedis.resps.LCSMatchResult; public interface StringBinaryCommands extends BitBinaryCommands { String set(byte[] key, byte[] value); String set(byte[] key, byte[] value, SetParams params); byte[] get(byte[] key); byte[] setGet(byte[] key, byte[] value); byte[] setGet(byte[] key, byte[] value, SetParams params); byte[] getDel(byte[] key); byte[] getEx(byte[] key, GetExParams params); long setrange(byte[] key, long offset, byte[] value); byte[] getrange(byte[] key, long startOffset, long endOffset); /** * @deprecated Use {@link StringBinaryCommands#setGet(byte[], byte[])}. */ @Deprecated byte[] getSet(byte[] key, byte[] value); /** * @deprecated Use {@link StringBinaryCommands#set(byte[], byte[], SetParams)} with {@link SetParams#nx()}. * Deprecated in Jedis 7.3.0. Mirrors Redis deprecation since 2.6.12. */ @Deprecated long setnx(byte[] key, byte[] value); /** * @deprecated Use {@link StringBinaryCommands#set(byte[], byte[], SetParams)} with {@link SetParams#ex(long)}. * Deprecated in Jedis 7.3.0. Mirrors Redis deprecation since 2.6.12. */ @Deprecated String setex(byte[] key, long seconds, byte[] value); /** * @deprecated Use {@link StringBinaryCommands#set(byte[], byte[], SetParams)} with {@link SetParams#px(long)}. * Deprecated in Jedis 7.3.0. Mirrors Redis deprecation since 2.6.12. */ @Deprecated String psetex(byte[] key, long milliseconds, byte[] value); List mget(byte[]... keys); String mset(byte[]... keysvalues); long msetnx(byte[]... keysvalues); /** * Multi-set with optional condition and expiration. *

* Sets the respective keys to the respective values, similar to {@link #mset(byte[]...) MSET}, * but allows conditional set (NX|XX) and expiration options via {@link MSetExParams}. * If the condition is not met for any key, no key is set. *

* Both MSET and MSETEX are atomic operations. This means that if multiple keys are provided, * another client will either see the changes for all keys at once, or no changes at all. *

* Options (in {@link MSetExParams}): NX or XX, and expiration: EX seconds | PX milliseconds | * EXAT unix-time-seconds | PXAT unix-time-milliseconds | KEEPTTL. *

* Time complexity: O(N) where N is the number of keys to set. * @param params condition and expiration parameters * @param keysvalues pairs of keys and their values, e.g. {@code msetex(params, "foo".getBytes(), "foovalue".getBytes(), "bar".getBytes(), "barvalue".getBytes())} * @return {@code true} if all the keys were set, {@code false} if none were set (condition not satisfied) * @see #mset(byte[]...) * @see #msetnx(byte[]...) */ boolean msetex(MSetExParams params, byte[]... keysvalues); long incr(byte[] key); long incrBy(byte[] key, long increment); double incrByFloat(byte[] key, double increment); long decr(byte[] key); long decrBy(byte[] key, long decrement); long append(byte[] key, byte[] value); /** * @deprecated Use {@link StringBinaryCommands#getrange(byte[], long, long)}. * Deprecated in Jedis 7.3.0. Mirrors Redis deprecation since 2.0.0. */ @Deprecated byte[] substr(byte[] key, int start, int end); long strlen(byte[] key); /** * Calculate the longest common subsequence of keyA and keyB. * @param keyA * @param keyB * @param params * @return According to LCSParams to decide to return content to fill LCSMatchResult. */ LCSMatchResult lcs(byte[] keyA, byte[] keyB, LCSParams params); } ================================================ FILE: src/main/java/redis/clients/jedis/commands/StringCommands.java ================================================ package redis.clients.jedis.commands; import java.util.List; import redis.clients.jedis.params.GetExParams; import redis.clients.jedis.params.SetParams; import redis.clients.jedis.params.MSetExParams; import redis.clients.jedis.params.LCSParams; import redis.clients.jedis.resps.LCSMatchResult; public interface StringCommands extends BitCommands { /** * Set Command * Set the string value as value of the key. The string can't be longer than 1073741824 bytes (1 GB). *

* Time complexity: O(1) * @param key * @param value * @return OK */ String set(String key, String value); /** * Set Command * Set the string value as value of the key. Can be used with optional params. *

* Time complexity: O(1) * @param key * @param value * @param params {@link SetParams} * @return simple-string-reply {@code OK} if {@code SET} was executed correctly, or {@code null} * if the {@code SET} operation was not performed because the user specified the NX or XX option * but the condition was not met. */ String set(String key, String value, SetParams params); /** * Get Command * Get the value of the specified key. If the key does not exist the special value 'nil' is * returned. If the value stored at key is not a string an error is returned because GET can only * handle string values. *

* Time complexity: O(1) * @param key * @return The value stored in key */ String get(String key); String setGet(String key, String value); String setGet(String key, String value, SetParams params); /** * GetDel Command * Get the value of key and delete the key. This command is similar to GET, except for the fact * that it also deletes the key on success (if and only if the key's value type is a string). *

* Time complexity: O(1) * @param key * @return The value stored in key */ String getDel(String key); /** * GetEx Command * Get the value of key and optionally set its expiration. GETEX is similar to {@link StringCommands#get(String) GET}, * but is a write command with additional options: * EX seconds -- Set the specified expire time, in seconds. * PX milliseconds -- Set the specified expire time, in milliseconds. * EXAT timestamp-seconds -- Set the specified Unix time at which the key will expire, in seconds. * PXAT timestamp-milliseconds -- Set the specified Unix time at which the key will expire, in milliseconds. * PERSIST -- Remove the time to live associated with the key. *

* Time complexity: O(1) * @param key * @param params {@link GetExParams} * @return The value stored in key */ String getEx(String key, GetExParams params); /** * SetRange Command * GETRANGE overwrite part of the string stored at key, starting at the specified offset, for the entire * length of value. If the offset is larger than the current length of the string at key, the string is * padded with zero-bytes to make offset fit. Non-existing keys are considered as empty strings, so this * command will make sure it holds a string large enough to be able to set value at offset. *

* Time complexity: O(1) * @param key * @param offset * @param value * @return The length of the string after it was modified by the command */ long setrange(String key, long offset, String value); /** * GetRange Command * Return the substring of the string value stored at key, determined by the offsets start * and end (both are inclusive). Negative offsets can be used in order to provide an offset starting * from the end of the string. So -1 means the last character, -2 the penultimate and so forth. *

* Time complexity: O(N) where N is the length of the returned string * @param key * @param startOffset * @param endOffset * @return The substring */ String getrange(String key, long startOffset, long endOffset); /** * GetSet Command * GETSET is an atomic set this value and return the old value command. Set key to the string * value and return the old value stored at key. The string can't be longer than 1073741824 byte (1 GB). *

* Time complexity: O(1) * @param key * @param value * @return The old value that was stored in key * @deprecated Use {@link StringCommands#setGet(java.lang.String, java.lang.String)}. */ @Deprecated String getSet(String key, String value); /** * SetNX Command * SETNX works exactly like {@link StringCommands#set(String, String) SET} with the only difference that if * the key already exists no operation is performed. SETNX actually means "SET if Not Exists". *

* Time complexity: O(1) * @param key * @param value * @return 1 if the key was set, 0 otherwise * @deprecated Use {@link StringCommands#set(String, String, SetParams)} with {@link SetParams#nx()}. * Deprecated in Jedis 7.3.0. Mirrors Redis deprecation since 2.6.12. */ @Deprecated long setnx(String key, String value); /** * SetEx Command * The command is exactly equivalent to the following group of commands: * {@link StringCommands#set(String, String) SET} + {@link KeyBinaryCommands#expire(byte[], long) EXPIRE}. * The operation is atomic. *

* Time complexity: O(1) * @param key * @param seconds * @param value * @return OK * @deprecated Use {@link StringCommands#set(String, String, SetParams)} with {@link SetParams#ex(long)}. * Deprecated in Jedis 7.3.0. Mirrors Redis deprecation since 2.6.12. */ @Deprecated String setex(String key, long seconds, String value); /** * PSetEx Command * PSETEX works exactly like {@link StringCommands#setex(String, long, String) SETEX} with the sole difference * that the expire time is specified in milliseconds instead of seconds. *

* Time complexity: O(1) * @param key * @param milliseconds * @param value * @return OK * @deprecated Use {@link StringCommands#set(String, String, SetParams)} with {@link SetParams#px(long)}. * Deprecated in Jedis 7.3.0. Mirrors Redis deprecation since 2.6.12. */ @Deprecated String psetex(String key, long milliseconds, String value); /** * MGet Command * Get the values of all the specified keys. If one or more keys don't exist or is not of type * String, a 'nil' value is returned instead of the value of the specified key, but the operation * never fails. *

* Time complexity: O(1) for every key * @param keys * @return Multi bulk reply */ List mget(String... keys); /** * MSet Command * Set the respective keys to the respective values. MSET will replace old values with new * values, while {@link StringCommands#msetnx(String...) MSETNX} will not perform any operation at all even * if just a single key already exists. *

* Because of this semantic MSETNX can be used in order to set different keys representing * different fields of an unique logic object in a way that ensures that either all the fields or * none at all are set. *

* Both MSET and MSETNX are atomic operations. This means that for instance if the keys A and B * are modified, another connection talking to Redis can either see the changes to both A and B at * once, or no modification at all. * @param keysvalues pairs of keys and their values * e.g mset("foo", "foovalue", "bar", "barvalue") * @return OK */ String mset(String... keysvalues); /** * MSetNX Command * Set the respective keys to the respective values. {@link StringCommands#mset(String...) MSET} will * replace old values with new values, while MSETNX will not perform any operation at all even if * just a single key already exists. *

* Because of this semantic MSETNX can be used in order to set different keys representing * different fields of an unique logic object in a way that ensures that either all the fields or * none at all are set. *

* Both MSET and MSETNX are atomic operations. This means that for instance if the keys A and B * are modified, another connection talking to Redis can either see the changes to both A and B at * once, or no modification at all. * @param keysvalues pairs of keys and their values * e.g msetnx("foo", "foovalue", "bar", "barvalue") * @return 1 if the all the keys were set, 0 if no key was set (at least one key already existed) */ long msetnx(String... keysvalues); /** * Multi-set with optional condition and expiration. *

* Sets the respective keys to the respective values, similar to {@link #mset(String...) MSET}, * but allows conditional set (NX|XX) and expiration options via {@link MSetExParams}. * If the condition is not met for any key, no key is set. *

* Both MSET and MSETEX are atomic operations. This means that if multiple keys are provided, * another client will either see the changes for all keys at once, or no changes at all. *

* Options (in {@link MSetExParams}): NX or XX, and expiration: EX seconds | PX milliseconds | * EXAT unix-time-seconds | PXAT unix-time-milliseconds | KEEPTTL. *

* Time complexity: O(N) where N is the number of keys to set. * @param params condition and expiration parameters * @param keysvalues pairs of keys and their values, e.g. {@code msetex(params, "foo", "foovalue", "bar", "barvalue")} * @return {@code true} if all the keys were set, {@code false} if none were set (condition not satisfied) * @see #mset(String...) * @see #msetnx(String...) */ boolean msetex(MSetExParams params, String... keysvalues); /** * Incr Command * Increment the number stored at key by one. If the key does not exist or contains a value of a * wrong type, set the key to the value of "0" before to perform the increment operation. *

* INCR commands are limited to 64-bit signed integers. *

* Note: this is actually a string operation, that is, in Redis there are not "integer" types. * Simply the string stored at the key is parsed as a base 10 64-bit signed integer, incremented, * and then converted back as a string. *

* Time complexity: O(1) * @param key the key to increment * @return The value of the key after the increment */ long incr(String key); /** * IncrBy Command * INCRBY work just like {@link StringCommands#incr(String) INCR} but instead to increment by 1 the * increment is integer. *

* INCR commands are limited to 64-bit signed integers. *

* Note: this is actually a string operation, that is, in Redis there are not "integer" types. * Simply the string stored at the key is parsed as a base 10 64-bit signed integer, incremented, * and then converted back as a string. *

* Time complexity: O(1) * @param key the key to increment * @param increment the value to increment by * @return The value of the key after the increment */ long incrBy(String key, long increment); /** * IncrByFloat Command * INCRBYFLOAT work just like {@link StringCommands#incrBy(String, long)} INCRBY} but increments by floats * instead of integers. *

* INCRBYFLOAT commands are limited to double precision floating point values. *

* Note: this is actually a string operation, that is, in Redis there are not "double" types. * Simply the string stored at the key is parsed as a base double precision floating point value, * incremented, and then converted back as a string. There is no DECRYBYFLOAT but providing a * negative value will work as expected. *

* Time complexity: O(1) * @param key the key to increment * @param increment the value to increment by * @return The value of the key after the increment */ double incrByFloat(String key, double increment); /** * Decr Command * Decrement the number stored at key by one. If the key does not exist or contains a value of a * wrong type, set the key to the value of "0" before to perform the decrement operation. *

* DECR commands are limited to 64-bit signed integers. *

* Note: this is actually a string operation, that is, in Redis there are not "integer" types. * Simply the string stored at the key is parsed as a base 10 64-bit signed integer, incremented, * and then converted back as a string. *

* Time complexity: O(1) * @param key the key to decrement * @return The value of the key after the decrement */ long decr(String key); /** * DecrBy Command * DECRBY work just like {@link StringCommands#decr(String) DECR} but instead to decrement by 1 the * decrement is integer. *

* DECRBY commands are limited to 64-bit signed integers. *

* Note: this is actually a string operation, that is, in Redis there are not "integer" types. * Simply the string stored at the key is parsed as a base 10 64-bit signed integer, incremented, * and then converted back as a string. *

* Time complexity: O(1) * @param key the key to decrement * @param decrement the value to decrement by * @return The value of the key after the decrement */ long decrBy(String key, long decrement); /** * Append Command * If the key already exists and is a string, this command appends the provided value at the end * of the string. If the key does not exist it is created and set as an empty string, so APPEND * will be very similar to SET in this special case. *

* Time complexity: O(1). The amortized time complexity is O(1) assuming the appended value is * small and the already present value is of any size, since the dynamic string library used by * Redis will double the free space available on every reallocation. * @param key the key to append to * @param value the value to append * @return The total length of the string after the append operation. */ long append(String key, String value); /** * SubStr Command * Return a subset of the string from offset start to offset end (both offsets are inclusive). * Negative offsets can be used in order to provide an offset starting from the end of the string. * So -1 means the last char, -2 the penultimate and so forth. *

* The function handles out of range requests without raising an error, but just limiting the * resulting range to the actual length of the string. *

* Time complexity: O(start+n) (with start being the start index and n the total length of the * requested range). Note that the lookup part of this command is O(1) so for small strings this * is actually an O(1) command. * @param key * @param start * @param end * @return The substring * @deprecated Use {@link StringCommands#getrange(String, long, long)}. * Deprecated in Jedis 7.3.0. Mirrors Redis deprecation since 2.0.0. */ @Deprecated String substr(String key, int start, int end); /** * StrLen Command * Return the length of the string value stored at key. * @param key * @return The length of the string at key, or 0 when key does not exist */ long strlen(String key); /** * Calculate the longest common subsequence of keyA and keyB. * @param keyA * @param keyB * @param params * @return According to LCSParams to decide to return content to fill LCSMatchResult. */ LCSMatchResult lcs(String keyA, String keyB, LCSParams params); } ================================================ FILE: src/main/java/redis/clients/jedis/commands/StringPipelineBinaryCommands.java ================================================ package redis.clients.jedis.commands; import java.util.List; import redis.clients.jedis.Response; import redis.clients.jedis.params.GetExParams; import redis.clients.jedis.params.SetParams; import redis.clients.jedis.params.MSetExParams; import redis.clients.jedis.params.LCSParams; import redis.clients.jedis.resps.LCSMatchResult; public interface StringPipelineBinaryCommands extends BitPipelineBinaryCommands { Response set(byte[] key, byte[] value); Response set(byte[] key, byte[] value, SetParams params); Response get(byte[] key); Response setGet(byte[] key, byte[] value); Response setGet(byte[] key, byte[] value, SetParams params); Response getDel(byte[] key); Response getEx(byte[] key, GetExParams params); Response setrange(byte[] key, long offset, byte[] value); Response getrange(byte[] key, long startOffset, long endOffset); /** * @deprecated {@link StringPipelineBinaryCommands#setGet(byte[], byte[], redis.clients.jedis.params.SetParams)}. */ @Deprecated Response getSet(byte[] key, byte[] value); Response setnx(byte[] key, byte[] value); Response setex(byte[] key, long seconds, byte[] value); Response psetex(byte[] key, long milliseconds, byte[] value); Response> mget(byte[]... keys); Response mset(byte[]... keysvalues); Response msetnx(byte[]... keysvalues); /** * Multi-set with optional condition and expiration. *

* Sets the respective keys to the respective values, similar to {@link #mset(byte[]...) MSET}, * but allows conditional set (NX|XX) and expiration options via {@link MSetExParams}. * If the condition is not met for any key, no key is set. *

* Both MSET and MSETEX are atomic operations. This means that if multiple keys are provided, * another client will either see the changes for all keys at once, or no changes at all. *

* Options (in {@link MSetExParams}): NX or XX, and expiration: EX seconds | PX milliseconds | * EXAT unix-time-seconds | PXAT unix-time-milliseconds | KEEPTTL. *

* Time complexity: O(N) where N is the number of keys to set. * @param params condition and expiration parameters * @param keysvalues pairs of keys and their values, e.g. {@code msetex(params, "foo".getBytes(), "foovalue".getBytes(), "bar".getBytes(), "barvalue".getBytes())} * @return {@code Response} that is {@code true} if all keys were set, {@code false} if none were set (condition not satisfied) * @see #mset(byte[]...) * @see #msetnx(byte[]...) */ Response msetex(MSetExParams params, byte[]... keysvalues); Response incr(byte[] key); Response incrBy(byte[] key, long increment); Response incrByFloat(byte[] key, double increment); Response decr(byte[] key); Response decrBy(byte[] key, long decrement); Response append(byte[] key, byte[] value); Response substr(byte[] key, int start, int end); Response strlen(byte[] key); Response lcs(byte[] keyA, byte[] keyB, LCSParams params); } ================================================ FILE: src/main/java/redis/clients/jedis/commands/StringPipelineCommands.java ================================================ package redis.clients.jedis.commands; import redis.clients.jedis.Response; import redis.clients.jedis.params.GetExParams; import redis.clients.jedis.params.SetParams; import redis.clients.jedis.params.MSetExParams; import redis.clients.jedis.params.LCSParams; import redis.clients.jedis.resps.LCSMatchResult; import java.util.List; public interface StringPipelineCommands extends BitPipelineCommands { Response set(String key, String value); Response set(String key, String value, SetParams params); Response get(String key); Response setGet(String key, String value); Response setGet(String key, String value, SetParams params); Response getDel(String key); Response getEx(String key, GetExParams params); Response setrange(String key, long offset, String value); Response getrange(String key, long startOffset, long endOffset); /** * @deprecated Use {@link StringPipelineCommands#setGet(java.lang.String, java.lang.String)}. */ @Deprecated Response getSet(String key, String value); Response setnx(String key, String value); Response setex(String key, long seconds, String value); Response psetex(String key, long milliseconds, String value); Response> mget(String... keys); Response mset(String... keysvalues); Response msetnx(String... keysvalues); /** * Multi-set with optional condition and expiration. *

* Sets the respective keys to the respective values, similar to {@link #mset(String...) MSET}, * but allows conditional set (NX|XX) and expiration options via {@link MSetExParams}. * If the condition is not met for any key, no key is set. *

* Both MSET and MSETEX are atomic operations. This means that if multiple keys are provided, * another client will either see the changes for all keys at once, or no changes at all. *

* Options (in {@link MSetExParams}): NX or XX, and expiration: EX seconds | PX milliseconds | * EXAT unix-time-seconds | PXAT unix-time-milliseconds | KEEPTTL. *

* Time complexity: O(N) where N is the number of keys to set. * @param params condition and expiration parameters * @param keysvalues pairs of keys and their values, e.g. {@code msetex(params, "foo", "foovalue", "bar", "barvalue")} * @return {@code Response} that is {@code true} if all keys were set, {@code false} if none were set (condition not satisfied) * @see #mset(String...) * @see #msetnx(String...) */ Response msetex(MSetExParams params, String... keysvalues); Response incr(String key); Response incrBy(String key, long increment); Response incrByFloat(String key, double increment); Response decr(String key); Response decrBy(String key, long decrement); Response append(String key, String value); Response substr(String key, int start, int end); Response strlen(String key); Response lcs(String keyA, String keyB, LCSParams params); } ================================================ FILE: src/main/java/redis/clients/jedis/commands/VectorSetBinaryCommands.java ================================================ package redis.clients.jedis.commands; import redis.clients.jedis.annots.Experimental; import redis.clients.jedis.params.VAddParams; import redis.clients.jedis.params.VSimParams; import redis.clients.jedis.resps.RawVector; import redis.clients.jedis.resps.VSimScoreAttribs; import java.util.List; import java.util.Map; /** * Interface for Redis Vector Set binary commands. Vector sets are a new data type introduced in * Redis 8.0 for vector similarity operations. */ public interface VectorSetBinaryCommands { /** * VADD Command Add a new element * into the vector set specified by key. *

* Time complexity: O(log(N)) for each element added, where N is the number of elements in the * vector set. * @param key the name of the key that will hold the vector set data * @param vector the vector as floating point numbers * @param element the name of the element that is being added to the vector set * @return 1 if key was added; 0 if key was not added */ @Experimental boolean vadd(byte[] key, float[] vector, byte[] element); /** * VADD Command Add a new element * into the vector set specified by key with additional parameters. *

* Time complexity: O(log(N)) for each element added, where N is the number of elements in the * vector set. * @param key the name of the key that will hold the vector set data * @param vector the vector as floating point numbers * @param element the name of the element that is being added to the vector set * @param params additional parameters for the VADD command * @return 1 if key was added; 0 if key was not added */ @Experimental boolean vadd(byte[] key, float[] vector, byte[] element, VAddParams params); /** * VADD Command Add a new element * into the vector set specified by key using FP32 binary format. *

* Time complexity: O(log(N)) for each element added, where N is the number of elements in the * vector set. * @param key the name of the key that will hold the vector set data * @param vectorBlob the vector as FP32 binary blob * @param element the name of the element that is being added to the vector set * @return 1 if key was added; 0 if key was not added */ @Experimental boolean vaddFP32(byte[] key, byte[] vectorBlob, byte[] element); /** * VADD Command Add a new element * into the vector set specified by key using FP32 binary format with additional parameters. *

* Time complexity: O(log(N)) for each element added, where N is the number of elements in the * vector set. * @param key the name of the key that will hold the vector set data * @param vectorBlob the vector as FP32 binary blob * @param element the name of the element that is being added to the vector set * @param params additional parameters for the VADD command * @return 1 if key was added; 0 if key was not added */ @Experimental boolean vaddFP32(byte[] key, byte[] vectorBlob, byte[] element, VAddParams params); /** * VADD Command Add a new element * into the vector set specified by key with dimension reduction and additional parameters. *

* Time complexity: O(log(N)) for each element added, where N is the number of elements in the * vector set. * @param key the name of the key that will hold the vector set data * @param vector the vector as floating point numbers * @param element the name of the element that is being added to the vector set * @param reduceDim the target dimension after reduction using random projection * @param params additional parameters for the VADD command * @return 1 if key was added; 0 if key was not added */ @Experimental boolean vadd(byte[] key, float[] vector, byte[] element, int reduceDim, VAddParams params); /** * VADD Command Add a new element * into the vector set specified by key using FP32 binary format with dimension reduction and * additional parameters. *

* Time complexity: O(log(N)) for each element added, where N is the number of elements in the * vector set. * @param key the name of the key that will hold the vector set data * @param vectorBlob the vector as FP32 binary blob * @param element the name of the element that is being added to the vector set * @param reduceDim the target dimension after reduction using random projection * @param params additional parameters for the VADD command * @return 1 if key was added; 0 if key was not added */ @Experimental boolean vaddFP32(byte[] key, byte[] vectorBlob, byte[] element, int reduceDim, VAddParams params); /** * VSIM Command Return elements * similar to a given vector. *

* Time complexity: O(log(N)) where N is the number of elements in the vector set. * @param key the name of the key that holds the vector set data * @param vector the vector to use as similarity reference * @return list of similar elements */ @Experimental List vsim(byte[] key, float[] vector); /** * VSIM Command Return elements * similar to a given vector with additional parameters. *

* Time complexity: O(log(N)) where N is the number of elements in the vector set. * @param key the name of the key that holds the vector set data * @param vector the vector to use as similarity reference * @param params additional parameters for the VSIM command * @return list of similar elements */ @Experimental List vsim(byte[] key, float[] vector, VSimParams params); /** * VSIM Command Return elements * similar to a given vector with their similarity scores. *

* Time complexity: O(log(N)) where N is the number of elements in the vector set. * @param key the name of the key that holds the vector set data * @param vector the vector to use as similarity reference * @param params additional parameters for the VSIM command (WITHSCORES will be automatically * added) * @return map of element names to their similarity scores */ @Experimental Map vsimWithScores(byte[] key, float[] vector, VSimParams params); /** * VSIM Command Return elements * similar to a given vector with their similarity scores and attributes. *

* Time complexity: O(log(N)) where N is the number of elements in the vector set. * @param key the name of the key that holds the vector set data * @param vector the vector to use as similarity reference * @param params additional parameters for the VSIM command (WITHSCORES and WITHATTRIBS will be * automatically added) * @return map of element names to their similarity scores and attributes */ @Experimental Map vsimWithScoresAndAttribs(byte[] key, float[] vector, VSimParams params); /** * VSIM Command Return elements * similar to a given element in the vector set. *

* Time complexity: O(log(N)) where N is the number of elements in the vector set. * @param key the name of the key that holds the vector set data * @param element the name of the element to use as similarity reference * @return list of similar elements */ @Experimental List vsimByElement(byte[] key, byte[] element); /** * VSIM Command Return elements * similar to a given element in the vector set with additional parameters. *

* Time complexity: O(log(N)) where N is the number of elements in the vector set. * @param key the name of the key that holds the vector set data * @param element the name of the element to use as similarity reference * @param params additional parameters for the VSIM command * @return list of similar elements */ @Experimental List vsimByElement(byte[] key, byte[] element, VSimParams params); /** * VSIM Command Return elements * similar to a given element in the vector set with their similarity scores. *

* Time complexity: O(log(N)) where N is the number of elements in the vector set. * @param key the name of the key that holds the vector set data * @param element the name of the element to use as similarity reference * @param params additional parameters for the VSIM command (WITHSCORES will be automatically * added) * @return map of element names to their similarity scores */ @Experimental Map vsimByElementWithScores(byte[] key, byte[] element, VSimParams params); /** * VSIM Command Return elements * similar to a given element in the vector set with their similarity scores and attributes. *

* Time complexity: O(log(N)) where N is the number of elements in the vector set. * @param key the name of the key that holds the vector set data * @param element the name of the element to use as similarity reference * @param params additional parameters for the VSIM command (WITHSCORES and WITHATTRIBS will be * automatically added) * @return map of element names to their similarity scores and attributes */ @Experimental Map vsimByElementWithScoresAndAttribs(byte[] key, byte[] element, VSimParams params); /** * VDIM Command Return the number * of dimensions of the vectors in the specified vector set. *

* Time complexity: O(1) * @param key the name of the key that holds the vector set * @return the number of vector set elements */ @Experimental long vdim(byte[] key); /** * VCARD Command Return the * number of elements in the specified vector set. *

* Time complexity: O(1) * @param key the name of the key that holds the vector set * @return the number of elements in the vector set */ @Experimental long vcard(byte[] key); /** * VEMB Command Return the * approximate vector associated with a given element in the vector set. *

* Time complexity: O(1) * @param key the name of the key that holds the vector set * @param element the name of the element whose vector you want to retrieve * @return list of real numbers representing the vector */ @Experimental List vemb(byte[] key, byte[] element); /** * VEMB Command Return the raw * vector data associated with a given element in the vector set. *

* Time complexity: O(1) * @param key the name of the key that holds the vector set * @param element the name of the element whose vector you want to retrieve * @return RawVector containing raw vector data, quantization type, and metadata */ RawVector vembRaw(byte[] key, byte[] element); /** * VREM Command Remove an element * from a vector set. *

* Time complexity: O(log(N)) for each element removed, where N is the number of elements in the * vector set * @param key the name of the key that holds the vector set * @param element the name of the element to remove from the vector set * @return true if the element was removed, false if either element or key do not exist */ @Experimental boolean vrem(byte[] key, byte[] element); /** * VLINKS Command Return the * neighbors of a specified element in a vector set. *

* Time complexity: O(1) * @param key the name of the key that holds the vector set * @param element the name of the element whose HNSW neighbors you want to inspect * @return list of neighbor element names */ @Experimental List> vlinks(byte[] key, byte[] element); /** * VLINKS Command Return the * neighbors of a specified element in a vector set with similarity scores. *

* Time complexity: O(1) * @param key the name of the key that holds the vector set * @param element the name of the element whose HNSW neighbors you want to inspect * @return List of map of neighbor element names to similarity scores per layer */ @Experimental List> vlinksWithScores(byte[] key, byte[] element); /** * VRANDMEMBER Command * Return a random element from a vector set. *

* Time complexity: O(1) * @param key the name of the key that holds the vector set * @return a random element name, or null if the key does not exist */ @Experimental byte[] vrandmember(byte[] key); /** * VRANDMEMBER Command * Return random elements from a vector set. *

* Time complexity: O(N) where N is the absolute value of the count argument * @param key the name of the key that holds the vector set * @param count the number of elements to return. Positive values return distinct elements; * negative values allow duplicates * @return list of random element names */ @Experimental List vrandmember(byte[] key, int count); /** * VGETATTR Command Get the * attributes of an element in a vector set. *

* Time complexity: O(1) * @param key the name of the key that holds the vector set * @param element the name of the element whose attributes to retrieve * @return the attributes of the element as a JSON string, or null if the element doesn't exist or * has no attributes */ @Experimental byte[] vgetattr(byte[] key, byte[] element); /** * VSETATTR Command Set the * attributes of an element in a vector set. *

* Time complexity: O(1) * @param key the name of the key that holds the vector set * @param element the name of the element whose attributes to set * @param attributes the attributes to set as a JSON string * @return true if the attributes were set successfully */ @Experimental boolean vsetattr(byte[] key, byte[] element, byte[] attributes); } ================================================ FILE: src/main/java/redis/clients/jedis/commands/VectorSetCommands.java ================================================ package redis.clients.jedis.commands; import java.util.List; import java.util.Map; import redis.clients.jedis.annots.Experimental; import redis.clients.jedis.params.VAddParams; import redis.clients.jedis.params.VSimParams; import redis.clients.jedis.resps.RawVector; import redis.clients.jedis.resps.VSimScoreAttribs; import redis.clients.jedis.resps.VectorInfo; /** * Interface for Redis Vector Set commands. Vector sets are a new data type introduced in Redis 8.0 * for vector similarity operations. */ public interface VectorSetCommands { /** * VADD Command Add a new element * into the vector set specified by key. *

* Time complexity: O(log(N)) for each element added, where N is the number of elements in the * vector set. * @param key the name of the key that will hold the vector set data * @param vector the vector as floating point numbers * @param element the name of the element that is being added to the vector set * @return 1 if key was added; 0 if key was not added */ @Experimental boolean vadd(String key, float[] vector, String element); /** * VADD Command Add a new element * into the vector set specified by key with additional parameters. *

* Time complexity: O(log(N)) for each element added, where N is the number of elements in the * vector set. * @param key the name of the key that will hold the vector set data * @param vector the vector as floating point numbers * @param element the name of the element that is being added to the vector set * @param params additional parameters for the VADD command * @return 1 if key was added; 0 if key was not added */ @Experimental boolean vadd(String key, float[] vector, String element, VAddParams params); /** * VADD Command Add a new element * into the vector set specified by key using FP32 binary format. *

* Time complexity: O(log(N)) for each element added, where N is the number of elements in the * vector set. * @param key the name of the key that will hold the vector set data * @param vectorBlob the vector as FP32 binary blob * @param element the name of the element that is being added to the vector set * @return 1 if key was added; 0 if key was not added */ @Experimental boolean vaddFP32(String key, byte[] vectorBlob, String element); /** * VADD Command Add a new element * into the vector set specified by key using FP32 binary format with additional parameters. *

* Time complexity: O(log(N)) for each element added, where N is the number of elements in the * vector set. * @param key the name of the key that will hold the vector set data * @param vectorBlob the vector as FP32 binary blob * @param element the name of the element that is being added to the vector set * @param params additional parameters for the VADD command * @return 1 if key was added; 0 if key was not added */ @Experimental boolean vaddFP32(String key, byte[] vectorBlob, String element, VAddParams params); /** * VADD Command Add a new element * into the vector set specified by key with dimension reduction and additional parameters. *

* Time complexity: O(log(N)) for each element added, where N is the number of elements in the * vector set. * @param key the name of the key that will hold the vector set data * @param vector the vector as floating point numbers * @param element the name of the element that is being added to the vector set * @param reduceDim the target dimension after reduction using random projection * @param params additional parameters for the VADD command * @return 1 if key was added; 0 if key was not added */ @Experimental boolean vadd(String key, float[] vector, String element, int reduceDim, VAddParams params); /** * VADD Command Add a new element * into the vector set specified by key using FP32 binary format with dimension reduction and * additional parameters. *

* Time complexity: O(log(N)) for each element added, where N is the number of elements in the * vector set. * @param key the name of the key that will hold the vector set data * @param vectorBlob the vector as FP32 binary blob * @param element the name of the element that is being added to the vector set * @param reduceDim the target dimension after reduction using random projection * @param params additional parameters for the VADD command * @return 1 if key was added; 0 if key was not added */ @Experimental boolean vaddFP32(String key, byte[] vectorBlob, String element, int reduceDim, VAddParams params); /** * VSIM Command Return elements * similar to a given vector. *

* Time complexity: O(log(N)) where N is the number of elements in the vector set. * @param key the name of the key that holds the vector set data * @param vector the vector to use as similarity reference * @return list of similar elements */ @Experimental List vsim(String key, float[] vector); /** * VSIM Command Return elements * similar to a given vector with additional parameters. *

* Time complexity: O(log(N)) where N is the number of elements in the vector set. * @param key the name of the key that holds the vector set data * @param vector the vector to use as similarity reference * @param params additional parameters for the VSIM command * @return list of similar elements */ @Experimental List vsim(String key, float[] vector, VSimParams params); /** * VSIM Command Return elements * similar to a given vector with their similarity scores. *

* Time complexity: O(log(N)) where N is the number of elements in the vector set. * @param key the name of the key that holds the vector set data * @param vector the vector to use as similarity reference * @param params additional parameters for the VSIM command (WITHSCORES will be automatically * added) * @return map of element names to their similarity scores */ @Experimental Map vsimWithScores(String key, float[] vector, VSimParams params); /** * VSIM Command Return elements * similar to a given vector with their similarity scores and attributes. *

* Time complexity: O(log(N)) where N is the number of elements in the vector set. * @param key the name of the key that holds the vector set data * @param vector the vector to use as similarity reference * @param params additional parameters for the VSIM command (WITHSCORES and WITHATTRIBS will be * automatically added) * @return map of element names to their similarity scores and attributes */ @Experimental Map vsimWithScoresAndAttribs(String key, float[] vector, VSimParams params); /** * VSIM Command Return elements * similar to a given element in the vector set. *

* Time complexity: O(log(N)) where N is the number of elements in the vector set. * @param key the name of the key that holds the vector set data * @param element the name of the element to use as similarity reference * @return list of similar elements */ @Experimental List vsimByElement(String key, String element); /** * VSIM Command Return elements * similar to a given element in the vector set with additional parameters. *

* Time complexity: O(log(N)) where N is the number of elements in the vector set. * @param key the name of the key that holds the vector set data * @param element the name of the element to use as similarity reference * @param params additional parameters for the VSIM command * @return list of similar elements */ @Experimental List vsimByElement(String key, String element, VSimParams params); /** * VSIM Command Return elements * similar to a given element in the vector set with their similarity scores. *

* Time complexity: O(log(N)) where N is the number of elements in the vector set. * @param key the name of the key that holds the vector set data * @param element the name of the element to use as similarity reference * @param params additional parameters for the VSIM command (WITHSCORES will be automatically * added) * @return map of element names to their similarity scores */ @Experimental Map vsimByElementWithScores(String key, String element, VSimParams params); /** * VSIM Command Return elements * similar to a given element in the vector set with their similarity scores and attributes. *

* Time complexity: O(log(N)) where N is the number of elements in the vector set. * @param key the name of the key that holds the vector set data * @param element the name of the element to use as similarity reference * @param params additional parameters for the VSIM command (WITHSCORES and WITHATTRIBS will be * automatically added) * @return map of element names to their similarity scores and attributes */ @Experimental Map vsimByElementWithScoresAndAttribs(String key, String element, VSimParams params); /** * VDIM Command Return the number * of dimensions of the vectors in the specified vector set. *

* Time complexity: O(1) * @param key the name of the key that holds the vector set * @return the number of vector set elements */ @Experimental long vdim(String key); /** * VCARD Command Return the * number of elements in the specified vector set. *

* Time complexity: O(1) * @param key the name of the key that holds the vector set * @return the number of elements in the vector set */ @Experimental long vcard(String key); /** * VEMB Command Return the * approximate vector associated with a given element in the vector set. *

* Time complexity: O(1) * @param key the name of the key that holds the vector set * @param element the name of the element whose vector you want to retrieve * @return list of real numbers representing the vector */ @Experimental List vemb(String key, String element); /** * VEMB Command Return the raw * vector data associated with a given element in the vector set. *

* Time complexity: O(1) * @param key the name of the key that holds the vector set * @param element the name of the element whose vector you want to retrieve * @return RawVector containing raw vector data, quantization type, and metadata */ @Experimental RawVector vembRaw(String key, String element); /** * VREM Command Remove an element * from a vector set. *

* Time complexity: O(log(N)) for each element removed, where N is the number of elements in the * vector set * @param key the name of the key that holds the vector set * @param element the name of the element to remove from the vector set * @return true if the element was removed, false if either element or key do not exist */ @Experimental boolean vrem(String key, String element); /** * VLINKS Command Return the * neighbors of a specified element in a vector set. *

* Time complexity: O(1) * @param key the name of the key that holds the vector set * @param element the name of the element whose HNSW neighbors you want to inspect * @return list of neighbor element names */ @Experimental List> vlinks(String key, String element); /** * VLINKS Command Return the * neighbors of a specified element in a vector set with similarity scores. *

* Time complexity: O(1) * @param key the name of the key that holds the vector set * @param element the name of the element whose HNSW neighbors you want to inspect * @return List of map of neighbor element names to similarity scores per layer */ @Experimental List> vlinksWithScores(String key, String element); /** * VRANDMEMBER Command * Return a random element from a vector set. *

* Time complexity: O(1) * @param key the name of the key that holds the vector set * @return a random element name, or null if the key does not exist */ @Experimental String vrandmember(String key); /** * VRANDMEMBER Command * Return random elements from a vector set. *

* Time complexity: O(N) where N is the absolute value of the count argument * @param key the name of the key that holds the vector set * @param count the number of elements to return. Positive values return distinct elements; * negative values allow duplicates * @return list of random element names */ @Experimental List vrandmember(String key, int count); /** * VGETATTR Command Get the * attributes of an element in a vector set. *

* Time complexity: O(1) * @param key the name of the key that holds the vector set * @param element the name of the element whose attributes to retrieve * @return the attributes of the element as a JSON string, or null if the element doesn't exist or * has no attributes */ @Experimental String vgetattr(String key, String element); /** * VSETATTR Command Set the * attributes of an element in a vector set. *

* Time complexity: O(1) * @param key the name of the key that holds the vector set * @param element the name of the element whose attributes to set * @param attributes the attributes to set as a JSON string * @return true if the attributes were set successfully */ @Experimental boolean vsetattr(String key, String element, String attributes); /** * VINFO Command Get information * about a vector set. *

* Time complexity: O(1) * @param key the name of the key that holds the vector set * @return information about the vector set */ @Experimental VectorInfo vinfo(String key); } ================================================ FILE: src/main/java/redis/clients/jedis/commands/VectorSetPipelineBinaryCommands.java ================================================ package redis.clients.jedis.commands; import java.util.List; import java.util.Map; import redis.clients.jedis.Response; import redis.clients.jedis.annots.Experimental; import redis.clients.jedis.params.VAddParams; import redis.clients.jedis.params.VSimParams; import redis.clients.jedis.resps.RawVector; /** * Interface for Redis Vector Set binary pipeline commands. Vector sets are a new data type * introduced in Redis 8.0 for vector similarity operations. */ public interface VectorSetPipelineBinaryCommands { /** * VADD Command Add a new element * into the vector set specified by key. *

* Time complexity: O(log(N)) for each element added, where N is the number of elements in the * vector set. * @param key the name of the key that will hold the vector set data * @param vector the vector as floating point numbers * @param element the name of the element that is being added to the vector set * @return Response wrapping 1 if key was added; 0 if key was not added */ @Experimental Response vadd(byte[] key, float[] vector, byte[] element); /** * VADD Command Add a new element * into the vector set specified by key with additional parameters. *

* Time complexity: O(log(N)) for each element added, where N is the number of elements in the * vector set. * @param key the name of the key that will hold the vector set data * @param vector the vector as floating point numbers * @param element the name of the element that is being added to the vector set * @param params additional parameters for the VADD command * @return Response wrapping 1 if key was added; 0 if key was not added */ @Experimental Response vadd(byte[] key, float[] vector, byte[] element, VAddParams params); /** * VADD Command Add a new element * into the vector set specified by key using FP32 binary format. *

* Time complexity: O(log(N)) for each element added, where N is the number of elements in the * vector set. * @param key the name of the key that will hold the vector set data * @param vectorBlob the vector as FP32 binary blob * @param element the name of the element that is being added to the vector set * @return Response wrapping 1 if key was added; 0 if key was not added */ @Experimental Response vaddFP32(byte[] key, byte[] vectorBlob, byte[] element); /** * VADD Command Add a new element * into the vector set specified by key using FP32 binary format with additional parameters. *

* Time complexity: O(log(N)) for each element added, where N is the number of elements in the * vector set. * @param key the name of the key that will hold the vector set data * @param vectorBlob the vector as FP32 binary blob * @param element the name of the element that is being added to the vector set * @param params additional parameters for the VADD command * @return Response wrapping 1 if key was added; 0 if key was not added */ @Experimental Response vaddFP32(byte[] key, byte[] vectorBlob, byte[] element, VAddParams params); /** * VADD Command Add a new element * into the vector set specified by key with dimension reduction and additional parameters. *

* Time complexity: O(log(N)) for each element added, where N is the number of elements in the * vector set. * @param key the name of the key that will hold the vector set data * @param vector the vector as floating point numbers * @param element the name of the element that is being added to the vector set * @param reduceDim the target dimension after reduction using random projection * @param params additional parameters for the VADD command * @return Response wrapping 1 if key was added; 0 if key was not added */ @Experimental Response vadd(byte[] key, float[] vector, byte[] element, int reduceDim, VAddParams params); /** * VADD Command Add a new element * into the vector set specified by key using FP32 binary format with dimension reduction and * additional parameters. *

* Time complexity: O(log(N)) for each element added, where N is the number of elements in the * vector set. * @param key the name of the key that will hold the vector set data * @param vectorBlob the vector as FP32 binary blob * @param element the name of the element that is being added to the vector set * @param reduceDim the target dimension after reduction using random projection * @param params additional parameters for the VADD command * @return Response wrapping 1 if key was added; 0 if key was not added */ @Experimental Response vaddFP32(byte[] key, byte[] vectorBlob, byte[] element, int reduceDim, VAddParams params); /** * VSIM Command Return elements * similar to a given vector. *

* Time complexity: O(log(N)) where N is the number of elements in the vector set. * @param key the name of the key that holds the vector set data * @param vector the vector to use as similarity reference * @return Response wrapping list of similar elements */ @Experimental Response> vsim(byte[] key, float[] vector); /** * VSIM Command Return elements * similar to a given vector with additional parameters. *

* Time complexity: O(log(N)) where N is the number of elements in the vector set. * @param key the name of the key that holds the vector set data * @param vector the vector to use as similarity reference * @param params additional parameters for the VSIM command * @return Response wrapping list of similar elements */ @Experimental Response> vsim(byte[] key, float[] vector, VSimParams params); /** * VSIM Command Return elements * similar to a given vector with their similarity scores. *

* Time complexity: O(log(N)) where N is the number of elements in the vector set. * @param key the name of the key that holds the vector set data * @param vector the vector to use as similarity reference * @param params additional parameters for the VSIM command (WITHSCORES will be automatically * added) * @return Response wrapping map of element names to their similarity scores */ @Experimental Response> vsimWithScores(byte[] key, float[] vector, VSimParams params); /** * VSIM Command Return elements * similar to a given element in the vector set. *

* Time complexity: O(log(N)) where N is the number of elements in the vector set. * @param key the name of the key that holds the vector set data * @param element the name of the element to use as similarity reference * @return Response wrapping list of similar elements */ @Experimental Response> vsimByElement(byte[] key, byte[] element); /** * VSIM Command Return elements * similar to a given element in the vector set with additional parameters. *

* Time complexity: O(log(N)) where N is the number of elements in the vector set. * @param key the name of the key that holds the vector set data * @param element the name of the element to use as similarity reference * @param params additional parameters for the VSIM command * @return Response wrapping list of similar elements */ @Experimental Response> vsimByElement(byte[] key, byte[] element, VSimParams params); /** * VSIM Command Return elements * similar to a given element in the vector set with their similarity scores. *

* Time complexity: O(log(N)) where N is the number of elements in the vector set. * @param key the name of the key that holds the vector set data * @param element the name of the element to use as similarity reference * @param params additional parameters for the VSIM command (WITHSCORES will be automatically * added) * @return Response wrapping map of element names to their similarity scores */ @Experimental Response> vsimByElementWithScores(byte[] key, byte[] element, VSimParams params); /** * VDIM Command Return the number * of dimensions of the vectors in the specified vector set. *

* Time complexity: O(1) * @param key the name of the key that holds the vector set * @return Response wrapping the number of vector set elements */ @Experimental Response vdim(byte[] key); /** * VCARD Command Return the * number of elements in the specified vector set. *

* Time complexity: O(1) * @param key the name of the key that holds the vector set * @return Response wrapping the number of elements in the vector set */ @Experimental Response vcard(byte[] key); /** * VEMB Command Return the * approximate vector associated with a given element in the vector set. *

* Time complexity: O(1) * @param key the name of the key that holds the vector set * @param element the name of the element whose vector you want to retrieve * @return Response wrapping list of real numbers representing the vector */ @Experimental Response> vemb(byte[] key, byte[] element); /** * VEMB Command Return the raw * vector data associated with a given element in the vector set. *

* Time complexity: O(1) * @param key the name of the key that holds the vector set * @param element the name of the element whose vector you want to retrieve * @return Response wrapping RawVector containing raw vector data, quantization type, and metadata */ @Experimental Response vembRaw(byte[] key, byte[] element); /** * VREM Command Remove an element * from a vector set. *

* Time complexity: O(log(N)) for each element removed, where N is the number of elements in the * vector set * @param key the name of the key that holds the vector set * @param element the name of the element to remove from the vector set * @return Response wrapping true if the element was removed, false if either element or key do * not exist */ @Experimental Response vrem(byte[] key, byte[] element); /** * VLINKS Command Return the * neighbors of a specified element in a vector set. *

* Time complexity: O(1) * @param key the name of the key that holds the vector set * @param element the name of the element whose HNSW neighbors you want to inspect * @return Response wrapping list of neighbor element names */ @Experimental Response>> vlinks(byte[] key, byte[] element); /** * VLINKS Command Return the * neighbors of a specified element in a vector set with similarity scores. *

* Time complexity: O(1) * @param key the name of the key that holds the vector set * @param element the name of the element whose HNSW neighbors you want to inspect * @return Response wrapping map of neighbor element names to similarity scores */ @Experimental Response>> vlinksWithScores(byte[] key, byte[] element); /** * VRANDMEMBER Command * Return a random element from a vector set. *

* Time complexity: O(1) * @param key the name of the key that holds the vector set * @return Response wrapping a random element name, or null if the key does not exist */ @Experimental Response vrandmember(byte[] key); /** * VRANDMEMBER Command * Return random elements from a vector set. *

* Time complexity: O(N) where N is the absolute value of the count argument * @param key the name of the key that holds the vector set * @param count the number of elements to return. Positive values return distinct elements; * negative values allow duplicates * @return Response wrapping list of random element names */ @Experimental Response> vrandmember(byte[] key, int count); /** * VGETATTR Command Get the * attributes of an element in a vector set. *

* Time complexity: O(1) * @param key the name of the key that holds the vector set * @param element the name of the element whose attributes to retrieve * @return Response wrapping the attributes of the element as a JSON string, or null if the * element doesn't exist or has no attributes */ @Experimental Response vgetattr(byte[] key, byte[] element); /** * VSETATTR Command Set the * attributes of an element in a vector set. *

* Time complexity: O(1) * @param key the name of the key that holds the vector set * @param element the name of the element whose attributes to set * @param attributes the attributes to set as a JSON string * @return Response wrapping true if the attributes were set successfully */ @Experimental Response vsetattr(byte[] key, byte[] element, byte[] attributes); } ================================================ FILE: src/main/java/redis/clients/jedis/commands/VectorSetPipelineCommands.java ================================================ package redis.clients.jedis.commands; import java.util.List; import java.util.Map; import redis.clients.jedis.Response; import redis.clients.jedis.annots.Experimental; import redis.clients.jedis.params.VAddParams; import redis.clients.jedis.params.VSimParams; import redis.clients.jedis.resps.RawVector; import redis.clients.jedis.resps.VectorInfo; /** * Interface for Redis Vector Set pipeline commands. Vector sets are a new data type introduced in * Redis 8.0 for vector similarity operations. */ public interface VectorSetPipelineCommands { /** * VADD Command Add a new element * into the vector set specified by key. *

* Time complexity: O(log(N)) for each element added, where N is the number of elements in the * vector set. * @param key the name of the key that will hold the vector set data * @param vector the vector as floating point numbers * @param element the name of the element that is being added to the vector set * @return Response wrapping 1 if key was added; 0 if key was not added */ @Experimental Response vadd(String key, float[] vector, String element); /** * VADD Command Add a new element * into the vector set specified by key with additional parameters. *

* Time complexity: O(log(N)) for each element added, where N is the number of elements in the * vector set. * @param key the name of the key that will hold the vector set data * @param vector the vector as floating point numbers * @param element the name of the element that is being added to the vector set * @param params additional parameters for the VADD command * @return Response wrapping 1 if key was added; 0 if key was not added */ @Experimental Response vadd(String key, float[] vector, String element, VAddParams params); /** * VADD Command Add a new element * into the vector set specified by key using FP32 binary format. *

* Time complexity: O(log(N)) for each element added, where N is the number of elements in the * vector set. * @param key the name of the key that will hold the vector set data * @param vectorBlob the vector as FP32 binary blob * @param element the name of the element that is being added to the vector set * @return Response wrapping 1 if key was added; 0 if key was not added */ @Experimental Response vaddFP32(String key, byte[] vectorBlob, String element); /** * VADD Command Add a new element * into the vector set specified by key using FP32 binary format with additional parameters. *

* Time complexity: O(log(N)) for each element added, where N is the number of elements in the * vector set. * @param key the name of the key that will hold the vector set data * @param vectorBlob the vector as FP32 binary blob * @param element the name of the element that is being added to the vector set * @param params additional parameters for the VADD command * @return Response wrapping 1 if key was added; 0 if key was not added */ @Experimental Response vaddFP32(String key, byte[] vectorBlob, String element, VAddParams params); /** * VADD Command Add a new element * into the vector set specified by key with dimension reduction and additional parameters. *

* Time complexity: O(log(N)) for each element added, where N is the number of elements in the * vector set. * @param key the name of the key that will hold the vector set data * @param vector the vector as floating point numbers * @param element the name of the element that is being added to the vector set * @param reduceDim the target dimension after reduction using random projection * @param params additional parameters for the VADD command * @return Response wrapping 1 if key was added; 0 if key was not added */ @Experimental Response vadd(String key, float[] vector, String element, int reduceDim, VAddParams params); /** * VADD Command Add a new element * into the vector set specified by key using FP32 binary format with dimension reduction and * additional parameters. *

* Time complexity: O(log(N)) for each element added, where N is the number of elements in the * vector set. * @param key the name of the key that will hold the vector set data * @param vectorBlob the vector as FP32 binary blob * @param element the name of the element that is being added to the vector set * @param reduceDim the target dimension after reduction using random projection * @param params additional parameters for the VADD command * @return Response wrapping 1 if key was added; 0 if key was not added */ @Experimental Response vaddFP32(String key, byte[] vectorBlob, String element, int reduceDim, VAddParams params); /** * VSIM Command Return elements * similar to a given vector. *

* Time complexity: O(log(N)) where N is the number of elements in the vector set. * @param key the name of the key that holds the vector set data * @param vector the vector to use as similarity reference * @return Response wrapping list of similar elements */ @Experimental Response> vsim(String key, float[] vector); /** * VSIM Command Return elements * similar to a given vector with additional parameters. *

* Time complexity: O(log(N)) where N is the number of elements in the vector set. * @param key the name of the key that holds the vector set data * @param vector the vector to use as similarity reference * @param params additional parameters for the VSIM command * @return Response wrapping list of similar elements */ @Experimental Response> vsim(String key, float[] vector, VSimParams params); /** * VSIM Command Return elements * similar to a given vector with their similarity scores. *

* Time complexity: O(log(N)) where N is the number of elements in the vector set. * @param key the name of the key that holds the vector set data * @param vector the vector to use as similarity reference * @param params additional parameters for the VSIM command (WITHSCORES will be automatically * added) * @return Response wrapping map of element names to their similarity scores */ @Experimental Response> vsimWithScores(String key, float[] vector, VSimParams params); /** * VSIM Command Return elements * similar to a given element in the vector set. *

* Time complexity: O(log(N)) where N is the number of elements in the vector set. * @param key the name of the key that holds the vector set data * @param element the name of the element to use as similarity reference * @return Response wrapping list of similar elements */ @Experimental Response> vsimByElement(String key, String element); /** * VSIM Command Return elements * similar to a given element in the vector set with additional parameters. *

* Time complexity: O(log(N)) where N is the number of elements in the vector set. * @param key the name of the key that holds the vector set data * @param element the name of the element to use as similarity reference * @param params additional parameters for the VSIM command * @return Response wrapping list of similar elements */ @Experimental Response> vsimByElement(String key, String element, VSimParams params); /** * VSIM Command Return elements * similar to a given element in the vector set with their similarity scores. *

* Time complexity: O(log(N)) where N is the number of elements in the vector set. * @param key the name of the key that holds the vector set data * @param element the name of the element to use as similarity reference * @param params additional parameters for the VSIM command (WITHSCORES will be automatically * added) * @return Response wrapping map of element names to their similarity scores */ @Experimental Response> vsimByElementWithScores(String key, String element, VSimParams params); /** * VDIM Command Return the number * of dimensions of the vectors in the specified vector set. *

* Time complexity: O(1) * @param key the name of the key that holds the vector set * @return Response wrapping the number of vector set elements */ @Experimental Response vdim(String key); /** * VCARD Command Return the * number of elements in the specified vector set. *

* Time complexity: O(1) * @param key the name of the key that holds the vector set * @return Response wrapping the number of elements in the vector set */ @Experimental Response vcard(String key); /** * VEMB Command Return the * approximate vector associated with a given element in the vector set. *

* Time complexity: O(1) * @param key the name of the key that holds the vector set * @param element the name of the element whose vector you want to retrieve * @return Response wrapping list of real numbers representing the vector */ @Experimental Response> vemb(String key, String element); /** * VEMB Command Return the raw * vector data associated with a given element in the vector set. *

* Time complexity: O(1) * @param key the name of the key that holds the vector set * @param element the name of the element whose vector you want to retrieve * @return Response wrapping RawVector containing raw vector data, quantization type, and metadata */ @Experimental Response vembRaw(String key, String element); /** * VREM Command Remove an element * from a vector set. *

* Time complexity: O(log(N)) for each element removed, where N is the number of elements in the * vector set * @param key the name of the key that holds the vector set * @param element the name of the element to remove from the vector set * @return Response wrapping true if the element was removed, false if either element or key do * not exist */ @Experimental Response vrem(String key, String element); /** * VLINKS Command Return the * neighbors of a specified element in a vector set. *

* Time complexity: O(1) * @param key the name of the key that holds the vector set * @param element the name of the element whose HNSW neighbors you want to inspect * @return Response wrapping list of neighbor element names */ @Experimental Response>> vlinks(String key, String element); /** * VLINKS Command Return the * neighbors of a specified element in a vector set with similarity scores. *

* Time complexity: O(1) * @param key the name of the key that holds the vector set * @param element the name of the element whose HNSW neighbors you want to inspect * @return Response wrapping map of neighbor element names to similarity scores */ @Experimental Response>> vlinksWithScores(String key, String element); /** * VRANDMEMBER Command * Return a random element from a vector set. *

* Time complexity: O(1) * @param key the name of the key that holds the vector set * @return Response wrapping a random element name, or null if the key does not exist */ @Experimental Response vrandmember(String key); /** * VRANDMEMBER Command * Return random elements from a vector set. *

* Time complexity: O(N) where N is the absolute value of the count argument * @param key the name of the key that holds the vector set * @param count the number of elements to return. Positive values return distinct elements; * negative values allow duplicates * @return Response wrapping list of random element names */ @Experimental Response> vrandmember(String key, int count); /** * VGETATTR Command Get the * attributes of an element in a vector set. *

* Time complexity: O(1) * @param key the name of the key that holds the vector set * @param element the name of the element whose attributes to retrieve * @return Response wrapping the attributes of the element as a JSON string, or null if the * element doesn't exist or has no attributes */ @Experimental Response vgetattr(String key, String element); /** * VSETATTR Command Set the * attributes of an element in a vector set. *

* Time complexity: O(1) * @param key the name of the key that holds the vector set * @param element the name of the element whose attributes to set * @param attributes the attributes to set as a JSON string * @return Response wrapping true if the attributes were set successfully */ @Experimental Response vsetattr(String key, String element, String attributes); /** * VINFO Command Get information * about a vector set. *

* Time complexity: O(1) * @param key the name of the key that holds the vector set * @return Response wrapping information about the vector set */ @Experimental Response vinfo(String key); } ================================================ FILE: src/main/java/redis/clients/jedis/commands/package-info.java ================================================ /** * This package contains the interfaces that contain methods representing Redis core commands. */ package redis.clients.jedis.commands; ================================================ FILE: src/main/java/redis/clients/jedis/csc/AbstractCache.java ================================================ package redis.clients.jedis.csc; import java.nio.ByteBuffer; import java.util.ArrayList; import java.util.Collection; import java.util.List; import java.util.Map; import java.util.Set; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.locks.ReentrantLock; import java.util.stream.Collectors; import redis.clients.jedis.annots.Experimental; import redis.clients.jedis.util.SafeEncoder; /** * The class to manage the client-side caching. User can provide an of implementation of this class * to the client object. */ @Experimental public abstract class AbstractCache implements Cache { private Cacheable cacheable; private final Map>> redisKeysToCacheKeys = new ConcurrentHashMap<>(); private final int maximumSize; private ReentrantLock lock = new ReentrantLock(); private volatile CacheStats stats = new CacheStats(); protected AbstractCache(int maximumSize) { this(maximumSize, DefaultCacheable.INSTANCE); } protected AbstractCache(int maximumSize, Cacheable cacheable) { this.maximumSize = maximumSize; this.cacheable = cacheable; } // Cache interface methods @Override public int getMaxSize() { return maximumSize; } @Override public abstract int getSize(); @Override public abstract Collection getCacheEntries(); @Override public CacheEntry get(CacheKey cacheKey) { CacheEntry entry = getFromStore(cacheKey); if (entry != null) { getEvictionPolicy().touch(cacheKey); } return entry; } @Override public CacheEntry set(CacheKey cacheKey, CacheEntry entry) { lock.lock(); try { entry = putIntoStore(cacheKey, entry); EvictionPolicy policy = getEvictionPolicy(); policy.touch(cacheKey); CacheKey evictedKey = policy.evictNext(); if (evictedKey != null) { delete(evictedKey); stats.evict(); } for (Object redisKey : cacheKey.getRedisKeys()) { ByteBuffer mapKey = makeKeyForRedisKeysToCacheKeys(redisKey); if (redisKeysToCacheKeys.containsKey(mapKey)) { redisKeysToCacheKeys.get(mapKey).add(cacheKey); } else { Set> set = ConcurrentHashMap.newKeySet(); set.add(cacheKey); redisKeysToCacheKeys.put(mapKey, set); } } stats.load(); return entry; } finally { lock.unlock(); } } @Override public boolean delete(CacheKey cacheKey) { lock.lock(); try { boolean removed = removeFromStore(cacheKey); getEvictionPolicy().reset(cacheKey); // removing it from redisKeysToCacheKeys as well // TODO: considering not doing it, what is the impact of not doing it ?? for (Object redisKey : cacheKey.getRedisKeys()) { ByteBuffer mapKey = makeKeyForRedisKeysToCacheKeys(redisKey); Set> cacheKeysRelatedtoRedisKey = redisKeysToCacheKeys.get(mapKey); if (cacheKeysRelatedtoRedisKey != null) { cacheKeysRelatedtoRedisKey.remove(cacheKey); } } return removed; } finally { lock.unlock(); } } @Override public List delete(List cacheKeys) { lock.lock(); try { return cacheKeys.stream().map(this::delete).collect(Collectors.toList()); } finally { lock.unlock(); } } @Override public List deleteByRedisKey(Object key) { lock.lock(); try { final ByteBuffer mapKey = makeKeyForRedisKeysToCacheKeys(key); Set> commands = redisKeysToCacheKeys.get(mapKey); List cacheKeys = new ArrayList<>(); if (commands != null) { cacheKeys.addAll(commands.stream().filter(this::removeFromStore).collect(Collectors.toList())); stats.invalidationByServer(cacheKeys.size()); redisKeysToCacheKeys.remove(mapKey); } stats.invalidationMessages(); return cacheKeys; } finally { lock.unlock(); } } @Override public List deleteByRedisKeys(List keys) { if (keys == null) { flush(); return null; } lock.lock(); try { return ((List) keys).stream() .map(this::deleteByRedisKey).flatMap(List::stream).collect(Collectors.toList()); } finally { lock.unlock(); } } @Override public int flush() { lock.lock(); try { int result = this.getSize(); clearStore(); redisKeysToCacheKeys.clear(); getEvictionPolicy().resetAll(); getStats().flush(); return result; } finally { lock.unlock(); } } @Override public boolean isCacheable(CacheKey cacheKey) { return cacheable.isCacheable(cacheKey.getRedisCommand(), cacheKey.getRedisKeys()); } @Override public boolean hasCacheKey(CacheKey cacheKey) { return containsKeyInStore(cacheKey); } @Override public abstract EvictionPolicy getEvictionPolicy(); @Override public CacheStats getStats() { return stats; } @Override public CacheStats getAndResetStats() { CacheStats result = stats; stats = new CacheStats(); return result; } @Override public boolean compatibilityMode() { return false; } // End of Cache interface methods // abstract methods to be implemented by the concrete classes protected abstract CacheEntry getFromStore(CacheKey cacheKey); protected abstract CacheEntry putIntoStore(CacheKey cacheKey, CacheEntry entry); protected abstract boolean removeFromStore(CacheKey cacheKey); // protected abstract Collection remove(Set> commands); protected abstract void clearStore(); protected abstract boolean containsKeyInStore(CacheKey cacheKey); // End of abstract methods to be implemented by the concrete classes /** * Normalizes Redis keys to ByteBuffer for use as map keys in {@link #redisKeysToCacheKeys}. *

* This method provides type safety by accepting only {@link String} and {@code byte[]} types, * which are the only types stored by {@link redis.clients.jedis.CommandArguments#getKeys()}. *

* Normalization strategy: *

    *
  • {@link String} keys are converted to {@code byte[]} using UTF-8 encoding via {@link SafeEncoder#encode(String)}
  • *
  • {@code byte[]} keys are used directly
  • *
  • Both are wrapped in {@link ByteBuffer} for content-based equality (similar to {@link redis.clients.jedis.util.JedisByteMap})
  • *
*

* Why ByteBuffer: {@link ByteBuffer} provides content-based {@code equals()} and {@code hashCode()} * for byte arrays, which is required for proper map key behavior. Plain {@code byte[]} uses identity-based * equality, which would break key lookups. *

* This normalization ensures that: *

    *
  • String key {@code "user:1"} and byte key {@code byte[]{0x75, 0x73, 0x65, 0x72, 0x3a, 0x31}} are treated as equal
  • *
  • Cache invalidation works correctly regardless of whether keys were added as String or byte[]
  • *
  • Type mismatches are caught early with clear error messages
  • *
* * @param key the Redis key (must be {@link String} or {@code byte[]}) * @return ByteBuffer wrapping the normalized byte representation * @throws IllegalArgumentException if key is not {@link String} or {@code byte[]} */ private ByteBuffer makeKeyForRedisKeysToCacheKeys(Object key) { if (key instanceof byte[]) { return makeKeyForRedisKeysToCacheKeys((byte[]) key); } else if (key instanceof String) { return makeKeyForRedisKeysToCacheKeys(SafeEncoder.encode((String) key)); } else { throw new IllegalArgumentException(key.getClass().getSimpleName() + " is not supported." + " Value: \"" + String.valueOf(key) + "\"."); } } /** * Wraps a byte array in a ByteBuffer for use as a map key. *

* ByteBuffer provides content-based equality, which is required for proper map key behavior * with byte arrays. * * @param b the byte array to wrap * @return ByteBuffer wrapping the byte array */ private static ByteBuffer makeKeyForRedisKeysToCacheKeys(byte[] b) { return ByteBuffer.wrap(b); } } ================================================ FILE: src/main/java/redis/clients/jedis/csc/Cache.java ================================================ package redis.clients.jedis.csc; import java.util.Collection; import java.util.List; /** * The cache that is used by a connection */ public interface Cache { /** * @return The size of the cache */ int getMaxSize(); /** * @return The current size of the cache */ int getSize(); /** * @return All the entries within the cache */ Collection getCacheEntries(); /** * Fetches a value from the cache * * @param cacheKey The key within the cache * @return The entry within the cache */ CacheEntry get(CacheKey cacheKey); /** * Puts a value into the cache * * @param cacheKey The key by which the value can be accessed within the cache * @param value The value to be put into the cache * @return The cache entry */ CacheEntry set(CacheKey cacheKey, CacheEntry value); /** * Delete an entry by cache key * @param cacheKey The cache key of the entry in the cache * @return True if the entry could be deleted, false if the entry wasn't found. */ boolean delete(CacheKey cacheKey); /** * Delete entries by cache key from the cache * * @param cacheKeys The cache keys of the entries that should be deleted * @return True for every entry that could be deleted. False if the entry was not there. */ List delete(List cacheKeys); /** * Delete an entry by the Redis key from the cache * * @param key The Redis key as binary * @return True if the entry could be deleted. False if the entry was not there. */ List deleteByRedisKey(Object key); /** * Delete entries by the Redis key from the cache * * @param keys The Redis keys as binaries * @return True for every entry that could be deleted. False if the entry was not there. */ List deleteByRedisKeys(List keys); /** * Flushes the entire cache * * @return Return the number of entries that were flushed */ int flush(); /** * @param cacheKey The key of the cache entry * @return True if the entry is cachable, false otherwise */ boolean isCacheable(CacheKey cacheKey); /** * * @param cacheKey The key of the cache entry * @return True if the cache already contains the key */ boolean hasCacheKey(CacheKey cacheKey); /** * @return The eviction policy that is used by the cache */ EvictionPolicy getEvictionPolicy(); /** * @return The statistics of the cache */ CacheStats getStats(); /** * @return The statistics of the cache */ CacheStats getAndResetStats(); /** * @return The compatibility of cache against different Redis versions */ boolean compatibilityMode(); } ================================================ FILE: src/main/java/redis/clients/jedis/csc/CacheConfig.java ================================================ package redis.clients.jedis.csc; public class CacheConfig { private int maxSize; private Cacheable cacheable; private EvictionPolicy evictionPolicy; private Class cacheClass; public int getMaxSize() { return maxSize; } public Cacheable getCacheable() { return cacheable; } public EvictionPolicy getEvictionPolicy() { return evictionPolicy; } public Class getCacheClass() { return cacheClass; } public static Builder builder() { return new Builder(); } public static class Builder { private final int DEFAULT_MAX_SIZE = 10000; private int maxSize = DEFAULT_MAX_SIZE; private Cacheable cacheable = DefaultCacheable.INSTANCE; private EvictionPolicy evictionPolicy; private Class cacheClass; public Builder maxSize(int maxSize) { this.maxSize = maxSize; return this; } public Builder evictionPolicy(EvictionPolicy policy) { this.evictionPolicy = policy; return this; } public Builder cacheable(Cacheable cacheable) { this.cacheable = cacheable; return this; } public Builder cacheClass(Class cacheClass) { this.cacheClass = cacheClass; return this; } public CacheConfig build() { CacheConfig cacheConfig = new CacheConfig(); cacheConfig.maxSize = this.maxSize; cacheConfig.cacheable = this.cacheable; cacheConfig.evictionPolicy = this.evictionPolicy; cacheConfig.cacheClass = this.cacheClass; return cacheConfig; } } } ================================================ FILE: src/main/java/redis/clients/jedis/csc/CacheConnection.java ================================================ package redis.clients.jedis.csc; import java.util.Objects; import java.util.concurrent.locks.ReentrantLock; import redis.clients.jedis.CommandObject; import redis.clients.jedis.Connection; import redis.clients.jedis.JedisClientConfig; import redis.clients.jedis.JedisSocketFactory; import redis.clients.jedis.Protocol; import redis.clients.jedis.RedisProtocol; import redis.clients.jedis.annots.VisibleForTesting; import redis.clients.jedis.exceptions.JedisException; import redis.clients.jedis.util.RedisInputStream; public class CacheConnection extends Connection { public static class Builder extends Connection.Builder { private Cache cache; private Builder(Cache cache) { if (cache == null) { throw new IllegalArgumentException("Cache cannot be null!"); } this.cache = cache; } public Cache getCache() { return cache; } @Override public Builder socketFactory(JedisSocketFactory socketFactory) { super.socketFactory(socketFactory); return this; } @Override public Builder clientConfig(JedisClientConfig clientConfig) { super.clientConfig(clientConfig); return this; } @Override public Connection build() { CacheConnection conn = new CacheConnection(this); conn.initializeFromClientConfig(); return conn; } } public static Builder builder(Cache cache) { return new Builder(cache); } @VisibleForTesting public static Builder builder() { throw new UnsupportedOperationException("Cache is required to build CacheConnection."); } private final Cache cache; private ReentrantLock lock; private static final String REDIS = "redis"; private static final String MIN_REDIS_VERSION = "7.4"; public CacheConnection(final JedisSocketFactory socketFactory, JedisClientConfig clientConfig, Cache cache) { super(socketFactory, clientConfig); this.cache = Objects.requireNonNull(cache); initializeClientSideCache(); } private CacheConnection(Builder builder) { super(builder); this.cache = builder.getCache(); } @Override protected void initializeFromClientConfig(JedisClientConfig config) { lock = new ReentrantLock(); super.initializeFromClientConfig(config); // this is required for the case ctor(builder). // will also be called for the case ctor(socketFactory, clientConfig, cache) but will return if (cache == null) return; initializeClientSideCache(); } @Override protected Object protocolRead(RedisInputStream inputStream) { lock.lock(); try { return Protocol.read(inputStream, cache); } finally { lock.unlock(); } } @Override protected void protocolReadPushes(RedisInputStream inputStream) { if (lock.tryLock()) { try { Protocol.readPushes(inputStream, cache, true); } finally { lock.unlock(); } } } @Override public void disconnect() { super.disconnect(); cache.flush(); } @Override public T executeCommand(final CommandObject commandObject) { final CacheKey cacheKey = new CacheKey(commandObject); if (!cache.isCacheable(cacheKey)) { cache.getStats().nonCacheable(); return super.executeCommand(commandObject); } CacheEntry cacheEntry = cache.get(cacheKey); if (cacheEntry != null) { // (probable) CACHE HIT !! cacheEntry = validateEntry(cacheEntry); if (cacheEntry != null) { // CACHE HIT confirmed !!! cache.getStats().hit(); return cacheEntry.getValue(); } } // CACHE MISS !! cache.getStats().miss(); T value = super.executeCommand(commandObject); cacheEntry = new CacheEntry<>(cacheKey, value, this); cache.set(cacheKey, cacheEntry); // this line actually provides a deep copy of cached object instance value = cacheEntry.getValue(); return value; } public Cache getCache() { return cache; } private void initializeClientSideCache() { if (protocol != RedisProtocol.RESP3) { throw new JedisException("Client side caching is only supported with RESP3."); } Objects.requireNonNull(cache); if (!cache.compatibilityMode()) { RedisVersion current = new RedisVersion(version); RedisVersion required = new RedisVersion(MIN_REDIS_VERSION); if (!REDIS.equals(server) || current.compareTo(required) < 0) { throw new JedisException( String.format("Client side caching is only supported with 'Redis %s' or later.", MIN_REDIS_VERSION)); } } sendCommand(Protocol.Command.CLIENT, "TRACKING", "ON"); String reply = getStatusCodeReply(); if (!"OK".equals(reply)) { throw new JedisException("Could not enable client tracking. Reply: " + reply); } } private CacheEntry validateEntry(CacheEntry cacheEntry) { CacheConnection cacheOwner = cacheEntry.getConnection(); if (cacheOwner == null || cacheOwner.isBroken() || !cacheOwner.isConnected()) { cache.delete(cacheEntry.getCacheKey()); return null; } else { try { cacheOwner.readPushesWithCheckingBroken(); } catch (JedisException e) { cache.delete(cacheEntry.getCacheKey()); return null; } return cache.get(cacheEntry.getCacheKey()); } } } ================================================ FILE: src/main/java/redis/clients/jedis/csc/CacheEntry.java ================================================ package redis.clients.jedis.csc; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.IOException; import java.io.ObjectInputStream; import java.io.ObjectOutputStream; import java.lang.ref.WeakReference; import redis.clients.jedis.exceptions.JedisCacheException; public class CacheEntry { private final CacheKey cacheKey; private final WeakReference connection; private final byte[] bytes; public CacheEntry(CacheKey cacheKey, T value, CacheConnection connection) { this.cacheKey = cacheKey; this.connection = new WeakReference<>(connection); this.bytes = toBytes(value); } public CacheKey getCacheKey() { return cacheKey; } public T getValue() { return toObject(bytes); } public CacheConnection getConnection() { return connection.get(); } private static byte[] toBytes(Object object) { try (ByteArrayOutputStream baos = new ByteArrayOutputStream(); ObjectOutputStream oos = new ObjectOutputStream(baos)) { oos.writeObject(object); oos.flush(); oos.close(); return baos.toByteArray(); } catch (IOException e) { throw new JedisCacheException("Failed to serialize object", e); } } private T toObject(byte[] data) { try (ByteArrayInputStream bais = new ByteArrayInputStream(data); ObjectInputStream ois = new ObjectInputStream(bais)) { return (T) ois.readObject(); } catch (IOException | ClassNotFoundException e) { throw new JedisCacheException("Failed to deserialize object", e); } } } ================================================ FILE: src/main/java/redis/clients/jedis/csc/CacheFactory.java ================================================ package redis.clients.jedis.csc; import java.lang.reflect.Constructor; import java.lang.reflect.InvocationTargetException; import java.util.Arrays; import redis.clients.jedis.exceptions.JedisCacheException; public final class CacheFactory { public static Cache getCache(CacheConfig config) { if (config.getCacheClass() == null) { if (config.getCacheable() == null) { throw new JedisCacheException("Cacheable is required to create the default cache!"); } return new DefaultCache(config.getMaxSize(), config.getCacheable(), getEvictionPolicy(config)); } return instantiateCustomCache(config); } private static Cache instantiateCustomCache(CacheConfig config) { try { if (config.getCacheable() != null) { Constructor ctorWithCacheable = findConstructorWithCacheable(config.getCacheClass()); if (ctorWithCacheable != null) { return (Cache) ctorWithCacheable.newInstance(config.getMaxSize(), getEvictionPolicy(config), config.getCacheable()); } } Constructor ctor = getConstructor(config.getCacheClass()); return (Cache) ctor.newInstance(config.getMaxSize(), getEvictionPolicy(config)); } catch (InstantiationException | IllegalAccessException | IllegalArgumentException | InvocationTargetException | SecurityException e) { throw new JedisCacheException("Failed to insantiate custom cache type!", e); } } private static Constructor findConstructorWithCacheable(Class customCacheType) { return Arrays.stream(customCacheType.getConstructors()) .filter(ctor -> Arrays.equals(ctor.getParameterTypes(), new Class[] { int.class, EvictionPolicy.class, Cacheable.class })) .findFirst().orElse(null); } private static Constructor getConstructor(Class customCacheType) { try { return customCacheType.getConstructor(int.class, EvictionPolicy.class); } catch (NoSuchMethodException e) { String className = customCacheType.getName(); throw new JedisCacheException(String.format( "Failed to find compatible constructor for custom cache type! Provide one of these;" // give hints about the compatible constructors + "\n - %s(int maxSize, EvictionPolicy evictionPolicy)\n - %s(int maxSize, EvictionPolicy evictionPolicy, Cacheable cacheable)", className, className), e); } } private static EvictionPolicy getEvictionPolicy(CacheConfig config) { if (config.getEvictionPolicy() == null) { // It will be default to LRUEviction, until we have other eviction implementations return new LRUEviction(config.getMaxSize()); } return config.getEvictionPolicy(); } } ================================================ FILE: src/main/java/redis/clients/jedis/csc/CacheKey.java ================================================ package redis.clients.jedis.csc; import java.util.List; import java.util.Objects; import redis.clients.jedis.CommandObject; import redis.clients.jedis.commands.ProtocolCommand; public class CacheKey { private final CommandObject command; public CacheKey(CommandObject command) { this.command = Objects.requireNonNull(command); } @Override public int hashCode() { return command.hashCode(); } @Override public boolean equals(Object obj) { if (this == obj) return true; if (obj == null || getClass() != obj.getClass()) return false; final CacheKey other = (CacheKey) obj; return Objects.equals(this.command, other.command); } public List getRedisKeys() { return command.getArguments().getKeys(); } public ProtocolCommand getRedisCommand() { return command.getArguments().getCommand(); } } ================================================ FILE: src/main/java/redis/clients/jedis/csc/CacheStats.java ================================================ package redis.clients.jedis.csc; import java.util.concurrent.atomic.AtomicLong; public class CacheStats { private AtomicLong hits = new AtomicLong(0); private AtomicLong misses = new AtomicLong(0); private AtomicLong loads = new AtomicLong(0); private AtomicLong evicts = new AtomicLong(0); private AtomicLong nonCacheable = new AtomicLong(0); private AtomicLong flush = new AtomicLong(0); private AtomicLong invalidationsByServer = new AtomicLong(0); private AtomicLong invalidationMessages = new AtomicLong(0); protected void hit() { hits.incrementAndGet(); } protected void miss() { misses.incrementAndGet(); } protected void load() { loads.incrementAndGet(); } protected void evict() { evicts.incrementAndGet(); } protected void nonCacheable() { nonCacheable.incrementAndGet(); } protected void flush() { flush.incrementAndGet(); } protected void invalidationByServer(int size) { invalidationsByServer.addAndGet(size); } protected void invalidationMessages() { invalidationMessages.incrementAndGet(); } public long getHitCount() { return hits.get(); } public long getMissCount() { return misses.get(); } public long getLoadCount() { return loads.get(); } public long getEvictCount() { return evicts.get(); } public long getNonCacheableCount() { return nonCacheable.get(); } public long getFlushCount() { return flush.get(); } public long getInvalidationCount() { return invalidationsByServer.get(); } public String toString() { return "CacheStats{" + "hits=" + hits + ", misses=" + misses + ", loads=" + loads + ", evicts=" + evicts + ", nonCacheable=" + nonCacheable + ", flush=" + flush + ", invalidationsByServer=" + invalidationsByServer + ", invalidationMessages=" + invalidationMessages + '}'; } } ================================================ FILE: src/main/java/redis/clients/jedis/csc/Cacheable.java ================================================ package redis.clients.jedis.csc; import java.util.List; import redis.clients.jedis.commands.ProtocolCommand; public interface Cacheable { boolean isCacheable(ProtocolCommand command, List keys); } ================================================ FILE: src/main/java/redis/clients/jedis/csc/DefaultCache.java ================================================ package redis.clients.jedis.csc; import java.util.Collection; import java.util.HashMap; import java.util.Map; public class DefaultCache extends AbstractCache { protected final Map cache; private final EvictionPolicy evictionPolicy; protected DefaultCache(int maximumSize) { this(maximumSize, new HashMap()); } protected DefaultCache(int maximumSize, Map map) { this(maximumSize, map, DefaultCacheable.INSTANCE, new LRUEviction(maximumSize)); } protected DefaultCache(int maximumSize, Cacheable cacheable) { this(maximumSize, new HashMap(), cacheable, new LRUEviction(maximumSize)); } protected DefaultCache(int maximumSize, Cacheable cacheable, EvictionPolicy evictionPolicy) { this(maximumSize, new HashMap(), cacheable, evictionPolicy); } protected DefaultCache(int maximumSize, Map map, Cacheable cacheable, EvictionPolicy evictionPolicy) { super(maximumSize, cacheable); this.cache = map; this.evictionPolicy = evictionPolicy; this.evictionPolicy.setCache(this); } @Override public int getSize() { return cache.size(); } @Override public Collection getCacheEntries() { return cache.values(); } @Override public EvictionPolicy getEvictionPolicy() { return this.evictionPolicy; } @Override public CacheEntry getFromStore(CacheKey key) { return cache.get(key); } @Override public CacheEntry putIntoStore(CacheKey key, CacheEntry entry) { return cache.put(key, entry); } @Override public boolean removeFromStore(CacheKey key) { return cache.remove(key) != null; } @Override protected final void clearStore() { cache.clear(); } @Override protected boolean containsKeyInStore(CacheKey cacheKey) { return cache.containsKey(cacheKey); } } ================================================ FILE: src/main/java/redis/clients/jedis/csc/DefaultCacheable.java ================================================ package redis.clients.jedis.csc; import java.util.HashSet; import java.util.List; import java.util.Set; import redis.clients.jedis.Protocol.Command; import redis.clients.jedis.commands.ProtocolCommand; import redis.clients.jedis.json.JsonProtocol.JsonCommand; import redis.clients.jedis.timeseries.TimeSeriesProtocol.TimeSeriesCommand; public class DefaultCacheable implements Cacheable { public static final DefaultCacheable INSTANCE = new DefaultCacheable(); private static final Set DEFAULT_CACHEABLE_COMMANDS = new HashSet() { { add(Command.BITCOUNT); add(Command.BITFIELD_RO); add(Command.BITPOS); add(Command.EXISTS); add(Command.GEODIST); add(Command.GEOHASH); add(Command.GEOPOS); add(Command.GEORADIUSBYMEMBER_RO); add(Command.GEORADIUS_RO); add(Command.GEOSEARCH); add(Command.GET); add(Command.GETBIT); add(Command.GETRANGE); add(Command.HEXISTS); add(Command.HGET); add(Command.HGETALL); add(Command.HKEYS); add(Command.HLEN); add(Command.HMGET); add(Command.HSTRLEN); add(Command.HVALS); add(JsonCommand.ARRINDEX); add(JsonCommand.ARRLEN); add(JsonCommand.GET); add(JsonCommand.MGET); add(JsonCommand.OBJKEYS); add(JsonCommand.OBJLEN); add(JsonCommand.STRLEN); add(JsonCommand.TYPE); add(Command.LCS); add(Command.LINDEX); add(Command.LLEN); add(Command.LPOS); add(Command.LRANGE); add(Command.MGET); add(Command.SCARD); add(Command.SDIFF); add(Command.SINTER); add(Command.SISMEMBER); add(Command.SMEMBERS); add(Command.SMISMEMBER); add(Command.STRLEN); add(Command.SUBSTR); add(Command.SUNION); add(TimeSeriesCommand.GET); add(TimeSeriesCommand.INFO); add(TimeSeriesCommand.RANGE); add(TimeSeriesCommand.REVRANGE); add(Command.TYPE); add(Command.XLEN); add(Command.XPENDING); add(Command.XRANGE); add(Command.XREVRANGE); add(Command.ZCARD); add(Command.ZCOUNT); add(Command.ZLEXCOUNT); add(Command.ZMSCORE); add(Command.ZRANGE); add(Command.ZRANGEBYLEX); add(Command.ZRANGEBYSCORE); add(Command.ZRANK); add(Command.ZREVRANGE); add(Command.ZREVRANGEBYLEX); add(Command.ZREVRANGEBYSCORE); add(Command.ZREVRANK); add(Command.ZSCORE); } }; public DefaultCacheable() { } public static boolean isDefaultCacheableCommand(ProtocolCommand command) { return DEFAULT_CACHEABLE_COMMANDS.contains(command); } @Override public boolean isCacheable(ProtocolCommand command, List keys) { return isDefaultCacheableCommand(command); } } ================================================ FILE: src/main/java/redis/clients/jedis/csc/EvictionPolicy.java ================================================ package redis.clients.jedis.csc; import java.util.List; /** * Describes the properties and functionality of an eviction policy *

* One policy instance belongs to exactly one cache instance */ public interface EvictionPolicy { /** * Types of eviction policies * * AGE - based on the time of access, e.g., LRU * FREQ - based on the frequency of access, e.g., LFU * HYBR - AGE + FREQ, e.g., CLOCK * MISC - Anythin that isn't time based, frequency based or a combination of the two, e.g., FIFO */ enum EvictionType { AGE, FREQ, HYBR, MISC } /** * @return The cache that is associated to this policy instance */ Cache getCache(); /** * Sets the cache that is associated to this policy instance * @param cache The cache instance */ void setCache(Cache cache); /** * @return The type of policy */ EvictionType getType(); /** * @return The name of the policy */ String getName(); /** * Evict the next element from the cache * This one should provide O(1) complexity * @return The key of the entry that was evicted */ CacheKey evictNext(); /** * * @param n The number of entries to evict * @return The list of keys of evicted entries */ List evictMany(int n); /** * Indicates that a cache key was touched * This one should provide O(1) complexity * @param cacheKey The key within the cache */ void touch(CacheKey cacheKey); /** * Resets the state that the eviction policy maintains about the cache key * @param cacheKey */ boolean reset(CacheKey cacheKey); /** * Resets the entire state of the eviction data * @return True if the reset could be performed successfully */ int resetAll(); } ================================================ FILE: src/main/java/redis/clients/jedis/csc/LRUEviction.java ================================================ package redis.clients.jedis.csc; import java.util.*; import java.util.concurrent.ConcurrentLinkedQueue; /** * Simple L(east) R(ecently) U(sed) eviction policy * ATTENTION: this class is not thread safe */ public class LRUEviction implements EvictionPolicy { // For future reference, in case there is a need to make it thread safe, // the LinkedHashMap can be wrapped in a Collections.synchronizedMap /** * The cache that is associated to that policy instance */ protected Cache cache; protected LinkedHashMap accessTimes; protected ArrayDeque pendingEvictions = new ArrayDeque(); protected ConcurrentLinkedQueue msg = new ConcurrentLinkedQueue(); private int initialCapacity; /** * Constructor that gets the cache passed * * @param initialCapacity */ public LRUEviction(int initialCapacity) { this.initialCapacity = initialCapacity; } @Override public void setCache(Cache cache) { this.cache = cache; this.accessTimes = new LinkedHashMap(initialCapacity, 1f, true) { @Override protected boolean removeEldestEntry(Map.Entry eldest) { boolean evictionRequired = cache.getSize() > cache.getMaxSize() || accessTimes.size() > cache.getMaxSize(); // here the cache check is only for performance gain; we are trying to avoid the sequence add + poll + hasCacheKey // and prefer to check it in cache once in early stage. // if there is nothing to remove in actual cache as of now, stop worrying about it. if (evictionRequired && cache.hasCacheKey(eldest.getKey())) { pendingEvictions.addLast(eldest.getKey()); } return evictionRequired; } }; } @Override public Cache getCache() { return this.cache; } @Override public EvictionType getType() { return EvictionType.AGE; } @Override public String getName() { return "Simple L(east) R(ecently) U(sed)"; } @Override public synchronized CacheKey evictNext() { CacheKey cacheKey = pendingEvictions.pollFirst(); while (cacheKey != null && !cache.hasCacheKey(cacheKey)) { cacheKey = pendingEvictions.pollFirst(); } return cacheKey; } @Override public synchronized List evictMany(int n) { List result = new ArrayList<>(); for (int i = 0; i < n; i++) { result.add(this.evictNext()); } return result; } @Override public synchronized void touch(CacheKey cacheKey) { this.accessTimes.put(cacheKey, new Date().getTime()); } @Override public synchronized boolean reset(CacheKey cacheKey) { return this.accessTimes.remove(cacheKey) != null; } @Override public synchronized int resetAll() { int result = this.accessTimes.size(); accessTimes.clear(); return result; } } ================================================ FILE: src/main/java/redis/clients/jedis/csc/RedisVersion.java ================================================ package redis.clients.jedis.csc; import java.util.Arrays; class RedisVersion implements Comparable { private String version; private Integer[] numbers; public RedisVersion(String version) { if (version == null) throw new IllegalArgumentException("Version can not be null"); this.version = version; this.numbers = Arrays.stream(version.split("\\.")).map(n -> Integer.parseInt(n)).toArray(Integer[]::new); } @Override public int compareTo(RedisVersion other) { int max = Math.max(this.numbers.length, other.numbers.length); for (int i = 0; i < max; i++) { int thisNumber = this.numbers.length > i ? this.numbers[i]:0; int otherNumber = other.numbers.length > i ? other.numbers[i]:0; if (thisNumber < otherNumber) return -1; if (thisNumber > otherNumber) return 1; } return 0; } @Override public String toString() { return this.version; } @Override public boolean equals(Object that) { if (this == that) return true; if (that == null) return false; if (this.getClass() != that.getClass()) return false; return this.compareTo((RedisVersion) that) == 0; } } ================================================ FILE: src/main/java/redis/clients/jedis/csc/package-info.java ================================================ /** * This package contains the classes and interfaces related to Server-assisted Client-side Caching. */ @Experimental package redis.clients.jedis.csc; import redis.clients.jedis.annots.Experimental; ================================================ FILE: src/main/java/redis/clients/jedis/csc/util/AllowAndDenyListWithStringKeys.java ================================================ package redis.clients.jedis.csc.util; import java.util.List; import java.util.Set; import redis.clients.jedis.commands.ProtocolCommand; import redis.clients.jedis.csc.DefaultCacheable; import redis.clients.jedis.csc.Cacheable; public class AllowAndDenyListWithStringKeys implements Cacheable { private final Set allowCommands; private final Set denyCommands; private final Set allowKeys; private final Set denyKeys; public AllowAndDenyListWithStringKeys(Set allowCommands, Set denyCommands, Set allowKeys, Set denyKeys) { this.allowCommands = allowCommands; this.denyCommands = denyCommands; this.allowKeys = allowKeys; this.denyKeys = denyKeys; } @Override public boolean isCacheable(ProtocolCommand command, List keys) { if (allowCommands != null && !allowCommands.contains(command)) { return false; } if (denyCommands != null && denyCommands.contains(command)) { return false; } for (Object key : keys) { if (!(key instanceof String)) { return false; } if (allowKeys != null && !allowKeys.contains((String) key)) { return false; } if (denyKeys != null && denyKeys.contains((String) key)) { return false; } } return DefaultCacheable.isDefaultCacheableCommand(command); } } ================================================ FILE: src/main/java/redis/clients/jedis/csc/util/package-info.java ================================================ /** * This package contains the helper classes related to Server-assisted Client-side Caching. */ @Experimental package redis.clients.jedis.csc.util; import redis.clients.jedis.annots.Experimental; ================================================ FILE: src/main/java/redis/clients/jedis/exceptions/ClusterAggregationException.java ================================================ package redis.clients.jedis.exceptions; /** * Exception thrown when cluster reply aggregation fails. *

* This exception is thrown when aggregating replies from multiple cluster nodes and the aggregation * policy requirements are not met (e.g., ALL_SUCCEEDED policy requires all replies to be equal, but * different values were received). *

*/ public class ClusterAggregationException extends JedisClusterOperationException { private static final long serialVersionUID = 1L; public ClusterAggregationException(String message) { super(message); } public ClusterAggregationException(Throwable cause) { super(cause); } public ClusterAggregationException(String message, Throwable cause) { super(message, cause); } } ================================================ FILE: src/main/java/redis/clients/jedis/exceptions/InvalidURIException.java ================================================ package redis.clients.jedis.exceptions; public class InvalidURIException extends JedisException { private static final long serialVersionUID = -781691993326357802L; public InvalidURIException(String message) { super(message); } public InvalidURIException(Throwable cause) { super(cause); } public InvalidURIException(String message, Throwable cause) { super(message, cause); } } ================================================ FILE: src/main/java/redis/clients/jedis/exceptions/JedisAccessControlException.java ================================================ package redis.clients.jedis.exceptions; /** * An access control error reply from Redis; i.e. {@code -WRONGPASS}, {@code -NOPERM}. */ public class JedisAccessControlException extends JedisDataException { public JedisAccessControlException(String message) { super(message); } public JedisAccessControlException(Throwable cause) { super(cause); } public JedisAccessControlException(String message, Throwable cause) { super(message, cause); } } ================================================ FILE: src/main/java/redis/clients/jedis/exceptions/JedisAskDataException.java ================================================ package redis.clients.jedis.exceptions; import redis.clients.jedis.HostAndPort; /** * {@code -ASK} reply from Redis. */ public class JedisAskDataException extends JedisRedirectionException { private static final long serialVersionUID = 3878126572474819403L; public JedisAskDataException(Throwable cause, HostAndPort targetHost, int slot) { super(cause, targetHost, slot); } public JedisAskDataException(String message, Throwable cause, HostAndPort targetHost, int slot) { super(message, cause, targetHost, slot); } public JedisAskDataException(String message, HostAndPort targetHost, int slot) { super(message, targetHost, slot); } } ================================================ FILE: src/main/java/redis/clients/jedis/exceptions/JedisBroadcastException.java ================================================ package redis.clients.jedis.exceptions; import java.util.Collections; import java.util.HashMap; import java.util.Map; import redis.clients.jedis.HostAndPort; /** * Exception thrown when a broadcast command fails on one or more cluster nodes. *

* This exception collects replies from all nodes, including both successful responses and errors. * Use {@link #getReplies()} to inspect the per-node results. *

* Note: This exception extends {@link JedisDataException} just so existing applications catching * JedisDataException do not get broken. */ // TODO: extends JedisException public class JedisBroadcastException extends JedisDataException { private static final String BROADCAST_ERROR_MESSAGE = "A failure occurred while broadcasting the command."; private final Map replies = new HashMap<>(); public JedisBroadcastException() { super(BROADCAST_ERROR_MESSAGE); } public void addReply(HostAndPort node, Object reply) { replies.put(node, reply); } public Map getReplies() { return Collections.unmodifiableMap(replies); } /** * Prepares the exception for throwing by: *

    *
  • Refreshing the stack trace to show where it's thrown from (not where it was created)
  • *
  • Setting the cause to the first error encountered for better debugging
  • *
* @return this exception, ready to be thrown */ public JedisBroadcastException prepareToThrow() { // Refresh stack trace to show throw location instead of creation location fillInStackTrace(); // Set the first exception as the cause for better debugging if (getCause() == null) { for (Object reply : replies.values()) { if (reply instanceof Throwable) { initCause((Throwable) reply); break; } } } return this; } @Override public String getMessage() { StringBuilder sb = new StringBuilder(BROADCAST_ERROR_MESSAGE); int errorCount = 0; int successCount = 0; String firstErrorMessage = null; for (Object reply : replies.values()) { if (reply instanceof Throwable) { errorCount++; if (firstErrorMessage == null) { firstErrorMessage = ((Throwable) reply).getMessage(); } } else { successCount++; } } sb.append(" (").append(successCount).append(" succeeded, ") .append(errorCount).append(" failed"); if (firstErrorMessage != null) { sb.append("; first error: ").append(firstErrorMessage); } sb.append(")"); return sb.toString(); } } ================================================ FILE: src/main/java/redis/clients/jedis/exceptions/JedisBusyException.java ================================================ package redis.clients.jedis.exceptions; /** * {@code -BUSY} reply from Redis. */ public class JedisBusyException extends JedisDataException { private static final long serialVersionUID = 3992655220229243478L; public JedisBusyException(final String message) { super(message); } public JedisBusyException(final Throwable cause) { super(cause); } public JedisBusyException(final String message, final Throwable cause) { super(message, cause); } } ================================================ FILE: src/main/java/redis/clients/jedis/exceptions/JedisCacheException.java ================================================ package redis.clients.jedis.exceptions; public class JedisCacheException extends JedisException { private static final long serialVersionUID = 3878126572474819403L; public JedisCacheException(String message) { super(message); } public JedisCacheException(Throwable cause) { super(cause); } public JedisCacheException(String message, Throwable cause) { super(message, cause); } } ================================================ FILE: src/main/java/redis/clients/jedis/exceptions/JedisClusterException.java ================================================ package redis.clients.jedis.exceptions; /** * Any {@code -CLUSTER...} reply from Redis. */ public class JedisClusterException extends JedisDataException { private static final long serialVersionUID = 3878126572474819403L; public JedisClusterException(Throwable cause) { super(cause); } public JedisClusterException(String message, Throwable cause) { super(message, cause); } public JedisClusterException(String message) { super(message); } } ================================================ FILE: src/main/java/redis/clients/jedis/exceptions/JedisClusterOperationException.java ================================================ package redis.clients.jedis.exceptions; import redis.clients.jedis.HostAndPort; /** * Error while processing cluster operations. This is not an error reply from Redis. *

* This exception can optionally include the {@link HostAndPort} of the node that caused the * failure, which is useful for debugging and error reporting in multi-node operations. */ public class JedisClusterOperationException extends JedisException { private static final long serialVersionUID = 8124535086306604887L; private final HostAndPort node; public JedisClusterOperationException(String message) { super(message); this.node = null; } public JedisClusterOperationException(Throwable cause) { super(cause); this.node = null; } public JedisClusterOperationException(String message, Throwable cause) { super(message, cause); this.node = null; } /** * Creates an exception with the node that caused the failure. * * @param message the detail message * @param node the node that caused the failure (may be null) */ public JedisClusterOperationException(String message, HostAndPort node) { super(message); this.node = node; } /** * Creates an exception with the node that caused the failure. * * @param message the detail message * @param cause the underlying cause * @param node the node that caused the failure (may be null) */ public JedisClusterOperationException(String message, Throwable cause, HostAndPort node) { super(message, cause); this.node = node; } /** * Returns the node that caused this exception, if known. * * @return the node that caused the failure, or null if unknown */ public HostAndPort getNode() { return node; } } ================================================ FILE: src/main/java/redis/clients/jedis/exceptions/JedisConnectionException.java ================================================ package redis.clients.jedis.exceptions; /** * A connection error. */ public class JedisConnectionException extends JedisException { private static final long serialVersionUID = 3878126572474819403L; public JedisConnectionException(String message) { super(message); } public JedisConnectionException(Throwable cause) { super(cause); } public JedisConnectionException(String message, Throwable cause) { super(message, cause); } } ================================================ FILE: src/main/java/redis/clients/jedis/exceptions/JedisDataException.java ================================================ package redis.clients.jedis.exceptions; /** * Any error reply from Redis. */ public class JedisDataException extends JedisException { private static final long serialVersionUID = 3878126572474819403L; public JedisDataException(String message) { super(message); } public JedisDataException(Throwable cause) { super(cause); } public JedisDataException(String message, Throwable cause) { super(message, cause); } } ================================================ FILE: src/main/java/redis/clients/jedis/exceptions/JedisException.java ================================================ package redis.clients.jedis.exceptions; /** * Umbrella exception class for all exceptions in Jedis library. */ public class JedisException extends RuntimeException { private static final long serialVersionUID = -2946266495682282677L; public JedisException(String message) { super(message); } public JedisException(Throwable e) { super(e); } public JedisException(String message, Throwable cause) { super(message, cause); } } ================================================ FILE: src/main/java/redis/clients/jedis/exceptions/JedisMovedDataException.java ================================================ package redis.clients.jedis.exceptions; import redis.clients.jedis.HostAndPort; /** * {@code -MOVED} reply from Redis. */ public class JedisMovedDataException extends JedisRedirectionException { private static final long serialVersionUID = 3878126572474819403L; public JedisMovedDataException(String message, HostAndPort targetNode, int slot) { super(message, targetNode, slot); } public JedisMovedDataException(Throwable cause, HostAndPort targetNode, int slot) { super(cause, targetNode, slot); } public JedisMovedDataException(String message, Throwable cause, HostAndPort targetNode, int slot) { super(message, cause, targetNode, slot); } } ================================================ FILE: src/main/java/redis/clients/jedis/exceptions/JedisNoScriptException.java ================================================ package redis.clients.jedis.exceptions; /** * {@code -NOSCRIPT} reply from Redis. */ public class JedisNoScriptException extends JedisDataException { private static final long serialVersionUID = 4674378093072060731L; public JedisNoScriptException(final String message) { super(message); } public JedisNoScriptException(final Throwable cause) { super(cause); } public JedisNoScriptException(final String message, final Throwable cause) { super(message, cause); } } ================================================ FILE: src/main/java/redis/clients/jedis/exceptions/JedisRedirectionException.java ================================================ package redis.clients.jedis.exceptions; import redis.clients.jedis.HostAndPort; /** * Umbrella exception class representing all redirection replies from Redis. * * @see JedisAskDataException * @see JedisMovedDataException */ public class JedisRedirectionException extends JedisDataException { private static final long serialVersionUID = 3878126572474819403L; private final HostAndPort targetNode; private final int slot; public JedisRedirectionException(String message, HostAndPort targetNode, int slot) { super(message); this.targetNode = targetNode; this.slot = slot; } public JedisRedirectionException(Throwable cause, HostAndPort targetNode, int slot) { super(cause); this.targetNode = targetNode; this.slot = slot; } public JedisRedirectionException(String message, Throwable cause, HostAndPort targetNode, int slot) { super(message, cause); this.targetNode = targetNode; this.slot = slot; } public final HostAndPort getTargetNode() { return targetNode; } public final int getSlot() { return slot; } } ================================================ FILE: src/main/java/redis/clients/jedis/exceptions/JedisValidationException.java ================================================ package redis.clients.jedis.exceptions; /** * A validation error. */ public class JedisValidationException extends JedisException { private static final long serialVersionUID = 1134169242443303479L; public JedisValidationException(String message) { super(message); } public JedisValidationException(Throwable cause) { super(cause); } public JedisValidationException(String message, Throwable cause) { super(message, cause); } } ================================================ FILE: src/main/java/redis/clients/jedis/exceptions/UnsupportedAggregationException.java ================================================ package redis.clients.jedis.exceptions; /** * Exception thrown when an unsupported aggregation operation is attempted. *

* This exception is thrown when the aggregation policy cannot handle the given data types. For * example, when trying to sum non-numeric values or compare non-comparable types. *

*/ public class UnsupportedAggregationException extends ClusterAggregationException { private static final long serialVersionUID = 1L; public UnsupportedAggregationException(String message) { super(message); } public UnsupportedAggregationException(Throwable cause) { super(cause); } public UnsupportedAggregationException(String message, Throwable cause) { super(message, cause); } } ================================================ FILE: src/main/java/redis/clients/jedis/exceptions/package-info.java ================================================ /** * This package contains the Exception classes. */ package redis.clients.jedis.exceptions; ================================================ FILE: src/main/java/redis/clients/jedis/executors/ClusterCommandExecutor.java ================================================ package redis.clients.jedis.executors; import java.time.Duration; import java.time.Instant; import java.util.List; import java.util.Map; import java.util.concurrent.ThreadLocalRandom; import java.util.concurrent.TimeUnit; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import redis.clients.jedis.*; import redis.clients.jedis.annots.VisibleForTesting; import redis.clients.jedis.exceptions.*; import redis.clients.jedis.executors.aggregators.MultiNodeResultAggregator; import redis.clients.jedis.providers.ClusterConnectionProvider; import redis.clients.jedis.util.IOUtils; import redis.clients.jedis.util.JedisAsserts; public class ClusterCommandExecutor implements CommandExecutor { private final Logger log = LoggerFactory.getLogger(getClass()); /** * Connection resolver used for keyed commands, to acquire connection based on slot. */ private final ConnectionResolver slotBasedConnectionResolver; /** * Connection resolver used for keyless commands, to acquire connection in round-robin fashion * from arbitrary node. */ private final ConnectionResolver roundRobinConnectionResolver; /** * Connection resolver used to enforce command execution on replicas. * * @see #executeCommandToReplica(CommandObject) */ private final ConnectionResolver replicaOnlyConnectionResolver; public final ClusterConnectionProvider provider; protected final int maxAttempts; protected final Duration maxTotalRetriesDuration; protected final CommandFlagsRegistry flags; /** * @deprecated use {@link #ClusterCommandExecutor(ClusterConnectionProvider, int, Duration, CommandFlagsRegistry)} * instead. This constructor will be removed in the next major version. */ @Deprecated public ClusterCommandExecutor(ClusterConnectionProvider provider, int maxAttempts, Duration maxTotalRetriesDuration) { this(provider, maxAttempts, maxTotalRetriesDuration, StaticCommandFlagsRegistry.registry()); } public ClusterCommandExecutor(ClusterConnectionProvider provider, int maxAttempts, Duration maxTotalRetriesDuration, CommandFlagsRegistry flags) { JedisAsserts.notNull(flags, "CommandFlagsRegistry must not be null"); JedisAsserts.notNull(provider, "provider must not be null"); JedisAsserts.isTrue(maxAttempts > 0, "maxAttempts must be greater than 0"); JedisAsserts.notNull(maxTotalRetriesDuration, "maxTotalRetriesDuration must not be null"); this.provider = provider; this.maxAttempts = maxAttempts; this.maxTotalRetriesDuration = maxTotalRetriesDuration; this.flags = flags; this.slotBasedConnectionResolver = ConnectionResolverFactory.createSlotBasedResolver( provider); this.roundRobinConnectionResolver = ConnectionResolverFactory.createRoundRobinResolver( provider, flags); this.replicaOnlyConnectionResolver = ConnectionResolverFactory.createReplicaOnlyResolver( provider); } @Override public void close() { this.provider.close(); } /** * Broadcast a command to cluster nodes. *

* This method uses {@link #doExecuteCommand} with a {@link SingleConnectionResolver} for each * node, which adds retry logic and connection failure handling to broadcast commands. * Redirections are not followed since we want to execute on specific nodes. *

* Error handling depends on the command's response policy: *

    *
  • {@code ONE_SUCCEEDED}: Returns success if at least one node succeeds
  • *
  • Other policies: Throws {@link JedisBroadcastException} if any node fails
  • *
* * @param commandObject the command to broadcast * @param primaryOnly if true, broadcast only to primary nodes; if false, broadcast to all nodes * including replicas * @return the reply from the command * @throws JedisBroadcastException if error handling criteria based on response policy are not met */ public final T broadcastCommand(CommandObject commandObject, boolean primaryOnly) { Map connectionMap = primaryOnly ? provider.getPrimaryNodesConnectionMap() : provider.getConnectionMap(); // Get the response policy for aggregation CommandFlagsRegistry.ResponsePolicy responsePolicy = flags.getResponsePolicy(commandObject .getArguments()); MultiNodeResultAggregator aggregator = new MultiNodeResultAggregator<>(responsePolicy); for (Map.Entry entry : connectionMap.entrySet()) { HostAndPort node = HostAndPort.from(entry.getKey()); ConnectionPool pool = entry.getValue(); try { // Create a resolver that acquires connections from this specific node's pool // A fresh connection is obtained on each resolve() call, allowing retries to work correctly // The doExecuteCommand method will close the connection after each attempt SingleConnectionResolver resolver = new SingleConnectionResolver(pool); // Don't follow redirections - we want to execute on specific nodes T aReply = doExecuteCommand(commandObject, resolver, false); aggregator.addSuccess(node, aReply); } catch (Exception anError) { aggregator.addError(node, anError); } } return aggregator.getResult(); } /** * Execute multiple command objects across different cluster shards and aggregate the results. *

* This method is designed for commands that need to operate on keys distributed across multiple * hash slots (e.g., DEL, EXISTS, MGET with keys from different slots). Each CommandObject in the * list is executed on its appropriate shard based on the key's hash slot, and the results are * aggregated using the command's response policy. *

* Error handling depends on the command's response policy: *

    *
  • {@code ONE_SUCCEEDED}: Returns success if at least one shard succeeds
  • *
  • Other policies: Throws {@link JedisBroadcastException} if any shard fails
  • *
* * @param commandObjects list of CommandObject instances, each targeting keys in the same hash slot * @param the return type of the command * @return the aggregated reply from all shards * @throws JedisBroadcastException if error handling criteria based on response policy are not met */ public final T executeMultiShardCommand(List> commandObjects) { if (commandObjects == null || commandObjects.isEmpty()) { throw new IllegalArgumentException("commandObjects must not be null or empty"); } // Get the response policy from the first command (all commands should have the same policy) CommandFlagsRegistry.ResponsePolicy responsePolicy = flags.getResponsePolicy( commandObjects.get(0).getArguments()); MultiNodeResultAggregator aggregator = new MultiNodeResultAggregator<>(responsePolicy); for (CommandObject commandObject : commandObjects) { try { // Execute each command on its appropriate shard using the existing retry logic T aReply = doExecuteCommand(commandObject, slotBasedConnectionResolver, true); aggregator.addSuccess(aReply); } catch (Exception anError) { // Extract node from exception (JedisClusterOperationException includes node info) aggregator.addError(anError); } } return aggregator.getResult(); } @Override public final T executeCommand(CommandObject commandObject) { CommandArguments args = commandObject.getArguments(); CommandFlagsRegistry.RequestPolicy requestPolicy = flags.getRequestPolicy(args); switch (requestPolicy) { case ALL_SHARDS: // Execute on all primary nodes (shards) return broadcastCommand(commandObject, true); case ALL_NODES: // Execute on all nodes including replicas return broadcastCommand(commandObject, false); // NOTE(imalinovskyi): Handling of special commands (SCAN, FT.CURSOR, etc.) should happen // in the custom abstractions and dedicated executor methods. case MULTI_SHARD: // Here we assume that MULTI_SHARD is already split into single-shard commands // and we just execute them one by one case SPECIAL: case DEFAULT: default: // Default behavior: check if keyless, otherwise use single-shard routing if (args.isKeyless()) { return executeKeylessCommand(commandObject); } else { return executeKeyedCommand(commandObject); } } } public final T executeCommandToReplica(CommandObject commandObject) { return doExecuteCommand(commandObject, replicaOnlyConnectionResolver, true); } private T executeKeylessCommand(CommandObject commandObject) { // For keyless commands, don't follow redirections - just retry with a different random node return doExecuteCommand(commandObject, roundRobinConnectionResolver, false); } private T executeKeyedCommand(CommandObject commandObject) { return doExecuteCommand(commandObject, slotBasedConnectionResolver, true); } /** * Executes a command with retry logic using the provided connection resolver. * * @param commandObject the command to execute * @param resolver the connection resolver to use for acquiring connections * @param followRedirections whether to follow cluster redirections (MOVED/ASK). Set to false for * keyless commands that should retry with the resolver instead. * @return the command result * @throws JedisClusterOperationException if all retry attempts fail, includes the last node tried */ private T doExecuteCommand(CommandObject commandObject, ConnectionResolver resolver, boolean followRedirections) { Instant deadline = Instant.now().plus(maxTotalRetriesDuration); JedisRedirectionException redirect = null; int consecutiveConnectionFailures = 0; Exception lastException = null; HostAndPort lastNode = null; // Track the last node we attempted to use for (int attemptsLeft = this.maxAttempts; attemptsLeft > 0; attemptsLeft--) { Connection connection = null; try { if (followRedirections && redirect != null) { // Following redirection, we need to use connection to the target node connection = provider.getConnection(redirect.getTargetNode()); if (redirect instanceof JedisAskDataException) { // TODO: Pipeline asking with the original command to make it faster.... connection.executeCommand(Protocol.Command.ASKING); } } else { connection = resolver.resolve(commandObject); } // Track the node we're using for error reporting lastNode = connection.getHostAndPort(); return execute(connection, commandObject); } catch (JedisClusterOperationException jcoe) { throw jcoe; } catch (JedisConnectionException jce) { lastException = jce; ++consecutiveConnectionFailures; log.debug("Failed connecting to Redis: {}", connection, jce); // "- 1" because we just did one, but the attemptsLeft counter hasn't been decremented yet boolean reset = handleConnectionProblem(attemptsLeft - 1, consecutiveConnectionFailures, deadline); if (reset) { consecutiveConnectionFailures = 0; redirect = null; } } catch (JedisRedirectionException jre) { // avoid updating lastException if it is a connection exception if (lastException == null || lastException instanceof JedisRedirectionException) { lastException = jre; } if (followRedirections) { log.debug("Redirected by server to {}", jre.getTargetNode()); redirect = jre; // if MOVED redirection occurred, if (jre instanceof JedisMovedDataException) { // it rebuilds cluster's slot cache recommended by Redis cluster specification provider.renewSlotCache(connection); } } else { // When followRedirections is false, throw the redirection exception immediately // instead of silently handling or ignoring it throw jre; } consecutiveConnectionFailures = 0; } finally { IOUtils.closeQuietly(connection); } if (Instant.now().isAfter(deadline)) { throw new JedisClusterOperationException("Cluster retry deadline exceeded.", lastException, lastNode); } } JedisClusterOperationException maxAttemptsException = new JedisClusterOperationException("No more cluster attempts left.", lastException, lastNode); throw maxAttemptsException; } /** * WARNING: This method is accessible for the purpose of testing. * This should not be used or overriden. */ @VisibleForTesting protected T execute(Connection connection, CommandObject commandObject) { return connection.executeCommand(commandObject); } /** * Related values should be reset if TRUE is returned. * @param attemptsLeft * @param consecutiveConnectionFailures * @param doneDeadline * @return true - if some actions are taken *
false - if no actions are taken */ private boolean handleConnectionProblem(int attemptsLeft, int consecutiveConnectionFailures, Instant doneDeadline) { if (this.maxAttempts < 3) { // Since we only renew the slots cache after two consecutive connection // failures (see consecutiveConnectionFailures above), we need to special // case the situation where we max out after two or fewer attempts. // Otherwise, on two or fewer max attempts, the slots cache would never be // renewed. if (attemptsLeft == 0) { provider.renewSlotCache(); return true; } return false; } if (consecutiveConnectionFailures < 2) { return false; } sleep(getBackoffSleepMillis(attemptsLeft, doneDeadline)); //We need this because if node is not reachable anymore - we need to finally initiate slots //renewing, or we can stuck with cluster state without one node in opposite case. //TODO make tracking of successful/unsuccessful operations for node - do renewing only //if there were no successful responses from this node last few seconds provider.renewSlotCache(); return true; } private static long getBackoffSleepMillis(int attemptsLeft, Instant deadline) { if (attemptsLeft <= 0) { return 0; } long millisLeft = Duration.between(Instant.now(), deadline).toMillis(); if (millisLeft < 0) { throw new JedisClusterOperationException("Cluster retry deadline exceeded."); } long maxBackOff = millisLeft / (attemptsLeft * attemptsLeft); return ThreadLocalRandom.current().nextLong(maxBackOff + 1); } /** * WARNING: This method is accessible for the purpose of testing. * This should not be used or overriden. */ @VisibleForTesting protected void sleep(long sleepMillis) { try { TimeUnit.MILLISECONDS.sleep(sleepMillis); } catch (InterruptedException e) { throw new JedisClusterOperationException(e); } } } ================================================ FILE: src/main/java/redis/clients/jedis/executors/CommandExecutor.java ================================================ package redis.clients.jedis.executors; import redis.clients.jedis.CommandObject; public interface CommandExecutor extends AutoCloseable { T executeCommand(CommandObject commandObject); } ================================================ FILE: src/main/java/redis/clients/jedis/executors/ConnectionResolver.java ================================================ package redis.clients.jedis.executors; import redis.clients.jedis.CommandFlagsRegistry; import redis.clients.jedis.CommandObject; import redis.clients.jedis.Connection; /** * Connection resolver interface for determining which connection to use for command execution. *

* This interface is used internally by the cluster command executor to resolve connections based on * the command type (keyed vs keyless) and read preference configuration. */ interface ConnectionResolver { /** * Intent of a connection request - whether the command is a read or write operation. */ enum ConnectionIntent { READ, WRITE } /** * Resolves the appropriate connection for executing the given command. * @param cmd the command object to execute * @return the connection to use for command execution */ Connection resolve(CommandObject cmd); /** * Determines the intent (READ or WRITE) for a given command based on its flags. * @param command the command object to check * @param flags the command flags registry to use for flag lookup * @return ConnectionIntent.READ if the command has the READONLY flag, otherwise * ConnectionIntent.WRITE */ default ConnectionIntent getIntent(CommandObject command, CommandFlagsRegistry flags) { return flags.getFlags(command.getArguments()).contains( CommandFlagsRegistry.CommandFlag.READONLY) ? ConnectionIntent.READ : ConnectionIntent.WRITE; } } ================================================ FILE: src/main/java/redis/clients/jedis/executors/ConnectionResolverFactory.java ================================================ package redis.clients.jedis.executors; import redis.clients.jedis.CommandFlagsRegistry; import redis.clients.jedis.providers.ClusterConnectionProvider; /** * Factory for creating {@link ConnectionResolver} instances. *

* This factory provides access to the package-private connection resolver implementations. */ final class ConnectionResolverFactory { private ConnectionResolverFactory() { // Utility class } /** * Creates a slot-based connection resolver for keyed commands. * @param provider the cluster connection provider* * @return a new SlotBasedConnectionResolver */ public static ConnectionResolver createSlotBasedResolver(ClusterConnectionProvider provider) { return new SlotBasedConnectionResolver(provider); } /** * Creates a round-robin connection resolver for keyless commands. * @param provider the cluster connection provider * @param flags the command flags registry * @return a new RoundRobinConnectionResolver */ public static ConnectionResolver createRoundRobinResolver(ClusterConnectionProvider provider, CommandFlagsRegistry flags) { return new RoundRobinConnectionResolver(provider, flags); } /** * Creates a replica-only connection resolver. * @param provider the cluster connection provider * @return a new ReplicaOnlyConnectionResolver */ public static ConnectionResolver createReplicaOnlyResolver(ClusterConnectionProvider provider) { return new ReplicaOnlyConnectionResolver(provider); } } ================================================ FILE: src/main/java/redis/clients/jedis/executors/DefaultCommandExecutor.java ================================================ package redis.clients.jedis.executors; import redis.clients.jedis.CommandObject; import redis.clients.jedis.Connection; import redis.clients.jedis.util.IOUtils; import redis.clients.jedis.providers.ConnectionProvider; public class DefaultCommandExecutor implements CommandExecutor { protected final ConnectionProvider provider; public DefaultCommandExecutor(ConnectionProvider provider) { this.provider = provider; } @Override public void close() { IOUtils.closeQuietly(this.provider); } @Override public final T executeCommand(CommandObject commandObject) { try (Connection connection = provider.getConnection(commandObject.getArguments())) { return connection.executeCommand(commandObject); } } } ================================================ FILE: src/main/java/redis/clients/jedis/executors/ReplicaOnlyConnectionResolver.java ================================================ package redis.clients.jedis.executors; import redis.clients.jedis.CommandObject; import redis.clients.jedis.Connection; import redis.clients.jedis.providers.ClusterConnectionProvider; /** * Connection resolver that always routes to replica nodes. *

* This resolver is used to enforce command execution on replicas, regardless of the command type or * global ReadFrom configuration. * @see redis.clients.jedis.executors.ClusterCommandExecutor#executeCommandToReplica(CommandObject) */ final class ReplicaOnlyConnectionResolver implements ConnectionResolver { private final ClusterConnectionProvider provider; ReplicaOnlyConnectionResolver(ClusterConnectionProvider provider) { this.provider = provider; } @Override public Connection resolve(CommandObject cmd) { return provider.getReplicaConnection(cmd.getArguments()); } } ================================================ FILE: src/main/java/redis/clients/jedis/executors/RetryableCommandExecutor.java ================================================ package redis.clients.jedis.executors; import java.time.Duration; import java.time.Instant; import java.util.concurrent.TimeUnit; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import redis.clients.jedis.CommandObject; import redis.clients.jedis.Connection; import redis.clients.jedis.annots.VisibleForTesting; import redis.clients.jedis.exceptions.JedisConnectionException; import redis.clients.jedis.exceptions.JedisException; import redis.clients.jedis.util.IOUtils; import redis.clients.jedis.providers.ConnectionProvider; import redis.clients.jedis.util.JedisAsserts; public class RetryableCommandExecutor implements CommandExecutor { private final Logger log = LoggerFactory.getLogger(getClass()); protected final ConnectionProvider provider; protected final int maxAttempts; protected final Duration maxTotalRetriesDuration; public RetryableCommandExecutor(ConnectionProvider provider, int maxAttempts, Duration maxTotalRetriesDuration) { JedisAsserts.notNull(provider, "provider must not be null"); JedisAsserts.isTrue(maxAttempts > 0, "maxAttempts must be greater than 0"); JedisAsserts.notNull(maxTotalRetriesDuration, "maxTotalRetriesDuration must not be null"); this.provider = provider; this.maxAttempts = maxAttempts; this.maxTotalRetriesDuration = maxTotalRetriesDuration; } @Override public void close() { IOUtils.closeQuietly(this.provider); } @Override public final T executeCommand(CommandObject commandObject) { Instant deadline = Instant.now().plus(maxTotalRetriesDuration); int consecutiveConnectionFailures = 0; JedisException lastException = null; for (int attemptsLeft = this.maxAttempts; attemptsLeft > 0; attemptsLeft--) { Connection connection = null; try { connection = provider.getConnection(commandObject.getArguments()); return execute(connection, commandObject); } catch (JedisConnectionException jce) { lastException = jce; ++consecutiveConnectionFailures; log.debug("Failed connecting to Redis: {}", connection, jce); // "- 1" because we just did one, but the attemptsLeft counter hasn't been decremented yet boolean reset = handleConnectionProblem(attemptsLeft - 1, consecutiveConnectionFailures, deadline); if (reset) { consecutiveConnectionFailures = 0; } } finally { if (connection != null) { connection.close(); } } if (Instant.now().isAfter(deadline)) { throw new JedisException("Retry deadline exceeded."); } } JedisException maxAttemptsException = new JedisException("No more attempts left."); if (lastException != null) { maxAttemptsException.addSuppressed(lastException); } throw maxAttemptsException; } /** * WARNING: This method is accessible for the purpose of testing. * This should not be used or overriden. */ @VisibleForTesting protected T execute(Connection connection, CommandObject commandObject) { return connection.executeCommand(commandObject); } /** * Related values should be reset if TRUE is returned. * * @param attemptsLeft * @param consecutiveConnectionFailures * @param doneDeadline * @return true - if some actions are taken *
false - if no actions are taken */ private boolean handleConnectionProblem(int attemptsLeft, int consecutiveConnectionFailures, Instant doneDeadline) { if (consecutiveConnectionFailures < 2) { return false; } sleep(getBackoffSleepMillis(attemptsLeft, doneDeadline)); return true; } private static long getBackoffSleepMillis(int attemptsLeft, Instant deadline) { if (attemptsLeft <= 0) { return 0; } long millisLeft = Duration.between(Instant.now(), deadline).toMillis(); if (millisLeft < 0) { throw new JedisException("Retry deadline exceeded."); } return millisLeft / (attemptsLeft * (attemptsLeft + 1)); } /** * WARNING: This method is accessible for the purpose of testing. * This should not be used or overriden. */ @VisibleForTesting protected void sleep(long sleepMillis) { try { TimeUnit.MILLISECONDS.sleep(sleepMillis); } catch (InterruptedException e) { throw new JedisException(e); } } } ================================================ FILE: src/main/java/redis/clients/jedis/executors/RoundRobinConnectionResolver.java ================================================ package redis.clients.jedis.executors; import java.util.ArrayList; import java.util.List; import java.util.Map; import java.util.concurrent.atomic.AtomicInteger; import redis.clients.jedis.CommandFlagsRegistry; import redis.clients.jedis.CommandObject; import redis.clients.jedis.Connection; import redis.clients.jedis.ConnectionPool; import redis.clients.jedis.exceptions.JedisClusterOperationException; import redis.clients.jedis.providers.ClusterConnectionProvider; /** * Connection resolver for keyless commands that acquires connections in round-robin fashion. *

* This resolver distributes keyless commands evenly across cluster nodes using round-robin * selection. Read operations can go to any node for load distribution, while write operations go to * primary nodes only. */ final class RoundRobinConnectionResolver implements ConnectionResolver { private final ClusterConnectionProvider provider; private final CommandFlagsRegistry flags; private final AtomicInteger roundRobinCounter = new AtomicInteger(0); RoundRobinConnectionResolver(ClusterConnectionProvider provider, CommandFlagsRegistry flags) { this.provider = provider; this.flags = flags; } @Override public Connection resolve(CommandObject cmd) { ConnectionResolver.ConnectionIntent intent = getIntent(cmd, flags); List> nodeList = selectConnectionPool(intent); int size = nodeList.size(); // Get and increment counter, then apply modulo with the current list size. // This handles the race condition where another thread may have updated the counter // based on a different (larger) node list size, which could result in an index // that is out of bounds for the current thread's smaller node list after topology change. int roundRobinIndex = Math.abs(roundRobinCounter.getAndIncrement() % size); Map.Entry selectedEntry = nodeList.get(roundRobinIndex); ConnectionPool pool = selectedEntry.getValue(); return pool.getResource(); } private List> selectConnectionPool( ConnectionResolver.ConnectionIntent intent) { Map connectionMap; if (intent == ConnectionResolver.ConnectionIntent.READ) { // For keyless READ commands, use all nodes for load distribution connectionMap = provider.getConnectionMap(); } else { // Write operations always go to primary nodes connectionMap = provider.getPrimaryNodesConnectionMap(); } if (connectionMap.isEmpty()) { throw new JedisClusterOperationException("No cluster nodes available."); } return new ArrayList<>(connectionMap.entrySet()); } } ================================================ FILE: src/main/java/redis/clients/jedis/executors/SimpleCommandExecutor.java ================================================ package redis.clients.jedis.executors; import redis.clients.jedis.CommandObject; import redis.clients.jedis.Connection; import redis.clients.jedis.util.IOUtils; public class SimpleCommandExecutor implements CommandExecutor { protected final Connection connection; public SimpleCommandExecutor(Connection connection) { this.connection = connection; } @Override public void close() { IOUtils.closeQuietly(connection); } @Override public final T executeCommand(CommandObject commandObject) { return connection.executeCommand(commandObject); } } ================================================ FILE: src/main/java/redis/clients/jedis/executors/SingleConnectionResolver.java ================================================ package redis.clients.jedis.executors; import redis.clients.jedis.CommandObject; import redis.clients.jedis.Connection; import redis.clients.jedis.ConnectionPool; /** * Connection resolver that acquires connections from a specific connection pool. *

* This resolver is used for broadcast commands where we want to execute commands on a specific * node's pool with the standard retry logic in {@link ClusterCommandExecutor#doExecuteCommand}. *

* Unlike other resolvers, this one is bound to a specific pool rather than using the cluster's * routing logic. A fresh connection is obtained from the pool on each call to {@link #resolve}, * which allows retry logic to work correctly since the {@code doExecuteCommand} method closes the * connection after each attempt. * @see ClusterCommandExecutor#broadcastCommand(CommandObject, boolean) */ final class SingleConnectionResolver implements ConnectionResolver { private final ConnectionPool pool; /** * Creates a resolver that acquires connections from the given pool. * @param pool the connection pool to acquire connections from */ SingleConnectionResolver(ConnectionPool pool) { this.pool = pool; } @Override public Connection resolve(CommandObject cmd) { return pool.getResource(); } } ================================================ FILE: src/main/java/redis/clients/jedis/executors/SlotBasedConnectionResolver.java ================================================ package redis.clients.jedis.executors; import redis.clients.jedis.CommandFlagsRegistry; import redis.clients.jedis.CommandObject; import redis.clients.jedis.Connection; import redis.clients.jedis.providers.ClusterConnectionProvider; /** * Connection resolver for keyed commands that acquires connections based on hash slot. *

* This resolver routes commands to the appropriate cluster node based on the key's hash slot. All * commands (both read and write) are routed to the primary node for the slot. */ final class SlotBasedConnectionResolver implements ConnectionResolver { private final ClusterConnectionProvider provider; SlotBasedConnectionResolver(ClusterConnectionProvider provider) { this.provider = provider; } @Override public Connection resolve(CommandObject cmd) { // Always route to primary node for slot-based routing return provider.getConnection(cmd.getArguments()); } } ================================================ FILE: src/main/java/redis/clients/jedis/executors/aggregators/Aggregator.java ================================================ package redis.clients.jedis.executors.aggregators; interface Aggregator { void add(I input); R getResult(); } ================================================ FILE: src/main/java/redis/clients/jedis/executors/aggregators/ClusterReplyAggregator.java ================================================ package redis.clients.jedis.executors.aggregators; import redis.clients.jedis.CommandFlagsRegistry; import redis.clients.jedis.exceptions.UnsupportedAggregationException; import java.util.Objects; /** * Stateful aggregator that keeps aggregating replies of the same type according to the given * ResponsePolicy. The proper underlying aggregator is lazily created on the first non-null value. * If all added values are null, getResult() returns null. */ class ClusterReplyAggregator implements Aggregator { private final CommandFlagsRegistry.ResponsePolicy policy; private Aggregator delegate; public ClusterReplyAggregator(CommandFlagsRegistry.ResponsePolicy policy) { this.policy = Objects.requireNonNull(policy, "policy cannot be null"); } /** * Adds a new value to the aggregator. Null values are ignored. */ @Override @SuppressWarnings("unchecked") public void add(T newReply) { if (newReply == null) { return; // ignore nulls } // Lazy initialization of delegate based on policy and first non-null value if (delegate == null) { switch (policy) { case AGG_SUM: if (!(newReply instanceof Number)) { throw new UnsupportedAggregationException( "AGG_SUM policy requires numeric type, but got: " + newReply.getClass().getName()); } delegate = (Aggregator) new SumAggregator<>(); // safe cast break; case AGG_MIN: delegate = new MinAggregator<>(); break; case AGG_MAX: delegate = new MaxAggregator<>(); break; case AGG_LOGICAL_AND: delegate = new LogicalAndAggregator<>(); break; case AGG_LOGICAL_OR: delegate = new LogicalOrAggregator<>(); break; case DEFAULT: delegate = new DefaultPolicyAggregator<>(); break; case ALL_SUCCEEDED: delegate = new FirstNonNullAggregator<>(); break; case ONE_SUCCEEDED: delegate = new FirstNonNullAggregator<>(); break; default: delegate = new FirstNonNullAggregator<>(); break; } } // Delegate the addition delegate.add(newReply); } /** * Returns the aggregated result so far. Returns null if no meaningful value has been added. */ @Override public T getResult() { return delegate == null ? null : delegate.getResult(); } } ================================================ FILE: src/main/java/redis/clients/jedis/executors/aggregators/DefaultPolicyAggregator.java ================================================ package redis.clients.jedis.executors.aggregators; import redis.clients.jedis.exceptions.UnsupportedAggregationException; import redis.clients.jedis.util.JedisByteHashMap; import redis.clients.jedis.util.JedisByteMap; import java.util.List; import java.util.Map; import java.util.Objects; import java.util.Set; /** * Aggregator for DEFAULT policy. Lazily creates a delegate aggregator based on the first non-null * sample value. All subsequent additions are delegated to the same aggregator. If all values are * null, getResult() returns null. */ class DefaultPolicyAggregator implements Aggregator { private Aggregator delegate; @Override public void add(T sample) { if (sample == null) { return; // ignore nulls } // Lazy initialization of delegate aggregator if (delegate == null) { delegate = createDelegateAggregator(sample); } delegate.add(sample); } @Override public T getResult() { return delegate == null ? null : delegate.getResult(); } @SuppressWarnings("unchecked") private Aggregator createDelegateAggregator(T sample) { Objects.requireNonNull(sample, "Sample value must not be null"); if (sample instanceof List) { return (Aggregator) new ListAggregator<>(); } if (sample instanceof Set) { return (Aggregator) new SetAggregator<>(); } if (sample instanceof JedisByteHashMap) { return (Aggregator) new JedisByteHashMapAggregator(); } if (sample instanceof JedisByteMap) { return (Aggregator) new JedisByteMapAggregator<>(); } if (sample instanceof Map) { return (Aggregator) new MapAggregator<>(); } throw new UnsupportedAggregationException( "DEFAULT policy requires List, Set, Map, JedisByteHashMap, or JedisByteMap types, but got: " + sample.getClass().getName()); } } ================================================ FILE: src/main/java/redis/clients/jedis/executors/aggregators/FirstNonNullAggregator.java ================================================ package redis.clients.jedis.executors.aggregators; /** * Aggregator that returns the first non-null value added. Subsequent additions are ignored. */ class FirstNonNullAggregator implements Aggregator { private T value; @Override public void add(T input) { if (value == null && input != null) { value = input; } } @Override public T getResult() { return value; } } ================================================ FILE: src/main/java/redis/clients/jedis/executors/aggregators/JedisByteHashMapAggregator.java ================================================ package redis.clients.jedis.executors.aggregators; import redis.clients.jedis.util.JedisByteHashMap; import java.util.ArrayList; import java.util.List; class JedisByteHashMapAggregator implements Aggregator { // Parts stores references to the maps added to the aggregator. // Defines the initial capacity of the list holding the parts. // Hard to come up with a reasonable default. // Start with 3 as min redis cluster has 3 masters. private static final int INITIAL_CAPACITY = 3; private List parts; @Override public void add(JedisByteHashMap map) { if (map == null) { return; } if (parts == null) { parts = new ArrayList<>(INITIAL_CAPACITY); } parts.add(map); } @Override public JedisByteHashMap getResult() { if (parts == null) { return null; } // Fast path: only one map added → return it if (parts.size() == 1) { return parts.get(0); } JedisByteHashMap result = new JedisByteHashMap(); for (JedisByteHashMap part : parts) { result.putAll(part); } return result; } } ================================================ FILE: src/main/java/redis/clients/jedis/executors/aggregators/JedisByteMapAggregator.java ================================================ package redis.clients.jedis.executors.aggregators; import redis.clients.jedis.util.JedisByteMap; import java.util.ArrayList; import java.util.List; class JedisByteMapAggregator implements Aggregator, JedisByteMap> { // Parts stores references to the maps added to the aggregator. // Defines the initial capacity of the list holding the parts. // Hard to come up with a reasonable default. // Start with 3 as min redis cluster has 3 masters. private static final int INITIAL_CAPACITY = 3; private List> parts; @Override public void add(JedisByteMap map) { if (map == null) { return; } if (parts == null) { parts = new ArrayList<>(INITIAL_CAPACITY); } parts.add(map); } @Override public JedisByteMap getResult() { if (parts == null) { return null; } // Fast path: only one non-null map if (parts.size() == 1) { return parts.get(0); } JedisByteMap result = new JedisByteMap<>(); for (JedisByteMap part : parts) { result.putAll(part); } return result; } } ================================================ FILE: src/main/java/redis/clients/jedis/executors/aggregators/ListAggregator.java ================================================ package redis.clients.jedis.executors.aggregators; import java.util.ArrayList; import java.util.List; class ListAggregator implements Aggregator, List> { // Parts stores references to the maps added to the aggregator. // Defines the initial capacity of the list holding the parts. // Hard to come up with a reasonable default. // Start with 3 as min redis cluster has 3 masters. private static final int INITIAL_CAPACITY = 3; private List> parts; private int totalSize; @Override public void add(List list) { if (list == null) { return; } if (parts == null) { parts = new ArrayList<>(INITIAL_CAPACITY); } parts.add(list); totalSize += list.size(); } @Override public List getResult() { if (parts == null) { return null; } if (parts.size() == 1) { return parts.get(0); } List result = new ArrayList<>(totalSize); for (List part : parts) { result.addAll(part); } return result; } } ================================================ FILE: src/main/java/redis/clients/jedis/executors/aggregators/LogicalAndAggregator.java ================================================ package redis.clients.jedis.executors.aggregators; /** * Aggregator that performs logical AND on added values. Supports Boolean, Long, * ArrayList<Boolean>, ArrayList<Long>. */ class LogicalAndAggregator extends LogicalBinaryAggregator { LogicalAndAggregator() { super("AGG_LOGICAL_AND"); } @Override protected boolean applyBooleanOp(boolean a, boolean b) { return a && b; } } ================================================ FILE: src/main/java/redis/clients/jedis/executors/aggregators/LogicalBinaryAggregator.java ================================================ package redis.clients.jedis.executors.aggregators; import redis.clients.jedis.exceptions.UnsupportedAggregationException; import java.util.ArrayList; /** * Abstract base class for logical binary operations (AND, OR). Supports Boolean, Long, * ArrayList, ArrayList. */ abstract class LogicalBinaryAggregator implements Aggregator { private T result; private final String operationName; protected LogicalBinaryAggregator(String operationName) { this.operationName = operationName; } @Override public void add(T input) { if (input == null) { return; // ignore nulls } if (result == null) { // First non-null input initializes the result if (input instanceof Boolean || input instanceof Long || input instanceof ArrayList) { result = input; return; } else { throw new UnsupportedAggregationException(operationName + " requires Boolean, Long, ArrayList, or ArrayList, but got: " + input.getClass().getName()); } } // Handle Boolean if (result instanceof Boolean && input instanceof Boolean) { result = (T) Boolean.valueOf(applyBooleanOp((Boolean) result, (Boolean) input)); return; } // Handle Long if (result instanceof Long && input instanceof Long) { boolean existingBool = (Long) result != 0; boolean newBool = (Long) input != 0; result = (T) Long.valueOf(applyBooleanOp(existingBool, newBool) ? 1L : 0L); return; } // Handle ArrayList if (result instanceof ArrayList && input instanceof ArrayList) { ArrayList existingList = (ArrayList) result; ArrayList newList = (ArrayList) input; if (existingList.size() != newList.size()) { throw new UnsupportedAggregationException( operationName + " requires ArrayLists of equal size, but got sizes: " + existingList.size() + " and " + newList.size()); } if (!existingList.isEmpty()) { Object firstExisting = existingList.get(0); Object firstNew = newList.get(0); // ArrayList if (firstExisting instanceof Boolean && firstNew instanceof Boolean) { ArrayList res = new ArrayList<>(existingList.size()); for (int i = 0; i < existingList.size(); i++) { res.add(applyBooleanOp((Boolean) existingList.get(i), (Boolean) newList.get(i))); } result = (T) res; return; } // ArrayList treated as boolean if (firstExisting instanceof Long && firstNew instanceof Long) { ArrayList res = new ArrayList<>(existingList.size()); for (int i = 0; i < existingList.size(); i++) { boolean e = ((Long) existingList.get(i)) != 0; boolean n = ((Long) newList.get(i)) != 0; res.add(applyBooleanOp(e, n) ? 1L : 0L); } result = (T) res; return; } } else { // Empty lists → result remains empty list result = (T) new ArrayList<>(); return; } } throw new UnsupportedAggregationException( operationName + " requires Boolean, Long, ArrayList, or ArrayList, but got: " + result.getClass().getName() + " and " + input.getClass().getName()); } @Override public T getResult() { return result; } /** * Template method for subclasses to implement the specific logical operation. * @param a first boolean operand * @param b second boolean operand * @return result of the logical operation */ protected abstract boolean applyBooleanOp(boolean a, boolean b); } ================================================ FILE: src/main/java/redis/clients/jedis/executors/aggregators/LogicalOrAggregator.java ================================================ package redis.clients.jedis.executors.aggregators; /** * Aggregator that performs logical OR on added values. Supports Boolean, Long, * ArrayList<Boolean>, ArrayList<Long>. */ class LogicalOrAggregator extends LogicalBinaryAggregator { LogicalOrAggregator() { super("AGG_LOGICAL_OR"); } @Override protected boolean applyBooleanOp(boolean a, boolean b) { return a || b; } } ================================================ FILE: src/main/java/redis/clients/jedis/executors/aggregators/MapAggregator.java ================================================ package redis.clients.jedis.executors.aggregators; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; class MapAggregator implements Aggregator, Map> { // Parts stores references to the maps added to the aggregator. // Defines the initial capacity of the list holding the parts. // Hard to come up with a reasonable default. // Start with 3 as min redis cluster has 3 masters. private static final int INITIAL_CAPACITY = 3; private List> parts; private int totalSize; @Override public void add(Map map) { if (map == null) { return; } if (parts == null) { parts = new ArrayList<>(INITIAL_CAPACITY); } parts.add(map); totalSize += map.size(); } @Override public Map getResult() { if (parts == null) { return null; } if (parts.size() == 1) { return parts.get(0); } Map result = new HashMap<>(totalSize); for (Map part : parts) { result.putAll(part); // last write wins } return result; } } ================================================ FILE: src/main/java/redis/clients/jedis/executors/aggregators/MaxAggregator.java ================================================ package redis.clients.jedis.executors.aggregators; import redis.clients.jedis.exceptions.UnsupportedAggregationException; import redis.clients.jedis.util.KeyValue; /** * Aggregator that returns the maximum value added so far. Supports Comparable types and * KeyValue where both key and value are Comparable. */ class MaxAggregator implements Aggregator { private T max; @Override public void add(T input) { if (input == null) { return; } if (max == null) { max = input; return; } // Handle KeyValue types if (max instanceof KeyValue && input instanceof KeyValue) { max = (T) aggregateKeyValueMax((KeyValue) max, (KeyValue) input); return; } // Handle Comparable types if (max instanceof Comparable && input instanceof Comparable) { Comparable maxComp = (Comparable) max; if (maxComp.compareTo(input) < 0) { max = input; } return; } throw new UnsupportedAggregationException( "AGG_MAX policy requires Comparable types or KeyValue, but got: " + max.getClass().getName() + " and " + input.getClass().getName()); } @Override public T getResult() { return max; } @SuppressWarnings("unchecked") private KeyValue aggregateKeyValueMax(KeyValue kv1, KeyValue kv2) { Object maxKey = ((Comparable) kv1.getKey()).compareTo(kv2.getKey()) >= 0 ? kv1.getKey() : kv2.getKey(); Object maxValue = ((Comparable) kv1.getValue()).compareTo(kv2.getValue()) >= 0 ? kv1.getValue() : kv2.getValue(); return new KeyValue<>(maxKey, maxValue); } } ================================================ FILE: src/main/java/redis/clients/jedis/executors/aggregators/MinAggregator.java ================================================ package redis.clients.jedis.executors.aggregators; import redis.clients.jedis.exceptions.UnsupportedAggregationException; import redis.clients.jedis.util.KeyValue; /** * Aggregator that returns the minimum value added so far. Supports Comparable types and * KeyValue where both key and value are Comparable. */ class MinAggregator implements Aggregator { private T min; @Override public void add(T input) { if (input == null) { return; } if (min == null) { min = input; return; } // Handle KeyValue types if (min instanceof KeyValue && input instanceof KeyValue) { min = (T) aggregateKeyValueMin((KeyValue) min, (KeyValue) input); return; } // Handle Comparable types if (min instanceof Comparable && input instanceof Comparable) { Comparable minComp = (Comparable) min; if (minComp.compareTo(input) > 0) { min = input; } return; } throw new UnsupportedAggregationException( "AGG_MIN policy requires Comparable types or KeyValue, but got: " + min.getClass().getName() + " and " + input.getClass().getName()); } @Override public T getResult() { return min; } @SuppressWarnings("unchecked") private KeyValue aggregateKeyValueMin(KeyValue kv1, KeyValue kv2) { Object minKey = ((Comparable) kv1.getKey()).compareTo(kv2.getKey()) <= 0 ? kv1.getKey() : kv2.getKey(); Object minValue = ((Comparable) kv1.getValue()).compareTo(kv2.getValue()) <= 0 ? kv1.getValue() : kv2.getValue(); return new KeyValue<>(minKey, minValue); } } ================================================ FILE: src/main/java/redis/clients/jedis/executors/aggregators/MultiNodeResultAggregator.java ================================================ package redis.clients.jedis.executors.aggregators; import redis.clients.jedis.CommandFlagsRegistry; import redis.clients.jedis.HostAndPort; import redis.clients.jedis.annots.Internal; import redis.clients.jedis.exceptions.JedisBroadcastException; import redis.clients.jedis.exceptions.JedisClusterOperationException; /** * Internal use only. Aggregates results from multiple node/shard executions based on response * policy. *

* This class centralizes the logic for collecting results and errors from multi-node command * execution (broadcast commands, multi-shard commands) and determining the final result based on * the {@link CommandFlagsRegistry.ResponsePolicy}. *

* Key behavior differences by policy: *

    *
  • {@code ONE_SUCCEEDED}: Returns success if at least one node succeeds, even if others fail. * Only throws if ALL nodes fail.
  • *
  • All other policies: Throws {@link JedisBroadcastException} if ANY node fails.
  • *
* @param the type of the command result */ @Internal public final class MultiNodeResultAggregator { private static final HostAndPort UNKNOWN_NODE = HostAndPort.from("unknown:0"); private final CommandFlagsRegistry.ResponsePolicy responsePolicy; private final JedisBroadcastException bcastError; private final Aggregator replyAggregator; private boolean hasError; private boolean hasSuccess; /** * Creates a new aggregator with the specified response policy. * @param responsePolicy the policy that determines how to aggregate results and handle errors */ public MultiNodeResultAggregator(CommandFlagsRegistry.ResponsePolicy responsePolicy) { this.responsePolicy = responsePolicy; this.replyAggregator = new ClusterReplyAggregator<>(responsePolicy); this.bcastError = new JedisBroadcastException(); this.hasError = false; this.hasSuccess = false; } /** * Records a successful result from a node. * @param node the node that returned the result * @param result the result from the node */ public void addSuccess(HostAndPort node, T result) { if (node != null) { bcastError.addReply(node, result); } aggregateSuccess(result); } /** * Records a successful result without node information. *

* Use this method when the node information is not readily available, such as in multi-shard * commands where extracting the node would require additional computation. * @param result the result from the operation */ public void addSuccess(T result) { aggregateSuccess(result); } /** * Aggregates a successful result into the accumulated reply. * @param result the result to aggregate */ private void aggregateSuccess(T result) { hasSuccess = true; // Always aggregate successful results, even if we've seen errors // This is important for ONE_SUCCEEDED policy where we need to return // a successful result even if some nodes failed replyAggregator.add(result); } /** * Records an error from a node. * @param node the node that returned the error * @param error the exception from the node */ public void addError(HostAndPort node, Exception error) { bcastError.addReply(node, error); hasError = true; } /** * Records an error, extracting the node information from the exception if available. *

* This method extracts node information from {@link JedisClusterOperationException} if present, * otherwise uses a placeholder "unknown:0" node. * @param error the exception from the failed operation */ public void addError(Exception error) { HostAndPort node = extractNodeFromException(error); addError(node, error); } /** * Extracts the node information from an exception if available. * @param error the exception to extract node info from * @return the node that caused the error, or a placeholder if unknown */ private static HostAndPort extractNodeFromException(Exception error) { if (error instanceof JedisClusterOperationException) { HostAndPort node = ((JedisClusterOperationException) error).getNode(); if (node != null) { return node; } } return UNKNOWN_NODE; } /** * Returns the aggregated result based on the response policy. *

* For {@code ONE_SUCCEEDED} policy: returns the aggregated result if at least one node succeeded, * throws {@link JedisBroadcastException} only if all nodes failed. *

* For all other policies: throws {@link JedisBroadcastException} if any node failed, otherwise * returns the aggregated result. * @return the aggregated result * @throws JedisBroadcastException if the policy criteria are not met */ public T getResult() { if (responsePolicy == CommandFlagsRegistry.ResponsePolicy.ONE_SUCCEEDED) { // ONE_SUCCEEDED: return success if at least one node succeeded if (hasSuccess) { return replyAggregator.getResult(); } // All nodes failed throw bcastError.prepareToThrow(); } else { // All other policies: throw if any node failed if (hasError) { throw bcastError.prepareToThrow(); } return replyAggregator.getResult(); } } /** * Returns the response policy being used by this aggregator. * @return the response policy */ public CommandFlagsRegistry.ResponsePolicy getResponsePolicy() { return responsePolicy; } } ================================================ FILE: src/main/java/redis/clients/jedis/executors/aggregators/SetAggregator.java ================================================ package redis.clients.jedis.executors.aggregators; import java.util.ArrayList; import java.util.Arrays; import java.util.HashSet; import java.util.List; import java.util.Set; class SetAggregator implements Aggregator, Set> { // Parts stores references to the maps added to the aggregator. // Defines the initial capacity of the list holding the parts. // Hard to come up with a reasonable default. // Start with 3 as min redis cluster has 3 masters. private static final int INITIAL_CAPACITY = 3; private List> parts; private int totalSize; @Override public void add(Set set) { if (set == null) { return; } if (parts == null) { parts = new ArrayList<>(INITIAL_CAPACITY); } parts.add(set); totalSize += set.size(); } @Override public Set getResult() { if (parts == null) { return null; } if (parts.size() == 1) { return parts.get(0); } // Check if we're dealing with Set - need special handling for proper deduplication for (Set set : parts) { if (!set.isEmpty()) { Object firstElement = set.iterator().next(); if (firstElement instanceof byte[]) { return mergeByteArraySets(); } break; } } Set result = new HashSet<>(totalSize); for (Set part : parts) { result.addAll(part); } return result; } @SuppressWarnings("unchecked") private Set mergeByteArraySets() { // Wrap byte arrays for proper equals/hashCode, deduplicate, then unwrap Set wrappedSet = new HashSet<>(totalSize); for (Set part : parts) { for (T element : part) { wrappedSet.add(new ByteArrayWrapper((byte[]) element)); } } // Unwrap back to byte[] Set result = new HashSet<>(wrappedSet.size()); for (ByteArrayWrapper wrapper : wrappedSet) { result.add(wrapper.unwrap()); } return (Set) result; } static final class ByteArrayWrapper { private final byte[] data; ByteArrayWrapper(byte[] data) { this.data = data; } byte[] unwrap() { return data; } @Override public boolean equals(Object o) { if (!(o instanceof ByteArrayWrapper)) { return false; } return Arrays.equals(data, ((ByteArrayWrapper) o).data); } @Override public int hashCode() { return Arrays.hashCode(data); } } } ================================================ FILE: src/main/java/redis/clients/jedis/executors/aggregators/SumAggregator.java ================================================ package redis.clients.jedis.executors.aggregators; import redis.clients.jedis.exceptions.UnsupportedAggregationException; /** * Aggregator that sums numeric values of the same type. Null inputs are ignored; if all inputs are * null, the result is null. Supports Integer, Long, and Double. */ class SumAggregator implements Aggregator { private T sum; @Override public void add(T value) { if (value == null) { return; } if (sum == null) { sum = value; return; } if (sum instanceof Long && value instanceof Long) { sum = (T) Long.valueOf(sum.longValue() + value.longValue()); } else if (sum instanceof Integer && value instanceof Integer) { sum = (T) Integer.valueOf(sum.intValue() + value.intValue()); } else if (sum instanceof Double && value instanceof Double) { sum = (T) Double.valueOf(sum.doubleValue() + value.doubleValue()); } else { throw new UnsupportedAggregationException( "SumAggregator requires numeric types of the same kind (Integer, Long, Double), but got: " + sum.getClass().getSimpleName() + " and " + value.getClass().getSimpleName()); } } @Override public T getResult() { return sum; } } ================================================ FILE: src/main/java/redis/clients/jedis/executors/package-info.java ================================================ /** * This package contains the implementations of CommandExecutor interface. */ package redis.clients.jedis.executors; ================================================ FILE: src/main/java/redis/clients/jedis/json/DefaultGsonObjectMapper.java ================================================ package redis.clients.jedis.json; import com.google.gson.Gson; /** * Use the default {@link Gson} configuration for serialization and deserialization JSON * operations. *

When none is explicitly set, this will be set.

* @see JsonObjectMapper Create a custom JSON serializer/deserializer */ public class DefaultGsonObjectMapper implements JsonObjectMapper { /** * Instance of Gson object with default gson configuration. */ private final Gson gson = new Gson(); @Override public T fromJson(String value, Class valueType) { return gson.fromJson(value, valueType); } @Override public String toJson(Object value) { return gson.toJson(value); } } ================================================ FILE: src/main/java/redis/clients/jedis/json/JsonBuilderFactory.java ================================================ package redis.clients.jedis.json; import static redis.clients.jedis.BuilderFactory.STRING; import java.util.ArrayList; import java.util.List; import java.util.stream.Collectors; import org.json.JSONArray; import org.json.JSONException; import org.json.JSONObject; import redis.clients.jedis.Builder; import redis.clients.jedis.BuilderFactory; import redis.clients.jedis.exceptions.JedisException; public final class JsonBuilderFactory { public static final Builder> JSON_TYPE = new Builder>() { @Override public Class build(Object data) { if (data == null) return null; String str = STRING.build(data); switch (str) { case "null": return null; case "boolean": return boolean.class; case "integer": return int.class; case "number": return float.class; case "string": return String.class; case "object": return Object.class; case "array": return List.class; default: throw new JedisException("Unknown type: " + str); } } @Override public String toString() { return "Class"; } }; public static final Builder>> JSON_TYPE_LIST = new Builder>>() { @Override public List> build(Object data) { List list = (List) data; List> classes = new ArrayList<>(list.size()); for (Object elem : list) { try { classes.add(JSON_TYPE.build(elem)); } catch (JedisException je) { classes.add(null); } } return classes; } }; public static final Builder>>> JSON_TYPE_RESPONSE_RESP3 = new Builder>>>() { @Override public List>> build(Object data) { return ((List) data).stream().map(JSON_TYPE_LIST::build).collect(Collectors.toList()); } }; public static final Builder>> JSON_TYPE_RESPONSE_RESP3_COMPATIBLE = new Builder>>() { @Override public List> build(Object data) { List>> fullReply = JSON_TYPE_RESPONSE_RESP3.build(data); return fullReply == null ? null : fullReply.get(0); } }; public static final Builder JSON_OBJECT = new Builder() { @Override public Object build(Object data) { if (data == null) { return null; } if (!(data instanceof byte[])) { return data; } String str = STRING.build(data); if (str.charAt(0) == '{') { try { return new JSONObject(str); } catch (Exception ex) { } } else if (str.charAt(0) == '[') { try { return new JSONArray(str); } catch (Exception ex) { } } return str; } }; public static final Builder JSON_ARRAY = new Builder() { @Override public JSONArray build(Object data) { if (data == null) { return null; } String str = STRING.build(data); try { return new JSONArray(str); } catch (JSONException ex) { // This is not necessary but we are doing this // just to make it safe for com.vaadin.external.google:android-json library throw new JedisException(ex); } } }; public static final Builder JSON_ARRAY_OR_DOUBLE_LIST = new Builder() { @Override public Object build(Object data) { if (data == null) return null; if (data instanceof List) return BuilderFactory.DOUBLE_LIST.build(data); return JSON_ARRAY.build(data); } }; public static final Builder> JSON_ARRAY_LIST = new Builder>() { @Override public List build(Object data) { if (data == null) { return null; } List list = (List) data; return list.stream().map(o -> JSON_ARRAY.build(o)).collect(Collectors.toList()); } }; private JsonBuilderFactory() { throw new InstantiationError("Must not instantiate this class"); } } ================================================ FILE: src/main/java/redis/clients/jedis/json/JsonObjectMapper.java ================================================ package redis.clients.jedis.json; /** * Represents the ability of serialize an object to JSON format string and deserialize it to the * typed object. * @see DefaultGsonObjectMapper Default implementation for JSON serializer/deserializer * engine with com.google.gson.Gson */ public interface JsonObjectMapper { /** * Perform deserialization from JSON format string to the given type object as argument. * @param value the JSON format * @param valueType the object type to convert * @param the type object to convert * @return the instance of an object to the type given argument */ T fromJson(String value, Class valueType); /** * Perform serialization from object to JSON format string. * @param value the object to convert * @return the JSON format string */ String toJson(Object value); } ================================================ FILE: src/main/java/redis/clients/jedis/json/JsonProtocol.java ================================================ package redis.clients.jedis.json; import redis.clients.jedis.commands.ProtocolCommand; import redis.clients.jedis.util.SafeEncoder; public class JsonProtocol { public enum JsonCommand implements ProtocolCommand { DEL("JSON.DEL"), GET("JSON.GET"), MGET("JSON.MGET"), MERGE("JSON.MERGE"), SET("JSON.SET"), TYPE("JSON.TYPE"), STRAPPEND("JSON.STRAPPEND"), STRLEN("JSON.STRLEN"), NUMINCRBY("JSON.NUMINCRBY"), ARRAPPEND("JSON.ARRAPPEND"), ARRINDEX("JSON.ARRINDEX"), ARRINSERT("JSON.ARRINSERT"), ARRLEN("JSON.ARRLEN"), ARRPOP("JSON.ARRPOP"), ARRTRIM("JSON.ARRTRIM"), CLEAR("JSON.CLEAR"), TOGGLE("JSON.TOGGLE"), OBJKEYS("JSON.OBJKEYS"), OBJLEN("JSON.OBJLEN"), DEBUG("JSON.DEBUG"), RESP("JSON.RESP"); private final byte[] raw; private JsonCommand(String alt) { raw = SafeEncoder.encode(alt); } @Override public byte[] getRaw() { return raw; } } } ================================================ FILE: src/main/java/redis/clients/jedis/json/JsonSetParams.java ================================================ package redis.clients.jedis.json; import redis.clients.jedis.CommandArguments; import redis.clients.jedis.params.IParams; public class JsonSetParams implements IParams { private boolean nx = false; private boolean xx = false; public JsonSetParams() { } public static JsonSetParams jsonSetParams() { return new JsonSetParams(); } public JsonSetParams nx() { this.nx = true; this.xx = false; return this; } public JsonSetParams xx() { this.nx = false; this.xx = true; return this; } @Override public void addParams(CommandArguments args) { if (nx) { args.add("NX"); } if (xx) { args.add("XX"); } } } ================================================ FILE: src/main/java/redis/clients/jedis/json/Path.java ================================================ package redis.clients.jedis.json; /** * Path is a RedisJSON (v1) path, representing a valid path into an object. * @deprecated RedisJSON (v1) support is deprecated. */ @Deprecated public class Path { public static final Path ROOT_PATH = new Path("."); private final String strPath; public Path(final String strPath) { this.strPath = strPath; } @Override public String toString() { return strPath; } public static Path of(final String strPath) { return new Path(strPath); } @Override public boolean equals(Object obj) { if (obj == null) { return false; } if (!(obj instanceof Path)) { return false; } if (obj == this) { return true; } return this.toString().equals(((Path) obj).toString()); } @Override public int hashCode() { return strPath.hashCode(); } } ================================================ FILE: src/main/java/redis/clients/jedis/json/Path2.java ================================================ package redis.clients.jedis.json; /** * Path is a RedisJSON v2 path, representing a valid path or a multi-path into an object. */ public class Path2 { public static final Path2 ROOT_PATH = new Path2("$"); private final String str; public Path2(final String str) { if (str == null) { throw new NullPointerException("Path cannot be null."); } if (str.isEmpty()) { throw new IllegalArgumentException("Path cannot be empty."); } if (str.charAt(0) == '$') { this.str = str; } else if (str.charAt(0) == '.') { this.str = '$' + str; } else { this.str = "$." + str; } } @Override public String toString() { return str; } public static Path2 of(final String path) { return new Path2(path); } @Override public boolean equals(Object obj) { if (obj == null) { return false; } if (!(obj instanceof Path2)) { return false; } if (obj == this) { return true; } return this.toString().equals(((Path2) obj).toString()); } @Override public int hashCode() { return str.hashCode(); } } ================================================ FILE: src/main/java/redis/clients/jedis/json/commands/RedisJsonCommands.java ================================================ package redis.clients.jedis.json.commands; public interface RedisJsonCommands extends RedisJsonV1Commands, RedisJsonV2Commands { } ================================================ FILE: src/main/java/redis/clients/jedis/json/commands/RedisJsonPipelineCommands.java ================================================ package redis.clients.jedis.json.commands; public interface RedisJsonPipelineCommands extends RedisJsonV1PipelineCommands, RedisJsonV2PipelineCommands { } ================================================ FILE: src/main/java/redis/clients/jedis/json/commands/RedisJsonV1Commands.java ================================================ package redis.clients.jedis.json.commands; import java.util.List; import redis.clients.jedis.json.JsonSetParams; import redis.clients.jedis.json.Path; /** * @deprecated RedisJSON (v1) support is deprecated. */ @Deprecated public interface RedisJsonV1Commands { @Deprecated default String jsonSetLegacy(String key, Object pojo) { return jsonSet(key, Path.ROOT_PATH, pojo); } @Deprecated default String jsonSetLegacy(String key, Object pojo, JsonSetParams params) { return jsonSet(key, Path.ROOT_PATH, pojo, params); } @Deprecated String jsonSet(String key, Path path, Object pojo); @Deprecated String jsonSetWithPlainString(String key, Path path, String string); @Deprecated String jsonSet(String key, Path path, Object pojo, JsonSetParams params); @Deprecated String jsonMerge(String key, Path path, Object pojo); Object jsonGet(String key); // both ver @Deprecated T jsonGet(String key, Class clazz); @Deprecated Object jsonGet(String key, Path... paths); @Deprecated String jsonGetAsPlainString(String key, Path path); @Deprecated T jsonGet(String key, Class clazz, Path... paths); @Deprecated default List jsonMGet(Class clazz, String... keys) { return jsonMGet(Path.ROOT_PATH, clazz, keys); } @Deprecated List jsonMGet(Path path, Class clazz, String... keys); long jsonDel(String key); // both ver @Deprecated long jsonDel(String key, Path path); long jsonClear(String key); // no test @Deprecated long jsonClear(String key, Path path); @Deprecated String jsonToggle(String key, Path path); @Deprecated Class jsonType(String key); @Deprecated Class jsonType(String key, Path path); @Deprecated long jsonStrAppend(String key, Object string); @Deprecated long jsonStrAppend(String key, Path path, Object string); @Deprecated Long jsonStrLen(String key); @Deprecated Long jsonStrLen(String key, Path path); @Deprecated double jsonNumIncrBy(String key, Path path, double value); @Deprecated Long jsonArrAppend(String key, Path path, Object... pojos); @Deprecated long jsonArrIndex(String key, Path path, Object scalar); @Deprecated long jsonArrInsert(String key, Path path, int index, Object... pojos); @Deprecated Object jsonArrPop(String key); @Deprecated T jsonArrPop(String key, Class clazz); @Deprecated Object jsonArrPop(String key, Path path); @Deprecated T jsonArrPop(String key, Class clazz, Path path); @Deprecated Object jsonArrPop(String key, Path path, int index); @Deprecated T jsonArrPop(String key, Class clazz, Path path, int index); @Deprecated Long jsonArrLen(String key); @Deprecated Long jsonArrLen(String key, Path path); @Deprecated Long jsonArrTrim(String key, Path path, int start, int stop); @Deprecated Long jsonObjLen(String key); @Deprecated Long jsonObjLen(String key, Path path); @Deprecated List jsonObjKeys(String key); @Deprecated List jsonObjKeys(String key, Path path); @Deprecated long jsonDebugMemory(String key); @Deprecated long jsonDebugMemory(String key, Path path); } ================================================ FILE: src/main/java/redis/clients/jedis/json/commands/RedisJsonV1PipelineCommands.java ================================================ package redis.clients.jedis.json.commands; import java.util.List; import redis.clients.jedis.Response; import redis.clients.jedis.json.JsonSetParams; import redis.clients.jedis.json.Path; /** * @deprecated RedisJSON (v1) support is deprecated. */ @Deprecated public interface RedisJsonV1PipelineCommands { @Deprecated default Response jsonSetLegacy(String key, Object pojo) { return jsonSet(key, Path.ROOT_PATH, pojo); } @Deprecated default Response jsonSetLegacy(String key, Object pojo, JsonSetParams params) { return jsonSet(key, Path.ROOT_PATH, pojo, params); } @Deprecated Response jsonSet(String key, Path path, Object pojo); @Deprecated Response jsonSet(String key, Path path, Object pojo, JsonSetParams params); @Deprecated Response jsonMerge(String key, Path path, Object pojo); Response jsonGet(String key); // both ver @Deprecated Response jsonGet(String key, Class clazz); @Deprecated Response jsonGet(String key, Path... paths); @Deprecated Response jsonGet(String key, Class clazz, Path... paths); @Deprecated default Response> jsonMGet(Class clazz, String... keys) { return jsonMGet(Path.ROOT_PATH, clazz, keys); } @Deprecated Response> jsonMGet(Path path, Class clazz, String... keys); Response jsonDel(String key); // both ver @Deprecated Response jsonDel(String key, Path path); @Deprecated Response jsonClear(String key); // no test @Deprecated Response jsonClear(String key, Path path); @Deprecated Response jsonToggle(String key, Path path); @Deprecated Response> jsonType(String key); @Deprecated Response> jsonType(String key, Path path); @Deprecated Response jsonStrAppend(String key, Object string); @Deprecated Response jsonStrAppend(String key, Path path, Object string); @Deprecated Response jsonStrLen(String key); @Deprecated Response jsonStrLen(String key, Path path); @Deprecated Response jsonNumIncrBy(String key, Path path, double value); @Deprecated Response jsonArrAppend(String key, Path path, Object... pojos); @Deprecated Response jsonArrIndex(String key, Path path, Object scalar); @Deprecated Response jsonArrInsert(String key, Path path, int index, Object... pojos); @Deprecated Response jsonArrPop(String key); @Deprecated Response jsonArrPop(String key, Class clazz); @Deprecated Response jsonArrPop(String key, Path path); @Deprecated Response jsonArrPop(String key, Class clazz, Path path); @Deprecated Response jsonArrPop(String key, Path path, int index); @Deprecated Response jsonArrPop(String key, Class clazz, Path path, int index); @Deprecated Response jsonArrLen(String key); @Deprecated Response jsonArrLen(String key, Path path); @Deprecated Response jsonArrTrim(String key, Path path, int start, int stop); } ================================================ FILE: src/main/java/redis/clients/jedis/json/commands/RedisJsonV2Commands.java ================================================ package redis.clients.jedis.json.commands; import java.util.List; import org.json.JSONArray; import redis.clients.jedis.json.JsonSetParams; import redis.clients.jedis.json.Path2; public interface RedisJsonV2Commands { default String jsonSet(String key, Object object) { return jsonSet(key, Path2.ROOT_PATH, object); } default String jsonSetWithEscape(String key, Object object) { return jsonSetWithEscape(key, Path2.ROOT_PATH, object); } default String jsonSet(String key, Object object, JsonSetParams params) { return jsonSet(key, Path2.ROOT_PATH, object, params); } default String jsonSetWithEscape(String key, Object object, JsonSetParams params) { return jsonSetWithEscape(key, Path2.ROOT_PATH, object, params); } String jsonSet(String key, Path2 path, Object object); String jsonSetWithEscape(String key, Path2 path, Object object); String jsonSet(String key, Path2 path, Object object, JsonSetParams params); String jsonSetWithEscape(String key, Path2 path, Object object, JsonSetParams params); String jsonMerge(String key, Path2 path, Object object); Object jsonGet(String key); // both ver Object jsonGet(String key, Path2... paths); default List jsonMGet(String... keys) { return jsonMGet(Path2.ROOT_PATH, keys); } List jsonMGet(Path2 path, String... keys); long jsonDel(String key); // both ver long jsonDel(String key, Path2 path); long jsonClear(String key); // no test long jsonClear(String key, Path2 path); List jsonToggle(String key, Path2 path); List> jsonType(String key, Path2 path); List jsonStrAppend(String key, Path2 path, Object string); List jsonStrLen(String key, Path2 path); Object jsonNumIncrBy(String key, Path2 path, double value); List jsonArrAppend(String key, Path2 path, Object... objects); List jsonArrAppendWithEscape(String key, Path2 path, Object... objects); List jsonArrIndex(String key, Path2 path, Object scalar); List jsonArrIndexWithEscape(String key, Path2 path, Object scalar); List jsonArrInsert(String key, Path2 path, int index, Object... objects); List jsonArrInsertWithEscape(String key, Path2 path, int index, Object... objects); List jsonArrPop(String key, Path2 path); List jsonArrPop(String key, Path2 path, int index); List jsonArrLen(String key, Path2 path); List jsonArrTrim(String key, Path2 path, int start, int stop); List jsonObjLen(String key, Path2 path); List> jsonObjKeys(String key, Path2 path); List jsonDebugMemory(String key, Path2 path); } ================================================ FILE: src/main/java/redis/clients/jedis/json/commands/RedisJsonV2PipelineCommands.java ================================================ package redis.clients.jedis.json.commands; import java.util.List; import org.json.JSONArray; import redis.clients.jedis.Response; import redis.clients.jedis.json.JsonSetParams; import redis.clients.jedis.json.Path2; public interface RedisJsonV2PipelineCommands { default Response jsonSet(String key, Object object) { return jsonSet(key, Path2.ROOT_PATH, object); } default Response jsonSetWithEscape(String key, Object object) { return jsonSetWithEscape(key, Path2.ROOT_PATH, object); } default Response jsonSet(String key, Object object, JsonSetParams params) { return jsonSet(key, Path2.ROOT_PATH, object, params); } default Response jsonSetWithEscape(String key, Object object, JsonSetParams params) { return jsonSetWithEscape(key, Path2.ROOT_PATH, object, params); } Response jsonSet(String key, Path2 path, Object object); Response jsonSetWithEscape(String key, Path2 path, Object object); Response jsonSet(String key, Path2 path, Object object, JsonSetParams params); Response jsonSetWithEscape(String key, Path2 path, Object object, JsonSetParams params); Response jsonMerge(String key, Path2 path, Object object); Response jsonGet(String key); // both ver Response jsonGet(String key, Path2... paths); default Response> jsonMGet(String... keys) { return jsonMGet(Path2.ROOT_PATH, keys); } Response> jsonMGet(Path2 path, String... keys); Response jsonDel(String key); // both ver Response jsonDel(String key, Path2 path); Response jsonClear(String key); // no test Response jsonClear(String key, Path2 path); Response> jsonToggle(String key, Path2 path); Response>> jsonType(String key, Path2 path); Response> jsonStrAppend(String key, Path2 path, Object string); Response> jsonStrLen(String key, Path2 path); Response jsonNumIncrBy(String key, Path2 path, double value); Response> jsonArrAppend(String key, Path2 path, Object... objects); Response> jsonArrAppendWithEscape(String key, Path2 path, Object... objects); Response> jsonArrIndex(String key, Path2 path, Object scalar); Response> jsonArrIndexWithEscape(String key, Path2 path, Object scalar); Response> jsonArrInsert(String key, Path2 path, int index, Object... objects); Response> jsonArrInsertWithEscape(String key, Path2 path, int index, Object... objects); Response> jsonArrPop(String key, Path2 path); Response> jsonArrPop(String key, Path2 path, int index); Response> jsonArrLen(String key, Path2 path); Response> jsonArrTrim(String key, Path2 path, int start, int stop); } ================================================ FILE: src/main/java/redis/clients/jedis/json/package-info.java ================================================ /** * This package contains the classes and interfaces related to RedisJSON module. */ package redis.clients.jedis.json; ================================================ FILE: src/main/java/redis/clients/jedis/mcf/CircuitBreakerThresholdsAdapter.java ================================================ package redis.clients.jedis.mcf; import io.github.resilience4j.circuitbreaker.CircuitBreakerConfig.SlidingWindowType; import redis.clients.jedis.MultiDbConfig; /** * Adapter that disables Resilience4j's built-in circuit breaker evaluation and help delegate * threshold decisions to Jedis's custom dual-threshold logic. *

* This adapter sets maximum values for failure rate (100%) and minimum calls (Integer.MAX_VALUE) to * effectively disable Resilience4j's automatic circuit breaker transitions, allowing * {@link MultiDbConnectionProvider.Database#evaluateThresholds(boolean)} to control when the * circuit breaker opens based on both minimum failure count AND failure rate. *

* @see MultiDbConnectionProvider.Database#evaluateThresholds(boolean) */ class CircuitBreakerThresholdsAdapter { /** Maximum failure rate threshold (100%) to disable Resilience4j evaluation */ private static final float FAILURE_RATE_THRESHOLD_MAX = 100.0f; /** Always set to 100% to disable Resilience4j's rate-based evaluation */ private float failureRateThreshold; /** Always set to Integer.MAX_VALUE to disable Resilience4j's call-count evaluation */ private int minimumNumberOfCalls; /** Sliding window size from configuration for metrics collection */ private int slidingWindowSize; /** * Returns Integer.MAX_VALUE to disable Resilience4j's minimum call evaluation. * @return Integer.MAX_VALUE to prevent automatic circuit breaker evaluation */ int getMinimumNumberOfCalls() { return minimumNumberOfCalls; } /** * Returns 100% to disable Resilience4j's failure rate evaluation. * @return 100.0f to prevent automatic circuit breaker evaluation */ float getFailureRateThreshold() { return failureRateThreshold; } /** * Returns TIME_BASED sliding window type for metrics collection. * @return SlidingWindowType.TIME_BASED */ SlidingWindowType getSlidingWindowType() { return SlidingWindowType.TIME_BASED; } /** * Returns the sliding window size for metrics collection. * @return sliding window size in seconds */ int getSlidingWindowSize() { return slidingWindowSize; } /** * Creates an adapter that disables Resilience4j's circuit breaker evaluation. *

* Sets failure rate to 100% and minimum calls to Integer.MAX_VALUE to ensure Resilience4j never * automatically opens the circuit breaker. Instead, Jedis's custom {@code evaluateThresholds()} * method controls circuit breaker state based on the original configuration's dual-threshold * logic. *

* @param multiDbConfig configuration containing sliding window size */ CircuitBreakerThresholdsAdapter(MultiDbConfig multiDbConfig) { // IMPORTANT: failureRateThreshold is set to max theoretically disable Resilience4j's evaluation // and rely on our custom evaluateThresholds() logic. failureRateThreshold = FAILURE_RATE_THRESHOLD_MAX; // IMPORTANT: minimumNumberOfCalls is set to max theoretically disable Resilience4j's evaluation // and rely on our custom evaluateThresholds() logic. minimumNumberOfCalls = Integer.MAX_VALUE; slidingWindowSize = multiDbConfig.getFailureDetector().getSlidingWindowSize(); } } ================================================ FILE: src/main/java/redis/clients/jedis/mcf/ConnectionFailoverException.java ================================================ package redis.clients.jedis.mcf; import redis.clients.jedis.exceptions.JedisException; public class ConnectionFailoverException extends JedisException { public ConnectionFailoverException(String s, Exception e) { super(s, e); } } ================================================ FILE: src/main/java/redis/clients/jedis/mcf/ConnectionInitializationContext.java ================================================ package redis.clients.jedis.mcf; import java.util.Map; import redis.clients.jedis.Endpoint; import redis.clients.jedis.mcf.InitializationPolicy.Decision; /** * Context for tracking connection initialization status across multiple database endpoints. *

* This class evaluates the current state of database connections and their health checks to * determine how many connections are available, failed, or still pending. It is used in conjunction * with {@link InitializationPolicy} to decide when the multi-database connection provider is ready * to be used. *

* @author Ali Takavci * @since 7.3 */ class ConnectionInitializationContext implements InitializationPolicy.InitializationContext { private int available = 0; private int failed = 0; private int pending = 0; /** * Creates a new ConnectionInitializationContext by evaluating the current state of database * connections and their health statuses. * @param databases map of database endpoints to their Database instances * @param healthStatusManager manager for tracking health status of endpoints */ public ConnectionInitializationContext( Map databases, HealthStatusManager healthStatusManager) { for (Map.Entry entry : databases.entrySet()) { Endpoint endpoint = entry.getKey(); // Check if health checks are enabled for this endpoint if (healthStatusManager.hasHealthCheck(endpoint)) { HealthStatus status = healthStatusManager.getHealthStatus(endpoint); if (status == HealthStatus.HEALTHY) { // Health check completed successfully available++; } else if (status == HealthStatus.UNHEALTHY) { // Health check completed with failure failed++; } else { // Health check not completed yet (UNKNOWN) pending++; } } else { // No health check configured - assume available available++; } } } @Override public int getAvailableConnections() { return available; } @Override public int getFailedConnections() { return failed; } @Override public int getPendingConnections() { return pending; } /** * Evaluates whether the current connection state conforms to the given initialization policy. * @param policy the initialization policy to evaluate against * @return the decision (CONTINUE, SUCCESS, or FAIL) based on the policy evaluation */ public Decision conformsTo(InitializationPolicy policy) { return policy.evaluate(this); } @Override public String toString() { return "ConnectionInitializationContext{" + "available=" + available + ", failed=" + failed + ", pending=" + pending + '}'; } } ================================================ FILE: src/main/java/redis/clients/jedis/mcf/DatabaseSwitchEvent.java ================================================ package redis.clients.jedis.mcf; import redis.clients.jedis.Endpoint; import redis.clients.jedis.mcf.MultiDbConnectionProvider.Database; public class DatabaseSwitchEvent { private final SwitchReason reason; private final String databaseName; private final Endpoint endpoint; public DatabaseSwitchEvent(SwitchReason reason, Endpoint endpoint, Database database) { this.reason = reason; this.databaseName = database.getCircuitBreaker().getName(); this.endpoint = endpoint; } public SwitchReason getReason() { return reason; } public String getDatabaseName() { return databaseName; } public Endpoint getEndpoint() { return endpoint; } } ================================================ FILE: src/main/java/redis/clients/jedis/mcf/HealthCheck.java ================================================ package redis.clients.jedis.mcf; import redis.clients.jedis.Endpoint; public interface HealthCheck { Endpoint getEndpoint(); HealthStatus getStatus(); void stop(); void start(); /** * Get the maximum wait duration (in milliseconds) to wait for health check HealthStatus reach * stable state. *

* Transition to stable state means either HEALTHY or UNHEALTHY. UNKNOWN is not considered stable. * This is calculated based on the health check strategy's timeout, retry delay and retry count. *

* @return the maximum wait duration in milliseconds */ long getMaxWaitFor(); } ================================================ FILE: src/main/java/redis/clients/jedis/mcf/HealthCheckCollection.java ================================================ package redis.clients.jedis.mcf; import java.util.Map; import java.util.concurrent.ConcurrentHashMap; import redis.clients.jedis.Endpoint; public class HealthCheckCollection { private Map healthChecks = new ConcurrentHashMap(); public HealthCheck add(HealthCheck healthCheck) { return healthChecks.put(healthCheck.getEndpoint(), healthCheck); } public HealthCheck[] addAll(HealthCheck[] healthChecks) { HealthCheck[] old = new HealthCheck[healthChecks.length]; for (int i = 0; i < healthChecks.length; i++) { old[i] = add(healthChecks[i]); } return old; } public HealthCheck remove(Endpoint endpoint) { HealthCheck old = healthChecks.remove(endpoint); if (old != null) { old.stop(); } return old; } public HealthCheck remove(HealthCheck healthCheck) { HealthCheck[] temp = new HealthCheck[1]; healthChecks.computeIfPresent(healthCheck.getEndpoint(), (key, existing) -> { if (existing == healthCheck) { temp[0] = existing; return null; } return existing; }); return temp[0]; } public HealthCheck get(Endpoint endpoint) { return healthChecks.get(endpoint); } public void close() { for (HealthCheck healthCheck : healthChecks.values()) { healthCheck.stop(); } } } ================================================ FILE: src/main/java/redis/clients/jedis/mcf/HealthCheckImpl.java ================================================ package redis.clients.jedis.mcf; import java.util.concurrent.ExecutionException; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.Future; import java.util.concurrent.ScheduledExecutorService; import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeoutException; import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.atomic.AtomicInteger; import java.util.concurrent.atomic.AtomicReference; import java.util.function.Consumer; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import redis.clients.jedis.Endpoint; import redis.clients.jedis.annots.VisibleForTesting; import redis.clients.jedis.mcf.ProbingPolicy.ProbeContext; import redis.clients.jedis.util.JedisAsserts; public class HealthCheckImpl implements HealthCheck { static class HealthProbeContext implements ProbeContext { private final ProbingPolicy policy; private int remainingProbes; private int successes; private int fails; private boolean isCompleted; private HealthStatus result; HealthProbeContext(ProbingPolicy policy, int maxProbes) { this.policy = policy; this.remainingProbes = maxProbes; } void record(boolean success) { if (success) { this.successes++; } else { this.fails++; } remainingProbes--; ProbingPolicy.Decision decision = policy.evaluate(this); if (decision == ProbingPolicy.Decision.SUCCESS) { setCompleted(HealthStatus.HEALTHY); } else if (decision == ProbingPolicy.Decision.FAIL) { setCompleted(HealthStatus.UNHEALTHY); } } public int getRemainingProbes() { return remainingProbes; } public int getSuccesses() { return successes; } public int getFails() { return fails; } void setCompleted(HealthStatus status) { this.result = status; this.isCompleted = true; } boolean isCompleted() { return isCompleted; } HealthStatus getResult() { return result; } } private static class HealthCheckResult { private final long timestamp; private final HealthStatus status; public HealthCheckResult(long timestamp, HealthStatus status) { this.timestamp = timestamp; this.status = status; } public long getTimestamp() { return timestamp; } public HealthStatus getStatus() { return status; } } private static final Logger log = LoggerFactory.getLogger(HealthCheckImpl.class); private static AtomicInteger workerCounter = new AtomicInteger(1); private static ExecutorService workers = Executors.newCachedThreadPool(r -> { Thread t = new Thread(r, "jedis-healthcheck-worker-" + workerCounter.getAndIncrement()); t.setDaemon(true); return t; }); private Endpoint endpoint; private HealthCheckStrategy strategy; private AtomicReference resultRef = new AtomicReference(); private Consumer statusChangeCallback; private final ScheduledExecutorService scheduler; HealthCheckImpl(Endpoint endpoint, HealthCheckStrategy strategy, Consumer statusChangeCallback) { JedisAsserts.isTrue(strategy.getNumProbes() > 0, "Number of HealthCheckStrategy probes must be greater than 0"); this.endpoint = endpoint; this.strategy = strategy; this.statusChangeCallback = statusChangeCallback; resultRef.set(new HealthCheckResult(0L, HealthStatus.UNKNOWN)); scheduler = Executors.newSingleThreadScheduledExecutor(r -> { Thread t = new Thread(r, "jedis-healthcheck-" + this.endpoint); t.setDaemon(true); return t; }); } public Endpoint getEndpoint() { return endpoint; } public HealthStatus getStatus() { return resultRef.get().getStatus(); } public void start() { scheduler.scheduleAtFixedRate(this::healthCheck, 0, strategy.getInterval(), TimeUnit.MILLISECONDS); } public void stop() { strategy.close(); this.statusChangeCallback = null; scheduler.shutdown(); try { // Wait for graceful shutdown then force if required if (!scheduler.awaitTermination(1, TimeUnit.SECONDS)) { scheduler.shutdownNow(); } } catch (InterruptedException e) { // Force shutdown immediately scheduler.shutdownNow(); Thread.currentThread().interrupt(); } } private HealthStatus doHealthCheck() { HealthStatus newStatus = strategy.doHealthCheck(endpoint); log.trace("Health check completed for {} with status {}", endpoint, newStatus); return newStatus; } private void healthCheck() { long me = System.currentTimeMillis(); HealthStatus update = null; HealthProbeContext probeContext = new HealthProbeContext(strategy.getPolicy(), strategy.getNumProbes()); while (!probeContext.isCompleted()) { Future future = workers.submit(this::doHealthCheck); try { update = future.get(strategy.getTimeout(), TimeUnit.MILLISECONDS); probeContext.record(update == HealthStatus.HEALTHY); } catch (TimeoutException | ExecutionException e) { future.cancel(true); if (log.isWarnEnabled()) { log.warn(String.format("Health check timed out or failed for %s.", endpoint), e); } probeContext.record(false); } catch (InterruptedException e) {// Health check thread was interrupted future.cancel(true); Thread.currentThread().interrupt(); // Restore interrupted status log.warn(String.format("Health check interrupted for %s.", endpoint), e); // thread interrupted, stop health check process return; } if (!probeContext.isCompleted()) { try { Thread.sleep(strategy.getDelayInBetweenProbes()); } catch (InterruptedException e) { Thread.currentThread().interrupt(); // Restore interrupted status log.warn(String.format("Health check interrupted while sleeping for %s.", endpoint), e); // thread interrupted, stop health check process return; } } } safeUpdate(me, probeContext.getResult()); } /** * just to avoid to replace status with an outdated result from another healthCheck * *
   * Health Check Race Condition Prevention
   *
   * Problem: Async health checks can complete out of order, causing stale results
   * to overwrite newer ones.
   *
   * Timeline Example:
   * ─────────────────────────────────────────────────────────────────
   * T0: Start Check #1 ────────────────────┐
   * T1: Start Check #2 ──────────┐         │
   * T2:                          │         │
   * T3: Check #2 completes ──────┘         │  → status = "Healthy"
   * T4: Check #1 completes ────────────────┘  → status = "Unhealthy" (STALE!)
   *
   *
   * Result: Final status shows "Unhealthy" even though the most recent
   * check (#2) returned "Healthy"
   *
   * How Parallel Health Checks Can Occur:
   * 1. Timeout scenario: A scheduled health check times out and future.cancel(true)
   *    is called, but the actual health check operation continues running in the
   *    background thread and may complete any time later
   * 2. Scheduler overlap: If a health check takes longer than the configured
   *    interval, the next scheduled check can start before the previous one finishes
   * 3. Interruption handling: When a health check thread is interrupted, it may
   *    still complete its operation before recognizing the interruption
   *
   * Solution: Track execution order/timestamp to ignore outdated results
   * 
* * @param owner the timestamp of the health check that is updating the status * @param status the new status to set */ @VisibleForTesting void safeUpdate(long owner, HealthStatus status) { HealthCheckResult newResult = new HealthCheckResult(owner, status); AtomicBoolean wasUpdated = new AtomicBoolean(false); HealthCheckResult oldResult = resultRef.getAndUpdate(current -> { if (current.getTimestamp() < owner) { wasUpdated.set(true); return newResult; } wasUpdated.set(false); return current; }); if (wasUpdated.get() && oldResult.getStatus() != status) { log.info("Health status changed for {} from {} to {}", endpoint, oldResult.getStatus(), status); // notify listeners notifyListeners(oldResult.getStatus(), status); } } private void notifyListeners(HealthStatus oldStatus, HealthStatus newStatus) { if (statusChangeCallback != null) { statusChangeCallback.accept(new HealthStatusChangeEvent(endpoint, oldStatus, newStatus)); } } @Override public long getMaxWaitFor() { return (strategy.getTimeout() + strategy.getDelayInBetweenProbes()) * strategy.getNumProbes(); } } ================================================ FILE: src/main/java/redis/clients/jedis/mcf/HealthCheckStrategy.java ================================================ package redis.clients.jedis.mcf; import java.io.Closeable; import redis.clients.jedis.Endpoint; public interface HealthCheckStrategy extends Closeable { /** * Get the interval (in milliseconds) between health checks. * @return the interval in milliseconds */ int getInterval(); /** * Get the timeout (in milliseconds) for a health check. * @return the timeout in milliseconds */ int getTimeout(); /** * Perform the health check for the given endpoint. * @param endpoint the endpoint to check * @return the health status */ HealthStatus doHealthCheck(Endpoint endpoint); /** * Close any resources used by the health check strategy. */ default void close() { } /** * Get the number of probes for health checks to repeat. * @return the number of probes */ int getNumProbes(); /** * Get the policy for health checks. * @return the policy */ ProbingPolicy getPolicy(); /** * Get the delay (in milliseconds) between retries for failed health checks. * @return the delay in milliseconds */ int getDelayInBetweenProbes(); public static class Config { private static final int INTERVAL_DEFAULT = 5000; private static final int TIMEOUT_DEFAULT = 1000; private static final int NUM_PROBES_DEFAULT = 3; private static final int DELAY_IN_BETWEEN_PROBES_DEFAULT = 500; protected final int interval; protected final int timeout; protected final int numProbes; protected final int delayInBetweenProbes; protected final ProbingPolicy policy; public Config(int interval, int timeout, int numProbes, int delayInBetweenProbes, ProbingPolicy policy) { this.interval = interval; this.timeout = timeout; this.numProbes = numProbes; this.delayInBetweenProbes = delayInBetweenProbes; this.policy = policy; } Config(Builder builder) { this.interval = builder.interval; this.timeout = builder.timeout; this.numProbes = builder.numProbes; this.delayInBetweenProbes = builder.delayInBetweenProbes; this.policy = builder.policy; } public int getInterval() { return interval; } public int getTimeout() { return timeout; } public int getNumProbes() { return numProbes; } public int getDelayInBetweenProbes() { return delayInBetweenProbes; } public ProbingPolicy getPolicy() { return policy; } /** * Create a new Config instance with default values. * @return a new Config instance */ public static Config create() { return builder().build(); } /** * Create a new builder for HealthCheckStrategy.Config. * @return a new Builder instance */ public static Builder builder() { return new Builder<>(); } /** * Base builder for HealthCheckStrategy.Config and its subclasses. * @param the builder type (for fluent API) * @param the config type being built */ public static class Builder, C extends Config> { protected int interval = INTERVAL_DEFAULT; protected int timeout = TIMEOUT_DEFAULT; protected int numProbes = NUM_PROBES_DEFAULT; protected ProbingPolicy policy = ProbingPolicy.BuiltIn.ALL_SUCCESS; protected int delayInBetweenProbes = DELAY_IN_BETWEEN_PROBES_DEFAULT; /** * Set the interval between health checks in milliseconds. * @param interval the interval in milliseconds (default: 1000) * @return this builder */ @SuppressWarnings("unchecked") public T interval(int interval) { this.interval = interval; return (T) this; } /** * Set the timeout for health checks in milliseconds. * @param timeout the timeout in milliseconds (default: 1000) * @return this builder */ @SuppressWarnings("unchecked") public T timeout(int timeout) { this.timeout = timeout; return (T) this; } /** * Set the number of probes for health check. * @param numProbes the number of repeats (default: 3) * @return this builder */ @SuppressWarnings("unchecked") public T numProbes(int numProbes) { this.numProbes = numProbes; return (T) this; } /** * Set the policy for health checks. * @param policy the policy (default: ProbingPolicy.BuiltIn.ALL_SUCCESS) * @return this builder */ @SuppressWarnings("unchecked") public T policy(ProbingPolicy policy) { this.policy = policy; return (T) this; } /** * Set the delay between retries for failed health checks in milliseconds. * @param delayInBetweenProbes the delay in milliseconds (default: 100) * @return this builder */ @SuppressWarnings("unchecked") public T delayInBetweenProbes(int delayInBetweenProbes) { this.delayInBetweenProbes = delayInBetweenProbes; return (T) this; } /** * Build the Config instance. * @return a new Config instance */ @SuppressWarnings("unchecked") public C build() { return (C) new Config(this); } } } } ================================================ FILE: src/main/java/redis/clients/jedis/mcf/HealthStatus.java ================================================ package redis.clients.jedis.mcf; public enum HealthStatus { UNKNOWN(0x00), HEALTHY(0x01), UNHEALTHY(0x02); private final int value; HealthStatus(int val) { this.value = val; } public boolean isHealthy() { return (this.value & HEALTHY.value) != 0; } } ================================================ FILE: src/main/java/redis/clients/jedis/mcf/HealthStatusChangeEvent.java ================================================ package redis.clients.jedis.mcf; import redis.clients.jedis.Endpoint; public class HealthStatusChangeEvent { private final Endpoint endpoint; private final HealthStatus oldStatus; private final HealthStatus newStatus; public HealthStatusChangeEvent(Endpoint endpoint, HealthStatus oldStatus, HealthStatus newStatus) { this.endpoint = endpoint; this.oldStatus = oldStatus; this.newStatus = newStatus; } public Endpoint getEndpoint() { return endpoint; } public HealthStatus getOldStatus() { return oldStatus; } public HealthStatus getNewStatus() { return newStatus; } } ================================================ FILE: src/main/java/redis/clients/jedis/mcf/HealthStatusListener.java ================================================ package redis.clients.jedis.mcf; public interface HealthStatusListener { void onStatusChange(HealthStatusChangeEvent event); } ================================================ FILE: src/main/java/redis/clients/jedis/mcf/HealthStatusManager.java ================================================ package redis.clients.jedis.mcf; import java.util.List; import java.util.Map; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.CopyOnWriteArrayList; import redis.clients.jedis.Endpoint; public class HealthStatusManager { private final HealthCheckCollection healthChecks = new HealthCheckCollection(); private final List listeners = new CopyOnWriteArrayList<>(); private final Map> endpointListeners = new ConcurrentHashMap>(); public void registerListener(HealthStatusListener listener) { listeners.add(listener); } public void unregisterListener(HealthStatusListener listener) { listeners.remove(listener); } public void registerListener(Endpoint endpoint, HealthStatusListener listener) { endpointListeners.computeIfAbsent(endpoint, k -> new CopyOnWriteArrayList<>()).add(listener); } public void unregisterListener(Endpoint endpoint, HealthStatusListener listener) { endpointListeners.computeIfPresent(endpoint, (k, v) -> { v.remove(listener); return v; }); } public void notifyListeners(HealthStatusChangeEvent eventArgs) { endpointListeners.computeIfPresent(eventArgs.getEndpoint(), (k, v) -> { for (HealthStatusListener listener : v) { listener.onStatusChange(eventArgs); } return v; }); for (HealthStatusListener listener : listeners) { listener.onStatusChange(eventArgs); } } public HealthCheck add(Endpoint endpoint, HealthCheckStrategy strategy) { HealthCheck hc = new HealthCheckImpl(endpoint, strategy, this::notifyListeners); HealthCheck old = healthChecks.add(hc); hc.start(); if (old != null) { old.stop(); } return hc; } public void addAll(Endpoint[] endpoints, HealthCheckStrategy strategy) { for (Endpoint endpoint : endpoints) { add(endpoint, strategy); } } public void remove(Endpoint endpoint) { HealthCheck old = healthChecks.remove(endpoint); if (old != null) { old.stop(); } } public void removeAll(Endpoint[] endpoints) { for (Endpoint endpoint : endpoints) { remove(endpoint); } } public HealthStatus getHealthStatus(Endpoint endpoint) { HealthCheck healthCheck = healthChecks.get(endpoint); return healthCheck != null ? healthCheck.getStatus() : HealthStatus.UNKNOWN; } public boolean hasHealthCheck(Endpoint endpoint) { return healthChecks.get(endpoint) != null; } public long getMaxWaitFor(Endpoint endpoint) { HealthCheck healthCheck = healthChecks.get(endpoint); return healthCheck != null ? healthCheck.getMaxWaitFor() : 0; } public void close() { healthChecks.close(); } } ================================================ FILE: src/main/java/redis/clients/jedis/mcf/InitializationPolicy.java ================================================ package redis.clients.jedis.mcf; import redis.clients.jedis.annots.Experimental; /** * Interface for initialization policies. *

* An initialization policy determines when a multi-database connection is ready to be returned * based on the availability of individual database connections. *

*

* The policy is evaluated based on the completion status of database connection health checks, and * the decision to continue waiting, succeed, or fail is based on the number of available, pending, * and failed connections. *

* @author Ali Takavci * @since 7.3 */ @Experimental public interface InitializationPolicy { enum Decision { CONTINUE, SUCCESS, FAIL } Decision evaluate(InitializationContext context); interface InitializationContext { int getAvailableConnections(); int getFailedConnections(); int getPendingConnections(); } /** * Built-in initialization policies. *

* The policy is evaluated based on the completion status of database health checks, and the * decision to continue waiting, succeed, or fail is based on the number of available, pending, * and failed connections. *

* Built-in policies are: *
    *
  • {@link BuiltIn#ALL_AVAILABLE} - All databases need to be available
  • *
  • {@link BuiltIn#MAJORITY_AVAILABLE} - Majority of databases need to be available
  • *
  • {@link BuiltIn#ONE_AVAILABLE} - At least one database needs to be available
  • *
*/ class BuiltIn { /** * Policy that requires all databases to be available before the connection is ready. */ public static final InitializationPolicy ALL_AVAILABLE = new AllAvailablePolicy(); /** * Policy that requires a majority of databases to be available before the connection is ready. */ public static final InitializationPolicy MAJORITY_AVAILABLE = new MajorityAvailablePolicy(); /** * Policy that requires at least one database to be available before the connection is ready. */ public static final InitializationPolicy ONE_AVAILABLE = new OneAvailablePolicy(); /* * All databases need to be available. The connection is ready only when all database health * checks have completed successfully. If any connection fails, the initialization fails. If all * connections are available, initialization succeeds. Otherwise, continue waiting. */ private static class AllAvailablePolicy implements InitializationPolicy { @Override public Decision evaluate(InitializationContext ctx) { // Any failure means overall failure if (ctx.getFailedConnections() > 0) { return Decision.FAIL; } // All connections completed successfully if (ctx.getPendingConnections() == 0) { // No connections configured at all - fail if (ctx.getAvailableConnections() == 0) { return Decision.FAIL; } return Decision.SUCCESS; } return Decision.CONTINUE; } } /* * A majority of databases need to be available. The connection is ready when more than half of * the database connections are available. This means initialization can succeed early once * majority is reached, or fail early if majority becomes impossible. */ private static class MajorityAvailablePolicy implements InitializationPolicy { @Override public Decision evaluate(InitializationContext ctx) { int total = ctx.getPendingConnections() + ctx.getAvailableConnections() + ctx.getFailedConnections(); int required = (total / 2) + 1; // Early success - majority reached if (ctx.getAvailableConnections() >= required) { return Decision.SUCCESS; } // Early failure - impossible to reach majority int maxPossibleAvailable = ctx.getAvailableConnections() + ctx.getPendingConnections(); if (maxPossibleAvailable < required) { return Decision.FAIL; } // Final evaluation - no more pending if (ctx.getPendingConnections() == 0) { return ctx.getAvailableConnections() >= required ? Decision.SUCCESS : Decision.FAIL; } return Decision.CONTINUE; } } /* * At least one database needs to be available. The connection is ready as soon as any database * connection is available. Initialization fails only if all connections fail. */ private static class OneAvailablePolicy implements InitializationPolicy { @Override public Decision evaluate(InitializationContext ctx) { // Any success means overall success if (ctx.getAvailableConnections() > 0) { return Decision.SUCCESS; } // All connections completed with failures if (ctx.getPendingConnections() == 0) { return Decision.FAIL; } return Decision.CONTINUE; } } } } ================================================ FILE: src/main/java/redis/clients/jedis/mcf/JedisFailoverException.java ================================================ package redis.clients.jedis.mcf; import redis.clients.jedis.exceptions.JedisConnectionException; /** * Exception thrown when a failover attempt fails due to lack of available/healthy databases. *

* This exception itself is not thrown, see the child exceptions for more details. *

* @see JedisFailoverException.JedisPermanentlyNotAvailableException * @see JedisFailoverException.JedisTemporarilyNotAvailableException */ public class JedisFailoverException extends JedisConnectionException { private static final String MESSAGE = "Database endpoint could not failover since the MultiDbConfig was not " + "provided with an additional database endpoint according to its prioritized sequence. " + "If applicable, consider falling back OR restarting with an available database endpoint"; public JedisFailoverException(String s) { super(s); } public JedisFailoverException() { super(MESSAGE); } /** * Exception thrown when a failover attempt fails due to lack of available/healthy databases, and * the max number of failover attempts has been exceeded. And there is still no healthy databases. *

* See the configuration properties * {@link redis.clients.jedis.MultiDbConfig#maxNumFailoverAttempts} and * {@link redis.clients.jedis.MultiDbConfig#delayInBetweenFailoverAttempts} for more details. */ public static class JedisPermanentlyNotAvailableException extends JedisFailoverException { public JedisPermanentlyNotAvailableException(String s) { super(s); } public JedisPermanentlyNotAvailableException() { super(); } } /** * Exception thrown when a failover attempt fails due to lack of available/healthy databases, but * the max number of failover attempts has not been exceeded yet. Though there is no healthy * database including the selected/current one, given configuration suggests that it should be a * temporary condition and it is possible that there will be a healthy database available. *

* See the configuration properties * {@link redis.clients.jedis.MultiDbConfig#maxNumFailoverAttempts} and * {@link redis.clients.jedis.MultiDbConfig#delayInBetweenFailoverAttempts} for more details. */ public static class JedisTemporarilyNotAvailableException extends JedisFailoverException { public JedisTemporarilyNotAvailableException(String s) { super(s); } public JedisTemporarilyNotAvailableException() { super(); } } } ================================================ FILE: src/main/java/redis/clients/jedis/mcf/LagAwareStrategy.java ================================================ package redis.clients.jedis.mcf; import java.time.Duration; import java.util.List; import java.util.function.Supplier; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import redis.clients.jedis.RedisCredentials; import redis.clients.jedis.SslOptions; import redis.clients.jedis.exceptions.JedisException; import redis.clients.jedis.Endpoint; public class LagAwareStrategy implements HealthCheckStrategy { private static final Logger log = LoggerFactory.getLogger(LagAwareStrategy.class); private final Config config; private final RedisRestAPI redisRestAPI; private String bdbId; public LagAwareStrategy(Config config) { this.config = config; this.redisRestAPI = new RedisRestAPI(config.getRestEndpoint(), config.getCredentialsSupplier(), config.getTimeout(), config.getSslOptions()); } @Override public int getInterval() { return config.interval; } @Override public int getTimeout() { return config.timeout; } @Override public int getNumProbes() { return config.getNumProbes(); } @Override public ProbingPolicy getPolicy() { return config.getPolicy(); } @Override public int getDelayInBetweenProbes() { return config.getDelayInBetweenProbes(); } @Override public HealthStatus doHealthCheck(Endpoint endpoint) { try { String bdb = bdbId; if (bdb == null) { // Try to find BDB that matches the database host String dbHost = endpoint.getHost(); List bdbs = redisRestAPI.getBdbs(); RedisRestAPI.BdbInfo matchingBdb = RedisRestAPI.BdbInfo.findMatchingBdb(bdbs, dbHost); if (matchingBdb == null) { String msg = String.format("No BDB found matching host '%s' for health check", dbHost); log.warn(msg); throw new JedisException(msg); } else { bdb = matchingBdb.getUid(); log.debug("Found matching BDB '{}' for host '{}'", bdb, dbHost); bdbId = bdb; } } if (this.config.isExtendedCheckEnabled()) { // Use extended check with lag validation if (redisRestAPI.checkBdbAvailability(bdb, true, this.config.getAvailabilityLagTolerance().toMillis())) { return HealthStatus.HEALTHY; } } else { // Use standard datapath validation only if (redisRestAPI.checkBdbAvailability(bdb, false)) { return HealthStatus.HEALTHY; } } } catch (Exception e) { log.error("Error while checking database availability", e); bdbId = null; throw new JedisException("Error while checking availability", e); } return HealthStatus.UNHEALTHY; } public static class Config extends HealthCheckStrategy.Config { public static final boolean EXTENDED_CHECK_DEFAULT = true; public static final Duration AVAILABILITY_LAG_TOLERANCE_DEFAULT = Duration.ofMillis(5000); private final Endpoint restEndpoint; private final Supplier credentialsSupplier; // SSL configuration for HTTPS connections to Redis Enterprise REST API private final SslOptions sslOptions; // Maximum acceptable lag in milliseconds (default: 5000); private final Duration availability_lag_tolerance; // Enable extended lag checking (default: true - performs lag validation in addition to standard // datapath // validation ) private final boolean extendedCheckEnabled; public Config(Endpoint restEndpoint, Supplier credentialsSupplier) { this(builder(restEndpoint, credentialsSupplier) .availabilityLagTolerance(AVAILABILITY_LAG_TOLERANCE_DEFAULT) .extendedCheckEnabled(EXTENDED_CHECK_DEFAULT)); } private Config(ConfigBuilder builder) { super(builder); this.restEndpoint = builder.restEndpoint; this.credentialsSupplier = builder.credentialsSupplier; this.sslOptions = builder.sslOptions; this.availability_lag_tolerance = builder.availabilityLagTolerance; this.extendedCheckEnabled = builder.extendedCheckEnabled; } public Endpoint getRestEndpoint() { return restEndpoint; } public Supplier getCredentialsSupplier() { return credentialsSupplier; } public SslOptions getSslOptions() { return sslOptions; } public Duration getAvailabilityLagTolerance() { return availability_lag_tolerance; } public boolean isExtendedCheckEnabled() { return extendedCheckEnabled; } /** * Create a new builder for LagAwareStrategy.Config. * @param restEndpoint the Redis Enterprise REST API endpoint * @param credentialsSupplier the credentials supplier * @return a new ConfigBuilder instance */ public static ConfigBuilder builder(Endpoint restEndpoint, Supplier credentialsSupplier) { return new ConfigBuilder(restEndpoint, credentialsSupplier); } /** * Use {@link LagAwareStrategy.Config#builder(Endpoint, Supplier)} instead. * @return a new Builder instance */ public static ConfigBuilder builder() { throw new UnsupportedOperationException( "Endpoint and credentials are required to build LagAwareStrategy.Config."); } /** * Create a new Config instance with default values. *

* Extended checks like lag validation is enabled by default. With a default lag tolerance of * 100ms. To perform only standard datapath validation, use * {@link #databaseAvailability(Endpoint, Supplier)}. To configure a custom lag tolerance, use * {@link #lagAwareWithTolerance(Endpoint, Supplier, Duration)} *

*/ public static Config create(Endpoint restEndpoint, Supplier credentialsSupplier) { return new ConfigBuilder(restEndpoint, credentialsSupplier).build(); } /** * Perform standard datapath validation only. *

* Extended checks like lag validation is disabled by default. To enable extended checks, use * {@link #lagAware(Endpoint, Supplier)} or * {@link #lagAwareWithTolerance(Endpoint, Supplier, Duration)} *

*/ public static Config databaseAvailability(Endpoint restEndpoint, Supplier credentialsSupplier) { return new ConfigBuilder(restEndpoint, credentialsSupplier).extendedCheckEnabled(false) .build(); } /** * Perform standard datapath validation and lag validation using the default lag tolerance. *

* To configure a custom lag tolerance, use * {@link #lagAwareWithTolerance(Endpoint, Supplier, Duration)} *

*/ public static Config lagAware(Endpoint restEndpoint, Supplier credentialsSupplier) { return new ConfigBuilder(restEndpoint, credentialsSupplier).extendedCheckEnabled(true) .build(); } /** * Perform standard datapath validation and lag validation using the specified lag tolerance. */ public static Config lagAwareWithTolerance(Endpoint restEndpoint, Supplier credentialsSupplier, Duration availabilityLagTolerance) { return new ConfigBuilder(restEndpoint, credentialsSupplier).extendedCheckEnabled(true) .availabilityLagTolerance(availabilityLagTolerance).build(); } /** * Builder for LagAwareStrategy.Config. */ public static class ConfigBuilder extends HealthCheckStrategy.Config.Builder { private final Endpoint restEndpoint; private final Supplier credentialsSupplier; // SSL configuration for HTTPS connections private SslOptions sslOptions; // Maximum acceptable lag in milliseconds (default: 100); private Duration availabilityLagTolerance = AVAILABILITY_LAG_TOLERANCE_DEFAULT; // Enable extended lag checking private boolean extendedCheckEnabled = EXTENDED_CHECK_DEFAULT; private ConfigBuilder(Endpoint restEndpoint, Supplier credentialsSupplier) { this.restEndpoint = restEndpoint; this.credentialsSupplier = credentialsSupplier; } /** * Set SSL options for HTTPS connections to Redis Enterprise REST API. This allows * configuration of custom truststore, keystore, and SSL parameters for secure connections. * @param sslOptions the SSL configuration options * @return this builder */ public ConfigBuilder sslOptions(SslOptions sslOptions) { this.sslOptions = sslOptions; return this; } /** * Set the maximum acceptable lag in milliseconds. * @param availabilityLagTolerance the lag tolerance in milliseconds (default: 100) * @return this builder */ public ConfigBuilder availabilityLagTolerance(Duration availabilityLagTolerance) { this.availabilityLagTolerance = availabilityLagTolerance; return this; } /** * Enable extended lag checking. When enabled, performs lag validation in addition to standard * datapath validation. When disabled performs only standard datapath validation - all slots * are available. * @param extendedCheckEnabled true to enable extended lag checking (default: false) * @return this builder */ public ConfigBuilder extendedCheckEnabled(boolean extendedCheckEnabled) { this.extendedCheckEnabled = extendedCheckEnabled; return this; } /** * Build the Config instance. * @return a new Config instance */ @Override public Config build() { return new Config(this); } } } } ================================================ FILE: src/main/java/redis/clients/jedis/mcf/MultiDbCommandExecutor.java ================================================ package redis.clients.jedis.mcf; import io.github.resilience4j.circuitbreaker.CircuitBreaker.State; import io.github.resilience4j.decorators.Decorators; import io.github.resilience4j.decorators.Decorators.DecorateSupplier; import redis.clients.jedis.CommandObject; import redis.clients.jedis.Connection; import redis.clients.jedis.annots.Experimental; import redis.clients.jedis.exceptions.JedisConnectionException; import redis.clients.jedis.executors.CommandExecutor; import redis.clients.jedis.mcf.MultiDbConnectionProvider.Database; /** * @author Allen Terleto (aterleto) *

* CommandExecutor with built-in retry, circuit-breaker, and failover to another database * endpoint. With this executor users can seamlessly failover to Disaster Recovery (DR), * Backup, and Active-Active cluster(s) by using simple configuration which is passed * through from Resilience4j - https://resilience4j.readme.io/docs *

*/ @Experimental public class MultiDbCommandExecutor extends MultiDbFailoverBase implements CommandExecutor { public MultiDbCommandExecutor(MultiDbConnectionProvider provider) { super(provider); } @Override public T executeCommand(CommandObject commandObject) { Database database = provider.getDatabase(); // Pass this by reference for thread safety DecorateSupplier supplier = Decorators .ofSupplier(() -> this.handleExecuteCommand(commandObject, database)); supplier.withCircuitBreaker(database.getCircuitBreaker()); supplier.withRetry(database.getRetry()); supplier.withFallback(provider.getFallbackExceptionList(), e -> this.handleDatabaseFailover(commandObject, database)); try { return supplier.decorate().get(); } catch (Exception e) { if (database.getCircuitBreaker().getState() == State.OPEN && isActiveDatabase(database)) { databaseFailover(database); } throw e; } } /** * Functional interface wrapped in retry and circuit breaker logic to handle happy path scenarios */ private T handleExecuteCommand(CommandObject commandObject, Database database) { Connection connection; try { connection = database.getConnection(); } catch (JedisConnectionException e) { provider.assertOperability(); throw e; } try { return connection.executeCommand(commandObject); } catch (Exception e) { if (database.retryOnFailover() && !isActiveDatabase(database) && isCircuitBreakerTrackedException(e, database)) { throw new ConnectionFailoverException( "Command failed during failover: " + database.getCircuitBreaker().getName(), e); } throw e; } finally { connection.close(); } } /** * Functional interface wrapped in retry and circuit breaker logic to handle open circuit breaker * failure scenarios */ private T handleDatabaseFailover(CommandObject commandObject, Database database) { databaseFailover(database); // Recursive call to the initiating method so the operation can be retried on the next database // connection return executeCommand(commandObject); } } ================================================ FILE: src/main/java/redis/clients/jedis/mcf/MultiDbConnectionProvider.java ================================================ package redis.clients.jedis.mcf; import io.github.resilience4j.circuitbreaker.CircuitBreaker; import io.github.resilience4j.circuitbreaker.CircuitBreaker.Metrics; import io.github.resilience4j.circuitbreaker.CircuitBreaker.State; import io.github.resilience4j.circuitbreaker.CircuitBreakerConfig; import io.github.resilience4j.circuitbreaker.CircuitBreakerRegistry; import io.github.resilience4j.core.IntervalFunction; import io.github.resilience4j.retry.Retry; import io.github.resilience4j.retry.RetryConfig; import io.github.resilience4j.retry.RetryRegistry; import java.util.Collections; import java.util.Comparator; import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Set; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.atomic.AtomicInteger; import java.util.concurrent.atomic.AtomicLong; import java.util.stream.Collectors; import java.util.concurrent.locks.Lock; import java.util.concurrent.locks.ReentrantLock; import java.util.concurrent.ScheduledExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.TimeUnit; import java.util.function.Consumer; import java.util.function.Predicate; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import redis.clients.jedis.*; import redis.clients.jedis.MultiDbConfig.DatabaseConfig; import redis.clients.jedis.annots.Experimental; import redis.clients.jedis.annots.VisibleForTesting; import redis.clients.jedis.exceptions.JedisConnectionException; import redis.clients.jedis.exceptions.JedisException; import redis.clients.jedis.exceptions.JedisValidationException; import redis.clients.jedis.mcf.InitializationPolicy.Decision; import redis.clients.jedis.mcf.JedisFailoverException.*; import redis.clients.jedis.providers.ConnectionProvider; import redis.clients.jedis.MultiDbConfig.StrategySupplier; import redis.clients.jedis.util.JedisAsserts; import redis.clients.jedis.util.Pool; /** * @author Allen Terleto (aterleto) *

* ConnectionProvider which supports multiple database endpoints each with their own * isolated connection pool. With this ConnectionProvider users can seamlessly failover to * Disaster Recovery (DR), Backup, and Active-Active database(s) by using simple * configuration which is passed through from Resilience4j - * docs *

* Support for manual failback is provided by way of {@link #setActiveDatabase(Endpoint)} *

*/ @Experimental public class MultiDbConnectionProvider implements ConnectionProvider { private final Logger log = LoggerFactory.getLogger(getClass()); /** * Ordered map of database. Users can move down (failover) or (up) failback the map depending on * their availability and order. */ private final Map databaseMap = new ConcurrentHashMap<>(); /** * Indicates the actively used database endpoint (connection pool) amongst the pre-configured list * which were provided at startup via the MultiDbConfig. All traffic will be routed with this * database */ private volatile Database activeDatabase; private final Lock activeDatabaseChangeLock = new ReentrantLock(true); /** * Functional interface for listening to database switch events. The event args contain the reason * for the switch, the endpoint, and the database. */ private Consumer databaseSwitchListener; private final List> fallbackExceptionList; private final HealthStatusManager healthStatusManager = new HealthStatusManager(); // Flag to control when handleHealthStatusChange should process events (only after initialization) private volatile boolean initializationComplete = false; // Failback mechanism fields private static final AtomicInteger failbackThreadCounter = new AtomicInteger(1); private final ScheduledExecutorService failbackScheduler = Executors .newSingleThreadScheduledExecutor(r -> { Thread t = new Thread(r, "jedis-failback-" + failbackThreadCounter.getAndIncrement()); t.setDaemon(true); return t; }); // Store retry and circuit breaker configs for dynamic database addition/removal private final RetryConfig retryConfig; private final CircuitBreakerConfig circuitBreakerConfig; private final MultiDbConfig multiDbConfig; private final AtomicLong failoverFreezeUntil = new AtomicLong(0); private final AtomicInteger failoverAttemptCount = new AtomicInteger(0); public MultiDbConnectionProvider(MultiDbConfig multiDbConfig) { if (multiDbConfig == null) throw new JedisValidationException( "MultiDbConfig must not be NULL for MultiDbConnectionProvider"); this.multiDbConfig = multiDbConfig; ////////////// Configure Retry //////////////////// MultiDbConfig.RetryConfig commandRetry = multiDbConfig.getCommandRetry(); this.retryConfig = buildRetryConfig(commandRetry); ////////////// Configure Circuit Breaker //////////////////// MultiDbConfig.CircuitBreakerConfig failureDetector = multiDbConfig.getFailureDetector(); this.circuitBreakerConfig = buildCircuitBreakerConfig(failureDetector, multiDbConfig); ////////////// Configure Database Map //////////////////// DatabaseConfig[] databaseConfigs = multiDbConfig.getDatabaseConfigs(); // Now add databases - health checks will start but events will be queued for (DatabaseConfig config : databaseConfigs) { addDatabaseInternal(multiDbConfig, config); } // Initialize StatusTracker for waiting on health check results StatusTracker statusTracker = new StatusTracker(healthStatusManager); // Wait for initial health check results and select active database based on weights activeDatabase = waitForInitializationPolicy(statusTracker); // Mark initialization as complete - handleHealthStatusChange can now process events initializationComplete = true; Database temp = activeDatabase; if (!temp.isHealthy()) { // Race condition: Direct assignment to 'activeDatabase' is not thread safe because // 'onHealthStatusChange' may execute concurrently once 'initializationComplete' // is set to true. // Simple rule is to never assign value of 'activeDatabase' outside of // 'activeDatabaseChangeLock' once the 'initializationComplete' is done. waitForInitializationPolicy(statusTracker); switchToHealthyDatabase(SwitchReason.HEALTH_CHECK, temp); } this.fallbackExceptionList = multiDbConfig.getFallbackExceptionList(); // Start periodic failback checker if (multiDbConfig.isFailbackSupported()) { long failbackInterval = multiDbConfig.getFailbackCheckInterval(); failbackScheduler.scheduleAtFixedRate(this::periodicFailbackCheck, failbackInterval, failbackInterval, TimeUnit.MILLISECONDS); } } /** * Builds a Resilience4j RetryConfig from Jedis MultiDbConfig.RetryConfig. * @param commandRetry the Jedis retry configuration * @return configured Resilience4j RetryConfig */ private RetryConfig buildRetryConfig(redis.clients.jedis.MultiDbConfig.RetryConfig commandRetry) { RetryConfig.Builder builder = RetryConfig.custom(); builder.maxAttempts(commandRetry.getMaxAttempts()); builder.intervalFunction(IntervalFunction.ofExponentialBackoff(commandRetry.getWaitDuration(), commandRetry.getExponentialBackoffMultiplier())); builder.failAfterMaxAttempts(false); // JedisConnectionException will be thrown builder.retryExceptions(commandRetry.getIncludedExceptionList().stream().toArray(Class[]::new)); List ignoreExceptions = commandRetry.getIgnoreExceptionList(); if (ignoreExceptions != null) { builder.ignoreExceptions(ignoreExceptions.stream().toArray(Class[]::new)); } return builder.build(); } /** * Builds Resilience4j CircuitBreakerConfig from Jedis CircuitBreakerConfig. * @param failureDetector the Jedis circuit breaker configuration * @param multiDbConfig the multi-database configuration (for adapter) * @return configured Resilience4j CircuitBreakerConfig */ private CircuitBreakerConfig buildCircuitBreakerConfig( MultiDbConfig.CircuitBreakerConfig failureDetector, MultiDbConfig multiDbConfig) { CircuitBreakerConfig.Builder builder = CircuitBreakerConfig.custom(); CircuitBreakerThresholdsAdapter adapter = new CircuitBreakerThresholdsAdapter(multiDbConfig); builder.minimumNumberOfCalls(adapter.getMinimumNumberOfCalls()); builder.failureRateThreshold(adapter.getFailureRateThreshold()); builder.slidingWindowSize(adapter.getSlidingWindowSize()); builder.slidingWindowType(adapter.getSlidingWindowType()); builder.recordExceptions( failureDetector.getIncludedExceptionList().stream().toArray(Class[]::new)); builder.automaticTransitionFromOpenToHalfOpenEnabled(false); // State transitions are forced. // No half open states are used List ignoreExceptions = failureDetector.getIgnoreExceptionList(); if (ignoreExceptions != null) { builder.ignoreExceptions(ignoreExceptions.stream().toArray(Class[]::new)); } return builder.build(); } /** * Adds a new database endpoint to the provider. * @param databaseConfig the configuration for the new database * @throws JedisValidationException if the endpoint already exists */ public void add(DatabaseConfig databaseConfig) { if (databaseConfig == null) { throw new JedisValidationException("DatabaseConfig must not be null"); } Endpoint endpoint = databaseConfig.getEndpoint(); if (databaseMap.containsKey(endpoint)) { throw new JedisValidationException( "Endpoint " + endpoint + " already exists in the provider"); } activeDatabaseChangeLock.lock(); try { addDatabaseInternal(multiDbConfig, databaseConfig); } finally { activeDatabaseChangeLock.unlock(); } } /** * Removes a database endpoint from the provider. * @param endpoint the endpoint to remove * @throws JedisValidationException if the endpoint doesn't exist or is the last remaining * endpoint */ public void remove(Endpoint endpoint) { if (endpoint == null) { throw new JedisValidationException("Endpoint must not be null"); } if (!databaseMap.containsKey(endpoint)) { throw new JedisValidationException( "Endpoint " + endpoint + " does not exist in the provider"); } if (databaseMap.size() < 2) { throw new JedisValidationException("Cannot remove the last remaining endpoint"); } log.debug("Removing endpoint {}", endpoint); Map.Entry notificationData = null; activeDatabaseChangeLock.lock(); try { Database databaseToRemove = databaseMap.get(endpoint); boolean isActiveDatabase = (activeDatabase == databaseToRemove); if (isActiveDatabase) { log.info("Active database is being removed. Finding a new active database..."); Map.Entry candidate = findWeightedHealthyDatabaseToIterate( databaseToRemove); if (candidate != null) { Database selectedDatabase = candidate.getValue(); if (setActiveDatabase(selectedDatabase, true)) { log.info("New active database set to {}", candidate.getKey()); notificationData = candidate; } } else { throw new JedisException( "Database can not be removed due to no healthy database available to switch!"); } } // Remove from health status manager first healthStatusManager.unregisterListener(endpoint, this::onHealthStatusChange); healthStatusManager.remove(endpoint); // Remove from database map databaseMap.remove(endpoint); // Close the database resources if (databaseToRemove != null) { databaseToRemove.setDisabled(true); databaseToRemove.close(); } } finally { activeDatabaseChangeLock.unlock(); } if (notificationData != null) { onDatabaseSwitch(SwitchReason.FORCED, notificationData.getKey(), notificationData.getValue()); } } /** * Internal method to add a database configuration. This method is not thread-safe and should be * called within appropriate locks. */ private void addDatabaseInternal(MultiDbConfig multiDbConfig, DatabaseConfig config) { if (databaseMap.containsKey(config.getEndpoint())) { throw new JedisValidationException( "Endpoint " + config.getEndpoint() + " already exists in the provider"); } String databaseId = "database:" + config.getEndpoint(); Retry retry = RetryRegistry.of(retryConfig).retry(databaseId); Retry.EventPublisher retryPublisher = retry.getEventPublisher(); retryPublisher.onRetry(event -> log.warn(String.valueOf(event))); retryPublisher.onError(event -> log.error(String.valueOf(event))); CircuitBreaker circuitBreaker = CircuitBreakerRegistry.of(circuitBreakerConfig) .circuitBreaker(databaseId); CircuitBreaker.EventPublisher circuitBreakerEventPublisher = circuitBreaker.getEventPublisher(); circuitBreakerEventPublisher.onCallNotPermitted(event -> log.error(String.valueOf(event))); circuitBreakerEventPublisher.onError(event -> log.error(String.valueOf(event))); circuitBreakerEventPublisher.onFailureRateExceeded(event -> log.error(String.valueOf(event))); circuitBreakerEventPublisher.onSlowCallRateExceeded(event -> log.error(String.valueOf(event))); TrackingConnectionPool pool = TrackingConnectionPool.builder() .hostAndPort(hostPort(config.getEndpoint())).clientConfig(config.getJedisClientConfig()) .poolConfig(config.getConnectionPoolConfig()).build(); Database database; StrategySupplier strategySupplier = config.getHealthCheckStrategySupplier(); if (strategySupplier != null) { HealthCheckStrategy hcs = strategySupplier.get(hostPort(config.getEndpoint()), config.getJedisClientConfig()); // Register listeners BEFORE adding databases to avoid missing events healthStatusManager.registerListener(config.getEndpoint(), this::onHealthStatusChange); HealthCheck hc = healthStatusManager.add(config.getEndpoint(), hcs); database = new Database(config.getEndpoint(), pool, retry, hc, circuitBreaker, config.getWeight(), multiDbConfig); } else { database = new Database(config.getEndpoint(), pool, retry, circuitBreaker, config.getWeight(), multiDbConfig); } databaseMap.put(config.getEndpoint(), database); // this is the place where we listen tracked errors and check if // thresholds are exceeded for the database circuitBreakerEventPublisher.onError(event -> { database.evaluateThresholds(false); }); } private HostAndPort hostPort(Endpoint endpoint) { return new HostAndPort(endpoint.getHost(), endpoint.getPort()); } /** * Handles health status changes for databases. This method is called by the health status manager * when the health status of a database changes. */ @VisibleForTesting void onHealthStatusChange(HealthStatusChangeEvent eventArgs) { Endpoint endpoint = eventArgs.getEndpoint(); HealthStatus newStatus = eventArgs.getNewStatus(); log.debug("Health status changed for {} from {} to {}", endpoint, eventArgs.getOldStatus(), newStatus); Database databaseWithHealthChange = databaseMap.get(endpoint); if (databaseWithHealthChange == null) return; if (initializationComplete) { if (!newStatus.isHealthy() && databaseWithHealthChange == activeDatabase) { databaseWithHealthChange.setGracePeriod(); switchToHealthyDatabase(SwitchReason.HEALTH_CHECK, databaseWithHealthChange); } } } /** * Waits for initial health check results and selects the first healthy database based on weight * priority. Uses the configured initialization policy to determine when enough databases are * available. * @param statusTracker the status tracker to use for waiting on health check results * @return the first healthy database found, ordered by weight (highest first) * @throws JedisConnectionException (or JedisValidationException in unlikely cases) if * initialization fails according to the policy */ @VisibleForTesting Database waitForInitializationPolicy(StatusTracker statusTracker) { InitializationPolicy policy = multiDbConfig.getInitializationPolicy(); log.debug("Waiting for initialization policy {} to complete for {} configured databases", policy.getClass().getSimpleName(), databaseMap.size()); // Evaluate immediately with the current statuses ConnectionInitializationContext ctx = new ConnectionInitializationContext(databaseMap, healthStatusManager); Decision decision = ctx.conformsTo(policy); log.debug("Initial policy evaluation: {} with context: {}", decision, ctx); if (decision == Decision.FAIL) { throw new JedisConnectionException( "Initialization failed due to initialization policy: " + ctx); } // Sort databases by weight in descending order List> sortedDatabases = databaseMap.entrySet().stream() .sorted(Map.Entry. comparingByValue( Comparator.comparing(Database::getWeight).reversed())) .collect(Collectors.toList()); // Check databases in weight order for (Map.Entry entry : sortedDatabases) { Endpoint endpoint = entry.getKey(); Database database = entry.getValue(); log.info("Evaluating database {} (weight: {})", endpoint, database.getWeight()); // Check if health checks are enabled for this endpoint if (healthStatusManager.hasHealthCheck(endpoint)) { log.info("Health checks enabled for {}, waiting for result", endpoint); // Wait for this database's health status to be determined statusTracker.waitForHealthStatus(endpoint); } else { // No health check configured - assume healthy log.debug("No health check configured for database {}, defaulting to HEALTHY", endpoint); } ConnectionInitializationContext evalCtx = new ConnectionInitializationContext(databaseMap, healthStatusManager); Decision d = evalCtx.conformsTo(policy); log.debug("Policy evaluation after {}: {}", endpoint, d); if (d == Decision.SUCCESS) { return selectBestAvailableDatabase(sortedDatabases); } if (d == Decision.FAIL) { throw new JedisConnectionException( "Initialization failed due to initialization policy: " + evalCtx); } // else CONTINUE -> move to the next pending endpoint } // All databases are unhealthy throw new JedisConnectionException( "All configured databases are unhealthy. Cannot initialize MultiDbConnectionProvider."); } /** * Selects the best available (healthy) database based on weight priority. * @param sortedDatabases the list of databases sorted by weight in descending order * @return the highest-weighted healthy database * @throws JedisConnectionException if no healthy database is available */ private Database selectBestAvailableDatabase( List> sortedDatabases) { log.info("Selecting initial database from {} configured databases", sortedDatabases.size()); // Select first healthy database in weight order for (Map.Entry entry : sortedDatabases) { Endpoint endpoint = entry.getKey(); Database database = entry.getValue(); HealthStatus status; // Check if health checks are enabled for this endpoint if (healthStatusManager.hasHealthCheck(endpoint)) { status = healthStatusManager.getHealthStatus(endpoint); } else { // No health check configured - assume healthy log.info("No health check configured for database {}, defaulting to HEALTHY", endpoint); status = HealthStatus.HEALTHY; } if (status.isHealthy()) { log.info("Found healthy database: {} (weight: {})", endpoint, database.getWeight()); return database; } else { log.info("Database {} is unhealthy, trying next database", endpoint); } } // No healthy database found (should not happen if policy succeeded) throw new JedisConnectionException( "No healthy database available after initialization policy succeeded."); } /** * Periodic failback checker - runs at configured intervals to check for failback opportunities */ @VisibleForTesting void periodicFailbackCheck() { try { // Find the best candidate database for failback Map.Entry bestCandidate = null; float bestWeight = activeDatabase.getWeight(); for (Map.Entry entry : databaseMap.entrySet()) { Database database = entry.getValue(); // Skip if this is already the active database if (database == activeDatabase) { continue; } // Skip if database is not healthy if (!database.isHealthy()) { continue; } // This database is a valid candidate if (database.getWeight() > bestWeight) { bestCandidate = entry; bestWeight = database.getWeight(); } } // Perform failback if we found a better candidate if (bestCandidate != null) { Database selectedDatabase = bestCandidate.getValue(); log.info("Performing failback from {} to {} (higher weight database available)", activeDatabase.getCircuitBreaker().getName(), selectedDatabase.getCircuitBreaker().getName()); if (setActiveDatabase(selectedDatabase, true)) { onDatabaseSwitch(SwitchReason.FAILBACK, bestCandidate.getKey(), selectedDatabase); } } } catch (Exception e) { log.error("Error during periodic failback check", e); } } Endpoint switchToHealthyDatabase(SwitchReason reason, Database iterateFrom) { Map.Entry databaseToIterate = findWeightedHealthyDatabaseToIterate( iterateFrom); if (databaseToIterate == null) { // throws exception anyway since not able to iterate handleNoHealthyDatabase(); } Database database = databaseToIterate.getValue(); boolean changed = setActiveDatabase(database, false); if (!changed) return null; failoverAttemptCount.set(0); onDatabaseSwitch(reason, databaseToIterate.getKey(), database); return databaseToIterate.getKey(); } private void handleNoHealthyDatabase() { int max = multiDbConfig.getMaxNumFailoverAttempts(); log.error("No healthy database available to switch to"); if (failoverAttemptCount.get() > max) { throw new JedisPermanentlyNotAvailableException(); } int currentAttemptCount = markAsFreeze() ? failoverAttemptCount.incrementAndGet() : failoverAttemptCount.get(); if (currentAttemptCount > max) { throw new JedisPermanentlyNotAvailableException(); } throw new JedisTemporarilyNotAvailableException(); } private boolean markAsFreeze() { long until = failoverFreezeUntil.get(); long now = System.currentTimeMillis(); if (until <= now) { long nextUntil = now + multiDbConfig.getDelayInBetweenFailoverAttempts(); if (failoverFreezeUntil.compareAndSet(until, nextUntil)) { return true; } } return false; } /** * Asserts that the active database is operable. If not, throws an exception. *

* This method is called by the circuit breaker command executor before executing a command. * @throws JedisPermanentlyNotAvailableException if the there is no operable database and the max * number of failover attempts has been exceeded. * @throws JedisTemporarilyNotAvailableException if the there is no operable database and the max * number of failover attempts has not been exceeded. */ @VisibleForTesting public void assertOperability() { Database current = activeDatabase; if (!current.isHealthy() && !this.canIterateFrom(current)) { handleNoHealthyDatabase(); } } private static Comparator> maxByWeight = Map.Entry . comparingByValue(Comparator.comparing(Database::getWeight)); private static Predicate> filterByHealth = c -> c.getValue() .isHealthy(); private Map.Entry findWeightedHealthyDatabaseToIterate(Database iterateFrom) { return databaseMap.entrySet().stream().filter(filterByHealth) .filter(entry -> entry.getValue() != iterateFrom).max(maxByWeight).orElse(null); } /** * Design decision was made to defer responsibility for cross-replication validation to the user. * Alternatively there was discussion to handle cross-database replication validation by setting a * key/value pair per hashslot in the active connection (with a TTL) and subsequently reading it * from the target connection. */ public void validateTargetConnection(Endpoint endpoint) { Database database = databaseMap.get(endpoint); validateTargetConnection(database); } private void validateTargetConnection(Database database) { CircuitBreaker circuitBreaker = database.getCircuitBreaker(); State originalState = circuitBreaker.getState(); try { // Transitions the state machine to a CLOSED state, allowing state transition, metrics and // event publishing. Safe since the activeDatabase has not yet been changed and therefore no // traffic will be routed yet circuitBreaker.transitionToClosedState(); try (Connection targetConnection = database.getConnection()) { targetConnection.ping(); } } catch (Exception e) { // If the original state was FORCED_OPEN, then transition it back which stops state // transition, metrics and // event publishing if (State.FORCED_OPEN.equals(originalState)) circuitBreaker.transitionToForcedOpenState(); throw new JedisValidationException(circuitBreaker.getName() + " failed to connect. Please check configuration and try again.", e); } } /** * Returns the set of all configured endpoints. * @return the set of all configured endpoints */ public Set getEndpoints() { return new HashSet<>(databaseMap.keySet()); } public void setActiveDatabase(Endpoint endpoint) { if (endpoint == null) { throw new JedisValidationException( "Provided endpoint is null. Please use one from the configuration"); } Database database = databaseMap.get(endpoint); if (database == null) { throw new JedisValidationException("Provided endpoint: " + endpoint + " is not within " + "the configured endpoints. Please use one from the configuration"); } if (setActiveDatabase(database, true)) { onDatabaseSwitch(SwitchReason.FORCED, endpoint, database); } } public void forceActiveDatabase(Endpoint endpoint, long forcedActiveDuration) { Database database = databaseMap.get(endpoint); if (database == null) { throw new JedisValidationException("Provided endpoint: " + endpoint + " is not within " + "the configured endpoints. Please use one from the configuration"); } database.clearGracePeriod(); if (!database.isHealthy()) { throw new JedisValidationException("Provided endpoint: " + endpoint + " is not healthy. Please consider a healthy endpoint from the configuration"); } databaseMap.entrySet().stream().forEach(entry -> { if (entry.getKey() != endpoint) { entry.getValue().setGracePeriod(forcedActiveDuration); } }); setActiveDatabase(endpoint); } private boolean setActiveDatabase(Database database, boolean validateConnection) { // Database database = databaseEntry.getValue(); // Field-level synchronization is used to avoid the edge case in which // setActiveDatabase() is called at the same time activeDatabaseChangeLock.lock(); Database oldDatabase; try { // Allows an attempt to reset the current database from a FORCED_OPEN to CLOSED state in the // event that no failover is possible if (activeDatabase == database && !database.isCBForcedOpen()) return false; if (validateConnection) validateTargetConnection(database); String originalDatabaseName = getDatabaseCircuitBreaker().getName(); if (activeDatabase == database) log.warn("Database/database endpoint '{}' successfully closed its circuit breaker", originalDatabaseName); else log.warn("Database/database endpoint successfully updated from '{}' to '{}'", originalDatabaseName, database.circuitBreaker.getName()); oldDatabase = activeDatabase; activeDatabase = database; } finally { activeDatabaseChangeLock.unlock(); } boolean switched = oldDatabase != database; if (switched && this.multiDbConfig.isFastFailover()) { log.info("Forcing disconnect of all active connections in old database: {}", oldDatabase.circuitBreaker.getName()); oldDatabase.forceDisconnect(); log.info("Disconnected all active connections in old database: {}", oldDatabase.circuitBreaker.getName()); } return switched; } @Override public void close() { if (healthStatusManager != null) { healthStatusManager.close(); } // Shutdown the failback scheduler failbackScheduler.shutdown(); try { if (!failbackScheduler.awaitTermination(1, TimeUnit.SECONDS)) { failbackScheduler.shutdownNow(); } } catch (InterruptedException e) { failbackScheduler.shutdownNow(); Thread.currentThread().interrupt(); } // Close all database connection pools for (Database database : databaseMap.values()) { database.close(); } } @Override public Connection getConnection() { return activeDatabase.getConnection(); } public Connection getConnection(Endpoint endpoint) { return databaseMap.get(endpoint).getConnection(); } @Override public Connection getConnection(CommandArguments args) { return activeDatabase.getConnection(); } @Override public Map> getConnectionMap() { ConnectionPool connectionPool = activeDatabase.connectionPool; return Collections.singletonMap(connectionPool.getFactory(), connectionPool); } public Database getDatabase() { return activeDatabase; } @VisibleForTesting public Database getDatabase(Endpoint endpoint) { return databaseMap.get(endpoint); } /** * Returns the active endpoint *

* Active endpoint is the one which is currently being used for all operations. It can change at * any time due to health checks, failover, failback, etc. * @return the active database endpoint */ public Endpoint getActiveEndpoint() { return activeDatabase.getEndpoint(); } /** * Returns the health state of the given endpoint * @param endpoint the endpoint to check * @return the health status of the endpoint */ public boolean isHealthy(Endpoint endpoint) { Database database = getDatabase(endpoint); if (database == null) { throw new JedisValidationException( "Endpoint " + endpoint + " does not exist in the provider"); } return database.isHealthy(); } public CircuitBreaker getDatabaseCircuitBreaker() { return activeDatabase.getCircuitBreaker(); } /** * Indicates the final database endpoint (connection pool), according to the pre-configured list * provided at startup via the MultiDbConfig, is unavailable and therefore no further failover is * possible. Users can manually failback to an available database */ public boolean canIterateFrom(Database iterateFrom) { Map.Entry e = findWeightedHealthyDatabaseToIterate(iterateFrom); return e != null; } public void onDatabaseSwitch(SwitchReason reason, Endpoint endpoint, Database database) { if (databaseSwitchListener != null) { DatabaseSwitchEvent eventArgs = new DatabaseSwitchEvent(reason, endpoint, database); databaseSwitchListener.accept(eventArgs); } } public void setDatabaseSwitchListener(Consumer databaseSwitchListener) { this.databaseSwitchListener = databaseSwitchListener; } public List> getFallbackExceptionList() { return fallbackExceptionList; } public static class Database { private TrackingConnectionPool connectionPool; private final Retry retry; private final CircuitBreaker circuitBreaker; private float weight; private final HealthCheck healthCheck; private final MultiDbConfig multiDbConfig; private boolean disabled = false; private final Endpoint endpoint; // Grace period tracking private volatile long gracePeriodEndsAt = 0; private final Logger log = LoggerFactory.getLogger(getClass()); private Database(Endpoint endpoint, TrackingConnectionPool connectionPool, Retry retry, CircuitBreaker circuitBreaker, float weight, MultiDbConfig multiDbConfig) { this.endpoint = endpoint; this.connectionPool = connectionPool; this.retry = retry; this.circuitBreaker = circuitBreaker; this.weight = weight; this.multiDbConfig = multiDbConfig; this.healthCheck = null; } private Database(Endpoint endpoint, TrackingConnectionPool connectionPool, Retry retry, HealthCheck hc, CircuitBreaker circuitBreaker, float weight, MultiDbConfig multiDbConfig) { this.endpoint = endpoint; this.connectionPool = connectionPool; this.retry = retry; this.circuitBreaker = circuitBreaker; this.weight = weight; this.multiDbConfig = multiDbConfig; this.healthCheck = hc; } public Endpoint getEndpoint() { return endpoint; } public Connection getConnection() { if (!isHealthy()) throw new JedisConnectionException("Database is not healthy"); if (connectionPool.isClosed()) { connectionPool = TrackingConnectionPool.from(connectionPool); } return connectionPool.getResource(); } @VisibleForTesting public ConnectionPool getConnectionPool() { return connectionPool; } public Retry getRetry() { return retry; } public CircuitBreaker getCircuitBreaker() { return circuitBreaker; } public HealthStatus getHealthStatus() { return healthCheck == null ? HealthStatus.HEALTHY : healthCheck.getStatus(); } /** * Assigned weight for this database */ public float getWeight() { return weight; } public void setWeight(float weight) { JedisAsserts.isTrue(weight > 0, "Database weight must be greater than 0"); this.weight = weight; } public boolean isCBForcedOpen() { if (circuitBreaker.getState() == State.FORCED_OPEN && !isInGracePeriod()) { log.info( "Transitioning circuit breaker from FORCED_OPEN to CLOSED state due to end of grace period!"); circuitBreaker.transitionToClosedState(); } return circuitBreaker.getState() == CircuitBreaker.State.FORCED_OPEN; } public boolean isHealthy() { return getHealthStatus().isHealthy() && !isCBForcedOpen() && !disabled && !isInGracePeriod(); } public boolean retryOnFailover() { return multiDbConfig.isRetryOnFailover(); } public int getCircuitBreakerMinNumOfFailures() { return multiDbConfig.getFailureDetector().getMinNumOfFailures(); } public float getCircuitBreakerFailureRateThreshold() { return multiDbConfig.getFailureDetector().getFailureRateThreshold(); } public boolean isDisabled() { return disabled; } public void setDisabled(boolean disabled) { this.disabled = disabled; } /** * Checks if the da is currently in grace period */ public boolean isInGracePeriod() { return System.currentTimeMillis() < gracePeriodEndsAt; } /** * Sets the grace period for this database */ public void setGracePeriod() { setGracePeriod(multiDbConfig.getGracePeriod()); } public void setGracePeriod(long gracePeriod) { long endTime = System.currentTimeMillis() + gracePeriod; if (endTime < gracePeriodEndsAt) return; gracePeriodEndsAt = endTime; } public void clearGracePeriod() { gracePeriodEndsAt = 0; } /** * Whether failback is supported by client */ public boolean isFailbackSupported() { return multiDbConfig.isFailbackSupported(); } public void forceDisconnect() { connectionPool.forceDisconnect(); } public void close() { connectionPool.close(); } void evaluateThresholds(boolean lastFailRecorded) { if (getCircuitBreaker().getState() == State.CLOSED && isThresholdsExceeded(this, lastFailRecorded)) { getCircuitBreaker().transitionToOpenState(); } } private static boolean isThresholdsExceeded(Database database, boolean lastFailRecorded) { Metrics metrics = database.getCircuitBreaker().getMetrics(); // ATTENTION: this is to increment fails in regard to the current call that is failing, // DO NOT remove the increment, it will change the behaviour in case of initial requests to // database fail int fails = metrics.getNumberOfFailedCalls() + (lastFailRecorded ? 0 : 1); int succ = metrics.getNumberOfSuccessfulCalls(); if (fails >= database.getCircuitBreakerMinNumOfFailures()) { float ratePercentThreshold = database.getCircuitBreakerFailureRateThreshold();// 0..100 int total = fails + succ; if (total == 0) return false; float failureRatePercent = (fails * 100.0f) / total; return failureRatePercent >= ratePercentThreshold; } return false; } @Override public String toString() { return circuitBreaker.getName() + "{" + "connectionPool=" + connectionPool + ", retry=" + retry + ", circuitBreaker=" + circuitBreaker + ", weight=" + weight + ", healthStatus=" + getHealthStatus() + ", multiDbConfig=" + multiDbConfig + '}'; } } } ================================================ FILE: src/main/java/redis/clients/jedis/mcf/MultiDbConnectionSupplier.java ================================================ package redis.clients.jedis.mcf; import io.github.resilience4j.circuitbreaker.CircuitBreaker.State; import io.github.resilience4j.decorators.Decorators; import io.github.resilience4j.decorators.Decorators.DecorateSupplier; import redis.clients.jedis.Connection; import redis.clients.jedis.annots.Experimental; import redis.clients.jedis.mcf.MultiDbConnectionProvider.Database; /** * ConnectionProvider with built-in retry, circuit-breaker, and failover to another /database * endpoint. With this executor users can seamlessly failover to Disaster Recovery (DR), Backup, and * Active-Active cluster(s) by using simple configuration */ @Experimental public class MultiDbConnectionSupplier extends MultiDbFailoverBase { public MultiDbConnectionSupplier(MultiDbConnectionProvider provider) { super(provider); } public Connection getConnection() { Database database = provider.getDatabase(); // Pass this by reference for thread safety DecorateSupplier supplier = Decorators .ofSupplier(() -> this.handleGetConnection(database)); supplier.withRetry(database.getRetry()); supplier.withCircuitBreaker(database.getCircuitBreaker()); supplier.withFallback(provider.getFallbackExceptionList(), e -> this.handleDatabaseFailover(database)); try { return supplier.decorate().get(); } catch (Exception e) { if (database.getCircuitBreaker().getState() == State.OPEN && isActiveDatabase(database)) { databaseFailover(database); } throw e; } } /** * Functional interface wrapped in retry and circuit breaker logic to handle happy path scenarios */ private Connection handleGetConnection(Database database) { Connection connection = database.getConnection(); connection.ping(); return connection; } /** * Functional interface wrapped in retry and circuit breaker logic to handle open circuit breaker * failure scenarios */ private Connection handleDatabaseFailover(Database database) { databaseFailover(database); // Recursive call to the initiating method so the operation can be retried on the next database // connection return getConnection(); } } ================================================ FILE: src/main/java/redis/clients/jedis/mcf/MultiDbFailoverBase.java ================================================ package redis.clients.jedis.mcf; import io.github.resilience4j.circuitbreaker.CircuitBreaker; import java.util.concurrent.locks.Lock; import java.util.concurrent.locks.ReentrantLock; import redis.clients.jedis.annots.Experimental; import redis.clients.jedis.mcf.MultiDbConnectionProvider.Database; import redis.clients.jedis.util.IOUtils; /** * @author Allen Terleto (aterleto) *

* Base class for CommandExecutor with built-in retry, circuit-breaker, and failover to * another database endpoint. With this executor users can seamlessly failover to Disaster * Recovery (DR), Backup, and Active-Active cluster(s) by using simple configuration *

*/ @Experimental public class MultiDbFailoverBase implements AutoCloseable { private final Lock lock = new ReentrantLock(true); protected final MultiDbConnectionProvider provider; public MultiDbFailoverBase(MultiDbConnectionProvider provider) { this.provider = provider; } @Override public void close() { IOUtils.closeQuietly(this.provider); } /** * Functional interface wrapped in retry and circuit breaker logic to handle open circuit breaker * failure scenarios */ protected void databaseFailover(Database database) { lock.lock(); CircuitBreaker circuitBreaker = database.getCircuitBreaker(); try { // Check state to handle race conditions since () is // non-idempotent if (!CircuitBreaker.State.FORCED_OPEN.equals(circuitBreaker.getState())) { // Transitions state machine to a FORCED_OPEN state, stopping state transition, metrics and // event publishing. // To recover/transition from this forced state the user will need to manually failback Database activeDatabase = provider.getDatabase(); // This should be possible only if active database is switched from by other reasons than // circuit breaker, just before circuit breaker triggers if (activeDatabase != database) { return; } database.setGracePeriod(); circuitBreaker.transitionToForcedOpenState(); // Iterating the active database will allow subsequent calls to the executeCommand() to use // the next // database's connection pool - according to the configuration's prioritization/order/weight provider.switchToHealthyDatabase(SwitchReason.CIRCUIT_BREAKER, database); } // this check relies on the fact that many failover attempts can hit with the same CB, // only the first one will trigger a failover, and make the CB FORCED_OPEN. // when the rest reaches here, the active database is already the next one, and should be // different than // active CB. If its the same one and there are no more databases to failover to, then throw // an // exception else if (database == provider.getDatabase()) { provider.switchToHealthyDatabase(SwitchReason.CIRCUIT_BREAKER, database); } // Ignore exceptions since we are already in a failure state } finally { lock.unlock(); } } boolean isActiveDatabase(Database database) { Database activeDatabase = provider.getDatabase(); return activeDatabase != null && activeDatabase.equals(database); } static boolean isCircuitBreakerTrackedException(Exception e, Database database) { return database.getCircuitBreaker().getCircuitBreakerConfig().getRecordExceptionPredicate() .test(e); } } ================================================ FILE: src/main/java/redis/clients/jedis/mcf/MultiDbPipeline.java ================================================ package redis.clients.jedis.mcf; import java.io.Closeable; import java.util.LinkedList; import java.util.List; import java.util.Queue; import redis.clients.jedis.*; import redis.clients.jedis.annots.Experimental; import redis.clients.jedis.util.KeyValue; /** * This is high memory dependent solution as all the appending commands will be hold in memory until * {@link MultiDbPipeline#sync() SYNC} (or {@link MultiDbPipeline#close() CLOSE}) gets called. */ @Experimental public class MultiDbPipeline extends AbstractPipeline implements Closeable { private final MultiDbConnectionSupplier failoverProvider; private final Queue>> commands = new LinkedList<>(); @Deprecated public MultiDbPipeline(MultiDbConnectionProvider pooledProvider) { super(new CommandObjects()); this.failoverProvider = new MultiDbConnectionSupplier(pooledProvider); try (Connection connection = failoverProvider.getConnection()) { RedisProtocol proto = connection.getRedisProtocol(); if (proto != null) this.commandObjects.setProtocol(proto); } } public MultiDbPipeline(MultiDbConnectionProvider pooledProvider, CommandObjects commandObjects) { super(commandObjects); this.failoverProvider = new MultiDbConnectionSupplier(pooledProvider); } @Override protected final Response appendCommand(CommandObject commandObject) { CommandArguments args = commandObject.getArguments(); Response response = new Response<>(commandObject.getBuilder()); commands.add(KeyValue.of(args, response)); return response; } @Override public void close() { sync(); // connection prepared and closed (in try-with-resources) in sync() } /** * Synchronize pipeline by reading all responses. This operation close the pipeline. In order to * get return values from pipelined commands, capture the different Response<?> of the * commands you execute. */ @Override public void sync() { if (commands.isEmpty()) return; try (Connection connection = failoverProvider.getConnection()) { commands.forEach((command) -> connection.sendCommand(command.getKey())); // following connection.getMany(int) flushes anyway, so no flush here. List unformatted = connection.getMany(commands.size()); unformatted.forEach((rawReply) -> commands.poll().getValue().set(rawReply)); } } public Response waitReplicas(int replicas, long timeout) { return appendCommand(commandObjects.waitReplicas(replicas, timeout)); } public Response> waitAOF(long numLocal, long numReplicas, long timeout) { return appendCommand(commandObjects.waitAOF(numLocal, numReplicas, timeout)); } } ================================================ FILE: src/main/java/redis/clients/jedis/mcf/MultiDbTransaction.java ================================================ package redis.clients.jedis.mcf; import static redis.clients.jedis.Protocol.Command.DISCARD; import static redis.clients.jedis.Protocol.Command.EXEC; import static redis.clients.jedis.Protocol.Command.MULTI; import static redis.clients.jedis.Protocol.Command.UNWATCH; import java.util.ArrayList; import java.util.LinkedList; import java.util.List; import java.util.Queue; import java.util.concurrent.atomic.AtomicInteger; import redis.clients.jedis.*; import redis.clients.jedis.annots.Experimental; import redis.clients.jedis.exceptions.JedisDataException; import redis.clients.jedis.util.KeyValue; /** * This is high memory dependent solution as all the appending commands will be hold in memory. */ @Experimental public class MultiDbTransaction extends AbstractTransaction { private static final Builder NO_OP_BUILDER = BuilderFactory.RAW_OBJECT; private final MultiDbConnectionSupplier failoverProvider; private final AtomicInteger extraCommandCount = new AtomicInteger(); private final Queue>> commands = new LinkedList<>(); private boolean inWatch = false; private boolean inMulti = false; /** * A MULTI command will be added to be sent to server. WATCH/UNWATCH/MULTI commands must not be * called with this object. * @param provider */ @Deprecated public MultiDbTransaction(MultiDbConnectionProvider provider) { this(provider, true); } /** * A user wanting to WATCH/UNWATCH keys followed by a call to MULTI ({@link #multi()}) it should * be {@code doMulti=false}. * @param provider * @param doMulti {@code false} should be set to enable manual WATCH, UNWATCH and MULTI */ @Deprecated public MultiDbTransaction(MultiDbConnectionProvider provider, boolean doMulti) { this.failoverProvider = new MultiDbConnectionSupplier(provider); try (Connection connection = failoverProvider.getConnection()) { RedisProtocol proto = connection.getRedisProtocol(); if (proto != null) this.commandObjects.setProtocol(proto); } if (doMulti) multi(); } /** * A user wanting to WATCH/UNWATCH keys followed by a call to MULTI ({@link #multi()}) it should * be {@code doMulti=false}. * @param provider * @param doMulti {@code false} should be set to enable manual WATCH, UNWATCH and MULTI * @param commandObjects command objects */ public MultiDbTransaction(MultiDbConnectionProvider provider, boolean doMulti, CommandObjects commandObjects) { super(commandObjects); this.failoverProvider = new MultiDbConnectionSupplier(provider); if (doMulti) multi(); } @Override public final void multi() { appendCommand(new CommandObject<>(new CommandArguments(MULTI), NO_OP_BUILDER)); extraCommandCount.incrementAndGet(); inMulti = true; } /** * @param keys * @return {@code null} */ @Override public final String watch(String... keys) { appendCommand(commandObjects.watch(keys)); extraCommandCount.incrementAndGet(); inWatch = true; return null; } /** * @param keys * @return {@code null} */ @Override public final String watch(byte[]... keys) { appendCommand(commandObjects.watch(keys)); extraCommandCount.incrementAndGet(); inWatch = true; return null; } /** * @return {@code null} */ @Override public final String unwatch() { appendCommand(new CommandObject<>(new CommandArguments(UNWATCH), NO_OP_BUILDER)); extraCommandCount.incrementAndGet(); inWatch = false; return null; } @Override protected final Response appendCommand(CommandObject commandObject) { CommandArguments args = commandObject.getArguments(); Response response = new Response<>(commandObject.getBuilder()); commands.add(KeyValue.of(args, response)); return response; } @Override public void close() { clear(); } private void clear() { if (inMulti) { discard(); } else if (inWatch) { unwatch(); } } @Override public final List exec() { if (!inMulti) { throw new IllegalStateException("EXEC without MULTI"); } try (Connection connection = failoverProvider.getConnection()) { commands.forEach((command) -> connection.sendCommand(command.getKey())); // following connection.getMany(int) flushes anyway, so no flush here. // ignore QUEUED (or ERROR) connection.getMany(commands.size()); // remove extra response builders for (int idx = 0; idx < extraCommandCount.get(); ++idx) { commands.poll(); } connection.sendCommand(EXEC); List unformatted = connection.getObjectMultiBulkReply(); if (unformatted == null) { commands.clear(); return null; } List formatted = new ArrayList<>(unformatted.size() - extraCommandCount.get()); for (Object rawReply : unformatted) { try { Response response = commands.poll().getValue(); response.set(rawReply); formatted.add(response.get()); } catch (JedisDataException e) { formatted.add(e); } } return formatted; } finally { inMulti = false; inWatch = false; } } @Override public final String discard() { if (!inMulti) { throw new IllegalStateException("DISCARD without MULTI"); } try (Connection connection = failoverProvider.getConnection()) { commands.forEach((command) -> connection.sendCommand(command.getKey())); // following connection.getMany(int) flushes anyway, so no flush here. // ignore QUEUED (or ERROR) connection.getMany(commands.size()); connection.sendCommand(DISCARD); return connection.getStatusCodeReply(); } finally { inMulti = false; inWatch = false; } } } ================================================ FILE: src/main/java/redis/clients/jedis/mcf/PingStrategy.java ================================================ package redis.clients.jedis.mcf; import org.apache.commons.pool2.impl.GenericObjectPoolConfig; import redis.clients.jedis.Connection; import redis.clients.jedis.Endpoint; import redis.clients.jedis.HostAndPort; import redis.clients.jedis.JedisClientConfig; import redis.clients.jedis.RedisClient; import redis.clients.jedis.MultiDbConfig.StrategySupplier; public class PingStrategy implements HealthCheckStrategy { private static final int MAX_HEALTH_CHECK_POOL_SIZE = 2; private final RedisClient jedis; private final HealthCheckStrategy.Config config; public PingStrategy(HostAndPort hostAndPort, JedisClientConfig jedisClientConfig) { this(hostAndPort, jedisClientConfig, HealthCheckStrategy.Config.create()); } public PingStrategy(HostAndPort hostAndPort, JedisClientConfig jedisClientConfig, HealthCheckStrategy.Config config) { GenericObjectPoolConfig poolConfig = new GenericObjectPoolConfig<>(); poolConfig.setMaxTotal(MAX_HEALTH_CHECK_POOL_SIZE); this.jedis = RedisClient.builder().hostAndPort(hostAndPort).clientConfig(jedisClientConfig) .poolConfig(poolConfig).build(); this.config = config; } @Override public int getInterval() { return config.getInterval(); } @Override public int getTimeout() { return config.getTimeout(); } @Override public int getNumProbes() { return config.getNumProbes(); } @Override public ProbingPolicy getPolicy() { return config.getPolicy(); } @Override public int getDelayInBetweenProbes() { return config.getDelayInBetweenProbes(); } @Override public HealthStatus doHealthCheck(Endpoint endpoint) { return "PONG".equals(jedis.ping()) ? HealthStatus.HEALTHY : HealthStatus.UNHEALTHY; } @Override public void close() { jedis.close(); } public static final StrategySupplier DEFAULT = PingStrategy::new; } ================================================ FILE: src/main/java/redis/clients/jedis/mcf/ProbingPolicy.java ================================================ package redis.clients.jedis.mcf; public interface ProbingPolicy { public enum Decision { CONTINUE, SUCCESS, FAIL } Decision evaluate(ProbeContext data); public static interface ProbeContext { public int getRemainingProbes(); public int getSuccesses(); public int getFails(); } public static class BuiltIn { public static final ProbingPolicy ALL_SUCCESS = new AllSuccessPolicy(); public static final ProbingPolicy ANY_SUCCESS = new AnySuccessPolicy(); public static final ProbingPolicy MAJORITY_SUCCESS = new MajoritySuccessPolicy(); /* * All probes need to be healthy. If a database doesn’t pass the health check for numProbes * times, then the check wasn’t successful. This means you can stop probing after you got the * first failed health check (e.g., timeout or unhealthy status) */ private static class AllSuccessPolicy implements ProbingPolicy { @Override public Decision evaluate(ProbeContext ctx) { // Any failure means overall failure if (ctx.getFails() > 0) { return Decision.FAIL; } // All probes completed successfully if (ctx.getRemainingProbes() == 0) { return Decision.SUCCESS; } return Decision.CONTINUE; } } /* * A database is healthy if at least one probe returned a healthy status. You can stop probing * as soon as you got the first healthy status. */ private static class AnySuccessPolicy implements ProbingPolicy { @Override public Decision evaluate(ProbeContext ctx) { // Any success means overall success if (ctx.getSuccesses() > 0) { return Decision.SUCCESS; } // All probes completed with failures if (ctx.getRemainingProbes() == 0) { return Decision.FAIL; } return Decision.CONTINUE; } } /* * A database is healthy if the majority of probes returned ‘healthy’. This means you can stop * probing as soon as the majority can’t be guaranteed any more (e.g., you have 4 probes and 2 * of them failed), or as soon as the majority is reached (e.g., 3 out of 4 were healthy) */ private static class MajoritySuccessPolicy implements ProbingPolicy { @Override public Decision evaluate(ProbeContext ctx) { int total = ctx.getRemainingProbes() + ctx.getSuccesses() + ctx.getFails(); int required = (total / 2) + 1; // Early success if (ctx.getSuccesses() >= required) { return Decision.SUCCESS; } // Early failure - impossible to reach majority int maxPossibleSuccesses = ctx.getSuccesses() + ctx.getRemainingProbes(); if (maxPossibleSuccesses < required) { return Decision.FAIL; } // Final evaluation if (ctx.getRemainingProbes() == 0) { return ctx.getSuccesses() >= required ? Decision.SUCCESS : Decision.FAIL; } return Decision.CONTINUE; } } } } ================================================ FILE: src/main/java/redis/clients/jedis/mcf/RedisRestAPI.java ================================================ package redis.clients.jedis.mcf; import java.io.IOException; import java.io.InputStream; import java.net.HttpURLConnection; import java.net.URL; import java.nio.charset.StandardCharsets; import java.security.GeneralSecurityException; import java.util.ArrayList; import java.util.Base64; import java.util.List; import java.util.function.Supplier; import javax.net.ssl.HttpsURLConnection; import javax.net.ssl.SSLContext; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import com.google.gson.JsonArray; import com.google.gson.JsonElement; import com.google.gson.JsonObject; import com.google.gson.JsonParser; import redis.clients.jedis.Endpoint; import redis.clients.jedis.RedisCredentials; import redis.clients.jedis.SslOptions; import redis.clients.jedis.SslVerifyMode; import redis.clients.jedis.annots.Internal; /** * Helper class to check the availability of a Redis database */ @Internal class RedisRestAPI { private static final Logger log = LoggerFactory.getLogger(RedisRestAPI.class); private static final String BDBS_URL = "https://%s:%s/v1/bdbs?fields=uid,endpoints"; private static final String AVAILABILITY_URL = "https://%s:%s/v1/bdbs/%s/availability"; private static final String LAGAWARE_AVAILABILITY_URL = "https://%s:%s/v1/bdbs/%s/availability?extend_check=lag"; private static final int DEFAULT_TIMEOUT_MS = 1000; private final Endpoint endpoint; private final Supplier credentialsSupplier; private final int timeoutMs; private final SslOptions sslOptions; private String bdbsUri; public RedisRestAPI(Endpoint endpoint, Supplier credentialsSupplier) { this(endpoint, credentialsSupplier, DEFAULT_TIMEOUT_MS); } public RedisRestAPI(Endpoint endpoint, Supplier credentialsSupplier, int timeoutMs) { this(endpoint, credentialsSupplier, timeoutMs, null); } public RedisRestAPI(Endpoint endpoint, Supplier credentialsSupplier, int timeoutMs, SslOptions sslOptions) { this.endpoint = endpoint; this.credentialsSupplier = credentialsSupplier; this.timeoutMs = timeoutMs; this.sslOptions = sslOptions; } public List getBdbs() throws IOException { if (bdbsUri == null) { bdbsUri = String.format(BDBS_URL, endpoint.getHost(), endpoint.getPort()); } HttpURLConnection conn = null; try { conn = createConnection(bdbsUri, "GET", credentialsSupplier.get()); conn.setRequestProperty("Accept", "application/json"); int code = conn.getResponseCode(); String responseBody = readResponse(conn); if (code != 200) { throw new IOException("Unexpected response code '" + code + "' for getBdbs: '" + responseBody + "' from '" + bdbsUri + "'"); } return parseBdbInfoFromResponse(responseBody); } finally { if (conn != null) conn.disconnect(); } } public boolean checkBdbAvailability(String uid, boolean lagAware) throws IOException { return checkBdbAvailability(uid, lagAware, null); } public boolean checkBdbAvailability(String uid, boolean extendedCheckEnabled, Long availabilityLagToleranceMs) throws IOException { String availabilityUri; if (extendedCheckEnabled) { // Use extended check with lag validation availabilityUri = String.format(LAGAWARE_AVAILABILITY_URL, endpoint.getHost(), endpoint.getPort(), uid); if (availabilityLagToleranceMs != null) { availabilityUri = availabilityUri + "&availability_lag_tolerance_ms=" + availabilityLagToleranceMs; } } else { // Use standard datapath validation only availabilityUri = String.format(AVAILABILITY_URL, endpoint.getHost(), endpoint.getPort(), uid); } HttpURLConnection conn = null; try { conn = createConnection(availabilityUri, "GET", credentialsSupplier.get()); conn.setRequestProperty("Accept", "application/json"); int code = conn.getResponseCode(); if (code == 200) { return true; } String body = readResponse(conn); log.warn("Availability check for {} returned body='{}' from '{}'", uid, body, availabilityUri); } finally { if (conn != null) conn.disconnect(); } return false; } HttpURLConnection createConnection(String urlString, String method, RedisCredentials credentials) throws IOException { URL url = new URL(urlString); HttpURLConnection connection = (HttpURLConnection) url.openConnection(); // Configure SSL if this is an HTTPS connection and SSL options are provided if (connection instanceof HttpsURLConnection && sslOptions != null) { HttpsURLConnection httpsConnection = (HttpsURLConnection) connection; try { SSLContext sslContext = sslOptions.createSslContext(); httpsConnection.setSSLSocketFactory(sslContext.getSocketFactory()); if (sslOptions.getSslVerifyMode() == SslVerifyMode.CA || sslOptions.getSslVerifyMode() == SslVerifyMode.INSECURE) { httpsConnection.setHostnameVerifier((h, s) -> true); // skip hostname check } } catch (GeneralSecurityException e) { throw new IOException("SSL configuration failed", e); } } connection.setRequestMethod(method); connection.setConnectTimeout(timeoutMs); connection.setReadTimeout(timeoutMs); connection.setRequestProperty("Authorization", getAuthenticationHeader(credentials)); return connection; } // This is just to avoid putting password chars directly into a string private static String getAuthenticationHeader(RedisCredentials credentials) throws IOException { // Build Basic auth without creating a password String final char[] pass = credentials.getPassword() != null ? credentials.getPassword() : new char[0]; final String user = credentials.getUser() != null ? credentials.getUser() : ""; final byte[] userBytes = user.getBytes(StandardCharsets.UTF_8); // Encode char[] directly to UTF-8 bytes java.nio.ByteBuffer bb = StandardCharsets.UTF_8.encode(java.nio.CharBuffer.wrap(pass)); byte[] passBytes = new byte[bb.remaining()]; bb.get(passBytes); // user ":" password byte[] combined = new byte[userBytes.length + 1 + passBytes.length]; System.arraycopy(userBytes, 0, combined, 0, userBytes.length); combined[userBytes.length] = (byte) ':'; System.arraycopy(passBytes, 0, combined, userBytes.length + 1, passBytes.length); String encodedAuth = Base64.getEncoder().encodeToString(combined); // Clear sensitive buffers java.util.Arrays.fill(passBytes, (byte) 0); java.util.Arrays.fill(combined, (byte) 0); return "Basic " + encodedAuth; } /** * Parses the response body and extracts BDB information including endpoints. * @param responseBody the JSON response containing BDBs with endpoints * @return list of BDB information objects */ static List parseBdbInfoFromResponse(String responseBody) { JsonArray bdbs = JsonParser.parseString(responseBody).getAsJsonArray(); List bdbInfoList = new ArrayList<>(); for (JsonElement bdbElement : bdbs) { if (!bdbElement.isJsonObject()) { continue; } JsonObject bdb = bdbElement.getAsJsonObject(); if (!bdb.has("uid")) { continue; } String bdbId = bdb.get("uid").getAsString(); List endpoints = new ArrayList<>(); if (bdb.has("endpoints") && bdb.get("endpoints").isJsonArray()) { JsonArray endpointsArray = bdb.getAsJsonArray("endpoints"); for (JsonElement endpointElement : endpointsArray) { if (!endpointElement.isJsonObject()) { continue; } JsonObject endpoint = endpointElement.getAsJsonObject(); // Extract addr array List addrList = new ArrayList<>(); if (endpoint.has("addr") && endpoint.get("addr").isJsonArray()) { JsonArray addresses = endpoint.getAsJsonArray("addr"); for (JsonElement addrElement : addresses) { if (addrElement.isJsonPrimitive()) { addrList.add(addrElement.getAsString()); } } } // Extract other fields String dnsName = endpoint.has("dns_name") ? endpoint.get("dns_name").getAsString() : null; Integer port = endpoint.has("port") ? endpoint.get("port").getAsInt() : null; String endpointUid = endpoint.has("uid") ? endpoint.get("uid").getAsString() : null; endpoints.add(new RedisRestAPI.EndpointInfo(addrList, dnsName, port, endpointUid)); } } bdbInfoList.add(new RedisRestAPI.BdbInfo(bdbId, endpoints)); } return bdbInfoList; } static String readResponse(HttpURLConnection connection) throws IOException { InputStream inputStream = null; try { inputStream = connection.getInputStream(); if (inputStream == null) { inputStream = connection.getErrorStream(); } } catch (IOException e) { // If there's an error, try to read from error stream inputStream = connection.getErrorStream(); } if (inputStream == null) { throw new IOException( "No response stream available from server (code=" + connection.getResponseCode() + ")"); } StringBuilder response = new StringBuilder(); byte[] buffer = new byte[1024]; int bytesRead; while ((bytesRead = inputStream.read(buffer)) != -1) { response.append(new String(buffer, 0, bytesRead, StandardCharsets.UTF_8)); } inputStream.close(); return response.toString(); } /** * Information about a Redis Enterprise BDB (database) including its endpoints. */ static class BdbInfo { private final String uid; private final List endpoints; BdbInfo(String uid, List endpoints) { this.uid = uid; this.endpoints = endpoints; } String getUid() { return uid; } List getEndpoints() { return endpoints; } /** * Find the BDB that matches the given database host by comparing endpoints. * @param bdbs list of BDB information * @param dbHost the database host to match * @return the matching BDB, or null if no match is found */ static BdbInfo findMatchingBdb(List bdbs, String dbHost) { for (BdbInfo bdb : bdbs) { for (EndpointInfo endpoint : bdb.getEndpoints()) { // First check dns_name if (dbHost.equals(endpoint.getDnsName())) { return bdb; } // Then check addr array for IP addresses if (endpoint.getAddr() != null) { for (String addr : endpoint.getAddr()) { if (dbHost.equals(addr)) { return bdb; } } } } } return null; // No matching BDB found } @Override public String toString() { return "BdbInfo{" + "uid='" + uid + '\'' + ", endpoints=" + endpoints + '}'; } } /** * Information about a Redis Enterprise BDB endpoint. */ static class EndpointInfo { private final List addr; private final String dnsName; private final Integer port; private final String uid; EndpointInfo(List addr, String dnsName, Integer port, String uid) { this.addr = addr; this.dnsName = dnsName; this.port = port; this.uid = uid; } List getAddr() { return addr; } String getDnsName() { return dnsName; } Integer getPort() { return port; } String getUid() { return uid; } @Override public String toString() { return "EndpointInfo{" + "addr=" + addr + ", dnsName='" + dnsName + '\'' + ", port=" + port + ", uid='" + uid + '\'' + '}'; } } } ================================================ FILE: src/main/java/redis/clients/jedis/mcf/StatusTracker.java ================================================ package redis.clients.jedis.mcf; import java.util.concurrent.CountDownLatch; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicReference; import redis.clients.jedis.Endpoint; import redis.clients.jedis.exceptions.JedisConnectionException; import redis.clients.jedis.exceptions.JedisValidationException; /** * StatusTracker is responsible for tracking and waiting for health status changes for specific * endpoints. It provides an event-driven approach to wait for health status transitions from * UNKNOWN to either HEALTHY or UNHEALTHY. */ public class StatusTracker { private final HealthStatusManager healthStatusManager; public StatusTracker(HealthStatusManager healthStatusManager) { this.healthStatusManager = healthStatusManager; } /** * Waits for a specific endpoint's health status to be determined (not UNKNOWN). Uses event-driven * approach with CountDownLatch to avoid polling. * @param endpoint the endpoint to wait for * @return the determined health status (HEALTHY or UNHEALTHY) * @throws JedisConnectionException if interrupted while waiting */ public HealthStatus waitForHealthStatus(Endpoint endpoint) { // First check if status is already determined HealthStatus currentStatus = healthStatusManager.getHealthStatus(endpoint); if (currentStatus != HealthStatus.UNKNOWN) { return currentStatus; } // Set up event-driven waiting final CountDownLatch latch = new CountDownLatch(1); final AtomicReference resultStatus = new AtomicReference<>(); // Create a temporary listener for this specific endpoint HealthStatusListener tempListener = new HealthStatusListener() { @Override public void onStatusChange(HealthStatusChangeEvent event) { if (event.getEndpoint().equals(endpoint) && event.getNewStatus() != HealthStatus.UNKNOWN) { resultStatus.set(event.getNewStatus()); latch.countDown(); } } }; // Register the temporary listener healthStatusManager.registerListener(endpoint, tempListener); try { // Double-check status after registering listener (race condition protection) currentStatus = healthStatusManager.getHealthStatus(endpoint); if (currentStatus != HealthStatus.UNKNOWN) { return currentStatus; } // Wait for the health status change event // just for safety to not block indefinitely boolean completed = latch.await(healthStatusManager.getMaxWaitFor(endpoint), TimeUnit.MILLISECONDS); if (!completed) { throw new JedisValidationException("Timeout while waiting for health check result"); } return resultStatus.get(); } catch (InterruptedException e) { Thread.currentThread().interrupt(); throw new JedisConnectionException("Interrupted while waiting for health check result", e); } finally { // Clean up: unregister the temporary listener healthStatusManager.unregisterListener(endpoint, tempListener); } } } ================================================ FILE: src/main/java/redis/clients/jedis/mcf/SwitchReason.java ================================================ package redis.clients.jedis.mcf; public enum SwitchReason { HEALTH_CHECK, CIRCUIT_BREAKER, FAILBACK, FORCED } ================================================ FILE: src/main/java/redis/clients/jedis/mcf/TrackingConnectionPool.java ================================================ package redis.clients.jedis.mcf; import java.util.Set; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.atomic.AtomicInteger; import org.apache.commons.pool2.PooledObject; import org.apache.commons.pool2.impl.GenericObjectPoolConfig; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import redis.clients.jedis.Connection; import redis.clients.jedis.ConnectionFactory; import redis.clients.jedis.ConnectionPool; import redis.clients.jedis.DefaultJedisClientConfig; import redis.clients.jedis.DefaultJedisSocketFactory; import redis.clients.jedis.HostAndPort; import redis.clients.jedis.JedisClientConfig; import redis.clients.jedis.csc.CacheConnection; import redis.clients.jedis.exceptions.JedisConnectionException; public class TrackingConnectionPool extends ConnectionPool { private static class FailFastConnectionFactory extends ConnectionFactory { private volatile boolean failFast = false; private final Set factoryTrackedObjects = ConcurrentHashMap.newKeySet(); public FailFastConnectionFactory(ConnectionFactory.Builder factoryBuilder, JedisClientConfig clientConfig) { super(factoryBuilder .connectionBuilder(createCustomConnectionBuilder(factoryBuilder, clientConfig))); } private static Connection.Builder createCustomConnectionBuilder( ConnectionFactory.Builder factoryBuilder, JedisClientConfig clientConfig) { Connection.Builder connBuilder = factoryBuilder.getCache() == null ? Connection.builder() : CacheConnection.builder(factoryBuilder.getCache()); return connBuilder.socketFactory(factoryBuilder.getJedisSocketFactory()) .clientConfig(clientConfig); } @Override public PooledObject makeObject() throws Exception { if (failFast) { throw new JedisConnectionException("Failed to create connection!"); } try { PooledObject object = super.makeObject(); factoryTrackedObjects.add(object.getObject()); try { object.getObject().initializeFromClientConfig(); } finally { factoryTrackedObjects.remove(object.getObject()); } // this can make a marginal improvement on fast failover duration! if (failFast) { object.getObject().close(); throw new JedisConnectionException("Failed to create connection!"); } return object; } catch (JedisConnectionException e) { throw e; } catch (Exception e) { throw new JedisConnectionException(e); } } public void forceDisconnect() { for (Connection connection : factoryTrackedObjects) { try { connection.forceDisconnect(); } catch (Exception e) { log.warn("Error while force disconnecting connection: " + connection.toIdentityString(), e); } } } } public static class Builder { private HostAndPort hostAndPort; private JedisClientConfig clientConfig; private GenericObjectPoolConfig poolConfig; public Builder hostAndPort(HostAndPort hostAndPort) { this.hostAndPort = hostAndPort; return this; } public Builder clientConfig(JedisClientConfig clientConfig) { this.clientConfig = clientConfig; return this; } public Builder poolConfig(GenericObjectPoolConfig poolConfig) { this.poolConfig = poolConfig; return this; } public TrackingConnectionPool build() { applyDefaults(); return new TrackingConnectionPool(this); } private void applyDefaults() { if (clientConfig == null) { clientConfig = DefaultJedisClientConfig.builder().build(); } if (poolConfig == null) { poolConfig = new GenericObjectPoolConfig<>(); } } } private static final Logger log = LoggerFactory.getLogger(TrackingConnectionPool.class); private final HostAndPort hostAndPort; private final JedisClientConfig clientConfig; private final GenericObjectPoolConfig poolConfig; private final AtomicInteger numWaiters = new AtomicInteger(); private final Set poolTrackedObjects = ConcurrentHashMap.newKeySet(); public static Builder builder() { return new Builder(); } private TrackingConnectionPool(Builder builder) { super(createfailFastFactory(builder), builder.poolConfig != null ? builder.poolConfig : new GenericObjectPoolConfig<>()); this.hostAndPort = builder.hostAndPort; this.clientConfig = builder.clientConfig; this.poolConfig = builder.poolConfig; this.attachAuthenticationListener(builder.clientConfig.getAuthXManager()); } private static FailFastConnectionFactory createfailFastFactory(Builder poolBuilder) { ConnectionFactory.Builder factoryBuilder = ConnectionFactory.builder() .clientConfig(poolBuilder.clientConfig).socketFactory( new DefaultJedisSocketFactory(poolBuilder.hostAndPort, poolBuilder.clientConfig)); return new FailFastConnectionFactory(factoryBuilder, poolBuilder.clientConfig); } public static TrackingConnectionPool from(TrackingConnectionPool existing) { return builder().hostAndPort(existing.hostAndPort).clientConfig(existing.clientConfig) .poolConfig(existing.poolConfig).build(); } @Override public Connection getResource() { try { numWaiters.incrementAndGet(); Connection conn = super.getResource(); poolTrackedObjects.add(conn); return conn; } catch (Exception e) { if (this.isClosed()) { throw new JedisConnectionException("Pool is closed!", e); } throw e; } finally { numWaiters.decrementAndGet(); } } @Override public void returnResource(final Connection resource) { super.returnResource(resource); poolTrackedObjects.remove(resource); } @Override public void returnBrokenResource(final Connection resource) { super.returnBrokenResource(resource); poolTrackedObjects.remove(resource); } public void forceDisconnect() { this.close(); ((FailFastConnectionFactory) this.getFactory()).failFast = true; int numOfConnected = poolTrackedObjects.size(); // we need to wait for all waiters to leave before we are done with disconnecting the // connections, since a user app thread might be either; // - in the middle of a factory call(create|init) and not yet show up in poolTrackedObjects // - blocked on an exhausted pool, waiting for resources to return back pool while (numWaiters.get() > 0 || numOfConnected > 0) { this.clear(); ((FailFastConnectionFactory) this.getFactory()).forceDisconnect(); numOfConnected = 0; for (Connection connection : poolTrackedObjects) { try { if (connection.isConnected()) { numOfConnected++; } connection.forceDisconnect(); } catch (Exception e) { log.warn("Error while force disconnecting connection: " + connection.toIdentityString(), e); } } try { // this is just to yield the thread for a fair share of CPU Thread.sleep(1); } catch (InterruptedException e) { } } ((FailFastConnectionFactory) this.getFactory()).failFast = false; } @Override public void close() { this.destroy(); this.detachAuthenticationListener(); } } ================================================ FILE: src/main/java/redis/clients/jedis/mcf/package-info.java ================================================ /** * This package contains the classes that are related to Active-Active cluster(s) and Multi-Cluster * failover. */ @Experimental package redis.clients.jedis.mcf; import redis.clients.jedis.annots.Experimental; ================================================ FILE: src/main/java/redis/clients/jedis/params/BaseGetExParams.java ================================================ package redis.clients.jedis.params; import java.util.Objects; import redis.clients.jedis.CommandArguments; import redis.clients.jedis.Protocol.Keyword; /** * Abstract base class for setting expiration parameters for Redis GET commands. * This class provides methods to set various expiration options such as EX, PX, EXAT, PXAT, and PERSIST. * *
    *
  • {@link #ex(long)} - Set the specified expire time, in seconds.
  • *
  • {@link #px(long)} - Set the specified expire time, in milliseconds.
  • *
  • {@link #exAt(long)} - Set the specified Unix time at which the key will expire, in seconds.
  • *
  • {@link #pxAt(long)} - Set the specified Unix time at which the key will expire, in milliseconds.
  • *
  • {@link #persist()} - Remove the time to live associated with the key.
  • *
* * @param the type of the subclass extending this base class */ abstract class BaseGetExParams implements IParams { private Keyword expiration; private Long expirationValue; private T expiration(Keyword type, Long value) { this.expiration = type; this.expirationValue = value; return (T) this; } /** * Set the specified expire time, in seconds. * @return parameter object */ public T ex(long secondsToExpire) { return expiration(Keyword.EX, secondsToExpire); } /** * Set the specified expire time, in milliseconds. * @return parameter object */ public T px(long millisecondsToExpire) { return expiration(Keyword.PX, millisecondsToExpire); } /** * Set the specified Unix time at which the key will expire, in seconds. * @param seconds * @return parameter object */ public T exAt(long seconds) { return expiration(Keyword.EXAT, seconds); } /** * Set the specified Unix time at which the key will expire, in milliseconds. * @param milliseconds * @return parameter object */ public T pxAt(long milliseconds) { return expiration(Keyword.PXAT, milliseconds); } /** * Remove the time to live associated with the key. * @return parameter object */ public T persist() { return expiration(Keyword.PERSIST, null); } @Override public void addParams(CommandArguments args) { if (expiration != null) { args.add(expiration); if (expirationValue != null) { args.add(expirationValue); } } } @Override public boolean equals(Object o) { if (this == o) return true; if (o == null || getClass() != o.getClass()) return false; BaseGetExParams that = (BaseGetExParams) o; return expiration == that.expiration && Objects.equals(expirationValue, that.expirationValue); } @Override public int hashCode() { return Objects.hash(expiration, expirationValue); } } ================================================ FILE: src/main/java/redis/clients/jedis/params/BaseSetExParams.java ================================================ package redis.clients.jedis.params; import redis.clients.jedis.CommandArguments; import redis.clients.jedis.Protocol.Keyword; import java.util.Objects; /** * BaseSetExParams is a base class for setting expiration parameters for Redis keys. * It provides methods to set expiration times in seconds or milliseconds, * as well as Unix timestamps for expiration. * *
    *
  • {@link #ex(long)} - Set the specified expire time, in seconds.
  • *
  • {@link #px(long)} - Set the specified expire time, in milliseconds.
  • *
  • {@link #exAt(long)} - Set the specified Unix time at which the key will expire, in seconds.
  • *
  • {@link #pxAt(long)} - Set the specified Unix time at which the key will expire, in milliseconds.
  • *
  • {@link #keepTtl()} - Retain the time to live associated with the key.
  • *
* * @param the type of the subclass extending this base class */ class BaseSetExParams> implements IParams { private Keyword expiration; private Long expirationValue; private T expiration(Keyword type, Long value) { this.expiration = type; this.expirationValue = value; return (T) this; } /** * Set the specified expire time, in seconds. * @param remainingSeconds * @return params object */ public T ex(long remainingSeconds) { return expiration(Keyword.EX, remainingSeconds); } /** * Set the specified expire time, in milliseconds. * @param remainingMilliseconds * @return params object */ public T px(long remainingMilliseconds) { return expiration(Keyword.PX, remainingMilliseconds); } /** * Set the specified Unix time at which the key will expire, in seconds. * @param timestampSeconds * @return params object */ public T exAt(long timestampSeconds) { return expiration(Keyword.EXAT, timestampSeconds); } /** * Set the specified Unix time at which the key will expire, in milliseconds. * @param timestampMilliseconds * @return params object */ public T pxAt(long timestampMilliseconds) { return expiration(Keyword.PXAT, timestampMilliseconds); } /** * @deprecated Use {@link BaseSetExParams#keepTtl()}. * @return params object */ @Deprecated public T keepttl() { return keepTtl(); } /** * Retain the time to live associated with the key. * @return params object */ public T keepTtl() { return expiration(Keyword.KEEPTTL, null); } @Override public void addParams(CommandArguments args) { if (expiration != null) { args.add(expiration); if (expirationValue != null) { args.add(expirationValue); } } } @Override public boolean equals(Object o) { if (this == o) return true; if (o == null || getClass() != o.getClass()) return false; BaseSetExParams setParams = (BaseSetExParams) o; return Objects.equals(expiration, setParams.expiration) && Objects.equals(expirationValue, setParams.expirationValue); } @Override public int hashCode() { return Objects.hash(expiration, expirationValue); } } ================================================ FILE: src/main/java/redis/clients/jedis/params/BitPosParams.java ================================================ package redis.clients.jedis.params; import redis.clients.jedis.CommandArguments; import redis.clients.jedis.args.BitCountOption; import java.util.Objects; public class BitPosParams implements IParams { private Long start; private Long end; private BitCountOption modifier; public BitPosParams() { } // TODO: deprecate ?? public BitPosParams(long start) { this.start = start; } // TODO: deprecate ?? public BitPosParams(long start, long end) { this(start); this.end = end; } public static BitPosParams bitPosParams() { return new BitPosParams(); } public BitPosParams start(long start) { this.start = start; return this; } /** * {@link BitPosParams#start(long) START} must be set for END option. */ public BitPosParams end(long end) { this.end = end; return this; } /** * Both {@link BitPosParams#start(long) START} and {@link BitPosParams#end(long) END} both must be * set for MODIFIER option. */ public BitPosParams modifier(BitCountOption modifier) { this.modifier = modifier; return this; } @Override public void addParams(CommandArguments args) { if (start != null) { args.add(start); if (end != null) { args.add(end); if (modifier != null) { args.add(modifier); } } } } @Override public boolean equals(Object o) { if (this == o) return true; if (o == null || getClass() != o.getClass()) return false; BitPosParams that = (BitPosParams) o; return Objects.equals(start, that.start) && Objects.equals(end, that.end) && Objects.equals(modifier, that.modifier); } @Override public int hashCode() { return Objects.hash(start, end, modifier); } } ================================================ FILE: src/main/java/redis/clients/jedis/params/ClientKillParams.java ================================================ package redis.clients.jedis.params; import java.util.ArrayList; import java.util.Objects; import redis.clients.jedis.CommandArguments; import redis.clients.jedis.Protocol.Keyword; import redis.clients.jedis.args.ClientType; import redis.clients.jedis.util.KeyValue; public class ClientKillParams implements IParams { public static enum SkipMe { YES, NO; } private final ArrayList> params = new ArrayList<>(); public ClientKillParams() { } public static ClientKillParams clientKillParams() { return new ClientKillParams(); } private ClientKillParams addParam(Keyword key, Object value) { params.add(KeyValue.of(key, value)); return this; } public ClientKillParams id(String clientId) { return addParam(Keyword.ID, clientId); } public ClientKillParams id(byte[] clientId) { return addParam(Keyword.ID, clientId); } public ClientKillParams type(ClientType type) { return addParam(Keyword.TYPE, type); } public ClientKillParams addr(String ipPort) { return addParam(Keyword.ADDR, ipPort); } public ClientKillParams addr(byte[] ipPort) { return addParam(Keyword.ADDR, ipPort); } public ClientKillParams addr(String ip, int port) { return addParam(Keyword.ADDR, ip + ':' + port); } public ClientKillParams skipMe(SkipMe skipMe) { return addParam(Keyword.SKIPME, skipMe); } public ClientKillParams user(String username) { return addParam(Keyword.USER, username); } public ClientKillParams laddr(String ipPort) { return addParam(Keyword.LADDR, ipPort); } public ClientKillParams laddr(String ip, int port) { return addParam(Keyword.LADDR, ip + ':' + port); } /** * Kill clients older than {@code maxAge} seconds. * * @param maxAge Clients older than this number of seconds will be killed. * @return The {@code ClientKillParams} instance, for call chaining. */ public ClientKillParams maxAge(long maxAge) { return addParam(Keyword.MAXAGE, maxAge); } @Override public void addParams(CommandArguments args) { params.forEach(kv -> args.add(kv.getKey()).add(kv.getValue())); } @Override public boolean equals(Object o) { if (this == o) return true; if (o == null || getClass() != o.getClass()) return false; ClientKillParams that = (ClientKillParams) o; return Objects.equals(params, that.params); } @Override public int hashCode() { return Objects.hash(params); } } ================================================ FILE: src/main/java/redis/clients/jedis/params/CommandListFilterByParams.java ================================================ package redis.clients.jedis.params; import redis.clients.jedis.CommandArguments; import redis.clients.jedis.Protocol.Keyword; import java.util.Objects; public class CommandListFilterByParams implements IParams { private String moduleName; private String category; private String pattern; public static CommandListFilterByParams commandListFilterByParams() { return new CommandListFilterByParams(); } public CommandListFilterByParams filterByModule(String moduleName) { this.moduleName = moduleName; return this; } public CommandListFilterByParams filterByAclCat(String category) { this.category = category; return this; } public CommandListFilterByParams filterByPattern(String pattern) { this.pattern = pattern; return this; } @Override public void addParams(CommandArguments args) { args.add(Keyword.FILTERBY); if (moduleName != null && category == null && pattern == null) { args.add(Keyword.MODULE); args.add(moduleName); } else if (moduleName == null && category != null && pattern == null) { args.add(Keyword.ACLCAT); args.add(category); } else if (moduleName == null && category == null && pattern != null) { args.add(Keyword.PATTERN); args.add(pattern); } else { throw new IllegalArgumentException("Must choose exactly one filter in " + getClass().getSimpleName()); } } @Override public boolean equals(Object o) { if (this == o) return true; if (o == null || getClass() != o.getClass()) return false; CommandListFilterByParams that = (CommandListFilterByParams) o; return Objects.equals(moduleName, that.moduleName) && Objects.equals(category, that.category) && Objects.equals(pattern, that.pattern); } @Override public int hashCode() { return Objects.hash(moduleName, category, pattern); } } ================================================ FILE: src/main/java/redis/clients/jedis/params/FailoverParams.java ================================================ package redis.clients.jedis.params; import redis.clients.jedis.CommandArguments; import redis.clients.jedis.HostAndPort; import redis.clients.jedis.Protocol.Keyword; import java.util.Objects; public class FailoverParams implements IParams { private HostAndPort to; private boolean force; private Long timeout; public static FailoverParams failoverParams() { return new FailoverParams(); } public FailoverParams to(String host, int port) { return to(new HostAndPort(host, port)); } public FailoverParams to(HostAndPort to) { this.to = to; return this; } /** * Both TO ({@link FailoverParams#to(redis.clients.jedis.HostAndPort)} or * {@link FailoverParams#to(java.lang.String, int)}) and * {@link FailoverParams#timeout(long) TIMEOUT} must be set in order for FORCE option. */ public FailoverParams force() { this.force = true; return this; } public FailoverParams timeout(long timeout) { this.timeout = timeout; return this; } @Override public void addParams(CommandArguments args) { if (to != null) { args.add(Keyword.TO).add(to.getHost()).add(to.getPort()); } if (force) { if (to == null || timeout == null) { throw new IllegalArgumentException("FAILOVER with force option requires both a timeout and target HOST and IP."); } args.add(Keyword.FORCE); } if (timeout != null) { args.add(Keyword.TIMEOUT).add(timeout); } } @Override public boolean equals(Object o) { if (this == o) return true; if (o == null || getClass() != o.getClass()) return false; FailoverParams that = (FailoverParams) o; return force == that.force && Objects.equals(to, that.to) && Objects.equals(timeout, that.timeout); } @Override public int hashCode() { return Objects.hash(to, force, timeout); } } ================================================ FILE: src/main/java/redis/clients/jedis/params/GeoAddParams.java ================================================ package redis.clients.jedis.params; import redis.clients.jedis.CommandArguments; import redis.clients.jedis.Protocol.Keyword; import java.util.Objects; public class GeoAddParams implements IParams { private boolean nx = false; private boolean xx = false; private boolean ch = false; public GeoAddParams() { } public static GeoAddParams geoAddParams() { return new GeoAddParams(); } /** * Don't update already existing elements. Always add new elements. * @return GetExParams */ public GeoAddParams nx() { this.nx = true; return this; } /** * Only update elements that already exist. Never add elements. * @return GetExParams */ public GeoAddParams xx() { this.xx = true; return this; } /** * Modify the return value from the number of new elements added, to the total number of elements * changed * @return GetExParams */ public GeoAddParams ch() { this.ch = true; return this; } @Override public void addParams(CommandArguments args) { if (nx) { args.add(Keyword.NX); } else if (xx) { args.add(Keyword.XX); } if (ch) { args.add(Keyword.CH); } } @Override public boolean equals(Object o) { if (this == o) return true; if (o == null || getClass() != o.getClass()) return false; GeoAddParams that = (GeoAddParams) o; return nx == that.nx && xx == that.xx && ch == that.ch; } @Override public int hashCode() { return Objects.hash(nx, xx, ch); } } ================================================ FILE: src/main/java/redis/clients/jedis/params/GeoRadiusParam.java ================================================ package redis.clients.jedis.params; import redis.clients.jedis.CommandArguments; import redis.clients.jedis.Protocol.Keyword; import redis.clients.jedis.args.SortingOrder; public class GeoRadiusParam implements IParams { private boolean withCoord = false; private boolean withDist = false; private boolean withHash = false; private Integer count = null; private boolean any = false; private SortingOrder sortingOrder = null; public GeoRadiusParam() { } public static GeoRadiusParam geoRadiusParam() { return new GeoRadiusParam(); } public GeoRadiusParam withCoord() { withCoord = true; return this; } public GeoRadiusParam withDist() { withDist = true; return this; } public GeoRadiusParam withHash() { withHash = true; return this; } public GeoRadiusParam sortAscending() { return sortingOrder(SortingOrder.ASC); } public GeoRadiusParam sortDescending() { return sortingOrder(SortingOrder.DESC); } public GeoRadiusParam sortingOrder(SortingOrder order) { this.sortingOrder = order; return this; } public GeoRadiusParam count(int count) { this.count = count; return this; } public GeoRadiusParam count(int count, boolean any) { this.count = count; this.any = any; return this; } public GeoRadiusParam any() { if (this.count == null) { throw new IllegalArgumentException("COUNT must be set before ANY to be set"); } this.any = true; return this; } @Override public void addParams(CommandArguments args) { if (withCoord) { args.add(Keyword.WITHCOORD); } if (withDist) { args.add(Keyword.WITHDIST); } if (withHash) { args.add(Keyword.WITHHASH); } if (count != null) { args.add(Keyword.COUNT).add(count); if (any) { args.add(Keyword.ANY); } } if (sortingOrder != null) { args.add(sortingOrder); } } } ================================================ FILE: src/main/java/redis/clients/jedis/params/GeoRadiusStoreParam.java ================================================ package redis.clients.jedis.params; import redis.clients.jedis.CommandArguments; import redis.clients.jedis.Protocol.Keyword; public class GeoRadiusStoreParam implements IParams { private boolean store = false; private boolean storeDist = false; private String key; public GeoRadiusStoreParam() { } public static GeoRadiusStoreParam geoRadiusStoreParam() { return new GeoRadiusStoreParam(); } /** * WARNING: In Redis, if STOREDIST exists, store will be ignored. *

* Refer: https://github.com/antirez/redis/blob/6.0/src/geo.c#L649 */ public GeoRadiusStoreParam store(String key) { if (key != null) { this.store = true; this.key = key; } return this; } public GeoRadiusStoreParam storeDist(String key) { if (key != null) { this.storeDist = true; this.key = key; } return this; } @Override public void addParams(CommandArguments args) { if (storeDist) { args.add(Keyword.STOREDIST).key(key); } else if (store) { args.add(Keyword.STORE).key(key); } else { throw new IllegalArgumentException(this.getClass().getSimpleName() + " must has store or storedist option"); } } } ================================================ FILE: src/main/java/redis/clients/jedis/params/GeoSearchParam.java ================================================ package redis.clients.jedis.params; import redis.clients.jedis.CommandArguments; import redis.clients.jedis.GeoCoordinate; import redis.clients.jedis.Protocol.Keyword; import redis.clients.jedis.args.GeoUnit; import redis.clients.jedis.args.SortingOrder; public class GeoSearchParam implements IParams { private boolean fromMember = false; private boolean fromLonLat = false; private String member; private GeoCoordinate coord; private boolean byRadius = false; private boolean byBox = false; private double radius; private double width; private double height; private GeoUnit unit; private boolean withCoord = false; private boolean withDist = false; private boolean withHash = false; private Integer count = null; private boolean any = false; private SortingOrder sortingOrder = null; public GeoSearchParam() { } public static GeoSearchParam geoSearchParam() { return new GeoSearchParam(); } public GeoSearchParam fromMember(String member) { this.fromMember = true; this.member = member; return this; } public GeoSearchParam fromLonLat(double longitude, double latitude) { this.fromLonLat = true; this.coord = new GeoCoordinate(longitude, latitude); return this; } public GeoSearchParam fromLonLat(GeoCoordinate coord) { this.fromLonLat = true; this.coord = coord; return this; } public GeoSearchParam byRadius(double radius, GeoUnit unit){ this.byRadius = true; this.radius = radius; this.unit = unit; return this; } public GeoSearchParam byBox(double width, double height, GeoUnit unit){ this.byBox = true; this.width = width; this.height = height; this.unit = unit; return this; } public GeoSearchParam withCoord() { withCoord = true; return this; } public GeoSearchParam withDist() { withDist = true; return this; } public GeoSearchParam withHash() { withHash = true; return this; } public GeoSearchParam asc() { return sortingOrder(SortingOrder.ASC); } public GeoSearchParam desc() { return sortingOrder(SortingOrder.DESC); } public GeoSearchParam sortingOrder(SortingOrder order) { sortingOrder = order; return this; } public GeoSearchParam count(int count) { this.count = count; return this; } public GeoSearchParam count(int count, boolean any) { this.count = count; this.any = true; return this; } public GeoSearchParam any() { if (this.count == null) { throw new IllegalArgumentException("COUNT must be set before ANY to be set"); } this.any = true; return this; } @Override public void addParams(CommandArguments args) { if (fromMember && fromLonLat) { throw new IllegalArgumentException("Both FROMMEMBER and FROMLONLAT cannot be used."); } else if (fromMember) { args.add(Keyword.FROMMEMBER).add(member); } else if (fromLonLat) { args.add(Keyword.FROMLONLAT).add(coord.getLongitude()).add(coord.getLatitude()); } else { throw new IllegalArgumentException("Either FROMMEMBER or FROMLONLAT must be used."); } if (byRadius && byBox) { throw new IllegalArgumentException("Both BYRADIUS and BYBOX cannot be used."); } else if (byRadius) { args.add(Keyword.BYRADIUS).add(radius).add(unit); } else if (byBox) { args.add(Keyword.BYBOX).add(width).add(height).add(unit); } else { throw new IllegalArgumentException("Either BYRADIUS or BYBOX must be used."); } if (withCoord) { args.add(Keyword.WITHCOORD); } if (withDist) { args.add(Keyword.WITHDIST); } if (withHash) { args.add(Keyword.WITHHASH); } if (count != null) { args.add(Keyword.COUNT).add(count); if (any) { args.add(Keyword.ANY); } } if (sortingOrder != null) { args.add(sortingOrder); } } } ================================================ FILE: src/main/java/redis/clients/jedis/params/GetExParams.java ================================================ package redis.clients.jedis.params; public class GetExParams extends BaseGetExParams { public static GetExParams getExParams() { return new GetExParams(); } } ================================================ FILE: src/main/java/redis/clients/jedis/params/HGetExParams.java ================================================ package redis.clients.jedis.params; /** * HGetExParams is a parameter class used when getting values of given keys in hash data with * optionally setting/changing expiry time in Redis. * It can be used to set the expiry time of the key in seconds or milliseconds. * *

This class includes the following methods:

*
    *
  • {@link #hGetExParams()} - Static factory method to create a new instance of HGetExParams.
  • *
  • {@link #ex(long)} - Set the specified expire time, in seconds.
  • *
  • {@link #px(long)} - Set the specified expire time, in milliseconds.
  • *
  • {@link #exAt(long)} - Set the specified Unix(epoch) time at which the key will expire, in seconds.
  • *
  • {@link #pxAt(long)} - Set the specified Unix(epoch) time at which the key will expire, in milliseconds.
  • *
  • {@link #persist()} - Remove the time-to-live associated with the key.
  • *
* *

Example usage:

*
 * {@code
 * HGetExParams params = HGetExParams.hGetExParams().persist();
 * }
 * 
* * @see BaseGetExParams */ public class HGetExParams extends BaseGetExParams { public static HGetExParams hGetExParams() { return new HGetExParams(); } } ================================================ FILE: src/main/java/redis/clients/jedis/params/HSetExParams.java ================================================ package redis.clients.jedis.params; import java.util.Objects; import redis.clients.jedis.CommandArguments; import redis.clients.jedis.Protocol.Keyword; /** * HSetExParams is a parameter class used when setting key-value pairs of hash data with * optional expiry and existance conditions in Redis. * It provides methods to specify whether the key should be set * only if it already exists or only if it does not already exist. * It can also be used to set the expiry time of the key in seconds or milliseconds. * *

This class includes the following methods:

*
    *
  • {@link #hSetExParams()} - Static factory method to create a new instance of HSetExParams.
  • *
  • {@link #fnx()} - Sets the condition to only set the key if it does not already exist.
  • *
  • {@link #fxx()} - Sets the condition to only set the key if it already exists.
  • *
  • {@link #ex(long)} - Set the specified expire time, in seconds.
  • *
  • {@link #px(long)} - Set the specified expire time, in milliseconds.
  • *
  • {@link #exAt(long)} - Set the specified Unix(epoch) time at which the key will expire, in seconds.
  • *
  • {@link #pxAt(long)} - Set the specified Unix(epoch) time at which the key will expire, in milliseconds.
  • *
  • {@link #keepTtl()} - Retain the time to live associated with the key.
  • *
* *

Example usage:

*
 * {@code
 * HSetExParams params = HSetExParams.hSetExParams().fnx();
 * }
 * 
* * @see BaseSetExParams */ public class HSetExParams extends BaseSetExParams { private Keyword existance; public static HSetExParams hSetExParams() { return new HSetExParams(); } /** * Only set the key if it does not already exist. * @return HSetExParams */ public HSetExParams fnx() { this.existance = Keyword.FNX; return this; } /** * Only set the key if it already exist. * @return HSetExParams */ public HSetExParams fxx() { this.existance = Keyword.FXX; return this; } @Override public void addParams(CommandArguments args) { if (existance != null) { args.add(existance); } super.addParams(args); } @Override public boolean equals(Object o) { if (this == o) return true; if (o == null || getClass() != o.getClass()) return false; HSetExParams setParams = (HSetExParams) o; return Objects.equals(existance, setParams.existance) && super.equals((BaseSetExParams) o); } @Override public int hashCode() { return Objects.hash(existance, super.hashCode()); } } ================================================ FILE: src/main/java/redis/clients/jedis/params/HotkeysParams.java ================================================ package redis.clients.jedis.params; import redis.clients.jedis.CommandArguments; import redis.clients.jedis.Protocol.Keyword; import redis.clients.jedis.args.HotkeysMetric; import redis.clients.jedis.util.JedisAsserts; import java.util.Arrays; import java.util.Objects; /** * Parameters for the HOTKEYS START command. */ public class HotkeysParams implements IParams { private HotkeysMetric[] metrics; private Integer count; private Integer duration; private Integer sample; private int[] slots; public HotkeysParams() { } public static HotkeysParams hotkeysParams() { return new HotkeysParams(); } /** * Specifies which metrics to track. At least one metric is required. * @param metrics the metrics to track (CPU, NET) * @return this */ public HotkeysParams metrics(HotkeysMetric... metrics) { JedisAsserts.notNull(metrics, "metrics must not be null"); JedisAsserts.isTrue(metrics.length > 0, "at least one metric is required"); this.metrics = metrics; return this; } /** * Maximum number of hot keys to track. * @param count must be between 10 and 64 * @return this * @throws IllegalArgumentException if count is not between 10 and 64 */ public HotkeysParams count(int count) { JedisAsserts.isTrue(count >= 10 && count <= 64, "count must be between 1 and 64"); this.count = count; return this; } /** * Auto-stop tracking after the specified number of seconds. * @param duration 0 means no auto-stop * @return this * @throws IllegalArgumentException if duration is negative */ public HotkeysParams duration(int duration) { JedisAsserts.isTrue(duration >= 0, "duration must be >= 0"); this.duration = duration; return this; } /** * Sample 1 in N commands. * @param sample 1 means all commands are sampled * @return this * @throws IllegalArgumentException if sample is less than 1 */ public HotkeysParams sample(int sample) { JedisAsserts.isTrue(sample >= 1, "sample must be >= 1"); this.sample = sample; return this; } /** * Filter by hash slots (cluster mode only). * @param slots the hash slots to filter (0-16383) * @return this * @throws IllegalArgumentException if any slot is not between 0 and 16383 */ public HotkeysParams slots(int... slots) { if (slots != null) { for (int slot : slots) { JedisAsserts.isTrue(slot >= 0 && slot <= 16383, "each slot must be between 0 and 16383"); } } this.slots = slots; return this; } @Override public void addParams(CommandArguments args) { JedisAsserts.notNull(metrics, "metrics must not be null"); JedisAsserts.isTrue(metrics.length > 0, "at least one metric is required"); args.add(Keyword.METRICS); args.add(metrics.length); for (HotkeysMetric metric : metrics) { args.add(metric); } if (count != null) { args.add(Keyword.COUNT); args.add(count); } if (duration != null) { args.add(Keyword.DURATION); args.add(duration); } if (sample != null) { args.add(Keyword.SAMPLE); args.add(sample); } if (slots != null && slots.length > 0) { args.add(Keyword.SLOTS); args.add(slots.length); for (int slot : slots) { args.add(slot); } } } @Override public boolean equals(Object o) { if (this == o) return true; if (o == null || getClass() != o.getClass()) return false; HotkeysParams that = (HotkeysParams) o; return Arrays.equals(metrics, that.metrics) && Objects.equals(count, that.count) && Objects.equals(duration, that.duration) && Objects.equals(sample, that.sample) && Arrays.equals(slots, that.slots); } @Override public int hashCode() { int result = Objects.hash(count, duration, sample); result = 31 * result + Arrays.hashCode(metrics); result = 31 * result + Arrays.hashCode(slots); return result; } } ================================================ FILE: src/main/java/redis/clients/jedis/params/IParams.java ================================================ package redis.clients.jedis.params; import redis.clients.jedis.CommandArguments; public interface IParams { void addParams(CommandArguments args); } ================================================ FILE: src/main/java/redis/clients/jedis/params/LCSParams.java ================================================ package redis.clients.jedis.params; import redis.clients.jedis.CommandArguments; import redis.clients.jedis.Protocol.Keyword; import java.util.Objects; public class LCSParams implements IParams { private boolean len = false; private boolean idx = false; private Long minMatchLen; private boolean withMatchLen = false; public static LCSParams LCSParams() { return new LCSParams(); } /** * When LEN is given the command returns the length of the longest common substring. * @return LCSParams */ public LCSParams len() { this.len = true; return this; } /** * When IDX is given the command returns an array with the LCS length * and all the ranges in both the strings, start and end offset for * each string, where there are matches. * @return LCSParams */ public LCSParams idx() { this.idx = true; return this; } /** * Specify the minimum match length. * @return LCSParams */ public LCSParams minMatchLen(long minMatchLen) { this.minMatchLen = minMatchLen; return this; } /** * When WITHMATCHLEN is given each array representing a match will also have the length of the match. * @return LCSParams */ public LCSParams withMatchLen() { this.withMatchLen = true; return this; } @Override public void addParams(CommandArguments args) { if (len) { args.add(Keyword.LEN); } if (idx) { args.add(Keyword.IDX); } if (minMatchLen != null) { args.add(Keyword.MINMATCHLEN).add(minMatchLen); } if (withMatchLen) { args.add(Keyword.WITHMATCHLEN); } } @Override public boolean equals(Object o) { if (this == o) return true; if (o == null || getClass() != o.getClass()) return false; LCSParams lcsParams = (LCSParams) o; return len == lcsParams.len && idx == lcsParams.idx && withMatchLen == lcsParams.withMatchLen && Objects.equals(minMatchLen, lcsParams.minMatchLen); } @Override public int hashCode() { return Objects.hash(len, idx, minMatchLen, withMatchLen); } } ================================================ FILE: src/main/java/redis/clients/jedis/params/LPosParams.java ================================================ package redis.clients.jedis.params; import redis.clients.jedis.CommandArguments; import redis.clients.jedis.Protocol.Keyword; import java.util.Objects; public class LPosParams implements IParams { private Integer rank; private Integer maxlen; public static LPosParams lPosParams() { return new LPosParams(); } public LPosParams rank(int rank) { this.rank = rank; return this; } public LPosParams maxlen(int maxLen) { this.maxlen = maxLen; return this; } @Override public void addParams(CommandArguments args) { if (rank != null) { args.add(Keyword.RANK).add(rank); } if (maxlen != null) { args.add(Keyword.MAXLEN).add(maxlen); } } @Override public boolean equals(Object o) { if (this == o) return true; if (o == null || getClass() != o.getClass()) return false; LPosParams that = (LPosParams) o; return Objects.equals(rank, that.rank) && Objects.equals(maxlen, that.maxlen); } @Override public int hashCode() { return Objects.hash(rank, maxlen); } } ================================================ FILE: src/main/java/redis/clients/jedis/params/LolwutParams.java ================================================ package redis.clients.jedis.params; import redis.clients.jedis.CommandArguments; import redis.clients.jedis.Protocol.Keyword; import java.util.Arrays; import java.util.Objects; public class LolwutParams implements IParams { private Integer version; private String[] opargs; public LolwutParams version(int version) { this.version = version; return this; } @Deprecated public LolwutParams args(String... args) { return optionalArguments(args); } public LolwutParams optionalArguments(String... args) { this.opargs = args; return this; } @Override public void addParams(CommandArguments args) { if (version != null) { args.add(Keyword.VERSION).add(version); if (opargs != null && opargs.length > 0) { args.addObjects((Object[]) opargs); } } } @Override public boolean equals(Object o) { if (this == o) return true; if (o == null || getClass() != o.getClass()) return false; LolwutParams that = (LolwutParams) o; return Objects.equals(version, that.version) && Arrays.equals(opargs, that.opargs); } @Override public int hashCode() { int result = Objects.hash(version); result = 31 * result + Arrays.hashCode(opargs); return result; } } ================================================ FILE: src/main/java/redis/clients/jedis/params/MSetExParams.java ================================================ package redis.clients.jedis.params; import redis.clients.jedis.CommandArguments; import redis.clients.jedis.Protocol.Keyword; import java.util.Objects; public class MSetExParams extends BaseSetExParams { private Keyword existance; public static MSetExParams setParams() { return new MSetExParams(); } /** * Only set the key if it does not already exist. * @return {@code this} */ public MSetExParams nx() { this.existance = Keyword.NX; return this; } /** * Only set the key if it already exist. * @return {@code this} */ public MSetExParams xx() { this.existance = Keyword.XX; return this; } @Override public void addParams(CommandArguments args) { if (existance != null) { args.add(existance); } super.addParams(args); } @Override public boolean equals(Object o) { if (this == o) return true; if (o == null || getClass() != o.getClass()) return false; MSetExParams setParams = (MSetExParams) o; return Objects.equals(existance, setParams.existance) && super.equals(o); } @Override public int hashCode() { return Objects.hash(existance, super.hashCode()); } } ================================================ FILE: src/main/java/redis/clients/jedis/params/MigrateParams.java ================================================ package redis.clients.jedis.params; import redis.clients.jedis.CommandArguments; import redis.clients.jedis.Protocol.Keyword; import java.util.Objects; public class MigrateParams implements IParams { private boolean copy = false; private boolean replace = false; private String username = null; private String password = null; public MigrateParams() { } public static MigrateParams migrateParams() { return new MigrateParams(); } public MigrateParams copy() { this.copy = true; return this; } public MigrateParams replace() { this.replace = true; return this; } public MigrateParams auth(String password) { this.password = password; return this; } public MigrateParams auth2(String username, String password) { this.username = username; this.password = password; return this; } @Override public void addParams(CommandArguments args) { if (copy) { args.add(Keyword.COPY); } if (replace) { args.add(Keyword.REPLACE); } if (username != null) { args.add(Keyword.AUTH2).add(username).add(password); } else if (password != null) { args.add(Keyword.AUTH).add(password); } } @Override public boolean equals(Object o) { if (this == o) return true; if (o == null || getClass() != o.getClass()) return false; MigrateParams that = (MigrateParams) o; return copy == that.copy && replace == that.replace && Objects.equals(username, that.username) && Objects.equals(password, that.password); } @Override public int hashCode() { return Objects.hash(copy, replace, username, password); } } ================================================ FILE: src/main/java/redis/clients/jedis/params/ModuleLoadExParams.java ================================================ package redis.clients.jedis.params; import java.util.ArrayList; import java.util.List; import java.util.Objects; import redis.clients.jedis.CommandArguments; import redis.clients.jedis.Protocol.Keyword; import redis.clients.jedis.util.KeyValue; public class ModuleLoadExParams implements IParams { private final List> configs = new ArrayList<>(); private final List args = new ArrayList<>(); public ModuleLoadExParams() { } public ModuleLoadExParams moduleLoadexParams() { return new ModuleLoadExParams(); } public ModuleLoadExParams config(String name, String value) { this.configs.add(KeyValue.of(name, value)); return this; } public ModuleLoadExParams arg(String arg) { this.args.add(arg); return this; } @Override public void addParams(CommandArguments args) { this.configs.forEach(kv -> args.add(Keyword.CONFIG).add(kv.getKey()).add(kv.getValue())); if (!this.args.isEmpty()) { args.add(Keyword.ARGS).addObjects(this.args); } } @Override public boolean equals(Object o) { if (this == o) return true; if (o == null || getClass() != o.getClass()) return false; ModuleLoadExParams that = (ModuleLoadExParams) o; return Objects.equals(configs, that.configs) && Objects.equals(args, that.args); } @Override public int hashCode() { return Objects.hash(configs, args); } } ================================================ FILE: src/main/java/redis/clients/jedis/params/RestoreParams.java ================================================ package redis.clients.jedis.params; import redis.clients.jedis.CommandArguments; import redis.clients.jedis.Protocol.Keyword; import java.util.Objects; public class RestoreParams implements IParams { private boolean replace; private boolean absTtl; private Long idleTime; private Long frequency; public static RestoreParams restoreParams() { return new RestoreParams(); } public RestoreParams replace() { this.replace = true; return this; } public RestoreParams absTtl() { this.absTtl = true; return this; } public RestoreParams idleTime(long idleTime) { this.idleTime = idleTime; return this; } public RestoreParams frequency(long frequency) { this.frequency = frequency; return this; } @Override public void addParams(CommandArguments args) { if (replace) { args.add(Keyword.REPLACE); } if (absTtl) { args.add(Keyword.ABSTTL); } if (idleTime != null) { args.add(Keyword.IDLETIME).add(idleTime); } if (frequency != null) { args.add(Keyword.FREQ).add(frequency); } } @Override public boolean equals(Object o) { if (this == o) return true; if (o == null || getClass() != o.getClass()) return false; RestoreParams that = (RestoreParams) o; return replace == that.replace && absTtl == that.absTtl && Objects.equals(idleTime, that.idleTime) && Objects.equals(frequency, that.frequency); } @Override public int hashCode() { return Objects.hash(replace, absTtl, idleTime, frequency); } } ================================================ FILE: src/main/java/redis/clients/jedis/params/ScanParams.java ================================================ package redis.clients.jedis.params; import static redis.clients.jedis.Protocol.Keyword.MATCH; import java.nio.ByteBuffer; import java.util.EnumMap; import java.util.Map; import java.util.Objects; import redis.clients.jedis.CommandArguments; import redis.clients.jedis.Protocol.Keyword; import redis.clients.jedis.Protocol; import redis.clients.jedis.util.SafeEncoder; public class ScanParams implements IParams { private final Map params = new EnumMap<>(Keyword.class); public static final String SCAN_POINTER_START = String.valueOf(0); public static final byte[] SCAN_POINTER_START_BINARY = SafeEncoder.encode(SCAN_POINTER_START); public ScanParams match(final byte[] pattern) { params.put(MATCH, ByteBuffer.wrap(pattern)); return this; } /** * @see MATCH option in Redis documentation */ public ScanParams match(final String pattern) { params.put(MATCH, ByteBuffer.wrap(SafeEncoder.encode(pattern))); return this; } /** * @see COUNT option in Redis documentation */ public ScanParams count(final Integer count) { params.put(Keyword.COUNT, ByteBuffer.wrap(Protocol.toByteArray(count))); return this; } @Override public void addParams(CommandArguments args) { for (Map.Entry param : params.entrySet()) { args.add(param.getKey()); args.add(param.getValue().array()); } } public byte[] binaryMatch() { if (params.containsKey(MATCH)) { return params.get(MATCH).array(); } else { return null; } } public String match() { if (params.containsKey(MATCH)) { return new String(params.get(MATCH).array()); } else { return null; } } @Override public boolean equals(Object o) { if (this == o) return true; if (o == null || getClass() != o.getClass()) return false; ScanParams that = (ScanParams) o; return Objects.equals(params, that.params); } @Override public int hashCode() { return Objects.hash(params); } } ================================================ FILE: src/main/java/redis/clients/jedis/params/SetParams.java ================================================ package redis.clients.jedis.params; import java.util.Objects; import redis.clients.jedis.CommandArguments; import redis.clients.jedis.Protocol.Keyword; import redis.clients.jedis.annots.Experimental; import redis.clients.jedis.util.CompareCondition; public class SetParams extends BaseSetExParams { private Keyword existance; private CompareCondition condition; public static SetParams setParams() { return new SetParams(); } /** * Only set the key if it does not already exist. * @return SetParams */ public SetParams nx() { this.existance = Keyword.NX; return this; } /** * Only set the key if it already exist. * @return SetParams */ public SetParams xx() { this.existance = Keyword.XX; return this; } /** * Set the specified expire time, in seconds. * @param remainingSeconds * @return SetParams */ @Override public SetParams ex(long remainingSeconds) { return super.ex(remainingSeconds); } /** * Set the specified expire time, in milliseconds. * @param remainingMilliseconds * @return SetParams */ @Override public SetParams px(long remainingMilliseconds) { return super.px(remainingMilliseconds); } /** * Set the specified Unix time at which the key will expire, in seconds. * @param timestampSeconds * @return SetParams */ @Override public SetParams exAt(long timestampSeconds) { return super.exAt(timestampSeconds); } /** * Set the specified Unix time at which the key will expire, in milliseconds. * @param timestampMilliseconds * @return SetParams */ @Override public SetParams pxAt(long timestampMilliseconds) { return super.pxAt(timestampMilliseconds); } /** * Retain the time to live associated with the key. * * @deprecated Since 6.1.0 use {@link #keepTtl()} instead. * @return SetParams */ @Override public SetParams keepttl() { return keepTtl(); } /** * Retain the time to live associated with the key. * @return SetParams */ @Override public SetParams keepTtl() { return super.keepTtl(); } /** * Set a compare condition for compare-and-set operations. * @param condition the condition to apply * @return {@link SetParams} */ @Experimental public SetParams condition(CompareCondition condition) { this.condition = condition; return this; } @Override public void addParams(CommandArguments args) { // Condition must be added before NX/XX per Redis command syntax if (condition != null) { condition.addTo(args); } if (existance != null) { args.add(existance); } super.addParams(args); } @Override public boolean equals(Object o) { if (this == o) return true; if (o == null || getClass() != o.getClass()) return false; SetParams setParams = (SetParams) o; return Objects.equals(existance, setParams.existance) && Objects.equals(condition, setParams.condition) && super.equals((BaseSetExParams) o); } @Override public int hashCode() { return Objects.hash(existance, condition, super.hashCode()); } } ================================================ FILE: src/main/java/redis/clients/jedis/params/ShutdownParams.java ================================================ package redis.clients.jedis.params; import redis.clients.jedis.CommandArguments; import redis.clients.jedis.Protocol.Keyword; import redis.clients.jedis.args.SaveMode; import java.util.Objects; public class ShutdownParams implements IParams { private SaveMode saveMode; private boolean now; private boolean force; public static ShutdownParams shutdownParams() { return new ShutdownParams(); } public ShutdownParams saveMode(SaveMode saveMode) { this.saveMode = saveMode; return this; } public ShutdownParams nosave() { return this.saveMode(SaveMode.NOSAVE); } public ShutdownParams save() { return this.saveMode(SaveMode.SAVE); } public ShutdownParams now() { this.now = true; return this; } public ShutdownParams force() { this.force = true; return this; } @Override public void addParams(CommandArguments args) { if (this.saveMode != null) { args.add(saveMode); } if (this.now) { args.add(Keyword.NOW); } if (this.force) { args.add(Keyword.FORCE); } } @Override public boolean equals(Object o) { if (this == o) return true; if (o == null || getClass() != o.getClass()) return false; ShutdownParams that = (ShutdownParams) o; return now == that.now && force == that.force && saveMode == that.saveMode; } @Override public int hashCode() { return Objects.hash(saveMode, now, force); } } ================================================ FILE: src/main/java/redis/clients/jedis/params/SortingParams.java ================================================ package redis.clients.jedis.params; import java.util.*; import redis.clients.jedis.CommandArguments; import redis.clients.jedis.Protocol; import redis.clients.jedis.Protocol.Keyword; import redis.clients.jedis.args.SortingOrder; import redis.clients.jedis.util.SafeEncoder; /** * Builder Class for {@code SORT} command parameters. */ // TODO: public class SortingParams implements IParams { private final List params = new ArrayList<>(); /** * Sort by weight in keys. *

* Takes a pattern that is used in order to generate the key names of the weights used for * sorting. Weight key names are obtained substituting the first occurrence of * with the actual * value of the elements on the list. *

* The pattern for a normal key/value pair is "field*" and for a value in a hash * "field*->fieldname". * @param pattern * @return the SortingParams Object */ public SortingParams by(final String pattern) { return by(SafeEncoder.encode(pattern)); } /** * Sort by weight in keys. *

* Takes a pattern that is used in order to generate the key names of the weights used for * sorting. Weight key names are obtained substituting the first occurrence of * with the actual * value of the elements on the list. *

* The pattern for a normal key/value pair is "field*" and for a value in a hash * "field*->fieldname". * @param pattern * @return the SortingParams Object */ public SortingParams by(final byte[] pattern) { params.add(Keyword.BY); params.add(pattern); return this; } /** * No sorting. *

* This is useful if you want to retrieve an external key (using {@link #get(String...) GET}) but * you don't want the sorting overhead. * @return the SortingParams Object */ public SortingParams nosort() { params.add(Keyword.BY); params.add(Keyword.NOSORT); return this; } /** * Get the Sorting in Descending Order. * @return the sortingParams Object */ public SortingParams desc() { return sortingOrder(SortingOrder.DESC); } /** * Get the Sorting in Ascending Order. This is the default order. * @return the SortingParams Object */ public SortingParams asc() { return sortingOrder(SortingOrder.ASC); } /** * Get by the Sorting Order. * @param order the Sorting order * @return the SortingParams object */ public SortingParams sortingOrder(SortingOrder order) { params.add(order.getRaw()); return this; } /** * Limit the Numbers of returned Elements. * @param start is zero based * @param count * @return the SortingParams Object */ public SortingParams limit(final int start, final int count) { params.add(Keyword.LIMIT); params.add(start); params.add(count); return this; } /** * Sort lexicographicaly. Note that Redis is utf-8 aware assuming you set the right value for the * LC_COLLATE environment variable. * @return the SortingParams Object */ public SortingParams alpha() { params.add(Keyword.ALPHA); return this; } /** * Retrieving external keys from the result of the search. *

* Takes a pattern that is used in order to generate the key names of the result of sorting. The * key names are obtained substituting the first occurrence of * with the actual value of the * elements on the list. *

* The pattern for a normal key/value pair is "field*" and for a value in a hash * "field*->fieldname". *

* To get the list itself use the char # as pattern. * @param patterns * @return the SortingParams Object */ public SortingParams get(String... patterns) { for (final String pattern : patterns) { params.add(Keyword.GET); params.add(pattern); } return this; } /** * Retrieving external keys from the result of the search. *

* Takes a pattern that is used in order to generate the key names of the result of sorting. The * key names are obtained substituting the first occurrence of * with the actual value of the * elements on the list. *

* The pattern for a normal key/value pair is "field*" and for a value in a hash * "field*->fieldname". *

* To get the list itself use the char # as pattern. * @param patterns * @return the SortingParams Object */ public SortingParams get(byte[]... patterns) { for (final byte[] pattern : patterns) { params.add(Keyword.GET); params.add(pattern); } return this; } @Override public void addParams(CommandArguments args) { args.addObjects(params); } @Override public boolean equals(Object o) { if (this == o) return true; if (o == null || getClass() != o.getClass()) return false; SortingParams that = (SortingParams) o; return Objects.equals(params, that.params); } @Override public int hashCode() { return Objects.hash(params); } } ================================================ FILE: src/main/java/redis/clients/jedis/params/VAddParams.java ================================================ package redis.clients.jedis.params; import redis.clients.jedis.CommandArguments; import redis.clients.jedis.Protocol; import redis.clients.jedis.annots.Experimental; /** * Parameters for the VADD command. */ @Experimental public class VAddParams implements IParams { private boolean cas; private QuantizationType quantization; private Integer ef; private String attributes; private Integer m; public enum QuantizationType { NOQUANT, Q8, BIN } public VAddParams() { } /** * Performs the operation partially using threads, in a check-and-set style. The neighbor * candidates collection, which is slow, is performed in the background, while the command is * executed in the main thread. * @return VAddParams */ public VAddParams cas() { this.cas = true; return this; } /** * Forces the vector to be created without int8 quantization. * @return VAddParams */ public VAddParams noQuant() { this.quantization = QuantizationType.NOQUANT; return this; } /** * Forces the vector to use signed 8-bit quantization. This is the default. * @return VAddParams */ public VAddParams q8() { this.quantization = QuantizationType.Q8; return this; } /** * Forces the vector to use binary quantization instead of int8. This is much faster and uses less * memory, but impacts the recall quality. * @return VAddParams */ public VAddParams bin() { this.quantization = QuantizationType.BIN; return this; } /** * Plays a role in the effort made to find good candidates when connecting the new node to the * existing Hierarchical Navigable Small World (HNSW) graph. The default is 200. Using a larger * value may help in achieving a better recall. * @param buildExplorationFactor the exploration factor * @return VAddParams */ public VAddParams ef(int buildExplorationFactor) { this.ef = buildExplorationFactor; return this; } /** * Associates attributes in the form of a JavaScript object to the newly created entry or updates * the attributes (if they already exist). * @param attributes the attributes as a JSON string * @return VAddParams */ public VAddParams setAttr(String attributes) { this.attributes = attributes; return this; } /** * The maximum number of connections that each node of the graph will have with other nodes. The * default is 16. More connections means more memory, but provides for more efficient graph * exploration. * @param numLinks the maximum number of connections * @return VAddParams */ public VAddParams m(int numLinks) { this.m = numLinks; return this; } @Override public void addParams(CommandArguments args) { if (cas) { args.add(Protocol.Keyword.CAS); } if (quantization != null) { switch (quantization) { case NOQUANT: args.add(Protocol.Keyword.NOQUANT); break; case Q8: args.add(Protocol.Keyword.Q8); break; case BIN: args.add(Protocol.Keyword.BIN); break; } } if (ef != null) { args.add(Protocol.Keyword.EF).add(ef); } if (attributes != null) { args.add(Protocol.Keyword.SETATTR).add(attributes); } if (m != null) { args.add(Protocol.Keyword.M).add(m); } } } ================================================ FILE: src/main/java/redis/clients/jedis/params/VSimParams.java ================================================ package redis.clients.jedis.params; import redis.clients.jedis.CommandArguments; import redis.clients.jedis.Protocol; import redis.clients.jedis.annots.Experimental; /** * Parameters for the VSIM command. */ @Experimental public class VSimParams implements IParams { private Integer count; private Double epsilon; private Integer ef; private String filter; private Integer filterEf; private boolean truth; private boolean noThread; public VSimParams() { } /** * Limits the number of returned results. * @param num the maximum number of results to return * @return VSimParams */ public VSimParams count(int num) { this.count = num; return this; } /** * Sets the epsilon (delta) parameter for distance-based filtering. Only elements with a * similarity score of (1 - epsilon) or better are returned. For example, epsilon=0.2 means only * elements with similarity >= 0.8 are returned. * @param delta a floating point number between 0 and 1 * @return VSimParams */ public VSimParams epsilon(double delta) { this.epsilon = delta; return this; } /** * Controls the search effort. Higher values explore more nodes, improving recall at the cost of * speed. Typical values range from 50 to 1000. * @param searchExplorationFactor the exploration factor * @return VSimParams */ public VSimParams ef(int searchExplorationFactor) { this.ef = searchExplorationFactor; return this; } /** * Applies a filter expression to restrict matching elements. * @param expression the filter expression * @return VSimParams */ public VSimParams filter(String expression) { this.filter = expression; return this; } /** * Limits the number of filtering attempts for the FILTER expression. * @param maxFilteringEffort the maximum filtering effort * @return VSimParams */ public VSimParams filterEf(int maxFilteringEffort) { this.filterEf = maxFilteringEffort; return this; } /** * Forces an exact linear scan of all elements, bypassing the HNSW graph. Use for benchmarking or * to calculate recall. This is significantly slower (O(N)). * @return VSimParams */ public VSimParams truth() { this.truth = true; return this; } /** * Executes the search in the main thread instead of a background thread. Useful for small vector * sets or benchmarks. This may block the server during execution. * @return VSimParams */ public VSimParams noThread() { this.noThread = true; return this; } @Override public void addParams(CommandArguments args) { if (count != null) { args.add(Protocol.Keyword.COUNT).add(count); } if (epsilon != null) { args.add(Protocol.Keyword.EPSILON).add(epsilon); } if (ef != null) { args.add(Protocol.Keyword.EF).add(ef); } if (filter != null) { args.add(Protocol.Keyword.FILTER).add(filter); } if (filterEf != null) { args.add(Protocol.Keyword.FILTER_EF).add(filterEf); } if (truth) { args.add(Protocol.Keyword.TRUTH); } if (noThread) { args.add(Protocol.Keyword.NOTHREAD); } } } ================================================ FILE: src/main/java/redis/clients/jedis/params/XAddParams.java ================================================ package redis.clients.jedis.params; import redis.clients.jedis.CommandArguments; import redis.clients.jedis.Protocol; import redis.clients.jedis.Protocol.Keyword; import redis.clients.jedis.StreamEntryID; import redis.clients.jedis.args.Rawable; import redis.clients.jedis.args.RawableFactory; import redis.clients.jedis.args.StreamDeletionPolicy; import redis.clients.jedis.util.SafeEncoder; import java.util.Arrays; import java.util.Objects; public class XAddParams implements IParams { private Rawable id; private Long maxLen; private boolean approximateTrimming; private boolean exactTrimming; private boolean nomkstream; private String minId; private Long limit; private StreamDeletionPolicy trimMode; private byte[] producerId; private byte[] idempotentId; private boolean idmpAuto; public static XAddParams xAddParams() { return new XAddParams(); } public XAddParams noMkStream() { this.nomkstream = true; return this; } public XAddParams id(byte[] id) { this.id = RawableFactory.from(id); return this; } public XAddParams id(String id) { this.id = RawableFactory.from(id); return this; } public XAddParams id(StreamEntryID id) { return id(id.toString()); } public XAddParams id(long time, long sequence) { return id(time + "-" + sequence); } public XAddParams id(long time) { return id(time + "-*"); } public XAddParams maxLen(long maxLen) { this.maxLen = maxLen; return this; } public XAddParams minId(String minId) { this.minId = minId; return this; } public XAddParams approximateTrimming() { this.approximateTrimming = true; return this; } public XAddParams exactTrimming() { this.exactTrimming = true; return this; } public XAddParams limit(long limit) { this.limit = limit; return this; } /** * When trimming, defines desired behaviour for handling consumer group references. * see {@link StreamDeletionPolicy} for details. * * @return XAddParams */ public XAddParams trimmingMode(StreamDeletionPolicy trimMode) { this.trimMode = trimMode; return this; } /** * Enable idempotent producer mode with automatic idempotent ID generation. * Redis will calculate an idempotent ID based on the message content. * * @param producerId unique producer identifier (binary) * @return XAddParams */ public XAddParams idmpAuto(byte[] producerId) { this.producerId = producerId; this.idmpAuto = true; this.idempotentId = null; return this; } /** * Enable idempotent producer mode with automatic idempotent ID generation. * Redis will calculate an idempotent ID based on the message content. * * @param producerId unique producer identifier (string) * @return XAddParams */ public XAddParams idmpAuto(String producerId) { return idmpAuto(SafeEncoder.encode(producerId)); } /** * Enable idempotent producer mode with explicit idempotent ID. * The caller provides both producer ID and idempotent ID. * * @param producerId unique producer identifier (binary) * @param idempotentId unique idempotent identifier for this message (binary) * @return XAddParams */ public XAddParams idmp(byte[] producerId, byte[] idempotentId) { this.producerId = producerId; this.idempotentId = idempotentId; this.idmpAuto = false; return this; } /** * Enable idempotent producer mode with explicit idempotent ID. * The caller provides both producer ID and idempotent ID. * * @param producerId unique producer identifier (string) * @param idempotentId unique idempotent identifier for this message (string) * @return XAddParams */ public XAddParams idmp(String producerId, String idempotentId) { return idmp(SafeEncoder.encode(producerId), SafeEncoder.encode(idempotentId)); } @Override public void addParams(CommandArguments args) { if (nomkstream) { args.add(Keyword.NOMKSTREAM); } if (trimMode != null) { args.add(trimMode); } if (producerId != null) { if (idmpAuto) { args.add(Keyword.IDMPAUTO).add(producerId); } else if (idempotentId != null) { args.add(Keyword.IDMP).add(producerId).add(idempotentId); } } if (maxLen != null) { args.add(Keyword.MAXLEN); if (approximateTrimming) { args.add(Protocol.BYTES_TILDE); } else if (exactTrimming) { args.add(Protocol.BYTES_EQUAL); } args.add(maxLen); } else if (minId != null) { args.add(Keyword.MINID); if (approximateTrimming) { args.add(Protocol.BYTES_TILDE); } else if (exactTrimming) { args.add(Protocol.BYTES_EQUAL); } args.add(minId); } if (limit != null) { args.add(Keyword.LIMIT).add(limit); } args.add(id != null ? id : StreamEntryID.NEW_ENTRY); } @Override public boolean equals(Object o) { if (this == o) return true; if (o == null || getClass() != o.getClass()) return false; XAddParams that = (XAddParams) o; return approximateTrimming == that.approximateTrimming && exactTrimming == that.exactTrimming && nomkstream == that.nomkstream && idmpAuto == that.idmpAuto && Objects.equals(id, that.id) && Objects.equals(maxLen, that.maxLen) && Objects.equals(minId, that.minId) && Objects.equals(limit, that.limit) && trimMode == that.trimMode && Objects.deepEquals(producerId, that.producerId) && Objects.deepEquals(idempotentId, that.idempotentId); } @Override public int hashCode() { return Objects.hash(id, maxLen, approximateTrimming, exactTrimming, nomkstream, minId, limit, trimMode, Arrays.hashCode(producerId), Arrays.hashCode(idempotentId), idmpAuto); } } ================================================ FILE: src/main/java/redis/clients/jedis/params/XAutoClaimParams.java ================================================ package redis.clients.jedis.params; import redis.clients.jedis.CommandArguments; import redis.clients.jedis.Protocol.Keyword; import java.util.Objects; public class XAutoClaimParams implements IParams { private Integer count; public XAutoClaimParams() { } public static XAutoClaimParams xAutoClaimParams() { return new XAutoClaimParams(); } /** * Set the count of stream entries/ids to return as part of the command output. * @param count COUNT * @return XAutoClaimParams */ public XAutoClaimParams count(int count) { this.count = count; return this; } @Override public void addParams(CommandArguments args) { if (count != null) { args.add(Keyword.COUNT.getRaw()).add(count); } } @Override public boolean equals(Object o) { if (this == o) return true; if (o == null || getClass() != o.getClass()) return false; XAutoClaimParams that = (XAutoClaimParams) o; return Objects.equals(count, that.count); } @Override public int hashCode() { return Objects.hash(count); } } ================================================ FILE: src/main/java/redis/clients/jedis/params/XCfgSetParams.java ================================================ package redis.clients.jedis.params; import redis.clients.jedis.CommandArguments; import redis.clients.jedis.Protocol.Keyword; import java.util.Objects; /** * Parameters for the XCFGSET command to configure idempotent producer settings for a stream. */ public class XCfgSetParams implements IParams { private Integer idmpDuration; private Integer idmpMaxsize; public static XCfgSetParams xCfgSetParams() { return new XCfgSetParams(); } /** * Set the duration (in seconds) that Redis keeps each idempotent ID. Minimum value: 1 second; * Maximum value: 86400 seconds (24h); default value: 100 seconds. * @param duration duration in seconds (1-86400) * @return XCfgSetParams * @throws IllegalArgumentException if duration is not between 1 and 86400 */ public XCfgSetParams idmpDuration(int duration) { if (duration < 1 || duration > 86400) { throw new IllegalArgumentException("IDMP-DURATION must be between 1 and 86400 seconds"); } this.idmpDuration = duration; return this; } /** * Set the maximum number of most recent idempotent IDs that Redis keeps for each producer ID. * Minimum value: 1; Maximum value: 10000; default value: 100. * @param maxsize maximum number of idempotent IDs per producer (1-10000) * @return XCfgSetParams * @throws IllegalArgumentException if maxsize is not between 1 and 10000 */ public XCfgSetParams idmpMaxsize(int maxsize) { if (maxsize < 1 || maxsize > 10000) { throw new IllegalArgumentException("IDMP-MAXSIZE must be between 1 and 10000"); } this.idmpMaxsize = maxsize; return this; } @Override public void addParams(CommandArguments args) { if (idmpDuration != null) { args.add("IDMP-DURATION").add(idmpDuration); } if (idmpMaxsize != null) { args.add("IDMP-MAXSIZE").add(idmpMaxsize); } } @Override public boolean equals(Object o) { if (this == o) return true; if (o == null || getClass() != o.getClass()) return false; XCfgSetParams that = (XCfgSetParams) o; return Objects.equals(idmpDuration, that.idmpDuration) && Objects.equals(idmpMaxsize, that.idmpMaxsize); } @Override public int hashCode() { return Objects.hash(idmpDuration, idmpMaxsize); } } ================================================ FILE: src/main/java/redis/clients/jedis/params/XClaimParams.java ================================================ package redis.clients.jedis.params; import redis.clients.jedis.CommandArguments; import redis.clients.jedis.Protocol.Keyword; import java.util.Objects; public class XClaimParams implements IParams { private Long idleTime; private Long idleUnixTime; private Integer retryCount; private boolean force; public XClaimParams() { } public static XClaimParams xClaimParams() { return new XClaimParams(); } /** * Set the idle time (last time it was delivered) of the message. * @param idleTime * @return XClaimParams */ public XClaimParams idle(long idleTime) { this.idleTime = idleTime; return this; } /** * Set the idle time to a specific Unix time (in milliseconds). * @param idleUnixTime * @return XClaimParams */ public XClaimParams time(long idleUnixTime) { this.idleUnixTime = idleUnixTime; return this; } /** * Set the retry counter to the specified value. * @param count * @return XClaimParams */ public XClaimParams retryCount(int count) { this.retryCount = count; return this; } /** * Creates the pending message entry in the PEL even if certain specified IDs are not already in * the PEL assigned to a different client. * @return XClaimParams */ public XClaimParams force() { this.force = true; return this; } @Override public void addParams(CommandArguments args) { if (idleTime != null) { args.add(Keyword.IDLE).add(idleTime); } if (idleUnixTime != null) { args.add(Keyword.TIME).add(idleUnixTime); } if (retryCount != null) { args.add(Keyword.RETRYCOUNT).add(retryCount); } if (force) { args.add(Keyword.FORCE); } } @Override public boolean equals(Object o) { if (this == o) return true; if (o == null || getClass() != o.getClass()) return false; XClaimParams that = (XClaimParams) o; return force == that.force && Objects.equals(idleTime, that.idleTime) && Objects.equals(idleUnixTime, that.idleUnixTime) && Objects.equals(retryCount, that.retryCount); } @Override public int hashCode() { return Objects.hash(idleTime, idleUnixTime, retryCount, force); } } ================================================ FILE: src/main/java/redis/clients/jedis/params/XPendingParams.java ================================================ package redis.clients.jedis.params; import static redis.clients.jedis.args.RawableFactory.from; import redis.clients.jedis.CommandArguments; import redis.clients.jedis.Protocol.Keyword; import redis.clients.jedis.StreamEntryID; import redis.clients.jedis.args.Rawable; import java.util.Objects; public class XPendingParams implements IParams { private Long idle; private Rawable start; private Rawable end; private Integer count; private Rawable consumer; public XPendingParams(StreamEntryID start, StreamEntryID end, int count) { this(start.toString(), end.toString(), count); } public XPendingParams(String start, String end, int count) { this(from(start), from(end), count); } public XPendingParams(byte[] start, byte[] end, int count) { this(from(start), from(end), count); } private XPendingParams(Rawable start, Rawable end, Integer count) { this.start = start; this.end = end; this.count = count; } public XPendingParams() { this.start = null; this.end = null; this.count = null; } public static XPendingParams xPendingParams(StreamEntryID start, StreamEntryID end, int count) { return new XPendingParams(start, end, count); } public static XPendingParams xPendingParams(String start, String end, int count) { return new XPendingParams(start, end, count); } public static XPendingParams xPendingParams(byte[] start, byte[] end, int count) { return new XPendingParams(start, end, count); } public static XPendingParams xPendingParams() { return new XPendingParams(); } public XPendingParams idle(long idle) { this.idle = idle; return this; } public XPendingParams start(StreamEntryID start) { this.start = from(start.toString()); return this; } public XPendingParams end(StreamEntryID end) { this.end = from(end.toString()); return this; } public XPendingParams count(int count) { this.count = count; return this; } public XPendingParams consumer(String consumer) { this.consumer = from(consumer); return this; } public XPendingParams consumer(byte[] consumer) { this.consumer = from(consumer); return this; } @Override public void addParams(CommandArguments args) { if (count == null) { throw new IllegalArgumentException("start, end and count must be set."); } if (start == null) start = from("-"); if (end == null) end = from("+"); if (idle != null) { args.add(Keyword.IDLE).add(idle); } args.add(start).add(end).add(count); if (consumer != null) { args.add(consumer); } } @Override public boolean equals(Object o) { if (this == o) return true; if (o == null || getClass() != o.getClass()) return false; XPendingParams that = (XPendingParams) o; return Objects.equals(idle, that.idle) && Objects.equals(start, that.start) && Objects.equals(end, that.end) && Objects.equals(count, that.count) && Objects.equals(consumer, that.consumer); } @Override public int hashCode() { return Objects.hash(idle, start, end, count, consumer); } } ================================================ FILE: src/main/java/redis/clients/jedis/params/XReadGroupParams.java ================================================ package redis.clients.jedis.params; import redis.clients.jedis.CommandArguments; import redis.clients.jedis.Protocol.Keyword; import java.util.Objects; public class XReadGroupParams implements IParams { private Integer count = null; private Integer block = null; private boolean noack = false; private Long claim = null; public static XReadGroupParams xReadGroupParams() { return new XReadGroupParams(); } public XReadGroupParams count(int count) { this.count = count; return this; } public XReadGroupParams block(int block) { this.block = block; return this; } public XReadGroupParams noAck() { this.noack = true; return this; } public XReadGroupParams claim(long minIdleMillis) { this.claim = minIdleMillis; return this; } @Override public void addParams(CommandArguments args) { if (count != null) { args.add(Keyword.COUNT).add(count); } if (block != null) { args.add(Keyword.BLOCK).add(block).blocking(); } if (noack) { args.add(Keyword.NOACK); } if (claim != null) { args.add(Keyword.CLAIM).add(claim); } } @Override public boolean equals(Object o) { if (this == o) return true; if (o == null || getClass() != o.getClass()) return false; XReadGroupParams that = (XReadGroupParams) o; return noack == that.noack && Objects.equals(count, that.count) && Objects.equals(block, that.block) && Objects.equals(claim, that.claim); } @Override public int hashCode() { return Objects.hash(count, block, noack, claim); } } ================================================ FILE: src/main/java/redis/clients/jedis/params/XReadParams.java ================================================ package redis.clients.jedis.params; import redis.clients.jedis.CommandArguments; import redis.clients.jedis.Protocol.Keyword; import java.util.Objects; public class XReadParams implements IParams { private Integer count = null; private Integer block = null; public static XReadParams xReadParams() { return new XReadParams(); } public XReadParams count(int count) { this.count = count; return this; } public XReadParams block(int block) { this.block = block; return this; } @Override public void addParams(CommandArguments args) { if (count != null) { args.add(Keyword.COUNT).add(count); } if (block != null) { args.add(Keyword.BLOCK).add(block).blocking(); } } @Override public boolean equals(Object o) { if (this == o) return true; if (o == null || getClass() != o.getClass()) return false; XReadParams that = (XReadParams) o; return Objects.equals(count, that.count) && Objects.equals(block, that.block); } @Override public int hashCode() { return Objects.hash(count, block); } } ================================================ FILE: src/main/java/redis/clients/jedis/params/XTrimParams.java ================================================ package redis.clients.jedis.params; import redis.clients.jedis.CommandArguments; import redis.clients.jedis.Protocol; import redis.clients.jedis.Protocol.Keyword; import redis.clients.jedis.args.StreamDeletionPolicy; import java.util.Objects; public class XTrimParams implements IParams { private Long maxLen; private boolean approximateTrimming; private boolean exactTrimming; private String minId; private Long limit; private StreamDeletionPolicy trimMode; public static XTrimParams xTrimParams() { return new XTrimParams(); } public XTrimParams maxLen(long maxLen) { this.maxLen = maxLen; return this; } public XTrimParams minId(String minId) { this.minId = minId; return this; } public XTrimParams approximateTrimming() { this.approximateTrimming = true; return this; } public XTrimParams exactTrimming() { this.exactTrimming = true; return this; } public XTrimParams limit(long limit) { this.limit = limit; return this; } /** * Defines desired behaviour for handling consumer group references. * see {@link StreamDeletionPolicy} for details. * * @return XAddParams */ public XTrimParams trimmingMode(StreamDeletionPolicy trimMode) { this.trimMode = trimMode; return this; } @Override public void addParams(CommandArguments args) { if (maxLen != null) { args.add(Keyword.MAXLEN); if (approximateTrimming) { args.add(Protocol.BYTES_TILDE); } else if (exactTrimming) { args.add(Protocol.BYTES_EQUAL); } args.add(Protocol.toByteArray(maxLen)); } else if (minId != null) { args.add(Keyword.MINID); if (approximateTrimming) { args.add(Protocol.BYTES_TILDE); } else if (exactTrimming) { args.add(Protocol.BYTES_EQUAL); } args.add(minId); } if (limit != null) { args.add(Keyword.LIMIT).add(limit); } if (trimMode != null) { args.add(trimMode); } } @Override public boolean equals(Object o) { if (this == o) return true; if (o == null || getClass() != o.getClass()) return false; XTrimParams that = (XTrimParams) o; return approximateTrimming == that.approximateTrimming && exactTrimming == that.exactTrimming && Objects.equals(maxLen, that.maxLen) && Objects.equals(minId, that.minId) && Objects.equals(limit, that.limit) && trimMode == that.trimMode; } @Override public int hashCode() { return Objects.hash(maxLen, approximateTrimming, exactTrimming, minId, limit, trimMode); } } ================================================ FILE: src/main/java/redis/clients/jedis/params/ZAddParams.java ================================================ package redis.clients.jedis.params; import redis.clients.jedis.CommandArguments; import redis.clients.jedis.Protocol.Keyword; import java.util.Objects; public class ZAddParams implements IParams { private Keyword existence; private Keyword comparison; private boolean change; public ZAddParams() { } public static ZAddParams zAddParams() { return new ZAddParams(); } /** * Only set the key if it does not already exist. * @return ZAddParams */ public ZAddParams nx() { this.existence = Keyword.NX; return this; } /** * Only set the key if it already exists. * @return ZAddParams */ public ZAddParams xx() { this.existence = Keyword.XX; return this; } /** * Only update existing elements if the new score is greater than the current score. * @return ZAddParams */ public ZAddParams gt() { this.comparison = Keyword.GT; return this; } /** * Only update existing elements if the new score is less than the current score. * @return ZAddParams */ public ZAddParams lt() { this.comparison = Keyword.LT; return this; } /** * Modify the return value from the number of new elements added to the total number of elements * changed * @return ZAddParams */ public ZAddParams ch() { this.change = true; return this; } @Override public void addParams(CommandArguments args) { if (existence != null) { args.add(existence); } if (comparison != null) { args.add(comparison); } if (change) { args.add(Keyword.CH); } } @Override public boolean equals(Object o) { if (this == o) return true; if (o == null || getClass() != o.getClass()) return false; ZAddParams that = (ZAddParams) o; return change == that.change && existence == that.existence && comparison == that.comparison; } @Override public int hashCode() { return Objects.hash(existence, comparison, change); } } ================================================ FILE: src/main/java/redis/clients/jedis/params/ZIncrByParams.java ================================================ package redis.clients.jedis.params; import redis.clients.jedis.CommandArguments; import redis.clients.jedis.Protocol.Keyword; import java.util.Objects; /** * Parameters for ZINCRBY commands. In fact, Redis doesn't have parameters for ZINCRBY. Instead * Redis has INCR parameter for ZADD. *

* When users call ZADD with INCR option, its restriction (only one member) and return type is same * to ZINCRBY. Document page for ZADD also describes INCR option to act like ZINCRBY. So we decided * to wrap "ZADD with INCR option" to ZINCRBY. *

* Works with Redis 3.0.2 and onwards. */ public class ZIncrByParams implements IParams { private Keyword existance; public ZIncrByParams() { } public static ZIncrByParams zIncrByParams() { return new ZIncrByParams(); } /** * Only set the key if it does not already exist. * @return ZIncrByParams */ public ZIncrByParams nx() { this.existance = Keyword.NX; return this; } /** * Only set the key if it already exist. * @return ZIncrByParams */ public ZIncrByParams xx() { this.existance = Keyword.XX; return this; } @Override public void addParams(CommandArguments args) { if (existance != null) { args.add(existance); } args.add(Keyword.INCR); } @Override public boolean equals(Object o) { if (this == o) return true; if (o == null || getClass() != o.getClass()) return false; ZIncrByParams that = (ZIncrByParams) o; return existance == that.existance; } @Override public int hashCode() { return Objects.hash(existance); } } ================================================ FILE: src/main/java/redis/clients/jedis/params/ZParams.java ================================================ package redis.clients.jedis.params; import java.util.ArrayList; import java.util.List; import java.util.Objects; import redis.clients.jedis.CommandArguments; import redis.clients.jedis.Protocol.Keyword; import redis.clients.jedis.args.Rawable; import redis.clients.jedis.util.SafeEncoder; public class ZParams implements IParams { public enum Aggregate implements Rawable { SUM, MIN, MAX; private final byte[] raw; private Aggregate() { raw = SafeEncoder.encode(name()); } @Override public byte[] getRaw() { return raw; } } private final List params = new ArrayList<>(); public ZParams weights(final double... weights) { params.add(Keyword.WEIGHTS); for (final double weight : weights) { params.add(weight); } return this; } public ZParams aggregate(final Aggregate aggregate) { params.add(Keyword.AGGREGATE); params.add(aggregate); return this; } @Override public void addParams(CommandArguments args) { args.addObjects(params); } @Override public boolean equals(Object o) { if (this == o) return true; if (o == null || getClass() != o.getClass()) return false; ZParams zParams = (ZParams) o; return Objects.equals(params, zParams.params); } @Override public int hashCode() { return Objects.hash(params); } } ================================================ FILE: src/main/java/redis/clients/jedis/params/ZRangeParams.java ================================================ package redis.clients.jedis.params; import static redis.clients.jedis.Protocol.Keyword.BYLEX; import static redis.clients.jedis.Protocol.Keyword.BYSCORE; import static redis.clients.jedis.args.RawableFactory.from; import redis.clients.jedis.CommandArguments; import redis.clients.jedis.Protocol.Keyword; import redis.clients.jedis.args.Rawable; import java.util.Objects; public class ZRangeParams implements IParams { private final Keyword by; private final Rawable min; private final Rawable max; private boolean rev = false; private boolean limit = false; private int offset; private int count; private ZRangeParams() { throw new InstantiationError("Empty constructor must not be called."); } public ZRangeParams(int min, int max) { this((long) min, (long) max); } public ZRangeParams(long min, long max) { this.by = null; this.min = from(min); this.max = from(max); } public static ZRangeParams zrangeParams(int min, int max) { return new ZRangeParams(min, max); } public static ZRangeParams zrangeParams(long min, long max) { return new ZRangeParams(min, max); } public ZRangeParams(double min, double max) { this.by = BYSCORE; this.min = from(min); this.max = from(max); } public static ZRangeParams zrangeByScoreParams(double min, double max) { return new ZRangeParams(min, max); } private ZRangeParams(Keyword by, Rawable min, Rawable max) { if (by == null || by == BYSCORE || by == BYLEX) { // ok } else { throw new IllegalArgumentException(by.name() + " is not a valid ZRANGE type argument."); } this.by = by; this.min = min; this.max = max; } public ZRangeParams(Keyword by, String min, String max) { this(by, from(min), from(max)); } public ZRangeParams(Keyword by, byte[] min, byte[] max) { this(by, from(min), from(max)); } public static ZRangeParams zrangeByLexParams(String min, String max) { return new ZRangeParams(BYLEX, min, max); } public static ZRangeParams zrangeByLexParams(byte[] min, byte[] max) { return new ZRangeParams(BYLEX, min, max); } public ZRangeParams rev() { this.rev = true; return this; } public ZRangeParams limit(int offset, int count) { this.limit = true; this.offset = offset; this.count = count; return this; } @Override public void addParams(CommandArguments args) { args.add(min).add(max); if (by != null) { args.add(by); } if (rev) { args.add(Keyword.REV); } if (limit) { args.add(Keyword.LIMIT).add(offset).add(count); } } @Override public boolean equals(Object o) { if (this == o) return true; if (o == null || getClass() != o.getClass()) return false; ZRangeParams that = (ZRangeParams) o; return rev == that.rev && limit == that.limit && offset == that.offset && count == that.count && by == that.by && Objects.equals(min, that.min) && Objects.equals(max, that.max); } @Override public int hashCode() { return Objects.hash(by, min, max, rev, limit, offset, count); } } ================================================ FILE: src/main/java/redis/clients/jedis/params/package-info.java ================================================ /** * This package contains the classes that represent optional parameters of core Redis commands. */ package redis.clients.jedis.params; ================================================ FILE: src/main/java/redis/clients/jedis/providers/ClusterConnectionProvider.java ================================================ package redis.clients.jedis.providers; import java.time.Duration; import java.util.ArrayList; import java.util.Collections; import java.util.List; import java.util.Map; import java.util.Set; import java.util.concurrent.ThreadLocalRandom; import org.apache.commons.pool2.impl.GenericObjectPoolConfig; import redis.clients.jedis.CommandArguments; import redis.clients.jedis.HostAndPort; import redis.clients.jedis.JedisClientConfig; import redis.clients.jedis.Connection; import redis.clients.jedis.ConnectionPool; import redis.clients.jedis.JedisClusterInfoCache; import redis.clients.jedis.annots.Experimental; import redis.clients.jedis.csc.Cache; import redis.clients.jedis.exceptions.JedisClusterOperationException; import redis.clients.jedis.exceptions.JedisException; import static redis.clients.jedis.RedisClusterClient.INIT_NO_ERROR_PROPERTY; public class ClusterConnectionProvider implements ConnectionProvider { protected final JedisClusterInfoCache cache; public ClusterConnectionProvider(Set clusterNodes, JedisClientConfig clientConfig) { this.cache = new JedisClusterInfoCache(clientConfig, clusterNodes); initializeSlotsCache(clusterNodes, clientConfig); } @Experimental public ClusterConnectionProvider(Set clusterNodes, JedisClientConfig clientConfig, Cache clientSideCache) { this.cache = new JedisClusterInfoCache(clientConfig, clientSideCache, clusterNodes); initializeSlotsCache(clusterNodes, clientConfig); } public ClusterConnectionProvider(Set clusterNodes, JedisClientConfig clientConfig, GenericObjectPoolConfig poolConfig) { this.cache = new JedisClusterInfoCache(clientConfig, poolConfig, clusterNodes); initializeSlotsCache(clusterNodes, clientConfig); } @Experimental public ClusterConnectionProvider(Set clusterNodes, JedisClientConfig clientConfig, Cache clientSideCache, GenericObjectPoolConfig poolConfig) { this.cache = new JedisClusterInfoCache(clientConfig, clientSideCache, poolConfig, clusterNodes); initializeSlotsCache(clusterNodes, clientConfig); } public ClusterConnectionProvider(Set clusterNodes, JedisClientConfig clientConfig, GenericObjectPoolConfig poolConfig, Duration topologyRefreshPeriod) { this.cache = new JedisClusterInfoCache(clientConfig, poolConfig, clusterNodes, topologyRefreshPeriod); initializeSlotsCache(clusterNodes, clientConfig); } @Experimental public ClusterConnectionProvider(Set clusterNodes, JedisClientConfig clientConfig, Cache clientSideCache, GenericObjectPoolConfig poolConfig, Duration topologyRefreshPeriod) { this.cache = new JedisClusterInfoCache(clientConfig, clientSideCache, poolConfig, clusterNodes, topologyRefreshPeriod); initializeSlotsCache(clusterNodes, clientConfig); } private void initializeSlotsCache(Set startNodes, JedisClientConfig clientConfig) { if (startNodes.isEmpty()) { throw new JedisClusterOperationException("No nodes to initialize cluster slots cache."); } ArrayList startNodeList = new ArrayList<>(startNodes); Collections.shuffle(startNodeList); JedisException firstException = null; for (HostAndPort hostAndPort : startNodeList) { try (Connection jedis = new Connection(hostAndPort, clientConfig)) { cache.discoverClusterNodesAndSlots(jedis); return; } catch (JedisException e) { if (firstException == null) { firstException = e; } // try next nodes } } if (System.getProperty(INIT_NO_ERROR_PROPERTY) != null) { return; } JedisClusterOperationException uninitializedException = new JedisClusterOperationException("Could not initialize cluster slots cache."); uninitializedException.addSuppressed(firstException); throw uninitializedException; } @Override public void close() { cache.close(); } public void renewSlotCache() { cache.renewClusterSlots(null); } public void renewSlotCache(Connection jedis) { cache.renewClusterSlots(jedis); } public Map getNodes() { return cache.getNodes(); } public Map getPrimaryNodes() { return cache.getPrimaryNodes(); } public HostAndPort getNode(int slot) { return slot >= 0 ? cache.getSlotNode(slot) : null; } public Connection getConnection(HostAndPort node) { return node != null ? cache.setupNodeIfNotExist(node).getResource() : getConnection(); } @Override public Connection getConnection(CommandArguments args) { Set slots = args.getKeyHashSlots(); if (slots.size() > 1) { throw new JedisClusterOperationException("Cannot get connection for command with multiple hash slots"); } int slot = slots.iterator().next(); return slot >= 0 ? getConnectionFromSlot(slot) : getConnection(); } public Connection getReplicaConnection(CommandArguments args) { Set slots = args.getKeyHashSlots(); if (slots.size() > 1) { throw new JedisClusterOperationException("Cannot get connection for command with multiple hash slots"); } int slot = slots.iterator().next(); return slot >= 0 ? getReplicaConnectionFromSlot(slot) : getConnection(); } @Override public Connection getConnection() { // In antirez's redis-rb-cluster implementation, getRandomConnection always return // valid connection (able to ping-pong) or exception if all connections are invalid List pools = cache.getShuffledPrimaryNodesPool(); JedisException suppressed = null; for (ConnectionPool pool : pools) { Connection jedis = null; try { jedis = pool.getResource(); if (jedis == null) { continue; } jedis.ping(); return jedis; } catch (JedisException ex) { if (suppressed == null) { // remembering first suppressed exception suppressed = ex; } if (jedis != null) { jedis.close(); } } } JedisClusterOperationException noReachableNode = new JedisClusterOperationException("No reachable node in cluster."); if (suppressed != null) { noReachableNode.addSuppressed(suppressed); } throw noReachableNode; } public Connection getConnectionFromSlot(int slot) { ConnectionPool connectionPool = cache.getSlotPool(slot); if (connectionPool != null) { // It can't guaranteed to get valid connection because of node assignment return connectionPool.getResource(); } else { // It's abnormal situation for cluster mode that we have just nothing for slot. // Try to rediscover state renewSlotCache(); connectionPool = cache.getSlotPool(slot); if (connectionPool != null) { return connectionPool.getResource(); } else { // no choice, fallback to new connection to random node return getConnection(); } } } public Connection getReplicaConnectionFromSlot(int slot) { List connectionPools = cache.getSlotReplicaPools(slot); ThreadLocalRandom random = ThreadLocalRandom.current(); if (connectionPools != null && !connectionPools.isEmpty()) { // pick up randomly a connection int idx = random.nextInt(connectionPools.size()); return connectionPools.get(idx).getResource(); } renewSlotCache(); connectionPools = cache.getSlotReplicaPools(slot); if (connectionPools != null && !connectionPools.isEmpty()) { int idx = random.nextInt(connectionPools.size()); return connectionPools.get(idx).getResource(); } return getConnectionFromSlot(slot); } @Override public Map getConnectionMap() { return Collections.unmodifiableMap(getNodes()); } @Override public Map getPrimaryNodesConnectionMap() { return Collections.unmodifiableMap(getPrimaryNodes()); } } ================================================ FILE: src/main/java/redis/clients/jedis/providers/ConnectionProvider.java ================================================ package redis.clients.jedis.providers; import java.util.Collections; import java.util.Map; import redis.clients.jedis.CommandArguments; import redis.clients.jedis.Connection; public interface ConnectionProvider extends AutoCloseable { Connection getConnection(); Connection getConnection(CommandArguments args); default Map getConnectionMap() { final Connection c = getConnection(); return Collections.singletonMap(c.toString(), c); } default Map getPrimaryNodesConnectionMap() { final Connection c = getConnection(); return Collections.singletonMap(c.toString(), c); } } ================================================ FILE: src/main/java/redis/clients/jedis/providers/ManagedConnectionProvider.java ================================================ package redis.clients.jedis.providers; import redis.clients.jedis.CommandArguments; import redis.clients.jedis.Connection; public class ManagedConnectionProvider implements ConnectionProvider { private Connection connection; public final void setConnection(Connection connection) { this.connection = connection; } @Override public void close() { } @Override public final Connection getConnection() { return connection; } @Override public final Connection getConnection(CommandArguments args) { return connection; } } ================================================ FILE: src/main/java/redis/clients/jedis/providers/PooledConnectionProvider.java ================================================ package redis.clients.jedis.providers; import java.util.Collections; import java.util.Map; import org.apache.commons.pool2.PooledObjectFactory; import org.apache.commons.pool2.impl.GenericObjectPoolConfig; import redis.clients.jedis.CommandArguments; import redis.clients.jedis.Connection; import redis.clients.jedis.ConnectionFactory; import redis.clients.jedis.ConnectionPool; import redis.clients.jedis.HostAndPort; import redis.clients.jedis.JedisClientConfig; import redis.clients.jedis.annots.Experimental; import redis.clients.jedis.csc.Cache; import redis.clients.jedis.util.Pool; public class PooledConnectionProvider implements ConnectionProvider { private final Pool pool; private Object connectionMapKey = ""; public PooledConnectionProvider(HostAndPort hostAndPort) { this(new ConnectionFactory(hostAndPort)); this.connectionMapKey = hostAndPort; } public PooledConnectionProvider(HostAndPort hostAndPort, JedisClientConfig clientConfig) { this(new ConnectionPool(hostAndPort, clientConfig)); this.connectionMapKey = hostAndPort; } @Experimental public PooledConnectionProvider(HostAndPort hostAndPort, JedisClientConfig clientConfig, Cache clientSideCache) { this(new ConnectionPool(hostAndPort, clientConfig, clientSideCache)); this.connectionMapKey = hostAndPort; } public PooledConnectionProvider(HostAndPort hostAndPort, JedisClientConfig clientConfig, GenericObjectPoolConfig poolConfig) { this(new ConnectionPool(hostAndPort, clientConfig, poolConfig)); this.connectionMapKey = hostAndPort; } @Experimental public PooledConnectionProvider(HostAndPort hostAndPort, JedisClientConfig clientConfig, Cache clientSideCache, GenericObjectPoolConfig poolConfig) { this(new ConnectionPool(hostAndPort, clientConfig, clientSideCache, poolConfig)); this.connectionMapKey = hostAndPort; } public PooledConnectionProvider(PooledObjectFactory factory) { this(new ConnectionPool(factory)); this.connectionMapKey = factory; } public PooledConnectionProvider(PooledObjectFactory factory, GenericObjectPoolConfig poolConfig) { this(new ConnectionPool(factory, poolConfig)); this.connectionMapKey = factory; } private PooledConnectionProvider(Pool pool) { this.pool = pool; } @Override public void close() { pool.close(); } public final Pool getPool() { return pool; } @Override public Connection getConnection() { return pool.getResource(); } @Override public Connection getConnection(CommandArguments args) { return pool.getResource(); } @Override public Map> getConnectionMap() { return Collections.singletonMap(connectionMapKey, pool); } } ================================================ FILE: src/main/java/redis/clients/jedis/providers/SentineledConnectionProvider.java ================================================ package redis.clients.jedis.providers; import java.time.Duration; import java.util.ArrayList; import java.util.Collection; import java.util.Collections; import java.util.List; import java.util.Map; import java.util.Set; import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.locks.Lock; import java.util.concurrent.locks.ReentrantLock; import org.apache.commons.pool2.impl.GenericObjectPoolConfig; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import redis.clients.jedis.CommandArguments; import redis.clients.jedis.Connection; import redis.clients.jedis.ConnectionPool; import redis.clients.jedis.HostAndPort; import redis.clients.jedis.Jedis; import redis.clients.jedis.JedisClientConfig; import redis.clients.jedis.JedisPubSub; import redis.clients.jedis.annots.Experimental; import redis.clients.jedis.csc.Cache; import redis.clients.jedis.exceptions.JedisConnectionException; import redis.clients.jedis.exceptions.JedisException; import redis.clients.jedis.util.Delay; import redis.clients.jedis.util.IOUtils; import redis.clients.jedis.util.Pool; public class SentineledConnectionProvider implements ConnectionProvider { private static final Logger LOG = LoggerFactory.getLogger(SentineledConnectionProvider.class); protected static final long DEFAULT_SUBSCRIBE_RETRY_WAIT_TIME_MILLIS = 5000; public static final Delay DEFAULT_RESUBSCRIBE_DELAY = Delay .constant(Duration.ofMillis(DEFAULT_SUBSCRIBE_RETRY_WAIT_TIME_MILLIS)); private static final Sleeper DEFAULT_SLEEPER = Thread::sleep; private volatile HostAndPort currentMaster; private volatile ConnectionPool pool; private final String masterName; private final JedisClientConfig masterClientConfig; private final Cache clientSideCache; private final GenericObjectPoolConfig masterPoolConfig; protected final Collection sentinelListeners = new ArrayList<>(); private final JedisClientConfig sentinelClientConfig; private final Delay resubscribeDelay; private final Lock initPoolLock = new ReentrantLock(true); private final SentinelConnectionFactory sentinelConnectionFactory; private final Sleeper sleeper; public SentineledConnectionProvider(String masterName, final JedisClientConfig masterClientConfig, Set sentinels, final JedisClientConfig sentinelClientConfig) { this(masterName, masterClientConfig, null, null, sentinels, sentinelClientConfig); } @Experimental public SentineledConnectionProvider(String masterName, final JedisClientConfig masterClientConfig, Cache clientSideCache, Set sentinels, final JedisClientConfig sentinelClientConfig) { this(masterName, masterClientConfig, clientSideCache, null, sentinels, sentinelClientConfig); } public SentineledConnectionProvider(String masterName, final JedisClientConfig masterClientConfig, final GenericObjectPoolConfig poolConfig, Set sentinels, final JedisClientConfig sentinelClientConfig) { this(masterName, masterClientConfig, poolConfig, sentinels, sentinelClientConfig, DEFAULT_RESUBSCRIBE_DELAY); } @Experimental public SentineledConnectionProvider(String masterName, final JedisClientConfig masterClientConfig, Cache clientSideCache, final GenericObjectPoolConfig poolConfig, Set sentinels, final JedisClientConfig sentinelClientConfig) { this(masterName, masterClientConfig, clientSideCache, poolConfig, sentinels, sentinelClientConfig, DEFAULT_RESUBSCRIBE_DELAY); } /** * * @deprecated use * {@link #SentineledConnectionProvider(String, JedisClientConfig, GenericObjectPoolConfig, Set, JedisClientConfig, Delay)} */ @Deprecated public SentineledConnectionProvider(String masterName, final JedisClientConfig masterClientConfig, final GenericObjectPoolConfig poolConfig, Set sentinels, final JedisClientConfig sentinelClientConfig, final long subscribeRetryWaitTimeMillis) { this(masterName, masterClientConfig, null, poolConfig, sentinels, sentinelClientConfig, Delay.constant(Duration.ofMillis(subscribeRetryWaitTimeMillis))); } /** * Creates a new SentineledConnectionProvider. * * @param masterName name of the master * @param masterClientConfig client configuration for the master * @param poolConfig pool configuration for the master * @param sentinels set of sentinel addresses * @param sentinelClientConfig client configuration for the sentinel * @param subscribeRetryWaitTimeMillis delay before resubscribing to sentinel after a connection loss * * @since 7.3.0 */ public SentineledConnectionProvider(String masterName, final JedisClientConfig masterClientConfig, final GenericObjectPoolConfig poolConfig, Set sentinels, final JedisClientConfig sentinelClientConfig, final Delay subscribeRetryWaitTimeMillis) { this(masterName, masterClientConfig, null, poolConfig, sentinels, sentinelClientConfig, subscribeRetryWaitTimeMillis); } /** * @deprecated use * {@link #SentineledConnectionProvider(String, JedisClientConfig, Cache, GenericObjectPoolConfig, Set, JedisClientConfig, Delay)} */ @Experimental @Deprecated public SentineledConnectionProvider(String masterName, final JedisClientConfig masterClientConfig, Cache clientSideCache, final GenericObjectPoolConfig poolConfig, Set sentinels, final JedisClientConfig sentinelClientConfig, final long subscribeRetryWaitTimeMillis) { this(masterName, masterClientConfig, clientSideCache, poolConfig, sentinels, sentinelClientConfig, Delay.constant(Duration.ofMillis(subscribeRetryWaitTimeMillis))); } @Experimental public SentineledConnectionProvider(String masterName, final JedisClientConfig masterClientConfig, Cache clientSideCache, final GenericObjectPoolConfig poolConfig, Set sentinels, final JedisClientConfig sentinelClientConfig, final Delay resubscribeDelay) { this(masterName, masterClientConfig, clientSideCache, poolConfig, sentinels, sentinelClientConfig, resubscribeDelay, null, null); } SentineledConnectionProvider(String masterName, final JedisClientConfig masterClientConfig, Cache clientSideCache, final GenericObjectPoolConfig poolConfig, Set sentinels, final JedisClientConfig sentinelClientConfig, final Delay resubscribeDelay, SentinelConnectionFactory sentinelConnectionFactory, Sleeper sleeper) { this.masterName = masterName; this.masterClientConfig = masterClientConfig; this.clientSideCache = clientSideCache; this.masterPoolConfig = poolConfig; this.sentinelClientConfig = sentinelClientConfig; this.resubscribeDelay = resubscribeDelay; this.sentinelConnectionFactory = sentinelConnectionFactory != null ? sentinelConnectionFactory : defaultSentinelConnectionFactory(); this.sleeper = sleeper != null ? sleeper : DEFAULT_SLEEPER; HostAndPort master = initSentinels(sentinels); initMaster(master); } @Override public Connection getConnection() { return pool.getResource(); } @Override public Connection getConnection(CommandArguments args) { return pool.getResource(); } @Override public Map> getConnectionMap() { return Collections.singletonMap(currentMaster, pool); } @Override public Map> getPrimaryNodesConnectionMap() { return Collections.singletonMap(currentMaster, pool); } @Override public void close() { sentinelListeners.forEach(SentinelListener::shutdown); pool.close(); } public HostAndPort getCurrentMaster() { return currentMaster; } private void initMaster(HostAndPort master) { initPoolLock.lock(); try { if (!master.equals(currentMaster)) { currentMaster = master; ConnectionPool newPool = createNodePool(currentMaster); ConnectionPool existingPool = pool; pool = newPool; LOG.info("Created connection pool to master at {}.", master); if (clientSideCache != null) { clientSideCache.flush(); } if (existingPool != null) { // although we clear the pool, we still have to check the returned object in getResource, // this call only clears idle instances, not borrowed instances // existingPool.clear(); // necessary?? existingPool.close(); } } } finally { initPoolLock.unlock(); } } private ConnectionPool createNodePool(HostAndPort master) { if (masterPoolConfig == null) { if (clientSideCache == null) { return new ConnectionPool(master, masterClientConfig); } else { return new ConnectionPool(master, masterClientConfig, clientSideCache); } } else { if (clientSideCache == null) { return new ConnectionPool(master, masterClientConfig, masterPoolConfig); } else { return new ConnectionPool(master, masterClientConfig, clientSideCache, masterPoolConfig); } } } private HostAndPort initSentinels(Set sentinels) { HostAndPort master = null; boolean sentinelAvailable = false; LOG.debug("Trying to find master from available sentinels..."); for (HostAndPort sentinel : sentinels) { LOG.debug("Connecting to Sentinel {}...", sentinel); try (Jedis jedis = sentinelConnectionFactory.createConnection(sentinel, sentinelClientConfig)) { List masterAddr = jedis.sentinelGetMasterAddrByName(masterName); // connected to sentinel... sentinelAvailable = true; if (masterAddr == null || masterAddr.size() != 2) { LOG.warn("Sentinel {} is not monitoring master {}.", sentinel, masterName); continue; } master = toHostAndPort(masterAddr); LOG.debug("Redis master reported at {}.", master); break; } catch (JedisException e) { // resolves #1036, it should handle JedisException there's another chance // of raising JedisDataException LOG.warn("Could not get master address from {}.", sentinel, e); } } if (master == null) { if (sentinelAvailable) { // can connect to sentinel, but master name seems to not monitored throw new JedisException( "Can connect to sentinel, but " + masterName + " seems to be not monitored."); } else { throw new JedisConnectionException( "All sentinels down, cannot determine where " + masterName + " is running."); } } LOG.info("Redis master running at {}. Starting sentinel listeners...", master); for (HostAndPort sentinel : sentinels) { SentinelListener listener = new SentinelListener(sentinel); // whether SentinelListener threads are alive or not, process can be stopped listener.setDaemon(true); sentinelListeners.add(listener); listener.start(); } return master; } /** * Must be of size 2. */ private static HostAndPort toHostAndPort(List masterAddr) { return toHostAndPort(masterAddr.get(0), masterAddr.get(1)); } private static HostAndPort toHostAndPort(String hostStr, String portStr) { return new HostAndPort(hostStr, Integer.parseInt(portStr)); } protected class SentinelListener extends Thread { protected final HostAndPort node; protected volatile Jedis sentinelJedis; protected AtomicBoolean running = new AtomicBoolean(false); protected long subscribeAttempt = 0; public SentinelListener(HostAndPort node) { super(String.format("%s-SentinelListener-[%s]", masterName, node.toString())); this.node = node; } @Override public void run() { running.set(true); while (running.get()) { try { // double check that it is not being shutdown if (!running.get()) { break; } sentinelJedis = sentinelConnectionFactory.createConnection(node, sentinelClientConfig); // code for active refresh List masterAddr = sentinelJedis.sentinelGetMasterAddrByName(masterName); if (masterAddr == null || masterAddr.size() != 2) { LOG.warn("Can not get master {} address. Sentinel: {}.", masterName, node); } else { initMaster(toHostAndPort(masterAddr)); } sentinelJedis.subscribe(new JedisPubSub() { @Override public void onSubscribe(String channel, int subscribedChannels) { // Successfully subscribed - reset attempt counter subscribeAttempt = 0; LOG.debug("Successfully subscribed to {} on Sentinel {}. Reset attempt counter.", channel, node); } @Override public void onMessage(String channel, String message) { LOG.debug("Sentinel {} published: {}.", node, message); String[] switchMasterMsg = message.split(" "); if (switchMasterMsg.length > 3) { if (masterName.equals(switchMasterMsg[0])) { initMaster(toHostAndPort(switchMasterMsg[3], switchMasterMsg[4])); } else { LOG.debug("Ignoring message on +switch-master for master {}. Our master is {}.", switchMasterMsg[0], masterName); } } else { LOG.error("Invalid message received on sentinel {} on channel +switch-master: {}.", node, message); } } }, "+switch-master"); } catch (JedisException e) { if (running.get()) { long subscribeRetryWaitTimeMillis = resubscribeDelay.delay(subscribeAttempt).toMillis(); subscribeAttempt++; LOG.warn("Lost connection to Sentinel {}. Sleeping {}ms and retrying.", node, subscribeRetryWaitTimeMillis, e); try { sleeper.sleep(subscribeRetryWaitTimeMillis); } catch (InterruptedException se) { LOG.error("Sleep interrupted.", se); } } else { LOG.debug("Unsubscribing from sentinel {}.", node); } } finally { IOUtils.closeQuietly(sentinelJedis); } } } // must not throw exception public void shutdown() { try { LOG.debug("Shutting down listener on {}.", node); running.set(false); // This isn't good, the Jedis object is not thread safe if (sentinelJedis != null) { sentinelJedis.close(); } } catch (RuntimeException e) { LOG.error("Error while shutting down.", e); } } } protected SentinelConnectionFactory defaultSentinelConnectionFactory() { return (node, config) -> new Jedis(node, config); } @FunctionalInterface interface Sleeper { void sleep(long millis) throws InterruptedException; } @FunctionalInterface protected interface SentinelConnectionFactory { Jedis createConnection(HostAndPort node, JedisClientConfig config); } } ================================================ FILE: src/main/java/redis/clients/jedis/providers/package-info.java ================================================ /** * This package contains the implementations of ConnectionProvider interface. */ package redis.clients.jedis.providers; ================================================ FILE: src/main/java/redis/clients/jedis/resps/AccessControlLogEntry.java ================================================ package redis.clients.jedis.resps; import java.io.Serializable; import java.util.LinkedHashMap; import java.util.Map; /** * This class holds information about an Access Control Log entry (returned by ACL LOG command) They * can be accessed via getters. For future purpose there is also {@link #getlogEntry} method that * returns a generic {@code Map} - in case where more info is returned from a server */ // TODO: remove public class AccessControlLogEntry implements Serializable { private static final long serialVersionUID = 1L; public static final String COUNT = "count"; public static final String REASON = "reason"; public static final String CONTEXT = "context"; public static final String OBJECT = "object"; public static final String USERNAME = "username"; public static final String AGE_SECONDS = "age-seconds"; public static final String CLIENT_INFO = "client-info"; // Redis 7.2 public static final String ENTRY_ID = "entry-id"; public static final String TIMESTAMP_CREATED = "timestamp-created"; public static final String TIMESTAMP_LAST_UPDATED = "timestamp-last-updated"; private final long count; private final String reason; private final String context; private final String object; private final String username; private final Double ageSeconds; private final Map clientInfo; private final Map logEntry; private final long entryId; private final long timestampCreated; private final long timestampLastUpdated; public AccessControlLogEntry(Map map) { count = (long) map.get(COUNT); reason = (String) map.get(REASON); context = (String) map.get(CONTEXT); object = (String) map.get(OBJECT); username = (String) map.get(USERNAME); ageSeconds = (Double) map.get(AGE_SECONDS); clientInfo = getMapFromRawClientInfo((String) map.get(CLIENT_INFO)); logEntry = map; // Redis 7.2 entryId = map.containsKey(ENTRY_ID) ? (long) map.get(ENTRY_ID) : -1L; timestampCreated = map.containsKey(TIMESTAMP_CREATED) ? (long) map.get(TIMESTAMP_CREATED) : -1L; timestampLastUpdated = map.containsKey(TIMESTAMP_LAST_UPDATED) ? (long) map.get(TIMESTAMP_LAST_UPDATED) : -1L; } public long getCount() { return count; } public String getReason() { return reason; } public String getContext() { return context; } public String getObject() { return object; } public String getUsername() { return username; } public Double getAgeSeconds() { return ageSeconds; } public Map getClientInfo() { return clientInfo; } /** * @return Generic map containing all key-value pairs returned by the server */ public Map getlogEntry() { return logEntry; } public long getEntryId() { return entryId; } public long getTimestampCreated() { return timestampCreated; } public long getTimestampLastUpdated() { return timestampLastUpdated; } /** * Convert the client-info string into a Map of String. When the value is empty, the value in the * map is set to an empty string The key order is maintained to reflect the string return by Redis * @param clientInfo * @return A Map with all client info */ private Map getMapFromRawClientInfo(String clientInfo) { String[] entries = clientInfo.split(" "); Map clientInfoMap = new LinkedHashMap<>(entries.length); for (String entry : entries) { String[] kvArray = entry.split("="); clientInfoMap.put(kvArray[0], (kvArray.length == 2) ? kvArray[1] : ""); } return clientInfoMap; } @Override public String toString() { return "AccessControlLogEntry{" + "count=" + count + ", reason='" + reason + '\'' + ", context='" + context + '\'' + ", object='" + object + '\'' + ", username='" + username + '\'' + ", ageSeconds='" + ageSeconds + '\'' + ", clientInfo=" + clientInfo + ", entryId=" + entryId + ", timestampCreated=" + timestampCreated + ", timestampLastUpdated=" + timestampLastUpdated + '}'; } } ================================================ FILE: src/main/java/redis/clients/jedis/resps/AccessControlUser.java ================================================ package redis.clients.jedis.resps; import java.util.Arrays; import java.util.List; import java.util.Map; import java.util.StringJoiner; // TODO: remove public class AccessControlUser { private final Map userInfo; private final List flags; private final List passwords; private final String commands; private final List keysList; private final String keys; private final List channelsList; private final String channels; private final List selectors; public AccessControlUser(Map map) { this.userInfo = map; this.flags = (List) map.get("flags"); this.passwords = (List) map.get("passwords"); this.commands = (String) map.get("commands"); Object localKeys = map.get("keys"); if (localKeys == null) { this.keys = null; this.keysList = null; } else if (localKeys instanceof List) { this.keysList = (List) localKeys; this.keys = joinStrings(this.keysList); } else { this.keys = (String) localKeys; this.keysList = Arrays.asList(this.keys.split(" ")); } Object localChannels = map.get("channels"); if (localChannels == null) { this.channels = null; this.channelsList = null; } else if (localChannels instanceof List) { this.channelsList = (List) localChannels; this.channels = joinStrings(this.channelsList); } else { this.channels = (String) localChannels; this.channelsList = Arrays.asList(this.channels.split(" ")); } this.selectors = (List) map.get("selectors"); } private static String joinStrings(List list) { StringJoiner joiner = new StringJoiner(" "); list.forEach(s -> joiner.add(s)); return joiner.toString(); } public List getFlags() { return flags; } /** * @deprecated Use {@link AccessControlUser#getPasswords()}. */ @Deprecated public List getPassword() { return passwords; } public List getPasswords() { return passwords; } public String getCommands() { return commands; } /** * @return Generic map containing all key-value pairs returned by the server */ public Map getUserInfo() { return userInfo; } public String getKeys() { return keys; } public List getKeysList() { return keysList; } public List getChannelsList() { return channelsList; } public String getChannels() { return channels; } public List getSelectors() { return selectors; } @Override public String toString() { return "AccessControlUser{" + "flags=" + flags + ", passwords=" + passwords + ", commands='" + commands + "', keys='" + keys + "', channels='" + channels + "', selectors=" + selectors + "}"; } } ================================================ FILE: src/main/java/redis/clients/jedis/resps/ClusterShardInfo.java ================================================ package redis.clients.jedis.resps; import java.util.List; import java.util.Map; /** * This class holds information about a shard of the cluster with command {@code CLUSTER SHARDS}. * They can be accessed via getters. There is also {@link ClusterShardInfo#getClusterShardInfo()} * method that returns a generic {@link Map} in case more info are returned from the server. */ public class ClusterShardInfo { public static final String SLOTS = "slots"; public static final String NODES = "nodes"; private final List> slots; private final List nodes; private final Map clusterShardInfo; /** * @param map contains key-value pairs with cluster shard info */ @SuppressWarnings("unchecked") public ClusterShardInfo(Map map) { slots = (List>) map.get(SLOTS); nodes = (List) map.get(NODES); clusterShardInfo = map; } public List> getSlots() { return slots; } public List getNodes() { return nodes; } public Map getClusterShardInfo() { return clusterShardInfo; } } ================================================ FILE: src/main/java/redis/clients/jedis/resps/ClusterShardNodeInfo.java ================================================ package redis.clients.jedis.resps; import java.util.Map; /** * This class holds information about a node of the cluster with command {@code CLUSTER SHARDS}. * They can be accessed via getters. There is also {@link ClusterShardNodeInfo#getClusterShardNodeInfo()} * method that returns a generic {@link Map} in case more info are returned from the server. */ public class ClusterShardNodeInfo { public static final String ID = "id"; public static final String ENDPOINT = "endpoint"; public static final String IP = "ip"; public static final String HOSTNAME = "hostname"; public static final String PORT = "port"; public static final String TLS_PORT = "tls-port"; public static final String ROLE = "role"; public static final String REPLICATION_OFFSET = "replication-offset"; public static final String HEALTH = "health"; private final String id; private final String endpoint; private final String ip; private final String hostname; private final Long port; private final Long tlsPort; private final String role; private final Long replicationOffset; private final String health; private final Map clusterShardNodeInfo; /** * @param map contains key-value pairs with node info */ public ClusterShardNodeInfo(Map map) { id = (String) map.get(ID); endpoint = (String) map.get(ENDPOINT); ip = (String) map.get(IP); hostname = (String) map.get(HOSTNAME); port = (Long) map.get(PORT); tlsPort = (Long) map.get(TLS_PORT); role = (String) map.get(ROLE); replicationOffset = (Long) map.get(REPLICATION_OFFSET); health = (String) map.get(HEALTH); clusterShardNodeInfo = map; } public String getId() { return id; } public String getEndpoint() { return endpoint; } public String getIp() { return ip; } public String getHostname() { return hostname; } public Long getPort() { return port; } public Long getTlsPort() { return tlsPort; } public String getRole() { return role; } public Long getReplicationOffset() { return replicationOffset; } public String getHealth() { return health; } public Map getClusterShardNodeInfo() { return clusterShardNodeInfo; } public boolean isSsl() { return tlsPort != null; } } ================================================ FILE: src/main/java/redis/clients/jedis/resps/CommandDocument.java ================================================ package redis.clients.jedis.resps; import redis.clients.jedis.Builder; import static redis.clients.jedis.BuilderFactory.STRING; import java.util.ArrayList; import java.util.Collections; import java.util.List; import java.util.Map; import java.util.stream.Collectors; import redis.clients.jedis.util.KeyValue; public class CommandDocument { private static final String SUMMARY_STR = "summary"; private static final String SINCE_STR = "since"; private static final String GROUP_STR = "group"; private static final String COMPLEXITY_STR = "complexity"; private static final String HISTORY_STR = "history"; private final String summary; private final String since; private final String group; private final String complexity; private final List history; @Deprecated public CommandDocument(String summary, String since, String group, String complexity, List history) { this.summary = summary; this.since = since; this.group = group; this.complexity = complexity; this.history = (List) history; } public CommandDocument(Map map) { this.summary = (String) map.get(SUMMARY_STR); this.since = (String) map.get(SINCE_STR); this.group = (String) map.get(GROUP_STR); this.complexity = (String) map.get(COMPLEXITY_STR); List historyObject = (List) map.get(HISTORY_STR); if (historyObject == null) { this.history = null; } else if (historyObject.isEmpty()) { this.history = Collections.emptyList(); } else if (historyObject.get(0) instanceof KeyValue) { this.history = historyObject.stream().map(o -> (KeyValue) o) .map(kv -> (String) kv.getKey() + ": " + (String) kv.getValue()) .collect(Collectors.toList()); } else { this.history = historyObject.stream().map(o -> (List) o) .map(l -> (String) l.get(0) + ": " + (String) l.get(1)) .collect(Collectors.toList()); } } public String getSummary() { return summary; } public String getSince() { return since; } public String getGroup() { return group; } public String getComplexity() { return complexity; } public List getHistory() { return history; } @Deprecated public static final Builder COMMAND_DOCUMENT_BUILDER = new Builder() { @Override public CommandDocument build(Object data) { List commandData = (List) data; String summary = STRING.build(commandData.get(1)); String since = STRING.build(commandData.get(3)); String group = STRING.build(commandData.get(5)); String complexity = STRING.build(commandData.get(7)); List history = null; if (STRING.build(commandData.get(8)).equals("history")) { List> rawHistory = (List>) commandData.get(9); history = new ArrayList<>(rawHistory.size()); for (List timePoint : rawHistory) { history.add(STRING.build(timePoint.get(0)) + ": " + STRING.build(timePoint.get(1))); } } return new CommandDocument(summary, since, group, complexity, history); } }; } ================================================ FILE: src/main/java/redis/clients/jedis/resps/CommandInfo.java ================================================ package redis.clients.jedis.resps; import redis.clients.jedis.Builder; import java.util.HashMap; import java.util.List; import java.util.Map; import static redis.clients.jedis.BuilderFactory.LONG; import static redis.clients.jedis.BuilderFactory.STRING; import static redis.clients.jedis.BuilderFactory.STRING_LIST; public class CommandInfo { private final String name; private final long arity; private final List flags; private final long firstKey; private final long lastKey; private final long step; private final List aclCategories; private final List tips; private final Map subcommands; /** * THIS IGNORES 'subcommands' parameter. * @param subcommands WILL BE IGNORED * @deprecated */ @Deprecated public CommandInfo(long arity, List flags, long firstKey, long lastKey, long step, List aclCategories, List tips, List subcommands) { this((String) null, arity, flags, firstKey, lastKey, step, aclCategories, tips, (Map) null); } private CommandInfo(String name, long arity, List flags, long firstKey, long lastKey, long step, List aclCategories, List tips, Map subcommands) { this.name = name; this.arity = arity; this.flags = flags; this.firstKey = firstKey; this.lastKey = lastKey; this.step = step; this.aclCategories = aclCategories; this.tips = tips; this.subcommands = subcommands; } /** * Command name */ public String getName() { return name; } /** * Arity is the number of arguments a command expects. It follows a simple pattern: * A positive integer means a fixed number of arguments. * A negative integer means a minimal number of arguments. * * Examples: * * GET's arity is 2 since the command only accepts one argument and always has the format GET _key_. * MGET's arity is -2 since the command accepts at least one argument, but possibly multiple ones: MGET _key1_ [key2] [key3] .... */ public long getArity() { return arity; } /** * Command flags */ public List getFlags() { return flags; } /** * The position of the command's first key name argument */ public long getFirstKey() { return firstKey; } /** * The position of the command's last key name argument * Commands that accept a single key have both first key and last key set to 1 */ public long getLastKey() { return lastKey; } /** * This value is the step, or increment, between the first key and last key values where the keys are */ public long getStep() { return step; } /** * An array of simple strings that are the ACL categories to which the command belongs */ public List getAclCategories() { return aclCategories; } /** * Helpful information about the command */ public List getTips() { return tips; } /** * All the command's subcommands, if any */ public Map getSubcommands() { return subcommands; } public static final Builder COMMAND_INFO_BUILDER = new Builder() { @Override public CommandInfo build(Object data) { if (data == null) { return null; } List commandData = (List) data; if (commandData.isEmpty()) { return null; } String name = STRING.build(commandData.get(0)); long arity = LONG.build(commandData.get(1)); List flags = STRING_LIST.build(commandData.get(2)); long firstKey = LONG.build(commandData.get(3)); long lastKey = LONG.build(commandData.get(4)); long step = LONG.build(commandData.get(5)); // Redis 6.0 List aclCategories = commandData.size() >= 7 ? STRING_LIST.build(commandData.get(6)) : null; // Redis 7.0 List tips = commandData.size() >= 8 ? STRING_LIST.build(commandData.get(7)) : null; Map subcommands = commandData.size() >= 10 ? COMMAND_INFO_RESPONSE.build(commandData.get(9)) : null; return new CommandInfo(name, arity, flags, firstKey, lastKey, step, aclCategories, tips, subcommands); } }; public static final Builder> COMMAND_INFO_RESPONSE = new Builder>() { @Override public Map build(Object data) { if (data == null) { return null; } List rawList = (List) data; Map map = new HashMap<>(rawList.size()); for (Object rawCommandInfo : rawList) { CommandInfo info = CommandInfo.COMMAND_INFO_BUILDER.build(rawCommandInfo); if (info != null) { map.put(info.getName(), info); } } return map; } }; } ================================================ FILE: src/main/java/redis/clients/jedis/resps/FunctionStats.java ================================================ package redis.clients.jedis.resps; import java.util.LinkedHashMap; import java.util.List; import java.util.Map; import redis.clients.jedis.Builder; import redis.clients.jedis.BuilderFactory; import redis.clients.jedis.util.KeyValue; public class FunctionStats { private final Map runningScript; private final Map> engines; public FunctionStats(Map script, Map> engines) { this.runningScript = script; this.engines = engines; } public Map getRunningScript() { return runningScript; } public Map> getEngines() { return engines; } public static final Builder FUNCTION_STATS_BUILDER = new Builder() { @Override public FunctionStats build(Object data) { if (data == null) return null; List list = (List) data; if (list.isEmpty()) return null; if (list.get(0) instanceof KeyValue) { Map runningScriptMap = null; Map> enginesMap = null; for (KeyValue kv : (List) list) { switch (BuilderFactory.STRING.build(kv.getKey())) { case "running_script": runningScriptMap = BuilderFactory.ENCODED_OBJECT_MAP.build(kv.getValue()); break; case "engines": List ilist = (List) kv.getValue(); enginesMap = new LinkedHashMap<>(ilist.size()); for (KeyValue ikv : (List) kv.getValue()) { enginesMap.put(BuilderFactory.STRING.build(ikv.getKey()), BuilderFactory.ENCODED_OBJECT_MAP.build(ikv.getValue())); } break; } } return new FunctionStats(runningScriptMap, enginesMap); } Map runningScriptMap = list.get(1) == null ? null : BuilderFactory.ENCODED_OBJECT_MAP.build(list.get(1)); List enginesList = (List) list.get(3); Map> enginesMap = new LinkedHashMap<>(enginesList.size() / 2); for (int i = 0; i < enginesList.size(); i += 2) { enginesMap.put(BuilderFactory.STRING.build(enginesList.get(i)), BuilderFactory.ENCODED_OBJECT_MAP.build(enginesList.get(i + 1))); } return new FunctionStats(runningScriptMap, enginesMap); } }; } ================================================ FILE: src/main/java/redis/clients/jedis/resps/GeoRadiusResponse.java ================================================ package redis.clients.jedis.resps; import redis.clients.jedis.GeoCoordinate; import redis.clients.jedis.util.SafeEncoder; import java.util.Arrays; import java.util.Objects; public class GeoRadiusResponse { private byte[] member; private double distance; private GeoCoordinate coordinate; private long rawScore; public GeoRadiusResponse(byte[] member) { this.member = member; } public void setDistance(double distance) { this.distance = distance; } public void setCoordinate(GeoCoordinate coordinate) { this.coordinate = coordinate; } public void setRawScore(long rawScore) { this.rawScore = rawScore; } public byte[] getMember() { return member; } public String getMemberByString() { return SafeEncoder.encode(member); } /** * @return The distance of the returned item from the specified center. The distance is returned * in the same unit as the unit specified as the radius argument of the command. */ public double getDistance() { return distance; } /** * @return The longitude,latitude coordinates of the matching item. */ public GeoCoordinate getCoordinate() { return coordinate; } /** * @return The raw geohash-encoded sorted set score of the item, in the form of a 52 bit unsigned * integer. This is only useful for low level hacks or debugging and is otherwise of little * interest for the general user. */ public long getRawScore() { return rawScore; } @Override public boolean equals(Object obj) { if (obj == this) { return true; } if (!(obj instanceof GeoRadiusResponse)) { return false; } GeoRadiusResponse response = (GeoRadiusResponse) obj; return Double.compare(distance, response.getDistance()) == 0 && rawScore == response.getRawScore() && coordinate.equals(response.coordinate) && Arrays.equals(member, response.getMember()); } @Override public int hashCode() { int hash = 7; hash = 67 * hash + Arrays.hashCode(this.member); hash = 67 * hash + (int) (Double.doubleToLongBits(this.distance) ^ (Double.doubleToLongBits(this.distance) >>> 32)); hash = 67 * hash + Objects.hashCode(this.coordinate); hash = 67 * hash + (int) (this.rawScore ^ (this.rawScore >>> 32)); return hash; } } ================================================ FILE: src/main/java/redis/clients/jedis/resps/HotkeysInfo.java ================================================ package redis.clients.jedis.resps; import redis.clients.jedis.Builder; import redis.clients.jedis.util.KeyValue; import java.io.Serializable; import java.util.Collections; import java.util.LinkedHashMap; import java.util.List; import java.util.Map; import static redis.clients.jedis.BuilderFactory.*; /** * Response object for the HOTKEYS GET command. Contains statistics about hot keys tracked by CPU * time and network bytes. */ public class HotkeysInfo implements Serializable { private static final long serialVersionUID = 1L; // Field names from the response public static final String TRACKING_ACTIVE = "tracking-active"; public static final String SAMPLE_RATIO = "sample-ratio"; public static final String SELECTED_SLOTS = "selected-slots"; public static final String SAMPLED_COMMANDS_SELECTED_SLOTS_US = "sampled-commands-selected-slots-us"; public static final String ALL_COMMANDS_SELECTED_SLOTS_US = "all-commands-selected-slots-us"; public static final String ALL_COMMANDS_ALL_SLOTS_US = "all-commands-all-slots-us"; public static final String NET_BYTES_SAMPLED_COMMANDS_SELECTED_SLOTS = "net-bytes-sampled-commands-selected-slots"; public static final String NET_BYTES_ALL_COMMANDS_SELECTED_SLOTS = "net-bytes-all-commands-selected-slots"; public static final String NET_BYTES_ALL_COMMANDS_ALL_SLOTS = "net-bytes-all-commands-all-slots"; public static final String COLLECTION_START_TIME_UNIX_MS = "collection-start-time-unix-ms"; public static final String COLLECTION_DURATION_MS = "collection-duration-ms"; public static final String TOTAL_CPU_TIME_USER_MS = "total-cpu-time-user-ms"; public static final String TOTAL_CPU_TIME_SYS_MS = "total-cpu-time-sys-ms"; public static final String TOTAL_NET_BYTES = "total-net-bytes"; public static final String BY_CPU_TIME_US = "by-cpu-time-us"; public static final String BY_NET_BYTES = "by-net-bytes"; private final boolean trackingActive; private final long sampleRatio; private final List selectedSlots; // List of [start, end] slot ranges private final Long sampledCommandSelectedSlotsUs; private final Long allCommandsSelectedSlotsUs; private final long allCommandsAllSlotsUs; private final Long netBytesSampledCommandsSelectedSlots; private final Long netBytesAllCommandsSelectedSlots; private final long netBytesAllCommandsAllSlots; private final long collectionStartTimeUnixMs; private final long collectionDurationMs; private final long totalCpuTimeUserMs; private final long totalCpuTimeSysMs; private final long totalNetBytes; private final Map byCpuTimeUs; private final Map byNetBytes; public HotkeysInfo(boolean trackingActive, long sampleRatio, List selectedSlots, Long sampledCommandSelectedSlotsUs, Long allCommandsSelectedSlotsUs, long allCommandsAllSlotsUs, Long netBytesSampledCommandsSelectedSlots, Long netBytesAllCommandsSelectedSlots, long netBytesAllCommandsAllSlots, long collectionStartTimeUnixMs, long collectionDurationMs, long totalCpuTimeUserMs, long totalCpuTimeSysMs, long totalNetBytes, Map byCpuTimeUs, Map byNetBytes) { this.trackingActive = trackingActive; this.sampleRatio = sampleRatio; this.selectedSlots = selectedSlots; this.sampledCommandSelectedSlotsUs = sampledCommandSelectedSlotsUs; this.allCommandsSelectedSlotsUs = allCommandsSelectedSlotsUs; this.allCommandsAllSlotsUs = allCommandsAllSlotsUs; this.netBytesSampledCommandsSelectedSlots = netBytesSampledCommandsSelectedSlots; this.netBytesAllCommandsSelectedSlots = netBytesAllCommandsSelectedSlots; this.netBytesAllCommandsAllSlots = netBytesAllCommandsAllSlots; this.collectionStartTimeUnixMs = collectionStartTimeUnixMs; this.collectionDurationMs = collectionDurationMs; this.totalCpuTimeUserMs = totalCpuTimeUserMs; this.totalCpuTimeSysMs = totalCpuTimeSysMs; this.totalNetBytes = totalNetBytes; this.byCpuTimeUs = byCpuTimeUs; this.byNetBytes = byNetBytes; } public boolean isTrackingActive() { return trackingActive; } public long getSampleRatio() { return sampleRatio; } /** * Returns the selected slots. Each element is an int array that can be: *
    *
  • A single slot: {@code [slot]} (array with 1 element)
  • *
  • A slot range: {@code [start, end]} (array with 2 elements, inclusive)
  • *
* @return list of slot entries, empty if all slots are selected */ public List getSelectedSlots() { return selectedSlots; } public Long getSampledCommandSelectedSlotsUs() { return sampledCommandSelectedSlotsUs; } public Long getAllCommandsSelectedSlotsUs() { return allCommandsSelectedSlotsUs; } public long getAllCommandsAllSlotsUs() { return allCommandsAllSlotsUs; } public Long getNetBytesSampledCommandsSelectedSlots() { return netBytesSampledCommandsSelectedSlots; } public Long getNetBytesAllCommandsSelectedSlots() { return netBytesAllCommandsSelectedSlots; } public long getNetBytesAllCommandsAllSlots() { return netBytesAllCommandsAllSlots; } public long getCollectionStartTimeUnixMs() { return collectionStartTimeUnixMs; } public long getCollectionDurationMs() { return collectionDurationMs; } public long getTotalCpuTimeUserMs() { return totalCpuTimeUserMs; } public long getTotalCpuTimeSysMs() { return totalCpuTimeSysMs; } public long getTotalNetBytes() { return totalNetBytes; } public Map getByCpuTimeUs() { return byCpuTimeUs; } public Map getByNetBytes() { return byNetBytes; } @SuppressWarnings("unchecked") private static Map parseKeyValueMap(Object data) { if (data == null) { return Collections.emptyMap(); } List list = (List) data; if (list.isEmpty()) { return Collections.emptyMap(); } Map result = new LinkedHashMap<>(); if (list.get(0) instanceof KeyValue) { for (KeyValue kv : (List>) list) { result.put(STRING.build(kv.getKey()), LONG.build(kv.getValue())); } } else { // RESP2 format: alternating key-value pairs for (int i = 0; i < list.size(); i += 2) { result.put(STRING.build(list.get(i)), LONG.build(list.get(i + 1))); } } return result; } /** * Parse selected-slots which is an array of slot entries. Each entry can be: *
    *
  • A single slot: [slot] (array with 1 element)
  • *
  • A slot range: [start, end] (array with 2 elements)
  • *
* Example: [[0, 2], [100]] means slots 0-2 (range) and slot 100 (single). */ @SuppressWarnings("unchecked") private static List parseSlotRanges(Object data) { if (data == null) { return Collections.emptyList(); } List list = (List) data; if (list.isEmpty()) { return Collections.emptyList(); } List result = new java.util.ArrayList<>(list.size()); for (Object item : list) { if (item instanceof List) { List range = (List) item; if (range.size() == 1) { // Single slot int slot = LONG.build(range.get(0)).intValue(); result.add(new int[] { slot }); } else if (range.size() == 2) { // Slot range int start = LONG.build(range.get(0)).intValue(); int end = LONG.build(range.get(1)).intValue(); result.add(new int[] { start, end }); } } } return result; } public static final Builder HOTKEYS_INFO_BUILDER = new Builder() { @Override @SuppressWarnings("unchecked") public HotkeysInfo build(Object data) { if (data == null) { return null; } List list = (List) data; if (list.isEmpty()) { return null; } // Check if the response is wrapped in an outer array (single element that is a List) // This happens when Redis returns [[key1, val1, key2, val2, ...]] if (list.size() == 1 && list.get(0) instanceof List) { list = (List) list.get(0); if (list.isEmpty()) { return null; } } boolean trackingActive = false; long sampleRatio = 1; List selectedSlots = Collections.emptyList(); Long sampledCommandSelectedSlotsUs = null; Long allCommandsSelectedSlotsUs = null; long allCommandsAllSlotsUs = 0; Long netBytesSampledCommandsSelectedSlots = null; Long netBytesAllCommandsSelectedSlots = null; long netBytesAllCommandsAllSlots = 0; long collectionStartTimeUnixMs = 0; long collectionDurationMs = 0; long totalCpuTimeUserMs = 0; long totalCpuTimeSysMs = 0; long totalNetBytes = 0; Map byCpuTimeUs = Collections.emptyMap(); Map byNetBytes = Collections.emptyMap(); if (list.get(0) instanceof KeyValue) { // RESP3 format for (KeyValue kv : (List>) list) { String key = STRING.build(kv.getKey()); Object value = kv.getValue(); switch (key) { case TRACKING_ACTIVE: trackingActive = LONG.build(value) == 1; break; case SAMPLE_RATIO: sampleRatio = LONG.build(value); break; case SELECTED_SLOTS: selectedSlots = parseSlotRanges(value); break; case SAMPLED_COMMANDS_SELECTED_SLOTS_US: sampledCommandSelectedSlotsUs = LONG.build(value); break; case ALL_COMMANDS_SELECTED_SLOTS_US: allCommandsSelectedSlotsUs = LONG.build(value); break; case ALL_COMMANDS_ALL_SLOTS_US: allCommandsAllSlotsUs = LONG.build(value); break; case NET_BYTES_SAMPLED_COMMANDS_SELECTED_SLOTS: netBytesSampledCommandsSelectedSlots = LONG.build(value); break; case NET_BYTES_ALL_COMMANDS_SELECTED_SLOTS: netBytesAllCommandsSelectedSlots = LONG.build(value); break; case NET_BYTES_ALL_COMMANDS_ALL_SLOTS: netBytesAllCommandsAllSlots = LONG.build(value); break; case COLLECTION_START_TIME_UNIX_MS: collectionStartTimeUnixMs = LONG.build(value); break; case COLLECTION_DURATION_MS: collectionDurationMs = LONG.build(value); break; case TOTAL_CPU_TIME_USER_MS: totalCpuTimeUserMs = LONG.build(value); break; case TOTAL_CPU_TIME_SYS_MS: totalCpuTimeSysMs = LONG.build(value); break; case TOTAL_NET_BYTES: totalNetBytes = LONG.build(value); break; case BY_CPU_TIME_US: byCpuTimeUs = parseKeyValueMap(value); break; case BY_NET_BYTES: byNetBytes = parseKeyValueMap(value); break; } } } else { // RESP2 format: alternating key-value pairs for (int i = 0; i < list.size(); i += 2) { String key = STRING.build(list.get(i)); Object value = list.get(i + 1); switch (key) { case TRACKING_ACTIVE: trackingActive = LONG.build(value) == 1; break; case SAMPLE_RATIO: sampleRatio = LONG.build(value); break; case SELECTED_SLOTS: selectedSlots = parseSlotRanges(value); break; case SAMPLED_COMMANDS_SELECTED_SLOTS_US: sampledCommandSelectedSlotsUs = LONG.build(value); break; case ALL_COMMANDS_SELECTED_SLOTS_US: allCommandsSelectedSlotsUs = LONG.build(value); break; case ALL_COMMANDS_ALL_SLOTS_US: allCommandsAllSlotsUs = LONG.build(value); break; case NET_BYTES_SAMPLED_COMMANDS_SELECTED_SLOTS: netBytesSampledCommandsSelectedSlots = LONG.build(value); break; case NET_BYTES_ALL_COMMANDS_SELECTED_SLOTS: netBytesAllCommandsSelectedSlots = LONG.build(value); break; case NET_BYTES_ALL_COMMANDS_ALL_SLOTS: netBytesAllCommandsAllSlots = LONG.build(value); break; case COLLECTION_START_TIME_UNIX_MS: collectionStartTimeUnixMs = LONG.build(value); break; case COLLECTION_DURATION_MS: collectionDurationMs = LONG.build(value); break; case TOTAL_CPU_TIME_USER_MS: totalCpuTimeUserMs = LONG.build(value); break; case TOTAL_CPU_TIME_SYS_MS: totalCpuTimeSysMs = LONG.build(value); break; case TOTAL_NET_BYTES: totalNetBytes = LONG.build(value); break; case BY_CPU_TIME_US: byCpuTimeUs = parseKeyValueMap(value); break; case BY_NET_BYTES: byNetBytes = parseKeyValueMap(value); break; } } } return new HotkeysInfo(trackingActive, sampleRatio, selectedSlots, sampledCommandSelectedSlotsUs, allCommandsSelectedSlotsUs, allCommandsAllSlotsUs, netBytesSampledCommandsSelectedSlots, netBytesAllCommandsSelectedSlots, netBytesAllCommandsAllSlots, collectionStartTimeUnixMs, collectionDurationMs, totalCpuTimeUserMs, totalCpuTimeSysMs, totalNetBytes, byCpuTimeUs, byNetBytes); } }; } ================================================ FILE: src/main/java/redis/clients/jedis/resps/LCSMatchResult.java ================================================ package redis.clients.jedis.resps; import java.util.Collections; import java.util.List; /** * Result for STRALGO LCS command. */ public class LCSMatchResult { private String matchString; private List matches; private long len; public LCSMatchResult(String matchString) { this.matchString = matchString; } public LCSMatchResult(long len) { this.len = len; } public LCSMatchResult(List matches, long len) { this.matches = matches; this.len = len; } /** * Creates new {@link LCSMatchResult}. * * @param matchString * @param matches * @param len */ public LCSMatchResult(String matchString, List matches, long len) { this.matchString = matchString; this.matches = Collections.unmodifiableList(matches); this.len = len; } public String getMatchString() { return matchString; } public List getMatches() { return matches; } public long getLen() { return len; } /** * Match position in each string. */ public static class MatchedPosition { private final Position a; private final Position b; private final long matchLen; public MatchedPosition(Position a, Position b, long matchLen) { this.a = a; this.b = b; this.matchLen = matchLen; } public Position getA() { return a; } public Position getB() { return b; } public long getMatchLen() { return matchLen; } } /** * Position range. */ public static class Position { private final long start; private final long end; public Position(long start, long end) { this.start = start; this.end = end; } public long getStart() { return start; } public long getEnd() { return end; } } } ================================================ FILE: src/main/java/redis/clients/jedis/resps/LatencyHistoryInfo.java ================================================ package redis.clients.jedis.resps; import redis.clients.jedis.Builder; import java.util.List; import static redis.clients.jedis.BuilderFactory.LONG; public class LatencyHistoryInfo { private final long timestamp; private final long latency; public LatencyHistoryInfo(long timestamp, long latency) { this.timestamp = timestamp; this.latency = latency; } public long getTimestamp() { return timestamp; } public long getLatency() { return latency; } public static final Builder LATENCY_HISTORY_BUILDER = new Builder() { @Override public LatencyHistoryInfo build(Object data) { List commandData = (List) data; long timestamp = LONG.build(commandData.get(0)); long latency = LONG.build(commandData.get(1)); return new LatencyHistoryInfo(timestamp, latency); } }; } ================================================ FILE: src/main/java/redis/clients/jedis/resps/LatencyLatestInfo.java ================================================ package redis.clients.jedis.resps; import redis.clients.jedis.Builder; import java.util.List; import static redis.clients.jedis.BuilderFactory.LONG; import static redis.clients.jedis.BuilderFactory.STRING; public class LatencyLatestInfo { private final String command; private final long timestamp; private final long lastEventLatency; private final long maxEventLatency; public LatencyLatestInfo(String command, long timestamp, long lastEventLatency, long maxEventLatency) { this.command = command; this.timestamp = timestamp; this.lastEventLatency = lastEventLatency; this.maxEventLatency = maxEventLatency; } public String getCommand() { return command; } public long getTimestamp() { return timestamp; } public long getLastEventLatency() { return lastEventLatency; } public long getMaxEventLatency() { return maxEventLatency; } public static final Builder LATENCY_LATEST_BUILDER = new Builder() { @Override public LatencyLatestInfo build(Object data) { List commandData = (List) data; String command = STRING.build(commandData.get(0)); long timestamp = LONG.build(commandData.get(1)); long lastEventLatency = LONG.build(commandData.get(2)); long maxEventLatency = LONG.build(commandData.get(3)); return new LatencyLatestInfo(command, timestamp, lastEventLatency, maxEventLatency); } }; } ================================================ FILE: src/main/java/redis/clients/jedis/resps/LibraryInfo.java ================================================ package redis.clients.jedis.resps; import static redis.clients.jedis.BuilderFactory.STRING; import static redis.clients.jedis.BuilderFactory.ENCODED_OBJECT_MAP; import java.util.List; import java.util.Map; import java.util.stream.Collectors; import redis.clients.jedis.Builder; import redis.clients.jedis.BuilderFactory; import redis.clients.jedis.util.KeyValue; //[library_name=mylib, engine=LUA, functions=[[name=myfunc, description=null, flags=[]]], library_code=#!LUA name=mylib // redis.register_function('myfunc', function(keys, args) return args[1] end)] public class LibraryInfo { private final String libraryName; private final String engine; private final List> functions; private final String libraryCode; /** * @deprecated Since 7.4. This constructor will be removed in the next major release. * Use {@link #LibraryInfo(String, String, List, String)} instead. */ @Deprecated public LibraryInfo(String libraryName, String engineName, List> functions) { this(libraryName, engineName, functions, null); } public LibraryInfo(String libraryName, String engineName, List> functions, String code) { this.libraryName = libraryName; this.engine = engineName; this.functions = functions; this.libraryCode = code; } public String getLibraryName() { return libraryName; } public String getEngine() { return engine; } public List> getFunctions() { return functions; } public String getLibraryCode() { return libraryCode; } private static class LibraryInfoHolder { String libraryName; String engineName; String libraryCode; List> functions; } public static final Builder LIBRARY_INFO = new Builder() { @Override public LibraryInfo build(Object data) { if (data == null) return null; List list = (List) data; if (list.isEmpty()) return null; LibraryInfoHolder holder = new LibraryInfoHolder(); if (list.get(0) instanceof KeyValue) { // RESP3 format: list of KeyValue objects for (KeyValue kv : (List) list) { processField(kv.getKey(), kv.getValue(), holder); } } else { // RESP2 format: flat list with alternating key-value pairs // Note: Redis Enterprise may include extra fields like "consistent" for (int i = 0; i + 1 < list.size(); i += 2) { processField(list.get(i), list.get(i + 1), holder); } } return new LibraryInfo(holder.libraryName, holder.engineName, holder.functions, holder.libraryCode); } private void processField(Object key, Object value, LibraryInfoHolder holder) { switch (BuilderFactory.STRING.build(key)) { case "library_name": holder.libraryName = BuilderFactory.STRING.build(value); break; case "engine": holder.engineName = BuilderFactory.STRING.build(value); break; case "functions": holder.functions = ((List) value).stream() .map(ENCODED_OBJECT_MAP::build) .collect(Collectors.toList()); break; case "library_code": holder.libraryCode = BuilderFactory.STRING.build(value); break; } } }; /** * @deprecated Use {@link LibraryInfo#LIBRARY_INFO}. */ @Deprecated public static final Builder LIBRARY_BUILDER = LIBRARY_INFO; public static final Builder> LIBRARY_INFO_LIST = new Builder>() { @Override public List build(Object data) { List list = (List) data; return list.stream().map(o -> LibraryInfo.LIBRARY_INFO.build(o)).collect(Collectors.toList()); } }; } ================================================ FILE: src/main/java/redis/clients/jedis/resps/RawVector.java ================================================ package redis.clients.jedis.resps; import redis.clients.jedis.annots.Experimental; /** * Result of a VEMB RAW command, containing raw vector data and metadata. For regular VEMB commands * (without RAW), use List<Double> directly. */ @Experimental public class RawVector { private final String quantizationType; private final byte[] rawData; private final Double norm; private final Double quantizationRange; /** * Constructor for RAW VEMB results. * @param quantizationType the quantization type (fp32, bin, or q8) * @param rawData the raw vector data blob * @param norm the L2 norm of the vector before normalization * @param quantizationRange the quantization range (only for q8, null otherwise) */ public RawVector(String quantizationType, byte[] rawData, Double norm, Double quantizationRange) { this.quantizationType = quantizationType; this.rawData = rawData; this.norm = norm; this.quantizationRange = quantizationRange; } /** * Get the quantization type. * @return quantization type string (fp32, bin, or q8) */ public String getQuantizationType() { return quantizationType; } /** * Get the raw vector data. * @return raw data blob */ public byte[] getRawData() { return rawData; } /** * Get the L2 norm. * @return L2 norm value */ public Double getNorm() { return norm; } /** * Get the quantization range (for q8 quantization). * @return quantization range, or null if not q8 quantization */ public Double getQuantizationRange() { return quantizationRange; } @Override public String toString() { return "RawVector{quantizationType='" + quantizationType + "', norm=" + norm + ", quantizationRange=" + quantizationRange + ", rawDataLength=" + (rawData != null ? rawData.length : 0) + "}"; } @Override public boolean equals(Object o) { if (this == o) return true; if (o == null || getClass() != o.getClass()) return false; RawVector that = (RawVector) o; if (quantizationType != null ? !quantizationType.equals(that.quantizationType) : that.quantizationType != null) return false; if (rawData != null ? !java.util.Arrays.equals(rawData, that.rawData) : that.rawData != null) return false; if (norm != null ? !norm.equals(that.norm) : that.norm != null) return false; return quantizationRange != null ? quantizationRange.equals(that.quantizationRange) : that.quantizationRange == null; } @Override public int hashCode() { int result = quantizationType != null ? quantizationType.hashCode() : 0; result = 31 * result + (rawData != null ? java.util.Arrays.hashCode(rawData) : 0); result = 31 * result + (norm != null ? norm.hashCode() : 0); result = 31 * result + (quantizationRange != null ? quantizationRange.hashCode() : 0); return result; } } ================================================ FILE: src/main/java/redis/clients/jedis/resps/ScanResult.java ================================================ package redis.clients.jedis.resps; import java.util.List; import redis.clients.jedis.params.ScanParams; import redis.clients.jedis.util.SafeEncoder; public class ScanResult { private byte[] cursor; private List results; public ScanResult(String cursor, List results) { this(SafeEncoder.encode(cursor), results); } public ScanResult(byte[] cursor, List results) { this.cursor = cursor; this.results = results; } /** * Returns the new value of the cursor * @return the new cursor value. {@link ScanParams#SCAN_POINTER_START} when a complete iteration has finished */ public String getCursor() { return SafeEncoder.encode(cursor); } /** * Is the iteration complete. I.e. was the complete dataset scanned. * @return {@code true} if the iteration is complete */ public boolean isCompleteIteration() { return ScanParams.SCAN_POINTER_START.equals(getCursor()); } public byte[] getCursorAsBytes() { return cursor; } /** * The scan results from the current call. * @return the scan results */ public List getResult() { return results; } } ================================================ FILE: src/main/java/redis/clients/jedis/resps/Slowlog.java ================================================ package redis.clients.jedis.resps; import java.util.ArrayList; import java.util.List; import redis.clients.jedis.BuilderFactory; import redis.clients.jedis.HostAndPort; import redis.clients.jedis.util.SafeEncoder; public class Slowlog { private final long id; private final long timeStamp; private final long executionTime; private final List args; private HostAndPort clientIpPort; private String clientName; private static final String COMMA = ","; @SuppressWarnings("unchecked") private Slowlog(List properties) { this.id = (Long) properties.get(0); this.timeStamp = (Long) properties.get(1); this.executionTime = (Long) properties.get(2); this.args = BuilderFactory.STRING_LIST.build(properties.get(3)); if (properties.size() == 4) return; this.clientIpPort = HostAndPort.from(SafeEncoder.encode((byte[]) properties.get(4))); this.clientName = SafeEncoder.encode((byte[]) properties.get(5)); } @SuppressWarnings("unchecked") public static List from(List nestedMultiBulkReply) { List logs = new ArrayList<>(nestedMultiBulkReply.size()); for (Object obj : nestedMultiBulkReply) { List properties = (List) obj; logs.add(new Slowlog(properties)); } return logs; } public long getId() { return id; } public long getTimeStamp() { return timeStamp; } public long getExecutionTime() { return executionTime; } public List getArgs() { return args; } public HostAndPort getClientIpPort() { return clientIpPort; } public String getClientName() { return clientName; } @Override public String toString() { return new StringBuilder().append(id).append(COMMA).append(timeStamp).append(COMMA) .append(executionTime).append(COMMA).append(args).toString(); } } ================================================ FILE: src/main/java/redis/clients/jedis/resps/StreamConsumerFullInfo.java ================================================ package redis.clients.jedis.resps; import java.io.Serializable; import java.util.List; import java.util.Map; import redis.clients.jedis.StreamEntryID; /** * This class holds information about a stream consumer with command * {@code xinfo stream mystream full}. They can be accessed via getters. There is also * {@link StreamConsumerFullInfo#getConsumerInfo()} method that returns a generic {@link Map} in * case more info are returned from the server. */ public class StreamConsumerFullInfo implements Serializable { public static final String NAME = "name"; public static final String SEEN_TIME = "seen-time"; public static final String ACTIVE_TIME = "active-time"; public static final String PEL_COUNT = "pel-count"; public static final String PENDING = "pending"; private final String name; private final Long seenTime; private final Long activeTime; // since Redis 7.2 private final Long pelCount; private final List> pending; private final Map consumerInfo; @SuppressWarnings("unchecked") public StreamConsumerFullInfo(Map map) { consumerInfo = map; name = (String) map.get(NAME); seenTime = (Long) map.get(SEEN_TIME); activeTime = (Long) map.get(ACTIVE_TIME); pending = (List>) map.get(PENDING); pelCount = (Long) map.get(PEL_COUNT); pending.forEach(entry -> entry.set(0, new StreamEntryID((String) entry.get(0)))); } public String getName() { return name; } public Long getSeenTime() { return seenTime; } /** * Since Redis 7.2. */ public Long getActiveTime() { return activeTime; } public Long getPelCount() { return pelCount; } public List> getPending() { return pending; } /** * All data. */ public Map getConsumerInfo() { return consumerInfo; } } ================================================ FILE: src/main/java/redis/clients/jedis/resps/StreamConsumerInfo.java ================================================ package redis.clients.jedis.resps; import java.util.Map; /** * This class holds information about a consumer. They can be accessed via getters. There is also * {@link StreamConsumersInfo#getConsumerInfo()}} method that returns a generic {@code Map} in case * more info are returned from the server. */ public class StreamConsumerInfo { public static final String NAME = "name"; public static final String IDLE = "idle"; public static final String PENDING = "pending"; public static final String INACTIVE = "inactive"; private final String name; private final long idle; private final long pending; private final Long inactive; private final Map consumerInfo; /** * @param map contains key-value pairs with consumer info */ public StreamConsumerInfo(Map map) { consumerInfo = map; name = (String) map.get(NAME); idle = (Long) map.get(IDLE); pending = (Long) map.get(PENDING); inactive = (Long) map.get(INACTIVE); } public String getName() { return name; } public long getIdle() { return idle; } public long getPending() { return pending; } /** * Since Redis 7.2. */ public Long getInactive() { return inactive; } /** * All data. * @return Generic map containing all key-value pairs returned by the server */ public Map getConsumerInfo() { return consumerInfo; } } ================================================ FILE: src/main/java/redis/clients/jedis/resps/StreamConsumersInfo.java ================================================ package redis.clients.jedis.resps; import java.util.Map; /** * This class holds information about a consumer. They can be accessed via getters. There is also * {@link StreamConsumersInfo#getConsumerInfo()}} method that returns a generic {@code Map} in case * more info are returned from the server. * @deprecated Use {@link StreamConsumerInfo}. */ // TODO: rename to StreamConsumerInfo ? @Deprecated public class StreamConsumersInfo extends StreamConsumerInfo { /** * @param map contains key-value pairs with consumer info */ public StreamConsumersInfo(Map map) { super(map); } } ================================================ FILE: src/main/java/redis/clients/jedis/resps/StreamEntry.java ================================================ package redis.clients.jedis.resps; import java.io.IOException; import java.io.Serializable; import java.util.Map; import redis.clients.jedis.StreamEntryID; public class StreamEntry implements Serializable { private static final long serialVersionUID = 1L; private StreamEntryID id; private Map fields; private Long millisElapsedFromDelivery; private Long deliveredCount; public StreamEntry(StreamEntryID id, Map fields) { this.id = id; this.fields = fields; } public StreamEntry(StreamEntryID id, Map fields, Long millisElapsedFromDelivery, Long deliveredCount) { this.id = id; this.fields = fields; this.millisElapsedFromDelivery = millisElapsedFromDelivery; this.deliveredCount = deliveredCount; } /** * @return the milliseconds since the last delivery of this message when CLAIM was used. *
    *
  • {@code null} when not applicable
  • *
  • {@code 0} means not claimed from the pending entries list (PEL)
  • *
  • {@code > 0} means claimed from the PEL
  • *
* @since 7.1 */ public Long getMillisElapsedFromDelivery() { return millisElapsedFromDelivery; } /** * @return the number of prior deliveries of this message when CLAIM was used: *
    *
  • {@code null} when not applicable
  • *
  • {@code 0} means not claimed from the pending entries list (PEL)
  • *
  • {@code > 0} means claimed from the PEL
  • *
* @since 7.1 */ public Long getDeliveredCount() { return deliveredCount; } public boolean isClaimed() { return this.deliveredCount != null && this.deliveredCount > 0; } public StreamEntryID getID() { return id; } public Map getFields() { return fields; } @Override public String toString() { return id + " " + fields; } private void writeObject(java.io.ObjectOutputStream out) throws IOException { out.writeUnshared(this.id); out.writeUnshared(this.fields); } private void readObject(java.io.ObjectInputStream in) throws IOException, ClassNotFoundException { this.id = (StreamEntryID) in.readUnshared(); this.fields = (Map) in.readUnshared(); } } ================================================ FILE: src/main/java/redis/clients/jedis/resps/StreamEntryBinary.java ================================================ package redis.clients.jedis.resps; import java.io.IOException; import java.io.Serializable; import java.util.Map; import redis.clients.jedis.StreamEntryID; public class StreamEntryBinary implements Serializable { private static final long serialVersionUID = 1L; private StreamEntryID id; private Map fields; private Long millisElapsedFromDelivery; private Long deliveredCount; public StreamEntryBinary(StreamEntryID id, Map fields) { this.id = id; this.fields = fields; } public StreamEntryBinary(StreamEntryID id, Map fields, Long millisElapsedFromDelivery, Long deliveredCount) { this.id = id; this.fields = fields; this.millisElapsedFromDelivery = millisElapsedFromDelivery; this.deliveredCount = deliveredCount; } /** * @return the milliseconds since the last delivery of this message when CLAIM was used. *
    *
  • {@code null} when not applicable
  • *
  • {@code 0} means not claimed from the pending entries list (PEL)
  • *
  • {@code > 0} means claimed from the PEL
  • *
* @since 7.1 */ public Long getMillisElapsedFromDelivery() { return millisElapsedFromDelivery; } /** * @return the number of prior deliveries of this message when CLAIM was used: *
    *
  • {@code null} when not applicable
  • *
  • {@code 0} means not claimed from the pending entries list (PEL)
  • *
  • {@code > 0} means claimed from the PEL
  • *
* @since 7.1 */ public Long getDeliveredCount() { return deliveredCount; } public boolean isClaimed() { return this.deliveredCount != null && this.deliveredCount > 0; } public StreamEntryID getID() { return id; } public Map getFields() { return fields; } @Override public String toString() { return id + " " + fields; } private void writeObject(java.io.ObjectOutputStream out) throws IOException { out.writeUnshared(this.id); out.writeUnshared(this.fields); } private void readObject(java.io.ObjectInputStream in) throws IOException, ClassNotFoundException { this.id = (StreamEntryID) in.readUnshared(); this.fields = (Map) in.readUnshared(); } } ================================================ FILE: src/main/java/redis/clients/jedis/resps/StreamEntryDeletionResult.java ================================================ package redis.clients.jedis.resps; /** * Represents the result of a stream entry deletion operation for XDELEX and XACKDEL commands. *
    *
  • NOT_FOUND (-1): ID doesn't exist in stream
  • *
  • DELETED (1): Entry was deleted/acknowledged and deleted
  • *
  • NOT_DELETED_UNACKNOWLEDGED_OR_STILL_REFERENCED (2): Entry wasn't deleted.
  • *
*/ public enum StreamEntryDeletionResult { /** * The stream entry ID doesn't exist in the stream. *

* Returned when trying to delete/acknowledge a non-existent entry. *

*/ NOT_FOUND(-1), /** * The entry was successfully deleted/acknowledged and deleted. *

* This is the typical successful case. *

*/ DELETED(1), /** * The entry was not deleted due to one of the following reasons: *
    *
  • For XDELEX: The entry was not acknowledged by any consumer group
  • *
  • For XACKDEL: The entry still has pending references in other consumer groups
  • *
*/ NOT_DELETED_UNACKNOWLEDGED_OR_STILL_REFERENCED(2); private final int code; StreamEntryDeletionResult(int code) { this.code = code; } /** * Gets the numeric code returned by Redis for this result. * @return the numeric code (-1, 1, or 2) */ public int getCode() { return code; } /** * Creates a StreamEntryDeletionResult from the numeric code returned by Redis. * @param code the numeric code from Redis * @return the corresponding StreamEntryDeletionResult * @throws IllegalArgumentException if the code is not recognized */ public static StreamEntryDeletionResult fromCode(int code) { switch (code) { case -1: return NOT_FOUND; case 1: return DELETED; case 2: return NOT_DELETED_UNACKNOWLEDGED_OR_STILL_REFERENCED; default: throw new IllegalArgumentException("Unknown stream entry deletion result code: " + code); } } /** * Creates a StreamEntryDeletionResult from a Long value returned by Redis. * @param value the Long value from Redis * @return the corresponding StreamEntryDeletionResult * @throws IllegalArgumentException if the value is null or not recognized */ public static StreamEntryDeletionResult fromLong(Long value) { if (value == null) { throw new IllegalArgumentException("Stream entry deletion result value cannot be null"); } return fromCode(value.intValue()); } @Override public String toString() { return name() + "(" + code + ")"; } } ================================================ FILE: src/main/java/redis/clients/jedis/resps/StreamFullInfo.java ================================================ package redis.clients.jedis.resps; import redis.clients.jedis.StreamEntryID; import java.io.Serializable; import java.util.List; import java.util.Map; /** * This class holds information about a stream info with command {@code xinfo stream mystream full}. * They can be accessed via getters. There is also {@link StreamFullInfo#getStreamFullInfo()} method * that returns a generic {@link Map} in case where more info are returned from the server. */ public class StreamFullInfo implements Serializable { public static final String LENGTH = "length"; public static final String RADIX_TREE_KEYS = "radix-tree-keys"; public static final String RADIX_TREE_NODES = "radix-tree-nodes"; public static final String GROUPS = "groups"; public static final String LAST_GENERATED_ID = "last-generated-id"; public static final String ENTRIES = "entries"; private final long length; private final long radixTreeKeys; private final long radixTreeNodes; private final List groups; private final StreamEntryID lastGeneratedId; private final List entries; private final Map streamFullInfo; /** * @param map contains key-value pairs with stream info */ @SuppressWarnings("unchecked") public StreamFullInfo(Map map) { streamFullInfo = map; length = (Long) map.get(LENGTH); radixTreeKeys = (Long) map.get(RADIX_TREE_KEYS); radixTreeNodes = (Long) map.get(RADIX_TREE_NODES); groups = (List) map.get(GROUPS); lastGeneratedId = (StreamEntryID) map.get(LAST_GENERATED_ID); entries = (List) map.get(ENTRIES); } public long getLength() { return length; } public long getRadixTreeKeys() { return radixTreeKeys; } public long getRadixTreeNodes() { return radixTreeNodes; } public List getGroups() { return groups; } public StreamEntryID getLastGeneratedId() { return lastGeneratedId; } public List getEntries() { return entries; } public Map getStreamFullInfo() { return streamFullInfo; } } ================================================ FILE: src/main/java/redis/clients/jedis/resps/StreamGroupFullInfo.java ================================================ package redis.clients.jedis.resps; import redis.clients.jedis.StreamEntryID; import java.io.Serializable; import java.util.List; import java.util.Map; /** * This class holds information about a stream group with command {@code xinfo stream mystream full}. * They can be accessed via getters. There is also {@link StreamGroupFullInfo#getGroupFullInfo()} * method that returns a generic {@link Map} in case more info are returned from the server. */ public class StreamGroupFullInfo implements Serializable { public static final String NAME = "name"; public static final String CONSUMERS = "consumers"; public static final String PENDING = "pending"; public static final String LAST_DELIVERED = "last-delivered-id"; public static final String PEL_COUNT = "pel-count"; private final String name; private final List consumers; private final List> pending; private final Long pelCount; private final StreamEntryID lastDeliveredId; private final Map groupFullInfo; /** * @param map contains key-value pairs with group info */ @SuppressWarnings("unchecked") public StreamGroupFullInfo(Map map) { groupFullInfo = map; name = (String) map.get(NAME); consumers = (List) map.get(CONSUMERS); pending = (List>) map.get(PENDING); lastDeliveredId = (StreamEntryID) map.get(LAST_DELIVERED); pelCount = (Long) map.get(PEL_COUNT); pending.stream().forEach(entry -> entry.set(0, new StreamEntryID((String) entry.get(0)))); } public String getName() { return name; } public List getConsumers() { return consumers; } public List> getPending() { return pending; } public StreamEntryID getLastDeliveredId() { return lastDeliveredId; } /** * @return Generic map containing all key-value pairs returned by the server */ public Map getGroupFullInfo() { return groupFullInfo; } public Long getPelCount() { return pelCount; } } ================================================ FILE: src/main/java/redis/clients/jedis/resps/StreamGroupInfo.java ================================================ package redis.clients.jedis.resps; import java.io.Serializable; import java.util.Map; import redis.clients.jedis.StreamEntryID; /** * This class holds information about a stream group. They can be accessed via getters. There is also * {@link StreamGroupInfo#getGroupInfo()} method that returns a generic {@code Map} in case more * info are returned from the server. */ public class StreamGroupInfo implements Serializable { public static final String NAME = "name"; public static final String CONSUMERS = "consumers"; public static final String PENDING = "pending"; public static final String LAST_DELIVERED = "last-delivered-id"; private final String name; private final long consumers; private final long pending; private final StreamEntryID lastDeliveredId; private final Map groupInfo; /** * @param map contains key-value pairs with group info */ public StreamGroupInfo(Map map) { groupInfo = map; name = (String) map.get(NAME); consumers = (long) map.get(CONSUMERS); pending = (long) map.get(PENDING); lastDeliveredId = (StreamEntryID) map.get(LAST_DELIVERED); } public String getName() { return name; } public long getConsumers() { return consumers; } public long getPending() { return pending; } public StreamEntryID getLastDeliveredId() { return lastDeliveredId; } /** * @return Generic map containing all key-value pairs returned by the server */ public Map getGroupInfo() { return groupInfo; } } ================================================ FILE: src/main/java/redis/clients/jedis/resps/StreamInfo.java ================================================ package redis.clients.jedis.resps; import java.io.Serializable; import java.util.Map; import redis.clients.jedis.StreamEntryID; /** * This class holds information about stream. They can be accessed via getters. There is also * {@link StreamInfo#getStreamInfo} method that returns a generic {@code Map} in case more info are * returned from the server. */ public class StreamInfo implements Serializable { public static final String LENGTH = "length"; public static final String RADIX_TREE_KEYS = "radix-tree-keys"; public static final String RADIX_TREE_NODES = "radix-tree-nodes"; public static final String GROUPS = "groups"; public static final String LAST_GENERATED_ID = "last-generated-id"; public static final String FIRST_ENTRY = "first-entry"; public static final String LAST_ENTRY = "last-entry"; public static final String IDMP_DURATION = "idmp-duration"; public static final String IDMP_MAXSIZE = "idmp-maxsize"; public static final String PIDS_TRACKED = "pids-tracked"; public static final String IIDS_TRACKED = "iids-tracked"; public static final String IIDS_ADDED = "iids-added"; public static final String IIDS_DUPLICATES = "iids-duplicates"; private final long length; private final long radixTreeKeys; private final long radixTreeNodes; private final long groups; private final StreamEntryID lastGeneratedId; private final StreamEntry firstEntry; private final StreamEntry lastEntry; private final Long idmpDuration; private final Long idmpMaxsize; private final Long pidsTracked; private final Long iidsTracked; private final Long iidsAdded; private final Long iidsDuplicates; private final Map streamInfo; /** * @param map contains key-value pairs with stream info */ public StreamInfo(Map map) { streamInfo = map; length = (Long) map.get(LENGTH); radixTreeKeys = (Long) map.get(RADIX_TREE_KEYS); radixTreeNodes = (Long) map.get(RADIX_TREE_NODES); groups = (Long) map.get(GROUPS); lastGeneratedId = (StreamEntryID) map.get(LAST_GENERATED_ID); firstEntry = (StreamEntry) map.get(FIRST_ENTRY); lastEntry = (StreamEntry) map.get(LAST_ENTRY); idmpDuration = (Long) map.get(IDMP_DURATION); idmpMaxsize = (Long) map.get(IDMP_MAXSIZE); pidsTracked = (Long) map.get(PIDS_TRACKED); iidsTracked = (Long) map.get(IIDS_TRACKED); iidsAdded = (Long) map.get(IIDS_ADDED); iidsDuplicates = (Long) map.get(IIDS_DUPLICATES); } public long getLength() { return length; } public long getRadixTreeKeys() { return radixTreeKeys; } public long getRadixTreeNodes() { return radixTreeNodes; } public long getGroups() { return groups; } public StreamEntryID getLastGeneratedId() { return lastGeneratedId; } public StreamEntry getFirstEntry() { return firstEntry; } public StreamEntry getLastEntry() { return lastEntry; } /** * @return The duration (in seconds) that each idempotent ID is kept, or null if not configured */ public Long getIdmpDuration() { return idmpDuration; } /** * @return The maximum number of most recent idempotent IDs kept for each producer ID, or null if not configured */ public Long getIdmpMaxsize() { return idmpMaxsize; } /** * @return The number of producer IDs currently tracked in the stream, or null if not available */ public Long getPidsTracked() { return pidsTracked; } /** * @return The number of idempotent IDs currently tracked in the stream (for all producers), or null if not available */ public Long getIidsTracked() { return iidsTracked; } /** * @return The count of all entries with an idempotent ID added to the stream during its lifetime (not including duplicates), or null if not available */ public Long getIidsAdded() { return iidsAdded; } /** * @return The count of duplicate idempotent IDs (for all producers) detected during the stream's lifetime, or null if not available */ public Long getIidsDuplicates() { return iidsDuplicates; } /** * @return Generic map containing all key-value pairs returned by the server */ public Map getStreamInfo() { return streamInfo; } } ================================================ FILE: src/main/java/redis/clients/jedis/resps/StreamPendingEntry.java ================================================ package redis.clients.jedis.resps; import java.io.IOException; import java.io.Serializable; import redis.clients.jedis.StreamEntryID; public class StreamPendingEntry implements Serializable { private static final long serialVersionUID = 1L; private StreamEntryID id; private String consumerName; private long idleTime; private long deliveredTimes; public StreamPendingEntry(StreamEntryID id, String consumerName, long idleTime, long deliveredTimes) { this.id = id; this.consumerName = consumerName; this.idleTime = idleTime; this.deliveredTimes = deliveredTimes; } public StreamEntryID getID() { return id; } public long getIdleTime() { return idleTime; } public long getDeliveredTimes() { return deliveredTimes; } public String getConsumerName() { return consumerName; } @Override public String toString() { return this.id + " " + this.consumerName + " idle:" + this.idleTime + " times:" + this.deliveredTimes; } private void writeObject(java.io.ObjectOutputStream out) throws IOException { out.writeUnshared(this.id); out.writeUTF(this.consumerName); out.writeLong(idleTime); out.writeLong(this.deliveredTimes); } private void readObject(java.io.ObjectInputStream in) throws IOException, ClassNotFoundException { this.id = (StreamEntryID) in.readUnshared(); this.consumerName = in.readUTF(); this.idleTime = in.readLong(); this.deliveredTimes = in.readLong(); } } ================================================ FILE: src/main/java/redis/clients/jedis/resps/StreamPendingSummary.java ================================================ package redis.clients.jedis.resps; import java.io.Serializable; import java.util.Map; import redis.clients.jedis.StreamEntryID; public class StreamPendingSummary implements Serializable { private static final long serialVersionUID = 1L; private final long total; private final StreamEntryID minId; private final StreamEntryID maxId; private final Map consumerMessageCount; public StreamPendingSummary(long total, StreamEntryID minId, StreamEntryID maxId, Map consumerMessageCount) { this.total = total; this.minId = minId; this.maxId = maxId; this.consumerMessageCount = consumerMessageCount; } public long getTotal() { return total; } public StreamEntryID getMinId() { return minId; } public StreamEntryID getMaxId() { return maxId; } public Map getConsumerMessageCount() { return consumerMessageCount; } } ================================================ FILE: src/main/java/redis/clients/jedis/resps/TrackingInfo.java ================================================ package redis.clients.jedis.resps; import redis.clients.jedis.Builder; import redis.clients.jedis.util.KeyValue; import java.util.Collections; import java.util.List; import static redis.clients.jedis.BuilderFactory.*; public class TrackingInfo { private final List flags; private final long redirect; private final List prefixes; public TrackingInfo(List flags, long redirect, List prefixes) { this.flags = flags; this.redirect = redirect; this.prefixes = prefixes; } public List getFlags() { return flags; } public long getRedirect() { return redirect; } public List getPrefixes() { return prefixes; } public static final Builder TRACKING_INFO_BUILDER = new Builder() { @Override public TrackingInfo build(Object data) { List commandData = (List) data; if (commandData.get(0) instanceof KeyValue) { List flags = Collections.emptyList(); long redirect = -1; List prefixes = Collections.emptyList(); for (KeyValue kv : (List) commandData) { switch (STRING.build(kv.getKey())) { case "flags": flags = STRING_LIST.build(kv.getValue()); break; case "redirect": redirect = LONG.build(kv.getValue()); break; case "prefixes": prefixes = STRING_LIST.build(kv.getValue()); break; } } return new TrackingInfo(flags, redirect, prefixes); } else { List flags = STRING_LIST.build(commandData.get(1)); long redirect = LONG.build(commandData.get(3)); List prefixes = STRING_LIST.build(commandData.get(5)); return new TrackingInfo(flags, redirect, prefixes); } } }; } ================================================ FILE: src/main/java/redis/clients/jedis/resps/Tuple.java ================================================ package redis.clients.jedis.resps; import java.util.Arrays; import java.util.Objects; import redis.clients.jedis.util.ByteArrayComparator; import redis.clients.jedis.util.SafeEncoder; public class Tuple implements Comparable { private byte[] element; private Double score; public Tuple(String element, Double score) { this(SafeEncoder.encode(element), score); } public Tuple(byte[] element, Double score) { super(); this.element = element; this.score = score; } @Override public int hashCode() { final int prime = 31; int result = 1; result = prime * result; if (null != element) { for (final byte b : element) { result = prime * result + b; } } long temp = Double.doubleToLongBits(score); result = prime * result + (int) (temp ^ (temp >>> 32)); return result; } @Override public boolean equals(Object obj) { if (obj == null) return false; if (obj == this) return true; if (!(obj instanceof Tuple)) return false; Tuple other = (Tuple) obj; if (!Arrays.equals(element, other.element)) return false; return Objects.equals(score, other.score); } @Override public int compareTo(Tuple other) { return compare(this, other); } public static int compare(Tuple t1, Tuple t2) { int compScore = Double.compare(t1.score, t2.score); if (compScore != 0) return compScore; return ByteArrayComparator.compare(t1.element, t2.element); } public String getElement() { if (null != element) { return SafeEncoder.encode(element); } else { return null; } } public byte[] getBinaryElement() { return element; } public double getScore() { return score; } @Override // TODO: element=score public String toString() { return '[' + SafeEncoder.encode(element) + ',' + score + ']'; } } ================================================ FILE: src/main/java/redis/clients/jedis/resps/VSimScoreAttribs.java ================================================ package redis.clients.jedis.resps; import redis.clients.jedis.annots.Experimental; /** * Response object containing both similarity score and attributes for VSIM command when used with * WITHSCORES and WITHATTRIBS options. */ @Experimental public class VSimScoreAttribs { private final Double score; private final String attributes; /** * Creates a new VSimScoreAttribs instance. * @param score the similarity score (0.0 to 1.0) * @param attributes the element attributes as JSON string, or null if no attributes */ public VSimScoreAttribs(Double score, String attributes) { this.score = score; this.attributes = attributes; } /** * Gets the similarity score. * @return the similarity score between 0.0 and 1.0 */ public Double getScore() { return score; } /** * Gets the element attributes. * @return the attributes as JSON string, or null if no attributes are set */ public String getAttributes() { return attributes; } @Override public String toString() { return "VSimScoreAttribs{" + "score=" + score + ", attributes='" + attributes + '\'' + '}'; } @Override public boolean equals(Object o) { if (this == o) return true; if (o == null || getClass() != o.getClass()) return false; VSimScoreAttribs that = (VSimScoreAttribs) o; if (score != null ? !score.equals(that.score) : that.score != null) return false; return attributes != null ? attributes.equals(that.attributes) : that.attributes == null; } @Override public int hashCode() { int result = score != null ? score.hashCode() : 0; result = 31 * result + (attributes != null ? attributes.hashCode() : 0); return result; } } ================================================ FILE: src/main/java/redis/clients/jedis/resps/VectorInfo.java ================================================ package redis.clients.jedis.resps; import java.io.Serializable; import java.util.Map; import redis.clients.jedis.annots.Experimental; /** * This class holds information about a vector set returned by the {@code VINFO} command. They can * be accessed via getters. There is also {@link VectorInfo#getVectorInfo()} method that returns a * generic {@link Map} in case where more info are returned from the server. */ @Experimental public class VectorInfo implements Serializable { public static final String VECTOR_DIM = "vector-dim"; public static final String TYPE = "quant-type"; public static final String SIZE = "size"; public static final String MAX_NODE_UID = "hnsw-max-node-uid"; public static final String VSET_UID = "vset-uid"; public static final String MAX_NODES = "hnsw-m"; public static final String PROJECTION_INPUT_DIM = "projection-input-dim"; public static final String ATTRIBUTES_COUNT = "attributes-count"; public static final String MAX_LEVEL = "max-level"; private final Long dimensionality; private final String type; // Will be converted to QuantizationType if needed private final Long size; private final Long maxNodeUid; private final Long vSetUid; private final Long maxNodes; private final Long projectionInputDim; private final Long attributesCount; private final Long maxLevel; private final Map vectorInfo; /** * @param map contains key-value pairs with vector set info */ public VectorInfo(Map map) { vectorInfo = map; dimensionality = (Long) map.get(VECTOR_DIM); type = (String) map.get(TYPE); size = (Long) map.get(SIZE); maxNodeUid = (Long) map.get(MAX_NODE_UID); vSetUid = (Long) map.get(VSET_UID); maxNodes = (Long) map.get(MAX_NODES); projectionInputDim = (Long) map.get(PROJECTION_INPUT_DIM); attributesCount = (Long) map.get(ATTRIBUTES_COUNT); maxLevel = (Long) map.get(MAX_LEVEL); } public Long getDimensionality() { return dimensionality; } public String getType() { return type; } public Long getSize() { return size; } public Long getMaxNodeUid() { return maxNodeUid; } public Long getVSetUid() { return vSetUid; } public Long getMaxNodes() { return maxNodes; } public Long getProjectionInputDim() { return projectionInputDim; } public Long getAttributesCount() { return attributesCount; } public Long getMaxLevel() { return maxLevel; } public Map getVectorInfo() { return vectorInfo; } } ================================================ FILE: src/main/java/redis/clients/jedis/resps/package-info.java ================================================ /** * This package contains custom responses of core Redis commands. */ package redis.clients.jedis.resps; ================================================ FILE: src/main/java/redis/clients/jedis/search/Apply.java ================================================ package redis.clients.jedis.search; import redis.clients.jedis.CommandArguments; import redis.clients.jedis.annots.Experimental; import redis.clients.jedis.params.IParams; import static redis.clients.jedis.search.SearchProtocol.SearchKeyword.APPLY; import static redis.clients.jedis.search.SearchProtocol.SearchKeyword.AS; /** * APPLY operation for search commands. Computes a new field based on an expression. */ @Experimental public class Apply implements IParams { private final String expression; private final String alias; private Apply(String expression, String alias) { this.expression = expression; this.alias = alias; } /** * Create an APPLY operation. * @param expression the expression to apply * @return a new Apply instance */ public static Apply of(String expression) { return new Apply(expression, null); } /** * Create an APPLY operation. * @param expression the expression to apply * @param alias the alias for the result * @return a new Apply instance */ public static Apply of(String expression, String alias) { return new Apply(expression, alias); } @Override public void addParams(CommandArguments args) { args.add(APPLY); args.add(expression); if (alias != null) { args.add(AS); args.add(alias); } } } ================================================ FILE: src/main/java/redis/clients/jedis/search/Combiner.java ================================================ package redis.clients.jedis.search; import redis.clients.jedis.CommandArguments; import redis.clients.jedis.annots.Experimental; import redis.clients.jedis.params.IParams; import java.util.List; import static redis.clients.jedis.search.SearchProtocol.SearchKeyword.YIELD_SCORE_AS; /** * Abstract combiner for combining multiple search scores. Instances are created via * {@link Combiners}. * @see Combiners */ @Experimental public abstract class Combiner implements IParams { private final String name; private String scoreAlias; protected Combiner(String name) { this.name = name; } public final String getName() { return name; } /** * Set an alias for the combined score field using YIELD_SCORE_AS. * @param alias the field name to use for the combined score * @return this instance */ public final Combiner as(String alias) { this.scoreAlias = alias; return this; } protected abstract List getOwnArgs(); @Override public final void addParams(CommandArguments args) { args.add(name); List ownArgs = getOwnArgs(); args.add(ownArgs.size()); ownArgs.forEach(args::add); if (scoreAlias != null) { args.add(YIELD_SCORE_AS); args.add(scoreAlias); } } } ================================================ FILE: src/main/java/redis/clients/jedis/search/Combiners.java ================================================ package redis.clients.jedis.search; import redis.clients.jedis.annots.Experimental; import java.util.ArrayList; import java.util.Collections; import java.util.List; import static redis.clients.jedis.search.SearchProtocol.SearchKeyword.*; /** * Factory class for creating {@link Combiner} instances. *

* Example usage: *

* *
 * // RRF with default parameters
 * Combiners.rrf()
 *
 * // RRF with custom window and constant
 * Combiners.rrf().window(10).constant(60)
 *
 * // Linear combination with weights
 * Combiners.linear().alpha(0.7).beta(0.3)
 *
 * // With score alias
 * Combiners.rrf().as("combined_score")
 * 
* * @see Combiner */ @Experimental public final class Combiners { private Combiners() { } /** * Create an RRF (Reciprocal Rank Fusion) combiner. * @return a new RRF combiner */ public static RRF rrf() { return new RRF(); } /** * Create a Linear combination combiner. * @return a new Linear combiner */ public static Linear linear() { return new Linear(); } /** * RRF (Reciprocal Rank Fusion) combiner. */ public static class RRF extends Combiner { private Integer window; private Double constant; RRF() { super("RRF"); } /** * Set the WINDOW parameter for RRF. * @param window the window size * @return this RRF instance */ public RRF window(int window) { this.window = window; return this; } /** * Set the CONSTANT parameter for RRF. * @param constant the constant value (typically 60) * @return this RRF instance */ public RRF constant(double constant) { this.constant = constant; return this; } @Override protected List getOwnArgs() { if (window == null && constant == null) { return Collections.emptyList(); } List args = new ArrayList<>(); if (window != null) { args.add(WINDOW); args.add(window); } if (constant != null) { args.add(CONSTANT); args.add(constant); } return args; } } /** * Linear combination combiner. */ public static class Linear extends Combiner { private Double alpha; private Double beta; private Integer window; Linear() { super("LINEAR"); } /** * Set the ALPHA parameter (weight for text search score). * @param alpha the alpha value (0.0 to 1.0) * @return this Linear instance */ public Linear alpha(double alpha) { this.alpha = alpha; return this; } /** * Set the BETA parameter (weight for vector similarity score). * @param beta the beta value (0.0 to 1.0) * @return this Linear instance */ public Linear beta(double beta) { this.beta = beta; return this; } /** * Set the WINDOW parameter for LINEAR. * @param window the window size * @return this Linear instance */ public Linear window(int window) { this.window = window; return this; } @Override protected List getOwnArgs() { if (alpha == null && beta == null && window == null) { return Collections.emptyList(); } List args = new ArrayList<>(); if (alpha != null) { args.add(ALPHA); args.add(alpha); } if (beta != null) { args.add(BETA); args.add(beta); } if (window != null) { args.add(WINDOW); args.add(window); } return args; } } } ================================================ FILE: src/main/java/redis/clients/jedis/search/Document.java ================================================ package redis.clients.jedis.search; import redis.clients.jedis.util.SafeEncoder; import java.io.Serializable; import java.util.Collections; import java.util.HashMap; import java.util.List; import java.util.Map; import redis.clients.jedis.Builder; import redis.clients.jedis.BuilderFactory; import redis.clients.jedis.util.KeyValue; /** * Document represents a single indexed document or entity in the engine */ public class Document implements Serializable { private static final long serialVersionUID = 4884173545291367373L; private final String id; private Double score; private final Map fields; public Document(String id) { this(id, 1.0); } public Document(String id, double score) { this(id, new HashMap<>(), score); } public Document(String id, Map fields) { this(id, fields, 1.0f); } public Document(String id, Map fields, double score) { this.id = id; this.fields = fields; this.score = score; } private Document(String id, Double score, Map fields) { this.id = id; this.score = score; this.fields = fields; } public Iterable> getProperties() { return fields.entrySet(); } /** * @return the document's id */ public String getId() { return id; } /** * @return the document's score */ public Double getScore() { return score; } /** * return the property value inside a key * * @param key key of the property * * @return the property value */ public Object get(String key) { return fields.get(key); } /** * return the property value inside a key * * @param key key of the property * * @return the property value */ public String getString(String key) { Object value = fields.get(key); if (value == null) { return null; } else if (value instanceof String) { return (String) value; } else if (value instanceof byte[]) { return SafeEncoder.encode((byte[]) value); } else { return String.valueOf(value); } } public boolean hasProperty(String key) { return fields.containsKey(key); } // TODO: private ?? public Document set(String key, Object value) { fields.put(key, value); return this; } /** * Set the document's score * * @param score new score to set * @return the document itself * @deprecated */ @Deprecated public Document setScore(float score) { this.score = (double) score; return this; } @Override public String toString() { return "id:" + this.getId() + ", score: " + this.getScore() + ", properties:" + this.getProperties(); } /// RESP2 --> public static Document load(String id, double score, byte[] payload, List fields) { return Document.load(id, score, fields, true); } public static Document load(String id, double score, List fields, boolean decode) { return load(id, score, fields, decode, null); } /** * Parse document object from FT.SEARCH reply. * @param id * @param score * @param fields * @param decode * @param isFieldDecode checked only if {@code decode=true} * @return document */ public static Document load(String id, double score, List fields, boolean decode, Map isFieldDecode) { Document ret = new Document(id, score); if (fields != null) { for (int i = 0; i < fields.size(); i += 2) { byte[] rawKey = fields.get(i); byte[] rawValue = fields.get(i + 1); String key = SafeEncoder.encode(rawKey); Object value = rawValue == null ? null : (decode && (isFieldDecode == null || !Boolean.FALSE.equals(isFieldDecode.get(key)))) ? SafeEncoder.encode(rawValue) : rawValue; ret.set(key, value); } } return ret; } /// <-- RESP2 /// RESP3 --> // TODO: final static Builder SEARCH_DOCUMENT = new PerFieldDecoderDocumentBuilder((Map) null); static final class PerFieldDecoderDocumentBuilder extends Builder { private static final String ID_STR = "id"; private static final String SCORE_STR = "score"; private static final String FIELDS_STR = "extra_attributes"; private final Map isFieldDecode; public PerFieldDecoderDocumentBuilder(Map isFieldDecode) { this.isFieldDecode = isFieldDecode != null ? isFieldDecode : Collections.emptyMap(); } @Override public Document build(Object data) { List list = (List) data; String id = null; Double score = null; Map fields = null; for (KeyValue kv : list) { String key = BuilderFactory.STRING.build(kv.getKey()); switch (key) { case ID_STR: id = BuilderFactory.STRING.build(kv.getValue()); break; case SCORE_STR: score = BuilderFactory.DOUBLE.build(kv.getValue()); break; case FIELDS_STR: fields = makeFieldsMap(isFieldDecode, kv.getValue()); break; } } return new Document(id, score, fields); } }; private static Map makeFieldsMap(Map isDecode, Object data) { if (data == null) return null; final List list = (List) data; Map map = new HashMap<>(list.size(), 1f); list.stream().filter((kv) -> (kv != null && kv.getKey() != null && kv.getValue() != null)) .forEach((kv) -> { String key = BuilderFactory.STRING.build(kv.getKey()); map.put(key, (Boolean.FALSE.equals(isDecode.get(key)) ? BuilderFactory.RAW_OBJECT : BuilderFactory.AGGRESSIVE_ENCODED_OBJECT).build(kv.getValue())); }); return map; } /// <-- RESP3 } ================================================ FILE: src/main/java/redis/clients/jedis/search/FTCreateParams.java ================================================ package redis.clients.jedis.search; import static redis.clients.jedis.search.SearchProtocol.SearchKeyword.*; import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; import java.util.Collections; import redis.clients.jedis.CommandArguments; import redis.clients.jedis.params.IParams; public class FTCreateParams implements IParams { private IndexDataType dataType; private Collection prefix; private String filter; private String language; private String languageField; private Double score; private String scoreField; private boolean maxTextFields; private boolean noOffsets; private Long temporary; private boolean noHL; private boolean noFields; private boolean noFreqs; private Collection stopwords; private boolean skipInitialScan; public FTCreateParams() { } public static FTCreateParams createParams() { return new FTCreateParams(); } /** * Currently supports HASH (default) and JSON. To index JSON, you must have the RedisJSON module * installed. */ public FTCreateParams on(IndexDataType dataType) { this.dataType = dataType; return this; } /** * Tells the index which keys it should index. You can add several prefixes to index. */ public FTCreateParams prefix(String... prefixes) { if (this.prefix == null) { this.prefix = new ArrayList<>(prefixes.length); } Arrays.stream(prefixes).forEach(p -> this.prefix.add(p)); return this; } /** * This method can be chained to add multiple prefixes. * * @see FTCreateParams#prefix(java.lang.String...) */ public FTCreateParams addPrefix(String prefix) { if (this.prefix == null) { this.prefix = new ArrayList<>(); } this.prefix.add(prefix); return this; } /** * A filter expression with the full RediSearch aggregation expression language. */ public FTCreateParams filter(String filter) { this.filter = filter; return this; } /** * Indicates the default language for documents in the index. */ public FTCreateParams language(String defaultLanguage) { this.language = defaultLanguage; return this; } /** * Document attribute set as the document language. */ public FTCreateParams languageField(String languageAttribute) { this.languageField = languageAttribute; return this; } /** * Default score for documents in the index. */ public FTCreateParams score(double defaultScore) { this.score = defaultScore; return this; } /** * Document attribute that you use as the document rank based on the user ranking. * Ranking must be between 0.0 and 1.0. */ public FTCreateParams scoreField(String scoreField) { this.scoreField = scoreField; return this; } /** * Forces RediSearch to encode indexes as if there were more than 32 text attributes. */ public FTCreateParams maxTextFields() { this.maxTextFields = true; return this; } /** * Does not store term offsets for documents. It saves memory, but does not allow exact searches * or highlighting. */ public FTCreateParams noOffsets() { this.noOffsets = true; return this; } /** * Creates a lightweight temporary index that expires after a specified period of inactivity. */ public FTCreateParams temporary(long seconds) { this.temporary = seconds; return this; } /** * Conserves storage space and memory by disabling highlighting support. */ public FTCreateParams noHL() { this.noHL = true; return this; } /** * @see FTCreateParams#noHL() */ public FTCreateParams noHighlights() { return noHL(); } /** * Does not store attribute bits for each term. It saves memory, but it does not allow filtering * by specific attributes. */ public FTCreateParams noFields() { this.noFields = true; return this; } /** * Avoids saving the term frequencies in the index. It saves memory, but does not allow sorting * based on the frequencies of a given term within the document. */ public FTCreateParams noFreqs() { this.noFreqs = true; return this; } /** * Sets the index with a custom stopword list, to be ignored during indexing and search time. */ public FTCreateParams stopwords(String... stopwords) { this.stopwords = Arrays.asList(stopwords); return this; } /** * The index does not have stopwords, not even the default ones. */ public FTCreateParams noStopwords() { this.stopwords = Collections.emptyList(); return this; } /** * Does not scan and index. */ public FTCreateParams skipInitialScan() { this.skipInitialScan = true; return this; } @Override public void addParams(CommandArguments args) { if (dataType != null) { args.add(ON).add(dataType); } if (prefix != null) { args.add(PREFIX).add(prefix.size()).addObjects(prefix); } if (filter != null) { args.add(FILTER).add(filter); } if (language != null) { args.add(LANGUAGE).add(language); } if (languageField != null) { args.add(LANGUAGE_FIELD).add(languageField); } if (score != null) { args.add(SCORE).add(score); } if (scoreField != null) { args.add(SCORE_FIELD).add(scoreField); } if (maxTextFields) { args.add(MAXTEXTFIELDS); } if (noOffsets) { args.add(NOOFFSETS); } if (temporary != null) { args.add(TEMPORARY).add(temporary); } if (noHL) { args.add(NOHL); } if (noFields) { args.add(NOFIELDS); } if (noFreqs) { args.add(NOFREQS); } if (stopwords != null) { args.add(STOPWORDS).add(stopwords.size()); stopwords.forEach(w -> args.add(w)); } if (skipInitialScan) { args.add(SKIPINITIALSCAN); } } } ================================================ FILE: src/main/java/redis/clients/jedis/search/FTProfileParams.java ================================================ package redis.clients.jedis.search; import static redis.clients.jedis.search.SearchProtocol.SearchKeyword.LIMITED; import redis.clients.jedis.CommandArguments; import redis.clients.jedis.params.IParams; public class FTProfileParams implements IParams { private boolean limited; public FTProfileParams() { } public static FTProfileParams profileParams() { return new FTProfileParams(); } /** * Removes details of {@code reader} iterator. */ public FTProfileParams limited() { this.limited = true; return this; } @Override public void addParams(CommandArguments args) { if (limited) { args.add(LIMITED); } } } ================================================ FILE: src/main/java/redis/clients/jedis/search/FTSearchParams.java ================================================ package redis.clients.jedis.search; import static redis.clients.jedis.search.SearchProtocol.SearchKeyword.*; import java.util.*; import redis.clients.jedis.CommandArguments; import redis.clients.jedis.Protocol; import redis.clients.jedis.annots.Internal; import redis.clients.jedis.args.GeoUnit; import redis.clients.jedis.args.SortingOrder; import redis.clients.jedis.params.IParams; import redis.clients.jedis.util.LazyRawable; /** * Query represents query parameters and filters to load results from the engine */ public class FTSearchParams implements IParams { private boolean noContent = false; private boolean verbatim = false; private boolean noStopwords = false; private boolean withScores = false; private final List filters = new LinkedList<>(); private Collection inKeys; private Collection inFields; private Collection returnFieldsNames; private boolean summarize; private SummarizeParams summarizeParams; private boolean highlight; private HighlightParams highlightParams; private Integer slop; private Long timeout; private boolean inOrder; private String language; private String expander; private String scorer; // private boolean explainScore; // TODO private String sortBy; private SortingOrder sortOrder; private int[] limit; private Map params; private Integer dialect; /// non command parameters private Map returnFieldDecodeMap = null; public FTSearchParams() { } public static FTSearchParams searchParams() { return new FTSearchParams(); } @Override public void addParams(CommandArguments args) { if (noContent) { args.add(NOCONTENT); } if (verbatim) { args.add(VERBATIM); } if (noStopwords) { args.add(NOSTOPWORDS); } if (withScores) { args.add(WITHSCORES); } if (!filters.isEmpty()) { filters.forEach(filter -> filter.addParams(args)); } if (inKeys != null && !inKeys.isEmpty()) { args.add(INKEYS).add(inKeys.size()).addObjects(inKeys); } if (inFields != null && !inFields.isEmpty()) { args.add(INFIELDS).add(inFields.size()).addObjects(inFields); } if (returnFieldsNames != null && !returnFieldsNames.isEmpty()) { args.add(RETURN); LazyRawable returnCountObject = new LazyRawable(); args.add(returnCountObject); // holding a place for setting the total count later. int returnCount = 0; for (FieldName fn : returnFieldsNames) { returnCount += fn.addCommandArguments(args); } returnCountObject.setRaw(Protocol.toByteArray(returnCount)); } if (summarizeParams != null) { args.addParams(summarizeParams); } else if (summarize) { args.add(SUMMARIZE); } if (highlightParams != null) { args.addParams(highlightParams); } else if (highlight) { args.add(HIGHLIGHT); } if (slop != null) { args.add(SLOP).add(slop); } if (timeout != null) { args.add(TIMEOUT).add(timeout); } if (inOrder) { args.add(INORDER); } if (language != null) { args.add(LANGUAGE).add(language); } if (expander != null) { args.add(EXPANDER).add(expander); } if (scorer != null) { args.add(SCORER).add(scorer); } // // if (explainScore) { // args.add(EXPLAINSCORE); // } if (sortBy != null) { args.add(SORTBY).add(sortBy); if (sortOrder != null) { args.add(sortOrder); } } if (limit != null) { args.add(LIMIT).add(limit[0]).add(limit[1]); } if (params != null && !params.isEmpty()) { args.add(PARAMS).add(params.size() << 1); params.entrySet().forEach(entry -> args.add(entry.getKey()).add(entry.getValue())); } if (dialect != null) { args.add(DIALECT).add(dialect); } } /** * Set the query not to return the contents of documents, and rather just return the ids * * @return the query itself */ public FTSearchParams noContent() { this.noContent = true; return this; } /** * Set the query to verbatim mode, disabling stemming and query expansion * * @return the query object */ public FTSearchParams verbatim() { this.verbatim = true; return this; } /** * Set the query not to filter for stopwords. In general this should not be used * * @return the query object */ public FTSearchParams noStopwords() { this.noStopwords = true; return this; } /** * Set the query to return a factored score for each results. This is useful to merge results from * multiple queries. * * @return the query object itself */ public FTSearchParams withScores() { this.withScores = true; return this; } public FTSearchParams filter(String field, double min, double max) { return filter(new NumericFilter(field, min, max)); } public FTSearchParams filter(String field, double min, boolean exclusiveMin, double max, boolean exclusiveMax) { return filter(new NumericFilter(field, min, exclusiveMin, max, exclusiveMax)); } public FTSearchParams filter(NumericFilter numericFilter) { filters.add(numericFilter); return this; } public FTSearchParams geoFilter(String field, double lon, double lat, double radius, GeoUnit unit) { return geoFilter(new GeoFilter(field, lon, lat, radius, unit)); } public FTSearchParams geoFilter(GeoFilter geoFilter) { filters.add(geoFilter); return this; } /** * Limit the query to results that are limited to a specific set of keys * * @param keys a list of TEXT fields in the schemas * @return the query object itself */ public FTSearchParams inKeys(String... keys) { return inKeys(Arrays.asList(keys)); } public FTSearchParams inKeys(Collection keys) { this.inKeys = keys; return this; } /** * Limit the query to results that are limited to a specific set of fields * * @param fields a list of TEXT fields in the schemas * @return the query object itself */ public FTSearchParams inFields(String... fields) { return inFields(Arrays.asList(fields)); } public FTSearchParams inFields(Collection fields) { if (this.inFields == null) { this.inFields = new ArrayList<>(fields); } else { this.inFields.addAll(fields); } return this; } /** * Result's projection - the fields to return by the query * * @param fields a list of TEXT fields in the schemas * @return the query object itself */ public FTSearchParams returnFields(String... fields) { if (returnFieldsNames == null) { returnFieldsNames = new ArrayList<>(); } Arrays.stream(fields).forEach(f -> returnFieldsNames.add(FieldName.of(f))); return this; } public FTSearchParams returnField(FieldName field) { return returnFields(Collections.singleton(field)); } public FTSearchParams returnFields(FieldName... fields) { return returnFields(Arrays.asList(fields)); } public FTSearchParams returnFields(Collection fields) { if (returnFieldsNames == null) { returnFieldsNames = new ArrayList<>(); } returnFieldsNames.addAll(fields); return this; } public FTSearchParams returnField(String field, boolean decode) { returnFields(field); addReturnFieldDecode(field, decode); return this; } public FTSearchParams returnField(FieldName field, boolean decode) { returnFields(field); addReturnFieldDecode(field.getAttribute() != null ? field.getAttribute() : field.getName(), decode); return this; } private void addReturnFieldDecode(String returnName, boolean decode) { if (returnFieldDecodeMap == null) { returnFieldDecodeMap = new HashMap<>(); } returnFieldDecodeMap.put(returnName, decode); } public FTSearchParams summarize() { this.summarize = true; return this; } public FTSearchParams summarize(SummarizeParams summarizeParams) { this.summarizeParams = summarizeParams; return this; } public FTSearchParams highlight() { this.highlight = true; return this; } public FTSearchParams highlight(HighlightParams highlightParams) { this.highlightParams = highlightParams; return this; } /** * Set the query custom scorer *

* See http://redisearch.io for documentation on extending RediSearch * * @param scorer a custom scorer. * * @return the query object itself */ public FTSearchParams scorer(String scorer) { this.scorer = scorer; return this; } // // public FTSearchParams explainScore() { // this.explainScore = true; // return this; // } public FTSearchParams slop(int slop) { this.slop = slop; return this; } public FTSearchParams timeout(long timeout) { this.timeout = timeout; return this; } public FTSearchParams inOrder() { this.inOrder = true; return this; } /** * Set the query language, for stemming purposes *

* See http://redisearch.io for documentation on languages and stemming * * @param language a language. * * @return the query object itself */ public FTSearchParams language(String language) { this.language = language; return this; } /** * Set the query to be sorted by a Sortable field defined in the schema * * @param sortBy the sorting field's name * @param order the sorting order * @return the query object itself */ public FTSearchParams sortBy(String sortBy, SortingOrder order) { this.sortBy = sortBy; this.sortOrder = order; return this; } /** * Limit the results to a certain offset and limit * * @param offset the first result to show, zero based indexing * @param num how many results we want to show * @return the query itself, for builder-style syntax */ public FTSearchParams limit(int offset, int num) { this.limit = new int[]{offset, num}; return this; } /** * Parameters can be referenced in the query string by a $ , followed by the parameter name, * e.g., $user , and each such reference in the search query to a parameter name is substituted * by the corresponding parameter value. * * @param name * @param value can be String, long or float * @return the query object itself */ public FTSearchParams addParam(String name, Object value) { if (params == null) { params = new HashMap<>(); } params.put(name, value); return this; } public FTSearchParams params(Map paramValues) { if (this.params == null) { this.params = new HashMap<>(paramValues); } else { this.params.putAll(params); } return this; } /** * Set the dialect version to execute the query accordingly * * @param dialect integer * @return the query object itself */ public FTSearchParams dialect(int dialect) { this.dialect = dialect; return this; } /** * This method will not replace the dialect if it has been already set. * @param dialect dialect * @return this */ @Internal public FTSearchParams dialectOptional(int dialect) { if (dialect != 0 && this.dialect == null) { this.dialect = dialect; } return this; } @Internal public boolean getNoContent() { return noContent; } @Internal public boolean getWithScores() { return withScores; } @Internal public Map getReturnFieldDecodeMap() { return returnFieldDecodeMap; } /** * NumericFilter wraps a range filter on a numeric field. It can be inclusive or exclusive */ public static class NumericFilter implements IParams { private final String field; private final double min; private final boolean exclusiveMin; private final double max; private final boolean exclusiveMax; public NumericFilter(String field, double min, double max) { this(field, min, false, max, false); } public NumericFilter(String field, double min, boolean exclusiveMin, double max, boolean exclusiveMax) { this.field = field; this.min = min; this.max = max; this.exclusiveMax = exclusiveMax; this.exclusiveMin = exclusiveMin; } @Override public void addParams(CommandArguments args) { args.add(FILTER).add(field) .add(formatNum(min, exclusiveMin)) .add(formatNum(max, exclusiveMax)); } private Object formatNum(double num, boolean exclude) { return exclude ? ("(" + num) : Protocol.toByteArray(num); } } /** * GeoFilter encapsulates a radius filter on a geographical indexed fields */ public static class GeoFilter implements IParams { private final String field; private final double lon; private final double lat; private final double radius; private final GeoUnit unit; public GeoFilter(String field, double lon, double lat, double radius, GeoUnit unit) { this.field = field; this.lon = lon; this.lat = lat; this.radius = radius; this.unit = unit; } @Override public void addParams(CommandArguments args) { args.add(GEOFILTER).add(field) .add(lon).add(lat) .add(radius).add(unit); } } public static class SummarizeParams implements IParams { private Collection fields; private Integer fragsNum; private Integer fragSize; private String separator; public SummarizeParams() { } public SummarizeParams fields(String... fields) { return fields(Arrays.asList(fields)); } public SummarizeParams fields(Collection fields) { this.fields = fields; return this; } public SummarizeParams fragsNum(int num) { this.fragsNum = num; return this; } public SummarizeParams fragSize(int size) { this.fragSize = size; return this; } public SummarizeParams separator(String separator) { this.separator = separator; return this; } @Override public void addParams(CommandArguments args) { args.add(SUMMARIZE); if (fields != null) { args.add(FIELDS).add(fields.size()).addObjects(fields); } if (fragsNum != null) { args.add(FRAGS).add(fragsNum); } if (fragSize != null) { args.add(LEN).add(fragSize); } if (separator != null) { args.add(SEPARATOR).add(separator); } } } public static SummarizeParams summarizeParams() { return new SummarizeParams(); } public static class HighlightParams implements IParams { private Collection fields; private String[] tags; public HighlightParams() { } public HighlightParams fields(String fields) { return fields(Arrays.asList(fields)); } public HighlightParams fields(Collection fields) { this.fields = fields; return this; } public HighlightParams tags(String open, String close) { this.tags = new String[]{open, close}; return this; } @Override public void addParams(CommandArguments args) { args.add(HIGHLIGHT); if (fields != null) { args.add(FIELDS).add(fields.size()).addObjects(fields); } if (tags != null) { args.add(TAGS).add(tags[0]).add(tags[1]); } } } public static HighlightParams highlightParams() { return new HighlightParams(); } } ================================================ FILE: src/main/java/redis/clients/jedis/search/FTSpellCheckParams.java ================================================ package redis.clients.jedis.search; import static redis.clients.jedis.search.SearchProtocol.SearchKeyword.DIALECT; import static redis.clients.jedis.search.SearchProtocol.SearchKeyword.DISTANCE; import static redis.clients.jedis.search.SearchProtocol.SearchKeyword.EXCLUDE; import static redis.clients.jedis.search.SearchProtocol.SearchKeyword.INCLUDE; import static redis.clients.jedis.search.SearchProtocol.SearchKeyword.TERMS; import java.util.ArrayList; import java.util.Collection; import java.util.Map; import redis.clients.jedis.CommandArguments; import redis.clients.jedis.args.Rawable; import redis.clients.jedis.params.IParams; import redis.clients.jedis.util.KeyValue; public class FTSpellCheckParams implements IParams { private Collection> terms; private Integer distance; private Integer dialect; public FTSpellCheckParams() { } public static FTSpellCheckParams spellCheckParams() { return new FTSpellCheckParams(); } /** * Specifies an inclusion (INCLUDE) of a custom dictionary. */ public FTSpellCheckParams includeTerm(String dictionary) { return addTerm(dictionary, INCLUDE); } /** * Specifies an exclusion (EXCLUDE) of a custom dictionary. */ public FTSpellCheckParams excludeTerm(String dictionary) { return addTerm(dictionary, EXCLUDE); } /** * Specifies an inclusion (INCLUDE) or exclusion (EXCLUDE) of a custom dictionary. */ private FTSpellCheckParams addTerm(String dictionary, Rawable type) { if (this.terms == null) { this.terms = new ArrayList<>(); } this.terms.add(KeyValue.of(dictionary, type)); return this; } /** * Maximum Levenshtein distance for spelling suggestions (default: 1, max: 4). */ public FTSpellCheckParams distance(int distance) { this.distance = distance; return this; } /** * Selects the dialect version under which to execute the query. */ public FTSpellCheckParams dialect(int dialect) { this.dialect = dialect; return this; } /** * This method will not replace the dialect if it has been already set. * @param dialect dialect * @return this */ public FTSpellCheckParams dialectOptional(int dialect) { if (dialect != 0 && this.dialect == null) { this.dialect = dialect; } return this; } @Override public void addParams(CommandArguments args) { if (terms != null) { terms.forEach(kv -> args.add(TERMS).add(kv.getValue()).add(kv.getKey())); } if (distance != null) { args.add(DISTANCE).add(distance); } if (dialect != null) { args.add(DIALECT).add(dialect); } } } ================================================ FILE: src/main/java/redis/clients/jedis/search/FieldName.java ================================================ package redis.clients.jedis.search; import java.util.List; import redis.clients.jedis.CommandArguments; import redis.clients.jedis.params.IParams; import redis.clients.jedis.search.SearchProtocol.SearchKeyword; public class FieldName implements IParams { private final String name; private String attribute; public FieldName(String name) { this.name = name; } public FieldName(String name, String attribute) { this.name = name; this.attribute = attribute; } public FieldName as(String attribute) { if (attribute == null) { throw new IllegalArgumentException("Setting null as field attribute is not allowed."); } if (this.attribute != null) { throw new IllegalStateException("Attribute for this field is already set."); } this.attribute = attribute; return this; } public final String getName() { return name; } public final String getAttribute() { return attribute; } public int addCommandArguments(List args) { args.add(name); if (attribute == null) { return 1; } args.add(SearchKeyword.AS); args.add(attribute); return 3; } public int addCommandArguments(CommandArguments args) { args.add(name); if (attribute == null) { return 1; } args.add(SearchKeyword.AS); args.add(attribute); return 3; } @Override public void addParams(CommandArguments args) { addCommandArguments(args); } @Override public String toString() { return attribute == null ? name : (name + " AS " + attribute); } public static FieldName of(String name) { return new FieldName(name); } public static FieldName[] convert(String... names) { if (names == null) { return null; } FieldName[] fields = new FieldName[names.length]; for (int i = 0; i < names.length; i++) { fields[i] = FieldName.of(names[i]); } return fields; } } ================================================ FILE: src/main/java/redis/clients/jedis/search/Filter.java ================================================ package redis.clients.jedis.search; import redis.clients.jedis.CommandArguments; import redis.clients.jedis.annots.Experimental; import redis.clients.jedis.params.IParams; import redis.clients.jedis.util.JedisAsserts; import static redis.clients.jedis.search.SearchProtocol.SearchKeyword.FILTER; /** * FILTER operation for search commands. Filters results based on an expression. */ @Experimental public class Filter implements IParams { private final String expression; private Filter(String expression) { this.expression = expression; } /** * Create a FILTER operation. * @param expression the filter expression * @return a new Filter instance */ public static Filter of(String expression) { JedisAsserts.notNull(expression, "Filter expression must not be null"); return new Filter(expression); } @Override public void addParams(CommandArguments args) { args.add(FILTER); args.add(expression); } } ================================================ FILE: src/main/java/redis/clients/jedis/search/FtSearchIteration.java ================================================ package redis.clients.jedis.search; import java.util.Collection; import java.util.function.IntFunction; import redis.clients.jedis.CommandArguments; import redis.clients.jedis.RedisProtocol; import redis.clients.jedis.providers.ConnectionProvider; import redis.clients.jedis.search.SearchResult.SearchResultBuilder; import redis.clients.jedis.util.JedisCommandIterationBase; public class FtSearchIteration extends JedisCommandIterationBase { private int batchStart; private final int batchSize; private final IntFunction args; /** * {@link FTSearchParams#limit(int, int)} will be ignored. */ public FtSearchIteration(ConnectionProvider connectionProvider, int batchSize, String indexName, String query, FTSearchParams params) { this(connectionProvider, null, batchSize, indexName, query, params); } /** * {@link Query#limit(java.lang.Integer, java.lang.Integer)} will be ignored. */ public FtSearchIteration(ConnectionProvider connectionProvider, int batchSize, String indexName, Query query) { this(connectionProvider, null, batchSize, indexName, query); } /** * {@link FTSearchParams#limit(int, int)} will be ignored. */ public FtSearchIteration(ConnectionProvider connectionProvider, RedisProtocol protocol, int batchSize, String indexName, String query, FTSearchParams params) { super(connectionProvider, protocol == RedisProtocol.RESP3 ? SearchResult.SEARCH_RESULT_BUILDER : new SearchResultBuilder(!params.getNoContent(), params.getWithScores(), true)); this.batchSize = batchSize; this.args = (limitFirst) -> new CommandArguments(SearchProtocol.SearchCommand.SEARCH) .add(indexName).add(query).addParams(params.limit(limitFirst, this.batchSize)); } /** * {@link Query#limit(java.lang.Integer, java.lang.Integer)} will be ignored. */ public FtSearchIteration(ConnectionProvider connectionProvider, RedisProtocol protocol, int batchSize, String indexName, Query query) { super(connectionProvider, protocol == RedisProtocol.RESP3 ? SearchResult.SEARCH_RESULT_BUILDER : new SearchResultBuilder(!query.getNoContent(), query.getWithScores(), true)); this.batchSize = batchSize; this.args = (limitFirst) -> new CommandArguments(SearchProtocol.SearchCommand.SEARCH) .add(indexName).addParams(query.limit(limitFirst, this.batchSize)); } @Override protected boolean isNodeCompleted(SearchResult reply) { return batchStart >= reply.getTotalResults() - batchSize; } @Override protected CommandArguments initCommandArguments() { batchStart = 0; return args.apply(batchStart); } @Override protected CommandArguments nextCommandArguments(SearchResult lastReply) { batchStart += batchSize; return args.apply(batchStart); } @Override protected Collection convertBatchToData(SearchResult batch) { return batch.getDocuments(); } } ================================================ FILE: src/main/java/redis/clients/jedis/search/IndexDataType.java ================================================ package redis.clients.jedis.search; public enum IndexDataType { HASH, JSON } ================================================ FILE: src/main/java/redis/clients/jedis/search/IndexDefinition.java ================================================ package redis.clients.jedis.search; import redis.clients.jedis.CommandArguments; import redis.clients.jedis.params.IParams; import redis.clients.jedis.search.SearchProtocol.SearchKeyword; /** * IndexDefinition encapsulates configuration for index definition creation and should be given to * the client on index creation */ public class IndexDefinition implements IParams { public enum Type { HASH, JSON } private final Type type; private String[] prefixes; private String filter; private String languageField; private String language; private String scoreFiled; private double score = 1.0; // Default score when score isn't defined public IndexDefinition() { this(null); } public IndexDefinition(Type type) { this.type = type; } public Type getType() { return type; } public String[] getPrefixes() { return prefixes; } public IndexDefinition setPrefixes(String... prefixes) { this.prefixes = prefixes; return this; } public String getFilter() { return filter; } public IndexDefinition setFilter(String filter) { this.filter = filter; return this; } public String getLanguageField() { return languageField; } public IndexDefinition setLanguageField(String languageField) { this.languageField = languageField; return this; } public String getLanguage() { return language; } public IndexDefinition setLanguage(String language) { this.language = language; return this; } public String getScoreFiled() { return scoreFiled; } public IndexDefinition setScoreFiled(String scoreFiled) { this.scoreFiled = scoreFiled; return this; } public double getScore() { return score; } public IndexDefinition setScore(double score) { this.score = score; return this; } @Override public void addParams(CommandArguments args) { if (type != null) { args.add(SearchKeyword.ON.name()); args.add(type.name()); } if (prefixes != null && prefixes.length > 0) { args.add(SearchKeyword.PREFIX.name()); args.add(Integer.toString(prefixes.length)); args.addObjects((Object[]) prefixes); } if (filter != null) { args.add(SearchKeyword.FILTER.name()); args.add(filter); } if (languageField != null) { args.add(SearchKeyword.LANGUAGE_FIELD.name()); args.add(languageField); } if (language != null) { args.add(SearchKeyword.LANGUAGE.name()); args.add(language); } if (scoreFiled != null) { args.add(SearchKeyword.SCORE_FIELD.name()); args.add(scoreFiled); } if (score != 1.0) { args.add(SearchKeyword.SCORE.name()); args.add(Double.toString(score)); } } } ================================================ FILE: src/main/java/redis/clients/jedis/search/IndexOptions.java ================================================ package redis.clients.jedis.search; import java.util.ArrayList; import java.util.Arrays; import java.util.List; import redis.clients.jedis.CommandArguments; import redis.clients.jedis.params.IParams; import redis.clients.jedis.search.SearchProtocol.SearchKeyword; /** * IndexOptions encapsulates flags for index creation and should be given to the client on index * creation * * @since 2.0 */ public class IndexOptions implements IParams { /** * Set this to tell the index not to save term offset vectors. This reduces memory consumption but * does not allow performing exact matches, and reduces overall relevance of multi-term queries */ public static final int USE_TERM_OFFSETS = 0x01; /** * If set (default), we keep flags per index record telling us what fields the term appeared on, * and allowing us to filter results by field */ public static final int KEEP_FIELD_FLAGS = 0x02; /** * With each document:term record, store how often the term appears within the document. This can * be used for sorting documents by their relevance to the given term. */ public static final int KEEP_TERM_FREQUENCIES = 0x08; public static final int DEFAULT_FLAGS = USE_TERM_OFFSETS | KEEP_FIELD_FLAGS | KEEP_TERM_FREQUENCIES; private final int flags; private List stopwords; private long expire = 0L; private IndexDefinition definition; /** * Default constructor * * @param flags flag mask */ public IndexOptions(int flags) { this.flags = flags; } /** * The default indexing options - use term offsets and keep fields flags */ public static IndexOptions defaultOptions() { return new IndexOptions(DEFAULT_FLAGS); } /** * Set a custom stopword list * * @param stopwords the list of stopwords * @return the options object itself, for builder-style construction */ public IndexOptions setStopwords(String... stopwords) { this.stopwords = Arrays.asList(stopwords); return this; } /** * Set the index to contain no stopwords, overriding the default list * * @return the options object itself, for builder-style constructions */ public IndexOptions setNoStopwords() { stopwords = new ArrayList<>(0); return this; } /** * Temporary * * @param expire * @return IndexOptions */ public IndexOptions setTemporary(long expire) { this.expire = expire; return this; } public IndexDefinition getDefinition() { return definition; } public IndexOptions setDefinition(IndexDefinition definition) { this.definition = definition; return this; } @Override public void addParams(CommandArguments args) { if (definition != null) { definition.addParams(args); } if ((flags & USE_TERM_OFFSETS) == 0) { args.add(SearchKeyword.NOOFFSETS.name()); } if ((flags & KEEP_FIELD_FLAGS) == 0) { args.add(SearchKeyword.NOFIELDS.name()); } if ((flags & KEEP_TERM_FREQUENCIES) == 0) { args.add(SearchKeyword.NOFREQS.name()); } if (expire > 0) { args.add(SearchKeyword.TEMPORARY.name()); args.add(Long.toString(this.expire)); } if (stopwords != null) { args.add(SearchKeyword.STOPWORDS.name()); args.add(Integer.toString(stopwords.size())); if (!stopwords.isEmpty()) { args.addObjects(stopwords); } } } } ================================================ FILE: src/main/java/redis/clients/jedis/search/Limit.java ================================================ package redis.clients.jedis.search; import redis.clients.jedis.CommandArguments; import redis.clients.jedis.annots.Experimental; import redis.clients.jedis.params.IParams; import static redis.clients.jedis.search.SearchProtocol.SearchKeyword.LIMIT; /** * LIMIT operation for search commands. Limits the number of results returned. */ @Experimental public class Limit implements IParams { private final int offset; private final int count; private Limit(int offset, int count) { this.offset = offset; this.count = count; } /** * Create a LIMIT operation. * @param offset the offset * @param count the count * @return a new Limit instance */ public static Limit of(int offset, int count) { return new Limit(offset, count); } @Override public void addParams(CommandArguments args) { args.add(LIMIT); args.add(offset); args.add(count); } } ================================================ FILE: src/main/java/redis/clients/jedis/search/ProfilingInfo.java ================================================ package redis.clients.jedis.search; import static redis.clients.jedis.BuilderFactory.AGGRESSIVE_ENCODED_OBJECT; import redis.clients.jedis.Builder; public class ProfilingInfo { private final Object profilingInfo; private ProfilingInfo(Object profilingInfo) { this.profilingInfo = profilingInfo; } public Object getProfilingInfo() { return profilingInfo; } @Override public String toString() { return String.valueOf(profilingInfo); } public static final Builder PROFILING_INFO_BUILDER = new Builder() { @Override public ProfilingInfo build(Object data) { return new ProfilingInfo(AGGRESSIVE_ENCODED_OBJECT.build(data)); } }; } ================================================ FILE: src/main/java/redis/clients/jedis/search/Query.java ================================================ package redis.clients.jedis.search; import java.util.HashMap; import java.util.LinkedList; import java.util.List; import java.util.Map; import redis.clients.jedis.CommandArguments; import redis.clients.jedis.Protocol; import redis.clients.jedis.params.IParams; import redis.clients.jedis.search.SearchProtocol.SearchKeyword; import redis.clients.jedis.util.LazyRawable; import redis.clients.jedis.util.SafeEncoder; /** * Query represents query parameters and filters to load results from the engine */ public class Query implements IParams { /** * Filter represents a filtering rules in a query */ public abstract static class Filter implements IParams { public final String property; public Filter(String property) { this.property = property; } } /** * NumericFilter wraps a range filter on a numeric field. It can be inclusive or exclusive */ public static class NumericFilter extends Filter { private final double min; private final boolean exclusiveMin; private final double max; private final boolean exclusiveMax; public NumericFilter(String property, double min, boolean exclusiveMin, double max, boolean exclusiveMax) { super(property); this.min = min; this.max = max; this.exclusiveMax = exclusiveMax; this.exclusiveMin = exclusiveMin; } public NumericFilter(String property, double min, double max) { this(property, min, false, max, false); } private byte[] formatNum(double num, boolean exclude) { return exclude ? SafeEncoder.encode("(" + num) : Protocol.toByteArray(num); } @Override public void addParams(CommandArguments args) { args.add(SearchKeyword.FILTER.getRaw()); args.add(SafeEncoder.encode(property)); args.add(formatNum(min, exclusiveMin)); args.add(formatNum(max, exclusiveMax)); } } /** * GeoFilter encapsulates a radius filter on a geographical indexed fields */ public static class GeoFilter extends Filter { public static final String KILOMETERS = "km"; public static final String METERS = "m"; public static final String FEET = "ft"; public static final String MILES = "mi"; private final double lon; private final double lat; private final double radius; private final String unit; public GeoFilter(String property, double lon, double lat, double radius, String unit) { super(property); this.lon = lon; this.lat = lat; this.radius = radius; this.unit = unit; } @Override public void addParams(CommandArguments args) { args.add(SearchKeyword.GEOFILTER.getRaw()); args.add(SafeEncoder.encode(property)); args.add(Protocol.toByteArray(lon)); args.add(Protocol.toByteArray(lat)); args.add(Protocol.toByteArray(radius)); args.add(SafeEncoder.encode(unit)); } } public static class Paging { int offset; int num; public Paging(int offset, int num) { this.offset = offset; this.num = num; } } public static class HighlightTags { private final String open; private final String close; public HighlightTags(String open, String close) { this.open = open; this.close = close; } } /** * The query's filter list. We only support AND operation on all those filters */ private final List _filters = new LinkedList<>(); /** * The textual part of the query */ private final String _queryString; /** * The sorting parameters */ private final Paging _paging = new Paging(0, 10); private boolean _verbatim = false; private boolean _noContent = false; private boolean _noStopwords = false; private boolean _withScores = false; private String _language = null; private String[] _fields = null; private String[] _keys = null; private String[] _returnFields = null; private FieldName[] returnFieldNames = null; private String[] highlightFields = null; private String[] summarizeFields = null; private String[] highlightTags = null; private String summarizeSeparator = null; private int summarizeNumFragments = -1; private int summarizeFragmentLen = -1; private String _sortBy = null; private boolean _sortAsc = true; private boolean wantsHighlight = false; private boolean wantsSummarize = false; private String _scorer = null; private Map _params = null; private Integer _dialect; private int _slop = -1; private long _timeout = -1; private boolean _inOrder = false; private String _expander = null; public Query() { this("*"); } /** * Create a new index * * @param queryString the textual part of the query */ public Query(String queryString) { _queryString = queryString; } @Override public void addParams(CommandArguments args) { args.add(SafeEncoder.encode(_queryString)); if (_verbatim) { args.add(SearchKeyword.VERBATIM.getRaw()); } if (_noContent) { args.add(SearchKeyword.NOCONTENT.getRaw()); } if (_noStopwords) { args.add(SearchKeyword.NOSTOPWORDS.getRaw()); } if (_withScores) { args.add(SearchKeyword.WITHSCORES.getRaw()); } if (_language != null) { args.add(SearchKeyword.LANGUAGE.getRaw()); args.add(SafeEncoder.encode(_language)); } if (_scorer != null) { args.add(SearchKeyword.SCORER.getRaw()); args.add(SafeEncoder.encode(_scorer)); } if (_fields != null && _fields.length > 0) { args.add(SearchKeyword.INFIELDS.getRaw()); args.add(Protocol.toByteArray(_fields.length)); for (String f : _fields) { args.add(SafeEncoder.encode(f)); } } if (_sortBy != null) { args.add(SearchKeyword.SORTBY.getRaw()); args.add(SafeEncoder.encode(_sortBy)); args.add((_sortAsc ? SearchKeyword.ASC : SearchKeyword.DESC).getRaw()); } if (_paging.offset != 0 || _paging.num != 10) { args.add(SearchKeyword.LIMIT.getRaw()).add(Protocol.toByteArray(_paging.offset)).add(Protocol.toByteArray(_paging.num)); } if (!_filters.isEmpty()) { _filters.forEach(filter -> filter.addParams(args)); } if (wantsHighlight) { args.add(SearchKeyword.HIGHLIGHT.getRaw()); if (highlightFields != null) { args.add(SearchKeyword.FIELDS.getRaw()); args.add(Protocol.toByteArray(highlightFields.length)); for (String s : highlightFields) { args.add(SafeEncoder.encode(s)); } } if (highlightTags != null) { args.add(SearchKeyword.TAGS.getRaw()); for (String t : highlightTags) { args.add(SafeEncoder.encode(t)); } } } if (wantsSummarize) { args.add(SearchKeyword.SUMMARIZE.getRaw()); if (summarizeFields != null) { args.add(SearchKeyword.FIELDS.getRaw()); args.add(Protocol.toByteArray(summarizeFields.length)); for (String s : summarizeFields) { args.add(SafeEncoder.encode(s)); } } if (summarizeNumFragments != -1) { args.add(SearchKeyword.FRAGS.getRaw()); args.add(Protocol.toByteArray(summarizeNumFragments)); } if (summarizeFragmentLen != -1) { args.add(SearchKeyword.LEN.getRaw()); args.add(Protocol.toByteArray(summarizeFragmentLen)); } if (summarizeSeparator != null) { args.add(SearchKeyword.SEPARATOR.getRaw()); args.add(SafeEncoder.encode(summarizeSeparator)); } } if (_keys != null && _keys.length > 0) { args.add(SearchKeyword.INKEYS.getRaw()); args.add(Protocol.toByteArray(_keys.length)); for (String f : _keys) { args.add(SafeEncoder.encode(f)); } } if (_returnFields != null && _returnFields.length > 0) { args.add(SearchKeyword.RETURN.getRaw()); args.add(Protocol.toByteArray(_returnFields.length)); for (String f : _returnFields) { args.add(SafeEncoder.encode(f)); } } else if (returnFieldNames != null && returnFieldNames.length > 0) { args.add(SearchKeyword.RETURN.getRaw()); // final int returnCountIndex = args.size(); LazyRawable returnCountObject = new LazyRawable(); // args.add(null); // holding a place for setting the total count later. args.add(returnCountObject); // holding a place for setting the total count later. int returnCount = 0; for (FieldName fn : returnFieldNames) { returnCount += fn.addCommandArguments(args); } // args.set(returnCountIndex, Protocol.toByteArray(returnCount)); returnCountObject.setRaw(Protocol.toByteArray(returnCount)); } if (_params != null && _params.size() > 0) { args.add(SearchKeyword.PARAMS.getRaw()); args.add(_params.size() << 1); for (Map.Entry entry : _params.entrySet()) { args.add(entry.getKey()); args.add(entry.getValue()); } } if (_dialect != null) { args.add(SearchKeyword.DIALECT.getRaw()); args.add(_dialect); } if (_slop >= 0) { args.add(SearchKeyword.SLOP.getRaw()); args.add(_slop); } if (_timeout >= 0) { args.add(SearchKeyword.TIMEOUT.getRaw()); args.add(_timeout); } if (_inOrder) { args.add(SearchKeyword.INORDER.getRaw()); } if (_expander != null) { args.add(SearchKeyword.EXPANDER.getRaw()); args.add(SafeEncoder.encode(_expander)); } } /** * Limit the results to a certain offset and limit * * @param offset the first result to show, zero based indexing * @param limit how many results we want to show * @return the query itself, for builder-style syntax */ public Query limit(Integer offset, Integer limit) { _paging.offset = offset; _paging.num = limit; return this; } /** * Add a filter to the query's filter list * * @param f either a numeric or geo filter object * @return the query itself */ public Query addFilter(Filter f) { _filters.add(f); return this; } /** * Set the query to verbatim mode, disabling stemming and query expansion * * @return the query object */ public Query setVerbatim() { this._verbatim = true; return this; } public boolean getNoContent() { return _noContent; } /** * Set the query not to return the contents of documents, and rather just return the ids * * @return the query itself */ public Query setNoContent() { this._noContent = true; return this; } /** * Set the query not to filter for stopwords. In general this should not be used * * @return the query object */ public Query setNoStopwords() { this._noStopwords = true; return this; } public boolean getWithScores() { return _withScores; } /** * Set the query to return a factored score for each results. This is useful to merge results from * multiple queries. * * @return the query object itself */ public Query setWithScores() { this._withScores = true; return this; } /** * Set the query language, for stemming purposes *

* See http://redisearch.io for documentation on languages and stemming * * @param language a language. * * @return the query object itself */ public Query setLanguage(String language) { this._language = language; return this; } /** * Set the query custom scorer *

* See http://redisearch.io for documentation on extending RediSearch * * @param scorer a custom scorer. * * @return the query object itself */ public Query setScorer(String scorer) { this._scorer = scorer; return this; } /** * Limit the query to results that are limited to a specific set of fields * * @param fields a list of TEXT fields in the schemas * @return the query object itself */ public Query limitFields(String... fields) { this._fields = fields; return this; } /** * Limit the query to results that are limited to a specific set of keys * * @param keys a list of TEXT fields in the schemas * @return the query object itself */ public Query limitKeys(String... keys) { this._keys = keys; return this; } /** * Result's projection - the fields to return by the query * * @param fields a list of TEXT fields in the schemas * @return the query object itself */ public Query returnFields(String... fields) { this._returnFields = fields; this.returnFieldNames = null; return this; } /** * Result's projection - the fields to return by the query * * @param fields a list of TEXT fields in the schemas * @return the query object itself */ public Query returnFields(FieldName... fields) { this.returnFieldNames = fields; this._returnFields = null; return this; } public Query highlightFields(HighlightTags tags, String... fields) { if (fields == null || fields.length > 0) { highlightFields = fields; } if (tags != null) { highlightTags = new String[]{tags.open, tags.close}; } else { highlightTags = null; } wantsHighlight = true; return this; } public Query highlightFields(String... fields) { return highlightFields(null, fields); } public Query summarizeFields(int contextLen, int fragmentCount, String separator, String... fields) { if (fields == null || fields.length > 0) { summarizeFields = fields; } summarizeFragmentLen = contextLen; summarizeNumFragments = fragmentCount; summarizeSeparator = separator; wantsSummarize = true; return this; } public Query summarizeFields(String... fields) { return summarizeFields(-1, -1, null, fields); } /** * Set the query to be sorted by a Sortable field defined in the schema * * @param field the sorting field's name * @param ascending if set to true, the sorting order is ascending, else descending * @return the query object itself */ public Query setSortBy(String field, boolean ascending) { _sortBy = field; _sortAsc = ascending; return this; } /** * Parameters can be referenced in the query string by a $ , followed by the parameter name, * e.g., $user , and each such reference in the search query to a parameter name is substituted * by the corresponding parameter value. * * @param name * @param value can be String, long or float * @return the query object itself */ public Query addParam(String name, Object value) { if (_params == null) { _params = new HashMap<>(); } _params.put(name, value); return this; } /** * Set the dialect version to execute the query accordingly * * @param dialect integer * @return the query object itself */ public Query dialect(int dialect) { _dialect = dialect; return this; } /** * This method will not replace the dialect if it has been already set. * @param dialect dialect * @return this */ public Query dialectOptional(int dialect) { if (dialect != 0 && this._dialect == null) { this._dialect = dialect; } return this; } /** * Set the slop to execute the query accordingly * * @param slop integer * @return the query object itself */ public Query slop(int slop) { _slop = slop; return this; } /** * Set the timeout to execute the query accordingly * * @param timeout long * @return the query object itself */ public Query timeout(long timeout) { _timeout = timeout; return this; } /** * Set the query terms appear in the same order in the document as in the query, regardless of the offsets between them * * @return the query object */ public Query setInOrder() { this._inOrder = true; return this; } /** * Set the query to use a custom query expander instead of the stemmer * * @param field the expander field's name * @return the query object itself */ public Query setExpander(String field) { _expander = field; return this; } } ================================================ FILE: src/main/java/redis/clients/jedis/search/RediSearchCommands.java ================================================ package redis.clients.jedis.search; import java.util.Arrays; import java.util.List; import java.util.Map; import java.util.Set; import redis.clients.jedis.annots.Experimental; import redis.clients.jedis.commands.ConfigCommands; import redis.clients.jedis.resps.Tuple; import redis.clients.jedis.search.aggr.AggregationBuilder; import redis.clients.jedis.search.aggr.AggregationResult; import redis.clients.jedis.search.hybrid.FTHybridParams; import redis.clients.jedis.search.hybrid.HybridResult; import redis.clients.jedis.search.schemafields.SchemaField; public interface RediSearchCommands { String ftCreate(String indexName, IndexOptions indexOptions, Schema schema); default String ftCreate(String indexName, SchemaField... schemaFields) { return ftCreate(indexName, Arrays.asList(schemaFields)); } default String ftCreate(String indexName, FTCreateParams createParams, SchemaField... schemaFields) { return ftCreate(indexName, createParams, Arrays.asList(schemaFields)); } default String ftCreate(String indexName, Iterable schemaFields) { return ftCreate(indexName, FTCreateParams.createParams(), schemaFields); } String ftCreate(String indexName, FTCreateParams createParams, Iterable schemaFields); default String ftAlter(String indexName, Schema.Field... fields) { return ftAlter(indexName, Schema.from(fields)); } String ftAlter(String indexName, Schema schema); default String ftAlter(String indexName, SchemaField... schemaFields) { return ftAlter(indexName, Arrays.asList(schemaFields)); } String ftAlter(String indexName, Iterable schemaFields); String ftAliasAdd(String aliasName, String indexName); String ftAliasUpdate(String aliasName, String indexName); String ftAliasDel(String aliasName); String ftDropIndex(String indexName); String ftDropIndexDD(String indexName); default SearchResult ftSearch(String indexName) { return ftSearch(indexName, "*"); } SearchResult ftSearch(String indexName, String query); SearchResult ftSearch(String indexName, String query, FTSearchParams params); SearchResult ftSearch(String indexName, Query query); @Deprecated SearchResult ftSearch(byte[] indexName, Query query); String ftExplain(String indexName, Query query); List ftExplainCLI(String indexName, Query query); AggregationResult ftAggregate(String indexName, AggregationBuilder aggr); AggregationResult ftCursorRead(String indexName, long cursorId, int count); String ftCursorDel(String indexName, long cursorId); Map.Entry ftProfileAggregate(String indexName, FTProfileParams profileParams, AggregationBuilder aggr); Map.Entry ftProfileSearch(String indexName, FTProfileParams profileParams, Query query); Map.Entry ftProfileSearch(String indexName, FTProfileParams profileParams, String query, FTSearchParams searchParams); String ftSynUpdate(String indexName, String synonymGroupId, String... terms); Map> ftSynDump(String indexName); long ftDictAdd(String dictionary, String... terms); long ftDictDel(String dictionary, String... terms); Set ftDictDump(String dictionary); long ftDictAddBySampleKey(String indexName, String dictionary, String... terms); long ftDictDelBySampleKey(String indexName, String dictionary, String... terms); Set ftDictDumpBySampleKey(String indexName, String dictionary); Map> ftSpellCheck(String index, String query); Map> ftSpellCheck(String index, String query, FTSpellCheckParams spellCheckParams); Map ftInfo(String indexName); Set ftTagVals(String indexName, String fieldName); /** * @deprecated {@link ConfigCommands#configGet(java.lang.String)} is used since Redis 8. */ @Deprecated Map ftConfigGet(String option); @Deprecated Map ftConfigGet(String indexName, String option); /** * @deprecated {@link ConfigCommands#configSet(java.lang.String, java.lang.String)} is used since Redis 8. */ @Deprecated String ftConfigSet(String option, String value); @Deprecated String ftConfigSet(String indexName, String option, String value); long ftSugAdd(String key, String string, double score); long ftSugAddIncr(String key, String string, double score); List ftSugGet(String key, String prefix); List ftSugGet(String key, String prefix, boolean fuzzy, int max); List ftSugGetWithScores(String key, String prefix); List ftSugGetWithScores(String key, String prefix, boolean fuzzy, int max); boolean ftSugDel(String key, String string); long ftSugLen(String key); Set ftList(); /** * Execute a hybrid query combining text search and vector similarity. * * @param indexName the index name * @param hybridParams the hybrid query arguments * @return the hybrid search results * @see FTHybridParams * @see HybridResult */ @Experimental HybridResult ftHybrid(String indexName, FTHybridParams hybridParams); } ================================================ FILE: src/main/java/redis/clients/jedis/search/RediSearchPipelineCommands.java ================================================ package redis.clients.jedis.search; import java.util.Arrays; import java.util.List; import java.util.Map; import java.util.Set; import redis.clients.jedis.Response; import redis.clients.jedis.annots.Experimental; import redis.clients.jedis.resps.Tuple; import redis.clients.jedis.search.aggr.AggregationBuilder; import redis.clients.jedis.search.aggr.AggregationResult; import redis.clients.jedis.search.hybrid.FTHybridParams; import redis.clients.jedis.search.hybrid.HybridResult; import redis.clients.jedis.search.schemafields.SchemaField; public interface RediSearchPipelineCommands { Response ftCreate(String indexName, IndexOptions indexOptions, Schema schema); default Response ftCreate(String indexName, SchemaField... schemaFields) { return ftCreate(indexName, Arrays.asList(schemaFields)); } default Response ftCreate(String indexName, FTCreateParams createParams, SchemaField... schemaFields) { return ftCreate(indexName, createParams, Arrays.asList(schemaFields)); } default Response ftCreate(String indexName, Iterable schemaFields) { return ftCreate(indexName, FTCreateParams.createParams(), schemaFields); } Response ftCreate(String indexName, FTCreateParams createParams, Iterable schemaFields); default Response ftAlter(String indexName, Schema.Field... fields) { return ftAlter(indexName, Schema.from(fields)); } Response ftAlter(String indexName, Schema schema); default Response ftAlter(String indexName, SchemaField... schemaFields) { return ftAlter(indexName, Arrays.asList(schemaFields)); } Response ftAlter(String indexName, Iterable schemaFields); Response ftAliasAdd(String aliasName, String indexName); Response ftAliasUpdate(String aliasName, String indexName); Response ftAliasDel(String aliasName); Response ftDropIndex(String indexName); Response ftDropIndexDD(String indexName); default Response ftSearch(String indexName) { return ftSearch(indexName, "*"); } Response ftSearch(String indexName, String query); Response ftSearch(String indexName, String query, FTSearchParams searchParams); Response ftSearch(String indexName, Query query); @Deprecated Response ftSearch(byte[] indexName, Query query); Response ftExplain(String indexName, Query query); Response> ftExplainCLI(String indexName, Query query); Response ftAggregate(String indexName, AggregationBuilder aggr); Response ftSynUpdate(String indexName, String synonymGroupId, String... terms); Response>> ftSynDump(String indexName); Response ftDictAdd(String dictionary, String... terms); Response ftDictDel(String dictionary, String... terms); Response> ftDictDump(String dictionary); Response ftDictAddBySampleKey(String indexName, String dictionary, String... terms); Response ftDictDelBySampleKey(String indexName, String dictionary, String... terms); Response> ftDictDumpBySampleKey(String indexName, String dictionary); Response>> ftSpellCheck(String index, String query); Response>> ftSpellCheck(String index, String query, FTSpellCheckParams spellCheckParams); Response> ftInfo(String indexName); Response> ftTagVals(String indexName, String fieldName); @Deprecated Response> ftConfigGet(String option); @Deprecated Response> ftConfigGet(String indexName, String option); @Deprecated Response ftConfigSet(String option, String value); @Deprecated Response ftConfigSet(String indexName, String option, String value); Response ftSugAdd(String key, String string, double score); Response ftSugAddIncr(String key, String string, double score); Response> ftSugGet(String key, String prefix); Response> ftSugGet(String key, String prefix, boolean fuzzy, int max); Response> ftSugGetWithScores(String key, String prefix); Response> ftSugGetWithScores(String key, String prefix, boolean fuzzy, int max); Response ftSugDel(String key, String string); Response ftSugLen(String key); /** * Execute a hybrid search combining text and vector search. * * @param indexName the index name * @param hybridParams the hybrid search arguments * @return the hybrid search results */ @Experimental Response ftHybrid(String indexName, FTHybridParams hybridParams); } ================================================ FILE: src/main/java/redis/clients/jedis/search/RediSearchUtil.java ================================================ package redis.clients.jedis.search; import java.nio.ByteBuffer; import java.nio.ByteOrder; import java.util.Arrays; import java.util.HashMap; import java.util.HashSet; import java.util.Map; import java.util.Set; import redis.clients.jedis.util.SafeEncoder; public class RediSearchUtil { /** * Jedis' {@code hset} methods do not support {@link Object}s as values. This method eases process * of converting a {@link Map} with Objects as values so that the returning Map can be set to a * {@code hset} method. * @param input map with object value * @return map with string value */ public static Map toStringMap(Map input) { return toStringMap(input, false); } /** * Jedis' {@code hset} methods do not support {@link Object}s as values. This method eases process * of converting a {@link Map} with Objects as values so that the returning Map can be set to a * {@code hset} method. * @param input map with object value * @param stringEscape whether to escape the String objects * @return map with string value */ public static Map toStringMap(Map input, boolean stringEscape) { Map output = new HashMap<>(input.size()); for (Map.Entry entry : input.entrySet()) { String key = entry.getKey(); Object obj = entry.getValue(); if (key == null || obj == null) { throw new NullPointerException("A null argument cannot be sent to Redis."); } String str; if (obj instanceof byte[]) { str = SafeEncoder.encode((byte[]) obj); } else if (obj instanceof redis.clients.jedis.GeoCoordinate) { redis.clients.jedis.GeoCoordinate geo = (redis.clients.jedis.GeoCoordinate) obj; str = geo.getLongitude() + "," + geo.getLatitude(); } else if (obj instanceof String) { str = stringEscape ? escape((String) obj) : (String) obj; } else { str = String.valueOf(obj); } output.put(key, str); } return output; } /** * x86 systems are little-endian and Java defaults to big-endian. This causes mismatching query * results when RediSearch is running in a x86 system. This method helps to convert concerned * arrays. * @param input float array * @return byte array */ public static byte[] toByteArray(float[] input) { byte[] bytes = new byte[Float.BYTES * input.length]; ByteBuffer.wrap(bytes).order(ByteOrder.LITTLE_ENDIAN).asFloatBuffer().put(input); return bytes; } /** * @deprecated Use {@link RediSearchUtil#toByteArray(float[])}. */ @Deprecated public static byte[] ToByteArray(float[] input) { return toByteArray(input); } private static final Set ESCAPE_CHARS = new HashSet<>(Arrays.asList(// ',', '.', '<', '>', '{', '}', '[', // ']', '"', '\'', ':', ';', '!', '@', // '#', '$', '%', '^', '&', '*', '(', // ')', '-', '+', '=', '~', '|' // )); public static String escape(String text) { return escape(text, false); } public static String escapeQuery(String query) { return escape(query, true); } public static String escape(String text, boolean querying) { char[] chars = text.toCharArray(); StringBuilder sb = new StringBuilder(); for (char ch : chars) { if (ESCAPE_CHARS.contains(ch) || (querying && ch == ' ')) { sb.append("\\"); } sb.append(ch); } return sb.toString(); } public static String unescape(String text) { return text.replace("\\", ""); } private RediSearchUtil() { throw new InstantiationError("Must not instantiate this class"); } } ================================================ FILE: src/main/java/redis/clients/jedis/search/Schema.java ================================================ package redis.clients.jedis.search; import java.util.ArrayList; import java.util.List; import java.util.Map; import redis.clients.jedis.CommandArguments; import redis.clients.jedis.args.Rawable; import redis.clients.jedis.params.IParams; import redis.clients.jedis.util.SafeEncoder; /** * Schema abstracts the schema definition when creating an index. Documents can contain fields not * mentioned in the schema, but the index will only index pre-defined fields */ public class Schema { public enum FieldType { TAG, TEXT, GEO, NUMERIC, VECTOR } // public for CommandObjects public final List fields; public Schema() { this.fields = new ArrayList<>(); } public static Schema from(Field... fields) { Schema schema = new Schema(); for (Field field : fields) { schema.addField(field); } return schema; } /** * Add a text field to the schema with a given weight * * @param name the field's name * @param weight its weight, a positive floating point number * @return the schema object */ public Schema addTextField(String name, double weight) { fields.add(new TextField(name, weight)); return this; } /** * Add a text field that can be sorted on * * @param name the field's name * @param weight its weight, a positive floating point number * @return the schema object */ public Schema addSortableTextField(String name, double weight) { fields.add(new TextField(name, weight, true)); return this; } /** * Add a geo filtering field to the schema. * * @param name the field's name * @return the schema object */ public Schema addGeoField(String name) { fields.add(new Field(name, FieldType.GEO, false)); return this; } /** * Add a numeric field to the schema * * @param name the fields's nam e * @return the schema object */ public Schema addNumericField(String name) { fields.add(new Field(name, FieldType.NUMERIC, false)); return this; } /* Add a numeric field that can be sorted on */ public Schema addSortableNumericField(String name) { fields.add(new Field(name, FieldType.NUMERIC, true)); return this; } public Schema addTagField(String name) { fields.add(new TagField(name)); return this; } public Schema addTagField(String name, String separator) { fields.add(new TagField(name, separator)); return this; } public Schema addTagField(String name, boolean caseSensitive) { fields.add(new TagField(name, caseSensitive, false)); return this; } public Schema addTagField(String name, String separator, boolean caseSensitive) { fields.add(new TagField(name, separator, caseSensitive, false)); return this; } public Schema addSortableTagField(String name, String separator) { fields.add(new TagField(name, separator, true)); return this; } public Schema addSortableTagField(String name, boolean caseSensitive) { fields.add(new TagField(name, caseSensitive, true)); return this; } public Schema addSortableTagField(String name, String separator, boolean caseSensitive) { fields.add(new TagField(name, separator, caseSensitive, true)); return this; } public Schema addVectorField(String name, VectorField.VectorAlgo algorithm, Map attributes) { fields.add(new VectorField(name, algorithm, attributes)); return this; } public Schema addFlatVectorField(String name, Map attributes) { fields.add(new VectorField(name, VectorField.VectorAlgo.FLAT, attributes)); return this; } public Schema addHNSWVectorField(String name, Map attributes) { fields.add(new VectorField(name, VectorField.VectorAlgo.HNSW, attributes)); return this; } /** * Add a Vamana vector field to the schema using the SVS-VAMANA algorithm. * This method provides a convenient way to add SVS-VAMANA vector fields. * * @param name the field's name * @param attributes the SVS-Vamana algorithm configuration attributes * @return the schema object */ public Schema addSvsVamanaVectorField(String name, Map attributes) { // Use the existing VectorField with SVS_VAMANA algorithm Map vamanaAttributes = new java.util.HashMap<>(attributes); fields.add(new VectorField(name, VectorField.VectorAlgo.SVS_VAMANA, vamanaAttributes)); return this; } public Schema addField(Field field) { fields.add(field); return this; } /*** * Chain as name to the last filed added to the schema * @param attribute */ // TODO: Not sure about this pattern. May consider removing later. public Schema as(String attribute) { fields.get(fields.size() - 1).as(attribute); return this; } @Override public String toString() { return "Schema{fields=" + fields + "}"; } public static class Field implements IParams { protected final FieldName fieldName; protected final FieldType type; protected final boolean sortable; protected final boolean noIndex; public Field(String name, FieldType type) { this(name, type, false, false); } public Field(String name, FieldType type, boolean sortable) { this(name, type, sortable, false); } public Field(String name, FieldType type, boolean sortable, boolean noindex) { this(FieldName.of(name), type, sortable, noindex); } public Field(FieldName name, FieldType type) { this(name, type, false, false); } public Field(FieldName name, FieldType type, boolean sortable, boolean noIndex) { this.fieldName = name; this.type = type; this.sortable = sortable; this.noIndex = noIndex; } public void as(String attribute){ this.fieldName.as(attribute); } @Override public final void addParams(CommandArguments args) { this.fieldName.addParams(args); args.add(type.name()); addTypeArgs(args); if (sortable) { args.add("SORTABLE"); } if (noIndex) { args.add("NOINDEX"); } } /** * Subclasses should override this method. * * @param args */ protected void addTypeArgs(CommandArguments args) { } @Override public String toString() { return "Field{name='" + fieldName + "', type=" + type + ", sortable=" + sortable + ", noindex=" + noIndex + "}"; } } /** * FullText field spec. */ public static class TextField extends Field { private final double weight; private final boolean nostem; private final String phonetic; public TextField(String name) { this(name, 1.0); } public TextField(FieldName name) { this(name, 1.0, false, false, false, null); } public TextField(String name, double weight) { this(name, weight, false); } public TextField(String name, double weight, boolean sortable) { this(name, weight, sortable, false); } public TextField(String name, double weight, boolean sortable, boolean nostem) { this(name, weight, sortable, nostem, false); } public TextField(String name, double weight, boolean sortable, boolean nostem, boolean noindex) { this(name, weight, sortable, nostem, noindex, null); } public TextField(String name, double weight, boolean sortable, boolean nostem, boolean noindex, String phonetic) { super(name, FieldType.TEXT, sortable, noindex); this.weight = weight; this.nostem = nostem; this.phonetic = phonetic; } public TextField(FieldName name, double weight, boolean sortable, boolean nostem, boolean noindex, String phonetic) { super(name, FieldType.TEXT, sortable, noindex); this.weight = weight; this.nostem = nostem; this.phonetic = phonetic; } @Override protected void addTypeArgs(CommandArguments args) { if (weight != 1.0) { args.add("WEIGHT"); args.add(Double.toString(weight)); } if (nostem) { args.add("NOSTEM"); } if (phonetic != null) { args.add("PHONETIC"); args.add(this.phonetic); } } @Override public String toString() { return "TextField{name='" + fieldName + "', type=" + type + ", sortable=" + sortable + ", noindex=" + noIndex + ", weight=" + weight + ", nostem=" + nostem + ", phonetic='" + phonetic + "'}"; } } public static class TagField extends Field { private final String separator; private final boolean caseSensitive; public TagField(String name) { this(name, null); } public TagField(String name, String separator) { this(name, separator, false); } public TagField(String name, boolean sortable) { this(name, null, sortable); } public TagField(String name, String separator, boolean sortable) { this(name, separator, false, sortable); } public TagField(String name, boolean caseSensitive, boolean sortable) { this(name, null, caseSensitive, sortable); } public TagField(String name, String separator, boolean caseSensitive, boolean sortable) { super(name, FieldType.TAG, sortable); this.separator = separator; this.caseSensitive = caseSensitive; } public TagField(FieldName name, String separator, boolean sortable) { this(name, separator, false, sortable); } public TagField(FieldName name, String separator, boolean caseSensitive, boolean sortable) { super(name, FieldType.TAG, sortable, false); this.separator = separator; this.caseSensitive = caseSensitive; } @Override public void addTypeArgs(CommandArguments args) { if (separator != null) { args.add("SEPARATOR"); args.add(separator); } if (caseSensitive) { args.add("CASESENSITIVE"); } } @Override public String toString() { return "TagField{name='" + fieldName + "', type=" + type + ", sortable=" + sortable + ", noindex=" + noIndex + ", separator='" + separator + "', caseSensitive='" + caseSensitive + "'}"; } } public static class VectorField extends Field { /** * Enumeration of supported vector indexing algorithms in Redis. * Each algorithm has different performance characteristics and use cases. */ public enum VectorAlgo implements Rawable { /** * FLAT algorithm provides exact vector search with perfect accuracy. * Best suited for smaller datasets (< 1M vectors) where search accuracy * is more important than search latency. */ FLAT("FLAT"), /** * HNSW (Hierarchical Navigable Small World) algorithm provides approximate * vector search with configurable accuracy-performance trade-offs. * Best suited for larger datasets (> 1M vectors) where search performance * and scalability are more important than perfect accuracy. */ HNSW("HNSW"), /** * SVS_VAMANA algorithm provides high-performance approximate vector search * optimized for specific use cases with advanced compression and optimization features. * *

Characteristics: *

    *
  • High-performance approximate search
  • *
  • Support for vector compression (LVQ, LeanVec)
  • *
  • Configurable graph construction and search parameters
  • *
  • Optimized for Intel platforms with fallback support
  • *
* *

Note: This algorithm may have specific requirements and limitations. * Consult the Redis documentation for detailed usage guidelines. */ SVS_VAMANA("SVS-VAMANA"); private final byte[] raw; /** * Creates a VectorAlgorithm enum value. * * @param redisParamName the Redis parameter name for this algorithm */ VectorAlgo(String redisParamName) { raw = SafeEncoder.encode(redisParamName); } /** * Returns the raw byte representation of the algorithm name for Redis commands. * * @return the raw bytes of the algorithm name */ @Override public byte[] getRaw() { return raw; } } private final VectorAlgo algorithm; private final Map attributes; public VectorField(String name, VectorAlgo algorithm, Map attributes) { super(name, FieldType.VECTOR); this.algorithm = algorithm; this.attributes = attributes; } @Override public void addTypeArgs(CommandArguments args) { args.add(algorithm); args.add(attributes.size() << 1); for (Map.Entry entry : attributes.entrySet()) { args.add(entry.getKey()); args.add(entry.getValue()); } } @Override public String toString() { return "VectorField{name='" + fieldName + "', type=" + type + ", algorithm=" + algorithm + ", attributes=" + attributes + "}"; } } } ================================================ FILE: src/main/java/redis/clients/jedis/search/Scorer.java ================================================ package redis.clients.jedis.search; import redis.clients.jedis.CommandArguments; import redis.clients.jedis.annots.Experimental; import redis.clients.jedis.params.IParams; import java.util.List; /** * Abstract scorer for text search. Instances are created via {@link Scorers}. * @see Scorers */ @Experimental public abstract class Scorer implements IParams { private final String name; protected Scorer(String name) { this.name = name; } public final String getName() { return name; } @Override public final void addParams(CommandArguments args) { args.add(name); } @Override public String toString() { return name; } } ================================================ FILE: src/main/java/redis/clients/jedis/search/Scorers.java ================================================ package redis.clients.jedis.search; import redis.clients.jedis.annots.Experimental; import java.util.Collections; import java.util.List; /** * Factory class for creating {@link Scorer} instances for text search. * @see Scorer */ @Experimental public class Scorers { // Predefined Scorer instances private static final Scorer TFIDF = scorer("TFIDF"); private static final Scorer TFIDF_DOCNORM = scorer("TFIDF.DOCNORM"); private static final Scorer BM25STD = scorer("BM25STD"); private static final Scorer BM25STD_NORM = scorer("BM25STD.NORM"); private static final Scorer DISMAX = scorer("DISMAX"); private static final Scorer DOCSCORE = scorer("DOCSCORE"); private static final Scorer HAMMING = scorer("HAMMING"); private Scorers() { } private static Scorer scorer(String name) { return new Scorer(name) { }; } public static Scorer tfidf() { return TFIDF; } public static Scorer tfidfDocnorm() { return TFIDF_DOCNORM; } public static Scorer bm25std() { return BM25STD; } public static Scorer bm25stdNorm() { return BM25STD_NORM; } public static Scorer dismax() { return DISMAX; } public static Scorer docscore() { return DOCSCORE; } public static Scorer hamming() { return HAMMING; } } ================================================ FILE: src/main/java/redis/clients/jedis/search/SearchBuilderFactory.java ================================================ package redis.clients.jedis.search; import static redis.clients.jedis.BuilderFactory.STRING; import java.util.Collections; import java.util.HashMap; import java.util.LinkedHashMap; import java.util.List; import java.util.Map; import java.util.stream.Collectors; import redis.clients.jedis.Builder; import redis.clients.jedis.BuilderFactory; import redis.clients.jedis.util.KeyValue; public final class SearchBuilderFactory { public static final Builder>> SEARCH_SYNONYM_GROUPS = new Builder>>() { @Override public Map> build(Object data) { List list = (List) data; if (list.isEmpty()) return Collections.emptyMap(); if (list.get(0) instanceof KeyValue) { return ((List) data).stream().collect(Collectors.toMap( kv -> STRING.build(kv.getKey()), kv -> BuilderFactory.STRING_LIST.build(kv.getValue()))); } Map> dump = new HashMap<>(list.size() / 2, 1f); for (int i = 0; i < list.size(); i += 2) { dump.put(STRING.build(list.get(i)), BuilderFactory.STRING_LIST.build(list.get(i + 1))); } return dump; } }; public static final Builder>> SEARCH_SPELLCHECK_RESPONSE = new Builder>>() { private static final String TERM = "TERM"; private static final String RESULTS = "results"; @Override public Map> build(Object data) { List rawDataList = (List) data; if (rawDataList.isEmpty()) return Collections.emptyMap(); if (rawDataList.get(0) instanceof KeyValue) { KeyValue rawData = (KeyValue) rawDataList.get(0); String header = STRING.build(rawData.getKey()); if (!RESULTS.equals(header)) { throw new IllegalStateException("Unrecognized header: " + header); } return ((List) rawData.getValue()).stream().collect(Collectors.toMap( rawTerm -> STRING.build(rawTerm.getKey()), rawTerm -> ((List>) rawTerm.getValue()).stream() .collect(Collectors.toMap(entry -> STRING.build(entry.get(0).getKey()), entry -> BuilderFactory.DOUBLE.build(entry.get(0).getValue()))), (x, y) -> x, LinkedHashMap::new)); } Map> returnTerms = new LinkedHashMap<>(rawDataList.size()); for (Object rawData : rawDataList) { List rawElements = (List) rawData; String header = STRING.build(rawElements.get(0)); if (!TERM.equals(header)) { throw new IllegalStateException("Unrecognized header: " + header); } String term = STRING.build(rawElements.get(1)); List> list = (List>) rawElements.get(2); Map entries = new LinkedHashMap<>(list.size()); list.forEach(entry -> entries.put(STRING.build(entry.get(1)), BuilderFactory.DOUBLE.build(entry.get(0)))); returnTerms.put(term, entries); } return returnTerms; } }; private SearchBuilderFactory() { throw new InstantiationError("Must not instantiate this class"); } } ================================================ FILE: src/main/java/redis/clients/jedis/search/SearchProtocol.java ================================================ package redis.clients.jedis.search; import redis.clients.jedis.args.Rawable; import redis.clients.jedis.commands.ProtocolCommand; import redis.clients.jedis.util.SafeEncoder; public class SearchProtocol { public static final int DEFAULT_DIALECT = 2; public enum SearchCommand implements ProtocolCommand { CREATE("FT.CREATE"), ALTER("FT.ALTER"), INFO("FT.INFO"), SEARCH("FT.SEARCH"), EXPLAIN("FT.EXPLAIN"), EXPLAINCLI("FT.EXPLAINCLI"), AGGREGATE("FT.AGGREGATE"), CURSOR("FT.CURSOR"), @Deprecated CONFIG("FT.CONFIG"), ALIASADD("FT.ALIASADD"), ALIASUPDATE("FT.ALIASUPDATE"), ALIASDEL("FT.ALIASDEL"), SYNUPDATE("FT.SYNUPDATE"), SYNDUMP("FT.SYNDUMP"), SUGADD("FT.SUGADD"), SUGGET("FT.SUGGET"), SUGDEL("FT.SUGDEL"), SUGLEN("FT.SUGLEN"), DROPINDEX("FT.DROPINDEX"), DICTADD("FT.DICTADD"), DICTDEL("FT.DICTDEL"), DICTDUMP("FT.DICTDUMP"), SPELLCHECK("FT.SPELLCHECK"), TAGVALS("FT.TAGVALS"), PROFILE("FT.PROFILE"), _LIST("FT._LIST"), HYBRID("FT.HYBRID"); private final byte[] raw; private SearchCommand(String alt) { raw = SafeEncoder.encode(alt); } @Override public byte[] getRaw() { return raw; } } public enum SearchKeyword implements Rawable { SCHEMA, TEXT, TAG, NUMERIC, GEO, GEOSHAPE, VECTOR, VERBATIM, NOCONTENT, NOSTOPWORDS, WITHSCORES, LANGUAGE, INFIELDS, SORTBY, ASC, DESC, LIMIT, HIGHLIGHT, FIELDS, TAGS, SUMMARIZE, FRAGS, LEN, SEPARATOR, INKEYS, RETURN, FILTER, GEOFILTER, ADD, INCR, MAX, FUZZY, READ, DEL, DD, TEMPORARY, STOPWORDS, NOFREQS, NOFIELDS, NOOFFSETS, NOHL, ON, SORTABLE, UNF, PREFIX, LANGUAGE_FIELD, SCORE, SCORE_FIELD, SCORER, PARAMS, AS, DIALECT, SLOP, TIMEOUT, INORDER, EXPANDER, MAXTEXTFIELDS, SKIPINITIALSCAN, WITHSUFFIXTRIE, NOSTEM, NOINDEX, PHONETIC, WEIGHT, CASESENSITIVE, LOAD, APPLY, GROUPBY, MAXIDLE, WITHCURSOR, DISTANCE, TERMS, INCLUDE, EXCLUDE, SEARCH, AGGREGATE, QUERY, LIMITED, COUNT, REDUCE, INDEXMISSING, INDEXEMPTY, ADDSCORES, // FT.HYBRID keywords VSIM, COMBINE, RRF, LINEAR, WINDOW, CONSTANT, ALPHA, BETA, KNN, RANGE, RADIUS, EPSILON, YIELD_SCORE_AS, K, EF_RUNTIME, NOSORT, @Deprecated SET, @Deprecated GET; private final byte[] raw; private SearchKeyword() { raw = SafeEncoder.encode(name()); } @Override public byte[] getRaw() { return raw; } } } ================================================ FILE: src/main/java/redis/clients/jedis/search/SearchResult.java ================================================ package redis.clients.jedis.search; import java.util.ArrayList; import java.util.Collections; import java.util.List; import java.util.Map; import java.util.Objects; import java.util.stream.Collectors; import redis.clients.jedis.Builder; import redis.clients.jedis.BuilderFactory; import redis.clients.jedis.annots.Internal; import redis.clients.jedis.util.KeyValue; /** * SearchResult encapsulates the returned result from a search query. It contains publicly * accessible fields for the total number of results, and an array of {@link Document} objects * containing the actual returned documents. */ public class SearchResult { private final long totalResults; private final List documents; private final List warnings; private SearchResult(long totalResults, List documents) { this(totalResults, documents, (List) null); } private SearchResult(long totalResults, List documents, List warnings) { this.totalResults = totalResults; this.documents = documents; this.warnings = warnings; } public long getTotalResults() { return totalResults; } public List getDocuments() { return Collections.unmodifiableList(documents); } public List getWarnings() { return warnings; } @Override public String toString() { return getClass().getSimpleName() + "{Total results:" + totalResults + ", Documents:" + documents + (warnings != null ? ", Warnings:" + warnings : "") + "}"; } public static class SearchResultBuilder extends Builder { private final boolean hasContent; private final boolean hasScores; private final boolean decode; private final Map isFieldDecode; public SearchResultBuilder(boolean hasContent, boolean hasScores, boolean decode) { this(hasContent, hasScores, decode, null); } public SearchResultBuilder(boolean hasContent, boolean hasScores, boolean decode, Map isFieldDecode) { this.hasContent = hasContent; this.hasScores = hasScores; this.decode = decode; this.isFieldDecode = isFieldDecode; } @Override public SearchResult build(Object data) { List resp = (List) data; int step = 1; int scoreOffset = 0; int contentOffset = 1; if (hasScores) { step += 1; scoreOffset = 1; contentOffset += 1; } if (hasContent) { step += 1; } // the first element is always the number of results long totalResults = (Long) resp.get(0); List documents = new ArrayList<>(resp.size() - 1); for (int i = 1; i < resp.size(); i += step) { String id = BuilderFactory.STRING.build(resp.get(i)); double score = hasScores ? BuilderFactory.DOUBLE.build(resp.get(i + scoreOffset)) : 1.0; List fields = hasContent ? (List) resp.get(i + contentOffset) : null; documents.add(Document.load(id, score, fields, decode, isFieldDecode)); } return new SearchResult(totalResults, documents); } } /// RESP3 --> // TODO: final public static Builder SEARCH_RESULT_BUILDER = new PerFieldDecoderSearchResultBuilder(Document.SEARCH_DOCUMENT); @Internal public static final class PerFieldDecoderSearchResultBuilder extends Builder { private static final String TOTAL_RESULTS_STR = "total_results"; private static final String RESULTS_STR = "results"; private static final String WARNINGS_STR = "warning"; private final Builder documentBuilder; public PerFieldDecoderSearchResultBuilder(Map isFieldDecode) { this(new Document.PerFieldDecoderDocumentBuilder(isFieldDecode)); } private PerFieldDecoderSearchResultBuilder(Builder builder) { this.documentBuilder = Objects.requireNonNull(builder); } @Override public SearchResult build(Object data) { List list = (List) data; long totalResults = -1; List results = null; List warnings = null; for (KeyValue kv : list) { String key = BuilderFactory.STRING.build(kv.getKey()); Object rawVal = kv.getValue(); switch (key) { case TOTAL_RESULTS_STR: totalResults = BuilderFactory.LONG.build(rawVal); break; case RESULTS_STR: results = ((List) rawVal).stream() .map(documentBuilder::build) .collect(Collectors.toList()); break; case WARNINGS_STR: warnings = BuilderFactory.STRING_LIST.build(rawVal); break; } } return new SearchResult(totalResults, results, warnings); } }; /// <-- RESP3 } ================================================ FILE: src/main/java/redis/clients/jedis/search/aggr/AggregationBuilder.java ================================================ package redis.clients.jedis.search.aggr; import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; import java.util.Collections; import java.util.List; import java.util.Map; import redis.clients.jedis.CommandArguments; import redis.clients.jedis.Protocol; import redis.clients.jedis.params.IParams; import redis.clients.jedis.search.FieldName; import redis.clients.jedis.search.SearchProtocol.SearchKeyword; import redis.clients.jedis.util.LazyRawable; /** * @author Guy Korland */ public class AggregationBuilder implements IParams { private final List aggrArgs = new ArrayList<>(); private Integer dialect; private boolean isWithCursor = false; public AggregationBuilder(String query) { aggrArgs.add(query); } public AggregationBuilder() { this("*"); } public AggregationBuilder load(String... fields) { return load(FieldName.convert(fields)); } public AggregationBuilder load(FieldName... fields) { aggrArgs.add(SearchKeyword.LOAD); LazyRawable rawLoadCount = new LazyRawable(); aggrArgs.add(rawLoadCount); int loadCount = 0; for (FieldName fn : fields) { loadCount += fn.addCommandArguments(aggrArgs); } rawLoadCount.setRaw(Protocol.toByteArray(loadCount)); return this; } public AggregationBuilder loadAll() { aggrArgs.add(SearchKeyword.LOAD); aggrArgs.add(Protocol.BYTES_ASTERISK); return this; } public AggregationBuilder limit(int offset, int count) { aggrArgs.add(SearchKeyword.LIMIT); aggrArgs.add(offset); aggrArgs.add(count); return this; } public AggregationBuilder limit(int count) { return limit(0, count); } public AggregationBuilder sortBy(SortedField... fields) { aggrArgs.add(SearchKeyword.SORTBY); aggrArgs.add(fields.length << 1); for (SortedField field : fields) { aggrArgs.add(field.getField()); aggrArgs.add(field.getOrder()); } return this; } public AggregationBuilder sortByAsc(String field) { return sortBy(SortedField.asc(field)); } public AggregationBuilder sortByDesc(String field) { return sortBy(SortedField.desc(field)); } /** * {@link AggregationBuilder#sortBy(redis.clients.jedis.search.aggr.SortedField...)} * (or {@link AggregationBuilder#sortByAsc(java.lang.String)} * or {@link AggregationBuilder#sortByDesc(java.lang.String)}) * MUST BE called JUST BEFORE this. * @param max limit * @return this */ public AggregationBuilder sortByMax(int max) { aggrArgs.add(SearchKeyword.MAX); aggrArgs.add(max); return this; } /** * Shortcut to {@link AggregationBuilder#sortBy(redis.clients.jedis.search.aggr.SortedField...)} * and {@link AggregationBuilder#sortByMax(int)}. * @param max limit * @param fields sorted fields * @return this */ public AggregationBuilder sortBy(int max, SortedField... fields) { sortBy(fields); sortByMax(max); return this; } public AggregationBuilder apply(String projection, String alias) { aggrArgs.add(SearchKeyword.APPLY); aggrArgs.add(projection); aggrArgs.add(SearchKeyword.AS); aggrArgs.add(alias); return this; } public AggregationBuilder groupBy(Group group) { aggrArgs.add(SearchKeyword.GROUPBY); group.addArgs(aggrArgs); return this; } public AggregationBuilder groupBy(Collection fields, Collection reducers) { String[] fieldsArr = new String[fields.size()]; Group g = new Group(fields.toArray(fieldsArr)); reducers.forEach((r) -> g.reduce(r)); groupBy(g); return this; } public AggregationBuilder groupBy(String field, Reducer... reducers) { return groupBy(Collections.singletonList(field), Arrays.asList(reducers)); } public AggregationBuilder filter(String expression) { aggrArgs.add(SearchKeyword.FILTER); aggrArgs.add(expression); return this; } public AggregationBuilder cursor(int count) { isWithCursor = true; aggrArgs.add(SearchKeyword.WITHCURSOR); aggrArgs.add(SearchKeyword.COUNT); aggrArgs.add(count); return this; } public AggregationBuilder cursor(int count, long maxIdle) { isWithCursor = true; aggrArgs.add(SearchKeyword.WITHCURSOR); aggrArgs.add(SearchKeyword.COUNT); aggrArgs.add(count); aggrArgs.add(SearchKeyword.MAXIDLE); aggrArgs.add(maxIdle); return this; } public AggregationBuilder verbatim() { aggrArgs.add(SearchKeyword.VERBATIM); return this; } public AggregationBuilder timeout(long timeout) { aggrArgs.add(SearchKeyword.TIMEOUT); aggrArgs.add(timeout); return this; } public AggregationBuilder addScores() { aggrArgs.add(SearchKeyword.ADDSCORES); return this; } public AggregationBuilder params(Map params) { aggrArgs.add(SearchKeyword.PARAMS); aggrArgs.add(params.size() << 1); params.forEach((k, v) -> { aggrArgs.add(k); aggrArgs.add(v); }); return this; } public AggregationBuilder dialect(int dialect) { this.dialect = dialect; return this; } /** * This method will not replace the dialect if it has been already set. * @param dialect dialect * @return this */ public AggregationBuilder dialectOptional(int dialect) { if (dialect != 0 && this.dialect == null) { this.dialect = dialect; } return this; } public boolean isWithCursor() { return isWithCursor; } @Override public void addParams(CommandArguments commArgs) { commArgs.addObjects(aggrArgs); if (dialect != null) { commArgs.add(SearchKeyword.DIALECT).add(dialect); } } } ================================================ FILE: src/main/java/redis/clients/jedis/search/aggr/AggregationResult.java ================================================ package redis.clients.jedis.search.aggr; import java.util.ArrayList; import java.util.Collections; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.stream.Collectors; import redis.clients.jedis.Builder; import redis.clients.jedis.BuilderFactory; import redis.clients.jedis.exceptions.JedisDataException; import redis.clients.jedis.util.KeyValue; import redis.clients.jedis.util.SafeEncoder; public class AggregationResult { private final long totalResults; private final List> results; private final List warnings; private Long cursorId = -1L; private AggregationResult(long totalResults, List> results) { this(totalResults, results, (List) null); } private AggregationResult(long totalResults, List> results, List warnings) { this.totalResults = totalResults; this.results = results; this.warnings = warnings; } private void setCursorId(Long cursorId) { this.cursorId = cursorId; } public Long getCursorId() { return cursorId; } public long getTotalResults() { return totalResults; } public List> getResults() { return Collections.unmodifiableList(results); } /** * @return results as {@link Row}s. * @see #getResults() */ public List getRows() { return results.stream().map(Row::new).collect(Collectors.toList()); } public Row getRow(int index) { return new Row(results.get(index)); } public List getWarnings() { return warnings; } public static final Builder SEARCH_AGGREGATION_RESULT = new Builder() { private static final String TOTAL_RESULTS_STR = "total_results"; private static final String RESULTS_STR = "results"; // private static final String FIELDS_STR = "fields"; private static final String FIELDS_STR = "extra_attributes"; private static final String WARNINGS_STR = "warning"; @Override public AggregationResult build(Object data) { // return new AggregationResult(data); List list = (List) data; if (list.get(0) instanceof KeyValue) { List kvList = (List) data; long totalResults = -1; List> results = null; List warnings = null; for (KeyValue kv : kvList) { String key = BuilderFactory.STRING.build(kv.getKey()); Object rawVal = kv.getValue(); switch (key) { case TOTAL_RESULTS_STR: totalResults = BuilderFactory.LONG.build(rawVal); break; case RESULTS_STR: List> resList = (List>) rawVal; results = new ArrayList<>(resList.size()); for (List rikv : resList) { for (KeyValue ikv : rikv) { if (FIELDS_STR.equals(BuilderFactory.STRING.build(ikv.getKey()))) { results.add(BuilderFactory.ENCODED_OBJECT_MAP.build(ikv.getValue())); break; } } } break; case WARNINGS_STR: warnings = BuilderFactory.STRING_LIST.build(rawVal); break; } } return new AggregationResult(totalResults, results, warnings); } list = (List) SafeEncoder.encodeObject(data); // the first element is always the number of results long totalResults = (Long) list.get(0); List> results = new ArrayList<>(list.size() - 1); for (int i = 1; i < list.size(); i++) { List mapList = (List) list.get(i); Map map = new HashMap<>(mapList.size() / 2, 1f); for (int j = 0; j < mapList.size(); j += 2) { Object r = mapList.get(j); if (r instanceof JedisDataException) { throw (JedisDataException) r; } map.put((String) r, mapList.get(j + 1)); } results.add(map); } return new AggregationResult(totalResults, results); } }; public static final Builder SEARCH_AGGREGATION_RESULT_WITH_CURSOR = new Builder() { @Override public AggregationResult build(Object data) { List list = (List) data; // return new AggregationResult(list.get(0), (long) list.get(1)); AggregationResult r = SEARCH_AGGREGATION_RESULT.build(list.get(0)); r.setCursorId((Long) list.get(1)); return r; } }; } ================================================ FILE: src/main/java/redis/clients/jedis/search/aggr/FtAggregateIteration.java ================================================ package redis.clients.jedis.search.aggr; import java.util.Collection; import redis.clients.jedis.CommandArguments; import redis.clients.jedis.providers.ConnectionProvider; import redis.clients.jedis.search.SearchProtocol; import redis.clients.jedis.util.JedisCommandIterationBase; public class FtAggregateIteration extends JedisCommandIterationBase { private final String indexName; private final CommandArguments args; /** * {@link AggregationBuilder#cursor(int, long) CURSOR} must be set. * @param connectionProvider connection provider * @param indexName index name * @param aggr cursor must be set */ public FtAggregateIteration(ConnectionProvider connectionProvider, String indexName, AggregationBuilder aggr) { super(connectionProvider, AggregationResult.SEARCH_AGGREGATION_RESULT_WITH_CURSOR); if (!aggr.isWithCursor()) throw new IllegalArgumentException("cursor must be set"); this.indexName = indexName; this.args = new CommandArguments(SearchProtocol.SearchCommand.AGGREGATE).add(this.indexName).addParams(aggr); } @Override protected boolean isNodeCompleted(AggregationResult reply) { return reply.getCursorId() == 0L; } @Override protected CommandArguments initCommandArguments() { return args; } @Override protected CommandArguments nextCommandArguments(AggregationResult lastReply) { return new CommandArguments(SearchProtocol.SearchCommand.CURSOR).add(SearchProtocol.SearchKeyword.READ) .add(indexName).add(lastReply.getCursorId()); } @Override protected Collection convertBatchToData(AggregationResult batch) { return batch.getRows(); } } ================================================ FILE: src/main/java/redis/clients/jedis/search/aggr/Group.java ================================================ package redis.clients.jedis.search.aggr; import java.util.ArrayList; import java.util.Arrays; import java.util.List; /** * Created by mnunberg on 2/22/18. */ public class Group { private final List fields = new ArrayList<>(); private final List reducers = new ArrayList<>(); public Group(String... fields) { this.fields.addAll(Arrays.asList(fields)); } public Group reduce(Reducer r) { reducers.add(r); return this; } public void addArgs(List args) { args.add(fields.size()); args.addAll(fields); reducers.forEach((r) -> r.addArgs(args)); } } ================================================ FILE: src/main/java/redis/clients/jedis/search/aggr/Reducer.java ================================================ package redis.clients.jedis.search.aggr; import java.util.List; import redis.clients.jedis.search.SearchProtocol.SearchKeyword; /** * Created by mnunberg on 2/22/18. * * This class is normally received via one of the subclasses or via Reducers */ public abstract class Reducer { private final String name; private final String field; private String alias; protected Reducer(String name) { this.name = name; this.field = null; } protected Reducer(String name, String field) { this.name = name; this.field = field; } public final Reducer as(String alias) { this.alias = alias; return this; } public final String getName() { return name; } public final String getField() { return field; } public final String getAlias() { return alias; } protected abstract List getOwnArgs(); public final void addArgs(List args) { args.add(SearchKeyword.REDUCE); args.add(name); List ownArgs = getOwnArgs(); if (field != null) { args.add(1 + ownArgs.size()); args.add(field); } else { args.add(ownArgs.size()); } args.addAll(ownArgs); if (alias != null) { args.add(SearchKeyword.AS); args.add(alias); } } } ================================================ FILE: src/main/java/redis/clients/jedis/search/aggr/Reducers.java ================================================ package redis.clients.jedis.search.aggr; import java.util.Arrays; import java.util.Collections; import java.util.List; /** * Created by mnunberg on 2/22/18. */ public class Reducers { public static Reducer count() { return new Reducer("COUNT") { @Override protected List getOwnArgs() { return Collections.emptyList(); } }; } private static Reducer singleFieldReducer(String name, String field) { return new Reducer(name, field) { @Override protected List getOwnArgs() { return Collections.emptyList(); } }; } public static Reducer count_distinct(String field) { return singleFieldReducer("COUNT_DISTINCT", field); } public static Reducer count_distinctish(String field) { return singleFieldReducer("COUNT_DISTINCTISH", field); } public static Reducer sum(String field) { return singleFieldReducer("SUM", field); } public static Reducer min(String field) { return singleFieldReducer("MIN", field); } public static Reducer max(String field) { return singleFieldReducer("MAX", field); } public static Reducer avg(String field) { return singleFieldReducer("AVG", field); } public static Reducer stddev(String field) { return singleFieldReducer("STDDEV", field); } public static Reducer quantile(String field, double percentile) { return new Reducer("QUANTILE", field) { @Override protected List getOwnArgs() { return Arrays.asList(percentile); } }; } public static Reducer first_value(String field) { return singleFieldReducer("FIRST_VALUE", field); } /** * REDUCE FIRST_VALUE {nargs} {property} [BY {property} [ASC|DESC]] * * @param field * @param sortBy * @return Reducer */ public static Reducer first_value(String field, SortedField sortBy) { return new Reducer("FIRST_VALUE", field) { @Override protected List getOwnArgs() { return Arrays.asList("BY", sortBy.getField(), sortBy.getOrder()); } }; } public static Reducer to_list(String field) { return singleFieldReducer("TOLIST", field); } public static Reducer random_sample(String field, int size) { return new Reducer("RANDOM_SAMPLE", field) { @Override protected List getOwnArgs() { return Arrays.asList(size); } }; } } ================================================ FILE: src/main/java/redis/clients/jedis/search/aggr/Row.java ================================================ package redis.clients.jedis.search.aggr; import java.util.Map; import redis.clients.jedis.util.DoublePrecision; public class Row { private final Map fields; public Row(Map fields) { this.fields = fields; } public boolean containsKey(String key) { return fields.containsKey(key); } public Object get(String key) { return fields.get(key); } public String getString(String key) { if (!containsKey(key)) { return ""; } return (String) fields.get(key); } public long getLong(String key) { if (!containsKey(key)) { return 0; } return Long.parseLong((String) fields.get(key)); } public double getDouble(String key) { if (!containsKey(key)) { return 0; } return DoublePrecision.parseFloatingPointNumber((String) fields.get(key)); } @Override public String toString() { return String.valueOf(fields); } } ================================================ FILE: src/main/java/redis/clients/jedis/search/aggr/SortedField.java ================================================ package redis.clients.jedis.search.aggr; /** * Created by mnunberg on 2/22/18. */ public class SortedField { public enum SortOrder { ASC, DESC } private final String fieldName; private final SortOrder sortOrder; public SortedField(String fieldName, SortOrder order) { this.fieldName = fieldName; this.sortOrder = order; } public final String getOrder() { return sortOrder.toString(); } public final String getField() { return fieldName; } public static SortedField asc(String field) { return new SortedField(field, SortOrder.ASC); } public static SortedField desc(String field) { return new SortedField(field, SortOrder.DESC); } } ================================================ FILE: src/main/java/redis/clients/jedis/search/aggr/package-info.java ================================================ /** * This package contains the classes related to Aggregation commands in RediSearch module. */ package redis.clients.jedis.search.aggr; ================================================ FILE: src/main/java/redis/clients/jedis/search/hybrid/FTHybridParams.java ================================================ package redis.clients.jedis.search.hybrid; import redis.clients.jedis.CommandArguments; import redis.clients.jedis.annots.Experimental; import redis.clients.jedis.params.IParams; import redis.clients.jedis.search.Combiner; import redis.clients.jedis.search.Combiners; import redis.clients.jedis.util.JedisAsserts; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; import static redis.clients.jedis.search.SearchProtocol.SearchKeyword.*; /** * Argument list builder for the Redis {@code FT.HYBRID} command. Combines text search and vector * similarity search with configurable combination strategies and post-processing operations. *

* Basic Usage: *

* *
 * FTHybridParams params = FTHybridParams.builder()
 *     .search(FTHybridSearchParams.builder().query("comfortable shoes").build())
 *     .vectorSearch(FTHybridVectorParams.builder().field("@embedding").vector("vec")
 *         .method(FTHybridVectorParams.Knn.of(10)).build())
 *     .combine(Combiners.rrf()).param("vec", vectorBlob).build();
 * 
* * @see FTHybridSearchParams * @see FTHybridVectorParams * @see Combiner * @see Combiners * @see FTHybridPostProcessingParams */ @Experimental public class FTHybridParams implements IParams { private final List searchArgs = new ArrayList<>(); private final List vectorArgs = new ArrayList<>(); private Combiner combiner; private FTHybridPostProcessingParams postProcessingArgs; private final Map params = new HashMap<>(); private Long timeout; private FTHybridParams() { } /** * @return a new {@link Builder} for {@link FTHybridParams}. */ public static Builder builder() { return new Builder(); } /** * Builder for {@link FTHybridParams}. */ public static class Builder { private final FTHybridParams instance = new FTHybridParams(); /** * Build the {@link FTHybridParams} instance. * @return the configured arguments */ public FTHybridParams build() { // Validate that both SEARCH and VSIM are configured (per FT.HYBRID requirements) JedisAsserts.isTrue(!instance.searchArgs.isEmpty(), "At least one SEARCH clause must be configured"); JedisAsserts.isTrue(!instance.vectorArgs.isEmpty(), "At least one VSIM clause must be configured"); return instance; } /** * Configure the SEARCH clause using {@link FTHybridSearchParams}. * @param searchArgs the search arguments * @return this builder */ public Builder search(FTHybridSearchParams searchArgs) { JedisAsserts.notNull(searchArgs, "Search args must not be null"); instance.searchArgs.add(searchArgs); return this; } /** * Configure the VSIM clause using {@link FTHybridVectorParams}. * @param vectorArgs the vector search arguments * @return this builder */ public Builder vectorSearch(FTHybridVectorParams vectorArgs) { JedisAsserts.notNull(vectorArgs, "Vector args must not be null"); instance.vectorArgs.add(vectorArgs); return this; } /** * Configure the COMBINE clause using a {@link Combiner}. * @param combiner the combiner (e.g., {@code Combiners.rrf()} or {@code Combiners.linear()}) * @return this builder * @see Combiners */ public Builder combine(Combiner combiner) { JedisAsserts.notNull(combiner, "Combiner must not be null"); instance.combiner = combiner; return this; } /** * Set the post-processing arguments. * @param postProcessingArgs the post-processing configuration * @return this builder */ public Builder postProcessing(FTHybridPostProcessingParams postProcessingArgs) { JedisAsserts.notNull(postProcessingArgs, "PostProcessingParams must not be null"); instance.postProcessingArgs = postProcessingArgs; return this; } /** * Add a parameter for parameterized queries. *

* Parameters can be referenced in queries using {@code $name} syntax. *

* @param name the parameter name * @param value the parameter value * @return this builder */ public Builder param(String name, Object value) { JedisAsserts.notNull(name, "Parameter name must not be null"); JedisAsserts.notNull(value, "Parameter value must not be null"); instance.params.put(name, value); return this; } /** * Set the maximum time to wait for the query to complete (in milliseconds). * @param timeout the timeout in milliseconds * @return this builder */ public Builder timeout(long timeout) { instance.timeout = timeout; return this; } } @Override public void addParams(CommandArguments args) { // SEARCH clause(s) for (FTHybridSearchParams searchArg : searchArgs) { searchArg.addParams(args); } // VSIM clause(s) for (FTHybridVectorParams vectorArg : vectorArgs) { vectorArg.addParams(args); } // COMBINE clause if (combiner != null) { args.add(COMBINE); combiner.addParams(args); } // Post-processing operations (LOAD, GROUPBY, APPLY, SORTBY, FILTER, LIMIT) if (postProcessingArgs != null) { postProcessingArgs.addParams(args); } // PARAMS clause if (!params.isEmpty()) { args.add(PARAMS); args.add(params.size() * 2); for (Map.Entry entry : params.entrySet()) { args.add(entry.getKey()); args.add(entry.getValue()); } } // TIMEOUT clause if (timeout != null) { args.add(TIMEOUT); args.add(timeout); } } } ================================================ FILE: src/main/java/redis/clients/jedis/search/hybrid/FTHybridPostProcessingParams.java ================================================ package redis.clients.jedis.search.hybrid; import redis.clients.jedis.CommandArguments; import redis.clients.jedis.annots.Experimental; import redis.clients.jedis.params.IParams; import redis.clients.jedis.search.Apply; import redis.clients.jedis.search.Filter; import redis.clients.jedis.search.Limit; import redis.clients.jedis.search.aggr.Group; import redis.clients.jedis.search.aggr.SortedField; import redis.clients.jedis.util.JedisAsserts; import java.util.ArrayList; import java.util.Arrays; import java.util.List; import java.util.Objects; import static redis.clients.jedis.search.SearchProtocol.SearchKeyword.GROUPBY; import static redis.clients.jedis.search.SearchProtocol.SearchKeyword.LOAD; import static redis.clients.jedis.search.SearchProtocol.SearchKeyword.NOSORT; import static redis.clients.jedis.search.SearchProtocol.SearchKeyword.SORTBY; /** * Arguments for post-processing operations in FT.HYBRID command. Supports LOAD, GROUPBY, APPLY, * SORTBY, FILTER, and LIMIT operations. *

* Operations are applied in a specific order: *

    *
  1. LOAD - fields to load
  2. *
  3. GROUPBY - grouping with reducers
  4. *
  5. APPLY - computed fields
  6. *
  7. SORTBY - sorting
  8. *
  9. FILTER - filtering results
  10. *
  11. LIMIT - pagination
  12. *
*/ @Experimental public class FTHybridPostProcessingParams implements IParams { private List loadFields; private boolean loadAll; private Group groupBy; private final List applies = new ArrayList<>(); private SortedField[] sortByFields; private boolean noSort; private Filter filter; private Limit limit; private static final String LOAD_ALL = "*"; private FTHybridPostProcessingParams() { } /** * @return a new {@link Builder} for {@link FTHybridPostProcessingParams}. */ public static Builder builder() { return new Builder(); } /** * Builder for {@link FTHybridPostProcessingParams}. */ public static class Builder { private final FTHybridPostProcessingParams instance = new FTHybridPostProcessingParams(); /** * Build the {@link FTHybridPostProcessingParams} instance. * @return the configured arguments */ public FTHybridPostProcessingParams build() { return instance; } /** * Set the fields to load in the results. *

* This method replaces any previous load configuration (including loadAll()). To load all * fields, use {@link #loadAll()} instead. * @param fields the field names to load (must not be empty) * @return this builder * @throws IllegalArgumentException if fields is null, empty, or contains "*" */ public Builder load(String... fields) { JedisAsserts.notNull(fields, "Fields must not be null"); JedisAsserts.isTrue(fields.length > 0, "At least one field is required"); // Validate no wildcards in specific field list for (String field : fields) { JedisAsserts.notNull(field, "Field names cannot be null"); JedisAsserts.isFalse(LOAD_ALL.equals(field), "Cannot use '*' in load(). Use loadAll() instead to load all fields."); } // Clear previous state and set new values instance.loadAll = false; instance.loadFields = Arrays.asList(fields); return this; } /** * Set to load all fields in the results using LOAD *. *

* This method replaces any previous load configuration (including specific fields). *

* Note: requires Redis version >= 8.6.0 * @return this builder */ public Builder loadAll() { instance.loadAll = true; instance.loadFields = null; return this; } /** * Add a GROUPBY operation using {@link Group} from the aggregation package. * @param group the group operation with reducers * @return this builder */ public Builder groupBy(Group group) { instance.groupBy = group; return this; } /** * Add an APPLY operation. * @param apply the apply operation * @return this builder */ public Builder apply(Apply apply) { instance.applies.add(apply); return this; } /** * Add a SORTBY operation using {@link SortedField} from the aggregation package. *

* Last call to {@link #sortBy(SortedField...)}/{@link #noSort()} wins. * @param fields the sorted fields * @return this builder * @see Builder#noSort() */ public Builder sortBy(SortedField... fields) { JedisAsserts.notNull(fields, "Sort by fields must not be null"); JedisAsserts.isTrue(fields.length > 0, "At least one field is required"); instance.sortByFields = fields; instance.noSort = false; return this; } /** * Disable the default sorting by score. This adds the NOSORT keyword to the command. *

* Last call to {@link #sortBy(SortedField...)}/{@link #noSort()} wins. * @return this builder * @see Builder#sortBy(SortedField...) */ public Builder noSort() { instance.noSort = true; instance.sortByFields = null; return this; } /** * Add a FILTER operation. * @param filter the filter operation * @return this builder */ public Builder filter(Filter filter) { instance.filter = filter; return this; } /** * Add a LIMIT operation. * @param limit the limit operation * @return this builder */ public Builder limit(Limit limit) { instance.limit = limit; return this; } } @Override public void addParams(CommandArguments args) { // LOAD clause if (loadAll || (loadFields != null && !loadFields.isEmpty())) { args.add(LOAD); if (loadAll) { // Special case for LOAD * args.add(LOAD_ALL); } else { args.add(loadFields.size()); for (String field : loadFields) { // Add @ prefix if not already present if (!field.startsWith("@")) { args.add("@" + field); } else { args.add(field); } } } } // GROUPBY - convert aggr.Group to CommandArguments if (groupBy != null) { List groupArgs = new ArrayList<>(); groupBy.addArgs(groupArgs); args.add(GROUPBY); args.addObjects(groupArgs); } for (Apply apply : applies) { apply.addParams(args); } // SORTBY or NOSORT - mutually exclusive if (noSort) { args.add(NOSORT); } else if (sortByFields != null && sortByFields.length > 0) { args.add(SORTBY); args.add(sortByFields.length * 2); for (SortedField field : sortByFields) { args.add(field.getField()); args.add(field.getOrder()); } } if (filter != null) { filter.addParams(args); } if (limit != null) { limit.addParams(args); } } @Override public boolean equals(Object o) { if (this == o) return true; if (o == null || getClass() != o.getClass()) return false; FTHybridPostProcessingParams that = (FTHybridPostProcessingParams) o; return loadAll == that.loadAll && noSort == that.noSort && Objects.equals(loadFields, that.loadFields) && Objects.equals(groupBy, that.groupBy) && Objects.equals(applies, that.applies) && Arrays.equals(sortByFields, that.sortByFields) && Objects.equals(filter, that.filter) && Objects.equals(limit, that.limit); } @Override public int hashCode() { int result = Objects.hash(loadFields, loadAll, groupBy, applies, noSort, filter, limit); result = 31 * result + Arrays.hashCode(sortByFields); return result; } } ================================================ FILE: src/main/java/redis/clients/jedis/search/hybrid/FTHybridSearchParams.java ================================================ package redis.clients.jedis.search.hybrid; import redis.clients.jedis.CommandArguments; import redis.clients.jedis.annots.Experimental; import redis.clients.jedis.params.IParams; import redis.clients.jedis.search.Scorer; import redis.clients.jedis.util.JedisAsserts; import static redis.clients.jedis.search.SearchProtocol.SearchKeyword.*; /** * Arguments for the SEARCH clause in FT.HYBRID command. Configures text search with optional scorer * and score aliasing. */ @Experimental public class FTHybridSearchParams implements IParams { private String query; private Scorer scorer; private String scoreAlias; private FTHybridSearchParams() { } /** * @return a new {@link Builder} for {@link FTHybridSearchParams}. */ public static Builder builder() { return new Builder(); } /** * Builder for {@link FTHybridSearchParams}. */ public static class Builder { private final FTHybridSearchParams instance = new FTHybridSearchParams(); /** * Build the {@link FTHybridSearchParams} instance. * @return the configured arguments */ public FTHybridSearchParams build() { JedisAsserts.notNull(instance.query, "Query must not be null"); return instance; } /** * Set the search query string. * @param query the query string * @return this builder */ public Builder query(String query) { instance.query = query; return this; } /** * Set the scorer for text search. * @param scorer the scorer configuration * @return this builder */ public Builder scorer(Scorer scorer) { instance.scorer = scorer; return this; } /** * Set an alias for the text search score in the results. * @param scoreAlias the score alias name * @return this builder */ public Builder scoreAlias(String scoreAlias) { instance.scoreAlias = scoreAlias; return this; } } @Override public void addParams(CommandArguments args) { args.add(SEARCH); args.add(query); if (scorer != null) { args.add(SCORER); scorer.addParams(args); } if (scoreAlias != null) { args.add(YIELD_SCORE_AS); args.add(scoreAlias); } } } ================================================ FILE: src/main/java/redis/clients/jedis/search/hybrid/FTHybridVectorParams.java ================================================ package redis.clients.jedis.search.hybrid; import redis.clients.jedis.CommandArguments; import redis.clients.jedis.annots.Experimental; import redis.clients.jedis.params.IParams; import redis.clients.jedis.util.JedisAsserts; import java.util.ArrayList; import java.util.List; import static redis.clients.jedis.search.SearchProtocol.SearchKeyword.*; /** * Arguments for the VSIM (Vector Similarity) clause in FT.HYBRID command. Configures vector search * with KNN or RANGE methods. */ @Experimental public class FTHybridVectorParams implements IParams { private String field; private String vector; private VectorMethod method; private final List filters = new ArrayList<>(); private String scoreAlias; private FTHybridVectorParams() { } /** * @return a new {@link Builder} for {@link FTHybridVectorParams}. */ public static Builder builder() { return new Builder(); } /** * Builder for {@link FTHybridVectorParams}. */ public static class Builder { private final FTHybridVectorParams instance = new FTHybridVectorParams(); /** * Build the {@link FTHybridVectorParams} instance. * @return the configured arguments */ public FTHybridVectorParams build() { JedisAsserts.notNull(instance.field, "Field is required for VSIM clause"); JedisAsserts.notNull(instance.vector, "Vector is required for VSIM clause"); JedisAsserts.notNull(instance.method, "Method (KNN or RANGE) is required for VSIM clause"); return instance; } /** * Set the vector field name. * @param field the field name (e.g., "@embedding") * @return this builder */ public Builder field(String field) { instance.field = field; return this; } /** * Set the param name to reference the query vector BLOB. * @param vector the vector param name * @return this builder */ public Builder vector(String vector) { instance.vector = vector; return this; } /** * Set the vector search method (KNN or RANGE). * @param method the vector search method * @return this builder */ public Builder method(VectorMethod method) { instance.method = method; return this; } /** * Add a FILTER expression for pre-filtering documents before vector scoring. Can be called * multiple times to add multiple filters. * @param filter the filter expression * @return this builder */ public Builder filter(String filter) { JedisAsserts.notNull(filter, "Filter expression must not be null"); instance.filters.add(filter); return this; } /** * Set an alias for the vector distance score in the results. * @param scoreAlias the score alias name * @return this builder */ public Builder scoreAlias(String scoreAlias) { instance.scoreAlias = scoreAlias; return this; } } @Override public void addParams(CommandArguments args) { args.add(VSIM); args.add(field); if (vector.startsWith("$")) { args.add(vector); } else { args.add(String.format("$%s", vector)); } method.addParams(args); // FILTER inside VSIM - can have multiple filters for (String filter : filters) { args.add(FILTER); args.add(filter); } if (scoreAlias != null) { args.add(YIELD_SCORE_AS); args.add(scoreAlias); } } /** * Base interface for vector search methods. */ public interface VectorMethod extends IParams { } /** * KNN (K-Nearest Neighbors) vector search method. */ public static class Knn implements VectorMethod { private final int k; private Integer efRuntime; private Knn(int k) { this.k = k; } /** * Create a KNN method with the specified K value. * @param k the number of nearest neighbors to return * @return a new Knn instance */ public static Knn of(int k) { return new Knn(k); } /** * Set the EF_RUNTIME parameter for HNSW algorithm. * @param efRuntime the EF_RUNTIME value * @return this Knn instance */ public Knn efRuntime(int efRuntime) { this.efRuntime = efRuntime; return this; } @Override public void addParams(CommandArguments args) { args.add(KNN); int paramCount = efRuntime != null ? 4 : 2; args.add(paramCount); args.add(K); args.add(k); if (efRuntime != null) { args.add(EF_RUNTIME); args.add(efRuntime); } } } /** * RANGE vector search method. */ public static class Range implements VectorMethod { private final double radius; private Double epsilon; private Range(double radius) { this.radius = radius; } /** * Create a RANGE method with the specified radius. * @param radius the search radius * @return a new Range instance */ public static Range of(double radius) { return new Range(radius); } /** * Set the epsilon parameter for range search. * @param epsilon the epsilon value * @return this Range instance */ public Range epsilon(double epsilon) { this.epsilon = epsilon; return this; } @Override public void addParams(CommandArguments args) { args.add(RANGE); int paramCount = epsilon != null ? 4 : 2; args.add(paramCount); args.add(RADIUS); args.add(radius); if (epsilon != null) { args.add(EPSILON); args.add(epsilon); } } } } ================================================ FILE: src/main/java/redis/clients/jedis/search/hybrid/HybridResult.java ================================================ package redis.clients.jedis.search.hybrid; import java.util.ArrayList; import java.util.Collections; import java.util.HashMap; import java.util.List; import java.util.Map; import redis.clients.jedis.Builder; import redis.clients.jedis.BuilderFactory; import redis.clients.jedis.annots.Experimental; import redis.clients.jedis.search.Document; import redis.clients.jedis.util.KeyValue; /** * Represents the results of an {@code FT.HYBRID} command. Extends the concept of search results * with additional hybrid-specific fields like execution time. Results are returned as * {@link Document} objects. */ @Experimental public class HybridResult { private static final String KEY_FIELD = "__key"; private static final String SCORE_FIELD = "__score"; private final long totalResults; private final double executionTime; private final List documents; private final List warnings; private HybridResult(long totalResults, double executionTime, List documents, List warnings) { this.totalResults = totalResults; this.executionTime = executionTime; this.documents = documents != null ? documents : Collections.emptyList(); this.warnings = warnings != null ? warnings : Collections.emptyList(); } /** * @return the total number of matching documents reported by the server */ public long getTotalResults() { return totalResults; } /** * @return the execution time reported by the server in seconds (or {@code 0.0} if not available) */ public double getExecutionTime() { return executionTime; } /** * @return an unmodifiable view of all documents returned by the command */ public List getDocuments() { return Collections.unmodifiableList(documents); } /** * @return a read-only view of all warnings reported by the server */ public List getWarnings() { return Collections.unmodifiableList(warnings); } @Override public String toString() { return getClass().getSimpleName() + "{Total results:" + totalResults + ", Execution time:" + executionTime + ", Documents:" + documents + (warnings != null ? ", Warnings:" + warnings : "") + "}"; } /** * Converts a flat map result to a Document. The map may contain __key and __score fields which * are extracted as the document id and score respectively. */ private static Document mapToDocument(Map map) { String id = null; Double score = null; Map fields = new HashMap<>(); for (Map.Entry entry : map.entrySet()) { String key = entry.getKey(); Object value = entry.getValue(); if (KEY_FIELD.equals(key)) { id = value != null ? value.toString() : null; } else if (SCORE_FIELD.equals(key)) { score = value != null ? Double.parseDouble(value.toString()) : null; } else { fields.put(key, value); } } return new Document(id, fields, score != null ? score : 1.0); } // RESP2/RESP3 Builder public static final Builder HYBRID_RESULT_BUILDER = new Builder() { private static final String TOTAL_RESULTS_STR = "total_results"; private static final String EXECUTION_TIME_STR = "execution_time"; private static final String RESULTS_STR = "results"; private static final String WARNINGS_STR = "warnings"; @Override public HybridResult build(Object data) { List list = (List) data; // Check if RESP3 (KeyValue) or RESP2 (flat list) if (!list.isEmpty() && list.get(0) instanceof KeyValue) { return buildResp3((List) list); } else { return buildResp2(list); } } private HybridResult buildResp3(List list) { long totalResults = -1; double executionTime = 0; List documents = null; List warnings = null; for (KeyValue kv : list) { String key = BuilderFactory.STRING.build(kv.getKey()); Object rawVal = kv.getValue(); switch (key) { case TOTAL_RESULTS_STR: totalResults = BuilderFactory.LONG.build(rawVal); break; case EXECUTION_TIME_STR: executionTime = BuilderFactory.DOUBLE.build(rawVal); break; case RESULTS_STR: documents = new ArrayList<>(); List resultsList = (List) rawVal; for (Object resultObj : resultsList) { Map resultMap = BuilderFactory.ENCODED_OBJECT_MAP.build(resultObj); documents.add(mapToDocument(resultMap)); } break; case WARNINGS_STR: warnings = BuilderFactory.STRING_LIST.build(rawVal); break; } } return new HybridResult(totalResults, executionTime, documents, warnings); } private HybridResult buildResp2(List list) { // RESP2 format: ["key1", value1, "key2", value2, ...] long totalResults = -1; double executionTime = 0; List documents = null; List warnings = null; for (int i = 0; i + 1 < list.size(); i += 2) { String key = BuilderFactory.STRING.build(list.get(i)); Object rawVal = list.get(i + 1); switch (key) { case TOTAL_RESULTS_STR: totalResults = BuilderFactory.LONG.build(rawVal); break; case EXECUTION_TIME_STR: executionTime = BuilderFactory.DOUBLE.build(rawVal); break; case RESULTS_STR: documents = new ArrayList<>(); List resultsList = (List) rawVal; for (Object resultObj : resultsList) { Map resultMap = BuilderFactory.ENCODED_OBJECT_MAP.build(resultObj); documents.add(mapToDocument(resultMap)); } break; case WARNINGS_STR: warnings = BuilderFactory.STRING_LIST.build(rawVal); break; } } return new HybridResult(totalResults, executionTime, documents, warnings); } }; } ================================================ FILE: src/main/java/redis/clients/jedis/search/package-info.java ================================================ /** * This package contains the classes and interfaces related to RediSearch module. */ package redis.clients.jedis.search; ================================================ FILE: src/main/java/redis/clients/jedis/search/querybuilder/DisjunctNode.java ================================================ package redis.clients.jedis.search.querybuilder; /** * A disjunct node. evaluates to true if any of its children are false. Conversely, this node * evaluates to false only iff all of its children are true, making it the exact inverse of * {@link IntersectNode} * * In RS, it looks like: * * {@code -(@f1:v1 @f2:v2)} * * @see DisjunctUnionNode which evalutes to true if all its children are false. */ public class DisjunctNode extends IntersectNode { @Override public String toString(Parenthesize mode) { String ret = super.toString(Parenthesize.NEVER); if (shouldParenthesize(mode)) { return "-(" + ret + ")"; } else { return "-" + ret; } } } ================================================ FILE: src/main/java/redis/clients/jedis/search/querybuilder/DisjunctUnionNode.java ================================================ package redis.clients.jedis.search.querybuilder; /** * A disjunct union node is the inverse of a {@link UnionNode}. It evaluates to true only iff * all its children are false. Conversely, it evaluates to false if any of its * children are true. * * As an RS query it looks like {@code -(@f1:v1|@f2:v2)} * * @see DisjunctNode which evaluates to true if any of its children are false. */ public class DisjunctUnionNode extends DisjunctNode { @Override protected String getJoinString() { return "|"; } } ================================================ FILE: src/main/java/redis/clients/jedis/search/querybuilder/DoubleRangeValue.java ================================================ package redis.clients.jedis.search.querybuilder; /** * @author mnunberg on 2/23/18. */ public class DoubleRangeValue extends RangeValue { private final double from; private final double to; private static void appendNum(StringBuilder sb, double n, boolean inclusive) { if (!inclusive) { sb.append("("); } if (n == Double.NEGATIVE_INFINITY) { sb.append("-inf"); } else if (n == Double.POSITIVE_INFINITY) { sb.append("inf"); } else { sb.append(n); } } public DoubleRangeValue(double from, double to) { this.from = from; this.to = to; } @Override protected void appendFrom(StringBuilder sb, boolean inclusive) { appendNum(sb, from, inclusive); } @Override protected void appendTo(StringBuilder sb, boolean inclusive) { appendNum(sb, to, inclusive); } } ================================================ FILE: src/main/java/redis/clients/jedis/search/querybuilder/GeoValue.java ================================================ package redis.clients.jedis.search.querybuilder; import java.util.Locale; import redis.clients.jedis.args.GeoUnit; /** * Created by mnunberg on 2/23/18. */ public class GeoValue extends Value { private final GeoUnit unit; private final double lon; private final double lat; private final double radius; public GeoValue(double lon, double lat, double radius, GeoUnit unit) { this.lon = lon; this.lat = lat; this.radius = radius; this.unit = unit; } @Override public String toString() { return "[" + lon + " " + lat + " " + radius + " " + unit.name().toLowerCase(Locale.ENGLISH) + "]"; } @Override public boolean isCombinable() { return false; } } ================================================ FILE: src/main/java/redis/clients/jedis/search/querybuilder/IntersectNode.java ================================================ package redis.clients.jedis.search.querybuilder; /** * The intersection node evaluates to true if any of its children are true. * * In RS: {@code @f1:v1 @f2:v2} */ public class IntersectNode extends QueryNode { @Override protected String getJoinString() { return " "; } } ================================================ FILE: src/main/java/redis/clients/jedis/search/querybuilder/LongRangeValue.java ================================================ package redis.clients.jedis.search.querybuilder; public class LongRangeValue extends RangeValue { private final long from; private final long to; @Override public boolean isCombinable() { return false; } private static void appendNum(StringBuilder sb, long n, boolean inclusive) { if (!inclusive) { sb.append("("); } if (n == Long.MIN_VALUE) { sb.append("-inf"); } else if (n == Long.MAX_VALUE) { sb.append("inf"); } else { sb.append(Long.toString(n)); } } public LongRangeValue(long from, long to) { this.from = from; this.to = to; } @Override protected void appendFrom(StringBuilder sb, boolean inclusive) { appendNum(sb, from, inclusive); } @Override protected void appendTo(StringBuilder sb, boolean inclusive) { appendNum(sb, to, inclusive); } } ================================================ FILE: src/main/java/redis/clients/jedis/search/querybuilder/Node.java ================================================ package redis.clients.jedis.search.querybuilder; import redis.clients.jedis.search.Query; /** * Created by mnunberg on 2/23/18. * * Base node interface */ public interface Node { enum Parenthesize { /** * Always encapsulate */ ALWAYS, /** * Never encapsulate. Note that this may be ignored if parentheses are semantically required * (e.g. {@code @foo:(val1|val2)}. However, something like {@code @foo:v1 @bar:v2} need not be * parenthesized. */ NEVER, /** * Determine encapsulation based on number of children. If the node only has one child, it is * not parenthesized, if it has more than one child, it is parenthesized */ DEFAULT } /** * Returns the string form of this node. * * @param mode Whether the string should be encapsulated in parentheses {@code (...)} * @return The string query. */ String toString(Parenthesize mode); /** * Returns the string form of this node. This may be passed to * {@link redis.clients.jedis.UnifiedJedis#ftSearch(String, Query)} * * @return The query string. */ @Override String toString(); } ================================================ FILE: src/main/java/redis/clients/jedis/search/querybuilder/OptionalNode.java ================================================ package redis.clients.jedis.search.querybuilder; /** * Created by mnunberg on 2/23/18. * * The optional node affects scoring and ordering. If it evaluates to true, the result is ranked * higher. It is helpful to combine it with a {@link UnionNode} to rank a document higher if it * meets one of several criteria. * * In RS: {@code ~(@lang:en @country:us)}. */ public class OptionalNode extends IntersectNode { @Override public String toString(Parenthesize mode) { String ret = super.toString(Parenthesize.NEVER); if (shouldParenthesize(mode)) { return "~(" + ret + ")"; } return "~" + ret; } } ================================================ FILE: src/main/java/redis/clients/jedis/search/querybuilder/QueryBuilders.java ================================================ package redis.clients.jedis.search.querybuilder; import java.util.Arrays; import static redis.clients.jedis.search.querybuilder.Values.value; /** * Created by mnunberg on 2/23/18. * * This class contains methods to construct query nodes. These query nodes can be added to parent * query nodes (building a chain) or used as the root query node. */ public class QueryBuilders { private QueryBuilders() { throw new InstantiationError("Must not instantiate this class"); } /** * Create a new intersection node with child nodes. An intersection node is true if all its * children are also true * * @param n sub-condition to add * @return The node */ public static QueryNode intersect(Node... n) { return new IntersectNode().add(n); } /** * Create a new intersection node with a field-value pair. * * @param field The field that should contain this value. If this value is empty, then any field * will be checked. * @param values Value to check for. The node will be true only if the field (or any field) * contains all of the values * @return The node */ public static QueryNode intersect(String field, Value... values) { return new IntersectNode().add(field, values); } /** * Helper method to create a new intersection node with a string value. * * @param field The field to check. If left null or empty, all fields will be checked. * @param stringValue The value to check * @return The node */ public static QueryNode intersect(String field, String stringValue) { return intersect(field, value(stringValue)); } /** * Create a union node. Union nodes evaluate to true if any of its children are true * * @param n Child node * @return The union node */ public static QueryNode union(Node... n) { return new UnionNode().add(n); } /** * Create a union node which can match an one or more values * * @param field Field to check. If empty, all fields are checked * @param values Values to search for. The node evaluates to true if {@code field} matches any of * the values * @return The union node */ public static QueryNode union(String field, Value... values) { return new UnionNode().add(field, values); } /** * Convenience method to match one or more strings. This is equivalent to * {@code union(field, value(v1), value(v2), value(v3)) ...} * * @param field Field to match * @param values Strings to check for * @return The union node */ public static QueryNode union(String field, String... values) { return union(field, (Value[]) Arrays.stream(values).map(Values::value).toArray()); } /** * Create a disjunct node. Disjunct nodes are true iff any of its children are not * true. Conversely, this node evaluates to false if all its children are true. * * @param n Child nodes to add * @return The disjunct node */ public static QueryNode disjunct(Node... n) { return new DisjunctNode().add(n); } /** * Create a disjunct node using one or more values. The node will evaluate to true iff the field * does not match any of the values. * * @param field Field to check for (empty or null for any field) * @param values The values to check for * @return The node */ public static QueryNode disjunct(String field, Value... values) { return new DisjunctNode().add(field, values); } /** * Create a disjunct node using one or more values. The node will evaluate to true iff the field * does not match any of the values. * * @param field Field to check for (empty or null for any field) * @param values The values to check for * @return The node */ public static QueryNode disjunct(String field, String... values) { return disjunct(field, (Value[]) Arrays.stream(values).map(Values::value).toArray()); } /** * Create a disjunct union node. This node evaluates to true if all of its children are not * true. Conversely, this node evaluates as false if any of its children are true. * * @param n * @return The node */ public static QueryNode disjunctUnion(Node... n) { return new DisjunctUnionNode().add(n); } public static QueryNode disjunctUnion(String field, Value... values) { return new DisjunctUnionNode().add(field, values); } public static QueryNode disjunctUnion(String field, String... values) { return disjunctUnion(field, (Value[]) Arrays.stream(values).map(Values::value).toArray()); } /** * Create an optional node. Optional nodes do not affect which results are returned but they * influence ordering and scoring. * * @param n The node to evaluate as optional * @return The new node */ public static QueryNode optional(Node... n) { return new OptionalNode().add(n); } public static QueryNode optional(String field, Value... values) { return new OptionalNode().add(field, values); } } ================================================ FILE: src/main/java/redis/clients/jedis/search/querybuilder/QueryNode.java ================================================ package redis.clients.jedis.search.querybuilder; import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; import java.util.List; import java.util.StringJoiner; public abstract class QueryNode implements Node { private final List children = new ArrayList<>(); protected abstract String getJoinString(); /** * Add a match criteria to this node * * @param field The field to check. If null or empty, then any field is checked * @param values Values to check for. * @return The current node, for chaining. */ public QueryNode add(String field, Value... values) { children.add(new ValueNode(field, getJoinString(), values)); return this; } /** * Convenience method to add a list of string values * * @param field Field to check for * @param values One or more string values. * @return The current node, for chaining. */ public QueryNode add(String field, String... values) { children.add(new ValueNode(field, getJoinString(), values)); return this; } /** * Add a list of values from a collection * * @param field The field to check * @param values Collection of values to match * @return The current node for chaining. */ public QueryNode add(String field, Collection values) { return add(field, values.toArray(new Value[0])); } /** * Add children nodes to this node. * * @param nodes Children nodes to add * @return The current node, for chaining. */ public QueryNode add(Node... nodes) { children.addAll(Arrays.asList(nodes)); return this; } protected boolean shouldParenthesize(Parenthesize mode) { if (mode == Parenthesize.ALWAYS) { return true; } else if (mode == Parenthesize.NEVER) { return false; } else { return children.size() > 1; } } @Override public String toString(Parenthesize parenMode) { StringBuilder sb = new StringBuilder(); StringJoiner sj = new StringJoiner(getJoinString()); if (shouldParenthesize(parenMode)) { sb.append('('); } for (Node n : children) { sj.add(n.toString(parenMode)); } sb.append(sj.toString()); if (shouldParenthesize(parenMode)) { sb.append(')'); } return sb.toString(); } @Override public String toString() { return toString(Parenthesize.DEFAULT); } } ================================================ FILE: src/main/java/redis/clients/jedis/search/querybuilder/RangeValue.java ================================================ package redis.clients.jedis.search.querybuilder; /** * @author mnunberg on 2/23/18. */ public abstract class RangeValue extends Value { private boolean inclusiveMin = true; private boolean inclusiveMax = true; @Override public boolean isCombinable() { return false; } protected abstract void appendFrom(StringBuilder sb, boolean inclusive); protected abstract void appendTo(StringBuilder sb, boolean inclusive); @Override public String toString() { StringBuilder sb = new StringBuilder(); sb.append('['); appendFrom(sb, inclusiveMin); sb.append(' '); appendTo(sb, inclusiveMax); sb.append(']'); return sb.toString(); } public RangeValue inclusiveMin(boolean val) { inclusiveMin = val; return this; } public RangeValue inclusiveMax(boolean val) { inclusiveMax = val; return this; } } ================================================ FILE: src/main/java/redis/clients/jedis/search/querybuilder/UnionNode.java ================================================ package redis.clients.jedis.search.querybuilder; /** * Created by mnunberg on 2/23/18. */ public class UnionNode extends QueryNode { @Override protected String getJoinString() { return "|"; } } ================================================ FILE: src/main/java/redis/clients/jedis/search/querybuilder/Value.java ================================================ package redis.clients.jedis.search.querybuilder; /** * Created by mnunberg on 2/23/18. */ public abstract class Value { public boolean isCombinable() { return false; } @Override public abstract String toString(); } ================================================ FILE: src/main/java/redis/clients/jedis/search/querybuilder/ValueNode.java ================================================ package redis.clients.jedis.search.querybuilder; import java.util.StringJoiner; /** * Created by mnunberg on 2/23/18. */ public class ValueNode implements Node { private final Value[] values; private final String field; private final String joinString; public ValueNode(String field, String joinstr, Value... values) { this.field = field; this.values = values; this.joinString = joinstr; } private static Value[] fromStrings(String[] values) { Value[] objs = new Value[values.length]; for (int i = 0; i < values.length; i++) { objs[i] = Values.value(values[i]); } return objs; } public ValueNode(String field, String joinstr, String... values) { this(field, joinstr, fromStrings(values)); } private String formatField() { if (field == null || field.isEmpty()) { return ""; } return '@' + field + ':'; } private String toStringCombinable(Parenthesize mode) { StringBuilder sb = new StringBuilder(formatField()); if (values.length > 1 || mode == Parenthesize.ALWAYS) { sb.append('('); } StringJoiner sj = new StringJoiner(joinString); for (Value v : values) { sj.add(v.toString()); } sb.append(sj.toString()); if (values.length > 1 || mode == Parenthesize.ALWAYS) { sb.append(')'); } return sb.toString(); } private String toStringDefault(Parenthesize mode) { boolean useParen = mode == Parenthesize.ALWAYS; if (!useParen) { useParen = mode != Parenthesize.NEVER && values.length > 1; } StringBuilder sb = new StringBuilder(); if (useParen) { sb.append('('); } StringJoiner sj = new StringJoiner(joinString); for (Value v : values) { sj.add(formatField() + v.toString()); } sb.append(sj.toString()); if (useParen) { sb.append(')'); } return sb.toString(); } @Override public String toString(Parenthesize mode) { if (values[0].isCombinable()) { return toStringCombinable(mode); } return toStringDefault(mode); } } ================================================ FILE: src/main/java/redis/clients/jedis/search/querybuilder/Values.java ================================================ package redis.clients.jedis.search.querybuilder; import redis.clients.jedis.GeoCoordinate; import redis.clients.jedis.args.GeoUnit; import java.util.StringJoiner; /** * Created by mnunberg on 2/23/18. */ public class Values { private Values() { throw new InstantiationError("Must not instantiate this class"); } private abstract static class ScalableValue extends Value { @Override public boolean isCombinable() { return true; } } public static Value value(String s) { return new ScalableValue() { @Override public String toString() { return s; } }; } public static GeoValue geo(GeoCoordinate coord, double radius, GeoUnit unit) { return new GeoValue(coord.getLongitude(), coord.getLatitude(), radius, unit); } public static RangeValue between(double from, double to) { return new DoubleRangeValue(from, to); } public static RangeValue between(int from, int to) { return new LongRangeValue(from, to); } // TODO: change to simpler [d] available since RedisStack 7.4.0-rc1; // currently kept for backward compatibility public static RangeValue eq(double d) { return new DoubleRangeValue(d, d); } // TODO: change to simpler [i] available since RedisStack 7.4.0-rc1; // currently kept for backward compatibility public static RangeValue eq(int i) { return new LongRangeValue(i, i); } public static RangeValue lt(double d) { return new DoubleRangeValue(Double.NEGATIVE_INFINITY, d).inclusiveMax(false); } public static RangeValue lt(int d) { return new LongRangeValue(Long.MIN_VALUE, d).inclusiveMax(false); } public static RangeValue gt(double d) { return new DoubleRangeValue(d, Double.POSITIVE_INFINITY).inclusiveMin(false); } public static RangeValue gt(int d) { return new LongRangeValue(d, Long.MAX_VALUE).inclusiveMin(false); } public static RangeValue le(double d) { return lt(d).inclusiveMax(true); } public static RangeValue le(int d) { return lt(d).inclusiveMax(true); } public static RangeValue ge(double d) { return gt(d).inclusiveMin(true); } public static RangeValue ge(int d) { return gt(d).inclusiveMin(true); } public static Value tags(String... tags) { if (tags.length == 0) { throw new IllegalArgumentException("Must have at least one tag"); } StringJoiner sj = new StringJoiner(" | "); for (String s : tags) { sj.add(s); } return new Value() { @Override public String toString() { return "{" + sj.toString() + "}"; } }; } } ================================================ FILE: src/main/java/redis/clients/jedis/search/schemafields/GeoField.java ================================================ package redis.clients.jedis.search.schemafields; import static redis.clients.jedis.search.SearchProtocol.SearchKeyword.*; import redis.clients.jedis.CommandArguments; import redis.clients.jedis.search.FieldName; public class GeoField extends SchemaField { private boolean indexMissing; private boolean sortable; private boolean noIndex; public GeoField(String fieldName) { super(fieldName); } public GeoField(FieldName fieldName) { super(fieldName); } public static GeoField of(String fieldName) { return new GeoField(fieldName); } public static GeoField of(FieldName fieldName) { return new GeoField(fieldName); } @Override public GeoField as(String attribute) { super.as(attribute); return this; } public GeoField indexMissing() { this.indexMissing = true; return this; } public GeoField sortable() { this.sortable = true; return this; } public GeoField noIndex() { this.noIndex = true; return this; } @Override public void addParams(CommandArguments args) { args.addParams(fieldName); args.add(GEO); if (indexMissing) { args.add(INDEXMISSING); } if (sortable) { args.add(SORTABLE); } if (noIndex) { args.add(NOINDEX); } } } ================================================ FILE: src/main/java/redis/clients/jedis/search/schemafields/GeoShapeField.java ================================================ package redis.clients.jedis.search.schemafields; import static redis.clients.jedis.search.SearchProtocol.SearchKeyword.*; import redis.clients.jedis.CommandArguments; import redis.clients.jedis.search.FieldName; public class GeoShapeField extends SchemaField { public enum CoordinateSystem { /** * For cartesian (X,Y). */ FLAT, /** * For geographic (lon, lat). */ SPHERICAL } private final CoordinateSystem system; private boolean indexMissing; private boolean noIndex; public GeoShapeField(String fieldName, CoordinateSystem system) { super(fieldName); this.system = system; } public GeoShapeField(FieldName fieldName, CoordinateSystem system) { super(fieldName); this.system = system; } public static GeoShapeField of(String fieldName, CoordinateSystem system) { return new GeoShapeField(fieldName, system); } @Override public GeoShapeField as(String attribute) { super.as(attribute); return this; } public GeoShapeField indexMissing() { this.indexMissing = true; return this; } public GeoShapeField noIndex() { this.noIndex = true; return this; } @Override public void addParams(CommandArguments args) { args.addParams(fieldName).add(GEOSHAPE).add(system); if (indexMissing) { args.add(INDEXMISSING); } if (noIndex) { args.add(NOINDEX); } } } ================================================ FILE: src/main/java/redis/clients/jedis/search/schemafields/NumericField.java ================================================ package redis.clients.jedis.search.schemafields; import static redis.clients.jedis.search.SearchProtocol.SearchKeyword.*; import redis.clients.jedis.CommandArguments; import redis.clients.jedis.search.FieldName; public class NumericField extends SchemaField { private boolean indexMissing; private boolean sortable; private boolean noIndex; public NumericField(String fieldName) { super(fieldName); } public NumericField(FieldName fieldName) { super(fieldName); } public static NumericField of(String fieldName) { return new NumericField(fieldName); } public static NumericField of(FieldName fieldName) { return new NumericField(fieldName); } @Override public NumericField as(String attribute) { super.as(attribute); return this; } public NumericField indexMissing() { this.indexMissing = true; return this; } /** * Sorts the results by the value of this field. */ public NumericField sortable() { this.sortable = true; return this; } /** * Avoid indexing. */ public NumericField noIndex() { this.noIndex = true; return this; } @Override public void addParams(CommandArguments args) { args.addParams(fieldName); args.add(NUMERIC); if (indexMissing) { args.add(INDEXMISSING); } if (sortable) { args.add(SORTABLE); } if (noIndex) { args.add(NOINDEX); } } } ================================================ FILE: src/main/java/redis/clients/jedis/search/schemafields/SchemaField.java ================================================ package redis.clients.jedis.search.schemafields; import redis.clients.jedis.params.IParams; import redis.clients.jedis.search.FieldName; public abstract class SchemaField implements IParams { protected final FieldName fieldName; public SchemaField(String fieldName) { this.fieldName = new FieldName(fieldName); } public SchemaField(FieldName fieldName) { this.fieldName = fieldName; } public SchemaField as(String attribute) { fieldName.as(attribute); return this; } public final FieldName getFieldName() { return fieldName; } public final String getName() { return fieldName.getName(); } } ================================================ FILE: src/main/java/redis/clients/jedis/search/schemafields/TagField.java ================================================ package redis.clients.jedis.search.schemafields; import static redis.clients.jedis.search.SearchProtocol.SearchKeyword.*; import redis.clients.jedis.CommandArguments; import redis.clients.jedis.search.FieldName; import redis.clients.jedis.util.SafeEncoder; public class TagField extends SchemaField { private boolean indexMissing; private boolean indexEmpty; private byte[] separator; private boolean caseSensitive; private boolean withSuffixTrie; private boolean sortable; private boolean sortableUNF; private boolean noIndex; public TagField(String fieldName) { super(fieldName); } public TagField(FieldName fieldName) { super(fieldName); } public static TagField of(String fieldName) { return new TagField(fieldName); } public static TagField of(FieldName fieldName) { return new TagField(fieldName); } @Override public TagField as(String attribute) { super.as(attribute); return this; } public TagField indexMissing() { this.indexMissing = true; return this; } public TagField indexEmpty() { this.indexEmpty = true; return this; } /** * Indicates how the text contained in the attribute is to be split into individual tags. * @param separator */ public TagField separator(char separator) { if (separator < 128) { this.separator = new byte[]{(byte) separator}; } else { this.separator = SafeEncoder.encode(String.valueOf(separator)); } return this; } /** * Keeps the original letter cases of the tags. */ public TagField caseSensitive() { this.caseSensitive = true; return this; } /** * Keeps a suffix trie with all terms which match the suffix. It is used to optimize * contains and suffix queries. */ public TagField withSuffixTrie() { this.withSuffixTrie = true; return this; } /** * Sorts the results by the value of this field. */ public TagField sortable() { this.sortable = true; return this; } /** * Sorts the results by the value of this field without normalization. */ public TagField sortableUNF() { this.sortableUNF = true; return this; } /** * @deprecated Use {@code TagField#sortableUNF()}. * @see TagField#sortableUNF() */ @Deprecated public TagField sortableUnNormalizedForm() { return sortableUNF(); } /** * Avoid indexing. */ public TagField noIndex() { this.noIndex = true; return this; } @Override public void addParams(CommandArguments args) { args.addParams(fieldName); args.add(TAG); if (indexMissing) { args.add(INDEXMISSING); } if (indexEmpty) { args.add(INDEXEMPTY); } if (separator != null) { args.add(SEPARATOR).add(separator); } if (caseSensitive) { args.add(CASESENSITIVE); } if (withSuffixTrie) { args.add(WITHSUFFIXTRIE); } if (sortableUNF) { args.add(SORTABLE).add(UNF); } else if (sortable) { args.add(SORTABLE); } if (noIndex) { args.add(NOINDEX); } } } ================================================ FILE: src/main/java/redis/clients/jedis/search/schemafields/TextField.java ================================================ package redis.clients.jedis.search.schemafields; import static redis.clients.jedis.search.SearchProtocol.SearchKeyword.*; import redis.clients.jedis.CommandArguments; import redis.clients.jedis.search.FieldName; public class TextField extends SchemaField { private boolean indexMissing; private boolean indexEmpty; private Double weight; private boolean noStem; private String phoneticMatcher; private boolean withSuffixTrie; private boolean sortable; private boolean sortableUNF; private boolean noIndex; public TextField(String fieldName) { super(fieldName); } public TextField(FieldName fieldName) { super(fieldName); } public static TextField of(String fieldName) { return new TextField(fieldName); } public static TextField of(FieldName fieldName) { return new TextField(fieldName); } @Override public TextField as(String attribute) { super.as(attribute); return this; } public TextField indexMissing() { this.indexMissing = true; return this; } public TextField indexEmpty() { this.indexEmpty = true; return this; } /** * Declares the importance of this attribute when calculating result accuracy. This is a * multiplication factor. * @param weight */ public TextField weight(double weight) { this.weight = weight; return this; } /** * Disable stemming when indexing. */ public TextField noStem() { this.noStem = true; return this; } /** * Perform phonetic matching. * @param matcher */ public TextField phonetic(String matcher) { this.phoneticMatcher = matcher; return this; } /** * Keeps a suffix trie with all terms which match the suffix. It is used to optimize * contains and suffix queries. */ public TextField withSuffixTrie() { this.withSuffixTrie = true; return this; } /** * Sorts the results by the value of this field. */ public TextField sortable() { this.sortable = true; return this; } /** * Sorts the results by the value of this field without normalization. */ public TextField sortableUNF() { this.sortableUNF = true; return this; } /** * @deprecated Use {@code TextField#sortableUNF()}. * @see TextField#sortableUNF() */ @Deprecated public TextField sortableUnNormalizedForm() { return sortableUNF(); } /** * Avoid indexing. */ public TextField noIndex() { this.noIndex = true; return this; } @Override public void addParams(CommandArguments args) { args.addParams(fieldName); args.add(TEXT); if (indexMissing) { args.add(INDEXMISSING); } if (indexEmpty) { args.add(INDEXEMPTY); } if (weight != null) { args.add(WEIGHT).add(weight); } if (noStem) { args.add(NOSTEM); } if (phoneticMatcher != null) { args.add(PHONETIC).add(phoneticMatcher); } if (withSuffixTrie) { args.add(WITHSUFFIXTRIE); } if (sortableUNF) { args.add(SORTABLE).add(UNF); } else if (sortable) { args.add(SORTABLE); } if (noIndex) { args.add(NOINDEX); } } } ================================================ FILE: src/main/java/redis/clients/jedis/search/schemafields/VectorField.java ================================================ package redis.clients.jedis.search.schemafields; import static redis.clients.jedis.search.SearchProtocol.SearchKeyword.INDEXMISSING; import static redis.clients.jedis.search.SearchProtocol.SearchKeyword.VECTOR; import java.util.LinkedHashMap; import java.util.Map; import redis.clients.jedis.CommandArguments; import redis.clients.jedis.args.Rawable; import redis.clients.jedis.search.FieldName; import redis.clients.jedis.util.SafeEncoder; /** * Represents a vector field in a Redis search index schema for performing semantic vector searches. * Vector fields enable high-performance similarity searches over vector embeddings using various * algorithms and distance metrics. * * @see Redis Vector Search Documentation */ public class VectorField extends SchemaField { /** * Enumeration of supported vector indexing algorithms in Redis. * Each algorithm has different performance characteristics and use cases. */ public enum VectorAlgorithm implements Rawable { /** * FLAT algorithm provides exact vector search with perfect accuracy. * Best suited for smaller datasets (< 1M vectors) where search accuracy * is more important than search latency. */ FLAT("FLAT"), /** * HNSW (Hierarchical Navigable Small World) algorithm provides approximate * vector search with configurable accuracy-performance trade-offs. * Best suited for larger datasets (> 1M vectors) where search performance * and scalability are more important than perfect accuracy. */ HNSW("HNSW"), /** * SVS_VAMANA algorithm provides high-performance approximate vector search * optimized for specific use cases with advanced compression and optimization features. * *

Characteristics: *

    *
  • High-performance approximate search
  • *
  • Support for vector compression (LVQ, LeanVec)
  • *
  • Configurable graph construction and search parameters
  • *
  • Optimized for Intel platforms with fallback support
  • *
* *

Note: This algorithm may have specific requirements and limitations. * Consult the Redis documentation for detailed usage guidelines. */ SVS_VAMANA("SVS-VAMANA"); private final byte[] raw; /** * Creates a VectorAlgorithm enum value. * * @param redisParamName the Redis parameter name for this algorithm */ VectorAlgorithm(String redisParamName) { raw = SafeEncoder.encode(redisParamName); } /** * Returns the raw byte representation of the algorithm name for Redis commands. * * @return the raw bytes of the algorithm name */ @Override public byte[] getRaw() { return raw; } } private final VectorAlgorithm algorithm; private final Map attributes; private boolean indexMissing; // private boolean noIndex; // throws Field `NOINDEX` does not have a type /** * Creates a new VectorField with the specified field name, algorithm, and attributes. * * @param fieldName the name of the vector field in the index * @param algorithm the vector indexing algorithm to use * @param attributes the algorithm-specific configuration attributes * @throws IllegalArgumentException if required attributes are missing or invalid */ public VectorField(String fieldName, VectorAlgorithm algorithm, Map attributes) { super(fieldName); this.algorithm = algorithm; this.attributes = attributes; } /** * Creates a new VectorField with the specified field name, algorithm, and attributes. * * @param fieldName the field name object containing the field name and optional alias * @param algorithm the vector indexing algorithm to use * @param attributes the algorithm-specific configuration attributes * @throws IllegalArgumentException if required attributes are missing or invalid * @see #VectorField(String, VectorAlgorithm, Map) for detailed attribute documentation */ public VectorField(FieldName fieldName, VectorAlgorithm algorithm, Map attributes) { super(fieldName); this.algorithm = algorithm; this.attributes = attributes; } /** * Sets an alias for this field that can be used in queries instead of the field name. * This is useful when the field name contains special characters or when you want * to use a shorter name in queries. * * @param attribute the alias name to use for this field in queries * @return this VectorField instance for method chaining */ @Override public VectorField as(String attribute) { super.as(attribute); return this; } /** * Configures the field to handle missing values during indexing. * When enabled, documents that don't contain this vector field will still be indexed, * but won't participate in vector searches. * *

This is useful when not all documents in your dataset contain vector embeddings, * but you still want to index them for other types of searches. * * @return this VectorField instance for method chaining */ public VectorField indexMissing() { this.indexMissing = true; return this; } /** * Adds the vector field parameters to the Redis command arguments. * This method is used internally when creating the search index. * * @param args the command arguments to add parameters to */ @Override public void addParams(CommandArguments args) { args.addParams(fieldName); args.add(VECTOR); args.add(algorithm); args.add(attributes.size() << 1); attributes.forEach((name, value) -> args.add(name).add(value)); if (indexMissing) { args.add(INDEXMISSING); } } /** * Creates a new Builder instance for constructing VectorField objects using the builder pattern. * The builder pattern provides a fluent interface for setting field properties and is especially * useful when dealing with complex vector field configurations. * * @return a new Builder instance */ public static Builder builder() { return new Builder(); } /** * Builder class for constructing VectorField instances using the builder pattern. * Provides a fluent interface for setting vector field properties and attributes. * *

Example usage: *

{@code
   * VectorField field = VectorField.builder()
   *     .fieldName("product_embedding")
   *     .algorithm(VectorAlgorithm.HNSW)
   *     .addAttribute("TYPE", "FLOAT32")
   *     .addAttribute("DIM", 768)
   *     .addAttribute("DISTANCE_METRIC", "COSINE")
   *     .addAttribute("M", 32)
   *     .addAttribute("EF_CONSTRUCTION", 200)
   *     .build();
   * }
*/ public static class Builder { private FieldName fieldName; private VectorAlgorithm algorithm; private Map attributes; /** * Private constructor to enforce use of the static builder() method. */ private Builder() { } /** * Builds and returns a new VectorField instance with the configured properties. * * @return a new VectorField instance * @throws IllegalArgumentException if required parameters (fieldName, algorithm, or attributes) are not set */ public VectorField build() { if (fieldName == null || algorithm == null || attributes == null || attributes.isEmpty()) { throw new IllegalArgumentException("All required VectorField parameters are not set."); } return new VectorField(fieldName, algorithm, attributes); } /** * Sets the field name for the vector field. * * @param fieldName the name of the vector field in the index * @return this Builder instance for method chaining */ public Builder fieldName(String fieldName) { this.fieldName = FieldName.of(fieldName); return this; } /** * Sets the field name using a FieldName object. * * @param fieldName the FieldName object containing the field name and optional alias * @return this Builder instance for method chaining */ public Builder fieldName(FieldName fieldName) { this.fieldName = fieldName; return this; } /** * Sets an alias for the field that can be used in queries. * * @param attribute the alias name to use for this field in queries * @return this Builder instance for method chaining */ public Builder as(String attribute) { this.fieldName.as(attribute); return this; } /** * Sets the vector indexing algorithm to use. * * @param algorithm the vector algorithm (FLAT, HNSW, or SVS_VAMANA) * @return this Builder instance for method chaining */ public Builder algorithm(VectorAlgorithm algorithm) { this.algorithm = algorithm; return this; } /** * Sets all vector field attributes at once, replacing any previously set attributes. * * @param attributes a map of attribute names to values * @return this Builder instance for method chaining */ public Builder attributes(Map attributes) { this.attributes = attributes; return this; } /** * Adds a single attribute to the vector field configuration. * If this is the first attribute added, initializes the attributes map. * * @param name the attribute name * @param value the attribute value * @return this Builder instance for method chaining */ public Builder addAttribute(String name, Object value) { if (this.attributes == null) { this.attributes = new LinkedHashMap<>(); } this.attributes.put(name, value); return this; } } } ================================================ FILE: src/main/java/redis/clients/jedis/timeseries/AggregationType.java ================================================ package redis.clients.jedis.timeseries; import java.util.Locale; import redis.clients.jedis.args.Rawable; import redis.clients.jedis.util.SafeEncoder; public enum AggregationType implements Rawable { AVG, SUM, MIN, MAX, RANGE, COUNT, FIRST, LAST, STD_P("STD.P"), STD_S("STD.S"), VAR_P("VAR.P"), VAR_S("VAR.S"), TWA, /** * Count the number of NaN values in the bucket. * @since RedisTimeSeries 8.6.0 */ COUNTNAN, /** * Count all values in the bucket, including NaN values. * @since RedisTimeSeries 8.6.0 */ COUNTALL; private final byte[] raw; private AggregationType() { raw = SafeEncoder.encode(name()); } private AggregationType(String alt) { raw = SafeEncoder.encode(alt); } @Override public byte[] getRaw() { return raw; } public static AggregationType safeValueOf(String str) { try { return AggregationType.valueOf(str.replace('.', '_').toUpperCase(Locale.ENGLISH)); } catch (IllegalArgumentException iae) { return null; } } } ================================================ FILE: src/main/java/redis/clients/jedis/timeseries/DuplicatePolicy.java ================================================ package redis.clients.jedis.timeseries; import redis.clients.jedis.args.Rawable; import redis.clients.jedis.util.SafeEncoder; /** * Policy that will define handling of duplicate samples. */ public enum DuplicatePolicy implements Rawable { /** * An error will occur for any out of order sample */ BLOCK, /** * Ignore the new value */ FIRST, /** * Override with latest value */ LAST, /** * Only override if the value is lower than the existing value */ MIN, /** * Only override if the value is higher than the existing value */ MAX, /** * If a previous sample exists, add the new sample to it so that the updated value is */ SUM; private final byte[] raw; private DuplicatePolicy() { raw = SafeEncoder.encode(name()); } @Override public byte[] getRaw() { return raw; } } ================================================ FILE: src/main/java/redis/clients/jedis/timeseries/EncodingFormat.java ================================================ package redis.clients.jedis.timeseries; import redis.clients.jedis.args.Rawable; import redis.clients.jedis.util.SafeEncoder; /** * Specifies the series samples encoding format. */ public enum EncodingFormat implements Rawable { COMPRESSED, UNCOMPRESSED; private final byte[] raw; private EncodingFormat() { raw = SafeEncoder.encode(name()); } @Override public byte[] getRaw() { return raw; } } ================================================ FILE: src/main/java/redis/clients/jedis/timeseries/RedisTimeSeriesCommands.java ================================================ package redis.clients.jedis.timeseries; import java.util.List; import java.util.Map; public interface RedisTimeSeriesCommands { /** * {@code TS.CREATE key} * * @param key */ String tsCreate(String key); /** * {@code TS.CREATE key [RETENTION retentionTime] [ENCODING [UNCOMPRESSED|COMPRESSED]] [CHUNK_SIZE size] [DUPLICATE_POLICY policy] [LABELS label value..]} * * @param key * @param createParams */ String tsCreate(String key, TSCreateParams createParams); /** * {@code TS.DEL key fromTimestamp toTimestamp} * * @param key * @param fromTimestamp * @param toTimestamp * @return The number of samples that were removed */ long tsDel(String key, long fromTimestamp, long toTimestamp); /** * {@code TS.ALTER key [RETENTION retentionTime] [LABELS label value..]} * * @param key * @param alterParams * @return OK */ String tsAlter(String key, TSAlterParams alterParams); /** * {@code TS.ADD key * value} * * @param key * @param value * @return timestamp */ long tsAdd(String key, double value); /** * {@code TS.ADD key timestamp value} * * @param key * @param timestamp * @param value * @return timestamp */ long tsAdd(String key, long timestamp, double value); /** * @param key * @param timestamp * @param value * @param createParams * @return timestamp * @deprecated Use {@link RedisTimeSeriesCommands#tsAdd(java.lang.String, long, double, redis.clients.jedis.timeseries.TSAddParams)}. */ @Deprecated long tsAdd(String key, long timestamp, double value, TSCreateParams createParams); /** * {@code TS.ADD key timestamp value * [RETENTION retentionTime] * [ENCODING ] * [CHUNK_SIZE size] * [DUPLICATE_POLICY policy] * [ON_DUPLICATE policy_ovr] * [LABELS label value..]} * * @param key * @param timestamp * @param value * @param addParams * @return timestamp */ long tsAdd(String key, long timestamp, double value, TSAddParams addParams); /** * {@code TS.MADD key timestamp value [key timestamp value ...]} * * @param entries key, timestamp, value * @return timestamps */ List tsMAdd(Map.Entry... entries); long tsIncrBy(String key, double value); long tsIncrBy(String key, double value, long timestamp); /** * {@code TS.INCRBY key addend * [TIMESTAMP timestamp] * [RETENTION retentionPeriod] * [ENCODING ] * [CHUNK_SIZE size] * [DUPLICATE_POLICY policy] * [IGNORE ignoreMaxTimediff ignoreMaxValDiff] * [LABELS [label value ...]]} * * @param key * @param addend * @param incrByParams * @return timestamp */ long tsIncrBy(String key, double addend, TSIncrByParams incrByParams); long tsDecrBy(String key, double value); long tsDecrBy(String key, double value, long timestamp); /** * {@code TS.DECRBY key subtrahend * [TIMESTAMP timestamp] * [RETENTION retentionPeriod] * [ENCODING ] * [CHUNK_SIZE size] * [DUPLICATE_POLICY policy] * [IGNORE ignoreMaxTimediff ignoreMaxValDiff] * [LABELS [label value ...]]} * * @param key * @param subtrahend * @param decrByParams * @return timestamp */ long tsDecrBy(String key, double subtrahend, TSDecrByParams decrByParams); /** * {@code TS.RANGE key fromTimestamp toTimestamp} * * @param key * @param fromTimestamp * @param toTimestamp * @return range elements */ List tsRange(String key, long fromTimestamp, long toTimestamp); /** * {@code TS.RANGE key fromTimestamp toTimestamp * [LATEST] * [FILTER_BY_TS ts...] * [FILTER_BY_VALUE min max] * [COUNT count] * [[ALIGN value] AGGREGATION aggregator bucketDuration [BUCKETTIMESTAMP bt] [EMPTY]]} * * @param key * @param rangeParams * @return range elements */ List tsRange(String key, TSRangeParams rangeParams); /** * {@code TS.REVRANGE key fromTimestamp toTimestamp} * * @param key * @param fromTimestamp * @param toTimestamp * @return range elements */ List tsRevRange(String key, long fromTimestamp, long toTimestamp); /** * {@code TS.REVRANGE key fromTimestamp toTimestamp * [LATEST] * [FILTER_BY_TS TS...] * [FILTER_BY_VALUE min max] * [COUNT count] * [[ALIGN value] AGGREGATION aggregator bucketDuration [BUCKETTIMESTAMP bt] [EMPTY]]} * * @param key * @param rangeParams * @return range elements */ List tsRevRange(String key, TSRangeParams rangeParams); /** * {@code TS.MRANGE fromTimestamp toTimestamp FILTER filter...} * * @param fromTimestamp * @param toTimestamp * @param filters * @return multi range elements */ Map tsMRange(long fromTimestamp, long toTimestamp, String... filters); /** * {@code TS.MRANGE fromTimestamp toTimestamp * [LATEST] * [FILTER_BY_TS ts...] * [FILTER_BY_VALUE min max] * [WITHLABELS | SELECTED_LABELS label...] * [COUNT count] * [[ALIGN value] AGGREGATION aggregator bucketDuration [BUCKETTIMESTAMP bt] [EMPTY]] * FILTER filter... * [GROUPBY label REDUCE reducer]} * * @param multiRangeParams * @return multi range elements */ Map tsMRange(TSMRangeParams multiRangeParams); /** * {@code TS.MREVRANGE fromTimestamp toTimestamp FILTER filter...} * * @param fromTimestamp * @param toTimestamp * @param filters * @return multi range elements */ Map tsMRevRange(long fromTimestamp, long toTimestamp, String... filters); /** * {@code TS.MREVRANGE fromTimestamp toTimestamp * [LATEST] * [FILTER_BY_TS TS...] * [FILTER_BY_VALUE min max] * [WITHLABELS | SELECTED_LABELS label...] * [COUNT count] * [[ALIGN value] AGGREGATION aggregator bucketDuration [BUCKETTIMESTAMP bt] [EMPTY]] * FILTER filter... * [GROUPBY label REDUCE reducer]} * * @param multiRangeParams * @return multi range elements */ Map tsMRevRange(TSMRangeParams multiRangeParams); /** * {@code TS.GET key} * * @param key the key * @return the element */ TSElement tsGet(String key); /** * {@code TS.GET key [LATEST]} * * @param key the key * @param getParams optional arguments * @return the element */ TSElement tsGet(String key, TSGetParams getParams); /** * {@code TS.MGET [LATEST] [ WITHLABELS | SELECTED_LABELS label...] FILTER filter...} * * @param multiGetParams optional arguments * @param filters secondary indexes * @return multi get elements */ Map tsMGet(TSMGetParams multiGetParams, String... filters); /** * {@code TS.CREATERULE sourceKey destKey AGGREGATION aggregationType timeBucket} * * @param sourceKey * @param destKey * @param aggregationType * @param timeBucket */ String tsCreateRule(String sourceKey, String destKey, AggregationType aggregationType, long timeBucket); /** * {@code TS.CREATERULE sourceKey destKey AGGREGATION aggregationType bucketDuration [alignTimestamp]} * * @param sourceKey * @param destKey * @param aggregationType * @param bucketDuration * @param alignTimestamp */ String tsCreateRule(String sourceKey, String destKey, AggregationType aggregationType, long bucketDuration, long alignTimestamp); /** * {@code TS.DELETERULE sourceKey destKey} * * @param sourceKey * @param destKey */ String tsDeleteRule(String sourceKey, String destKey); /** * {@code TS.QUERYINDEX filter...} * * @param filters * @return list of timeseries keys */ List tsQueryIndex(String... filters); TSInfo tsInfo(String key); TSInfo tsInfoDebug(String key); } ================================================ FILE: src/main/java/redis/clients/jedis/timeseries/RedisTimeSeriesPipelineCommands.java ================================================ package redis.clients.jedis.timeseries; import java.util.List; import java.util.Map; import redis.clients.jedis.Response; public interface RedisTimeSeriesPipelineCommands { Response tsCreate(String key); Response tsCreate(String key, TSCreateParams createParams); Response tsDel(String key, long fromTimestamp, long toTimestamp); Response tsAlter(String key, TSAlterParams alterParams); Response tsAdd(String key, double value); Response tsAdd(String key, long timestamp, double value); @Deprecated Response tsAdd(String key, long timestamp, double value, TSCreateParams createParams); Response tsAdd(String key, long timestamp, double value, TSAddParams addParams); Response> tsMAdd(Map.Entry... entries); Response tsIncrBy(String key, double value); Response tsIncrBy(String key, double value, long timestamp); Response tsIncrBy(String key, double addend, TSIncrByParams incrByParams); Response tsDecrBy(String key, double value); Response tsDecrBy(String key, double value, long timestamp); Response tsDecrBy(String key, double subtrahend, TSDecrByParams decrByParams); Response> tsRange(String key, long fromTimestamp, long toTimestamp); Response> tsRange(String key, TSRangeParams rangeParams); Response> tsRevRange(String key, long fromTimestamp, long toTimestamp); Response> tsRevRange(String key, TSRangeParams rangeParams); Response> tsMRange(long fromTimestamp, long toTimestamp, String... filters); Response> tsMRange(TSMRangeParams multiRangeParams); Response> tsMRevRange(long fromTimestamp, long toTimestamp, String... filters); Response> tsMRevRange(TSMRangeParams multiRangeParams); Response tsGet(String key); Response tsGet(String key, TSGetParams getParams); Response> tsMGet(TSMGetParams multiGetParams, String... filters); Response tsCreateRule(String sourceKey, String destKey, AggregationType aggregationType, long timeBucket); Response tsCreateRule(String sourceKey, String destKey, AggregationType aggregationType, long bucketDuration, long alignTimestamp); Response tsDeleteRule(String sourceKey, String destKey); Response> tsQueryIndex(String... filters); Response tsInfo(String key); Response tsInfoDebug(String key); } ================================================ FILE: src/main/java/redis/clients/jedis/timeseries/TSAddParams.java ================================================ package redis.clients.jedis.timeseries; import static redis.clients.jedis.Protocol.toByteArray; import static redis.clients.jedis.timeseries.TimeSeriesProtocol.TimeSeriesKeyword.*; import java.util.LinkedHashMap; import java.util.Map; import java.util.Objects; import redis.clients.jedis.CommandArguments; import redis.clients.jedis.params.IParams; /** * Represents optional arguments of TS.ADD command. */ public class TSAddParams implements IParams { private Long retentionPeriod; private EncodingFormat encoding; private Long chunkSize; private DuplicatePolicy duplicatePolicy; private DuplicatePolicy onDuplicate; private boolean ignore; private long ignoreMaxTimediff; private double ignoreMaxValDiff; private Map labels; public TSAddParams() { } public static TSAddParams addParams() { return new TSAddParams(); } public TSAddParams retention(long retentionPeriod) { this.retentionPeriod = retentionPeriod; return this; } public TSAddParams encoding(EncodingFormat encoding) { this.encoding = encoding; return this; } public TSAddParams chunkSize(long chunkSize) { this.chunkSize = chunkSize; return this; } public TSAddParams duplicatePolicy(DuplicatePolicy duplicatePolicy) { this.duplicatePolicy = duplicatePolicy; return this; } public TSAddParams onDuplicate(DuplicatePolicy onDuplicate) { this.onDuplicate = onDuplicate; return this; } public TSAddParams ignore(long maxTimediff, double maxValDiff) { this.ignore = true; this.ignoreMaxTimediff = maxTimediff; this.ignoreMaxValDiff = maxValDiff; return this; } /** * Set label-value pairs * * @param labels label-value pairs * @return the object itself */ public TSAddParams labels(Map labels) { this.labels = labels; return this; } /** * Add label-value pair. Multiple pairs can be added through chaining. * @param label * @param value * @return the object itself */ public TSAddParams label(String label, String value) { if (this.labels == null) { this.labels = new LinkedHashMap<>(); } this.labels.put(label, value); return this; } @Override public void addParams(CommandArguments args) { if (retentionPeriod != null) { args.add(RETENTION).add(toByteArray(retentionPeriod)); } if (encoding != null) { args.add(ENCODING).add(encoding); } if (chunkSize != null) { args.add(CHUNK_SIZE).add(toByteArray(chunkSize)); } if (duplicatePolicy != null) { args.add(DUPLICATE_POLICY).add(duplicatePolicy); } if (duplicatePolicy != null) { args.add(DUPLICATE_POLICY).add(duplicatePolicy); } if (onDuplicate != null) { args.add(ON_DUPLICATE).add(onDuplicate); } if (ignore) { args.add(IGNORE).add(ignoreMaxTimediff).add(ignoreMaxValDiff); } if (labels != null) { args.add(LABELS); labels.entrySet().forEach((entry) -> args.add(entry.getKey()).add(entry.getValue())); } } @Override public boolean equals(Object o) { if (this == o) { return true; } if (o == null || getClass() != o.getClass()) { return false; } TSAddParams that = (TSAddParams) o; return ignore == that.ignore && ignoreMaxTimediff == that.ignoreMaxTimediff && Double.compare(ignoreMaxValDiff, that.ignoreMaxValDiff) == 0 && Objects.equals(retentionPeriod, that.retentionPeriod) && encoding == that.encoding && Objects.equals(chunkSize, that.chunkSize) && duplicatePolicy == that.duplicatePolicy && onDuplicate == that.onDuplicate && Objects.equals(labels, that.labels); } @Override public int hashCode() { int result = Objects.hashCode(retentionPeriod); result = 31 * result + Objects.hashCode(encoding); result = 31 * result + Objects.hashCode(chunkSize); result = 31 * result + Objects.hashCode(duplicatePolicy); result = 31 * result + Objects.hashCode(onDuplicate); result = 31 * result + Boolean.hashCode(ignore); result = 31 * result + Long.hashCode(ignoreMaxTimediff); result = 31 * result + Double.hashCode(ignoreMaxValDiff); result = 31 * result + Objects.hashCode(labels); return result; } } ================================================ FILE: src/main/java/redis/clients/jedis/timeseries/TSAlterParams.java ================================================ package redis.clients.jedis.timeseries; import static redis.clients.jedis.Protocol.toByteArray; import static redis.clients.jedis.timeseries.TimeSeriesProtocol.TimeSeriesKeyword.*; import java.util.Collections; import java.util.LinkedHashMap; import java.util.Map; import java.util.Objects; import redis.clients.jedis.CommandArguments; import redis.clients.jedis.params.IParams; /** * Represents optional arguments of TS.ALTER command. */ public class TSAlterParams implements IParams { private Long retentionPeriod; private Long chunkSize; private DuplicatePolicy duplicatePolicy; private boolean ignore; private long ignoreMaxTimediff; private double ignoreMaxValDiff; private Map labels; public TSAlterParams() { } public static TSAlterParams alterParams() { return new TSAlterParams(); } public TSAlterParams retention(long retentionPeriod) { this.retentionPeriod = retentionPeriod; return this; } public TSAlterParams chunkSize(long chunkSize) { this.chunkSize = chunkSize; return this; } public TSAlterParams duplicatePolicy(DuplicatePolicy duplicatePolicy) { this.duplicatePolicy = duplicatePolicy; return this; } public TSAlterParams ignore(long maxTimediff, double maxValDiff) { this.ignore = true; this.ignoreMaxTimediff = maxTimediff; this.ignoreMaxValDiff = maxValDiff; return this; } /** * Set label-value pairs * * @param labels label-value pairs * @return the object itself */ public TSAlterParams labels(Map labels) { this.labels = labels; return this; } /** * Add label-value pair. Multiple pairs can be added through chaining. * @param label * @param value * @return the object itself */ public TSAlterParams label(String label, String value) { if (this.labels == null) { this.labels = new LinkedHashMap<>(); } this.labels.put(label, value); return this; } public TSAlterParams labelsReset() { return this.labels(Collections.emptyMap()); } @Override public void addParams(CommandArguments args) { if (retentionPeriod != null) { args.add(RETENTION).add(toByteArray(retentionPeriod)); } if (chunkSize != null) { args.add(CHUNK_SIZE).add(toByteArray(chunkSize)); } if (duplicatePolicy != null) { args.add(DUPLICATE_POLICY).add(duplicatePolicy); } if (ignore) { args.add(IGNORE).add(ignoreMaxTimediff).add(ignoreMaxValDiff); } if (labels != null) { args.add(LABELS); labels.entrySet().forEach((entry) -> args.add(entry.getKey()).add(entry.getValue())); } } @Override public boolean equals(Object o) { if (this == o) { return true; } if (o == null || getClass() != o.getClass()) { return false; } TSAlterParams that = (TSAlterParams) o; return ignore == that.ignore && ignoreMaxTimediff == that.ignoreMaxTimediff && Double.compare(ignoreMaxValDiff, that.ignoreMaxValDiff) == 0 && Objects.equals(retentionPeriod, that.retentionPeriod) && Objects.equals(chunkSize, that.chunkSize) && duplicatePolicy == that.duplicatePolicy && Objects.equals(labels, that.labels); } @Override public int hashCode() { int result = Objects.hashCode(retentionPeriod); result = 31 * result + Objects.hashCode(chunkSize); result = 31 * result + Objects.hashCode(duplicatePolicy); result = 31 * result + Boolean.hashCode(ignore); result = 31 * result + Long.hashCode(ignoreMaxTimediff); result = 31 * result + Double.hashCode(ignoreMaxValDiff); result = 31 * result + Objects.hashCode(labels); return result; } } ================================================ FILE: src/main/java/redis/clients/jedis/timeseries/TSArithByParams.java ================================================ package redis.clients.jedis.timeseries; import static redis.clients.jedis.Protocol.toByteArray; import static redis.clients.jedis.timeseries.TimeSeriesProtocol.TimeSeriesKeyword.*; import java.util.LinkedHashMap; import java.util.Map; import java.util.Objects; import redis.clients.jedis.CommandArguments; import redis.clients.jedis.params.IParams; /** * Represents optional arguments of TS.INCRBY or TS.DECRBY commands. */ class TSArithByParams> implements IParams { private Long timestamp; private Long retentionPeriod; private EncodingFormat encoding; private Long chunkSize; private DuplicatePolicy duplicatePolicy; private boolean ignore; private long ignoreMaxTimediff; private double ignoreMaxValDiff; private Map labels; TSArithByParams() { } public T timestamp(long timestamp) { this.timestamp = timestamp; return (T) this; } public T retention(long retentionPeriod) { this.retentionPeriod = retentionPeriod; return (T) this; } public T encoding(EncodingFormat encoding) { this.encoding = encoding; return (T) this; } public T chunkSize(long chunkSize) { this.chunkSize = chunkSize; return (T) this; } public T duplicatePolicy(DuplicatePolicy duplicatePolicy) { this.duplicatePolicy = duplicatePolicy; return (T) this; } public T ignore(long maxTimediff, double maxValDiff) { this.ignore = true; this.ignoreMaxTimediff = maxTimediff; this.ignoreMaxValDiff = maxValDiff; return (T) this; } /** * Set label-value pairs * * @param labels label-value pairs * @return the object itself */ public T labels(Map labels) { this.labels = labels; return (T) this; } /** * Add label-value pair. Multiple pairs can be added through chaining. * @param label * @param value * @return the object itself */ public T label(String label, String value) { if (this.labels == null) { this.labels = new LinkedHashMap<>(); } this.labels.put(label, value); return (T) this; } @Override public void addParams(CommandArguments args) { if (timestamp != null) { args.add(TIMESTAMP).add(timestamp); } if (retentionPeriod != null) { args.add(RETENTION).add(toByteArray(retentionPeriod)); } if (encoding != null) { args.add(ENCODING).add(encoding); } if (chunkSize != null) { args.add(CHUNK_SIZE).add(toByteArray(chunkSize)); } if (duplicatePolicy != null) { args.add(DUPLICATE_POLICY).add(duplicatePolicy); } if (ignore) { args.add(IGNORE).add(ignoreMaxTimediff).add(ignoreMaxValDiff); } if (labels != null) { args.add(LABELS); labels.entrySet().forEach((entry) -> args.add(entry.getKey()).add(entry.getValue())); } } @Override public boolean equals(Object o) { if (this == o) { return true; } if (o == null || getClass() != o.getClass()) { return false; } TSArithByParams that = (TSArithByParams) o; return ignore == that.ignore && ignoreMaxTimediff == that.ignoreMaxTimediff && Double.compare(ignoreMaxValDiff, that.ignoreMaxValDiff) == 0 && Objects.equals(timestamp, that.timestamp) && Objects.equals(retentionPeriod, that.retentionPeriod) && encoding == that.encoding && Objects.equals(chunkSize, that.chunkSize) && duplicatePolicy == that.duplicatePolicy && Objects.equals(labels, that.labels); } @Override public int hashCode() { int result = Objects.hashCode(timestamp); result = 31 * result + Objects.hashCode(retentionPeriod); result = 31 * result + Objects.hashCode(encoding); result = 31 * result + Objects.hashCode(chunkSize); result = 31 * result + Objects.hashCode(duplicatePolicy); result = 31 * result + Boolean.hashCode(ignore); result = 31 * result + Long.hashCode(ignoreMaxTimediff); result = 31 * result + Double.hashCode(ignoreMaxValDiff); result = 31 * result + Objects.hashCode(labels); return result; } } ================================================ FILE: src/main/java/redis/clients/jedis/timeseries/TSCreateParams.java ================================================ package redis.clients.jedis.timeseries; import static redis.clients.jedis.Protocol.toByteArray; import static redis.clients.jedis.timeseries.TimeSeriesProtocol.TimeSeriesKeyword.*; import java.util.LinkedHashMap; import java.util.Map; import java.util.Objects; import redis.clients.jedis.CommandArguments; import redis.clients.jedis.params.IParams; /** * Represents optional arguments of TS.CREATE command. */ public class TSCreateParams implements IParams { private Long retentionPeriod; private EncodingFormat encoding; private Long chunkSize; private DuplicatePolicy duplicatePolicy; private boolean ignore; private long ignoreMaxTimediff; private double ignoreMaxValDiff; private Map labels; public TSCreateParams() { } public static TSCreateParams createParams() { return new TSCreateParams(); } public TSCreateParams retention(long retentionPeriod) { this.retentionPeriod = retentionPeriod; return this; } // TODO: deprecate public TSCreateParams uncompressed() { return encoding(EncodingFormat.UNCOMPRESSED); } // TODO: deprecate public TSCreateParams compressed() { return encoding(EncodingFormat.COMPRESSED); } public TSCreateParams encoding(EncodingFormat encoding) { this.encoding = encoding; return this; } public TSCreateParams chunkSize(long chunkSize) { this.chunkSize = chunkSize; return this; } public TSCreateParams duplicatePolicy(DuplicatePolicy duplicatePolicy) { this.duplicatePolicy = duplicatePolicy; return this; } public TSCreateParams ignore(long maxTimediff, double maxValDiff) { this.ignore = true; this.ignoreMaxTimediff = maxTimediff; this.ignoreMaxValDiff = maxValDiff; return this; } /** * Set label-value pairs * * @param labels label-value pairs * @return the object itself */ public TSCreateParams labels(Map labels) { this.labels = labels; return this; } /** * Add label-value pair. Multiple pairs can be added through chaining. * @param label * @param value * @return the object itself */ public TSCreateParams label(String label, String value) { if (this.labels == null) { this.labels = new LinkedHashMap<>(); } this.labels.put(label, value); return this; } @Override public void addParams(CommandArguments args) { if (retentionPeriod != null) { args.add(RETENTION).add(toByteArray(retentionPeriod)); } if (encoding != null) { args.add(ENCODING).add(encoding); } if (chunkSize != null) { args.add(CHUNK_SIZE).add(toByteArray(chunkSize)); } if (duplicatePolicy != null) { args.add(DUPLICATE_POLICY).add(duplicatePolicy); } if (ignore) { args.add(IGNORE).add(ignoreMaxTimediff).add(ignoreMaxValDiff); } if (labels != null) { args.add(LABELS); labels.entrySet().forEach((entry) -> args.add(entry.getKey()).add(entry.getValue())); } } @Override public boolean equals(Object o) { if (this == o) { return true; } if (o == null || getClass() != o.getClass()) { return false; } TSCreateParams that = (TSCreateParams) o; return ignore == that.ignore && ignoreMaxTimediff == that.ignoreMaxTimediff && Double.compare(ignoreMaxValDiff, that.ignoreMaxValDiff) == 0 && Objects.equals(retentionPeriod, that.retentionPeriod) && encoding == that.encoding && Objects.equals(chunkSize, that.chunkSize) && duplicatePolicy == that.duplicatePolicy && Objects.equals(labels, that.labels); } @Override public int hashCode() { int result = Objects.hashCode(retentionPeriod); result = 31 * result + Objects.hashCode(encoding); result = 31 * result + Objects.hashCode(chunkSize); result = 31 * result + Objects.hashCode(duplicatePolicy); result = 31 * result + Boolean.hashCode(ignore); result = 31 * result + Long.hashCode(ignoreMaxTimediff); result = 31 * result + Double.hashCode(ignoreMaxValDiff); result = 31 * result + Objects.hashCode(labels); return result; } } ================================================ FILE: src/main/java/redis/clients/jedis/timeseries/TSDecrByParams.java ================================================ package redis.clients.jedis.timeseries; /** * Represents optional arguments of TS.DECRBY command. */ public class TSDecrByParams extends TSArithByParams { public TSDecrByParams() { } public static TSDecrByParams decrByParams() { return new TSDecrByParams(); } } ================================================ FILE: src/main/java/redis/clients/jedis/timeseries/TSElement.java ================================================ package redis.clients.jedis.timeseries; public class TSElement { private final long timestamp; private final double value; public TSElement(long timestamp, double value) { this.timestamp = timestamp; this.value = value; } public long getTimestamp() { return timestamp; } public double getValue() { return value; } @Override public int hashCode() { return 31 * Long.hashCode(timestamp) + Long.hashCode(Double.doubleToLongBits(value)); } @Override public boolean equals(Object obj) { if (obj == null) return false; if (obj == this) return true; if (!(obj instanceof TSElement)) return false; TSElement other = (TSElement) obj; return this.timestamp == other.timestamp && this.value == other.value; } @Override public String toString() { return "(" + timestamp + ":" + value + ")"; } } ================================================ FILE: src/main/java/redis/clients/jedis/timeseries/TSGetParams.java ================================================ package redis.clients.jedis.timeseries; import static redis.clients.jedis.timeseries.TimeSeriesProtocol.TimeSeriesKeyword.LATEST; import redis.clients.jedis.CommandArguments; import redis.clients.jedis.params.IParams; /** * Represents optional arguments of TS.GET command. */ public class TSGetParams implements IParams { private boolean latest; public static TSGetParams getParams() { return new TSGetParams(); } public TSGetParams latest() { this.latest = true; return this; } @Override public void addParams(CommandArguments args) { if (latest) { args.add(LATEST); } } @Override public boolean equals(Object o) { if (this == o) { return true; } if (o == null || getClass() != o.getClass()) { return false; } TSGetParams that = (TSGetParams) o; return latest == that.latest; } @Override public int hashCode() { return Boolean.hashCode(latest); } } ================================================ FILE: src/main/java/redis/clients/jedis/timeseries/TSIncrByParams.java ================================================ package redis.clients.jedis.timeseries; /** * Represents optional arguments of TS.INCRBY command. */ public class TSIncrByParams extends TSArithByParams { public TSIncrByParams() { } public static TSIncrByParams incrByParams() { return new TSIncrByParams(); } } ================================================ FILE: src/main/java/redis/clients/jedis/timeseries/TSInfo.java ================================================ package redis.clients.jedis.timeseries; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.stream.Collectors; import redis.clients.jedis.Builder; import redis.clients.jedis.BuilderFactory; import redis.clients.jedis.util.DoublePrecision; import redis.clients.jedis.util.KeyValue; import redis.clients.jedis.util.SafeEncoder; public class TSInfo { private static final String DUPLICATE_POLICY_PROPERTY = "duplicatePolicy"; private static final String LABELS_PROPERTY = "labels"; private static final String RULES_PROPERTY = "rules"; private static final String CHUNKS_PROPERTY = "Chunks"; private static final String CHUNKS_BYTES_PER_SAMPLE_PROPERTY = "bytesPerSample"; private final Map properties; private final Map labels; private final Map rules; private final List> chunks; private TSInfo(Map properties, Map labels, Map rules, List> chunks) { this.properties = properties; this.labels = labels; this.rules = rules; this.chunks = chunks; } public Map getProperties() { return properties; } public Object getProperty(String property) { return properties.get(property); } public Long getIntegerProperty(String property) { return (Long) properties.get(property); } public Map getLabels() { return labels; } public String getLabel(String label) { return labels.get(label); } public Map getRules() { return rules; } public Rule getRule(String rule) { return rules.get(rule); } public List> getChunks() { return chunks; } public static Builder TIMESERIES_INFO = new Builder() { @Override public TSInfo build(Object data) { List list = (List) data; Map properties = new HashMap<>(); Map labels = null; Map rules = null; List> chunks = null; for (int i = 0; i < list.size(); i += 2) { String prop = SafeEncoder.encode((byte[]) list.get(i)); Object value = list.get(i + 1); if (value instanceof List) { switch (prop) { case LABELS_PROPERTY: labels = BuilderFactory.STRING_MAP_FROM_PAIRS.build(value); value = labels; break; case RULES_PROPERTY: List rulesDataList = (List) value; List> rulesValueList = new ArrayList<>(rulesDataList.size()); rules = new HashMap<>(rulesDataList.size()); for (Object ruleData : rulesDataList) { List encodedRule = (List) SafeEncoder.encodeObject(ruleData); rulesValueList.add(encodedRule); rules.put((String) encodedRule.get(0), new Rule((String) encodedRule.get(0), (Long) encodedRule.get(1), AggregationType.safeValueOf((String) encodedRule.get(2)), (Long) encodedRule.get(3))); } value = rulesValueList; break; case CHUNKS_PROPERTY: List chunksDataList = (List) value; List> chunksValueList = new ArrayList<>(chunksDataList.size()); chunks = new ArrayList<>(chunksDataList.size()); for (Object chunkData : chunksDataList) { Map chunk = BuilderFactory.ENCODED_OBJECT_MAP.build(chunkData); chunksValueList.add(new HashMap<>(chunk)); if (chunk.containsKey(CHUNKS_BYTES_PER_SAMPLE_PROPERTY)) { chunk.put(CHUNKS_BYTES_PER_SAMPLE_PROPERTY, DoublePrecision.parseEncodedFloatingPointNumber(chunk.get(CHUNKS_BYTES_PER_SAMPLE_PROPERTY))); } chunks.add(chunk); } value = chunksValueList; break; default: value = SafeEncoder.encodeObject(value); break; } } else if (value instanceof byte[]) { value = SafeEncoder.encode((byte[]) value); if (DUPLICATE_POLICY_PROPERTY.equals(prop)) { try { value = DuplicatePolicy.valueOf(((String) value).toUpperCase()); } catch (Exception e) { } } } properties.put(prop, value); } return new TSInfo(properties, labels, rules, chunks); } }; public static Builder TIMESERIES_INFO_RESP3 = new Builder() { @Override public TSInfo build(Object data) { List list = (List) data; Map properties = new HashMap<>(); Map labels = null; Map rules = null; List> chunks = null; for (KeyValue propertyValue : list) { String prop = BuilderFactory.STRING.build(propertyValue.getKey()); Object value = propertyValue.getValue(); if (value instanceof List) { switch (prop) { case LABELS_PROPERTY: labels = BuilderFactory.STRING_MAP.build(value); value = labels; break; case RULES_PROPERTY: List rulesDataList = (List) value; Map> rulesValueMap = new HashMap<>(rulesDataList.size(), 1f); rules = new HashMap<>(rulesDataList.size()); for (KeyValue rkv : rulesDataList) { String ruleName = BuilderFactory.STRING.build(rkv.getKey()); List ruleValueList = BuilderFactory.ENCODED_OBJECT_LIST.build(rkv.getValue()); rulesValueMap.put(ruleName, ruleValueList); rules.put(ruleName, new Rule(ruleName, ruleValueList)); } value = rulesValueMap; break; case CHUNKS_PROPERTY: List> chunksDataList = (List>) value; List> chunksValueList = new ArrayList<>(chunksDataList.size()); chunks = new ArrayList<>(chunksDataList.size()); for (List chunkDataAsList : chunksDataList) { Map chunk = chunkDataAsList.stream() .collect(Collectors.toMap(kv -> BuilderFactory.STRING.build(kv.getKey()), kv -> BuilderFactory.ENCODED_OBJECT.build(kv.getValue()))); chunksValueList.add(chunk); chunks.add(chunk); } value = chunksValueList; break; default: value = SafeEncoder.encodeObject(value); break; } } else if (value instanceof byte[]) { value = BuilderFactory.STRING.build(value); if (DUPLICATE_POLICY_PROPERTY.equals(prop)) { try { value = DuplicatePolicy.valueOf(((String) value).toUpperCase()); } catch (Exception e) { } } } properties.put(prop, value); } return new TSInfo(properties, labels, rules, chunks); } }; public static class Rule { private final String compactionKey; private final long bucketDuration; private final AggregationType aggregator; private final long alignmentTimestamp; private Rule(String compaction, List encodedValues) { this(compaction, (Long) encodedValues.get(0), AggregationType.safeValueOf((String) encodedValues.get(1)), (Long) encodedValues.get(2)); } private Rule(String compaction, long bucket, AggregationType aggregation, long alignment) { this.compactionKey = compaction; this.bucketDuration = bucket; this.aggregator = aggregation; this.alignmentTimestamp = alignment; } public String getCompactionKey() { return compactionKey; } public long getBucketDuration() { return bucketDuration; } public AggregationType getAggregator() { return aggregator; } public long getAlignmentTimestamp() { return alignmentTimestamp; } } } ================================================ FILE: src/main/java/redis/clients/jedis/timeseries/TSMGetElement.java ================================================ package redis.clients.jedis.timeseries; import java.util.Map; import redis.clients.jedis.util.KeyValue; public class TSMGetElement extends KeyValue { private final Map labels; public TSMGetElement(String key, Map labels, TSElement value) { super(key, value); this.labels = labels; } public Map getLabels() { return labels; } public TSElement getElement() { return getValue(); } @Override public String toString() { return new StringBuilder().append(getClass().getSimpleName()) .append("{key=").append(getKey()) .append(", labels=").append(labels) .append(", element=").append(getElement()) .append('}').toString(); } } ================================================ FILE: src/main/java/redis/clients/jedis/timeseries/TSMGetParams.java ================================================ package redis.clients.jedis.timeseries; import static redis.clients.jedis.timeseries.TimeSeriesProtocol.TimeSeriesKeyword.LATEST; import static redis.clients.jedis.timeseries.TimeSeriesProtocol.TimeSeriesKeyword.SELECTED_LABELS; import static redis.clients.jedis.timeseries.TimeSeriesProtocol.TimeSeriesKeyword.WITHLABELS; import java.util.Arrays; import redis.clients.jedis.CommandArguments; import redis.clients.jedis.params.IParams; /** * Represents optional arguments of TS.MGET command. */ public class TSMGetParams implements IParams { private boolean latest; private boolean withLabels; private String[] selectedLabels; public static TSMGetParams multiGetParams() { return new TSMGetParams(); } public TSMGetParams latest() { this.latest = true; return this; } public TSMGetParams withLabels(boolean withLabels) { this.withLabels = withLabels; return this; } public TSMGetParams withLabels() { return withLabels(true); } public TSMGetParams selectedLabels(String... labels) { this.selectedLabels = labels; return this; } @Override public void addParams(CommandArguments args) { if (latest) { args.add(LATEST); } if (withLabels) { args.add(WITHLABELS); } else if (selectedLabels != null) { args.add(SELECTED_LABELS); for (String label : selectedLabels) { args.add(label); } } } @Override public boolean equals(Object o) { if (this == o) { return true; } if (o == null || getClass() != o.getClass()) { return false; } TSMGetParams that = (TSMGetParams) o; return latest == that.latest && withLabels == that.withLabels && Arrays.equals(selectedLabels, that.selectedLabels); } @Override public int hashCode() { int result = Boolean.hashCode(latest); result = 31 * result + Boolean.hashCode(withLabels); result = 31 * result + Arrays.hashCode(selectedLabels); return result; } } ================================================ FILE: src/main/java/redis/clients/jedis/timeseries/TSMRangeElements.java ================================================ package redis.clients.jedis.timeseries; import java.util.List; import java.util.Map; import redis.clients.jedis.util.KeyValue; public class TSMRangeElements extends KeyValue> { private final Map labels; private final List aggregators; private final List reducers; private final List sources; public TSMRangeElements(String key, Map labels, List value) { super(key, value); this.labels = labels; this.aggregators = null; this.reducers = null; this.sources = null; } public TSMRangeElements(String key, Map labels, List aggregators, List value) { super(key, value); this.labels = labels; this.aggregators = aggregators; this.reducers = null; this.sources = null; } public TSMRangeElements(String key, Map labels, List reducers, List sources, List value) { super(key, value); this.labels = labels; this.aggregators = null; this.reducers = reducers; this.sources = sources; } public Map getLabels() { return labels; } public List getAggregators() { return aggregators; } public List getReducers() { return reducers; } public List getSources() { return sources; } public List getElements() { return getValue(); } @Override public String toString() { StringBuilder sb = new StringBuilder().append(getClass().getSimpleName()) .append("{key=").append(getKey()).append(", labels=").append(labels); if (aggregators != null) { sb.append(", aggregators=").append(aggregators); } if (reducers != null && sources != null) { sb.append(", reducers").append(reducers).append(", sources").append(sources); } return sb.append(", elements=").append(getElements()).append('}').toString(); } } ================================================ FILE: src/main/java/redis/clients/jedis/timeseries/TSMRangeParams.java ================================================ package redis.clients.jedis.timeseries; import static redis.clients.jedis.Protocol.BYTES_TILDE; import static redis.clients.jedis.Protocol.toByteArray; import static redis.clients.jedis.timeseries.TimeSeriesProtocol.MINUS; import static redis.clients.jedis.timeseries.TimeSeriesProtocol.PLUS; import static redis.clients.jedis.timeseries.TimeSeriesProtocol.TimeSeriesKeyword.*; import static redis.clients.jedis.util.SafeEncoder.encode; import java.util.Arrays; import java.util.Objects; import redis.clients.jedis.CommandArguments; import redis.clients.jedis.params.IParams; /** * Represents optional arguments of TS.MRANGE and TS.MREVRANGE commands. */ public class TSMRangeParams implements IParams { private Long fromTimestamp; private Long toTimestamp; private boolean latest; private long[] filterByTimestamps; private double[] filterByValues; private boolean withLabels; private String[] selectedLabels; private Integer count; private byte[] align; private AggregationType aggregationType; private long bucketDuration; private byte[] bucketTimestamp; private boolean empty; private String[] filters; private String groupByLabel; private String groupByReduce; public TSMRangeParams(long fromTimestamp, long toTimestamp) { this.fromTimestamp = fromTimestamp; this.toTimestamp = toTimestamp; } public static TSMRangeParams multiRangeParams(long fromTimestamp, long toTimestamp) { return new TSMRangeParams(fromTimestamp, toTimestamp); } public TSMRangeParams() { } public static TSMRangeParams multiRangeParams() { return new TSMRangeParams(); } public TSMRangeParams fromTimestamp(long fromTimestamp) { this.fromTimestamp = fromTimestamp; return this; } public TSMRangeParams toTimestamp(long toTimestamp) { this.toTimestamp = toTimestamp; return this; } public TSMRangeParams latest() { this.latest = true; return this; } public TSMRangeParams filterByTS(long... timestamps) { this.filterByTimestamps = timestamps; return this; } public TSMRangeParams filterByValues(double min, double max) { this.filterByValues = new double[] {min, max}; return this; } public TSMRangeParams withLabels(boolean withLabels) { this.withLabels = withLabels; return this; } public TSMRangeParams withLabels() { return withLabels(true); } public TSMRangeParams selectedLabels(String... labels) { this.selectedLabels = labels; return this; } public TSMRangeParams count(int count) { this.count = count; return this; } private TSMRangeParams align(byte[] raw) { this.align = raw; return this; } /** * This requires AGGREGATION. */ public TSMRangeParams align(long timestamp) { return align(toByteArray(timestamp)); } /** * This requires AGGREGATION. */ public TSMRangeParams alignStart() { return align(MINUS); } /** * This requires AGGREGATION. */ public TSMRangeParams alignEnd() { return align(PLUS); } public TSMRangeParams aggregation(AggregationType aggregationType, long bucketDuration) { this.aggregationType = aggregationType; this.bucketDuration = bucketDuration; return this; } /** * This requires AGGREGATION. */ public TSMRangeParams bucketTimestamp(String bucketTimestamp) { this.bucketTimestamp = encode(bucketTimestamp); return this; } /** * This requires AGGREGATION. */ public TSMRangeParams bucketTimestampLow() { this.bucketTimestamp = MINUS; return this; } /** * This requires AGGREGATION. */ public TSMRangeParams bucketTimestampHigh() { this.bucketTimestamp = PLUS; return this; } /** * This requires AGGREGATION. */ public TSMRangeParams bucketTimestampMid() { this.bucketTimestamp = BYTES_TILDE; return this; } /** * This requires AGGREGATION. */ public TSMRangeParams empty() { this.empty = true; return this; } public TSMRangeParams filter(String... filters) { this.filters = filters; return this; } public TSMRangeParams groupBy(String label, String reduce) { this.groupByLabel = label; this.groupByReduce = reduce; return this; } @Override public void addParams(CommandArguments args) { if (filters == null) { throw new IllegalArgumentException("FILTER arguments must be set."); } if (fromTimestamp == null) { args.add(MINUS); } else { args.add(toByteArray(fromTimestamp)); } if (toTimestamp == null) { args.add(PLUS); } else { args.add(toByteArray(toTimestamp)); } if (latest) { args.add(LATEST); } if (filterByTimestamps != null) { args.add(FILTER_BY_TS); for (long ts : filterByTimestamps) { args.add(toByteArray(ts)); } } if (filterByValues != null) { args.add(FILTER_BY_VALUE); for (double value : filterByValues) { args.add(toByteArray(value)); } } if (withLabels) { args.add(WITHLABELS); } else if (selectedLabels != null) { args.add(SELECTED_LABELS); for (String label : selectedLabels) { args.add(label); } } if (count != null) { args.add(COUNT).add(toByteArray(count)); } if (aggregationType != null) { if (align != null) { args.add(ALIGN).add(align); } args.add(AGGREGATION).add(aggregationType).add(toByteArray(bucketDuration)); if (bucketTimestamp != null) { args.add(BUCKETTIMESTAMP).add(bucketTimestamp); } if (empty) { args.add(EMPTY); } } args.add(FILTER); for (String filter : filters) { args.add(filter); } if (groupByLabel != null && groupByReduce != null) { args.add(GROUPBY).add(groupByLabel).add(REDUCE).add(groupByReduce); } } @Override public boolean equals(Object o) { if (this == o) { return true; } if (o == null || getClass() != o.getClass()) { return false; } TSMRangeParams that = (TSMRangeParams) o; return latest == that.latest && withLabels == that.withLabels && bucketDuration == that.bucketDuration && empty == that.empty && Objects.equals(fromTimestamp, that.fromTimestamp) && Objects.equals(toTimestamp, that.toTimestamp) && Arrays.equals(filterByTimestamps, that.filterByTimestamps) && Arrays.equals(filterByValues, that.filterByValues) && Arrays.equals(selectedLabels, that.selectedLabels) && Objects.equals(count, that.count) && Arrays.equals(align, that.align) && aggregationType == that.aggregationType && Arrays.equals(bucketTimestamp, that.bucketTimestamp) && Arrays.equals(filters, that.filters) && Objects.equals(groupByLabel, that.groupByLabel) && Objects.equals(groupByReduce, that.groupByReduce); } @Override public int hashCode() { int result = Objects.hashCode(fromTimestamp); result = 31 * result + Objects.hashCode(toTimestamp); result = 31 * result + Boolean.hashCode(latest); result = 31 * result + Arrays.hashCode(filterByTimestamps); result = 31 * result + Arrays.hashCode(filterByValues); result = 31 * result + Boolean.hashCode(withLabels); result = 31 * result + Arrays.hashCode(selectedLabels); result = 31 * result + Objects.hashCode(count); result = 31 * result + Arrays.hashCode(align); result = 31 * result + Objects.hashCode(aggregationType); result = 31 * result + Long.hashCode(bucketDuration); result = 31 * result + Arrays.hashCode(bucketTimestamp); result = 31 * result + Boolean.hashCode(empty); result = 31 * result + Arrays.hashCode(filters); result = 31 * result + Objects.hashCode(groupByLabel); result = 31 * result + Objects.hashCode(groupByReduce); return result; } } ================================================ FILE: src/main/java/redis/clients/jedis/timeseries/TSRangeParams.java ================================================ package redis.clients.jedis.timeseries; import static redis.clients.jedis.Protocol.BYTES_TILDE; import static redis.clients.jedis.Protocol.toByteArray; import static redis.clients.jedis.timeseries.TimeSeriesProtocol.MINUS; import static redis.clients.jedis.timeseries.TimeSeriesProtocol.PLUS; import static redis.clients.jedis.timeseries.TimeSeriesProtocol.TimeSeriesKeyword.*; import static redis.clients.jedis.util.SafeEncoder.encode; import java.util.Arrays; import java.util.Objects; import redis.clients.jedis.CommandArguments; import redis.clients.jedis.params.IParams; /** * Represents optional arguments of TS.RANGE and TS.REVRANGE commands. */ public class TSRangeParams implements IParams { private Long fromTimestamp; private Long toTimestamp; private boolean latest; private long[] filterByTimestamps; private double[] filterByValues; private Integer count; private byte[] align; private AggregationType aggregationType; private long bucketDuration; private byte[] bucketTimestamp; private boolean empty; public TSRangeParams(long fromTimestamp, long toTimestamp) { this.fromTimestamp = fromTimestamp; this.toTimestamp = toTimestamp; } public static TSRangeParams rangeParams(long fromTimestamp, long toTimestamp) { return new TSRangeParams(fromTimestamp, toTimestamp); } public TSRangeParams() { } public static TSRangeParams rangeParams() { return new TSRangeParams(); } public TSRangeParams fromTimestamp(long fromTimestamp) { this.fromTimestamp = fromTimestamp; return this; } public TSRangeParams toTimestamp(long toTimestamp) { this.toTimestamp = toTimestamp; return this; } public TSRangeParams latest() { this.latest = true; return this; } public TSRangeParams filterByTS(long... timestamps) { this.filterByTimestamps = timestamps; return this; } public TSRangeParams filterByValues(double min, double max) { this.filterByValues = new double[]{min, max}; return this; } public TSRangeParams count(int count) { this.count = count; return this; } private TSRangeParams align(byte[] raw) { this.align = raw; return this; } /** * This requires AGGREGATION. */ public TSRangeParams align(long timestamp) { return align(toByteArray(timestamp)); } /** * This requires AGGREGATION. */ public TSRangeParams alignStart() { return align(MINUS); } /** * This requires AGGREGATION. */ public TSRangeParams alignEnd() { return align(PLUS); } public TSRangeParams aggregation(AggregationType aggregationType, long bucketDuration) { this.aggregationType = aggregationType; this.bucketDuration = bucketDuration; return this; } /** * This requires AGGREGATION. */ public TSRangeParams bucketTimestamp(String bucketTimestamp) { this.bucketTimestamp = encode(bucketTimestamp); return this; } /** * This requires AGGREGATION. */ public TSRangeParams bucketTimestampLow() { this.bucketTimestamp = MINUS; return this; } /** * This requires AGGREGATION. */ public TSRangeParams bucketTimestampHigh() { this.bucketTimestamp = PLUS; return this; } /** * This requires AGGREGATION. */ public TSRangeParams bucketTimestampMid() { this.bucketTimestamp = BYTES_TILDE; return this; } /** * This requires AGGREGATION. */ public TSRangeParams empty() { this.empty = true; return this; } @Override public void addParams(CommandArguments args) { if (fromTimestamp == null) { args.add(MINUS); } else { args.add(toByteArray(fromTimestamp)); } if (toTimestamp == null) { args.add(PLUS); } else { args.add(toByteArray(toTimestamp)); } if (latest) { args.add(LATEST); } if (filterByTimestamps != null) { args.add(FILTER_BY_TS); for (long ts : filterByTimestamps) { args.add(toByteArray(ts)); } } if (filterByValues != null) { args.add(FILTER_BY_VALUE); for (double value : filterByValues) { args.add(toByteArray(value)); } } if (count != null) { args.add(COUNT).add(toByteArray(count)); } if (aggregationType != null) { if (align != null) { args.add(ALIGN).add(align); } args.add(AGGREGATION).add(aggregationType).add(toByteArray(bucketDuration)); if (bucketTimestamp != null) { args.add(BUCKETTIMESTAMP).add(bucketTimestamp); } if (empty) { args.add(EMPTY); } } } @Override public boolean equals(Object o) { if (this == o) { return true; } if (o == null || getClass() != o.getClass()) { return false; } TSRangeParams that = (TSRangeParams) o; return latest == that.latest && bucketDuration == that.bucketDuration && empty == that.empty && Objects.equals(fromTimestamp, that.fromTimestamp) && Objects.equals(toTimestamp, that.toTimestamp) && Arrays.equals(filterByTimestamps, that.filterByTimestamps) && Arrays.equals(filterByValues, that.filterByValues) && Objects.equals(count, that.count) && Arrays.equals(align, that.align) && aggregationType == that.aggregationType && Arrays.equals(bucketTimestamp, that.bucketTimestamp); } @Override public int hashCode() { int result = Objects.hashCode(fromTimestamp); result = 31 * result + Objects.hashCode(toTimestamp); result = 31 * result + Boolean.hashCode(latest); result = 31 * result + Arrays.hashCode(filterByTimestamps); result = 31 * result + Arrays.hashCode(filterByValues); result = 31 * result + Objects.hashCode(count); result = 31 * result + Arrays.hashCode(align); result = 31 * result + Objects.hashCode(aggregationType); result = 31 * result + Long.hashCode(bucketDuration); result = 31 * result + Arrays.hashCode(bucketTimestamp); result = 31 * result + Boolean.hashCode(empty); return result; } } ================================================ FILE: src/main/java/redis/clients/jedis/timeseries/TimeSeriesBuilderFactory.java ================================================ package redis.clients.jedis.timeseries; import java.util.LinkedHashMap; import java.util.List; import java.util.Map; import java.util.function.Function; import java.util.stream.Collectors; import redis.clients.jedis.Builder; import redis.clients.jedis.BuilderFactory; import redis.clients.jedis.util.KeyValue; public final class TimeSeriesBuilderFactory { public static final Builder TIMESERIES_ELEMENT = new Builder() { @Override public TSElement build(Object data) { List list = (List) data; if (list == null || list.isEmpty()) return null; return new TSElement(BuilderFactory.LONG.build(list.get(0)), BuilderFactory.DOUBLE.build(list.get(1))); } }; public static final Builder> TIMESERIES_ELEMENT_LIST = new Builder>() { @Override public List build(Object data) { return ((List) data).stream().map((pairObject) -> (List) pairObject) .map((pairList) -> new TSElement(BuilderFactory.LONG.build(pairList.get(0)), BuilderFactory.DOUBLE.build(pairList.get(1)))) .collect(Collectors.toList()); } }; public static final Builder> TIMESERIES_MRANGE_RESPONSE = new Builder>() { @Override public Map build(Object data) { return ((List) data).stream().map((tsObject) -> (List) tsObject) .map((tsList) -> new TSMRangeElements(BuilderFactory.STRING.build(tsList.get(0)), BuilderFactory.STRING_MAP_FROM_PAIRS.build(tsList.get(1)), TIMESERIES_ELEMENT_LIST.build(tsList.get(2)))) .collect(Collectors.toMap(TSMRangeElements::getKey, Function.identity(), (x, y) -> x, LinkedHashMap::new)); } }; public static final Builder> TIMESERIES_MRANGE_RESPONSE_RESP3 = new Builder>() { @Override public Map build(Object data) { List dataList = (List) data; Map map = new LinkedHashMap<>(dataList.size() / 2, 1f); for (KeyValue kv : dataList) { String key = BuilderFactory.STRING.build(kv.getKey()); List valueList = (List) kv.getValue(); TSMRangeElements elements; switch (valueList.size()) { case 3: List aggrMapObj = (List) valueList.get(1); KeyValue aggKV = (KeyValue) aggrMapObj.get(0); assert "aggregators".equalsIgnoreCase(BuilderFactory.STRING.build(aggKV.getKey())); elements = new TSMRangeElements(key, BuilderFactory.STRING_MAP.build(valueList.get(0)), ((List) aggKV.getValue()).stream().map(BuilderFactory.STRING::build) .map(AggregationType::safeValueOf).collect(Collectors.toList()), TIMESERIES_ELEMENT_LIST.build(valueList.get(2))); break; case 4: List rdcMapObj = (List) valueList.get(1); assert "reducers".equalsIgnoreCase(BuilderFactory.STRING.build(rdcMapObj.get(0).getKey())); List srcMapObj = (List) valueList.get(2); assert "sources".equalsIgnoreCase(BuilderFactory.STRING.build(srcMapObj.get(0).getKey())); elements = new TSMRangeElements(key, BuilderFactory.STRING_MAP.build(valueList.get(0)), BuilderFactory.STRING_LIST.build(rdcMapObj.get(0).getValue()), BuilderFactory.STRING_LIST.build(srcMapObj.get(0).getValue()), TIMESERIES_ELEMENT_LIST.build(valueList.get(3))); break; default: throw new IllegalStateException(); } map.put(key, elements); } return map; } }; public static final Builder> TIMESERIES_MGET_RESPONSE = new Builder>() { @Override public Map build(Object data) { return ((List) data).stream().map((tsObject) -> (List) tsObject) .map((tsList) -> new TSMGetElement(BuilderFactory.STRING.build(tsList.get(0)), BuilderFactory.STRING_MAP_FROM_PAIRS.build(tsList.get(1)), TIMESERIES_ELEMENT.build(tsList.get(2)))) .collect(Collectors.toMap(TSMGetElement::getKey, Function.identity())); } }; public static final Builder> TIMESERIES_MGET_RESPONSE_RESP3 = new Builder>() { @Override public Map build(Object data) { List dataList = (List) data; Map map = new LinkedHashMap<>(dataList.size()); for (KeyValue kv : dataList) { String key = BuilderFactory.STRING.build(kv.getKey()); List valueList = (List) kv.getValue(); TSMGetElement value = new TSMGetElement(key, BuilderFactory.STRING_MAP.build(valueList.get(0)), TIMESERIES_ELEMENT.build(valueList.get(1))); map.put(key, value); } return map; } }; private TimeSeriesBuilderFactory() { throw new InstantiationError("Must not instantiate this class"); } } ================================================ FILE: src/main/java/redis/clients/jedis/timeseries/TimeSeriesProtocol.java ================================================ package redis.clients.jedis.timeseries; import redis.clients.jedis.args.Rawable; import redis.clients.jedis.commands.ProtocolCommand; import redis.clients.jedis.util.SafeEncoder; public class TimeSeriesProtocol { public static final byte[] PLUS = SafeEncoder.encode("+"); public static final byte[] MINUS = SafeEncoder.encode("-"); public enum TimeSeriesCommand implements ProtocolCommand { CREATE("TS.CREATE"), RANGE("TS.RANGE"), REVRANGE("TS.REVRANGE"), MRANGE("TS.MRANGE"), MREVRANGE("TS.MREVRANGE"), CREATERULE("TS.CREATERULE"), DELETERULE("TS.DELETERULE"), ADD("TS.ADD"), MADD("TS.MADD"), DEL("TS.DEL"), INCRBY("TS.INCRBY"), DECRBY("TS.DECRBY"), INFO("TS.INFO"), GET("TS.GET"), MGET("TS.MGET"), ALTER("TS.ALTER"), QUERYINDEX("TS.QUERYINDEX"); private final byte[] raw; private TimeSeriesCommand(String alt) { raw = SafeEncoder.encode(alt); } @Override public byte[] getRaw() { return raw; } } public enum TimeSeriesKeyword implements Rawable { RESET, FILTER, AGGREGATION, LABELS, RETENTION, TIMESTAMP, WITHLABELS, SELECTED_LABELS, COUNT, ENCODING, COMPRESSED, UNCOMPRESSED, CHUNK_SIZE, DUPLICATE_POLICY, IGNORE, ON_DUPLICATE, ALIGN, FILTER_BY_TS, FILTER_BY_VALUE, GROUPBY, REDUCE, DEBUG, LATEST, EMPTY, BUCKETTIMESTAMP; private final byte[] raw; private TimeSeriesKeyword() { raw = SafeEncoder.encode(name()); } @Override public byte[] getRaw() { return raw; } } } ================================================ FILE: src/main/java/redis/clients/jedis/timeseries/package-info.java ================================================ /** * This package contains the classes and interfaces related to RedisTimeSeries module. */ package redis.clients.jedis.timeseries; ================================================ FILE: src/main/java/redis/clients/jedis/util/ByteArrayComparator.java ================================================ package redis.clients.jedis.util; public final class ByteArrayComparator { private ByteArrayComparator() { throw new InstantiationError("Must not instantiate this class"); } public static int compare(final byte[] val1, final byte[] val2) { int len1 = val1.length; int len2 = val2.length; int lmin = Math.min(len1, len2); for (int i = 0; i < lmin; i++) { byte b1 = val1[i]; byte b2 = val2[i]; if (b1 < b2) return -1; if (b1 > b2) return 1; } if (len1 < len2) return -1; if (len1 > len2) return 1; return 0; } } ================================================ FILE: src/main/java/redis/clients/jedis/util/CompareCondition.java ================================================ package redis.clients.jedis.util; import java.util.Arrays; import java.util.Objects; import redis.clients.jedis.CommandArguments; import redis.clients.jedis.Protocol.Keyword; import redis.clients.jedis.annots.Experimental; /** * A compare condition to be used with commands that support conditional value checks (e.g. SET with * IFEQ/IFNE/IFDEQ/IFDNE and DELEX). This abstraction lets callers express value-based or * digest-based comparisons. *

* Digest-based comparisons use a 64-bit XXH3 digest represented as a 16-character lower-case * hexadecimal string. *

*/ @Experimental public final class CompareCondition { /** * The kind of condition represented by this instance. */ public enum Condition { /** current value must equal provided value */ VALUE_EQUAL(Keyword.IFEQ), /** current value must not equal provided value */ VALUE_NOT_EQUAL(Keyword.IFNE), /** current value's digest must equal provided digest */ DIGEST_EQUAL(Keyword.IFDEQ), /** current value's digest must not equal provided digest */ DIGEST_NOT_EQUAL(Keyword.IFDNE); private final Keyword keyword; Condition(Keyword keyword) { this.keyword = keyword; } /** The protocol keyword to emit for this condition. */ public Keyword getKeyword() { return keyword; } } private final Condition condition; private final Object payload; // String or byte[] private CompareCondition(Condition condition, Object payload) { if (!(payload instanceof String) && !(payload instanceof byte[])) { throw new IllegalArgumentException("payload must be String or byte[]"); } this.condition = condition; this.payload = payload; } // Factory methods: value-based public static CompareCondition valueEq(String value) { JedisAsserts.notNull(value, "value must not be null"); return new CompareCondition(Condition.VALUE_EQUAL, value); } public static CompareCondition valueNe(String value) { JedisAsserts.notNull(value, "value must not be null"); return new CompareCondition(Condition.VALUE_NOT_EQUAL, value); } public static CompareCondition valueEq(byte[] value) { JedisAsserts.notNull(value, "value must not be null"); return new CompareCondition(Condition.VALUE_EQUAL, value); } public static CompareCondition valueNe(byte[] value) { JedisAsserts.notNull(value, "value must not be null"); return new CompareCondition(Condition.VALUE_NOT_EQUAL, value); } // Factory methods: digest-based public static CompareCondition digestEq(String hex16) { JedisAsserts.notNull(hex16, "digest must not be null"); return new CompareCondition(Condition.DIGEST_EQUAL, hex16); } public static CompareCondition digestNe(String hex16) { JedisAsserts.notNull(hex16, "digest must not be null"); return new CompareCondition(Condition.DIGEST_NOT_EQUAL, hex16); } public static CompareCondition digestEq(byte[] digest) { JedisAsserts.notNull(digest, "digest must not be null"); return new CompareCondition(Condition.DIGEST_EQUAL, digest); } public static CompareCondition digestNe(byte[] digest) { JedisAsserts.notNull(digest, "digest must not be null"); return new CompareCondition(Condition.DIGEST_NOT_EQUAL, digest); } /** * Append this condition to the command arguments by emitting the appropriate keyword and payload. */ public void addTo(CommandArguments args) { args.add(condition.getKeyword()).add(payload); } /** The kind of this condition. */ public Condition getCondition() { return condition; } /** The payload for this condition (String or byte[]). */ public Object getPayload() { return payload; } @Override public boolean equals(Object o) { if (this == o) return true; if (o == null || getClass() != o.getClass()) return false; CompareCondition that = (CompareCondition) o; if (condition != that.condition) return false; // Handle byte[] comparison if (payload instanceof byte[] && that.payload instanceof byte[]) { return Arrays.equals((byte[]) payload, (byte[]) that.payload); } return Objects.equals(payload, that.payload); } @Override public int hashCode() { int result = Objects.hash(condition); if (payload instanceof byte[]) { result = 31 * result + Arrays.hashCode((byte[]) payload); } else { result = 31 * result + Objects.hashCode(payload); } return result; } @Override public String toString() { return "CompareCondition{" + "condition=" + condition + (payload != null ? ", payload=" + payload : "") + '}'; } } ================================================ FILE: src/main/java/redis/clients/jedis/util/Delay.java ================================================ package redis.clients.jedis.util; import java.time.Duration; import java.util.concurrent.ThreadLocalRandom; public abstract class Delay { protected Delay() { } /** * Calculate a specific delay based on the attempt. * @param attempt the attempt to calculate the delay from. * @return the calculated delay. */ public abstract Duration delay(long attempt); /** * Creates a constant delay. * @param delay the constant delay duration * @return a Delay that always returns the same duration */ public static Delay constant(Duration delay) { return new ConstantDelay(delay); } /** * Creates an exponential delay with equal jitter. Based on AWS exponential backoff strategy: * https://aws.amazon.com/blogs/architecture/exponential-backoff-and-jitter/ Formula: temp = * min(upper, base * 2^attempt) sleep = temp/2 + random_between(0, temp/2) result = max(lower, * sleep) * @param lower the minimum delay duration (lower bound) * @param upper the maximum delay duration (upper bound) * @param base the base delay duration * @return a Delay with exponential backoff and equal jitter */ public static Delay exponentialWithJitter(Duration lower, Duration upper, Duration base) { return new EqualJitterDelay(lower, upper, base); } static class ConstantDelay extends Delay { private final Duration delay; ConstantDelay(Duration delay) { this.delay = delay; } @Override public Duration delay(long attempt) { return delay; } } static class EqualJitterDelay extends Delay { private final long lowerMillis; private final long upperMillis; private final long baseMillis; EqualJitterDelay(Duration lower, Duration upper, Duration base) { this.lowerMillis = lower.toMillis(); this.upperMillis = upper.toMillis(); this.baseMillis = base.toMillis(); } @Override public Duration delay(long attempt) { // temp = min(upper, base * 2^attempt) long exponential = baseMillis * (1L << Math.min(attempt, 62)); long temp = Math.min(upperMillis, exponential); // sleep = temp/2 + random_between(0, temp/2) long half = temp / 2; long jitter = ThreadLocalRandom.current().nextLong(half + 1); long delayMillis = half + jitter; // Apply lower bound delayMillis = Math.max(lowerMillis, delayMillis); return Duration.ofMillis(delayMillis); } } } ================================================ FILE: src/main/java/redis/clients/jedis/util/DoublePrecision.java ================================================ package redis.clients.jedis.util; public final class DoublePrecision { private DoublePrecision() { throw new InstantiationError("Must not instantiate this class"); } public static Double parseFloatingPointNumber(String str) throws NumberFormatException { if (str == null) return null; try { return Double.valueOf(str); } catch (NumberFormatException e) { switch (str) { case "inf": case "+inf": return Double.POSITIVE_INFINITY; case "-inf": return Double.NEGATIVE_INFINITY; case "nan": case "-nan": // for some module commands // TODO: remove return Double.NaN; default: throw e; } } } public static Double parseEncodedFloatingPointNumber(Object val) throws NumberFormatException { if (val == null) return null; else if (val instanceof Double) return (Double) val; else return parseFloatingPointNumber((String) val); } } ================================================ FILE: src/main/java/redis/clients/jedis/util/IOUtils.java ================================================ package redis.clients.jedis.util; import java.io.IOException; import java.net.Socket; public class IOUtils { public static void closeQuietly(Socket sock) { // It's same thing as Apache Commons - IOUtils.closeQuietly() if (sock != null) { try { sock.close(); } catch (IOException e) { // ignored } } } public static void closeQuietly(AutoCloseable resource) { // It's same thing as Apache Commons - IOUtils.closeQuietly() if (resource != null) { try { resource.close(); } catch (Exception e) { // ignored } } } private IOUtils() { throw new InstantiationError("Must not instantiate this class"); } } ================================================ FILE: src/main/java/redis/clients/jedis/util/JedisAsserts.java ================================================ package redis.clients.jedis.util; /** * Assertion utility class that assists in validating arguments. This class is part of the internal API and may change without * further notice. * * @author ivo.gaydazhiev */ public class JedisAsserts { /** * Assert that an object is not {@code null} . * * @param object the object to check * @param message the exception message to use if the assertion fails * @throws IllegalArgumentException if the object is {@code null} */ public static void notNull(Object object, String message) { if (object == null) { throw new IllegalArgumentException(message); } } /** * Assert that {@code value} is {@code true}. * * @param value the value to check * @param message the exception message to use if the assertion fails * @throws IllegalArgumentException if the value is {@code false} */ public static void isTrue(boolean value, String message) { if (!value) { throw new IllegalArgumentException(message); } } /** * Assert that {@code value} is {@code false}. * * @param value the value to check * @param message the exception message to use if the assertion fails * @throws IllegalArgumentException if the value is {@code true} */ public static void isFalse(boolean value, String message) { if (value) { throw new IllegalArgumentException(message); } } } ================================================ FILE: src/main/java/redis/clients/jedis/util/JedisByteHashMap.java ================================================ package redis.clients.jedis.util; import java.io.Serializable; import java.util.Arrays; import java.util.Collection; import java.util.Iterator; import java.util.LinkedHashMap; import java.util.LinkedHashSet; import java.util.Map; import java.util.Set; public class JedisByteHashMap implements Map, Cloneable, Serializable { private static final long serialVersionUID = -6971431362627219416L; private final Map internalMap = new LinkedHashMap<>(); @Override public void clear() { internalMap.clear(); } @Override public boolean containsKey(Object key) { if (key instanceof byte[]) return internalMap.containsKey(new ByteArrayWrapper((byte[]) key)); return internalMap.containsKey(key); } @Override public boolean containsValue(Object value) { return internalMap.containsValue(value); } @Override public Set> entrySet() { Iterator> iterator = internalMap.entrySet() .iterator(); LinkedHashSet> hashSet = new LinkedHashSet<>(); while (iterator.hasNext()) { Entry entry = iterator.next(); hashSet.add(new JedisByteEntry(entry.getKey().data, entry.getValue())); } return hashSet; } @Override public byte[] get(Object key) { if (key instanceof byte[]) return internalMap.get(new ByteArrayWrapper((byte[]) key)); return internalMap.get(key); } @Override public boolean isEmpty() { return internalMap.isEmpty(); } @Override public Set keySet() { Set keySet = new LinkedHashSet<>(); Iterator iterator = internalMap.keySet().iterator(); while (iterator.hasNext()) { keySet.add(iterator.next().data); } return keySet; } @Override public byte[] put(byte[] key, byte[] value) { return internalMap.put(new ByteArrayWrapper(key), value); } @Override @SuppressWarnings("unchecked") public void putAll(Map m) { Iterator iterator = m.entrySet().iterator(); while (iterator.hasNext()) { Entry next = (Entry) iterator .next(); internalMap.put(new ByteArrayWrapper(next.getKey()), next.getValue()); } } @Override public byte[] remove(Object key) { if (key instanceof byte[]) return internalMap.remove(new ByteArrayWrapper((byte[]) key)); return internalMap.remove(key); } @Override public int size() { return internalMap.size(); } @Override public Collection values() { return internalMap.values(); } private static final class ByteArrayWrapper implements Serializable { private static final long serialVersionUID = 1L; private final byte[] data; private final int hashCode; public ByteArrayWrapper(byte[] data) { if (data == null) { throw new NullPointerException(); } this.data = data; this.hashCode = Arrays.hashCode(data); } @Override public boolean equals(Object other) { if (other == null) return false; if (other == this) return true; if (!(other instanceof ByteArrayWrapper)) return false; return Arrays.equals(data, ((ByteArrayWrapper) other).data); } @Override public int hashCode() { return hashCode; } } private static final class JedisByteEntry implements Entry { private byte[] value; private byte[] key; public JedisByteEntry(byte[] key, byte[] value) { this.key = key; this.value = value; } @Override public byte[] getKey() { return this.key; } @Override public byte[] getValue() { return this.value; } @Override public byte[] setValue(byte[] value) { this.value = value; return value; } } } ================================================ FILE: src/main/java/redis/clients/jedis/util/JedisByteMap.java ================================================ package redis.clients.jedis.util; import java.io.Serializable; import java.util.*; public class JedisByteMap implements Map, Cloneable, Serializable { private static final long serialVersionUID = -6971431362627219416L; private final Map internalMap = new LinkedHashMap<>(); @Override public void clear() { internalMap.clear(); } @Override public boolean containsKey(Object key) { if (key instanceof byte[]) return internalMap.containsKey(new ByteArrayWrapper((byte[]) key)); return internalMap.containsKey(key); } @Override public boolean containsValue(Object value) { return internalMap.containsValue(value); } @Override public Set> entrySet() { Iterator> iterator = internalMap.entrySet() .iterator(); LinkedHashSet> hashSet = new LinkedHashSet<>(); while (iterator.hasNext()) { Entry entry = iterator.next(); hashSet.add(new JedisByteEntry(entry.getKey().data, entry.getValue())); } return hashSet; } @Override public T get(Object key) { if (key instanceof byte[]) return internalMap.get(new ByteArrayWrapper((byte[]) key)); return internalMap.get(key); } @Override public boolean isEmpty() { return internalMap.isEmpty(); } @Override public Set keySet() { Set keySet = new LinkedHashSet<>(); Iterator iterator = internalMap.keySet().iterator(); while (iterator.hasNext()) { keySet.add(iterator.next().data); } return keySet; } @Override public T put(byte[] key, T value) { return internalMap.put(new ByteArrayWrapper(key), value); } @Override @SuppressWarnings("unchecked") public void putAll(Map m) { Iterator iterator = m.entrySet().iterator(); while (iterator.hasNext()) { Entry next = (Entry) iterator .next(); internalMap.put(new ByteArrayWrapper(next.getKey()), next.getValue()); } } @Override public T remove(Object key) { if (key instanceof byte[]) return internalMap.remove(new ByteArrayWrapper((byte[]) key)); return internalMap.remove(key); } @Override public int size() { return internalMap.size(); } @Override public Collection values() { return internalMap.values(); } private static final class ByteArrayWrapper implements Serializable { private static final long serialVersionUID = 1L; private final byte[] data; private final int hashCode; public ByteArrayWrapper(byte[] data) { if (data == null) { throw new NullPointerException(); } this.data = data; this.hashCode = Arrays.hashCode(data); } @Override public boolean equals(Object other) { if (other == null) return false; if (other == this) return true; if (!(other instanceof ByteArrayWrapper)) return false; return Arrays.equals(data, ((ByteArrayWrapper) other).data); } @Override public int hashCode() { return hashCode; } } private static final class JedisByteEntry implements Entry { private final byte[] key; private T value; public JedisByteEntry(byte[] key, T value) { this.key = key; this.value = value; } @Override public byte[] getKey() { return this.key; } @Override public T getValue() { return this.value; } @Override public T setValue(T value) { this.value = value; return value; } } } ================================================ FILE: src/main/java/redis/clients/jedis/util/JedisClusterCRC16.java ================================================ package redis.clients.jedis.util; /** * CRC16 Implementation according to CCITT standard Polynomial : 1021 (x^16 + x^12 + x^5 + 1) See Appendix A. CRC16 reference implementation in ANSI * C */ public final class JedisClusterCRC16 { private static final int[] LOOKUP_TABLE = { 0x0000, 0x1021, 0x2042, 0x3063, 0x4084, 0x50A5, 0x60C6, 0x70E7, 0x8108, 0x9129, 0xA14A, 0xB16B, 0xC18C, 0xD1AD, 0xE1CE, 0xF1EF, 0x1231, 0x0210, 0x3273, 0x2252, 0x52B5, 0x4294, 0x72F7, 0x62D6, 0x9339, 0x8318, 0xB37B, 0xA35A, 0xD3BD, 0xC39C, 0xF3FF, 0xE3DE, 0x2462, 0x3443, 0x0420, 0x1401, 0x64E6, 0x74C7, 0x44A4, 0x5485, 0xA56A, 0xB54B, 0x8528, 0x9509, 0xE5EE, 0xF5CF, 0xC5AC, 0xD58D, 0x3653, 0x2672, 0x1611, 0x0630, 0x76D7, 0x66F6, 0x5695, 0x46B4, 0xB75B, 0xA77A, 0x9719, 0x8738, 0xF7DF, 0xE7FE, 0xD79D, 0xC7BC, 0x48C4, 0x58E5, 0x6886, 0x78A7, 0x0840, 0x1861, 0x2802, 0x3823, 0xC9CC, 0xD9ED, 0xE98E, 0xF9AF, 0x8948, 0x9969, 0xA90A, 0xB92B, 0x5AF5, 0x4AD4, 0x7AB7, 0x6A96, 0x1A71, 0x0A50, 0x3A33, 0x2A12, 0xDBFD, 0xCBDC, 0xFBBF, 0xEB9E, 0x9B79, 0x8B58, 0xBB3B, 0xAB1A, 0x6CA6, 0x7C87, 0x4CE4, 0x5CC5, 0x2C22, 0x3C03, 0x0C60, 0x1C41, 0xEDAE, 0xFD8F, 0xCDEC, 0xDDCD, 0xAD2A, 0xBD0B, 0x8D68, 0x9D49, 0x7E97, 0x6EB6, 0x5ED5, 0x4EF4, 0x3E13, 0x2E32, 0x1E51, 0x0E70, 0xFF9F, 0xEFBE, 0xDFDD, 0xCFFC, 0xBF1B, 0xAF3A, 0x9F59, 0x8F78, 0x9188, 0x81A9, 0xB1CA, 0xA1EB, 0xD10C, 0xC12D, 0xF14E, 0xE16F, 0x1080, 0x00A1, 0x30C2, 0x20E3, 0x5004, 0x4025, 0x7046, 0x6067, 0x83B9, 0x9398, 0xA3FB, 0xB3DA, 0xC33D, 0xD31C, 0xE37F, 0xF35E, 0x02B1, 0x1290, 0x22F3, 0x32D2, 0x4235, 0x5214, 0x6277, 0x7256, 0xB5EA, 0xA5CB, 0x95A8, 0x8589, 0xF56E, 0xE54F, 0xD52C, 0xC50D, 0x34E2, 0x24C3, 0x14A0, 0x0481, 0x7466, 0x6447, 0x5424, 0x4405, 0xA7DB, 0xB7FA, 0x8799, 0x97B8, 0xE75F, 0xF77E, 0xC71D, 0xD73C, 0x26D3, 0x36F2, 0x0691, 0x16B0, 0x6657, 0x7676, 0x4615, 0x5634, 0xD94C, 0xC96D, 0xF90E, 0xE92F, 0x99C8, 0x89E9, 0xB98A, 0xA9AB, 0x5844, 0x4865, 0x7806, 0x6827, 0x18C0, 0x08E1, 0x3882, 0x28A3, 0xCB7D, 0xDB5C, 0xEB3F, 0xFB1E, 0x8BF9, 0x9BD8, 0xABBB, 0xBB9A, 0x4A75, 0x5A54, 0x6A37, 0x7A16, 0x0AF1, 0x1AD0, 0x2AB3, 0x3A92, 0xFD2E, 0xED0F, 0xDD6C, 0xCD4D, 0xBDAA, 0xAD8B, 0x9DE8, 0x8DC9, 0x7C26, 0x6C07, 0x5C64, 0x4C45, 0x3CA2, 0x2C83, 0x1CE0, 0x0CC1, 0xEF1F, 0xFF3E, 0xCF5D, 0xDF7C, 0xAF9B, 0xBFBA, 0x8FD9, 0x9FF8, 0x6E17, 0x7E36, 0x4E55, 0x5E74, 0x2E93, 0x3EB2, 0x0ED1, 0x1EF0, }; public static int getSlot(String key) { if (key == null) { throw new NullPointerException("Slot calculation of null is impossible"); } key = JedisClusterHashTag.getHashTag(key); // optimization with modulo operator with power of 2 equivalent to getCRC16(key) % 16384 return getCRC16(key) & (16384 - 1); } public static int getSlot(byte[] key) { if (key == null) { throw new NullPointerException("Slot calculation of null is impossible"); } int s = -1; int e = -1; boolean sFound = false; for (int i = 0; i < key.length; i++) { if (key[i] == '{' && !sFound) { s = i; sFound = true; } if (key[i] == '}' && sFound) { e = i; break; } } if (s > -1 && e > -1 && e != s + 1) { return getCRC16(key, s + 1, e) & (16384 - 1); } return getCRC16(key) & (16384 - 1); } /** * Create a CRC16 checksum from the bytes. implementation is from mp911de/lettuce, modified with * some more optimizations * @param bytes * @param s * @param e * @return CRC16 as integer value See Issue 733 */ public static int getCRC16(byte[] bytes, int s, int e) { int crc = 0x0000; for (int i = s; i < e; i++) { crc = ((crc << 8) ^ LOOKUP_TABLE[((crc >>> 8) ^ (bytes[i] & 0xFF)) & 0xFF]); } return crc & 0xFFFF; } public static int getCRC16(byte[] bytes) { return getCRC16(bytes, 0, bytes.length); } public static int getCRC16(String key) { byte[] bytesKey = SafeEncoder.encode(key); return getCRC16(bytesKey, 0, bytesKey.length); } private JedisClusterCRC16() { throw new InstantiationError("Must not instantiate this class"); } } ================================================ FILE: src/main/java/redis/clients/jedis/util/JedisClusterHashTag.java ================================================ package redis.clients.jedis.util; /** * Holds various methods/utilities to manipulate and parse redis hash-tags. See Cluster-Spec : Keys hash tags */ public final class JedisClusterHashTag { private JedisClusterHashTag() { throw new InstantiationError("Must not instantiate this class"); } public static String getHashTag(String key) { return extractHashTag(key, true); } public static boolean isClusterCompliantMatchPattern(byte[] matchPattern) { return isClusterCompliantMatchPattern(SafeEncoder.encode(matchPattern)); } public static boolean isClusterCompliantMatchPattern(String matchPattern) { String tag = extractHashTag(matchPattern, false); return tag != null && !tag.isEmpty(); } private static String extractHashTag(String key, boolean returnKeyOnAbsence) { int s = key.indexOf("{"); if (s > -1) { int e = key.indexOf("}", s + 1); if (e > -1 && e != s + 1) { return key.substring(s + 1, e); } } return returnKeyOnAbsence ? key : null; } } ================================================ FILE: src/main/java/redis/clients/jedis/util/JedisCommandIterationBase.java ================================================ package redis.clients.jedis.util; import java.util.ArrayList; import java.util.Collection; import java.util.Collections; import java.util.LinkedList; import java.util.Map; import java.util.NoSuchElementException; import java.util.Queue; import java.util.function.Supplier; import redis.clients.jedis.Builder; import redis.clients.jedis.CommandArguments; import redis.clients.jedis.Connection; import redis.clients.jedis.providers.ConnectionProvider; /** * @param Type of each batch reply * @param Type of each data */ public abstract class JedisCommandIterationBase { private final Builder builder; private final Queue connections; private Map.Entry connection; private B lastReply; private boolean roundRobinCompleted; private boolean iterationCompleted; protected JedisCommandIterationBase(ConnectionProvider connectionProvider, Builder responseBuilder) { Map connectionMap = connectionProvider.getConnectionMap(); ArrayList connectionList = new ArrayList<>(connectionMap.entrySet()); Collections.shuffle(connectionList); this.connections = new LinkedList<>(connectionList); this.builder = responseBuilder; this.iterationCompleted = true; this.roundRobinCompleted = this.connections.isEmpty(); } public final boolean isIterationCompleted() { return roundRobinCompleted; } protected abstract boolean isNodeCompleted(B reply); protected abstract CommandArguments initCommandArguments(); protected abstract CommandArguments nextCommandArguments(B lastReply); public final B nextBatch() { if (roundRobinCompleted) { throw new NoSuchElementException(); } CommandArguments args; if (iterationCompleted) { connection = connections.poll(); args = initCommandArguments(); } else { args = nextCommandArguments(lastReply); } Object rawReply; if (connection.getValue() instanceof Connection) { rawReply = ((Connection) connection.getValue()).executeCommand(args); } else if (connection.getValue() instanceof Pool) { try (Connection c = ((Pool) connection.getValue()).getResource()) { rawReply = c.executeCommand(args); } } else { throw new IllegalArgumentException(connection.getValue().getClass() + "is not supported."); } lastReply = builder.build(rawReply); iterationCompleted = isNodeCompleted(lastReply); if (iterationCompleted) { if (connections.isEmpty()) { roundRobinCompleted = true; } } return lastReply; } protected abstract Collection convertBatchToData(B batch); public final Collection nextBatchList() { return convertBatchToData(nextBatch()); } public final Collection collect(Collection c) { while (!isIterationCompleted()) { c.addAll(nextBatchList()); } return c; } } ================================================ FILE: src/main/java/redis/clients/jedis/util/JedisURIHelper.java ================================================ package redis.clients.jedis.util; import java.net.URI; import redis.clients.jedis.HostAndPort; import redis.clients.jedis.Protocol; import redis.clients.jedis.RedisProtocol; /** * Utility class for handling Redis URIs. * This class provides methods to extract various components from a Redis URI, * such as host, port, user, password, database index, and protocol. * It also includes methods to validate the URI and check its scheme. * *

URI syntax

* *
* redis[s]{@code ://}[[username][{@code :}password]@] * host[{@code :}port][{@code /}database] *
* * *

Authentication

*

Authentication details can be provided in the URI in the form of a username and password. * Redis URIs may contain authentication details that effectively lead to usernames with passwords, * password-only, or no authentication.

*

Examples:

*
    *
  • Username and Password: redis://username:password@host:port
  • *
  • Password-only: redis://:password@host:port
  • *
  • Empty password: redis://username:@host:port
  • *
  • No Authentication: redis://host:port
  • *
*/ public final class JedisURIHelper { private static final String REDIS = "redis"; private static final String REDISS = "rediss"; private JedisURIHelper() { throw new InstantiationError("Must not instantiate this class"); } public static HostAndPort getHostAndPort(URI uri) { return new HostAndPort(uri.getHost(), uri.getPort()); } /** * Extracts the user from the given URI. *

* For details on the URI format and authentication examples, see {@link JedisURIHelper}. *

* @param uri the URI to extract the user from * @return the user as a String, or null if user is empty or {@link URI#getUserInfo()} info is missing */ public static String getUser(URI uri) { String userInfo = uri.getUserInfo(); if (userInfo != null) { String user = userInfo.split(":", 2)[0]; if (user.isEmpty()) { user = null; // return null user is not specified } return user; } return null; } /** * Extracts the password from the given URI. *

* For details on the URI format and authentication examples, see {@link JedisURIHelper}. *

* @param uri the URI to extract the password from * @return the password as a String, or null if {@link URI#getUserInfo()} info is missing * @throws IllegalArgumentException if {@link URI#getUserInfo()} is provided but does not contain * a password */ public static String getPassword(URI uri) { String userInfo = uri.getUserInfo(); if (userInfo != null) { String[] userAndPassword = userInfo.split(":", 2); if (userAndPassword.length < 2) { throw new IllegalArgumentException("Password not provided in uri."); } return userAndPassword[1]; } return null; } /** * Checks if the given URI has a database index component. * * @param uri the URI to check * @return true if the URI has a database index component, false otherwise */ public static boolean hasDbIndex(URI uri) { if (uri.getPath() == null || uri.getPath().isEmpty()) { return false; } String[] pathSplit = uri.getPath().split("/", 2); return pathSplit.length > 1 && !pathSplit[1].isEmpty(); } /** * Returns the database index from the given URI. * * @param uri * @return database index, or default database (0) if not specified */ public static int getDBIndex(URI uri) { String[] pathSplit = uri.getPath().split("/", 2); if (pathSplit.length > 1) { String dbIndexStr = pathSplit[1]; if (dbIndexStr.isEmpty()) { return Protocol.DEFAULT_DATABASE; } return Integer.parseInt(dbIndexStr); } else { return Protocol.DEFAULT_DATABASE; } } /** * Returns the Redis protocol from the given URI. * * @param uri * @return Redis protocol, or null if not specified */ public static RedisProtocol getRedisProtocol(URI uri) { if (uri.getQuery() == null) return null; String[] params = uri.getQuery().split("&"); for (String param : params) { int idx = param.indexOf("="); if (idx < 0) continue; if ("protocol".equals(param.substring(0, idx))) { String ver = param.substring(idx + 1); for (RedisProtocol proto : RedisProtocol.values()) { if (proto.version().equals(ver)) { return proto; } } throw new IllegalArgumentException("Unknown protocol " + ver); } } return null; // null (default) when not defined } public static boolean isValid(URI uri) { if (isEmpty(uri.getScheme()) || isEmpty(uri.getHost()) || uri.getPort() == -1) { return false; } return true; } private static boolean isEmpty(String value) { return value == null || value.trim().length() == 0; } public static boolean isRedisScheme(URI uri) { return REDIS.equals(uri.getScheme()); } public static boolean isRedisSSLScheme(URI uri) { return REDISS.equals(uri.getScheme()); } } ================================================ FILE: src/main/java/redis/clients/jedis/util/KeyValue.java ================================================ package redis.clients.jedis.util; import java.util.AbstractMap.SimpleImmutableEntry; public class KeyValue extends SimpleImmutableEntry { public KeyValue(K key, V value) { super(key, value); } public static KeyValue of(K key, V value) { return new KeyValue<>(key, value); } } ================================================ FILE: src/main/java/redis/clients/jedis/util/LazyRawable.java ================================================ package redis.clients.jedis.util; import redis.clients.jedis.args.Rawable; public class LazyRawable implements Rawable { private byte[] raw = null; public void setRaw(byte[] raw) { this.raw = raw; } @Override public byte[] getRaw() { return raw; } } ================================================ FILE: src/main/java/redis/clients/jedis/util/Pool.java ================================================ package redis.clients.jedis.util; import org.apache.commons.pool2.PooledObjectFactory; import org.apache.commons.pool2.impl.GenericObjectPool; import org.apache.commons.pool2.impl.GenericObjectPoolConfig; import redis.clients.jedis.exceptions.JedisException; public class Pool extends GenericObjectPool { // Legacy public Pool(GenericObjectPoolConfig poolConfig, PooledObjectFactory factory) { this(factory, poolConfig); } public Pool(final PooledObjectFactory factory, final GenericObjectPoolConfig poolConfig) { super(factory, poolConfig); } public Pool(final PooledObjectFactory factory) { super(factory); } @Override public void close() { destroy(); } public void destroy() { try { super.close(); } catch (RuntimeException e) { throw new JedisException("Could not destroy the pool", e); } } public T getResource() { try { return super.borrowObject(); } catch (JedisException je) { throw je; } catch (Exception e) { throw new JedisException("Could not get a resource from the pool", e); } } public void returnResource(final T resource) { if (resource == null) { return; } try { super.returnObject(resource); } catch (RuntimeException e) { throw new JedisException("Could not return the resource to the pool", e); } } public void returnBrokenResource(final T resource) { if (resource == null) { return; } try { super.invalidateObject(resource); } catch (Exception e) { throw new JedisException("Could not return the broken resource to the pool", e); } } @Override public void addObjects(int count) { try { for (int i = 0; i < count; i++) { addObject(); } } catch (Exception e) { throw new JedisException("Error trying to add idle objects", e); } } } ================================================ FILE: src/main/java/redis/clients/jedis/util/PrefixedKeyArgumentPreProcessor.java ================================================ package redis.clients.jedis.util; import redis.clients.jedis.CommandKeyArgumentPreProcessor; import redis.clients.jedis.annots.Experimental; import redis.clients.jedis.args.Rawable; import redis.clients.jedis.args.RawableFactory; @Experimental public class PrefixedKeyArgumentPreProcessor implements CommandKeyArgumentPreProcessor { private final byte[] prefixBytes; private final String prefixString; public PrefixedKeyArgumentPreProcessor(String prefix) { this(prefix, SafeEncoder.encode(prefix)); } public PrefixedKeyArgumentPreProcessor(String prefixString, byte[] prefixBytes) { this.prefixString = prefixString; this.prefixBytes = prefixBytes; } @Override public Object actualKey(Object paramKey) { return prefixKey(paramKey, prefixString, prefixBytes); } private static Object prefixKey(Object key, String prefixString, byte[] prefixBytes) { if (key instanceof Rawable) { byte[] raw = ((Rawable) key).getRaw(); return RawableFactory.from(prefixKeyWithBytes(raw, prefixBytes)); } else if (key instanceof byte[]) { return prefixKeyWithBytes((byte[]) key, prefixBytes); } else if (key instanceof String) { String raw = (String) key; return prefixString + raw; } throw new IllegalArgumentException("\"" + key.toString() + "\" is not a valid argument."); } private static byte[] prefixKeyWithBytes(byte[] key, byte[] prefixBytes) { byte[] namespaced = new byte[prefixBytes.length + key.length]; System.arraycopy(prefixBytes, 0, namespaced, 0, prefixBytes.length); System.arraycopy(key, 0, namespaced, prefixBytes.length, key.length); return namespaced; } } ================================================ FILE: src/main/java/redis/clients/jedis/util/RedisInputStream.java ================================================ /* * Copyright 2009-2010 MBTE Sweden AB. Licensed under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance with the License. You may obtain a * copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable * law or agreed to in writing, software distributed under the License is distributed on an "AS IS" * BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License * for the specific language governing permissions and limitations under the License. */ package redis.clients.jedis.util; import java.io.ByteArrayOutputStream; import java.io.FilterInputStream; import java.io.IOException; import java.io.InputStream; import java.math.BigInteger; import redis.clients.jedis.annots.Experimental; import redis.clients.jedis.exceptions.JedisConnectionException; /** * This class assumes (to some degree) that we are reading a RESP stream. As such it assumes certain * conventions regarding CRLF line termination. It also assumes that if the Protocol layer requires * a byte that if that byte is not there it is a stream error. */ public class RedisInputStream extends FilterInputStream { private static final int INPUT_BUFFER_SIZE = Integer.parseInt( System.getProperty("jedis.bufferSize.input", System.getProperty("jedis.bufferSize", "8192"))); protected final byte[] buf; protected int count, limit; public RedisInputStream(InputStream in, int size) { super(in); if (size <= 0) { throw new IllegalArgumentException("Buffer size <= 0"); } buf = new byte[size]; } public RedisInputStream(InputStream in) { this(in, INPUT_BUFFER_SIZE); } @Experimental public boolean peek(byte b) throws JedisConnectionException { ensureFill(); // in current design, at least one reply is expected. so ensureFillSafe() is not necessary. return buf[count] == b; } public byte readByte() throws JedisConnectionException { ensureFill(); return buf[count++]; } private void ensureCrLf() { final byte[] buf = this.buf; ensureFill(); if (buf[count++] == '\r') { ensureFill(); if (buf[count++] == '\n') { return; } } throw new JedisConnectionException("Unexpected character!"); } public String readLine() { final StringBuilder sb = new StringBuilder(); while (true) { ensureFill(); byte b = buf[count++]; if (b == '\r') { ensureFill(); // Must be one more byte byte c = buf[count++]; if (c == '\n') { break; } sb.append((char) b); sb.append((char) c); } else { sb.append((char) b); } } final String reply = sb.toString(); if (reply.isEmpty()) { throw new JedisConnectionException("It seems like server has closed the connection."); } return reply; } public byte[] readLineBytes() { /* * This operation should only require one fill. In that typical case we optimize allocation and * copy of the byte array. In the edge case where more than one fill is required then we take a * slower path and expand a byte array output stream as is necessary. */ ensureFill(); int pos = count; final byte[] buf = this.buf; while (true) { if (pos == limit) { return readLineBytesSlowly(); } if (buf[pos++] == '\r') { if (pos == limit) { return readLineBytesSlowly(); } if (buf[pos++] == '\n') { break; } } } final int N = (pos - count) - 2; final byte[] line = new byte[N]; System.arraycopy(buf, count, line, 0, N); count = pos; return line; } /** * Slow path in case a line of bytes cannot be read in one #fill() operation. This is still faster * than creating the StringBuilder, String, then encoding as byte[] in Protocol, then decoding * back into a String. */ private byte[] readLineBytesSlowly() { ByteArrayOutputStream bout = null; while (true) { ensureFill(); byte b = buf[count++]; if (b == '\r') { ensureFill(); // Must be one more byte byte c = buf[count++]; if (c == '\n') { break; } if (bout == null) { bout = new ByteArrayOutputStream(16); } bout.write(b); bout.write(c); } else { if (bout == null) { bout = new ByteArrayOutputStream(16); } bout.write(b); } } return bout == null ? new byte[0] : bout.toByteArray(); } public Object readNullCrLf() { ensureCrLf(); return null; } public boolean readBooleanCrLf() { final byte[] buf = this.buf; ensureFill(); final byte b = buf[count++]; ensureCrLf(); switch (b) { case 't': return true; case 'f': return false; default: throw new JedisConnectionException("Unexpected character!"); } } public int readIntCrLf() { return (int) readLongCrLf(); } public long readLongCrLf() { final byte[] buf = this.buf; ensureFill(); final boolean isNeg = buf[count] == '-'; if (isNeg) { ++count; } long value = 0; while (true) { ensureFill(); final int b = buf[count++]; if (b == '\r') { ensureFill(); if (buf[count++] != '\n') { throw new JedisConnectionException("Unexpected character!"); } break; } else { value = value * 10 + b - '0'; } } return (isNeg ? -value : value); } public double readDoubleCrLf() { return DoublePrecision.parseFloatingPointNumber(readLine()); } public BigInteger readBigIntegerCrLf() { return new BigInteger(readLine()); } @Override public int read(byte[] b, int off, int len) throws JedisConnectionException { ensureFill(); final int length = Math.min(limit - count, len); System.arraycopy(buf, count, b, off, length); count += length; return length; } /** * This method assumes there are required bytes to be read. If we cannot read anymore bytes an * exception is thrown to quickly ascertain that the stream was smaller than expected. */ private void ensureFill() throws JedisConnectionException { if (count >= limit) { try { limit = in.read(buf); count = 0; if (limit == -1) { throw new JedisConnectionException("Unexpected end of stream."); } } catch (IOException e) { throw new JedisConnectionException(e); } } } @Override public int available() throws IOException { int availableInBuf = limit - count; int availableInSocket = this.in.available(); return (availableInBuf > availableInSocket) ? availableInBuf : availableInSocket; } } ================================================ FILE: src/main/java/redis/clients/jedis/util/RedisOutputStream.java ================================================ package redis.clients.jedis.util; import java.io.FilterOutputStream; import java.io.IOException; import java.io.OutputStream; /** * The class implements a buffered output stream without synchronization There are also special * operations like in-place string encoding. This stream fully ignore mark/reset and should not be * used outside Jedis */ public final class RedisOutputStream extends FilterOutputStream { private static final int OUTPUT_BUFFER_SIZE = Integer.parseInt( System.getProperty("jedis.bufferSize.output", System.getProperty("jedis.bufferSize", "8192"))); protected final byte[] buf; protected int count; private final static int[] sizeTable = { 9, 99, 999, 9999, 99999, 999999, 9999999, 99999999, 999999999, Integer.MAX_VALUE }; private final static byte[] DigitTens = { '0', '0', '0', '0', '0', '0', '0', '0', '0', '0', '1', '1', '1', '1', '1', '1', '1', '1', '1', '1', '2', '2', '2', '2', '2', '2', '2', '2', '2', '2', '3', '3', '3', '3', '3', '3', '3', '3', '3', '3', '4', '4', '4', '4', '4', '4', '4', '4', '4', '4', '5', '5', '5', '5', '5', '5', '5', '5', '5', '5', '6', '6', '6', '6', '6', '6', '6', '6', '6', '6', '7', '7', '7', '7', '7', '7', '7', '7', '7', '7', '8', '8', '8', '8', '8', '8', '8', '8', '8', '8', '9', '9', '9', '9', '9', '9', '9', '9', '9', '9', }; private final static byte[] DigitOnes = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', }; private final static byte[] digits = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z' }; public RedisOutputStream(final OutputStream out) { this(out, OUTPUT_BUFFER_SIZE); } public RedisOutputStream(final OutputStream out, final int size) { super(out); if (size <= 0) { throw new IllegalArgumentException("Buffer size <= 0"); } buf = new byte[size]; } private void flushBuffer() throws IOException { if (count > 0) { out.write(buf, 0, count); count = 0; } } public void write(final byte b) throws IOException { if (count == buf.length) { flushBuffer(); } buf[count++] = b; } @Override public void write(final byte[] b) throws IOException { write(b, 0, b.length); } @Override public void write(final byte[] b, final int off, final int len) throws IOException { if (len >= buf.length) { flushBuffer(); out.write(b, off, len); } else { if (len >= buf.length - count) { flushBuffer(); } System.arraycopy(b, off, buf, count, len); count += len; } } public void writeCrLf() throws IOException { if (2 >= buf.length - count) { flushBuffer(); } buf[count++] = '\r'; buf[count++] = '\n'; } public void writeIntCrLf(int value) throws IOException { if (value < 0) { write((byte) '-'); value = -value; } int size = 0; while (value > sizeTable[size]) size++; size++; if (size >= buf.length - count) { flushBuffer(); } int q, r; int charPos = count + size; while (value >= 65536) { q = value / 100; r = value - ((q << 6) + (q << 5) + (q << 2)); value = q; buf[--charPos] = DigitOnes[r]; buf[--charPos] = DigitTens[r]; } for (;;) { q = (value * 52429) >>> (16 + 3); r = value - ((q << 3) + (q << 1)); buf[--charPos] = digits[r]; value = q; if (value == 0) break; } count += size; writeCrLf(); } @Override public void flush() throws IOException { flushBuffer(); out.flush(); } } ================================================ FILE: src/main/java/redis/clients/jedis/util/SafeEncoder.java ================================================ package redis.clients.jedis.util; import java.nio.charset.Charset; import java.nio.charset.StandardCharsets; import java.util.ArrayList; import java.util.List; /** * The only reason to have this is to be able to compatible with java 1.5 :( */ public final class SafeEncoder { public static volatile Charset DEFAULT_CHARSET = StandardCharsets.UTF_8; private SafeEncoder() { throw new InstantiationError("Must not instantiate this class"); } public static byte[][] encodeMany(final String... strs) { byte[][] many = new byte[strs.length][]; for (int i = 0; i < strs.length; i++) { many[i] = encode(strs[i]); } return many; } public static byte[] encode(final String str) { if (str == null) { throw new IllegalArgumentException("null value cannot be sent to redis"); } return str.getBytes(DEFAULT_CHARSET); } public static String encode(final byte[] data) { return new String(data, DEFAULT_CHARSET); } /** * This method takes an object and will convert all bytes[] and list of byte[] and will encode the * object in a recursive way. * @param dataToEncode * @return the object fully encoded */ public static Object encodeObject(Object dataToEncode) { if (dataToEncode instanceof byte[]) { return SafeEncoder.encode((byte[]) dataToEncode); } if (dataToEncode instanceof KeyValue) { KeyValue keyValue = (KeyValue) dataToEncode; return new KeyValue<>(encodeObject(keyValue.getKey()), encodeObject(keyValue.getValue())); } if (dataToEncode instanceof List) { List arrayToDecode = (List) dataToEncode; List returnValueArray = new ArrayList(arrayToDecode.size()); for (Object arrayEntry : arrayToDecode) { // recursive call and add to list returnValueArray.add(encodeObject(arrayEntry)); } return returnValueArray; } return dataToEncode; } /** * Converts a byte array to uppercase by converting lowercase ASCII letters (a-z) to uppercase (A-Z). * This method is optimized for ASCII text and performs direct byte manipulation, which is significantly * faster than converting to String and calling String.toUpperCase(). *

* This method only works correctly for ASCII text. Non-ASCII characters are left unchanged. * For Redis command names (which are always ASCII), this is safe and provides ~47% performance * improvement over the String-based approach. * * @param data the byte array to convert to uppercase * @return a new byte array with lowercase ASCII letters converted to uppercase */ public static byte[] toUpperCase(final byte[] data) { if (data == null) { return null; } byte[] uppercaseBytes = new byte[data.length]; for (int i = 0; i < data.length; i++) { if (data[i] >= 'a' && data[i] <= 'z') { uppercaseBytes[i] = (byte) (data[i] - 32); } else { uppercaseBytes[i] = data[i]; } } return uppercaseBytes; } } ================================================ FILE: src/main/java/redis/clients/jedis/util/package-info.java ================================================ /** * This package contains the utility classes. */ package redis.clients.jedis.util; ================================================ FILE: src/main/resources/redis/clients/jedis/pom.properties ================================================ groupId=${project.groupId} artifactId=${project.artifactId} version=${project.version} ================================================ FILE: src/test/java/io/redis/examples/BitMapsExample.java ================================================ // EXAMPLE: bitmap_tutorial // HIDE_START package io.redis.examples; import org.junit.jupiter.api.Test; import redis.clients.jedis.RedisClient; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertTrue; public class BitMapsExample { @Test public void run() { RedisClient jedis = RedisClient.create("redis://localhost:6379"); // HIDE_END // REMOVE_START jedis.del("pings:2024-01-01-00:00"); // REMOVE_END // STEP_START ping boolean res1 = jedis.setbit("pings:2024-01-01-00:00", 123, true); System.out.println(res1); // >>> false boolean res2 = jedis.getbit("pings:2024-01-01-00:00", 123); System.out.println(res2); // >>> true boolean res3 = jedis.getbit("pings:2024-01-01-00:00", 456); System.out.println(res3); // >>> false // STEP_END // REMOVE_START assertFalse(res1); assertTrue(res2); assertFalse(res3); // REMOVE_END // STEP_START bitcount long res4 = jedis.bitcount("pings:2024-01-01-00:00"); System.out.println(res4); // >>> 1 // STEP_END // REMOVE_START assertEquals(1, res4); // REMOVE_END // HIDE_START jedis.close(); } } // HIDE_END ================================================ FILE: src/test/java/io/redis/examples/BitfieldExample.java ================================================ // EXAMPLE: bitfield_tutorial // REMOVE_START package io.redis.examples; import org.junit.jupiter.api.Test; import java.util.List; // REMOVE_END // HIDE_START import redis.clients.jedis.RedisClient; import static org.junit.jupiter.api.Assertions.assertEquals; // HIDE_END // HIDE_START public class BitfieldExample { @Test public void run() { RedisClient jedis = RedisClient.create("redis://localhost:6379"); // HIDE_END //REMOVE_START // Clear any keys here before using them in tests. jedis.del("bike:1:stats"); //REMOVE_END // STEP_START bf List res1 = jedis.bitfield("bike:1:stats", "SET", "u32", "#0", "1000"); System.out.println(res1); // >>> [0] List res2 = jedis.bitfield("bike:1:stats", "INCRBY", "u32", "#0", "-50", "INCRBY", "u32", "#1", "1"); System.out.println(res2); // >>> [950, 1] List res3 = jedis.bitfield("bike:1:stats", "INCRBY", "u32", "#0", "500", "INCRBY", "u32", "#1", "1"); System.out.println(res3); // >>> [1450, 2] List res4 = jedis.bitfield("bike:1:stats", "GET", "u32", "#0", "GET", "u32", "#1"); System.out.println(res4); // >>> [1450, 2] // STEP_END // Tests for 'bf' step. // REMOVE_START assertEquals("[0]", res1.toString()); assertEquals("[950, 1]", res2.toString()); assertEquals("[1450, 2]", res3.toString()); assertEquals("[1450, 2]", res4.toString()); // REMOVE_END // HIDE_START jedis.close(); } } // HIDE_END ================================================ FILE: src/test/java/io/redis/examples/BloomFilterExample.java ================================================ // EXAMPLE: bf_tutorial // HIDE_START package io.redis.examples; import redis.clients.jedis.RedisClient; import org.junit.jupiter.api.Test; import java.util.List; import static org.junit.jupiter.api.Assertions.assertEquals; public class BloomFilterExample { @Test public void run() { RedisClient jedis = RedisClient.create("redis://localhost:6379"); // HIDE_END // REMOVE_START jedis.del("bikes:models"); // REMOVE_END // STEP_START bloom String res1 = jedis.bfReserve("bikes:models", 0.01, 1000); System.out.println(res1); // >>> OK // REMOVE_START assertEquals("OK", res1); // REMOVE_END boolean res2 = jedis.bfAdd("bikes:models", "Smoky Mountain Striker"); System.out.println(res2); // >>> True boolean res3 = jedis.bfExists("bikes:models", "Smoky Mountain Striker"); System.out.println(res3); // >>> True List res4 = jedis.bfMAdd("bikes:models", "Rocky Mountain Racer", "Cloudy City Cruiser", "Windy City Wippet"); System.out.println(res4); // >>> [True, True, True] List res5 = jedis.bfMExists("bikes:models", "Rocky Mountain Racer", "Cloudy City Cruiser", "Windy City Wippet"); System.out.println(res5); // >>> [True, True, True] // STEP_END // HIDE_START jedis.close(); } } // HIDE_END ================================================ FILE: src/test/java/io/redis/examples/CMSExample.java ================================================ //EXAMPLE: cms_tutorial //HIDE_START package io.redis.examples; //HIDE_END //REMOVE_START import redis.clients.jedis.RedisClient; import org.junit.jupiter.api.Test; import java.util.HashMap; import java.util.List; import java.util.Map; //REMOVE_END public class CMSExample { @Test public void run() { //HIDE_START RedisClient jedis = RedisClient.create("redis://localhost:6379"); //HIDE_END //REMOVE_START jedis.del("bikes:profit"); //REMOVE_END //STEP_START cms String res1 = jedis.cmsInitByProb("bikes:profit", 0.001d, 0.002d); System.out.println(res1); // >>> OK long res2 = jedis.cmsIncrBy("bikes:profit", "Smoky Mountain Striker", 100L); System.out.println(res2); // >>> 100 List res3 = jedis.cmsIncrBy("bikes:profit", new HashMap() {{ put("Rocky Mountain Racer", 200L); put("Cloudy City Cruiser", 150L); }}); System.out.println(res3); // >>> [200, 150] List res4 = jedis.cmsQuery("bikes:profit", "Smoky Mountain Striker"); System.out.println(res4); // >>> [100] Map res5 = jedis.cmsInfo("bikes:profit"); System.out.println(res5.get("width") + " " + res5.get("depth") + " " + res5.get("count")); // >>> 2000 9 450 //STEP_END //HIDE_START jedis.close(); //HIDE_END } } ================================================ FILE: src/test/java/io/redis/examples/CmdsCnxmgmtExample.java ================================================ // EXAMPLE: cmds_cnxmgmt // REMOVE_START package io.redis.examples; import org.junit.jupiter.api.Test; // REMOVE_END import redis.clients.jedis.Jedis; import static org.junit.jupiter.api.Assertions.assertEquals; // HIDE_START public class CmdsCnxmgmtExample { @Test public void run() { // HIDE_END Jedis jedis = new Jedis("redis://localhost:6379"); // STEP_START auth1 // REMOVE_START jedis.configSet("requirepass", "temp_pass"); // REMOVE_END // Note: you must use the `Jedis` class rather than `RedisClient` // to access the `auth` commands. String authResult1 = jedis.auth("default", "temp_pass"); System.out.println(authResult1); // >>> OK // REMOVE_START assertEquals("OK", authResult1); jedis.configSet("requirepass", ""); // REMOVE_END // STEP_END // STEP_START auth2 // REMOVE_START jedis.aclSetUser("test-user", "on", ">strong_password", "+acl"); // REMOVE_END // Note: you must use the `Jedis` class rather than `RedisClient` // to access the `auth` commands. String authResult2 = jedis.auth("test-user", "strong_password"); System.out.println(authResult2); // >>> OK // REMOVE_START assertEquals("OK", authResult2); jedis.aclDelUser("test-user"); // REMOVE_END // STEP_END // HIDE_START } } // HIDE_END ================================================ FILE: src/test/java/io/redis/examples/CmdsGenericExample.java ================================================ // EXAMPLE: cmds_generic // REMOVE_START package io.redis.examples; import org.junit.jupiter.api.Test; // REMOVE_END // HIDE_START import redis.clients.jedis.RedisClient; import redis.clients.jedis.args.ExpiryOption; import static org.junit.jupiter.api.Assertions.assertEquals; // HIDE_END // HIDE_START public class CmdsGenericExample { @Test public void run() { RedisClient jedis = RedisClient.create("redis://localhost:6379"); //REMOVE_START // Clear any keys here before using them in tests. //REMOVE_END // HIDE_END // STEP_START del String delResult1 = jedis.set("key1", "Hello"); System.out.println(delResult1); // >>> OK String delResult2 = jedis.set("key2", "World"); System.out.println(delResult2); // >>> OK long delResult3 = jedis.del("key1", "key2", "key3"); System.out.println(delResult3); // >>> 2 // STEP_END // Tests for 'del' step. // REMOVE_START assertEquals("OK", delResult1); assertEquals("OK", delResult2); assertEquals(2, delResult3); // REMOVE_END // STEP_START expire String expireResult1 = jedis.set("mykey", "Hello"); System.out.println(expireResult1); // >>> OK long expireResult2 = jedis.expire("mykey", 10); System.out.println(expireResult2); // >>> 1 long expireResult3 = jedis.ttl("mykey"); System.out.println(expireResult3); // >>> 10 String expireResult4 = jedis.set("mykey", "Hello World"); System.out.println(expireResult4); // >>> OK long expireResult5 = jedis.ttl("mykey"); System.out.println(expireResult5); // >>> -1 long expireResult6 = jedis.expire("mykey", 10, ExpiryOption.XX); System.out.println(expireResult6); // >>> 0 long expireResult7 = jedis.ttl("mykey"); System.out.println(expireResult7); // >>> -1 long expireResult8 = jedis.expire("mykey", 10, ExpiryOption.NX); System.out.println(expireResult8); // >>> 1 long expireResult9 = jedis.ttl("mykey"); System.out.println(expireResult9); // >>> 10 // STEP_END // Tests for 'expire' step. // REMOVE_START assertEquals("OK", expireResult1); assertEquals(1, expireResult2); assertEquals(10, expireResult3); assertEquals("OK", expireResult4); assertEquals(-1, expireResult5); assertEquals(0, expireResult6); assertEquals(-1, expireResult7); assertEquals(1, expireResult8); assertEquals(10, expireResult9); jedis.del("mykey"); // REMOVE_END // STEP_START ttl String ttlResult1 = jedis.set("mykey", "Hello"); System.out.println(ttlResult1); // >>> OK long ttlResult2 = jedis.expire("mykey", 10); System.out.println(ttlResult2); // >>> 1 long ttlResult3 = jedis.ttl("mykey"); System.out.println(ttlResult3); // >>> 10 // STEP_END // Tests for 'ttl' step. // REMOVE_START assertEquals("OK", ttlResult1); assertEquals(1, ttlResult2); assertEquals(10, ttlResult3); jedis.del("mykey"); // REMOVE_END // HIDE_START jedis.close(); } } // HIDE_END ================================================ FILE: src/test/java/io/redis/examples/CmdsHashExample.java ================================================ // EXAMPLE: cmds_hash // REMOVE_START package io.redis.examples; import org.junit.jupiter.api.Test; // REMOVE_END import java.util.HashMap; import java.util.Map; import java.util.List; import java.util.Collections; // HIDE_START import redis.clients.jedis.RedisClient; // HIDE_END import static java.util.stream.Collectors.toList; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertNull; // HIDE_START public class CmdsHashExample { @Test public void run() { RedisClient jedis = RedisClient.create("redis://localhost:6379"); //REMOVE_START // Clear any keys here before using them in tests. jedis.del("myhash"); //REMOVE_END // HIDE_END // STEP_START hget Map hGetExampleParams = new HashMap<>(); hGetExampleParams.put("field1", "foo"); long hGetResult1 = jedis.hset("myhash", hGetExampleParams); System.out.println(hGetResult1); // >>> 1 String hGetResult2 = jedis.hget("myhash", "field1"); System.out.println(hGetResult2); // >>> foo String hGetResult3 = jedis.hget("myhash", "field2"); System.out.println(hGetResult3); // >>> null // STEP_END // REMOVE_START // Tests for 'hget' step. assertEquals(1, hGetResult1); assertEquals("foo", hGetResult2); assertNull(hGetResult3); jedis.del("myhash"); // REMOVE_END // STEP_START hgetall Map hGetAllExampleParams = new HashMap<>(); hGetAllExampleParams.put("field1", "Hello"); hGetAllExampleParams.put("field2", "World"); long hGetAllResult1 = jedis.hset("myhash", hGetAllExampleParams); System.out.println(hGetAllResult1); // >>> 2 Map hGetAllResult2 = jedis.hgetAll("myhash"); System.out.println( hGetAllResult2.entrySet().stream() .sorted((s1, s2)-> s1.getKey().compareTo(s2.getKey())) .collect(toList()) .toString() ); // >>> [field1=Hello, field2=World] // STEP_END // REMOVE_START // Tests for 'hgetall' step. assertEquals(2, hGetAllResult1); assertEquals("[field1=Hello, field2=World]", hGetAllResult2.entrySet().stream() .sorted((s1, s2)-> s1.getKey().compareTo(s2.getKey())) .collect(toList()) .toString() ); jedis.del("myhash"); // REMOVE_END // STEP_START hset Map hSetExampleParams = new HashMap<>(); hSetExampleParams.put("field1", "Hello"); long hSetResult1 = jedis.hset("myhash", hSetExampleParams); System.out.println(hSetResult1); // >>> 1 String hSetResult2 = jedis.hget("myhash", "field1"); System.out.println(hSetResult2); // >>> Hello hSetExampleParams.clear(); hSetExampleParams.put("field2", "Hi"); hSetExampleParams.put("field3", "World"); long hSetResult3 = jedis.hset("myhash",hSetExampleParams); System.out.println(hSetResult3); // >>> 2 String hSetResult4 = jedis.hget("myhash", "field2"); System.out.println(hSetResult4); // >>> Hi String hSetResult5 = jedis.hget("myhash", "field3"); System.out.println(hSetResult5); // >>> World Map hSetResult6 = jedis.hgetAll("myhash"); for (String key: hSetResult6.keySet()) { System.out.println("Key: " + key + ", Value: " + hSetResult6.get(key)); } // >>> Key: field3, Value: World // >>> Key: field2, Value: Hi // >>> Key: field1, Value: Hello // STEP_END // REMOVE_START // Tests for 'hset' step. assertEquals(1, hSetResult1); assertEquals("Hello", hSetResult2); assertEquals(2, hSetResult3); assertEquals("Hi", hSetResult4); assertEquals("World", hSetResult5); assertEquals(3, hSetResult6.size()); assertEquals("Hello", hSetResult6.get("field1")); assertEquals("Hi", hSetResult6.get("field2")); assertEquals("World", hSetResult6.get("field3")); jedis.del("myhash"); // REMOVE_END // STEP_START hvals Map hValsExampleParams = new HashMap<>(); hValsExampleParams.put("field1", "Hello"); hValsExampleParams.put("field2", "World"); long hValsResult1 = jedis.hset("myhash", hValsExampleParams); System.out.println(hValsResult1); // >>> 2 List hValsResult2 = jedis.hvals("myhash"); Collections.sort(hValsResult2); System.out.println(hValsResult2); // >>> [Hello, World] // STEP_END // REMOVE_START // Tests for 'hvals' step. assertEquals(2, hValsResult1); assertEquals("[Hello, World]", hValsResult2.toString()); jedis.del("myhash"); // REMOVE_END // HIDE_START jedis.close(); } } // HIDE_END ================================================ FILE: src/test/java/io/redis/examples/CmdsListExample.java ================================================ // EXAMPLE: cmds_list // REMOVE_START package io.redis.examples; import org.junit.jupiter.api.Test; // REMOVE_END import java.util.List; // HIDE_START import redis.clients.jedis.RedisClient; import static org.junit.jupiter.api.Assertions.assertEquals; public class CmdsListExample { @Test public void run() { RedisClient jedis = RedisClient.create("redis://localhost:6379"); //REMOVE_START jedis.del("mylist"); //REMOVE_END // HIDE_END // STEP_START llen long lLenResult1 = jedis.lpush("mylist", "World"); System.out.println(lLenResult1); // >>> 1 long lLenResult2 = jedis.lpush("mylist", "Hello"); System.out.println(lLenResult2); // >>> 2 long lLenResult3 = jedis.llen("mylist"); System.out.println(lLenResult3); // >>> 2 // STEP_END // REMOVE_START assertEquals(1, lLenResult1); assertEquals(2, lLenResult2); assertEquals(2, lLenResult3); jedis.del("mylist"); // REMOVE_END // STEP_START lpop long lPopResult1 = jedis.rpush( "mylist", "one", "two", "three", "four", "five" ); System.out.println(lPopResult1); // >>> 5 String lPopResult2 = jedis.lpop("mylist"); System.out.println(lPopResult2); // >>> one List lPopResult3 = jedis.lpop("mylist", 2); System.out.println(lPopResult3); // >>> [two, three] List lPopResult4 = jedis.lrange("mylist", 0, -1); System.out.println(lPopResult4); // >>> [four, five] // STEP_END // REMOVE_START assertEquals(5, lPopResult1); assertEquals("one", lPopResult2); assertEquals("[two, three]", lPopResult3.toString()); assertEquals("[four, five]", lPopResult4.toString()); jedis.del("mylist"); // REMOVE_END // STEP_START lpush long lPushResult1 = jedis.lpush("mylist", "World"); System.out.println(lPushResult1); // >>> 1 long lPushResult2 = jedis.lpush("mylist", "Hello"); System.out.println(lPushResult2); // >>> 2 List lPushResult3 = jedis.lrange("mylist", 0, -1); System.out.println(lPushResult3); // >>> [Hello, World] // STEP_END // REMOVE_START assertEquals(1, lPushResult1); assertEquals(2, lPushResult2); assertEquals("[Hello, World]", lPushResult3.toString()); jedis.del("mylist"); // REMOVE_END // STEP_START lrange long lRangeResult1 = jedis.rpush("mylist", "one", "two", "three"); System.out.println(lRangeResult1); // >>> 3 List lRangeResult2 = jedis.lrange("mylist", 0, 0); System.out.println(lRangeResult2); // >>> [one] List lRangeResult3 = jedis.lrange("mylist", -3, 2); System.out.println(lRangeResult3); // >>> [one, two, three] List lRangeResult4 = jedis.lrange("mylist", -100, 100); System.out.println(lRangeResult4); // >>> [one, two, three] List lRangeResult5 = jedis.lrange("mylist", 5, 10); System.out.println(lRangeResult5); // >>> [] // STEP_END // REMOVE_START assertEquals(3, lRangeResult1); assertEquals("[one]", lRangeResult2.toString()); assertEquals("[one, two, three]", lRangeResult3.toString()); assertEquals("[one, two, three]", lRangeResult4.toString()); assertEquals("[]", lRangeResult5.toString()); jedis.del("mylist"); // REMOVE_END // STEP_START rpop long rPopResult1 = jedis.rpush( "mylist", "one", "two", "three", "four", "five" ); System.out.println(rPopResult1); // >>> 5 String rPopResult2 = jedis.rpop("mylist"); System.out.println(rPopResult2); // >>> five List rPopResult3 = jedis.rpop("mylist", 2); System.out.println(rPopResult3); // >>> [four, three] List rPopResult4 = jedis.lrange("mylist", 0, -1); System.out.println(rPopResult4); // >>> [one, two] // STEP_END // REMOVE_START assertEquals(5, rPopResult1); assertEquals("five", rPopResult2); assertEquals("[four, three]", rPopResult3.toString()); assertEquals("[one, two]", rPopResult4.toString()); jedis.del("mylist"); // REMOVE_END // STEP_START rpush long rPushResult1 = jedis.rpush("mylist", "hello"); System.out.println(rPushResult1); // >>> 1 long rPushResult2 = jedis.rpush("mylist", "world"); System.out.println(rPushResult2); // >>> 2 List rPushResult3 = jedis.lrange("mylist", 0, -1); System.out.println(rPushResult3); // >>> [hello, world] // STEP_END // REMOVE_START assertEquals(1, rPushResult1); assertEquals(2, rPushResult2); assertEquals("[hello, world]", rPushResult3.toString()); jedis.del("mylist"); // REMOVE_END // HIDE_START jedis.close(); } } // HIDE_END ================================================ FILE: src/test/java/io/redis/examples/CmdsServerMgmtExample.java ================================================ // EXAMPLE: cmds_servermgmt // REMOVE_START package io.redis.examples; import org.junit.jupiter.api.Test; // REMOVE_END import java.util.Set; import redis.clients.jedis.Jedis; // HIDE_START import redis.clients.jedis.RedisClient; import static org.junit.jupiter.api.Assertions.assertEquals; public class CmdsServerMgmtExample { @Test public void run() { RedisClient jedis = RedisClient.create("redis://localhost:6379"); // HIDE_END // STEP_START flushall // REMOVE_START jedis.set("testkey1", "1"); jedis.set("testkey2", "2"); jedis.set("testkey3", "3"); // REMOVE_END String flushAllResult1 = jedis.flushAll(); System.out.println(flushAllResult1); // >>> OK Set flushAllResult2 = jedis.keys("*"); System.out.println(flushAllResult2); // >>> [] // STEP_END // REMOVE_START assertEquals("OK", flushAllResult1); assertEquals("[]", flushAllResult2.toString()); // REMOVE_END // STEP_START info // Note: you must use the `Jedis` class to access the `info` // command rather than `RedisClient`. Jedis jedis2 = new Jedis("redis://localhost:6379"); String infoResult = jedis2.info(); // Check the first 8 characters of the result (the full `info` string // is much longer than this). System.out.println(infoResult.substring(0, 8)); // >>> # Server jedis2.close(); // STEP_END // REMOVE_START assertEquals("# Server", infoResult.substring(0, 8)); // REMOVE_END // HIDE_START jedis.close(); } } // HIDE_END ================================================ FILE: src/test/java/io/redis/examples/CmdsSetExample.java ================================================ // EXAMPLE: cmds_set // REMOVE_START package io.redis.examples; import org.junit.jupiter.api.Test; // REMOVE_END import static java.util.stream.Collectors.toList; import static org.junit.jupiter.api.Assertions.assertArrayEquals; import static org.junit.jupiter.api.Assertions.assertEquals; import java.util.Set; // HIDE_START import redis.clients.jedis.RedisClient; public class CmdsSetExample { @Test public void run() { RedisClient jedis = RedisClient.create("redis://localhost:6379"); //REMOVE_START jedis.del("myset"); //REMOVE_END // HIDE_END // STEP_START sadd long sAddResult1 = jedis.sadd("myset", "Hello"); System.out.println(sAddResult1); // >>> 1 long sAddResult2 = jedis.sadd("myset", "World"); System.out.println(sAddResult2); // >>> 1 long sAddResult3 = jedis.sadd("myset", "World"); System.out.println(sAddResult3); // >>> 0 Set sAddResult4 = jedis.smembers("myset"); System.out.println(sAddResult4.stream().sorted().collect(toList())); // >>> [Hello, World] // STEP_END // REMOVE_START assertEquals(1, sAddResult1); assertEquals(1, sAddResult2); assertEquals(0, sAddResult3); assertArrayEquals(new String[] {"Hello", "World"}, sAddResult4.stream().sorted().toArray()); jedis.del("myset"); // REMOVE_END // STEP_START smembers long sMembersResult1 = jedis.sadd("myset", "Hello", "World"); System.out.println(sMembersResult1); // >>> 2 Set sMembersResult2 = jedis.smembers("myset"); System.out.println(sMembersResult2.stream().sorted().collect(toList())); // >>> [Hello, World] // STEP_END // REMOVE_START assertEquals(2, sMembersResult1); assertArrayEquals(new String[] {"Hello", "World"}, sMembersResult2.stream().sorted().toArray()); // REMOVE_END // HIDE_START jedis.close(); } } // HIDE_END ================================================ FILE: src/test/java/io/redis/examples/CmdsSortedSetExample.java ================================================ // EXAMPLE: cmds_sorted_set // REMOVE_START package io.redis.examples; import org.junit.jupiter.api.Test; // REMOVE_END import java.util.HashMap; import java.util.Map; import java.util.List; import redis.clients.jedis.RedisClient; import redis.clients.jedis.params.ZRangeParams; import redis.clients.jedis.resps.Tuple; import static org.junit.jupiter.api.Assertions.assertEquals; // HIDE_START public class CmdsSortedSetExample { @Test public void run() { RedisClient jedis = RedisClient.create("redis://localhost:6379"); //REMOVE_START // Clear any keys here before using them in tests. jedis.del("myzset"); //REMOVE_END // HIDE_END // STEP_START bzmpop // STEP_END // Tests for 'bzmpop' step. // REMOVE_START // REMOVE_END // STEP_START bzpopmax // STEP_END // Tests for 'bzpopmax' step. // REMOVE_START // REMOVE_END // STEP_START bzpopmin // STEP_END // Tests for 'bzpopmin' step. // REMOVE_START // REMOVE_END // STEP_START zadd Map zAddExampleParams = new HashMap<>(); zAddExampleParams.put("one", 1.0); long zAddResult1 = jedis.zadd("myzset", zAddExampleParams); System.out.println(zAddResult1); // >>> 1 zAddExampleParams.clear(); zAddExampleParams.put("uno", 1.0); long zAddResult2 = jedis.zadd("myzset", zAddExampleParams); System.out.println(zAddResult2); // >>> 1 zAddExampleParams.clear(); zAddExampleParams.put("two", 2.0); zAddExampleParams.put("three", 3.0); long zAddResult3 = jedis.zadd("myzset", zAddExampleParams); System.out.println(zAddResult3); // >>> 2 List zAddResult4 = jedis.zrangeWithScores("myzset", new ZRangeParams(0, -1)); for (Tuple item: zAddResult4) { System.out.println("Element: " + item.getElement() + ", Score: " + item.getScore()); } // >>> Element: one, Score: 1.0 // >>> Element: uno, Score: 1.0 // >>> Element: two, Score: 2.0 // >>> Element: three, Score: 3.0 // STEP_END // Tests for 'zadd' step. // REMOVE_START assertEquals(1, zAddResult1); assertEquals(1, zAddResult2); assertEquals(2, zAddResult3); assertEquals(new Tuple("one", 1.0), zAddResult4.get(0)); assertEquals(new Tuple("uno", 1.0), zAddResult4.get(1)); assertEquals(new Tuple("two", 2.0), zAddResult4.get(2)); assertEquals(new Tuple("three", 3.0), zAddResult4.get(3)); jedis.del("myzset"); // REMOVE_END // STEP_START zcard // STEP_END // Tests for 'zcard' step. // REMOVE_START // REMOVE_END // STEP_START zcount // STEP_END // Tests for 'zcount' step. // REMOVE_START // REMOVE_END // STEP_START zdiff // STEP_END // Tests for 'zdiff' step. // REMOVE_START // REMOVE_END // STEP_START zdiffstore // STEP_END // Tests for 'zdiffstore' step. // REMOVE_START // REMOVE_END // STEP_START zincrby // STEP_END // Tests for 'zincrby' step. // REMOVE_START // REMOVE_END // STEP_START zinter // STEP_END // Tests for 'zinter' step. // REMOVE_START // REMOVE_END // STEP_START zintercard // STEP_END // Tests for 'zintercard' step. // REMOVE_START // REMOVE_END // STEP_START zinterstore // STEP_END // Tests for 'zinterstore' step. // REMOVE_START // REMOVE_END // STEP_START zlexcount // STEP_END // Tests for 'zlexcount' step. // REMOVE_START // REMOVE_END // STEP_START zmpop // STEP_END // Tests for 'zmpop' step. // REMOVE_START // REMOVE_END // STEP_START zmscore // STEP_END // Tests for 'zmscore' step. // REMOVE_START // REMOVE_END // STEP_START zpopmax // STEP_END // Tests for 'zpopmax' step. // REMOVE_START // REMOVE_END // STEP_START zpopmin // STEP_END // Tests for 'zpopmin' step. // REMOVE_START // REMOVE_END // STEP_START zrandmember // STEP_END // Tests for 'zrandmember' step. // REMOVE_START // REMOVE_END // STEP_START zrange1 Map zRangeExampleParams1 = new HashMap<>(); zRangeExampleParams1.put("one", 1.0); zRangeExampleParams1.put("two", 2.0); zRangeExampleParams1.put("three", 3.0); long zRangeResult1 = jedis.zadd("myzset", zRangeExampleParams1); System.out.println(zRangeResult1); // >>> 3 List zRangeResult2 = jedis.zrange("myzset", new ZRangeParams(0, -1)); System.out.println(String.join(", ", zRangeResult2)); // >>> one, two, three List zRangeResult3 = jedis.zrange("myzset", new ZRangeParams(2, 3)); System.out.println(String.join(", ", zRangeResult3)); // >> three List zRangeResult4 = jedis.zrange("myzset", new ZRangeParams(-2, -1)); System.out.println(String.join(", ", zRangeResult4)); // >> two, three // STEP_END // Tests for 'zrange1' step. // REMOVE_START assertEquals(3, zRangeResult1); assertEquals("one, two, three", String.join(", ", zRangeResult2)); assertEquals("three", String.join(", ", zRangeResult3)); assertEquals("two, three", String.join(", ", zRangeResult4)); jedis.del("myzset"); // REMOVE_END // STEP_START zrange2 Map zRangeExampleParams2 = new HashMap<>(); zRangeExampleParams2.put("one", 1.0); zRangeExampleParams2.put("two", 2.0); zRangeExampleParams2.put("three", 3.0); long zRangeResult5 = jedis.zadd("myzset", zRangeExampleParams2); System.out.println(zRangeResult5); // >>> 3 List zRangeResult6 = jedis.zrangeWithScores("myzset", new ZRangeParams(0, 1)); for (Tuple item: zRangeResult6) { System.out.println("Element: " + item.getElement() + ", Score: " + item.getScore()); } // >>> Element: one, Score: 1.0 // >>> Element: two, Score: 2.0 // STEP_END // Tests for 'zrange2' step. // REMOVE_START assertEquals(3, zRangeResult5); assertEquals(new Tuple("one", 1.0), zRangeResult6.get(0)); assertEquals(new Tuple("two", 2.0), zRangeResult6.get(1)); jedis.del("myzset"); // REMOVE_END // STEP_START zrange3 Map zRangeExampleParams3 = new HashMap<>(); zRangeExampleParams3.put("one", 1.0); zRangeExampleParams3.put("two", 2.0); zRangeExampleParams3.put("three", 3.0); long zRangeResult7 = jedis.zadd("myzset", zRangeExampleParams3); System.out.println(zRangeResult7); // >>> 3 List zRangeResult8 = jedis.zrangeByScore("myzset", "(1", "+inf", 1, 1); System.out.println(String.join(", ", zRangeResult8)); // >>> three // STEP_END // Tests for 'zrange3' step. // REMOVE_START assertEquals(3, zRangeResult7); assertEquals("three", String.join(", ", zRangeResult8)); jedis.del("myzset"); // REMOVE_END // STEP_START zrangebylex // STEP_END // Tests for 'zrangebylex' step. // REMOVE_START // REMOVE_END // STEP_START zrangebyscore // STEP_END // Tests for 'zrangebyscore' step. // REMOVE_START // REMOVE_END // STEP_START zrangestore // STEP_END // Tests for 'zrangestore' step. // REMOVE_START // REMOVE_END // STEP_START zrank // STEP_END // Tests for 'zrank' step. // REMOVE_START // REMOVE_END // STEP_START zrem // STEP_END // Tests for 'zrem' step. // REMOVE_START // REMOVE_END // STEP_START zremrangebylex // STEP_END // Tests for 'zremrangebylex' step. // REMOVE_START // REMOVE_END // STEP_START zremrangebyrank // STEP_END // Tests for 'zremrangebyrank' step. // REMOVE_START // REMOVE_END // STEP_START zremrangebyscore // STEP_END // Tests for 'zremrangebyscore' step. // REMOVE_START // REMOVE_END // STEP_START zrevrange // STEP_END // Tests for 'zrevrange' step. // REMOVE_START // REMOVE_END // STEP_START zrevrangebylex // STEP_END // Tests for 'zrevrangebylex' step. // REMOVE_START // REMOVE_END // STEP_START zrevrangebyscore // STEP_END // Tests for 'zrevrangebyscore' step. // REMOVE_START // REMOVE_END // STEP_START zrevrank // STEP_END // Tests for 'zrevrank' step. // REMOVE_START // REMOVE_END // STEP_START zscan // STEP_END // Tests for 'zscan' step. // REMOVE_START // REMOVE_END // STEP_START zscore // STEP_END // Tests for 'zscore' step. // REMOVE_START // REMOVE_END // STEP_START zunion // STEP_END // Tests for 'zunion' step. // REMOVE_START // REMOVE_END // STEP_START zunionstore // STEP_END // Tests for 'zunionstore' step. // REMOVE_START // REMOVE_END // HIDE_START jedis.close(); } } // HIDE_END ================================================ FILE: src/test/java/io/redis/examples/CmdsStringExample.java ================================================ // EXAMPLE: cmds_string // REMOVE_START package io.redis.examples; import org.junit.jupiter.api.Test; // REMOVE_END // HIDE_START import redis.clients.jedis.RedisClient; import static org.junit.jupiter.api.Assertions.assertEquals; // HIDE_END // HIDE_START public class CmdsStringExample { @Test public void run() { RedisClient jedis = RedisClient.create("redis://localhost:6379"); //REMOVE_START // Clear any keys here before using them in tests. jedis.del("mykey"); //REMOVE_END // HIDE_END // STEP_START incr String incrResult1 = jedis.set("mykey", "10"); System.out.println(incrResult1); // >>> OK long incrResult2 = jedis.incr("mykey"); System.out.println(incrResult2); // >>> 11 String incrResult3 = jedis.get("mykey"); System.out.println(incrResult3); // >>> 11 // STEP_END // Tests for 'incr' step. // REMOVE_START assertEquals("OK", incrResult1); assertEquals(11, incrResult2); assertEquals("11", incrResult3); jedis.del("mykey"); // REMOVE_END // HIDE_START jedis.close(); } } // HIDE_END ================================================ FILE: src/test/java/io/redis/examples/CuckooFilterExample.java ================================================ // EXAMPLE: cuckoo_tutorial // HIDE_START package io.redis.examples; import org.junit.jupiter.api.Test; import redis.clients.jedis.RedisClient; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertTrue; public class CuckooFilterExample { @Test public void run() { RedisClient jedis = RedisClient.create("redis://localhost:6379"); // HIDE_END // REMOVE_START jedis.del("bikes:models"); // REMOVE_END // STEP_START cuckoo String res1 = jedis.cfReserve("bikes:models", 1000000); System.out.println(res1); // >>> OK // REMOVE_START assertEquals(res1, "OK"); // REMOVE_END boolean res2 = jedis.cfAdd("bikes:models", "Smoky Mountain Striker"); System.out.println(res2); // >>> True boolean res3 = jedis.cfExists("bikes:models", "Smoky Mountain Striker"); System.out.println(res3); // >>> True boolean res4 = jedis.cfExists("bikes:models", "Terrible Bike Name"); System.out.println(res4); // >>> False boolean res5 = jedis.cfDel("bikes:models", "Smoky Mountain Striker"); System.out.println(res5); // >>> True // REMOVE_START assertTrue(res5); // REMOVE_END // STEP_END // HIDE_START jedis.close(); } } // HIDE_END ================================================ FILE: src/test/java/io/redis/examples/GeoExample.java ================================================ //EXAMPLE: geo_tutorial package io.redis.examples; // REMOVE_START import org.junit.jupiter.api.Test; // REMOVE_END import redis.clients.jedis.GeoCoordinate; import redis.clients.jedis.RedisClient; import redis.clients.jedis.args.GeoUnit; import redis.clients.jedis.resps.GeoRadiusResponse; import java.util.List; import java.util.stream.Collectors; import static org.junit.jupiter.api.Assertions.assertEquals; public class GeoExample { @Test public void run() { try (RedisClient jedis = RedisClient.create("redis://localhost:6379")) { // REMOVE_START jedis.del("bikes:rentable"); // REMOVE_END // STEP_START geoadd long res1 = jedis.geoadd("bikes:rentable", -122.27652, 37.805186, "station:1"); System.out.println(res1); // 1 long res2 = jedis.geoadd("bikes:rentable", -122.2674626, 37.8062344, "station:2"); System.out.println(res2); // 1 long res3 = jedis.geoadd("bikes:rentable", -122.2469854, 37.8104049, "station:3"); System.out.println(res2); // 1 // STEP_END // REMOVE_START assertEquals(1, res1); assertEquals(1, res1); assertEquals(1, res1); // REMOVE_END // STEP_START geosearch List res4 = jedis.geosearch( "bikes:rentable", new GeoCoordinate(-122.27652, 37.805186), 5, GeoUnit.KM ); List members = res4.stream() // .map(GeoRadiusResponse::getMemberByString) // .collect(Collectors.toList()); System.out.println(members); // [station:1, station:2, station:3] // STEP_END // REMOVE_START assertEquals("[station:1, station:2, station:3]", members.toString()); // REMOVE_END } } } ================================================ FILE: src/test/java/io/redis/examples/GeoIndexExample.java ================================================ // EXAMPLE: geoindex // REMOVE_START package io.redis.examples; import org.junit.jupiter.api.Test; // REMOVE_END // HIDE_START import org.json.JSONObject; import redis.clients.jedis.RedisClient; import redis.clients.jedis.json.Path2; import redis.clients.jedis.search.Document; import redis.clients.jedis.search.FTCreateParams; import redis.clients.jedis.search.FTSearchParams; import redis.clients.jedis.search.IndexDataType; import redis.clients.jedis.search.schemafields.*; import redis.clients.jedis.search.schemafields.GeoShapeField.CoordinateSystem; import redis.clients.jedis.search.SearchResult; import redis.clients.jedis.exceptions.JedisDataException; import static org.junit.jupiter.api.Assertions.assertEquals; // HIDE_END // HIDE_START public class GeoIndexExample { @Test public void run() { RedisClient jedis = RedisClient.create("redis://localhost:6379"); //REMOVE_START // Clear any keys here before using them in tests. try { jedis.ftDropIndex("productidx"); } catch (JedisDataException j) {} try { jedis.ftDropIndex("geomidx"); } catch (JedisDataException j) {} jedis.del("product:46885", "product:46886", "shape:1", "shape:2", "shape:3", "shape:4"); //REMOVE_END // HIDE_END // STEP_START create_geo_idx SchemaField[] geoSchema = { GeoField.of("$.location").as("location") }; String geoIdxCreateResult = jedis.ftCreate("productidx", FTCreateParams.createParams() .on(IndexDataType.JSON) .addPrefix("product:"), geoSchema ); // STEP_END // REMOVE_START assertEquals("OK", geoIdxCreateResult); // REMOVE_END // STEP_START add_geo_json JSONObject prd46885 = new JSONObject() .put("description", "Navy Blue Slippers") .put("price", 45.99) .put("city", "Denver") .put("location", "-104.991531, 39.742043"); String jsonAddResult1 = jedis.jsonSet("product:46885", new Path2("$"), prd46885); System.out.println(jsonAddResult1); // >>> OK JSONObject prd46886 = new JSONObject() .put("description", "Bright Green Socks") .put("price", 25.50) .put("city", "Fort Collins") .put("location", "-105.0618814,40.5150098"); String jsonAddResult2 = jedis.jsonSet("product:46886", new Path2("$"), prd46886); System.out.println(jsonAddResult2); // >>> OK // STEP_END // REMOVE_START assertEquals("OK", jsonAddResult1); assertEquals("OK", jsonAddResult2); // REMOVE_END // STEP_START geo_query SearchResult geoResult = jedis.ftSearch("productidx", "@location:[-104.800644 38.846127 100 mi]" ); System.out.println(geoResult.getTotalResults()); // >>> 1 for (Document doc: geoResult.getDocuments()) { System.out.println(doc.getId()); } // >>> product:46885 // STEP_END // REMOVE_START assertEquals("OK", jsonAddResult1); assertEquals("OK", jsonAddResult2); assertEquals("product:46885", geoResult.getDocuments().get(0).getId()); // REMOVE_END // STEP_START create_gshape_idx SchemaField[] geomSchema = { TextField.of("$.name").as("name"), GeoShapeField.of("$.geom", CoordinateSystem.FLAT).as("geom") }; String geomIndexCreateResult = jedis.ftCreate("geomidx", FTCreateParams.createParams() .on(IndexDataType.JSON) .addPrefix("shape"), geomSchema ); System.out.println(geomIndexCreateResult); // >>> OK // STEP_END // REMOVE_START assertEquals("OK", geomIndexCreateResult); // REMOVE_END // STEP_START add_gshape_json JSONObject shape1 = new JSONObject() .put("name", "Green Square") .put("geom", "POLYGON ((1 1, 1 3, 3 3, 3 1, 1 1))"); String gmJsonRes1 = jedis.jsonSet("shape:1", new Path2("$"), shape1); System.out.println(gmJsonRes1); // >>> OK JSONObject shape2 = new JSONObject() .put("name", "Red Rectangle") .put("geom", "POLYGON ((2 2.5, 2 3.5, 3.5 3.5, 3.5 2.5, 2 2.5))"); String gmJsonRes2 = jedis.jsonSet("shape:2", new Path2("$"), shape2); System.out.println(gmJsonRes2); // >>> OK JSONObject shape3 = new JSONObject() .put("name", "Blue Triangle") .put("geom", "POLYGON ((3.5 1, 3.75 2, 4 1, 3.5 1))"); String gmJsonRes3 = jedis.jsonSet("shape:3", new Path2("$"), shape3); System.out.println(gmJsonRes3); // >>> OK JSONObject shape4 = new JSONObject() .put("name", "Purple Point") .put("geom", "POINT (2 2)"); String gmJsonRes4 = jedis.jsonSet("shape:4", new Path2("$"), shape4); System.out.println(gmJsonRes4); // >>> OK // STEP_END // REMOVE_START assertEquals("OK", gmJsonRes1); assertEquals("OK", gmJsonRes2); assertEquals("OK", gmJsonRes3); assertEquals("OK", gmJsonRes4); // REMOVE_END // STEP_START gshape_query SearchResult geomResult = jedis.ftSearch("geomidx", "(-@name:(Green Square) @geom:[WITHIN $qshape])", FTSearchParams.searchParams() .addParam("qshape", "POLYGON ((1 1, 1 3, 3 3, 3 1, 1 1))") .dialect(4) .limit(0, 1) ); System.out.println(geomResult.getTotalResults()); // >>> 1 for (Document doc: geomResult.getDocuments()) { System.out.println(doc.getId()); } // shape:4 // STEP_END // REMOVE_START assertEquals(1, geomResult.getTotalResults()); assertEquals("shape:4", geomResult.getDocuments().get(0).getId()); // REMOVE_END // HIDE_START jedis.close(); } } // HIDE_END ================================================ FILE: src/test/java/io/redis/examples/HashExample.java ================================================ //EXAMPLE: hash_tutorial package io.redis.examples; import redis.clients.jedis.RedisClient; import java.util.HashMap; import java.util.List; import java.util.Map; //REMOVE_START import org.junit.jupiter.api.Test; import static org.junit.jupiter.api.Assertions.assertEquals; //REMOVE_END public class HashExample { @Test public void run() { try (RedisClient jedis = RedisClient.create("redis://localhost:6379")) { // REMOVE_START jedis.del("bike:1", "bike:1:stats"); // REMOVE_END // STEP_START set_get_all Map bike1 = new HashMap<>(); bike1.put("model", "Deimos"); bike1.put("brand", "Ergonom"); bike1.put("type", "Enduro bikes"); bike1.put("price", "4972"); Long res1 = jedis.hset("bike:1", bike1); System.out.println(res1); // 4 String res2 = jedis.hget("bike:1", "model"); System.out.println(res2); // Deimos String res3 = jedis.hget("bike:1", "price"); System.out.println(res3); // 4972 Map res4 = jedis.hgetAll("bike:1"); System.out.println(res4); // {type=Enduro bikes, brand=Ergonom, price=4972, model=Deimos} // STEP_END // REMOVE_START assertEquals(4, res1.longValue()); assertEquals("Deimos", res2); assertEquals("4972", res3); assertEquals("Deimos", res4.get("model")); assertEquals("Ergonom", res4.get("brand")); assertEquals("Enduro bikes", res4.get("type")); assertEquals("4972", res4.get("price")); // REMOVE_END // STEP_START hmget List res5 = jedis.hmget("bike:1", "model", "price"); System.out.println(res5); // [Deimos, 4972] // STEP_END // REMOVE_START assert res5.toString().equals("[Deimos, 4972]"); // REMOVE_END // STEP_START hincrby Long res6 = jedis.hincrBy("bike:1", "price", 100); System.out.println(res6); // 5072 Long res7 = jedis.hincrBy("bike:1", "price", -100); System.out.println(res7); // 4972 // STEP_END // REMOVE_START assertEquals(5072, res6.longValue()); assertEquals(4972, res7.longValue()); // REMOVE_END // STEP_START incrby_get_mget Long res8 = jedis.hincrBy("bike:1:stats", "rides", 1); System.out.println(res8); // 1 Long res9 = jedis.hincrBy("bike:1:stats", "rides", 1); System.out.println(res9); // 2 Long res10 = jedis.hincrBy("bike:1:stats", "rides", 1); System.out.println(res10); // 3 Long res11 = jedis.hincrBy("bike:1:stats", "crashes", 1); System.out.println(res11); // 1 Long res12 = jedis.hincrBy("bike:1:stats", "owners", 1); System.out.println(res12); // 1 String res13 = jedis.hget("bike:1:stats", "rides"); System.out.println(res13); // 3 List res14 = jedis.hmget("bike:1:stats", "crashes", "owners"); System.out.println(res14); // [1, 1] // STEP_END // REMOVE_START assertEquals(1, res8.longValue()); assertEquals(2, res9.longValue()); assertEquals(3, res10.longValue()); assertEquals(1, res11.longValue()); assertEquals(1, res12.longValue()); assertEquals("3", res13); assertEquals("[1, 1]", res14.toString()); // REMOVE_END } } } ================================================ FILE: src/test/java/io/redis/examples/HomeJsonExample.java ================================================ // EXAMPLE: java_home_json // BINDER_ID jedis-java_home_json // REMOVE_START package io.redis.examples; import org.junit.jupiter.api.Test; import static org.junit.jupiter.api.Assertions.assertArrayEquals; import static org.junit.jupiter.api.Assertions.assertEquals; // REMOVE_END // STEP_START import import redis.clients.jedis.RedisClient; import redis.clients.jedis.exceptions.JedisDataException; import redis.clients.jedis.json.Path2; import redis.clients.jedis.search.*; import redis.clients.jedis.search.aggr.*; import redis.clients.jedis.search.schemafields.*; import org.json.JSONObject; import java.util.HashMap; import java.util.List; import java.util.Map; // STEP_END public class HomeJsonExample { @Test public void run() { // STEP_START create_data JSONObject user1 = new JSONObject() .put("name", "Paul John") .put("email", "paul.john@example.com") .put("age", 42) .put("city", "London"); JSONObject user2 = new JSONObject() .put("name", "Eden Zamir") .put("email", "eden.zamir@example.com") .put("age", 29) .put("city", "Tel Aviv"); JSONObject user3 = new JSONObject() .put("name", "Paul Zamir") .put("email", "paul.zamir@example.com") .put("age", 35) .put("city", "Tel Aviv"); // STEP_END // STEP_START connect RedisClient jedis = RedisClient.create("redis://localhost:6379"); // STEP_END // STEP_START cleanup_json try {jedis.ftDropIndex("idx:users");} catch (JedisDataException j){} jedis.del("user:1", "user:2", "user:3"); // STEP_END // STEP_START make_index SchemaField[] schema = { TextField.of("$.name").as("name"), TextField.of("$.city").as("city"), NumericField.of("$.age").as("age") }; String createResult = jedis.ftCreate("idx:users", FTCreateParams.createParams() .on(IndexDataType.JSON) .addPrefix("user:"), schema ); System.out.println(createResult); // >>> OK // STEP_END // REMOVE_START assertEquals("OK", createResult); // REMOVE_END // STEP_START add_data String user1Set = jedis.jsonSet("user:1", new Path2("$"), user1); String user2Set = jedis.jsonSet("user:2", new Path2("$"), user2); String user3Set = jedis.jsonSet("user:3", new Path2("$"), user3); // STEP_END // REMOVE_START assertEquals("OK", user1Set); assertEquals("OK", user2Set); assertEquals("OK", user3Set); // REMOVE_END // STEP_START query1 SearchResult findPaulResult = jedis.ftSearch("idx:users", "Paul @age:[30 40]" ); System.out.println(findPaulResult.getTotalResults()); // >>> 1 List paulDocs = findPaulResult.getDocuments(); for (Document doc: paulDocs) { System.out.println(doc.getId()); } // >>> user:3 // STEP_END // REMOVE_START assertEquals("user:3", paulDocs.get(0).getId()); // REMOVE_END // STEP_START query2 SearchResult citiesResult = jedis.ftSearch("idx:users", "Paul", FTSearchParams.searchParams() .returnFields("city") ); System.out.println(citiesResult.getTotalResults()); // >>> 2 for (Document doc: citiesResult.getDocuments()) { System.out.println(doc.getId()); } // >>> user:1 // >>> user:3 // STEP_END // REMOVE_START assertArrayEquals( new String[] {"user:1", "user:3"}, citiesResult.getDocuments().stream().map(Document::getId).sorted().toArray() ); // REMOVE_END // STEP_START query3 AggregationResult aggResult = jedis.ftAggregate("idx:users", new AggregationBuilder("*") .groupBy("@city", Reducers.count().as("count")) ); System.out.println(aggResult.getTotalResults()); // >>> 2 for (Row cityRow: aggResult.getRows()) { System.out.printf("%s - %d%n", cityRow.getString("city"), cityRow.getLong("count")); } // >>> London - 1 // >>> Tel Aviv - 2 // STEP_END // REMOVE_START assertArrayEquals( new String[] {"London - 1", "Tel Aviv - 2"}, aggResult.getRows().stream() .map(r -> r.getString("city") + " - " + r.getString("count")) .sorted().toArray()); // REMOVE_END // STEP_START cleanup_hash try {jedis.ftDropIndex("hash-idx:users");} catch (JedisDataException j){} jedis.del("huser:1", "huser:2", "huser:3"); // STEP_END // STEP_START make_hash_index SchemaField[] hashSchema = { TextField.of("name"), TextField.of("city"), NumericField.of("age") }; String hashCreateResult = jedis.ftCreate("hash-idx:users", FTCreateParams.createParams() .on(IndexDataType.HASH) .addPrefix("huser:"), hashSchema ); System.out.println(hashCreateResult); // >>> OK // STEP_END // REMOVE_START assertEquals("OK", hashCreateResult); // REMOVE_END // STEP_START add_hash_data Map user1Info = new HashMap<>(); user1Info.put("name", "Paul John"); user1Info.put("email", "paul.john@example.com"); user1Info.put("age", "42"); user1Info.put("city", "London"); long huser1Set = jedis.hset("huser:1", user1Info); System.out.println(huser1Set); // >>> 4 Map user2Info = new HashMap<>(); user2Info.put("name", "Eden Zamir"); user2Info.put("email", "eden.zamir@example.com"); user2Info.put("age", "29"); user2Info.put("city", "Tel Aviv"); long huser2Set = jedis.hset("huser:2", user2Info); System.out.println(huser2Set); // >>> 4 Map user3Info = new HashMap<>(); user3Info.put("name", "Paul Zamir"); user3Info.put("email", "paul.zamir@example.com"); user3Info.put("age", "35"); user3Info.put("city", "Tel Aviv"); long huser3Set = jedis.hset("huser:3", user3Info); System.out.println(huser3Set); // >>> 4 // STEP_END // REMOVE_START assertEquals(4, huser1Set); assertEquals(4, huser2Set); assertEquals(4, huser3Set); // REMOVE_END // STEP_START query1_hash SearchResult findPaulHashResult = jedis.ftSearch("hash-idx:users", "Paul @age:[30 40]" ); System.out.println(findPaulHashResult.getTotalResults()); // >>> 1 List paulHashDocs = findPaulHashResult.getDocuments(); for (Document doc: paulHashDocs) { System.out.println(doc.getId()); } // >>> user:3 // STEP_END // REMOVE_START assertEquals("huser:3", paulHashDocs.get(0).getId()); // REMOVE_END // STEP_START close jedis.close(); // STEP_END } } ================================================ FILE: src/test/java/io/redis/examples/HomeProbDtsExample.java ================================================ // EXAMPLE: home_prob_dts package io.redis.examples; // REMOVE_START import org.junit.jupiter.api.Test; import redis.clients.jedis.RedisClient; import static org.junit.jupiter.api.Assertions.assertTrue; import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertEquals; // REMOVE_END import java.util.HashMap; import java.util.List; import java.util.Map; public class HomeProbDtsExample { @Test public void run() { RedisClient jedis = RedisClient.create("redis://localhost:6379"); // REMOVE_START jedis.del( "recorded_users", "other_users", "group:1", "group:2", "both_groups", "items_sold", "male_heights", "female_heights", "all_heights", "top_3_songs" ); // REMOVE_END // STEP_START bloom List res1 = jedis.bfMAdd( "recorded_users", "andy", "cameron", "david", "michelle" ); System.out.println(res1); // >>> [true, true, true, true] boolean res2 = jedis.bfExists("recorded_users", "cameron"); System.out.println(res2); // >>> true boolean res3 = jedis.bfExists("recorded_users", "kaitlyn"); System.out.println(res3); // >>> false // STEP_END // REMOVE_START assertEquals("[true, true, true, true]", res1.toString()); assertTrue(res2); assertFalse(res3); // REMOVE_END // STEP_START cuckoo boolean res4 = jedis.cfAdd("other_users", "paolo"); System.out.println(res4); // >>> true boolean res5 = jedis.cfAdd("other_users", "kaitlyn"); System.out.println(res5); // >>> true boolean res6 = jedis.cfAdd("other_users", "rachel"); System.out.println(res6); // >>> true List res7 = jedis.cfMExists( "other_users", "paolo", "rachel", "andy" ); System.out.println(res7); // >>> [true, true, false] boolean res8 = jedis.cfDel("other_users", "paolo"); System.out.println(res8); // >>> true boolean res9 = jedis.cfExists("other_users", "paolo"); System.out.println(res9); // >>> false // STEP_END // REMOVE_START assertTrue(res4); assertTrue(res5); assertTrue(res6); assertEquals("[true, true, false]", res7.toString()); assertTrue(res8); assertFalse(res9); // REMOVE_END // STEP_START hyperloglog long res10 = jedis.pfadd("group:1", "andy", "cameron", "david"); System.out.println(res10); // >>> 1 long res11 = jedis.pfcount("group:1"); System.out.println(res11); // >>> 3 long res12 = jedis.pfadd( "group:2", "kaitlyn", "michelle", "paolo", "rachel" ); System.out.println(res12); // >>> 1 long res13 = jedis.pfcount("group:2"); System.out.println(res13); // >>> 4 String res14 = jedis.pfmerge("both_groups", "group:1", "group:2"); System.out.println(res14); // >>> OK long res15 = jedis.pfcount("both_groups"); System.out.println(res15); // >>> 7 // STEP_END // REMOVE_START assertEquals(1, res10); assertEquals(3, res11); assertEquals(1, res12); assertEquals(4, res13); assertEquals("OK", res14); assertEquals(7, res15); // REMOVE_END // STEP_START cms // Specify that you want to keep the counts within 0.01 // (0.1%) of the true value with a 0.005 (0.05%) chance // of going outside this limit. String res16 = jedis.cmsInitByProb("items_sold", 0.01, 0.005); System.out.println(res16); // >>> OK Map firstItemIncrements = new HashMap<>(); firstItemIncrements.put("bread", 300L); firstItemIncrements.put("tea", 200L); firstItemIncrements.put("coffee", 200L); firstItemIncrements.put("beer", 100L); List res17 = jedis.cmsIncrBy("items_sold", firstItemIncrements ); res17.sort(null); System.out.println(); // >>> [100, 200, 200, 300] Map secondItemIncrements = new HashMap<>(); secondItemIncrements.put("bread", 100L); secondItemIncrements.put("coffee", 150L); List res18 = jedis.cmsIncrBy("items_sold", secondItemIncrements ); res18.sort(null); System.out.println(res18); // >>> [350, 400] List res19 = jedis.cmsQuery( "items_sold", "bread", "tea", "coffee", "beer" ); res19.sort(null); System.out.println(res19); // >>> [100, 200, 350, 400] // STEP_END // REMOVE_START assertEquals("OK", res16); assertEquals("[100, 200, 200, 300]", res17.toString()); assertEquals("[350, 400]", res18.toString()); assertEquals("[100, 200, 350, 400]", res19.toString()); // REMOVE_END // STEP_START tdigest String res20 = jedis.tdigestCreate("male_heights"); System.out.println(res20); // >>> OK String res21 = jedis.tdigestAdd("male_heights", 175.5, 181, 160.8, 152, 177, 196, 164); System.out.println(res21); // >>> OK double res22 = jedis.tdigestMin("male_heights"); System.out.println(res22); // >>> 152.0 double res23 = jedis.tdigestMax("male_heights"); System.out.println(res23); // >>> 196.0 List res24 = jedis.tdigestQuantile("male_heights", 0.75); System.out.println(res24); // >>> [181.0] // Note that the CDF value for 181 is not exactly 0.75. // Both values are estimates. List res25 = jedis.tdigestCDF("male_heights", 181); System.out.println(res25); // >>> [0.7857142857142857] String res26 = jedis.tdigestCreate("female_heights"); System.out.println(res26); // >>> OK String res27 = jedis.tdigestAdd("female_heights", 155.5, 161, 168.5, 170, 157.5, 163, 171); System.out.println(res27); // >>> OK List res28 = jedis.tdigestQuantile("female_heights", 0.75); System.out.println(res28); // >>> [170.0] String res29 = jedis.tdigestMerge( "all_heights", "male_heights", "female_heights" ); System.out.println(res29); // >>> OK List res30 = jedis.tdigestQuantile("all_heights", 0.75); System.out.println(res30); // >>> [175.5] // STEP_END // REMOVE_START assertEquals("OK", res20); assertEquals("OK", res21); assertEquals(152.0, res22); assertEquals(196.0, res23); assertEquals("[181.0]", res24.toString()); assertEquals("[0.7857142857142857]", res25.toString()); assertEquals("OK", res26); assertEquals("OK", res27); assertEquals("[170.0]", res28.toString()); assertEquals("OK", res29); assertEquals("[175.5]", res30.toString()); // REMOVE_END // STEP_START topk String res31 = jedis.topkReserve("top_3_songs", 3L, 2000L, 7L, 0.925D); System.out.println(res31); // >>> OK Map songIncrements = new HashMap<>(); songIncrements.put("Starfish Trooper", 3000L); songIncrements.put("Only one more time", 1850L); songIncrements.put("Rock me, Handel", 1325L); songIncrements.put("How will anyone know?", 3890L); songIncrements.put("Average lover", 4098L); songIncrements.put("Road to everywhere", 770L); List res32 = jedis.topkIncrBy("top_3_songs", songIncrements ); System.out.println(res32); // >>> [null, null, null, null, null, Rock me, Handel] List res33 = jedis.topkList("top_3_songs"); System.out.println(res33); // >>> [Average lover, How will anyone know?, Starfish Trooper] List res34 = jedis.topkQuery("top_3_songs", "Starfish Trooper", "Road to everywhere" ); System.out.println(res34); // >>> [true, false] // STEP_END // REMOVE_START assertEquals("OK", res31); // Value of res32 is not deterministic. assertEquals("[Average lover, How will anyone know?, Starfish Trooper]", res33.toString()); assertEquals("[true, false]", res34.toString()); // REMOVE_END } } ================================================ FILE: src/test/java/io/redis/examples/HyperLogLogExample.java ================================================ // EXAMPLE: hll_tutorial package io.redis.examples; import org.junit.jupiter.api.Test; import redis.clients.jedis.RedisClient; import static org.junit.jupiter.api.Assertions.assertEquals; public class HyperLogLogExample { @Test public void run() { // HIDE_START RedisClient jedis = RedisClient.create("redis://localhost:6379"); // HIDE_END // REMOVE_START jedis.del("bikes", "commuter_bikes", "all_bikes"); // REMOVE_END // STEP_START pfadd long res1 = jedis.pfadd("bikes", "Hyperion", "Deimos", "Phoebe", "Quaoar"); System.out.println(res1); // >>> 1 long res2 = jedis.pfcount("bikes"); System.out.println(res2); // >>> 4 long res3 = jedis.pfadd("commuter_bikes", "Salacia", "Mimas", "Quaoar"); System.out.println(res3); // >>> 1 String res4 = jedis.pfmerge("all_bikes", "bikes", "commuter_bikes"); System.out.println(res4); // >>> OK // REMOVE_START assertEquals("OK", res4); // REMOVE_END long res5 = jedis.pfcount("all_bikes"); System.out.println(res5); // >>> 6 // STEP_END // HIDE_START jedis.close(); // HIDE_END } } ================================================ FILE: src/test/java/io/redis/examples/JsonExample.java ================================================ // EXAMPLE: json_tutorial // REMOVE_START package io.redis.examples; import org.junit.jupiter.api.Test; import java.util.List; // REMOVE_END // HIDE_START import redis.clients.jedis.RedisClient; import redis.clients.jedis.json.Path2; import org.json.JSONArray; import org.json.JSONObject; import static org.junit.jupiter.api.Assertions.assertEquals; // HIDE_END // HIDE_START public class JsonExample { @Test public void run() { RedisClient jedis = RedisClient.create("redis://localhost:6379"); // HIDE_END //REMOVE_START // Clear any keys here before using them in tests. jedis.del("bike", "bike:1", "crashes", "newbike", "riders", "bikes:inventory"); //REMOVE_END // STEP_START set_get String res1 = jedis.jsonSet("bike", new Path2("$"), "\"Hyperion\""); System.out.println(res1); // >>> OK Object res2 = jedis.jsonGet("bike", new Path2("$")); System.out.println(res2); // >>> ["Hyperion"] List> res3 = jedis.jsonType("bike", new Path2("$")); System.out.println(res3); // >>> [class java.lang.String] // STEP_END // Tests for 'set_get' step. // REMOVE_START assertEquals("OK", res1); assertEquals("[\"Hyperion\"]", res2.toString()); assertEquals("[class java.lang.String]", res3.toString()); // REMOVE_END // STEP_START str List res4 = jedis.jsonStrLen("bike", new Path2("$")); System.out.println(res4); // >>> [8] List res5 = jedis.jsonStrAppend("bike", new Path2("$"), " (Enduro bikes)"); System.out.println(res5); // >>> [23] Object res6 = jedis.jsonGet("bike", new Path2("$")); System.out.println(res6); // >>> ["Hyperion (Enduro bikes)"] // STEP_END // Tests for 'str' step. // REMOVE_START assertEquals("[8]", res4.toString()); assertEquals("[23]", res5.toString()); assertEquals("[\"Hyperion (Enduro bikes)\"]", res6.toString()); // REMOVE_END // STEP_START num String res7 = jedis.jsonSet("crashes", new Path2("$"), 0); System.out.println(res7); // >>> OK Object res8 = jedis.jsonNumIncrBy("crashes", new Path2("$"), 1); System.out.println(res8); // >>> [1] Object res9 = jedis.jsonNumIncrBy("crashes", new Path2("$"), 1.5); System.out.println(res9); // >>> [2.5] Object res10 = jedis.jsonNumIncrBy("crashes", new Path2("$"), -0.75); System.out.println(res10); // >>> [1.75] // STEP_END // Tests for 'num' step. // REMOVE_START assertEquals("OK", res7); assertEquals("[1]", res8.toString()); assertEquals("[2.5]", res9.toString()); assertEquals("[1.75]", res10.toString()); // REMOVE_END // STEP_START arr String res11 = jedis.jsonSet("newbike", new Path2("$"), new JSONArray() .put("Deimos") .put(new JSONObject().put("crashes", 0)) .put((Object) null) ); System.out.println(res11); // >>> OK Object res12 = jedis.jsonGet("newbike", new Path2("$")); System.out.println(res12); // >>> [["Deimos",{"crashes":0},null]] Object res13 = jedis.jsonGet("newbike", new Path2("$[1].crashes")); System.out.println(res13); // >>> [0] long res14 = jedis.jsonDel("newbike", new Path2("$.[-1]")); System.out.println(res14); // >>> 1 Object res15 = jedis.jsonGet("newbike", new Path2("$")); System.out.println(res15); // >>> [["Deimos",{"crashes":0}]] // STEP_END // Tests for 'arr' step. // REMOVE_START assertEquals("OK", res11); assertEquals("[[\"Deimos\",{\"crashes\":0},null]]", res12.toString()); assertEquals("[0]", res13.toString()); assertEquals(1, res14); assertEquals("[[\"Deimos\",{\"crashes\":0}]]", res15.toString()); // REMOVE_END // STEP_START arr2 String res16 = jedis.jsonSet("riders", new Path2("$"), new JSONArray()); System.out.println(res16); // >>> OK List res17 = jedis.jsonArrAppendWithEscape("riders", new Path2("$"), "Norem"); System.out.println(res17); // >>> [1] Object res18 = jedis.jsonGet("riders", new Path2("$")); System.out.println(res18); // >>> [["Norem"]] List res19 = jedis.jsonArrInsertWithEscape( "riders", new Path2("$"), 1, "Prickett", "Royce", "Castilla" ); System.out.println(res19); // >>> [4] Object res20 = jedis.jsonGet("riders", new Path2("$")); System.out.println(res20); // >>> [["Norem","Prickett","Royce","Castilla"]] List res21 = jedis.jsonArrTrim("riders", new Path2("$"), 1, 1); System.out.println(res21); // >>> [1] Object res22 = jedis.jsonGet("riders", new Path2("$")); System.out.println(res22); // >>> [["Prickett"]] Object res23 = jedis.jsonArrPop("riders", new Path2("$")); System.out.println(res23); // >>> [Prickett] Object res24 = jedis.jsonArrPop("riders", new Path2("$")); System.out.println(res24); // >>> [null] // STEP_END // Tests for 'arr2' step. // REMOVE_START assertEquals("OK", res16); assertEquals("[1]", res17.toString()); assertEquals("[[\"Norem\"]]", res18.toString()); assertEquals("[4]", res19.toString()); assertEquals("[[\"Norem\",\"Prickett\",\"Royce\",\"Castilla\"]]", res20.toString()); assertEquals("[1]", res21.toString()); assertEquals("[[\"Prickett\"]]", res22.toString()); assertEquals("[Prickett]", res23.toString()); assertEquals("[null]", res24.toString()); // REMOVE_END // STEP_START obj String res25 = jedis.jsonSet("bike:1", new Path2("$"), new JSONObject() .put("model", "Deimos") .put("brand", "Ergonom") .put("price", 4972) ); System.out.println(res25); // >>> OK List res26 = jedis.jsonObjLen("bike:1", new Path2("$")); System.out.println(res26); // >>> [3] List> res27 = jedis.jsonObjKeys("bike:1", new Path2("$")); System.out.println(res27); // >>> [[price, model, brand]] // STEP_END // Tests for 'obj' step. // REMOVE_START assertEquals("OK", res25); assertEquals("[3]", res26.toString()); assertEquals("[[price, model, brand]]", res27.toString()); // REMOVE_END // STEP_START set_bikes String inventory_json = "{" + " \"inventory\": {" + " \"mountain_bikes\": [" + " {" + " \"id\": \"bike:1\"," + " \"model\": \"Phoebe\"," + " \"description\": \"This is a mid-travel trail slayer that is a " + "fantastic daily driver or one bike quiver. The Shimano Claris 8-speed groupset " + "gives plenty of gear range to tackle hills and there\u2019s room for mudguards " + "and a rack too. This is the bike for the rider who wants trail manners with " + "low fuss ownership.\"," + " \"price\": 1920," + " \"specs\": {\"material\": \"carbon\", \"weight\": 13.1}," + " \"colors\": [\"black\", \"silver\"]" + " }," + " {" + " \"id\": \"bike:2\"," + " \"model\": \"Quaoar\"," + " \"description\": \"Redesigned for the 2020 model year, this " + "bike impressed our testers and is the best all-around trail bike we've ever " + "tested. The Shimano gear system effectively does away with an external cassette, " + "so is super low maintenance in terms of wear and tear. All in all it's an " + "impressive package for the price, making it very competitive.\"," + " \"price\": 2072," + " \"specs\": {\"material\": \"aluminium\", \"weight\": 7.9}," + " \"colors\": [\"black\", \"white\"]" + " }," + " {" + " \"id\": \"bike:3\"," + " \"model\": \"Weywot\"," + " \"description\": \"This bike gives kids aged six years and older " + "a durable and uberlight mountain bike for their first experience on tracks and easy " + "cruising through forests and fields. A set of powerful Shimano hydraulic disc brakes " + "provide ample stopping ability. If you're after a budget option, this is one of the " + "best bikes you could get.\"," + " \"price\": 3264," + " \"specs\": {\"material\": \"alloy\", \"weight\": 13.8}" + " }" + " ]," + " \"commuter_bikes\": [" + " {" + " \"id\": \"bike:4\"," + " \"model\": \"Salacia\"," + " \"description\": \"This bike is a great option for anyone who just " + "wants a bike to get about on With a slick-shifting Claris gears from Shimano\u2019s, " + "this is a bike which doesn\u2019t break the bank and delivers craved performance. " + "It\u2019s for the rider who wants both efficiency and capability.\"," + " \"price\": 1475," + " \"specs\": {\"material\": \"aluminium\", \"weight\": 16.6}," + " \"colors\": [\"black\", \"silver\"]" + " }," + " {" + " \"id\": \"bike:5\"," + " \"model\": \"Mimas\"," + " \"description\": \"A real joy to ride, this bike got very high scores " + "in last years Bike of the year report. The carefully crafted 50-34 tooth chainset " + "and 11-32 tooth cassette give an easy-on-the-legs bottom gear for climbing, and the " + "high-quality Vittoria Zaffiro tires give balance and grip.It includes a low-step " + "frame , our memory foam seat, bump-resistant shocks and conveniently placed thumb " + "throttle. Put it all together and you get a bike that helps redefine what can be " + "done for this price.\"," + " \"price\": 3941," + " \"specs\": {\"material\": \"alloy\", \"weight\": 11.6}" + " }" + " ]" + " }" + "}"; String res28 = jedis.jsonSet("bikes:inventory", new Path2("$"), inventory_json); System.out.println(res28); // >>> OK // STEP_END // Tests for 'set_bikes' step. // REMOVE_START assertEquals("OK", res28); // REMOVE_END // STEP_START get_bikes Object res29 = jedis.jsonGet("bikes:inventory", new Path2("$.inventory.*")); System.out.println(res29); // >>> [[{"specs":{"material":"carbon","weight":13.1},"price":1920, ... // STEP_END // Tests for 'get_bikes' step. // REMOVE_START assertEquals( "[[{\"specs\":{\"material\":\"carbon\",\"weight\":13.1},\"price\":1920," + "\"description\":\"This is a mid-travel trail slayer that is a " + "fantastic daily driver or one bike quiver. The Shimano Claris 8-speed " + "groupset gives plenty of gear range to tackle hills and " + "there\\u2019s room for mudguards and a rack too. This is the bike " + "for the rider who wants trail manners with low fuss " + "ownership.\",\"model\":\"Phoebe\",\"id\":\"bike:1\",\"colors\":" + "[\"black\",\"silver\"]},{\"specs\":{\"material\":\"aluminium\",\"weight\":7.9}," + "\"price\":2072,\"description\":\"Redesigned for the 2020 model year, this " + "bike impressed our testers and is the best all-around trail bike we've " + "ever tested. The Shimano gear system effectively does away with an " + "external cassette, so is super low maintenance in terms of wear and tear. " + "All in all it's an impressive package for the price, making it very " + "competitive.\",\"model\":\"Quaoar\",\"id\":\"bike:2\",\"colors\":" + "[\"black\",\"white\"]},{\"specs\":{\"material\":\"alloy\",\"weight\":13.8}," + "\"price\":3264,\"description\":\"This bike gives kids aged six years and " + "older a durable and uberlight mountain bike for their first experience " + "on tracks and easy cruising through forests and fields. A set of " + "powerful Shimano hydraulic disc brakes provide ample stopping ability. " + "If you're after a budget option, this is one of the best bikes you could " + "get.\",\"model\":\"Weywot\",\"id\":\"bike:3\"}],[{\"specs\":" + "{\"material\":\"aluminium\",\"weight\":16.6},\"price\":1475,\"description\":" + "\"This bike is a great option for anyone who just wants a bike to get about " + "on With a slick-shifting Claris gears from Shimano\\u2019s, this is a bike " + "which doesn\\u2019t break the bank and delivers craved performance. " + "It\\u2019s for the rider who wants both efficiency and " + "capability.\",\"model\":\"Salacia\",\"id\":\"bike:4\",\"colors\":" + "[\"black\",\"silver\"]},{\"specs\":{\"material\":\"alloy\",\"weight\":11.6}," + "\"price\":3941,\"description\":\"A real joy to ride, this bike got very " + "high scores in last years Bike of the year report. The carefully crafted " + "50-34 tooth chainset and 11-32 tooth cassette give an easy-on-the-legs " + "bottom gear for climbing, and the high-quality Vittoria Zaffiro tires " + "give balance and grip.It includes a low-step frame , our memory foam " + "seat, bump-resistant shocks and conveniently placed thumb throttle. Put " + "it all together and you get a bike that helps redefine what can be done " + "for this price.\",\"model\":\"Mimas\",\"id\":\"bike:5\"}]]", res29.toString() ); // REMOVE_END // STEP_START get_mtnbikes Object res30 = jedis.jsonGet( "bikes:inventory", new Path2("$.inventory.mountain_bikes[*].model") ); System.out.println(res30); // >>> ["Phoebe","Quaoar","Weywot"] Object res31 = jedis.jsonGet( "bikes:inventory", new Path2("$.inventory[\"mountain_bikes\"][*].model") ); System.out.println(res31); // >>> ["Phoebe","Quaoar","Weywot"] Object res32 = jedis.jsonGet( "bikes:inventory", new Path2("$..mountain_bikes[*].model") ); System.out.println(res32); // >>> ["Phoebe","Quaoar","Weywot"] // STEP_END // Tests for 'get_mtnbikes' step. // REMOVE_START assertEquals("[\"Phoebe\",\"Quaoar\",\"Weywot\"]", res30.toString()); assertEquals("[\"Phoebe\",\"Quaoar\",\"Weywot\"]", res31.toString()); assertEquals("[\"Phoebe\",\"Quaoar\",\"Weywot\"]", res32.toString()); // REMOVE_END // STEP_START get_models Object res33 = jedis.jsonGet("bikes:inventory", new Path2("$..model")); System.out.println(res33); // >>> ["Phoebe","Quaoar","Weywot","Salacia","Mimas"] // STEP_END // Tests for 'get_models' step. // REMOVE_START assertEquals("[\"Phoebe\",\"Quaoar\",\"Weywot\",\"Salacia\",\"Mimas\"]", res33.toString()); // REMOVE_END // STEP_START get2mtnbikes Object res34 = jedis.jsonGet( "bikes:inventory", new Path2("$..mountain_bikes[0:2].model") ); System.out.println(res34); // >>> ["Phoebe","Quaoar"] // STEP_END // Tests for 'get2mtnbikes' step. // REMOVE_START assertEquals("[\"Phoebe\",\"Quaoar\"]", res34.toString()); // REMOVE_END // STEP_START filter1 Object res35 = jedis.jsonGet( "bikes:inventory", new Path2("$..mountain_bikes[?(@.price < 3000 && @.specs.weight < 10)]") ); System.out.println(res35); // >>> [{"specs":{"material":"aluminium","weight":7.9},"price":2072,... // STEP_END // Tests for 'filter1' step. // REMOVE_START assertEquals( "[{\"specs\":{\"material\":\"aluminium\",\"weight\":7.9},\"price\":2072," + "\"description\":\"Redesigned for the 2020 model year, this bike impressed " + "our testers and is the best all-around trail bike we've ever tested. The " + "Shimano gear system effectively does away with an external cassette, " + "so is super low maintenance in terms of wear and tear. All in all it's an " + "impressive package for the price, making it very competitive.\",\"model\":" + "\"Quaoar\",\"id\":\"bike:2\",\"colors\":[\"black\",\"white\"]}]", res35.toString() ); // REMOVE_END // STEP_START filter2 Object res36 = jedis.jsonGet( "bikes:inventory", new Path2("$..[?(@.specs.material == 'alloy')].model") ); System.out.println(res36); // >>> ["Weywot","Mimas"] // STEP_END // Tests for 'filter2' step. // REMOVE_START assertEquals("[\"Weywot\",\"Mimas\"]", res36.toString()); // REMOVE_END // STEP_START filter3 Object res37 = jedis.jsonGet( "bikes:inventory", new Path2("$..[?(@.specs.material =~ '(?i)al')].model") ); System.out.println(res37); // >>> ["Quaoar","Weywot","Salacia","Mimas"] // STEP_END // Tests for 'filter3' step. // REMOVE_START assertEquals("[\"Quaoar\",\"Weywot\",\"Salacia\",\"Mimas\"]", res37.toString()); // REMOVE_END // STEP_START filter4 jedis.jsonSet( "bikes:inventory", new Path2("$.inventory.mountain_bikes[0].regex_pat"), "\"(?i)al\"" ); jedis.jsonSet( "bikes:inventory", new Path2("$.inventory.mountain_bikes[1].regex_pat"), "\"(?i)al\"" ); jedis.jsonSet( "bikes:inventory", new Path2("$.inventory.mountain_bikes[2].regex_pat"), "\"(?i)al\"" ); Object res38 = jedis.jsonGet( "bikes:inventory", new Path2("$.inventory.mountain_bikes[?(@.specs.material =~ @.regex_pat)].model") ); System.out.println(res38); // >>> ["Quaoar","Weywot"] // STEP_END // Tests for 'filter4' step. // REMOVE_START assertEquals("[\"Quaoar\",\"Weywot\"]", res38.toString()); // REMOVE_END // STEP_START update_bikes Object res39 = jedis.jsonGet("bikes:inventory", new Path2("$..price")); System.out.println(res39); // >>> [1920,2072,3264,1475,3941] Object res40 = jedis.jsonNumIncrBy("bikes:inventory", new Path2("$..price"), -100); System.out.println(res40); // >>> [1820,1972,3164,1375,3841] Object res41 = jedis.jsonNumIncrBy("bikes:inventory", new Path2("$..price"), 100); System.out.println(res41); // >>> [1920,2072,3264,1475,3941] // STEP_END // Tests for 'update_bikes' step. // REMOVE_START assertEquals("[1920,2072,3264,1475,3941]", res39.toString()); assertEquals("[1820,1972,3164,1375,3841]", res40.toString()); assertEquals("[1920,2072,3264,1475,3941]", res41.toString()); // REMOVE_END // STEP_START update_filters1 jedis.jsonSet("bikes:inventory", new Path2("$.inventory.*[?(@.price<2000)].price"), 1500); Object res42 = jedis.jsonGet("bikes:inventory", new Path2("$..price")); System.out.println(res42); // >>> [1500,2072,3264,1500,3941] // STEP_END // Tests for 'update_filters1' step. // REMOVE_START assertEquals("[1500,2072,3264,1500,3941]", res42.toString()); // REMOVE_END // STEP_START update_filters2 List res43 = jedis.jsonArrAppendWithEscape( "bikes:inventory", new Path2("$.inventory.*[?(@.price<2000)].colors"), "\"pink\"" ); System.out.println(res43); // >>> [3, 3] Object res44 = jedis.jsonGet("bikes:inventory", new Path2("$..[*].colors")); System.out.println(res44); // >>> [["black","silver","\"pink\""],["black","white"],["black","silver","\"pink\""]] // STEP_END // Tests for 'update_filters2' step. // REMOVE_START assertEquals("[3, 3]", res43.toString()); assertEquals( "[[\"black\",\"silver\",\"\\\"pink\\\"\"],[\"black\",\"white\"]," + "[\"black\",\"silver\",\"\\\"pink\\\"\"]]", res44.toString()); // REMOVE_END // HIDE_START jedis.close(); } } // HIDE_END ================================================ FILE: src/test/java/io/redis/examples/ListExample.java ================================================ // EXAMPLE: list_tutorial // HIDE_START package io.redis.examples; import org.junit.jupiter.api.Test; import redis.clients.jedis.RedisClient; import redis.clients.jedis.args.ListDirection; import java.util.List; import static org.junit.jupiter.api.Assertions.*; public class ListExample { @Test public void run() { RedisClient jedis = RedisClient.create("redis://localhost:6379"); // HIDE_END // REMOVE_START jedis.del("bikes:repairs"); jedis.del("bikes:finished"); // REMOVE_END // STEP_START queue long res1 = jedis.lpush("bikes:repairs", "bike:1"); System.out.println(res1); // >>> 1 long res2 = jedis.lpush("bikes:repairs", "bike:2"); System.out.println(res2); // >>> 2 String res3 = jedis.rpop("bikes:repairs"); System.out.println(res3); // >>> bike:1 String res4 = jedis.rpop("bikes:repairs"); System.out.println(res4); // >>> bike:2 // STEP_END // REMOVE_START assertEquals(1, res1); assertEquals(2, res2); assertEquals("bike:1", res3); assertEquals("bike:2", res4); // REMOVE_END // STEP_START stack long res5 = jedis.lpush("bikes:repairs", "bike:1"); System.out.println(res5); // >>> 1 long res6 = jedis.lpush("bikes:repairs", "bike:2"); System.out.println(res6); // >>> 2 String res7 = jedis.lpop("bikes:repairs"); System.out.println(res7); // >>> bike:2 String res8 = jedis.lpop("bikes:repairs"); System.out.println(res8); // >>> bike:1 // STEP_END // REMOVE_START assertEquals(1, res5); assertEquals(2, res6); assertEquals("bike:2", res7); assertEquals("bike:1", res8); // REMOVE_END // STEP_START llen long res9 = jedis.llen("bikes:repairs"); System.out.println(res9); // >>> 0 // STEP_END // REMOVE_START assertEquals(0, res9); // REMOVE_END // STEP_START lmove_lrange long res10 = jedis.lpush("bikes:repairs", "bike:1"); System.out.println(res10); // >>> 1 long res11 = jedis.lpush("bikes:repairs", "bike:2"); System.out.println(res11); // >>> 2 String res12 = jedis.lmove("bikes:repairs", "bikes:finished", ListDirection.LEFT, ListDirection.LEFT); System.out.println(res12); // >>> bike:2 List res13 = jedis.lrange("bikes:repairs", 0, -1); System.out.println(res13); // >>> [bike:1] List res14 = jedis.lrange("bikes:finished", 0, -1); System.out.println(res14); // >>> [bike:2] // STEP_END // REMOVE_START assertEquals(1, res10); assertEquals(2, res11); assertEquals("bike:2", res12); assertEquals("[bike:1]", res13.toString()); assertEquals("[bike:2]", res14.toString()); jedis.del("bikes:repairs"); // REMOVE_END // STEP_START lpush_rpush long res15 = jedis.rpush("bikes:repairs", "bike:1"); System.out.println(res15); // >>> 1 long res16 = jedis.rpush("bikes:repairs", "bike:2"); System.out.println(res16); // >>> 2 long res17 = jedis.lpush("bikes:repairs", "bike:important_bike"); System.out.println(res17); // >>> 3 List res18 = jedis.lrange("bikes:repairs", 0, -1); System.out.println(res18); // >>> [bike:important_bike, bike:1, bike:2] // STEP_END // REMOVE_START assertEquals(1, res15); assertEquals(2, res16); assertEquals(3, res17); assertEquals("[bike:important_bike, bike:1, bike:2]", res18.toString()); jedis.del("bikes:repairs"); // REMOVE_END // STEP_START variadic long res19 = jedis.rpush("bikes:repairs", "bike:1", "bike:2", "bike:3"); System.out.println(res19); // >>> 3 long res20 = jedis.lpush("bikes:repairs", "bike:important_bike", "bike:very_important_bike"); System.out.println(res20); // >>> 5 List res21 = jedis.lrange("bikes:repairs", 0, -1); System.out.println(res21); // >>> [bike:very_important_bike, bike:important_bike, bike:1, bike:2, bike:3] // STEP_END // REMOVE_START assertEquals(3, res19); assertEquals(5, res20); assertEquals("[bike:very_important_bike, bike:important_bike, bike:1, bike:2, bike:3]",res21.toString()); jedis.del("bikes:repairs"); // REMOVE_END // STEP_START lpop_rpop long res22 = jedis.rpush("bikes:repairs", "bike:1", "bike:2", "bike:3"); System.out.println(res22); // >>> 3 String res23 = jedis.rpop("bikes:repairs"); System.out.println(res23); // >>> bike:3 String res24 = jedis.lpop("bikes:repairs"); System.out.println(res24); // >>> bike:1 String res25 = jedis.rpop("bikes:repairs"); System.out.println(res25); // >>> bike:2 String res26 = jedis.rpop("bikes:repairs"); System.out.println(res26); // >>> null // STEP_END // REMOVE_START assertEquals(3, res22); assertEquals("bike:3", res23); assertEquals("bike:1", res24); assertEquals("bike:2", res25); assertNull(res26); // REMOVE_END // STEP_START ltrim long res27 = jedis.rpush("bikes:repairs", "bike:1", "bike:2", "bike:3", "bike:4", "bike:5"); System.out.println(res27); // >>> 5 String res28 = jedis.ltrim("bikes:repairs", 0, 2); System.out.println(res28); // >>> OK List res29 = jedis.lrange("bikes:repairs", 0, -1); System.out.println(res29); // >>> [bike:1, bike:2, bike:3] // STEP_END // REMOVE_START assertEquals(5, res27); assertEquals("OK", res28); assertEquals("[bike:1, bike:2, bike:3]", res29.toString()); jedis.del("bikes:repairs"); // REMOVE_END // STEP_START ltrim_end_of_list res27 = jedis.rpush("bikes:repairs", "bike:1", "bike:2", "bike:3", "bike:4", "bike:5"); System.out.println(res27); // >>> 5 res28 = jedis.ltrim("bikes:repairs", -3, -1); System.out.println(res2); // >>> OK res29 = jedis.lrange("bikes:repairs", 0, -1); System.out.println(res29); // >>> [bike:3, bike:4, bike:5] // STEP_END // REMOVE_START assertEquals(5, res27); assertEquals("OK", res28); assertEquals("[bike:3, bike:4, bike:5]", res29.toString()); jedis.del("bikes:repairs"); // REMOVE_END // STEP_START brpop long res31 = jedis.rpush("bikes:repairs", "bike:1", "bike:2"); System.out.println(res31); // >>> 2 List res32 = jedis.brpop(1, "bikes:repairs"); System.out.println(res32); // >>> (bikes:repairs, bike:2) List res33 = jedis.brpop(1,"bikes:repairs"); System.out.println(res33); // >>> (bikes:repairs, bike:1) List res34 = jedis.brpop(1,"bikes:repairs"); System.out.println(res34); // >>> null // STEP_END // REMOVE_START assertEquals(2, res31); assertEquals("[bikes:repairs, bike:2]", res32.toString()); assertEquals( "[bikes:repairs, bike:1]", res33.toString()); assertNull(res34); jedis.del("bikes:repairs"); jedis.del("new_bikes"); // REMOVE_END // STEP_START rule_1 long res35 = jedis.del("new_bikes"); System.out.println(res35); // >>> 0 long res36 = jedis.lpush("new_bikes", "bike:1", "bike:2", "bike:3"); System.out.println(res36); // >>> 3 // STEP_END // REMOVE_START assertEquals(0, res35); assertEquals(3, res36); jedis.del("new_bikes"); // REMOVE_END // STEP_START rule_1.1 String res37 = jedis.set("new_bikes", "bike:1"); System.out.println(res37); // >>> OK String res38 = jedis.type("new_bikes"); System.out.println(res38); // >>> string try { long res39 = jedis.lpush("new_bikes", "bike:2", "bike:3"); } catch (Exception e) { e.printStackTrace(); // >>> redis.clients.jedis.exceptions.JedisDataException: // >>> WRONGTYPE Operation against a key holding the wrong kind of value } // STEP_END // REMOVE_START assertEquals("OK",res37); assertEquals("string",res38); jedis.del("new_bikes"); // REMOVE_END // STEP_START rule_2 jedis.lpush("bikes:repairs", "bike:1", "bike:2", "bike:3"); System.out.println(res36); // >>> 3 boolean res40 = jedis.exists("bikes:repairs"); System.out.println(res40); // >>> true String res41 = jedis.lpop("bikes:repairs"); System.out.println(res41); // >>> bike:3 String res42 = jedis.lpop("bikes:repairs"); System.out.println(res42); // >>> bike:2 String res43 = jedis.lpop("bikes:repairs"); System.out.println(res43); // >>> bike:1 boolean res44 = jedis.exists("bikes:repairs"); System.out.println(res44); // >>> false // STEP_END // REMOVE_START assertTrue(res40); assertEquals(res41, "bike:3"); assertEquals(res42, "bike:2"); assertEquals(res43, "bike:1"); assertFalse(res44); // REMOVE_END // STEP_START rule_3 long res45 = jedis.del("bikes:repairs"); System.out.println(res45); // >>> 0 long res46 = jedis.llen("bikes:repairs"); System.out.println(res46); // >>> 0 String res47 = jedis.lpop("bikes:repairs"); System.out.println(res47); // >>> null // STEP_END // REMOVE_START assertEquals(0, res45); assertEquals(0, res46); assertNull(res47); // REMOVE_END // STEP_START ltrim.1 long res48 = jedis.lpush("bikes:repairs", "bike:1", "bike:2", "bike:3", "bike:4", "bike:5"); System.out.println(res48); // >>> 5 String res49 = jedis.ltrim("bikes:repairs", 0, 2); System.out.println(res49); // >>> OK List res50 = jedis.lrange("bikes:repairs", 0, -1); System.out.println(res50); // >>> [bike:5, bike:4, bike:3] // STEP_END // REMOVE_START assertEquals(5, res48); assertEquals("OK", res49); assertEquals("[bike:5, bike:4, bike:3]", res50.toString()); jedis.del("bikes:repairs"); // REMOVE_END // HIDE_START jedis.close(); } } // HIDE_END ================================================ FILE: src/test/java/io/redis/examples/PipeTransExample.java ================================================ // EXAMPLE: pipe_trans_tutorial // REMOVE_START package io.redis.examples; import org.junit.jupiter.api.Test; // REMOVE_END import java.util.List; import redis.clients.jedis.RedisClient; import redis.clients.jedis.AbstractPipeline; import redis.clients.jedis.AbstractTransaction; import redis.clients.jedis.Response; import static org.junit.jupiter.api.Assertions.assertEquals; public class PipeTransExample { @Test public void run() { RedisClient jedis = RedisClient.create("redis://localhost:6379"); // REMOVE_START for (int i = 0; i < 5; i++) { jedis.del(String.format("seat:%d", i)); } jedis.del("counter:1", "counter:2", "counter:3", "shellpath"); // REMOVE_END // STEP_START basic_pipe // Make sure you close the pipeline after use to release resources // and return the connection to the pool. try (AbstractPipeline pipe = jedis.pipelined()) { for (int i = 0; i < 5; i++) { pipe.set(String.format("seat:%d", i), String.format("#%d", i)); } pipe.sync(); } try (AbstractPipeline pipe = jedis.pipelined()) { Response resp0 = pipe.get("seat:0"); Response resp3 = pipe.get("seat:3"); Response resp4 = pipe.get("seat:4"); pipe.sync(); // Responses are available after the pipeline has executed. System.out.println(resp0.get()); // >>> #0 System.out.println(resp3.get()); // >>> #3 System.out.println(resp4.get()); // >>> #4 // REMOVE_START assertEquals("#0", resp0.get()); assertEquals("#3", resp3.get()); assertEquals("#4", resp4.get()); // REMOVE_END } // STEP_END // STEP_START basic_trans try ( AbstractTransaction trans = jedis.multi()) { trans.incrBy("counter:1", 1); trans.incrBy("counter:2", 2); trans.incrBy("counter:3", 3); trans.exec(); } System.out.println(jedis.get("counter:1")); // >>> 1 System.out.println(jedis.get("counter:2")); // >>> 2 System.out.println(jedis.get("counter:3")); // >>> 3 // STEP_END // REMOVE_START assertEquals("1", jedis.get("counter:1")); assertEquals("2", jedis.get("counter:2")); assertEquals("3", jedis.get("counter:3")); // REMOVE_END // STEP_START trans_watch // Set initial value of `shellpath`. jedis.set("shellpath", "/usr/syscmds/"); // Start the transaction and watch the key we are about to update. try (AbstractTransaction trans = jedis.transaction(false)) { // create a Transaction object without sending MULTI command trans.watch("shellpath"); // send WATCH command(s) trans.multi(); // send MULTI command String currentPath = jedis.get("shellpath"); String newPath = currentPath + ":/usr/mycmds/"; // Commands added to the `trans` object // will be buffered until `trans.exec()` is called. Response setResult = trans.set("shellpath", newPath); List transResults = trans.exec(); // The `exec()` call returns null if the transaction failed. if (transResults != null) { // Responses are available if the transaction succeeded. System.out.println(setResult.get()); // >>> OK // You can also get the results from the list returned by // `trans.exec()`. for (Object item: transResults) { System.out.println(item); } // >>> OK System.out.println(jedis.get("shellpath")); // >>> /usr/syscmds/:/usr/mycmds/ } // REMOVE_START assertEquals("/usr/syscmds/:/usr/mycmds/", jedis.get("shellpath")); assertEquals("OK", setResult.get()); assertEquals(1, transResults.size()); assertEquals("OK", transResults.get(0).toString()); // REMOVE_END } // STEP_END // HIDE_START jedis.close(); } } // HIDE_END ================================================ FILE: src/test/java/io/redis/examples/QueryAggExample.java ================================================ // EXAMPLE: query_agg // REMOVE_START package io.redis.examples; import org.junit.jupiter.api.Test; // REMOVE_END // HIDE_START import java.util.List; import java.util.ArrayList; import redis.clients.jedis.RedisClient; import redis.clients.jedis.json.Path2; import redis.clients.jedis.search.FTCreateParams; import redis.clients.jedis.search.IndexDataType; import redis.clients.jedis.search.schemafields.*; import redis.clients.jedis.search.aggr.*; import redis.clients.jedis.exceptions.JedisDataException; import static org.junit.jupiter.api.Assertions.assertArrayEquals; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertTrue; // HIDE_END // HIDE_START public class QueryAggExample { @Test public void run() { RedisClient jedis = RedisClient.create("redis://localhost:6379"); //REMOVE_START // Clear any keys here before using them in tests. try { jedis.ftDropIndex("idx:bicycle"); } catch (JedisDataException j) {} //REMOVE_END // HIDE_END SchemaField[] schema = { TextField.of("$.brand").as("brand"), TextField.of("$.model").as("model"), TextField.of("$.description").as("description"), NumericField.of("$.price").as("price"), TagField.of("$.condition").as("condition") }; jedis.ftCreate("idx:bicycle", FTCreateParams.createParams() .on(IndexDataType.JSON) .addPrefix("bicycle:"), schema ); String[] bicycleJsons = new String[] { " {" + " \"pickup_zone\": \"POLYGON((-74.0610 40.7578, -73.9510 40.7578, -73.9510 40.6678, " + "-74.0610 40.6678, -74.0610 40.7578))\"," + " \"store_location\": \"-74.0060,40.7128\"," + " \"brand\": \"Velorim\"," + " \"model\": \"Jigger\"," + " \"price\": 270," + " \"description\": \"Small and powerful, the Jigger is the best ride for the smallest of tikes! " + "This is the tiniest kids’ pedal bike on the market available without a coaster brake, the Jigger " + "is the vehicle of choice for the rare tenacious little rider raring to go.\"," + " \"condition\": \"new\"" + " }", " {" + " \"pickup_zone\": \"POLYGON((-118.2887 34.0972, -118.1987 34.0972, -118.1987 33.9872, " + "-118.2887 33.9872, -118.2887 34.0972))\"," + " \"store_location\": \"-118.2437,34.0522\"," + " \"brand\": \"Bicyk\"," + " \"model\": \"Hillcraft\"," + " \"price\": 1200," + " \"description\": \"Kids want to ride with as little weight as possible. Especially " + "on an incline! They may be at the age when a 27.5'' wheel bike is just too clumsy coming " + "off a 24'' bike. The Hillcraft 26 is just the solution they need!\"," + " \"condition\": \"used\"" + " }", " {" + " \"pickup_zone\": \"POLYGON((-87.6848 41.9331, -87.5748 41.9331, -87.5748 41.8231, " + "-87.6848 41.8231, -87.6848 41.9331))\"," + " \"store_location\": \"-87.6298,41.8781\"," + " \"brand\": \"Nord\"," + " \"model\": \"Chook air 5\"," + " \"price\": 815," + " \"description\": \"The Chook Air 5 gives kids aged six years and older a durable " + "and uberlight mountain bike for their first experience on tracks and easy cruising through " + "forests and fields. The lower top tube makes it easy to mount and dismount in any " + "situation, giving your kids greater safety on the trails.\"," + " \"condition\": \"used\"" + " }", " {" + " \"pickup_zone\": \"POLYGON((-80.2433 25.8067, -80.1333 25.8067, -80.1333 25.6967, " + "-80.2433 25.6967, -80.2433 25.8067))\"," + " \"store_location\": \"-80.1918,25.7617\"," + " \"brand\": \"Eva\"," + " \"model\": \"Eva 291\"," + " \"price\": 3400," + " \"description\": \"The sister company to Nord, Eva launched in 2005 as the first " + "and only women-dedicated bicycle brand. Designed by women for women, allEva bikes " + "are optimized for the feminine physique using analytics from a body metrics database. " + "If you like 29ers, try the Eva 291. It’s a brand new bike for 2022.. This " + "full-suspension, cross-country ride has been designed for velocity. The 291 has " + "100mm of front and rear travel, a superlight aluminum frame and fast-rolling " + "29-inch wheels. Yippee!\"," + " \"condition\": \"used\"" + " }", " {" + " \"pickup_zone\": \"POLYGON((-122.4644 37.8199, -122.3544 37.8199, -122.3544 37.7099, " + "-122.4644 37.7099, -122.4644 37.8199))\"," + " \"store_location\": \"-122.4194,37.7749\"," + " \"brand\": \"Noka Bikes\"," + " \"model\": \"Kahuna\"," + " \"price\": 3200," + " \"description\": \"Whether you want to try your hand at XC racing or are looking " + "for a lively trail bike that's just as inspiring on the climbs as it is over rougher " + "ground, the Wilder is one heck of a bike built specifically for short women. Both the " + "frames and components have been tweaked to include a women’s saddle, different bars " + "and unique colourway.\"," + " \"condition\": \"used\"" + " }", " {" + " \"pickup_zone\": \"POLYGON((-0.1778 51.5524, 0.0822 51.5524, 0.0822 51.4024, " + "-0.1778 51.4024, -0.1778 51.5524))\"," + " \"store_location\": \"-0.1278,51.5074\"," + " \"brand\": \"Breakout\"," + " \"model\": \"XBN 2.1 Alloy\"," + " \"price\": 810," + " \"description\": \"The XBN 2.1 Alloy is our entry-level road bike – but that’s " + "not to say that it’s a basic machine. With an internal weld aluminium frame, a full " + "carbon fork, and the slick-shifting Claris gears from Shimano’s, this is a bike which " + "doesn’t break the bank and delivers craved performance.\"," + " \"condition\": \"new\"" + " }", " {" + " \"pickup_zone\": \"POLYGON((2.1767 48.9016, 2.5267 48.9016, 2.5267 48.5516, " + "2.1767 48.5516, 2.1767 48.9016))\"," + " \"store_location\": \"2.3522,48.8566\"," + " \"brand\": \"ScramBikes\"," + " \"model\": \"WattBike\"," + " \"price\": 2300," + " \"description\": \"The WattBike is the best e-bike for people who still " + "feel young at heart. It has a Bafang 1000W mid-drive system and a 48V 17.5AH " + "Samsung Lithium-Ion battery, allowing you to ride for more than 60 miles on one " + "charge. It’s great for tackling hilly terrain or if you just fancy a more " + "leisurely ride. With three working modes, you can choose between E-bike, " + "assisted bicycle, and normal bike modes.\"," + " \"condition\": \"new\"" + " }", " {" + " \"pickup_zone\": \"POLYGON((13.3260 52.5700, 13.6550 52.5700, 13.6550 52.2700, " + "13.3260 52.2700, 13.3260 52.5700))\"," + " \"store_location\": \"13.4050,52.5200\"," + " \"brand\": \"Peaknetic\"," + " \"model\": \"Secto\"," + " \"price\": 430," + " \"description\": \"If you struggle with stiff fingers or a kinked neck or " + "back after a few minutes on the road, this lightweight, aluminum bike alleviates " + "those issues and allows you to enjoy the ride. From the ergonomic grips to the " + "lumbar-supporting seat position, the Roll Low-Entry offers incredible comfort. " + "The rear-inclined seat tube facilitates stability by allowing you to put a foot " + "on the ground to balance at a stop, and the low step-over frame makes it " + "accessible for all ability and mobility levels. The saddle is very soft, with " + "a wide back to support your hip joints and a cutout in the center to redistribute " + "that pressure. Rim brakes deliver satisfactory braking control, and the wide tires " + "provide a smooth, stable ride on paved roads and gravel. Rack and fender mounts " + "facilitate setting up the Roll Low-Entry as your preferred commuter, and the " + "BMX-like handlebar offers space for mounting a flashlight, bell, or phone holder.\"," + " \"condition\": \"new\"" + " }", " {" + " \"pickup_zone\": \"POLYGON((1.9450 41.4301, 2.4018 41.4301, 2.4018 41.1987, " + "1.9450 41.1987, 1.9450 41.4301))\"," + " \"store_location\": \"2.1734, 41.3851\"," + " \"brand\": \"nHill\"," + " \"model\": \"Summit\"," + " \"price\": 1200," + " \"description\": \"This budget mountain bike from nHill performs well both " + "on bike paths and on the trail. The fork with 100mm of travel absorbs rough " + "terrain. Fat Kenda Booster tires give you grip in corners and on wet trails. " + "The Shimano Tourney drivetrain offered enough gears for finding a comfortable " + "pace to ride uphill, and the Tektro hydraulic disc brakes break smoothly. " + "Whether you want an affordable bike that you can take to work, but also take " + "trail in mountains on the weekends or you’re just after a stable, comfortable " + "ride for the bike path, the Summit gives a good value for money.\"," + " \"condition\": \"new\"" + " }", " {" + " \"pickup_zone\": \"POLYGON((12.4464 42.1028, 12.5464 42.1028, " + "12.5464 41.7028, 12.4464 41.7028, 12.4464 42.1028))\"," + " \"store_location\": \"12.4964,41.9028\"," + " \"model\": \"ThrillCycle\"," + " \"brand\": \"BikeShind\"," + " \"price\": 815," + " \"description\": \"An artsy, retro-inspired bicycle that’s as " + "functional as it is pretty: The ThrillCycle steel frame offers a smooth ride. " + "A 9-speed drivetrain has enough gears for coasting in the city, but we wouldn’t " + "suggest taking it to the mountains. Fenders protect you from mud, and a rear " + "basket lets you transport groceries, flowers and books. The ThrillCycle comes " + "with a limited lifetime warranty, so this little guy will last you long " + "past graduation.\"," + " \"condition\": \"refurbished\"" + " }" }; for (int i = 0; i < bicycleJsons.length; i++) { jedis.jsonSet("bicycle:" + i, new Path2("$"), bicycleJsons[i]); } // STEP_START agg1 AggregationResult res1 = jedis.ftAggregate("idx:bicycle", new AggregationBuilder("@condition:{new}") .load("__key", "price") .apply("@price - (@price * 0.1)", "discounted") ); List rows1 = res1.getRows(); System.out.println(rows1.size()); // >>> 5 for (int i = 0; i < rows1.size(); i++) { System.out.println(rows1.get(i)); } // >>> {__key=bicycle:0, discounted=243, price=270} // >>> {__key=bicycle:5, discounted=729, price=810} // >>> {__key=bicycle:6, discounted=2070, price=2300} // >>> {__key=bicycle:7, discounted=387, price=430} // >>> {__key=bicycle:8, discounted=1080, price=1200} // STEP_END // Tests for 'agg1' step. // REMOVE_START assertEquals(5, rows1.size()); assertArrayEquals( new String[]{"bicycle:0", "bicycle:5", "bicycle:6", "bicycle:7", "bicycle:8"}, rows1.stream().map(e->e.get("__key")).sorted().toArray() ); // REMOVE_END // STEP_START agg2 AggregationResult res2 = jedis.ftAggregate("idx:bicycle", new AggregationBuilder("*") .load("price") .apply("@price<1000", "price_category") .groupBy("@condition", Reducers.sum("@price_category").as("num_affordable")) ); List rows2 = res2.getRows(); System.out.println(rows2.size()); // >>> 3 for (int i = 0; i < rows2.size(); i++) { System.out.println(rows2.get(i)); } // >>> {condition=refurbished, num_affordable=1} // >>> {condition=used, num_affordable=1} // >>> {condition=new, num_affordable=3} // STEP_END // Tests for 'agg2' step. // REMOVE_START assertEquals(3, rows2.size()); assertArrayEquals( new String[] { "new, 3", "refurbished, 1", "used, 1" }, rows2.stream().map(e->e.get("condition") + ", " + e.get("num_affordable")) .sorted().toArray() ); // REMOVE_END // STEP_START agg3 AggregationResult res3 = jedis.ftAggregate("idx:bicycle", new AggregationBuilder("*") .apply("'bicycle'", "type") .groupBy("@type", Reducers.count().as("num_total")) ); List rows3 = res3.getRows(); System.out.println(rows3.size()); // >>> 1 for (int i = 0; i < rows3.size(); i++) { System.out.println(rows3.get(i)); } // >>> {type=bicycle, num_total=10} // STEP_END // Tests for 'agg3' step. // REMOVE_START assertEquals(1, rows3.size()); assertEquals("{type=bicycle, num_total=10}", rows3.get(0).toString()); // REMOVE_END // STEP_START agg4 AggregationResult res4 = jedis.ftAggregate("idx:bicycle", new AggregationBuilder("*") .load("__key") .groupBy("@condition", Reducers.to_list("__key").as("bicycles")) ); List rows4 = res4.getRows(); System.out.println(rows4.size()); // >>> 3 for (int i = 0; i < rows4.size(); i++) { System.out.println(rows4.get(i)); } // >>> {condition=refurbished, bicycles=[bicycle:9]} // >>> {condition=used, bicycles=[bicycle:3, bicycle:4, bicycle:1, bicycle:2]} // >>> {condition=new, bicycles=[bicycle:7, bicycle:0, bicycle:5, bicycle:6, bicycle:8]} // STEP_END // Tests for 'agg4' step. // REMOVE_START assertEquals(3, rows4.size()); Row test4Row = rows4.get(0); assertEquals("refurbished", test4Row.getString("condition")); ArrayList test4Bikes = (ArrayList) test4Row.get("bicycles"); assertEquals(1, test4Bikes.size()); assertTrue(test4Bikes.contains("bicycle:9")); test4Row = rows4.get(1); assertEquals("used", test4Row.getString("condition")); test4Bikes = (ArrayList) test4Row.get("bicycles"); assertEquals(4, test4Bikes.size()); assertTrue(test4Bikes.contains("bicycle:1")); assertTrue(test4Bikes.contains("bicycle:2")); assertTrue(test4Bikes.contains("bicycle:3")); assertTrue(test4Bikes.contains("bicycle:4")); test4Row = rows4.get(2); assertEquals("new", test4Row.getString("condition")); test4Bikes = (ArrayList) test4Row.get("bicycles"); assertEquals(5, test4Bikes.size()); assertTrue(test4Bikes.contains("bicycle:0")); assertTrue(test4Bikes.contains("bicycle:5")); assertTrue(test4Bikes.contains("bicycle:6")); assertTrue(test4Bikes.contains("bicycle:7")); assertTrue(test4Bikes.contains("bicycle:8")); // REMOVE_END // HIDE_START jedis.close(); } } // HIDE_END ================================================ FILE: src/test/java/io/redis/examples/QueryEmExample.java ================================================ // EXAMPLE: query_em // REMOVE_START package io.redis.examples; import org.junit.jupiter.api.Test; // REMOVE_END // HIDE_START import java.util.List; import redis.clients.jedis.RedisClient; import redis.clients.jedis.json.Path2; import redis.clients.jedis.search.*; import redis.clients.jedis.search.schemafields.*; import redis.clients.jedis.exceptions.JedisDataException; import static org.junit.jupiter.api.Assertions.assertArrayEquals; import static org.junit.jupiter.api.Assertions.assertEquals; public class QueryEmExample { @Test public void run() { RedisClient jedis = RedisClient.create("redis://localhost:6379"); //REMOVE_START // Clear any keys here before using them in tests. try {jedis.ftDropIndex("idx:bicycle");} catch (JedisDataException j){} try {jedis.ftDropIndex("idx:email");} catch (JedisDataException j){} //REMOVE_END SchemaField[] schema = { TextField.of("$.brand").as("brand"), TextField.of("$.model").as("model"), TextField.of("$.description").as("description"), NumericField.of("$.price").as("price"), TagField.of("$.condition").as("condition") }; jedis.ftCreate("idx:bicycle", FTCreateParams.createParams() .on(IndexDataType.JSON) .addPrefix("bicycle:"), schema ); String[] bicycleJsons = new String[] { " {" + " \"pickup_zone\": \"POLYGON((-74.0610 40.7578, -73.9510 40.7578, -73.9510 40.6678, " + "-74.0610 40.6678, -74.0610 40.7578))\"," + " \"store_location\": \"-74.0060,40.7128\"," + " \"brand\": \"Velorim\"," + " \"model\": \"Jigger\"," + " \"price\": 270," + " \"description\": \"Small and powerful, the Jigger is the best ride for the smallest of tikes! " + "This is the tiniest kids’ pedal bike on the market available without a coaster brake, the Jigger " + "is the vehicle of choice for the rare tenacious little rider raring to go.\"," + " \"condition\": \"new\"" + " }", " {" + " \"pickup_zone\": \"POLYGON((-118.2887 34.0972, -118.1987 34.0972, -118.1987 33.9872, " + "-118.2887 33.9872, -118.2887 34.0972))\"," + " \"store_location\": \"-118.2437,34.0522\"," + " \"brand\": \"Bicyk\"," + " \"model\": \"Hillcraft\"," + " \"price\": 1200," + " \"description\": \"Kids want to ride with as little weight as possible. Especially " + "on an incline! They may be at the age when a 27.5'' wheel bike is just too clumsy coming " + "off a 24'' bike. The Hillcraft 26 is just the solution they need!\"," + " \"condition\": \"used\"" + " }", " {" + " \"pickup_zone\": \"POLYGON((-87.6848 41.9331, -87.5748 41.9331, -87.5748 41.8231, " + "-87.6848 41.8231, -87.6848 41.9331))\"," + " \"store_location\": \"-87.6298,41.8781\"," + " \"brand\": \"Nord\"," + " \"model\": \"Chook air 5\"," + " \"price\": 815," + " \"description\": \"The Chook Air 5 gives kids aged six years and older a durable " + "and uberlight mountain bike for their first experience on tracks and easy cruising through " + "forests and fields. The lower top tube makes it easy to mount and dismount in any " + "situation, giving your kids greater safety on the trails.\"," + " \"condition\": \"used\"" + " }", " {" + " \"pickup_zone\": \"POLYGON((-80.2433 25.8067, -80.1333 25.8067, -80.1333 25.6967, " + "-80.2433 25.6967, -80.2433 25.8067))\"," + " \"store_location\": \"-80.1918,25.7617\"," + " \"brand\": \"Eva\"," + " \"model\": \"Eva 291\"," + " \"price\": 3400," + " \"description\": \"The sister company to Nord, Eva launched in 2005 as the first " + "and only women-dedicated bicycle brand. Designed by women for women, allEva bikes " + "are optimized for the feminine physique using analytics from a body metrics database. " + "If you like 29ers, try the Eva 291. It’s a brand new bike for 2022.. This " + "full-suspension, cross-country ride has been designed for velocity. The 291 has " + "100mm of front and rear travel, a superlight aluminum frame and fast-rolling " + "29-inch wheels. Yippee!\"," + " \"condition\": \"used\"" + " }", " {" + " \"pickup_zone\": \"POLYGON((-122.4644 37.8199, -122.3544 37.8199, -122.3544 37.7099, " + "-122.4644 37.7099, -122.4644 37.8199))\"," + " \"store_location\": \"-122.4194,37.7749\"," + " \"brand\": \"Noka Bikes\"," + " \"model\": \"Kahuna\"," + " \"price\": 3200," + " \"description\": \"Whether you want to try your hand at XC racing or are looking " + "for a lively trail bike that's just as inspiring on the climbs as it is over rougher " + "ground, the Wilder is one heck of a bike built specifically for short women. Both the " + "frames and components have been tweaked to include a women’s saddle, different bars " + "and unique colourway.\"," + " \"condition\": \"used\"" + " }", " {" + " \"pickup_zone\": \"POLYGON((-0.1778 51.5524, 0.0822 51.5524, 0.0822 51.4024, " + "-0.1778 51.4024, -0.1778 51.5524))\"," + " \"store_location\": \"-0.1278,51.5074\"," + " \"brand\": \"Breakout\"," + " \"model\": \"XBN 2.1 Alloy\"," + " \"price\": 810," + " \"description\": \"The XBN 2.1 Alloy is our entry-level road bike – but that’s " + "not to say that it’s a basic machine. With an internal weld aluminium frame, a full " + "carbon fork, and the slick-shifting Claris gears from Shimano’s, this is a bike which " + "doesn’t break the bank and delivers craved performance.\"," + " \"condition\": \"new\"" + " }", " {" + " \"pickup_zone\": \"POLYGON((2.1767 48.9016, 2.5267 48.9016, 2.5267 48.5516, " + "2.1767 48.5516, 2.1767 48.9016))\"," + " \"store_location\": \"2.3522,48.8566\"," + " \"brand\": \"ScramBikes\"," + " \"model\": \"WattBike\"," + " \"price\": 2300," + " \"description\": \"The WattBike is the best e-bike for people who still " + "feel young at heart. It has a Bafang 1000W mid-drive system and a 48V 17.5AH " + "Samsung Lithium-Ion battery, allowing you to ride for more than 60 miles on one " + "charge. It’s great for tackling hilly terrain or if you just fancy a more " + "leisurely ride. With three working modes, you can choose between E-bike, " + "assisted bicycle, and normal bike modes.\"," + " \"condition\": \"new\"" + " }", " {" + " \"pickup_zone\": \"POLYGON((13.3260 52.5700, 13.6550 52.5700, 13.6550 52.2700, " + "13.3260 52.2700, 13.3260 52.5700))\"," + " \"store_location\": \"13.4050,52.5200\"," + " \"brand\": \"Peaknetic\"," + " \"model\": \"Secto\"," + " \"price\": 430," + " \"description\": \"If you struggle with stiff fingers or a kinked neck or " + "back after a few minutes on the road, this lightweight, aluminum bike alleviates " + "those issues and allows you to enjoy the ride. From the ergonomic grips to the " + "lumbar-supporting seat position, the Roll Low-Entry offers incredible comfort. " + "The rear-inclined seat tube facilitates stability by allowing you to put a foot " + "on the ground to balance at a stop, and the low step-over frame makes it " + "accessible for all ability and mobility levels. The saddle is very soft, with " + "a wide back to support your hip joints and a cutout in the center to redistribute " + "that pressure. Rim brakes deliver satisfactory braking control, and the wide tires " + "provide a smooth, stable ride on paved roads and gravel. Rack and fender mounts " + "facilitate setting up the Roll Low-Entry as your preferred commuter, and the " + "BMX-like handlebar offers space for mounting a flashlight, bell, or phone holder.\"," + " \"condition\": \"new\"" + " }", " {" + " \"pickup_zone\": \"POLYGON((1.9450 41.4301, 2.4018 41.4301, 2.4018 41.1987, " + "1.9450 41.1987, 1.9450 41.4301))\"," + " \"store_location\": \"2.1734, 41.3851\"," + " \"brand\": \"nHill\"," + " \"model\": \"Summit\"," + " \"price\": 1200," + " \"description\": \"This budget mountain bike from nHill performs well both " + "on bike paths and on the trail. The fork with 100mm of travel absorbs rough " + "terrain. Fat Kenda Booster tires give you grip in corners and on wet trails. " + "The Shimano Tourney drivetrain offered enough gears for finding a comfortable " + "pace to ride uphill, and the Tektro hydraulic disc brakes break smoothly. " + "Whether you want an affordable bike that you can take to work, but also take " + "trail in mountains on the weekends or you’re just after a stable, comfortable " + "ride for the bike path, the Summit gives a good value for money.\"," + " \"condition\": \"new\"" + " }", " {" + " \"pickup_zone\": \"POLYGON((12.4464 42.1028, 12.5464 42.1028, " + "12.5464 41.7028, 12.4464 41.7028, 12.4464 42.1028))\"," + " \"store_location\": \"12.4964,41.9028\"," + " \"model\": \"ThrillCycle\"," + " \"brand\": \"BikeShind\"," + " \"price\": 815," + " \"description\": \"An artsy, retro-inspired bicycle that’s as " + "functional as it is pretty: The ThrillCycle steel frame offers a smooth ride. " + "A 9-speed drivetrain has enough gears for coasting in the city, but we wouldn’t " + "suggest taking it to the mountains. Fenders protect you from mud, and a rear " + "basket lets you transport groceries, flowers and books. The ThrillCycle comes " + "with a limited lifetime warranty, so this little guy will last you long " + "past graduation.\"," + " \"condition\": \"refurbished\"" + " }" }; for (int i = 0; i < bicycleJsons.length; i++) { jedis.jsonSet("bicycle:" + i, Path2.ROOT_PATH, bicycleJsons[i]); } // HIDE_END // STEP_START em1 SearchResult res1 = jedis.ftSearch("idx:bicycle", "@price:[270 270]"); System.out.println(res1.getTotalResults()); // >>> 1 List docs1 = res1.getDocuments(); for (int i = 0; i < docs1.size(); i++) { System.out.println(docs1.get(i).getId()); } // >>> bicycle:0 SearchResult res2 = jedis.ftSearch("idx:bicycle", "*", FTSearchParams.searchParams() .filter("price", 270, 270) ); System.out.println(res2.getTotalResults()); // >>> 1 List docs2 = res2.getDocuments(); for (int i = 0; i < docs2.size(); i++) { System.out.println(docs2.get(i).getId()); } // >>> bicycle:0 // STEP_END // Tests for 'em1' step. // REMOVE_START assertEquals(1, res1.getTotalResults()); assertEquals("bicycle:0", docs1.get(0).getId()); assertEquals(1, res2.getTotalResults()); assertEquals("bicycle:0", docs2.get(0).getId()); // REMOVE_END // STEP_START em2 SearchResult res3 = jedis.ftSearch("idx:bicycle", "@condition:{new}"); System.out.println(res3.getTotalResults()); // >>> 5 List docs3 = res3.getDocuments(); for (int i = 0; i < docs3.size(); i++) { System.out.println(docs3.get(i).getId()); } // >>> bicycle:5 // >>> bicycle:0 // >>> bicycle:6 // >>> bicycle:7 // >>> bicycle:8 // STEP_END // Tests for 'em2' step. // REMOVE_START assertEquals(5, res3.getTotalResults()); assertArrayEquals( new String[] {"bicycle:0", "bicycle:5", "bicycle:6", "bicycle:7", "bicycle:8" }, docs3.stream().map(Document::getId).sorted().toArray() ); // REMOVE_END // STEP_START em3 SchemaField[] emailSchema = { TextField.of("$.email").as("email") }; jedis.ftCreate("idx:email", new FTCreateParams() .addPrefix("key:") .on(IndexDataType.JSON), emailSchema ); jedis.jsonSet("key:1", Path2.ROOT_PATH, "{\"email\": \"test@redis.com\"}"); SearchResult res4 = jedis.ftSearch("idx:email", RediSearchUtil.escapeQuery("@email{test@redis.com}"), new FTSearchParams().dialect(2) ); System.out.println(res4.getTotalResults()); // STEP_END // Tests for 'em3' step. // REMOVE_START jedis.ftDropIndex("idx:email"); // REMOVE_END // STEP_START em4 SearchResult res5 = jedis.ftSearch("idx:bicycle", "@description:\"rough terrain\"" ); System.out.println(res5.getTotalResults()); // >>> 1 List docs5 = res5.getDocuments(); for (int i = 0; i < docs5.size(); i++) { System.out.println(docs5.get(i).getId()); } // >>> bicycle:8 // STEP_END // Tests for 'em4' step. // REMOVE_START assertEquals(1, res5.getTotalResults()); assertEquals("bicycle:8", docs5.get(0).getId()); // REMOVE_END // HIDE_START jedis.close(); } } // HIDE_END ================================================ FILE: src/test/java/io/redis/examples/QueryFtExample.java ================================================ // EXAMPLE: query_ft // REMOVE_START package io.redis.examples; import org.junit.jupiter.api.Test; // REMOVE_END // HIDE_START import java.util.List; import redis.clients.jedis.RedisClient; import redis.clients.jedis.search.*; import redis.clients.jedis.search.schemafields.*; import redis.clients.jedis.exceptions.JedisDataException; import redis.clients.jedis.json.Path2; import static org.junit.jupiter.api.Assertions.assertArrayEquals; import static org.junit.jupiter.api.Assertions.assertEquals; // HIDE_END // HIDE_START public class QueryFtExample { @Test public void run() { RedisClient jedis = RedisClient.create("redis://localhost:6379"); //REMOVE_START // Clear any keys here before using them in tests. try {jedis.ftDropIndex("idx:bicycle");} catch (JedisDataException j){} //REMOVE_END // HIDE_END SchemaField[] schema = { TextField.of("$.brand").as("brand"), TextField.of("$.model").as("model"), TextField.of("$.description").as("description"), NumericField.of("$.price").as("price"), TagField.of("$.condition").as("condition") }; jedis.ftCreate("idx:bicycle", FTCreateParams.createParams() .on(IndexDataType.JSON) .addPrefix("bicycle:"), schema ); String[] bicycleJsons = new String[] { " {" + " \"pickup_zone\": \"POLYGON((-74.0610 40.7578, -73.9510 40.7578, -73.9510 40.6678, " + "-74.0610 40.6678, -74.0610 40.7578))\"," + " \"store_location\": \"-74.0060,40.7128\"," + " \"brand\": \"Velorim\"," + " \"model\": \"Jigger\"," + " \"price\": 270," + " \"description\": \"Small and powerful, the Jigger is the best ride for the smallest of tikes! " + "This is the tiniest kids’ pedal bike on the market available without a coaster brake, the Jigger " + "is the vehicle of choice for the rare tenacious little rider raring to go.\"," + " \"condition\": \"new\"" + " }", " {" + " \"pickup_zone\": \"POLYGON((-118.2887 34.0972, -118.1987 34.0972, -118.1987 33.9872, " + "-118.2887 33.9872, -118.2887 34.0972))\"," + " \"store_location\": \"-118.2437,34.0522\"," + " \"brand\": \"Bicyk\"," + " \"model\": \"Hillcraft\"," + " \"price\": 1200," + " \"description\": \"Kids want to ride with as little weight as possible. Especially " + "on an incline! They may be at the age when a 27.5'' wheel bike is just too clumsy coming " + "off a 24'' bike. The Hillcraft 26 is just the solution they need!\"," + " \"condition\": \"used\"" + " }", " {" + " \"pickup_zone\": \"POLYGON((-87.6848 41.9331, -87.5748 41.9331, -87.5748 41.8231, " + "-87.6848 41.8231, -87.6848 41.9331))\"," + " \"store_location\": \"-87.6298,41.8781\"," + " \"brand\": \"Nord\"," + " \"model\": \"Chook air 5\"," + " \"price\": 815," + " \"description\": \"The Chook Air 5 gives kids aged six years and older a durable " + "and uberlight mountain bike for their first experience on tracks and easy cruising through " + "forests and fields. The lower top tube makes it easy to mount and dismount in any " + "situation, giving your kids greater safety on the trails.\"," + " \"condition\": \"used\"" + " }", " {" + " \"pickup_zone\": \"POLYGON((-80.2433 25.8067, -80.1333 25.8067, -80.1333 25.6967, " + "-80.2433 25.6967, -80.2433 25.8067))\"," + " \"store_location\": \"-80.1918,25.7617\"," + " \"brand\": \"Eva\"," + " \"model\": \"Eva 291\"," + " \"price\": 3400," + " \"description\": \"The sister company to Nord, Eva launched in 2005 as the first " + "and only women-dedicated bicycle brand. Designed by women for women, allEva bikes " + "are optimized for the feminine physique using analytics from a body metrics database. " + "If you like 29ers, try the Eva 291. It’s a brand new bike for 2022.. This " + "full-suspension, cross-country ride has been designed for velocity. The 291 has " + "100mm of front and rear travel, a superlight aluminum frame and fast-rolling " + "29-inch wheels. Yippee!\"," + " \"condition\": \"used\"" + " }", " {" + " \"pickup_zone\": \"POLYGON((-122.4644 37.8199, -122.3544 37.8199, -122.3544 37.7099, " + "-122.4644 37.7099, -122.4644 37.8199))\"," + " \"store_location\": \"-122.4194,37.7749\"," + " \"brand\": \"Noka Bikes\"," + " \"model\": \"Kahuna\"," + " \"price\": 3200," + " \"description\": \"Whether you want to try your hand at XC racing or are looking " + "for a lively trail bike that's just as inspiring on the climbs as it is over rougher " + "ground, the Wilder is one heck of a bike built specifically for short women. Both the " + "frames and components have been tweaked to include a women’s saddle, different bars " + "and unique colourway.\"," + " \"condition\": \"used\"" + " }", " {" + " \"pickup_zone\": \"POLYGON((-0.1778 51.5524, 0.0822 51.5524, 0.0822 51.4024, " + "-0.1778 51.4024, -0.1778 51.5524))\"," + " \"store_location\": \"-0.1278,51.5074\"," + " \"brand\": \"Breakout\"," + " \"model\": \"XBN 2.1 Alloy\"," + " \"price\": 810," + " \"description\": \"The XBN 2.1 Alloy is our entry-level road bike – but that’s " + "not to say that it’s a basic machine. With an internal weld aluminium frame, a full " + "carbon fork, and the slick-shifting Claris gears from Shimano’s, this is a bike which " + "doesn’t break the bank and delivers craved performance.\"," + " \"condition\": \"new\"" + " }", " {" + " \"pickup_zone\": \"POLYGON((2.1767 48.9016, 2.5267 48.9016, 2.5267 48.5516, " + "2.1767 48.5516, 2.1767 48.9016))\"," + " \"store_location\": \"2.3522,48.8566\"," + " \"brand\": \"ScramBikes\"," + " \"model\": \"WattBike\"," + " \"price\": 2300," + " \"description\": \"The WattBike is the best e-bike for people who still " + "feel young at heart. It has a Bafang 1000W mid-drive system and a 48V 17.5AH " + "Samsung Lithium-Ion battery, allowing you to ride for more than 60 miles on one " + "charge. It’s great for tackling hilly terrain or if you just fancy a more " + "leisurely ride. With three working modes, you can choose between E-bike, " + "assisted bicycle, and normal bike modes.\"," + " \"condition\": \"new\"" + " }", " {" + " \"pickup_zone\": \"POLYGON((13.3260 52.5700, 13.6550 52.5700, 13.6550 52.2700, " + "13.3260 52.2700, 13.3260 52.5700))\"," + " \"store_location\": \"13.4050,52.5200\"," + " \"brand\": \"Peaknetic\"," + " \"model\": \"Secto\"," + " \"price\": 430," + " \"description\": \"If you struggle with stiff fingers or a kinked neck or " + "back after a few minutes on the road, this lightweight, aluminum bike alleviates " + "those issues and allows you to enjoy the ride. From the ergonomic grips to the " + "lumbar-supporting seat position, the Roll Low-Entry offers incredible comfort. " + "The rear-inclined seat tube facilitates stability by allowing you to put a foot " + "on the ground to balance at a stop, and the low step-over frame makes it " + "accessible for all ability and mobility levels. The saddle is very soft, with " + "a wide back to support your hip joints and a cutout in the center to redistribute " + "that pressure. Rim brakes deliver satisfactory braking control, and the wide tires " + "provide a smooth, stable ride on paved roads and gravel. Rack and fender mounts " + "facilitate setting up the Roll Low-Entry as your preferred commuter, and the " + "BMX-like handlebar offers space for mounting a flashlight, bell, or phone holder.\"," + " \"condition\": \"new\"" + " }", " {" + " \"pickup_zone\": \"POLYGON((1.9450 41.4301, 2.4018 41.4301, 2.4018 41.1987, " + "1.9450 41.1987, 1.9450 41.4301))\"," + " \"store_location\": \"2.1734, 41.3851\"," + " \"brand\": \"nHill\"," + " \"model\": \"Summit\"," + " \"price\": 1200," + " \"description\": \"This budget mountain bike from nHill performs well both " + "on bike paths and on the trail. The fork with 100mm of travel absorbs rough " + "terrain. Fat Kenda Booster tires give you grip in corners and on wet trails. " + "The Shimano Tourney drivetrain offered enough gears for finding a comfortable " + "pace to ride uphill, and the Tektro hydraulic disc brakes break smoothly. " + "Whether you want an affordable bike that you can take to work, but also take " + "trail in mountains on the weekends or you’re just after a stable, comfortable " + "ride for the bike path, the Summit gives a good value for money.\"," + " \"condition\": \"new\"" + " }", " {" + " \"pickup_zone\": \"POLYGON((12.4464 42.1028, 12.5464 42.1028, " + "12.5464 41.7028, 12.4464 41.7028, 12.4464 42.1028))\"," + " \"store_location\": \"12.4964,41.9028\"," + " \"model\": \"ThrillCycle\"," + " \"brand\": \"BikeShind\"," + " \"price\": 815," + " \"description\": \"An artsy, retro-inspired bicycle that’s as " + "functional as it is pretty: The ThrillCycle steel frame offers a smooth ride. " + "A 9-speed drivetrain has enough gears for coasting in the city, but we wouldn’t " + "suggest taking it to the mountains. Fenders protect you from mud, and a rear " + "basket lets you transport groceries, flowers and books. The ThrillCycle comes " + "with a limited lifetime warranty, so this little guy will last you long " + "past graduation.\"," + " \"condition\": \"refurbished\"" + " }" }; for (int i = 0; i < bicycleJsons.length; i++) { jedis.jsonSet("bicycle:" + i, Path2.ROOT_PATH, bicycleJsons[i]); } // STEP_START ft1 SearchResult res1 = jedis.ftSearch("idx:bicycle", "@description: kids"); System.out.println(res1.getTotalResults()); // >>> 2 List docs1 = res1.getDocuments(); for (int i = 0; i < docs1.size(); i++) { System.out.println(docs1.get(i).getId()); } // >>> bicycle:2 // >>> bicycle:1 // STEP_END // Tests for 'ft1' step. // REMOVE_START assertEquals(2, res1.getTotalResults()); assertArrayEquals( new String[] {"bicycle:1", "bicycle:2" }, docs1.stream().map(Document::getId).sorted().toArray() ); // REMOVE_END // STEP_START ft2 SearchResult res2 = jedis.ftSearch("idx:bicycle", "@model: ka*"); System.out.println(res2.getTotalResults()); // >>> 1 List docs2 = res2.getDocuments(); for (int i = 0; i < docs2.size(); i++) { System.out.println(docs2.get(i).getId()); } // >>> bicycle:4 // STEP_END // Tests for 'ft2' step. // REMOVE_START assertEquals(1, res2.getTotalResults()); assertEquals("bicycle:4", docs2.get(0).getId()); // REMOVE_END // STEP_START ft3 SearchResult res3 = jedis.ftSearch("idx:bicycle", "@brand: *bikes"); System.out.println(res3.getTotalResults()); // >>> 2 List docs3 = res3.getDocuments(); for (int i = 0; i < docs3.size(); i++) { System.out.println(docs3.get(i).getId()); } // >>> bicycle:6 // >>> bicycle:4 // STEP_END // Tests for 'ft3' step. // REMOVE_START assertEquals(2, res3.getTotalResults()); assertArrayEquals( new String[] {"bicycle:4", "bicycle:6" }, docs3.stream().map(Document::getId).sorted().toArray() ); // REMOVE_END // STEP_START ft4 SearchResult res4 = jedis.ftSearch("idx:bicycle", "%optamized%"); System.out.println(res4.getTotalResults()); // >>> 1 List docs4 = res4.getDocuments(); for (int i = 0; i < docs4.size(); i++) { System.out.println(docs4.get(i).getId()); } // >>> bicycle:3 // STEP_END // Tests for 'ft4' step. // REMOVE_START assertEquals(1, res4.getTotalResults()); assertEquals("bicycle:3", docs4.get(0).getId()); // REMOVE_END // STEP_START ft5 SearchResult res5 = jedis.ftSearch("idx:bicycle", "%%optamised%%"); System.out.println(res5.getTotalResults()); // >>> 1 List docs5 = res5.getDocuments(); for (int i = 0; i < docs5.size(); i++) { System.out.println(docs5.get(i).getId()); } // >>> bicycle:3 // STEP_END // Tests for 'ft5' step. // REMOVE_START assertEquals(1, res5.getTotalResults()); assertEquals("bicycle:3", docs5.get(0).getId()); // REMOVE_END // HIDE_START jedis.close(); } } // HIDE_END ================================================ FILE: src/test/java/io/redis/examples/QueryGeoExample.java ================================================ // EXAMPLE: query_geo // REMOVE_START package io.redis.examples; import org.junit.jupiter.api.Test; // REMOVE_END // HIDE_START import java.util.List; import java.util.stream.Stream; import redis.clients.jedis.RedisClient; import redis.clients.jedis.search.*; import redis.clients.jedis.search.schemafields.*; import redis.clients.jedis.search.schemafields.GeoShapeField.CoordinateSystem; import redis.clients.jedis.exceptions.JedisDataException; import redis.clients.jedis.json.Path2; import static org.junit.jupiter.api.Assertions.assertArrayEquals; import static org.junit.jupiter.api.Assertions.assertEquals; // HIDE_END // HIDE_START public class QueryGeoExample { @Test public void run() { RedisClient jedis = RedisClient.create("redis://localhost:6379"); //REMOVE_START // Clear any keys here before using them in tests. try { jedis.ftDropIndex("idx:bicycle"); } catch (JedisDataException j) {} //REMOVE_END // HIDE_END SchemaField[] schema = { TextField.of("$.brand").as("brand"), TextField.of("$.model").as("model"), TextField.of("$.description").as("description"), NumericField.of("$.price").as("price"), TagField.of("$.condition").as("condition"), GeoField.of("$.store_location").as("store_location"), GeoShapeField.of("$.pickup_zone", CoordinateSystem.FLAT).as("pickup_zone") }; jedis.ftCreate("idx:bicycle", FTCreateParams.createParams() .on(IndexDataType.JSON) .addPrefix("bicycle:"), schema ); String[] bicycleJsons = new String[] { " {" + " \"pickup_zone\": \"POLYGON((-74.0610 40.7578, -73.9510 40.7578, -73.9510 40.6678, " + "-74.0610 40.6678, -74.0610 40.7578))\"," + " \"store_location\": \"-74.0060,40.7128\"," + " \"brand\": \"Velorim\"," + " \"model\": \"Jigger\"," + " \"price\": 270," + " \"description\": \"Small and powerful, the Jigger is the best ride for the smallest of tikes! " + "This is the tiniest kids’ pedal bike on the market available without a coaster brake, the Jigger " + "is the vehicle of choice for the rare tenacious little rider raring to go.\"," + " \"condition\": \"new\"" + " }", " {" + " \"pickup_zone\": \"POLYGON((-118.2887 34.0972, -118.1987 34.0972, -118.1987 33.9872, " + "-118.2887 33.9872, -118.2887 34.0972))\"," + " \"store_location\": \"-118.2437,34.0522\"," + " \"brand\": \"Bicyk\"," + " \"model\": \"Hillcraft\"," + " \"price\": 1200," + " \"description\": \"Kids want to ride with as little weight as possible. Especially " + "on an incline! They may be at the age when a 27.5'' wheel bike is just too clumsy coming " + "off a 24'' bike. The Hillcraft 26 is just the solution they need!\"," + " \"condition\": \"used\"" + " }", " {" + " \"pickup_zone\": \"POLYGON((-87.6848 41.9331, -87.5748 41.9331, -87.5748 41.8231, " + "-87.6848 41.8231, -87.6848 41.9331))\"," + " \"store_location\": \"-87.6298,41.8781\"," + " \"brand\": \"Nord\"," + " \"model\": \"Chook air 5\"," + " \"price\": 815," + " \"description\": \"The Chook Air 5 gives kids aged six years and older a durable " + "and uberlight mountain bike for their first experience on tracks and easy cruising through " + "forests and fields. The lower top tube makes it easy to mount and dismount in any " + "situation, giving your kids greater safety on the trails.\"," + " \"condition\": \"used\"" + " }", " {" + " \"pickup_zone\": \"POLYGON((-80.2433 25.8067, -80.1333 25.8067, -80.1333 25.6967, " + "-80.2433 25.6967, -80.2433 25.8067))\"," + " \"store_location\": \"-80.1918,25.7617\"," + " \"brand\": \"Eva\"," + " \"model\": \"Eva 291\"," + " \"price\": 3400," + " \"description\": \"The sister company to Nord, Eva launched in 2005 as the first " + "and only women-dedicated bicycle brand. Designed by women for women, allEva bikes " + "are optimized for the feminine physique using analytics from a body metrics database. " + "If you like 29ers, try the Eva 291. It’s a brand new bike for 2022.. This " + "full-suspension, cross-country ride has been designed for velocity. The 291 has " + "100mm of front and rear travel, a superlight aluminum frame and fast-rolling " + "29-inch wheels. Yippee!\"," + " \"condition\": \"used\"" + " }", " {" + " \"pickup_zone\": \"POLYGON((-122.4644 37.8199, -122.3544 37.8199, -122.3544 37.7099, " + "-122.4644 37.7099, -122.4644 37.8199))\"," + " \"store_location\": \"-122.4194,37.7749\"," + " \"brand\": \"Noka Bikes\"," + " \"model\": \"Kahuna\"," + " \"price\": 3200," + " \"description\": \"Whether you want to try your hand at XC racing or are looking " + "for a lively trail bike that's just as inspiring on the climbs as it is over rougher " + "ground, the Wilder is one heck of a bike built specifically for short women. Both the " + "frames and components have been tweaked to include a women’s saddle, different bars " + "and unique colourway.\"," + " \"condition\": \"used\"" + " }", " {" + " \"pickup_zone\": \"POLYGON((-0.1778 51.5524, 0.0822 51.5524, 0.0822 51.4024, " + "-0.1778 51.4024, -0.1778 51.5524))\"," + " \"store_location\": \"-0.1278,51.5074\"," + " \"brand\": \"Breakout\"," + " \"model\": \"XBN 2.1 Alloy\"," + " \"price\": 810," + " \"description\": \"The XBN 2.1 Alloy is our entry-level road bike – but that’s " + "not to say that it’s a basic machine. With an internal weld aluminium frame, a full " + "carbon fork, and the slick-shifting Claris gears from Shimano’s, this is a bike which " + "doesn’t break the bank and delivers craved performance.\"," + " \"condition\": \"new\"" + " }", " {" + " \"pickup_zone\": \"POLYGON((2.1767 48.9016, 2.5267 48.9016, 2.5267 48.5516, " + "2.1767 48.5516, 2.1767 48.9016))\"," + " \"store_location\": \"2.3522,48.8566\"," + " \"brand\": \"ScramBikes\"," + " \"model\": \"WattBike\"," + " \"price\": 2300," + " \"description\": \"The WattBike is the best e-bike for people who still " + "feel young at heart. It has a Bafang 1000W mid-drive system and a 48V 17.5AH " + "Samsung Lithium-Ion battery, allowing you to ride for more than 60 miles on one " + "charge. It’s great for tackling hilly terrain or if you just fancy a more " + "leisurely ride. With three working modes, you can choose between E-bike, " + "assisted bicycle, and normal bike modes.\"," + " \"condition\": \"new\"" + " }", " {" + " \"pickup_zone\": \"POLYGON((13.3260 52.5700, 13.6550 52.5700, 13.6550 52.2700, " + "13.3260 52.2700, 13.3260 52.5700))\"," + " \"store_location\": \"13.4050,52.5200\"," + " \"brand\": \"Peaknetic\"," + " \"model\": \"Secto\"," + " \"price\": 430," + " \"description\": \"If you struggle with stiff fingers or a kinked neck or " + "back after a few minutes on the road, this lightweight, aluminum bike alleviates " + "those issues and allows you to enjoy the ride. From the ergonomic grips to the " + "lumbar-supporting seat position, the Roll Low-Entry offers incredible comfort. " + "The rear-inclined seat tube facilitates stability by allowing you to put a foot " + "on the ground to balance at a stop, and the low step-over frame makes it " + "accessible for all ability and mobility levels. The saddle is very soft, with " + "a wide back to support your hip joints and a cutout in the center to redistribute " + "that pressure. Rim brakes deliver satisfactory braking control, and the wide tires " + "provide a smooth, stable ride on paved roads and gravel. Rack and fender mounts " + "facilitate setting up the Roll Low-Entry as your preferred commuter, and the " + "BMX-like handlebar offers space for mounting a flashlight, bell, or phone holder.\"," + " \"condition\": \"new\"" + " }", " {" + " \"pickup_zone\": \"POLYGON((1.9450 41.4301, 2.4018 41.4301, 2.4018 41.1987, " + "1.9450 41.1987, 1.9450 41.4301))\"," + " \"store_location\": \"2.1734, 41.3851\"," + " \"brand\": \"nHill\"," + " \"model\": \"Summit\"," + " \"price\": 1200," + " \"description\": \"This budget mountain bike from nHill performs well both " + "on bike paths and on the trail. The fork with 100mm of travel absorbs rough " + "terrain. Fat Kenda Booster tires give you grip in corners and on wet trails. " + "The Shimano Tourney drivetrain offered enough gears for finding a comfortable " + "pace to ride uphill, and the Tektro hydraulic disc brakes break smoothly. " + "Whether you want an affordable bike that you can take to work, but also take " + "trail in mountains on the weekends or you’re just after a stable, comfortable " + "ride for the bike path, the Summit gives a good value for money.\"," + " \"condition\": \"new\"" + " }", " {" + " \"pickup_zone\": \"POLYGON((12.4464 42.1028, 12.5464 42.1028, " + "12.5464 41.7028, 12.4464 41.7028, 12.4464 42.1028))\"," + " \"store_location\": \"12.4964,41.9028\"," + " \"model\": \"ThrillCycle\"," + " \"brand\": \"BikeShind\"," + " \"price\": 815," + " \"description\": \"An artsy, retro-inspired bicycle that’s as " + "functional as it is pretty: The ThrillCycle steel frame offers a smooth ride. " + "A 9-speed drivetrain has enough gears for coasting in the city, but we wouldn’t " + "suggest taking it to the mountains. Fenders protect you from mud, and a rear " + "basket lets you transport groceries, flowers and books. The ThrillCycle comes " + "with a limited lifetime warranty, so this little guy will last you long " + "past graduation.\"," + " \"condition\": \"refurbished\"" + " }" }; for (int i = 0; i < bicycleJsons.length; i++) { jedis.jsonSet("bicycle:" + i, Path2.ROOT_PATH, bicycleJsons[i]); } // STEP_START geo1 SearchResult res1 = jedis.ftSearch("idx:bicycle", "@store_location:[$lon $lat $radius $units]", FTSearchParams.searchParams() .addParam("lon", -0.1778) .addParam("lat", 51.5524) .addParam("radius", 20) .addParam("units", "mi") .dialect(2) ); System.out.println(res1.getTotalResults()); // >>> 1 List docs1 = res1.getDocuments(); for (Document document : docs1) { System.out.println(document.getId()); } // >>> bicycle:5 // STEP_END // Tests for 'geo1' step. // REMOVE_START assertEquals(1, res1.getTotalResults()); assertEquals("bicycle:5", docs1.get(0).getId()); // REMOVE_END // STEP_START geo2 SearchResult res2 = jedis.ftSearch("idx:bicycle", "@pickup_zone:[CONTAINS $bike]", FTSearchParams.searchParams() .addParam("bike", "POINT(-0.1278 51.5074)") .dialect(3) ); System.out.println(res2.getTotalResults()); // >>> 1 List docs2 = res2.getDocuments(); for (Document document : docs2) { System.out.println(document.getId()); } // >>> bicycle:5 // STEP_END // Tests for 'geo2' step. // REMOVE_START assertEquals(1, res2.getTotalResults()); assertEquals("bicycle:5", docs2.get(0).getId()); // REMOVE_END // STEP_START geo3 SearchResult res3 = jedis.ftSearch("idx:bicycle", "@pickup_zone:[WITHIN $europe]", FTSearchParams.searchParams() .addParam("europe", "POLYGON((-25 35, 40 35, 40 70, -25 70, -25 35))") .dialect(3) ); System.out.println(res3.getTotalResults()); // >>> 5 List docs3 = res3.getDocuments(); for (Document document : docs3) { System.out.println(document.getId()); } // >>> bicycle:5 // >>> bicycle:6 // >>> bicycle:7 // >>> bicycle:8 // >>> bicycle:9 // STEP_END // Tests for 'geo3' step. // REMOVE_START assertEquals(5, res3.getTotalResults()); assertArrayEquals( Stream.of("bicycle:5", "bicycle:6", "bicycle:7", "bicycle:8", "bicycle:9").sorted() .toArray(), docs3.stream().map(Document::getId).sorted().toArray()); // REMOVE_END // HIDE_START jedis.close(); } } // HIDE_END ================================================ FILE: src/test/java/io/redis/examples/QueryRangeExample.java ================================================ // EXAMPLE: query_range // REMOVE_START package io.redis.examples; import org.junit.jupiter.api.Test; // REMOVE_END // HIDE_START import java.util.List; // REMOVE_START import java.util.stream.Stream; // REMOVE_END import redis.clients.jedis.RedisClient; import redis.clients.jedis.search.*; import redis.clients.jedis.search.schemafields.*; import redis.clients.jedis.exceptions.JedisDataException; import redis.clients.jedis.json.Path2; import redis.clients.jedis.args.SortingOrder; import static org.junit.jupiter.api.Assertions.assertArrayEquals; import static org.junit.jupiter.api.Assertions.assertEquals; // HIDE_END // HIDE_START public class QueryRangeExample { @Test public void run() { RedisClient jedis = RedisClient.create("redis://localhost:6379"); //REMOVE_START // Clear any keys here before using them in tests. try { jedis.ftDropIndex("idx:bicycle"); } catch (JedisDataException j) {} //REMOVE_END SchemaField[] schema = { TextField.of("$.brand").as("brand"), TextField.of("$.model").as("model"), TextField.of("$.description").as("description"), NumericField.of("$.price").as("price"), TagField.of("$.condition").as("condition") }; jedis.ftCreate("idx:bicycle", FTCreateParams.createParams() .on(IndexDataType.JSON) .addPrefix("bicycle:"), schema ); String[] bicycleJsons = new String[] { " {" + " \"pickup_zone\": \"POLYGON((-74.0610 40.7578, -73.9510 40.7578, -73.9510 40.6678, " + "-74.0610 40.6678, -74.0610 40.7578))\"," + " \"store_location\": \"-74.0060,40.7128\"," + " \"brand\": \"Velorim\"," + " \"model\": \"Jigger\"," + " \"price\": 270," + " \"description\": \"Small and powerful, the Jigger is the best ride for the smallest of tikes! " + "This is the tiniest kids’ pedal bike on the market available without a coaster brake, the Jigger " + "is the vehicle of choice for the rare tenacious little rider raring to go.\"," + " \"condition\": \"new\"" + " }", " {" + " \"pickup_zone\": \"POLYGON((-118.2887 34.0972, -118.1987 34.0972, -118.1987 33.9872, " + "-118.2887 33.9872, -118.2887 34.0972))\"," + " \"store_location\": \"-118.2437,34.0522\"," + " \"brand\": \"Bicyk\"," + " \"model\": \"Hillcraft\"," + " \"price\": 1200," + " \"description\": \"Kids want to ride with as little weight as possible. Especially " + "on an incline! They may be at the age when a 27.5'' wheel bike is just too clumsy coming " + "off a 24'' bike. The Hillcraft 26 is just the solution they need!\"," + " \"condition\": \"used\"" + " }", " {" + " \"pickup_zone\": \"POLYGON((-87.6848 41.9331, -87.5748 41.9331, -87.5748 41.8231, " + "-87.6848 41.8231, -87.6848 41.9331))\"," + " \"store_location\": \"-87.6298,41.8781\"," + " \"brand\": \"Nord\"," + " \"model\": \"Chook air 5\"," + " \"price\": 815," + " \"description\": \"The Chook Air 5 gives kids aged six years and older a durable " + "and uberlight mountain bike for their first experience on tracks and easy cruising through " + "forests and fields. The lower top tube makes it easy to mount and dismount in any " + "situation, giving your kids greater safety on the trails.\"," + " \"condition\": \"used\"" + " }", " {" + " \"pickup_zone\": \"POLYGON((-80.2433 25.8067, -80.1333 25.8067, -80.1333 25.6967, " + "-80.2433 25.6967, -80.2433 25.8067))\"," + " \"store_location\": \"-80.1918,25.7617\"," + " \"brand\": \"Eva\"," + " \"model\": \"Eva 291\"," + " \"price\": 3400," + " \"description\": \"The sister company to Nord, Eva launched in 2005 as the first " + "and only women-dedicated bicycle brand. Designed by women for women, allEva bikes " + "are optimized for the feminine physique using analytics from a body metrics database. " + "If you like 29ers, try the Eva 291. It’s a brand new bike for 2022.. This " + "full-suspension, cross-country ride has been designed for velocity. The 291 has " + "100mm of front and rear travel, a superlight aluminum frame and fast-rolling " + "29-inch wheels. Yippee!\"," + " \"condition\": \"used\"" + " }", " {" + " \"pickup_zone\": \"POLYGON((-122.4644 37.8199, -122.3544 37.8199, -122.3544 37.7099, " + "-122.4644 37.7099, -122.4644 37.8199))\"," + " \"store_location\": \"-122.4194,37.7749\"," + " \"brand\": \"Noka Bikes\"," + " \"model\": \"Kahuna\"," + " \"price\": 3200," + " \"description\": \"Whether you want to try your hand at XC racing or are looking " + "for a lively trail bike that's just as inspiring on the climbs as it is over rougher " + "ground, the Wilder is one heck of a bike built specifically for short women. Both the " + "frames and components have been tweaked to include a women’s saddle, different bars " + "and unique colourway.\"," + " \"condition\": \"used\"" + " }", " {" + " \"pickup_zone\": \"POLYGON((-0.1778 51.5524, 0.0822 51.5524, 0.0822 51.4024, " + "-0.1778 51.4024, -0.1778 51.5524))\"," + " \"store_location\": \"-0.1278,51.5074\"," + " \"brand\": \"Breakout\"," + " \"model\": \"XBN 2.1 Alloy\"," + " \"price\": 810," + " \"description\": \"The XBN 2.1 Alloy is our entry-level road bike – but that’s " + "not to say that it’s a basic machine. With an internal weld aluminium frame, a full " + "carbon fork, and the slick-shifting Claris gears from Shimano’s, this is a bike which " + "doesn’t break the bank and delivers craved performance.\"," + " \"condition\": \"new\"" + " }", " {" + " \"pickup_zone\": \"POLYGON((2.1767 48.9016, 2.5267 48.9016, 2.5267 48.5516, " + "2.1767 48.5516, 2.1767 48.9016))\"," + " \"store_location\": \"2.3522,48.8566\"," + " \"brand\": \"ScramBikes\"," + " \"model\": \"WattBike\"," + " \"price\": 2300," + " \"description\": \"The WattBike is the best e-bike for people who still " + "feel young at heart. It has a Bafang 1000W mid-drive system and a 48V 17.5AH " + "Samsung Lithium-Ion battery, allowing you to ride for more than 60 miles on one " + "charge. It’s great for tackling hilly terrain or if you just fancy a more " + "leisurely ride. With three working modes, you can choose between E-bike, " + "assisted bicycle, and normal bike modes.\"," + " \"condition\": \"new\"" + " }", " {" + " \"pickup_zone\": \"POLYGON((13.3260 52.5700, 13.6550 52.5700, 13.6550 52.2700, " + "13.3260 52.2700, 13.3260 52.5700))\"," + " \"store_location\": \"13.4050,52.5200\"," + " \"brand\": \"Peaknetic\"," + " \"model\": \"Secto\"," + " \"price\": 430," + " \"description\": \"If you struggle with stiff fingers or a kinked neck or " + "back after a few minutes on the road, this lightweight, aluminum bike alleviates " + "those issues and allows you to enjoy the ride. From the ergonomic grips to the " + "lumbar-supporting seat position, the Roll Low-Entry offers incredible comfort. " + "The rear-inclined seat tube facilitates stability by allowing you to put a foot " + "on the ground to balance at a stop, and the low step-over frame makes it " + "accessible for all ability and mobility levels. The saddle is very soft, with " + "a wide back to support your hip joints and a cutout in the center to redistribute " + "that pressure. Rim brakes deliver satisfactory braking control, and the wide tires " + "provide a smooth, stable ride on paved roads and gravel. Rack and fender mounts " + "facilitate setting up the Roll Low-Entry as your preferred commuter, and the " + "BMX-like handlebar offers space for mounting a flashlight, bell, or phone holder.\"," + " \"condition\": \"new\"" + " }", " {" + " \"pickup_zone\": \"POLYGON((1.9450 41.4301, 2.4018 41.4301, 2.4018 41.1987, " + "1.9450 41.1987, 1.9450 41.4301))\"," + " \"store_location\": \"2.1734, 41.3851\"," + " \"brand\": \"nHill\"," + " \"model\": \"Summit\"," + " \"price\": 1200," + " \"description\": \"This budget mountain bike from nHill performs well both " + "on bike paths and on the trail. The fork with 100mm of travel absorbs rough " + "terrain. Fat Kenda Booster tires give you grip in corners and on wet trails. " + "The Shimano Tourney drivetrain offered enough gears for finding a comfortable " + "pace to ride uphill, and the Tektro hydraulic disc brakes break smoothly. " + "Whether you want an affordable bike that you can take to work, but also take " + "trail in mountains on the weekends or you’re just after a stable, comfortable " + "ride for the bike path, the Summit gives a good value for money.\"," + " \"condition\": \"new\"" + " }", " {" + " \"pickup_zone\": \"POLYGON((12.4464 42.1028, 12.5464 42.1028, " + "12.5464 41.7028, 12.4464 41.7028, 12.4464 42.1028))\"," + " \"store_location\": \"12.4964,41.9028\"," + " \"model\": \"ThrillCycle\"," + " \"brand\": \"BikeShind\"," + " \"price\": 815," + " \"description\": \"An artsy, retro-inspired bicycle that’s as " + "functional as it is pretty: The ThrillCycle steel frame offers a smooth ride. " + "A 9-speed drivetrain has enough gears for coasting in the city, but we wouldn’t " + "suggest taking it to the mountains. Fenders protect you from mud, and a rear " + "basket lets you transport groceries, flowers and books. The ThrillCycle comes " + "with a limited lifetime warranty, so this little guy will last you long " + "past graduation.\"," + " \"condition\": \"refurbished\"" + " }" }; for (int i = 0; i < bicycleJsons.length; i++) { jedis.jsonSet("bicycle:" + i, Path2.ROOT_PATH, bicycleJsons[i]); } // HIDE_END // STEP_START range1 SearchResult res1 = jedis.ftSearch( "idx:bicycle", "@price:[500 1000]", FTSearchParams.searchParams().returnFields("price")); System.out.println(res1.getTotalResults()); // >>> 3 List docs1 = res1.getDocuments(); for (Document document : docs1) { System.out.println(document.getId() + " : price " + document.getString("price")); } // >>> bicycle:2 : price 815 // >>> bicycle:5 : price 810 // >>> bicycle:9 : price 815 // STEP_END // Tests for 'range1' step. // REMOVE_START assertEquals(3, res1.getTotalResults()); assertArrayEquals( Stream.of("bicycle:5", "bicycle:9", "bicycle:2").sorted().toArray(), docs1.stream().map(Document::getId).sorted().toArray() ); // REMOVE_END // STEP_START range2 SearchResult res2 = jedis.ftSearch("idx:bicycle", "*", FTSearchParams.searchParams() .returnFields("price") .filter("price", 500, 1000) ); System.out.println(res2.getTotalResults()); // >>> 3 List docs2 = res2.getDocuments(); for (Document document : docs2) { System.out.println(document.getId() + " : price " + document.getString("price")); } // >>> bicycle:2 : price 815 // >>> bicycle:5 : price 810 // >>> bicycle:9 : price 815 // STEP_END // Tests for 'range2' step. // REMOVE_START assertEquals(3, res2.getTotalResults()); assertArrayEquals( Stream.of("bicycle:5", "bicycle:9", "bicycle:2").sorted().toArray(), docs2.stream().map(Document::getId).sorted().toArray() ); // REMOVE_END // STEP_START range3 SearchResult res3 = jedis.ftSearch("idx:bicycle", "*", FTSearchParams.searchParams() .returnFields("price") .filter("price", 1000, true, Double.POSITIVE_INFINITY, false) ); System.out.println(res3.getTotalResults()); // >>> 5 List docs3 = res3.getDocuments(); for (Document document : docs3) { System.out.println(document.getId() + " : price " + document.getString("price")); } // >>> bicycle:1 : price 1200 // >>> bicycle:4 : price 3200 // >>> bicycle:6 : price 2300 // >>> bicycle:3 : price 3400 // >>> bicycle:8 : price 1200 // STEP_END // Tests for 'range3' step. // REMOVE_START assertEquals(5, res3.getTotalResults()); assertArrayEquals( Stream.of("bicycle:1", "bicycle:4", "bicycle:6", "bicycle:3", "bicycle:8") .sorted().toArray(), docs3.stream().map(Document::getId).sorted().toArray()); // REMOVE_END // STEP_START range4 SearchResult res4 = jedis.ftSearch("idx:bicycle", "@price:[-inf 2000]", FTSearchParams.searchParams() .returnFields("price") .sortBy("price", SortingOrder.ASC) .limit(0, 5) ); System.out.println(res4.getTotalResults()); // >>> 7 List docs4 = res4.getDocuments(); for (Document document : docs4) { System.out.println(document.getId() + " : price " + document.getString("price")); } // >>> bicycle:0 : price 270 // >>> bicycle:7 : price 430 // >>> bicycle:5 : price 810 // >>> bicycle:2 : price 815 // >>> bicycle:9 : price 815 // STEP_END // Tests for 'range4' step. // REMOVE_START assertEquals(7, res4.getTotalResults()); assertArrayEquals( new String[] {"bicycle:0", "bicycle:2", "bicycle:5", "bicycle:7", "bicycle:9"}, docs4.stream().map(Document::getId).sorted().toArray() ); // REMOVE_END // HIDE_START jedis.close(); } } // HIDE_END ================================================ FILE: src/test/java/io/redis/examples/SearchQuickstartExample.java ================================================ // EXAMPLE: search_quickstart package io.redis.examples; import java.math.BigDecimal; import java.util.*; import redis.clients.jedis.*; import redis.clients.jedis.exceptions.*; import redis.clients.jedis.search.*; import redis.clients.jedis.search.aggr.*; import redis.clients.jedis.search.schemafields.*; // REMOVE_START import org.junit.jupiter.api.Test; import static org.junit.jupiter.api.Assertions.assertEquals; // REMOVE_END class Bicycle { public String brand; public String model; public BigDecimal price; public String description; public String condition; public Bicycle(String brand, String model, BigDecimal price, String condition, String description) { this.brand = brand; this.model = model; this.price = price; this.condition = condition; this.description = description; } } public class SearchQuickstartExample { @Test public void run() { // STEP_START connect RedisClient jedis = RedisClient.create("localhost", 6379); // STEP_END // REMOVE_START try { jedis.ftDropIndex("idx:bicycle"); } catch (JedisDataException e) { System.out.println("Can't connect to Redis: " + e.getMessage()); } // REMOVE_END // STEP_START create_index SchemaField[] schema = { TextField.of("$.brand").as("brand"), TextField.of("$.model").as("model"), TextField.of("$.description").as("description"), NumericField.of("$.price").as("price"), TagField.of("$.condition").as("condition") }; jedis.ftCreate("idx:bicycle", FTCreateParams.createParams() .on(IndexDataType.JSON) .addPrefix("bicycle:"), schema ); // STEP_END Bicycle[] bicycles = { new Bicycle( "Velorim", "Jigger", new BigDecimal(270), "new", "Small and powerful, the Jigger is the best ride " + "for the smallest of tikes! This is the tiniest " + "kids’ pedal bike on the market available without" + " a coaster brake, the Jigger is the vehicle of " + "choice for the rare tenacious little rider " + "raring to go." ), new Bicycle( "Bicyk", "Hillcraft", new BigDecimal(1200), "used", "Kids want to ride with as little weight as possible." + " Especially on an incline! They may be at the age " + "when a 27.5 inch wheel bike is just too clumsy coming " + "off a 24 inch bike. The Hillcraft 26 is just the solution" + " they need!" ), new Bicycle( "Nord", "Chook air 5", new BigDecimal(815), "used", "The Chook Air 5 gives kids aged six years and older " + "a durable and uberlight mountain bike for their first" + " experience on tracks and easy cruising through forests" + " and fields. The lower top tube makes it easy to mount" + " and dismount in any situation, giving your kids greater" + " safety on the trails." ), new Bicycle( "Eva", "Eva 291", new BigDecimal(3400), "used", "The sister company to Nord, Eva launched in 2005 as the" + " first and only women-dedicated bicycle brand. Designed" + " by women for women, allEva bikes are optimized for the" + " feminine physique using analytics from a body metrics" + " database. If you like 29ers, try the Eva 291. It's a " + "brand new bike for 2022.. This full-suspension, " + "cross-country ride has been designed for velocity. The" + " 291 has 100mm of front and rear travel, a superlight " + "aluminum frame and fast-rolling 29-inch wheels. Yippee!" ), new Bicycle( "Noka Bikes", "Kahuna", new BigDecimal(3200), "used", "Whether you want to try your hand at XC racing or are " + "looking for a lively trail bike that's just as inspiring" + " on the climbs as it is over rougher ground, the Wilder" + " is one heck of a bike built specifically for short women." + " Both the frames and components have been tweaked to " + "include a women’s saddle, different bars and unique " + "colourway." ), new Bicycle( "Breakout", "XBN 2.1 Alloy", new BigDecimal(810), "new", "The XBN 2.1 Alloy is our entry-level road bike – but that’s" + " not to say that it’s a basic machine. With an internal " + "weld aluminium frame, a full carbon fork, and the slick-shifting" + " Claris gears from Shimano’s, this is a bike which doesn’t" + " break the bank and delivers craved performance." ), new Bicycle( "ScramBikes", "WattBike", new BigDecimal(2300), "new", "The WattBike is the best e-bike for people who still feel young" + " at heart. It has a Bafang 1000W mid-drive system and a 48V" + " 17.5AH Samsung Lithium-Ion battery, allowing you to ride for" + " more than 60 miles on one charge. It’s great for tackling hilly" + " terrain or if you just fancy a more leisurely ride. With three" + " working modes, you can choose between E-bike, assisted bicycle," + " and normal bike modes." ), new Bicycle( "Peaknetic", "Secto", new BigDecimal(430), "new", "If you struggle with stiff fingers or a kinked neck or back after" + " a few minutes on the road, this lightweight, aluminum bike" + " alleviates those issues and allows you to enjoy the ride. From" + " the ergonomic grips to the lumbar-supporting seat position, the" + " Roll Low-Entry offers incredible comfort. The rear-inclined seat" + " tube facilitates stability by allowing you to put a foot on the" + " ground to balance at a stop, and the low step-over frame makes it" + " accessible for all ability and mobility levels. The saddle is" + " very soft, with a wide back to support your hip joints and a" + " cutout in the center to redistribute that pressure. Rim brakes" + " deliver satisfactory braking control, and the wide tires provide" + " a smooth, stable ride on paved roads and gravel. Rack and fender" + " mounts facilitate setting up the Roll Low-Entry as your preferred" + " commuter, and the BMX-like handlebar offers space for mounting a" + " flashlight, bell, or phone holder." ), new Bicycle( "nHill", "Summit", new BigDecimal(1200), "new", "This budget mountain bike from nHill performs well both on bike" + " paths and on the trail. The fork with 100mm of travel absorbs" + " rough terrain. Fat Kenda Booster tires give you grip in corners" + " and on wet trails. The Shimano Tourney drivetrain offered enough" + " gears for finding a comfortable pace to ride uphill, and the" + " Tektro hydraulic disc brakes break smoothly. Whether you want an" + " affordable bike that you can take to work, but also take trail in" + " mountains on the weekends or you’re just after a stable," + " comfortable ride for the bike path, the Summit gives a good value" + " for money." ), new Bicycle( "ThrillCycle", "BikeShind", new BigDecimal(815), "refurbished", "An artsy, retro-inspired bicycle that’s as functional as it is" + " pretty: The ThrillCycle steel frame offers a smooth ride. A" + " 9-speed drivetrain has enough gears for coasting in the city, but" + " we wouldn’t suggest taking it to the mountains. Fenders protect" + " you from mud, and a rear basket lets you transport groceries," + " flowers and books. The ThrillCycle comes with a limited lifetime" + " warranty, so this little guy will last you long past graduation." ), }; // STEP_START add_documents for (int i = 0; i < bicycles.length; i++) { jedis.jsonSetWithEscape(String.format("bicycle:%d", i), bicycles[i]); } // STEP_END // STEP_START wildcard_query Query query1 = new Query("*"); List result1 = jedis.ftSearch("idx:bicycle", query1).getDocuments(); System.out.println("Documents found:" + result1.size()); // Prints: Documents found: 10 // STEP_END // REMOVE_START assertEquals(10, result1.size(), "Validate total results"); // REMOVE_END // STEP_START query_single_term Query query2 = new Query("@model:Jigger"); List result2 = jedis.ftSearch("idx:bicycle", query2).getDocuments(); System.out.println(result2); // Prints: [id:bicycle:0, score: 1.0, payload:null, // properties:[$={"brand":"Velorim","model":"Jigger","price":270,"description":"Small and powerful, the Jigger is the best ride for the smallest of tikes! This is the tiniest kids’ pedal bike on the market available without a coaster brake, the Jigger is the vehicle of choice for the rare tenacious little rider raring to go.","condition":"new"}]] // STEP_END // REMOVE_START assertEquals("bicycle:0", result2.get(0).getId(), "Validate bike id"); // REMOVE_END // STEP_START query_single_term_limit_fields Query query3 = new Query("@model:Jigger").returnFields("price"); List result3 = jedis.ftSearch("idx:bicycle", query3).getDocuments(); System.out.println(result3); // Prints: [id:bicycle:0, score: 1.0, payload:null, properties:[price=270]] // STEP_END // REMOVE_START assertEquals("bicycle:0", result3.get(0).getId(),"Validate cargo bike id"); // REMOVE_END // STEP_START query_single_term_and_num_range Query query4 = new Query("basic @price:[500 1000]"); List result4 = jedis.ftSearch("idx:bicycle", query4).getDocuments(); System.out.println(result4); // Prints: [id:bicycle:5, score: 1.0, payload:null, // properties:[$={"brand":"Breakout","model":"XBN 2.1 Alloy","price":810,"description":"The XBN 2.1 Alloy is our entry-level road bike – but that’s not to say that it’s a basic machine. With an internal weld aluminium frame, a full carbon fork, and the slick-shifting Claris gears from Shimano’s, this is a bike which doesn’t break the bank and delivers craved performance.","condition":"new"}]] // STEP_END // REMOVE_START assertEquals("bicycle:5", result4.get(0).getId(), "Validate bike id"); // REMOVE_END // STEP_START query_exact_matching Query query5 = new Query("@brand:\"Noka Bikes\""); List result5 = jedis.ftSearch("idx:bicycle", query5).getDocuments(); System.out.println(result5); // Prints: [id:bicycle:4, score: 1.0, payload:null, // properties:[$={"brand":"Noka Bikes","model":"Kahuna","price":3200,"description":"Whether you want to try your hand at XC racing or are looking for a lively trail bike that's just as inspiring on the climbs as it is over rougher ground, the Wilder is one heck of a bike built specifically for short women. Both the frames and components have been tweaked to include a women’s saddle, different bars and unique colourway.","condition":"used"}]] // STEP_END // REMOVE_START assertEquals("bicycle:4", result5.get(0).getId(), "Validate bike id"); // REMOVE_END // STEP_START simple_aggregation AggregationBuilder ab = new AggregationBuilder("*").groupBy("@condition", Reducers.count().as("count")); AggregationResult ar = jedis.ftAggregate("idx:bicycle", ab); for (int i = 0; i < ar.getTotalResults(); i++) { System.out.println(ar.getRow(i).getString("condition") + " - " + ar.getRow(i).getString("count")); } // Prints: // refurbished - 1 // used - 5 // new - 4 assertEquals(3, ar.getTotalResults(), "Validate aggregation results"); // STEP_END jedis.close(); } } ================================================ FILE: src/test/java/io/redis/examples/SetGetExample.java ================================================ // EXAMPLE: set_and_get // HIDE_START package io.redis.examples; import redis.clients.jedis.RedisClient; // REMOVE_START import org.junit.jupiter.api.Test; import static org.junit.jupiter.api.Assertions.assertEquals; // REMOVE_END public class SetGetExample { @Test public void run() { RedisClient jedis = RedisClient.create("redis://localhost:6379"); // HIDE_END String status = jedis.set("bike:1", "Process 134"); if ("OK".equals(status)) System.out.println("Successfully added a bike."); String value = jedis.get("bike:1"); if (value != null) System.out.println("The name of the bike is: " + value + "."); // REMOVE_START assertEquals("OK", status); assertEquals("Process 134", value); // REMOVE_END // HIDE_START jedis.close(); } } // HIDE_END ================================================ FILE: src/test/java/io/redis/examples/SetsExample.java ================================================ //EXAMPLE: sets_tutorial //HIDE_START package io.redis.examples; import redis.clients.jedis.RedisClient; import org.junit.jupiter.api.Test; import java.util.List; import java.util.Set; import static org.junit.jupiter.api.Assertions.assertTrue; import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertEquals; public class SetsExample { @Test public void run() { RedisClient jedis = RedisClient.create("redis://localhost:6379"); // HIDE_END // REMOVE_START jedis.del("bikes:racing:france"); jedis.del("bikes:racing:usa"); // REMOVE_END // STEP_START sadd long res1 = jedis.sadd("bikes:racing:france", "bike:1"); System.out.println(res1); // >>> 1 long res2 = jedis.sadd("bikes:racing:france", "bike:1"); System.out.println(res2); // >>> 0 long res3 = jedis.sadd("bikes:racing:france", "bike:2", "bike:3"); System.out.println(res3); // >>> 2 long res4 = jedis.sadd("bikes:racing:usa", "bike:1", "bike:4"); System.out.println(res4); // >>> 2 // STEP_END // REMOVE_START assertEquals(1,res1); assertEquals(0,res2); assertEquals(2,res3); assertEquals(2,res4); // REMOVE_END // STEP_START sismember // HIDE_START jedis.sadd("bikes:racing:france", "bike:1", "bike:2", "bike:3"); jedis.sadd("bikes:racing:usa", "bike:1", "bike:4"); // HIDE_END boolean res5 = jedis.sismember("bikes:racing:usa", "bike:1"); System.out.println(res5); // >>> true boolean res6 = jedis.sismember("bikes:racing:usa", "bike:2"); System.out.println(res6); // >>> false // STEP_END // REMOVE_START assertTrue(res5); assertFalse(res6); // REMOVE_END // STEP_START sinter // HIDE_START jedis.sadd("bikes:racing:france", "bike:1", "bike:2", "bike:3"); jedis.sadd("bikes:racing:usa", "bike:1", "bike:4"); // HIDE_END Set res7 = jedis.sinter("bikes:racing:france", "bikes:racing:usa"); System.out.println(res7); // >>> [bike:1] // STEP_END // REMOVE_START assertEquals("[bike:1]",res7.toString()); // REMOVE_END // STEP_START scard jedis.sadd("bikes:racing:france", "bike:1", "bike:2", "bike:3"); long res8 = jedis.scard("bikes:racing:france"); System.out.println(res8); // >>> 3 // STEP_END // REMOVE_START assertEquals(3,res8); jedis.del("bikes:racing:france"); // REMOVE_END // STEP_START sadd_smembers long res9 = jedis.sadd("bikes:racing:france", "bike:1", "bike:2", "bike:3"); System.out.println(res9); // >>> 3 Set res10 = jedis.smembers("bikes:racing:france"); System.out.println(res10); // >>> [bike:1, bike:2, bike:3] // STEP_END // REMOVE_START assertEquals(3,res9); assertEquals("[bike:1, bike:2, bike:3]",res10.toString()); // REMOVE_END // STEP_START smismember boolean res11 = jedis.sismember("bikes:racing:france", "bike:1"); System.out.println(res11); // >>> true List res12 = jedis.smismember("bikes:racing:france", "bike:2", "bike:3", "bike:4"); System.out.println(res12); // >>> [true,true,false] // STEP_END // REMOVE_START assertTrue(res11); assertEquals("[true, true, false]",res12.toString()); // REMOVE_END // STEP_START sdiff jedis.sadd("bikes:racing:france", "bike:1", "bike:2", "bike:3"); jedis.sadd("bikes:racing:usa", "bike:1", "bike:4"); Set res13 = jedis.sdiff("bikes:racing:france", "bikes:racing:usa"); System.out.println(res13); // >>> [bike:2, bike:3] // REMOVE_START assertEquals("[bike:2, bike:3]",res13.toString()); jedis.del("bikes:racing:france"); jedis.del("bikes:racing:usa"); // REMOVE_END // STEP_END // STEP_START multisets jedis.sadd("bikes:racing:france", "bike:1", "bike:2", "bike:3"); jedis.sadd("bikes:racing:usa", "bike:1", "bike:4"); jedis.sadd("bikes:racing:italy", "bike:1", "bike:2", "bike:3", "bike:4"); Set res14 = jedis.sinter("bikes:racing:france", "bikes:racing:usa", "bikes:racing:italy"); System.out.println(res14); // >>> [bike:1] Set res15 = jedis.sunion("bikes:racing:france", "bikes:racing:usa", "bikes:racing:italy"); System.out.println(res15); // >>> [bike:1, bike:2, bike:3, bike:4] Set res16 = jedis.sdiff("bikes:racing:france", "bikes:racing:usa", "bikes:racing:italy"); System.out.println(res16); // >>> [] Set res17 = jedis.sdiff("bikes:racing:usa", "bikes:racing:france"); System.out.println(res17); // >>> [bike:4] Set res18 = jedis.sdiff("bikes:racing:france", "bikes:racing:usa"); System.out.println(res18); // >>> [bike:2, bike:3] // REMOVE_START assertEquals("[bike:1]",res14.toString()); assertEquals("[bike:1, bike:2, bike:3, bike:4]",res15.toString()); assertEquals("[]",res16.toString()); assertEquals("[bike:4]",res17.toString()); assertEquals("[bike:2, bike:3]",res18.toString()); jedis.del("bikes:racing:france"); jedis.del("bikes:racing:usa"); jedis.del("bikes:racing:italy"); // REMOVE_END // STEP_END // STEP_START srem jedis.sadd("bikes:racing:france", "bike:1", "bike:2", "bike:3", "bike:4", "bike:5"); long res19 = jedis.srem("bikes:racing:france", "bike:1"); System.out.println(res18); // >>> 1 String res20 = jedis.spop("bikes:racing:france"); System.out.println(res20); // >>> bike:3 Set res21 = jedis.smembers("bikes:racing:france"); System.out.println(res21); // >>> [bike:2, bike:4, bike:5] String res22 = jedis.srandmember("bikes:racing:france"); System.out.println(res22); // >>> bike:4 // STEP_END // REMOVE_START assertEquals(1,res19); // REMOVE_END // HIDE_START jedis.close(); // HIDE_END } } ================================================ FILE: src/test/java/io/redis/examples/SortedSetsExample.java ================================================ //EXAMPLE: ss_tutorial //HIDE_START package io.redis.examples; import redis.clients.jedis.RedisClient; import redis.clients.jedis.resps.Tuple; //HIDE_END //REMOVE_START import org.junit.jupiter.api.Test; import java.util.HashMap; import java.util.List; import static org.junit.jupiter.api.Assertions.assertEquals; //REMOVE_END public class SortedSetsExample { @Test public void run() { //HIDE_START RedisClient jedis = RedisClient.create("redis://localhost:6379"); //HIDE_END //REMOVE_START jedis.del("racer_scores"); //REMOVE_END //STEP_START zadd long res1 = jedis.zadd("racer_scores", 10d, "Norem"); System.out.println(res1); // >>> 1 long res2 = jedis.zadd("racer_scores", 12d, "Castilla"); System.out.println(res2); // >>> 1 long res3 = jedis.zadd("racer_scores", new HashMap() {{ put("Sam-Bodden", 8d); put("Royce", 10d); put("Ford", 6d); put("Prickett", 14d); put("Castilla", 12d); }}); System.out.println(res3); // >>> 4 //STEP_END //STEP_START zrange List res4 = jedis.zrange("racer_scores", 0, -1); System.out.println(res4); // >>> [Ford, Sam-Bodden, Norem, Royce, Castil, Castilla, Prickett] List res5 = jedis.zrevrange("racer_scores", 0, -1); System.out.println(res5); // >>> [Prickett, Castilla, Castil, Royce, Norem, Sam-Bodden, Ford] //STEP_END //STEP_START zrange_withscores List res6 = jedis.zrangeWithScores("racer_scores", 0, -1); System.out.println(res6); // >>> [[Ford,6.0], [Sam-Bodden,8.0], [Norem,10.0], [Royce,10.0], [Castil,12.0], [Castilla,12.0], [Prickett,14.0]] //STEP_END //STEP_START zrangebyscore List res7 = jedis.zrangeByScore("racer_scores", Double.MIN_VALUE, 10d); System.out.println(res7); // >>> [Ford, Sam-Bodden, Norem, Royce] //STEP_END //STEP_START zremrangebyscore long res8 = jedis.zrem("racer_scores", "Castilla"); System.out.println(res8); // >>> 1 long res9 = jedis.zremrangeByScore("racer_scores", Double.MIN_VALUE, 9d); System.out.println(res9); // >>> 2 List res10 = jedis.zrange("racer_scores", 0, -1); System.out.println(res10); // >>> [Norem, Royce, Prickett] //STEP_END //REMOVE_START assertEquals(3, jedis.zcard("racer_scores")); //REMOVE_END //STEP_START zrank long res11 = jedis.zrank("racer_scores", "Norem"); System.out.println(res11); // >>> 0 long res12 = jedis.zrevrank("racer_scores", "Norem"); System.out.println(res12); // >>> 2 //STEP_END //STEP_START zadd_lex long res13 = jedis.zadd("racer_scores", new HashMap() {{ put("Norem", 0d); put("Sam-Bodden", 0d); put("Royce", 0d); put("Ford", 0d); put("Prickett", 0d); put("Castilla", 0d); }}); System.out.println(res13); // >>> 3 List res14 = jedis.zrange("racer_scores", 0, -1); System.out.println(res14); // >>> [Castilla, Ford, Norem, Prickett, Royce, Sam-Bodden] List res15 = jedis.zrangeByLex("racer_scores", "[A", "[L"); System.out.println(res15); // >>> [Castilla, Ford] //STEP_END //STEP_START leaderboard long res16 = jedis.zadd("racer_scores", 100d, "Wood"); System.out.println(res16); // >>> 1 long res17 = jedis.zadd("racer_scores", 100d, "Henshaw"); System.out.println(res17); // >>> 1 long res18 = jedis.zadd("racer_scores", 100d, "Henshaw"); System.out.println(res18); // >>> 0 double res19 = jedis.zincrby("racer_scores", 50d, "Wood"); System.out.println(res19); // >>> 150.0 double res20 = jedis.zincrby("racer_scores", 50d, "Henshaw"); System.out.println(res20); // >>> 200.0 //STEP_END //HIDE_START jedis.close(); //HIDE_END } } ================================================ FILE: src/test/java/io/redis/examples/StreamsExample.java ================================================ //EXAMPLE: stream_tutorial //HIDE_START package io.redis.examples; import redis.clients.jedis.StreamEntryID; import redis.clients.jedis.RedisClient; //HIDE_END //REMOVE_START import org.junit.jupiter.api.Test; import redis.clients.jedis.exceptions.JedisDataException; import redis.clients.jedis.params.*; import redis.clients.jedis.resps.*; import java.util.*; import static org.junit.jupiter.api.Assertions.assertEquals; //REMOVE_END public class StreamsExample { @Test public void run() { //HIDE_START RedisClient jedis = RedisClient.create("redis://localhost:6379"); //HIDE_END //REMOVE_START jedis.del("race:france", "race:italy", "race:usa"); //REMOVE_END // STEP_START xadd StreamEntryID res1 = jedis.xadd("race:france",new HashMap(){{put("rider","Castilla");put("speed","30.2");put("position","1");put("location_id","1");}} , XAddParams.xAddParams()); System.out.println(res1); // >>> 1701760582225-0 StreamEntryID res2 = jedis.xadd("race:france",new HashMap(){{put("rider","Norem");put("speed","28.8");put("position","3");put("location_id","1");}} , XAddParams.xAddParams()); System.out.println(res2); // >>> 1701760582225-1 StreamEntryID res3 = jedis.xadd("race:france",new HashMap(){{put("rider","Prickett");put("speed","29.7");put("position","2");put("location_id","1");}} , XAddParams.xAddParams()); System.out.println(res3); // >>> 1701760582226-0 //STEP_END //REMOVE_START assertEquals(jedis.xlen("race:france"),3); //REMOVE_END //STEP_START xrange List res4 = jedis.xrange("race:france","1701760582225-0","+",2); System.out.println(res4); // >>> [1701760841292-0 {rider=Castilla, speed=30.2, location_id=1, position=1}, 1701760841292-1 {rider=Norem, speed=28.8, location_id=1, position=3}] //STEP_END //STEP_START xread_block List>> res5= jedis.xread(XReadParams.xReadParams().block(300).count(100),new HashMap(){{put("race:france",new StreamEntryID());}}); System.out.println( res5 ); // >>> [race:france=[1701761996660-0 {rider=Castilla, speed=30.2, location_id=1, position=1}, 1701761996661-0 {rider=Norem, speed=28.8, location_id=1, position=3}, 1701761996661-1 {rider=Prickett, speed=29.7, location_id=1, position=2}]] //STEP_END //STEP_START xadd_2 StreamEntryID res6 = jedis.xadd("race:france",new HashMap(){{put("rider","Castilla");put("speed","29.9");put("position","2");put("location_id","1");}} , XAddParams.xAddParams()); System.out.println(res6); // >>> 1701762285679-0 //STEP_END //STEP_START xlen long res7 = jedis.xlen("race:france"); System.out.println(res7); // >>> 4 //STEP_END //STEP_START xadd_id StreamEntryID res8 = jedis.xadd("race:usa", new HashMap(){{put("racer","Castilla");}},XAddParams.xAddParams().id("0-1")); System.out.println(res8); // >>> 0-1 StreamEntryID res9 = jedis.xadd("race:usa", new HashMap(){{put("racer","Norem");}},XAddParams.xAddParams().id("0-2")); System.out.println(res9); // >>> 0-2 //STEP_END //STEP_START xadd_bad_id try { StreamEntryID res10 = jedis.xadd("race:usa", new HashMap(){{put("racer","Prickett");}},XAddParams.xAddParams().id("0-1")); System.out.println(res10); // >>> 0-1 } catch (JedisDataException e){ System.out.println(e); // >>> ERR The ID specified in XADD is equal or smaller than the target stream top item } //STEP_END //STEP_START xadd_7 StreamEntryID res11 = jedis.xadd("race:usa", new HashMap(){{put("racer","Norem");}},XAddParams.xAddParams().id("0-*")); System.out.println(res11); //STEP_END //STEP_START xrange_all List res12 = jedis.xrange("race:france","-","+"); System.out.println( res12 ); // >>> [1701764734160-0 {rider=Castilla, speed=30.2, location_id=1, position=1}, 1701764734160-1 {rider=Norem, speed=28.8, location_id=1, position=3}, 1701764734161-0 {rider=Prickett, speed=29.7, location_id=1, position=2}, 1701764734162-0 {rider=Castilla, speed=29.9, location_id=1, position=2}] //STEP_END //STEP_START xrange_time List res13 = jedis.xrange("race:france",String.valueOf(System.currentTimeMillis()-1000),String.valueOf(System.currentTimeMillis()+1000)); System.out.println( res13 ); // >>> [1701764734160-0 {rider=Castilla, speed=30.2, location_id=1, position=1}, 1701764734160-1 {rider=Norem, speed=28.8, location_id=1, position=3}, 1701764734161-0 {rider=Prickett, speed=29.7, location_id=1, position=2}, 1701764734162-0 {rider=Castilla, speed=29.9, location_id=1, position=2}] //STEP_END //STEP_START xrange_step_1 List res14 = jedis.xrange("race:france","-","+",2); System.out.println(res14); // >>> [1701764887638-0 {rider=Castilla, speed=30.2, location_id=1, position=1}, 1701764887638-1 {rider=Norem, speed=28.8, location_id=1, position=3}] //STEP_END //STEP_START xrange_step_2 List res15 = jedis.xrange("race:france",String.valueOf(System.currentTimeMillis()-1000)+"-0","+",2); System.out.println(res15); // >>> [1701764887638-0 {rider=Castilla, speed=30.2, location_id=1, position=1}, 1701764887638-1 {rider=Norem, speed=28.8, location_id=1, position=3}] //STEP_END //STEP_START xrange_empty List res16 = jedis.xrange("race:france",String.valueOf(System.currentTimeMillis()+1000)+"-0","+",2); System.out.println(res16); // >>> [] // STEP_END //STEP_START xrevrange List res17 = jedis.xrevrange("race:france","+","-",1); System.out.println(res17); // >>> [1701765218592-0 {rider=Castilla, speed=29.9, location_id=1, position=2}] //STEP_END //STEP_START xread List>> res18= jedis.xread(XReadParams.xReadParams().count(2),new HashMap(){{put("race:france",new StreamEntryID());}}); System.out.println( res18 ); // >>> [race:france=[1701765384638-0 {rider=Castilla, speed=30.2, location_id=1, position=1}, 1701765384638-1 {rider=Norem, speed=28.8, location_id=1, position=3}]] //STEP_END //STEP_START xgroup_create String res19 = jedis.xgroupCreate("race:france","france_riders",StreamEntryID.LAST_ENTRY,false); System.out.println(res19); // >>> OK //STEP_END //STEP_START xgroup_create_mkstream String res20 = jedis.xgroupCreate("race:italy","italy_riders",StreamEntryID.LAST_ENTRY,true); System.out.println(res20); // >>> OK //STEP_END //STEP_START xgroup_read StreamEntryID id1 = jedis.xadd("race:italy", new HashMap(){{put("rider","Castilaa");}},XAddParams.xAddParams()); StreamEntryID id2 = jedis.xadd("race:italy", new HashMap(){{put("rider","Royce");}},XAddParams.xAddParams()); StreamEntryID id3 = jedis.xadd("race:italy", new HashMap(){{put("rider","Sam-Bodden");}},XAddParams.xAddParams()); StreamEntryID id4 = jedis.xadd("race:italy", new HashMap(){{put("rider","Prickett");}},XAddParams.xAddParams()); StreamEntryID id5 = jedis.xadd("race:italy", new HashMap(){{put("rider","Norem");}},XAddParams.xAddParams()); List>> res21 = jedis.xreadGroup("italy_riders","Alice", XReadGroupParams.xReadGroupParams().count(1),new HashMap(){{put("race:italy",StreamEntryID.UNRECEIVED_ENTRY);}}); System.out.println(res21); // >>> [race:italy=[1701766299006-0 {rider=Castilaa}]] //STEP_END //STEP_START xgroup_read_id List>> res22 = jedis.xreadGroup("italy_riders","Alice", XReadGroupParams.xReadGroupParams().count(1),new HashMap(){{put("race:italy",new StreamEntryID());}}); System.out.println(res22); // >>> [race:italy=[1701766299006-0 {rider=Castilaa}]] //STEP_END //STEP_START xack long res23 = jedis.xack("race:italy","italy_riders",id1); System.out.println(res23); // >>> 1 List>> res24 = jedis.xreadGroup("italy_riders","Alice", XReadGroupParams.xReadGroupParams().count(1),new HashMap(){{put("race:italy",new StreamEntryID());}}); System.out.println(res24); // >>> [race:italy=[]] //STEP_END //STEP_START xgroup_read_bob List>> res25 = jedis.xreadGroup("italy_riders","Bob", XReadGroupParams.xReadGroupParams().count(2),new HashMap(){{put("race:italy",StreamEntryID.UNRECEIVED_ENTRY);}}); System.out.println(res25); // >>> [race:italy=[1701767632261-1 {rider=Royce}, 1701767632262-0 {rider=Sam-Bodden}]] //STEP_END //STEP_START xpending StreamPendingSummary res26 = jedis.xpending("race:italy","italy_riders"); System.out.println(res26.getConsumerMessageCount()); // >>> {Bob=2} //STEP_END //STEP_START xpending_plus_minus List res27 = jedis.xpending("race:italy","italy_riders",XPendingParams.xPendingParams().start(StreamEntryID.MINIMUM_ID).end(StreamEntryID.MAXIMUM_ID).count(10)); System.out.println(res27); // >>> [1701768567412-1 Bob idle:0 times:1, 1701768567412-2 Bob idle:0 times:1] //STEP_END //STEP_START xrange_pending List res28 = jedis.xrange("race:italy",id2.toString(),id2.toString()); System.out.println(res28); // >>> [1701768744819-1 {rider=Royce}] //STEP_END //STEP_START xclaim List res29 = jedis.xclaim("race:italy","italy_riders","Alice", 0L, XClaimParams.xClaimParams().time(60000),id2); System.out.println(res29); // >>> [1701769004195-1 {rider=Royce}] //STEP_END //STEP_START xautoclaim Map.Entry> res30 = jedis.xautoclaim("race:italy","italy_riders","Alice",1L,new StreamEntryID("0-0"),XAutoClaimParams.xAutoClaimParams().count(1)); System.out.println(res30); // >>> [1701769266831-2=[1701769266831-1 {rider=Royce}] //STEP_END //STEP_START xautoclaim_cursor Map.Entry> res31 = jedis.xautoclaim("race:italy","italy_riders","Alice",1L,new StreamEntryID(id2.toString()),XAutoClaimParams.xAutoClaimParams().count(1)); System.out.println(res31); // >>> [0-0=[1701769605847-2 {rider=Sam-Bodden}] //STEP_END //STEP_START xinfo StreamInfo res32 = jedis.xinfoStream("race:italy"); System.out.println( res32.getStreamInfo() ); // >>> {radix-tree-keys=1, radix-tree-nodes=2, entries-added=5, length=5, groups=1, max-deleted-entry-id=0-0, first-entry=1701769637612-0 {rider=Castilaa}, last-generated-id=1701769637612-4, last-entry=1701769637612-4 {rider=Norem}, recorded-first-entry-id=1701769637612-0} //STEP_END //STEP_START xinfo_groups List res33 = jedis.xinfoGroups("race:italy"); for (StreamGroupInfo a : res33){ System.out.println( a.getGroupInfo() ); // >>> {last-delivered-id=1701770253659-0, lag=2, pending=2, name=italy_riders, consumers=2, entries-read=3} } //STEP_END //STEP_START xinfo_consumers List res34 = jedis.xinfoConsumers("race:italy","italy_riders"); for (StreamConsumerInfo a : res34){ System.out.println( a.getConsumerInfo() ); // {inactive=1, idle=1, pending=1, name=Alice} , {inactive=3, idle=3, pending=1, name=Bob} } //STEP_END //STEP_START maxlen jedis.xadd("race:italy", new HashMap(){{put("rider","Jones");}},XAddParams.xAddParams().maxLen(10)); jedis.xadd("race:italy", new HashMap(){{put("rider","Wood");}},XAddParams.xAddParams().maxLen(10)); jedis.xadd("race:italy", new HashMap(){{put("rider","Henshaw");}},XAddParams.xAddParams().maxLen(10)); long res35 = jedis.xlen("race:italy"); System.out.println(res35); // >>> 8 List res36 = jedis.xrange("race:italy","-","+"); System.out.println(res36); // >>> [1701771219852-0 {rider=Castilaa}, 1701771219852-1 {rider=Royce}, 1701771219853-0 {rider=Sam-Bodden}, 1701771219853-1 {rider=Prickett}, 1701771219853-2 {rider=Norem}, 1701771219858-0 {rider=Jones}, 1701771219858-1 {rider=Wood}, 1701771219859-0 {rider=Henshaw}] StreamEntryID id6 = jedis.xadd("race:italy", new HashMap(){{put("rider","Smith");}},XAddParams.xAddParams().maxLen(2)); List res37 = jedis.xrange("race:italy","-","+"); System.out.println(res37); // >>> [1701771067332-1 {rider=Henshaw}, 1701771067332-2 {rider=Smith}] //STEP_END //STEP_START xtrim long res38 = jedis.xtrim("race:italy",XTrimParams.xTrimParams().maxLen(10).exactTrimming()); System.out.println(res38); /// >>> 0 //STEP_END //STEP_START xtrim2 long res39 = jedis.xtrim("race:italy",XTrimParams.xTrimParams().maxLen(10)); System.out.println(res39); /// >>> 0 //STEP_END //STEP_START xdel List res40 = jedis.xrange("race:italy","-","+"); System.out.println(res40); // >>> [1701771356428-2 {rider=Henshaw}, 1701771356429-0 {rider=Smith}] long res41 = jedis.xdel("race:italy",id6); System.out.println(res41); // >>> 1 List res42 = jedis.xrange("race:italy","-","+"); System.out.println(res42); // >>> [1701771517639-1 {rider=Henshaw}] //STEP_END //HIDE_START jedis.close(); //HIDE_END } } ================================================ FILE: src/test/java/io/redis/examples/StringExample.java ================================================ // EXAMPLE: set_tutorial package io.redis.examples; //REMOVE_START import static org.junit.jupiter.api.Assertions.*; import org.junit.jupiter.api.Test; //REMOVE_END import redis.clients.jedis.RedisClient; import redis.clients.jedis.params.SetParams; import java.util.ArrayList; import java.util.Arrays; import java.util.List; public class StringExample { @Test public void run() { try (RedisClient jedis = RedisClient.create("redis://localhost:6379")) { // STEP_START set_get String res1 = jedis.set("bike:1", "Deimos"); System.out.println(res1); // OK String res2 = jedis.get("bike:1"); System.out.println(res2); // Deimos // STEP_END // REMOVE_START assertEquals("OK", res1); assertEquals("Deimos", res2); // REMOVE_END // STEP_START setnx_xx Long res3 = jedis.setnx("bike:1", "bike"); System.out.println(res3); // 0 (because key already exists) System.out.println(jedis.get("bike:1")); // Deimos (value is unchanged) String res4 = jedis.set("bike:1", "bike", SetParams.setParams().xx()); // set the value to "bike" if it // already // exists System.out.println(res4); // OK // STEP_END // REMOVE_START assertEquals(0L, res3.longValue()); assertEquals("OK", res4); // REMOVE_END // STEP_START mset String res5 = jedis.mset("bike:1", "Deimos", "bike:2", "Ares", "bike:3", "Vanth"); System.out.println(res5); // OK List res6 = jedis.mget("bike:1", "bike:2", "bike:3"); System.out.println(res6); // [Deimos, Ares, Vanth] // STEP_END // REMOVE_START assertEquals("OK", res5); List expected = new ArrayList<>(Arrays.asList("Deimos", "Ares", "Vanth")); assertEquals(expected, res6); // REMOVE_END // STEP_START incr jedis.set("total_crashes", "0"); Long res7 = jedis.incr("total_crashes"); System.out.println(res7); // 1 Long res8 = jedis.incrBy("total_crashes", 10); System.out.println(res8); // 11 // STEP_END // REMOVE_START assertEquals(1L, res7.longValue()); assertEquals(11L, res8.longValue()); // REMOVE_END } } } ================================================ FILE: src/test/java/io/redis/examples/TDigestExample.java ================================================ //EXAMPLE: tdigest_tutorial //HIDE_START package io.redis.examples; //HIDE_END //REMOVE_START import java.util.List; import org.junit.jupiter.api.Test; import redis.clients.jedis.RedisClient; import static org.junit.jupiter.api.Assertions.assertEquals; //REMOVE_END public class TDigestExample { @Test public void run(){ //HIDE_START RedisClient jedis = RedisClient.create("redis://127.0.0.1:6379"); //HIDE_END //REMOVE_START jedis.del("racer_ages"); jedis.del("bikes:sales"); //REMOVE_END //STEP_START tdig_start String res1 = jedis.tdigestCreate("bikes:sales", 100); System.out.println(res1); // >>> True String res2 = jedis.tdigestAdd("bikes:sales", 21); System.out.println(res2); // >>> OK String res3 = jedis.tdigestAdd("bikes:sales", 150, 95, 75, 34); System.out.println(res3); // >>> OK //STEP_END //REMOVE_START assertEquals("OK","OK"); //REMOVE_END //STEP_START tdig_cdf String res4 = jedis.tdigestCreate("racer_ages"); System.out.println(res4); // >>> True String res5 = jedis.tdigestAdd("racer_ages", 45.88, 44.2, 58.03, 19.76, 39.84, 69.28, 50.97, 25.41, 19.27, 85.71, 42.63); System.out.println(res5); // >>> OK List res6 = jedis.tdigestRank("racer_ages", 50); System.out.println(res6); // >>> [7] List res7 = jedis.tdigestRank("racer_ages", 50, 40); System.out.println(res7); // >>> [7, 4] //STEP_END //STEP_START tdig_quant List res8 = jedis.tdigestQuantile("racer_ages", 0.5); System.out.println(res8); // >>> [44.2] List res9 = jedis.tdigestByRank("racer_ages", 4); System.out.println(res9); // >>> [42.63] //STEP_END //STEP_START tdig_min double res10 = jedis.tdigestMin("racer_ages"); System.out.println(res10); // >>> 19.27 double res11 = jedis.tdigestMax("racer_ages"); System.out.println(res11); // >>> 85.71 //STEP_END //STEP_START tdig_reset String res12 = jedis.tdigestReset("racer_ages"); System.out.println(res12); // >>> OK //STEP_END //HIDE_START jedis.close(); //HIDE_END } } ================================================ FILE: src/test/java/io/redis/examples/TimeSeriesTutorialExample.java ================================================ // EXAMPLE: time_series_tutorial // REMOVE_START package io.redis.examples; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertTrue; import org.junit.jupiter.api.Test; // REMOVE_END import redis.clients.jedis.RedisClient; import redis.clients.jedis.timeseries.*; import redis.clients.jedis.timeseries.TSElement; import java.util.*; public class TimeSeriesTutorialExample { @Test public void run() { RedisClient jedis = RedisClient.create("redis://localhost:6379"); // REMOVE_START // Clear any keys before using them in tests jedis.del( "thermometer:1", "thermometer:2", "thermometer:3", "rg:1", "rg:2", "rg:3", "rg:4", "sensor3", "wind:1", "wind:2", "wind:3", "wind:4", "hyg:1", "hyg:compacted" ); // REMOVE_END // STEP_START create String res1 = jedis.tsCreate("thermometer:1"); System.out.println(res1); // >>> OK String res2 = jedis.type("thermometer:1"); System.out.println(res2); // >>> TSDB-TYPE TSInfo res3 = jedis.tsInfo("thermometer:1"); System.out.println(res3.getProperty("totalSamples")); // >>> 0 // STEP_END // REMOVE_START assertEquals("OK", res1); assertEquals("TSDB-TYPE", res2); assertEquals((Long) 0L, res3.getProperty("totalSamples")); // REMOVE_END // STEP_START create_retention long res4 = jedis.tsAdd("thermometer:2", 1L, 10.8, TSCreateParams.createParams().retention(100)); System.out.println(res4); // >>> 1 TSInfo res5 = jedis.tsInfo("thermometer:2"); System.out.println(res5.getProperty("retentionTime")); // >>> 100 // STEP_END // REMOVE_START assertEquals(1L, res4); assertEquals((Long) 100L, res5.getProperty("retentionTime")); // REMOVE_END // STEP_START create_labels Map labels = new HashMap<>(); labels.put("location", "UK"); labels.put("type", "Mercury"); long res6 = jedis.tsAdd("thermometer:3", 1L, 10.4, TSCreateParams.createParams().labels(labels)); System.out.println(res6); // >>> 1 TSInfo res7 = jedis.tsInfo("thermometer:3"); System.out.println("Labels: " + res7.getLabels()); // >>> Labels: {location=UK, type=Mercury} // STEP_END // REMOVE_START assertEquals(1L, res6); assertEquals(labels, res7.getLabels()); // REMOVE_END // STEP_START madd List res8 = jedis.tsMAdd( new AbstractMap.SimpleEntry<>("thermometer:1", new TSElement(1L, 9.2)), new AbstractMap.SimpleEntry<>("thermometer:1", new TSElement(2L, 9.9)), new AbstractMap.SimpleEntry<>("thermometer:2", new TSElement(2L, 10.3)) ); System.out.println(res8); // >>> [1, 2, 2] // STEP_END // REMOVE_START assertEquals(Arrays.asList(1L, 2L, 2L), res8); // REMOVE_END // STEP_START get // The last recorded temperature for thermometer:2 // was 10.3 at time 2. TSElement res9 = jedis.tsGet("thermometer:2"); System.out.println("(" + res9.getTimestamp() + ", " + res9.getValue() + ")"); // >>> (2, 10.3) // STEP_END // REMOVE_START assertEquals(2L, res9.getTimestamp()); assertEquals(10.3, res9.getValue(), 0.001); // REMOVE_END // STEP_START range // Add 5 data points to a time series named "rg:1" String res10 = jedis.tsCreate("rg:1"); System.out.println(res10); // >>> OK List res11 = jedis.tsMAdd( new AbstractMap.SimpleEntry<>("rg:1", new TSElement(0L, 18.0)), new AbstractMap.SimpleEntry<>("rg:1", new TSElement(1L, 14.0)), new AbstractMap.SimpleEntry<>("rg:1", new TSElement(2L, 22.0)), new AbstractMap.SimpleEntry<>("rg:1", new TSElement(3L, 18.0)), new AbstractMap.SimpleEntry<>("rg:1", new TSElement(4L, 24.0)) ); System.out.println(res11); // >>> [0, 1, 2, 3, 4] // Retrieve all the data points in ascending order List res12 = jedis.tsRange("rg:1", 0L, 4L); System.out.println(res12); // >>> [(0:18.0), (1:14.0), (2:22.0), (3:18.0), (4:24.0)] // Retrieve data points up to time 1 (inclusive) List res13 = jedis.tsRange("rg:1", 0L, 1L); System.out.println(res13); // >>> [(0:18.0), (1:14.0)] // Retrieve data points from time 3 onwards List res14 = jedis.tsRange("rg:1", 3L, 4L); System.out.println(res14); // >>> [(3:18.0), (4:24.0)] // Retrieve all the data points in descending order List res15 = jedis.tsRevRange("rg:1", 0L, 4L); System.out.println(res15); // >>> [(4:24.0), (3:18.0), (2:22.0), (1:14.0), (0:18.0)] // Retrieve data points up to time 1 (inclusive), in descending order List res16 = jedis.tsRevRange("rg:1", 0L, 1L); System.out.println(res16); // >>> [(1:14.0), (0:18.0)] // STEP_END // REMOVE_START assertEquals("OK", res10); assertEquals(Arrays.asList(0L, 1L, 2L, 3L, 4L), res11); assertEquals(Arrays.asList( new TSElement(0L, 18.0), new TSElement(1L, 14.0), new TSElement(2L, 22.0), new TSElement(3L, 18.0), new TSElement(4L, 24.0)), res12); assertEquals(Arrays.asList(new TSElement(0L, 18.0), new TSElement(1L, 14.0)), res13); assertEquals(Arrays.asList(new TSElement(3L, 18.0), new TSElement(4L, 24.0)), res14); assertEquals(Arrays.asList( new TSElement(4L, 24.0), new TSElement(3L, 18.0), new TSElement(2L, 22.0), new TSElement(1L, 14.0), new TSElement(0L, 18.0)), res15); assertEquals(Arrays.asList(new TSElement(1L, 14.0), new TSElement(0L, 18.0)), res16); // REMOVE_END // STEP_START range_filter List res17 = jedis.tsRange("rg:1", TSRangeParams.rangeParams() .fromTimestamp(0L) .toTimestamp(4L) .filterByTS(0L, 2L, 4L) ); System.out.println(res17); // >>> [(0:18.0), (2:22.0), (4:24.0)] List res18 = jedis.tsRevRange("rg:1", TSRangeParams.rangeParams() .fromTimestamp(0L) .toTimestamp(4L) .filterByTS(0L, 2L, 4L) .filterByValues(20.0, 25.0) ); System.out.println(res18); // >>> [(4:24.0), (2:22.0)] List res19 = jedis.tsRevRange("rg:1", TSRangeParams.rangeParams() .fromTimestamp(0L) .toTimestamp(4L) .filterByTS(0L, 2L, 4L) .filterByValues(22.0, 22.0) .count(1) ); System.out.println(res19); // >>> [(2:22.0)] // STEP_END // REMOVE_START assertEquals(Arrays.asList( new TSElement(0L, 18.0), new TSElement(2L, 22.0), new TSElement(4L, 24.0)), res17); assertEquals(Arrays.asList(new TSElement(4L, 24.0), new TSElement(2L, 22.0)), res18); assertEquals(Arrays.asList(new TSElement(2L, 22.0)), res19); // REMOVE_END // STEP_START query_multi // Create three new "rg:" time series (two in the US // and one in the UK, with different units) and add some // data points. Map usLabels1 = new HashMap<>(); usLabels1.put("location", "us"); usLabels1.put("unit", "cm"); Map usLabels2 = new HashMap<>(); usLabels2.put("location", "us"); usLabels2.put("unit", "in"); Map ukLabels = new HashMap<>(); ukLabels.put("location", "uk"); ukLabels.put("unit", "mm"); String res20 = jedis.tsCreate("rg:2", TSCreateParams.createParams().labels(usLabels1)); System.out.println(res20); // >>> OK String res21 = jedis.tsCreate("rg:3", TSCreateParams.createParams().labels(usLabels2)); System.out.println(res21); // >>> OK String res22 = jedis.tsCreate("rg:4", TSCreateParams.createParams().labels(ukLabels)); System.out.println(res22); // >>> OK List res23 = jedis.tsMAdd( new AbstractMap.SimpleEntry<>("rg:2", new TSElement(0L, 1.8)), new AbstractMap.SimpleEntry<>("rg:3", new TSElement(0L, 0.9)), new AbstractMap.SimpleEntry<>("rg:4", new TSElement(0L, 25.0)) ); System.out.println(res23); // >>> [0, 0, 0] List res24 = jedis.tsMAdd( new AbstractMap.SimpleEntry<>("rg:2", new TSElement(1L, 2.1)), new AbstractMap.SimpleEntry<>("rg:3", new TSElement(1L, 0.77)), new AbstractMap.SimpleEntry<>("rg:4", new TSElement(1L, 18.0)) ); System.out.println(res24); // >>> [1, 1, 1] List res25 = jedis.tsMAdd( new AbstractMap.SimpleEntry<>("rg:2", new TSElement(2L, 2.3)), new AbstractMap.SimpleEntry<>("rg:3", new TSElement(2L, 1.1)), new AbstractMap.SimpleEntry<>("rg:4", new TSElement(2L, 21.0)) ); System.out.println(res25); // >>> [2, 2, 2] List res26 = jedis.tsMAdd( new AbstractMap.SimpleEntry<>("rg:2", new TSElement(3L, 1.9)), new AbstractMap.SimpleEntry<>("rg:3", new TSElement(3L, 0.81)), new AbstractMap.SimpleEntry<>("rg:4", new TSElement(3L, 19.0)) ); System.out.println(res26); // >>> [3, 3, 3] List res27 = jedis.tsMAdd( new AbstractMap.SimpleEntry<>("rg:2", new TSElement(4L, 1.78)), new AbstractMap.SimpleEntry<>("rg:3", new TSElement(4L, 0.74)), new AbstractMap.SimpleEntry<>("rg:4", new TSElement(4L, 23.0)) ); System.out.println(res27); // >>> [4, 4, 4] // Retrieve the last data point from each US time series. Map res28 = jedis.tsMGet( TSMGetParams.multiGetParams().latest(), "location=us" ); System.out.println(res28); // >>> {rg:2=TSMGetElement{key=rg:2, labels={}, element=(4:1.78)}... // Retrieve the same data points, but include the `unit` // label in the results. Map res29 = jedis.tsMGet( TSMGetParams.multiGetParams().selectedLabels("unit"), "location=us" ); System.out.println(res29); // >>> {rg:2=TSMGetElement{key=rg:2, labels={unit=cm}, element=(4:1.78)}... // Retrieve data points up to time 2 (inclusive) from all // time series that use millimeters as the unit. Include all // labels in the results. Map res30 = jedis.tsMRange( TSMRangeParams.multiRangeParams(0L, 2L) .withLabels() .filter("unit=mm") ); System.out.println(res30); // >>> {rg:4=TSMRangeElements{key=rg:4, labels={location=uk, unit=mm}, value=[(0:25.0), (1:18.0), (2:21.0)]}} // Retrieve data points from time 1 to time 3 (inclusive) from // all time series that use centimeters or millimeters as the unit, // but only return the `location` label. Return the results // in descending order of timestamp. Map res31 = jedis.tsMRevRange( TSMRangeParams.multiRangeParams(1L, 3L) .selectedLabels("location") .filter("unit=(cm,mm)") ); System.out.println(res31); // >>> {rg:2=TSMRangeElements{key=rg:2, labels={location=us, unit=cm}, value=[(1:2.1)... // STEP_END // REMOVE_START assertEquals("OK", res20); assertEquals("OK", res21); assertEquals("OK", res22); assertEquals(Arrays.asList(0L, 0L, 0L), res23); assertEquals(Arrays.asList(1L, 1L, 1L), res24); assertEquals(Arrays.asList(2L, 2L, 2L), res25); assertEquals(Arrays.asList(3L, 3L, 3L), res26); assertEquals(Arrays.asList(4L, 4L, 4L), res27); assertEquals(2, res28.size()); assertTrue(res28.containsKey("rg:2")); TSMGetElement res28rg2 = res28.get("rg:2"); assertEquals("rg:2", res28rg2.getKey()); assertEquals(0, res28rg2.getLabels().size()); assertEquals(4L, res28rg2.getElement().getTimestamp()); assertEquals(1.78, res28rg2.getElement().getValue(), 0.001); assertTrue(res28.containsKey("rg:3")); TSMGetElement res28rg3 = res28.get("rg:3"); assertEquals("rg:3", res28rg3.getKey()); assertEquals(0, res28rg3.getLabels().size()); assertEquals(4L, res28rg3.getElement().getTimestamp()); assertEquals(0.74, res28rg3.getElement().getValue(), 0.001); assertEquals(2, res29.size()); assertTrue(res29.containsKey("rg:2")); TSMGetElement res29rg2 = res29.get("rg:2"); assertEquals("rg:2", res29rg2.getKey()); assertEquals(1, res29rg2.getLabels().size()); assertEquals("cm", res29rg2.getLabels().get("unit")); assertEquals(4L, res29rg2.getElement().getTimestamp()); assertEquals(1.78, res29rg2.getElement().getValue(), 0.001); assertEquals(1, res30.size()); assertTrue(res30.containsKey("rg:4")); TSMRangeElements res30rg4 = res30.get("rg:4"); assertEquals("rg:4", res30rg4.getKey()); assertEquals(2, res30rg4.getLabels().size()); assertEquals("uk", res30rg4.getLabels().get("location")); assertEquals("mm", res30rg4.getLabels().get("unit")); assertEquals(3, res30rg4.getElements().size()); assertEquals(0L, res30rg4.getElements().get(0).getTimestamp()); assertEquals(25.0, res30rg4.getElements().get(0).getValue(), 0.001); assertEquals(1L, res30rg4.getElements().get(1).getTimestamp()); assertEquals(18.0, res30rg4.getElements().get(1).getValue(), 0.001); assertEquals(2L, res30rg4.getElements().get(2).getTimestamp()); assertEquals(21.0, res30rg4.getElements().get(2).getValue(), 0.001); assertEquals(2, res31.size()); assertTrue(res31.containsKey("rg:2")); TSMRangeElements res31rg2 = res31.get("rg:2"); assertEquals("rg:2", res31rg2.getKey()); assertEquals(1, res31rg2.getLabels().size()); assertEquals("us", res31rg2.getLabels().get("location")); assertEquals(3, res31rg2.getElements().size()); assertEquals(3L, res31rg2.getElements().get(0).getTimestamp()); assertEquals(1.9, res31rg2.getElements().get(0).getValue(), 0.001); assertEquals(2L, res31rg2.getElements().get(1).getTimestamp()); assertEquals(2.3, res31rg2.getElements().get(1).getValue(), 0.001); assertEquals(1L, res31rg2.getElements().get(2).getTimestamp()); assertEquals(2.1, res31rg2.getElements().get(2).getValue(), 0.001); // REMOVE_END // STEP_START agg List res32 = jedis.tsRange("rg:2", TSRangeParams.rangeParams() .fromTimestamp(0L) .toTimestamp(4L) .aggregation(AggregationType.AVG, 2) ); System.out.println(res32); // >>> [(0:1.9500000000000002), (2:2.0999999999999996), (4:1.78)] // STEP_END // REMOVE_START assertEquals( Arrays.asList( new TSElement(0L, 1.9500000000000002), new TSElement(2L, 2.0999999999999996), new TSElement(4L, 1.78) ), res32 ); // REMOVE_END // STEP_START agg_bucket String res33 = jedis.tsCreate("sensor3"); System.out.println(res33); // >>> OK List res34 = jedis.tsMAdd( new AbstractMap.SimpleEntry<>("sensor3", new TSElement(10L, 1000.0)), new AbstractMap.SimpleEntry<>("sensor3", new TSElement(20L, 2000.0)), new AbstractMap.SimpleEntry<>("sensor3", new TSElement(30L, 3000.0)), new AbstractMap.SimpleEntry<>("sensor3", new TSElement(40L, 4000.0)), new AbstractMap.SimpleEntry<>("sensor3", new TSElement(50L, 5000.0)), new AbstractMap.SimpleEntry<>("sensor3", new TSElement(60L, 6000.0)), new AbstractMap.SimpleEntry<>("sensor3", new TSElement(70L, 7000.0)) ); System.out.println(res34); // >>> [10, 20, 30, 40, 50, 60, 70] List res35 = jedis.tsRange("sensor3", TSRangeParams.rangeParams() .fromTimestamp(10L) .toTimestamp(70L) .aggregation(AggregationType.MIN, 25) ); System.out.println(res35); // >>> [(0:1000.0), (25:3000.0), (50:5000.0)] // STEP_END // REMOVE_START assertEquals("OK", res33); assertEquals(Arrays.asList(10L, 20L, 30L, 40L, 50L, 60L, 70L), res34); assertEquals( Arrays.asList( new TSElement(0L, 1000.0), new TSElement(25L, 3000.0), new TSElement(50L, 5000.0) ), res35 ); // REMOVE_END // STEP_START agg_align List res36 = jedis.tsRange("sensor3", TSRangeParams.rangeParams() .fromTimestamp(10L) .toTimestamp(70L) .aggregation(AggregationType.MIN, 25) .alignStart() ); System.out.println(res36); // >>> [(10:1000.0), (35:4000.0), (60:6000.0)] // STEP_END // REMOVE_START assertEquals( Arrays.asList( new TSElement(10L, 1000.0), new TSElement(35L, 4000.0), new TSElement(60L, 6000.0) ), res36 ); // REMOVE_END // STEP_START agg_multi Map ukCountry = new HashMap<>(); ukCountry.put("country", "uk"); Map usCountry = new HashMap<>(); usCountry.put("country", "us"); jedis.tsCreate("wind:1", TSCreateParams.createParams().labels(ukCountry)); jedis.tsCreate("wind:2", TSCreateParams.createParams().labels(ukCountry)); jedis.tsCreate("wind:3", TSCreateParams.createParams().labels(usCountry)); jedis.tsCreate("wind:4", TSCreateParams.createParams().labels(usCountry)); jedis.tsMAdd( new AbstractMap.SimpleEntry<>("wind:1", new TSElement(0L, 10.0)), new AbstractMap.SimpleEntry<>("wind:2", new TSElement(0L, 12.0)), new AbstractMap.SimpleEntry<>("wind:3", new TSElement(0L, 8.0)), new AbstractMap.SimpleEntry<>("wind:4", new TSElement(0L, 15.0)) ); jedis.tsMAdd( new AbstractMap.SimpleEntry<>("wind:1", new TSElement(1L, 11.0)), new AbstractMap.SimpleEntry<>("wind:2", new TSElement(1L, 13.0)), new AbstractMap.SimpleEntry<>("wind:3", new TSElement(1L, 9.0)), new AbstractMap.SimpleEntry<>("wind:4", new TSElement(1L, 16.0)) ); jedis.tsMAdd( new AbstractMap.SimpleEntry<>("wind:1", new TSElement(2L, 9.0)), new AbstractMap.SimpleEntry<>("wind:2", new TSElement(2L, 11.0)), new AbstractMap.SimpleEntry<>("wind:3", new TSElement(2L, 7.0)), new AbstractMap.SimpleEntry<>("wind:4", new TSElement(2L, 14.0)) ); jedis.tsMAdd( new AbstractMap.SimpleEntry<>("wind:1", new TSElement(3L, 12.0)), new AbstractMap.SimpleEntry<>("wind:2", new TSElement(3L, 14.0)), new AbstractMap.SimpleEntry<>("wind:3", new TSElement(3L, 10.0)), new AbstractMap.SimpleEntry<>("wind:4", new TSElement(3L, 17.0)) ); jedis.tsMAdd( new AbstractMap.SimpleEntry<>("wind:1", new TSElement(4L, 8.0)), new AbstractMap.SimpleEntry<>("wind:2", new TSElement(4L, 10.0)), new AbstractMap.SimpleEntry<>("wind:3", new TSElement(4L, 6.0)), new AbstractMap.SimpleEntry<>("wind:4", new TSElement(4L, 13.0)) ); // Group by country with max reduction Map res44 = jedis.tsMRange( TSMRangeParams.multiRangeParams(0L, 4L) .filter("country=(us,uk)") .groupBy("country", "max")); System.out.println(res44); // >>> {country=uk=TSMRangeElements{key=country=uk, labels={}, value=[(0:12.0)... // Group by country with avg reduction Map res45 = jedis.tsMRange( TSMRangeParams.multiRangeParams(0L, 4L) .filter("country=(us,uk)") .groupBy("country", "avg")); System.out.println(res45); // >>> {country=uk=TSMRangeElements{key=country=uk, labels={}, value=[(0:11.0)... // STEP_END // REMOVE_START assertEquals(2, res44.size()); assertTrue(res44.containsKey("country=uk")); TSMRangeElements res44uk = res44.get("country=uk"); assertEquals("country=uk", res44uk.getKey()); assertEquals(0, res44uk.getLabels().size()); assertEquals(5, res44uk.getElements().size()); assertEquals(0L, res44uk.getElements().get(0).getTimestamp()); assertEquals(12.0, res44uk.getElements().get(0).getValue(), 0.001); assertEquals(1L, res44uk.getElements().get(1).getTimestamp()); assertEquals(13.0, res44uk.getElements().get(1).getValue(), 0.001); assertEquals(2L, res44uk.getElements().get(2).getTimestamp()); assertEquals(11.0, res44uk.getElements().get(2).getValue(), 0.001); assertEquals(3L, res44uk.getElements().get(3).getTimestamp()); assertEquals(14.0, res44uk.getElements().get(3).getValue(), 0.001); assertEquals(4L, res44uk.getElements().get(4).getTimestamp()); assertEquals(10.0, res44uk.getElements().get(4).getValue(), 0.001); assertTrue(res44.containsKey("country=us")); TSMRangeElements res44us = res44.get("country=us"); assertEquals("country=us", res44us.getKey()); assertEquals(0, res44us.getLabels().size()); assertEquals(5, res44us.getElements().size()); assertEquals(0L, res44us.getElements().get(0).getTimestamp()); assertEquals(15.0, res44us.getElements().get(0).getValue(), 0.001); assertEquals(1L, res44us.getElements().get(1).getTimestamp()); assertEquals(16.0, res44us.getElements().get(1).getValue(), 0.001); assertEquals(2L, res44us.getElements().get(2).getTimestamp()); assertEquals(14.0, res44us.getElements().get(2).getValue(), 0.001); assertEquals(3L, res44us.getElements().get(3).getTimestamp()); assertEquals(17.0, res44us.getElements().get(3).getValue(), 0.001); assertEquals(4L, res44us.getElements().get(4).getTimestamp()); assertEquals(13.0, res44us.getElements().get(4).getValue(), 0.001); assertEquals(2, res45.size()); assertTrue(res45.containsKey("country=uk")); TSMRangeElements res45uk = res45.get("country=uk"); assertEquals("country=uk", res45uk.getKey()); assertEquals(0, res45uk.getLabels().size()); assertEquals(5, res45uk.getElements().size()); assertEquals(0L, res45uk.getElements().get(0).getTimestamp()); assertEquals(11.0, res45uk.getElements().get(0).getValue(), 0.001); assertEquals(1L, res45uk.getElements().get(1).getTimestamp()); assertEquals(12.0, res45uk.getElements().get(1).getValue(), 0.001); assertEquals(2L, res45uk.getElements().get(2).getTimestamp()); assertEquals(10.0, res45uk.getElements().get(2).getValue(), 0.001); assertEquals(3L, res45uk.getElements().get(3).getTimestamp()); assertEquals(13.0, res45uk.getElements().get(3).getValue(), 0.001); assertEquals(4L, res45uk.getElements().get(4).getTimestamp()); assertEquals(9.0, res45uk.getElements().get(4).getValue(), 0.001); assertTrue(res45.containsKey("country=us")); TSMRangeElements res45us = res45.get("country=us"); assertEquals("country=us", res45us.getKey()); assertEquals(0, res45us.getLabels().size()); assertEquals(5, res45us.getElements().size()); assertEquals(0L, res45us.getElements().get(0).getTimestamp()); assertEquals(11.5, res45us.getElements().get(0).getValue(), 0.001); assertEquals(1L, res45us.getElements().get(1).getTimestamp()); assertEquals(12.5, res45us.getElements().get(1).getValue(), 0.001); assertEquals(2L, res45us.getElements().get(2).getTimestamp()); assertEquals(10.5, res45us.getElements().get(2).getValue(), 0.001); assertEquals(3L, res45us.getElements().get(3).getTimestamp()); assertEquals(13.5, res45us.getElements().get(3).getValue(), 0.001); assertEquals(4L, res45us.getElements().get(4).getTimestamp()); assertEquals(9.5, res45us.getElements().get(4).getValue(), 0.001); // REMOVE_END // STEP_START create_compaction String res46 = jedis.tsCreate("hyg:1"); System.out.println(res46); // >>> OK String res47 = jedis.tsCreate("hyg:compacted"); System.out.println(res47); // >>> OK String res48 = jedis.tsCreateRule("hyg:1", "hyg:compacted", AggregationType.MIN, 3); System.out.println(res48); // >>> OK TSInfo res49 = jedis.tsInfo("hyg:1"); System.out.println("Rules: " + res49.getProperty("rules")); // >>> Rules: [{compactionKey=hyg:compacted, bucketDuration=3, aggregationType=MIN, alignmentTimestamp=0}] TSInfo res50 = jedis.tsInfo("hyg:compacted"); System.out.println("Source key: " + res50.getProperty("sourceKey")); // >>> Source key: hyg:1 // STEP_END // REMOVE_START assertEquals("OK", res46); assertEquals("OK", res47); assertEquals("OK", res48); assertEquals("hyg:1", res50.getProperty("sourceKey")); // REMOVE_END // STEP_START comp_add List res51 = jedis.tsMAdd( new AbstractMap.SimpleEntry<>("hyg:1", new TSElement(0L, 75.0)), new AbstractMap.SimpleEntry<>("hyg:1", new TSElement(1L, 77.0)), new AbstractMap.SimpleEntry<>("hyg:1", new TSElement(2L, 78.0)) ); System.out.println(res51); // >>> [0, 1, 2] List res52 = jedis.tsRange("hyg:compacted", 0L, 10L); System.out.println(res52); // >>> [] long res53 = jedis.tsAdd("hyg:1", 3L, 79.0); System.out.println(res53); // >>> 3 List res54 = jedis.tsRange("hyg:compacted", 0L, 10L); System.out.println(res54); // >>> [(0:75.0)] // STEP_END // REMOVE_START assertEquals(Arrays.asList(0L, 1L, 2L), res51); assertEquals(Arrays.asList(), res52); assertEquals(3L, res53); assertEquals(Arrays.asList(new TSElement(0L, 75.0)), res54); // REMOVE_END // STEP_START del TSInfo res55 = jedis.tsInfo("thermometer:1"); System.out.println(res55.getProperty("totalSamples")); // >>> 2 System.out.println(res55.getProperty("firstTimestamp")); // >>> 1 System.out.println(res55.getProperty("lastTimestamp")); // >>> 2 long res56 = jedis.tsAdd("thermometer:1", 3L, 9.7); System.out.println(res56); // >>> 3 TSInfo res57 = jedis.tsInfo("thermometer:1"); System.out.println(res57.getProperty("totalSamples")); // >>> 3 long res58 = jedis.tsDel("thermometer:1", 1L, 2L); System.out.println(res58); // >>> 2 TSInfo res59 = jedis.tsInfo("thermometer:1"); System.out.println(res59.getProperty("totalSamples")); // >>> 1 long res60 = jedis.tsDel("thermometer:1", 3L, 3L); System.out.println(res60); // >>> 1 TSInfo res61 = jedis.tsInfo("thermometer:1"); System.out.println(res61.getProperty("totalSamples")); // >>> 0 // STEP_END // REMOVE_START assertEquals((Long) 2L, res55.getProperty("totalSamples")); assertEquals((Long) 1L, res55.getProperty("firstTimestamp")); assertEquals((Long) 2L, res55.getProperty("lastTimestamp")); assertEquals(3L, res56); assertEquals((Long) 3L, res57.getProperty("totalSamples")); assertEquals(2L, res58); assertEquals((Long) 1L, res59.getProperty("totalSamples")); assertEquals(1L, res60); assertEquals((Long) 0L, res61.getProperty("totalSamples")); // REMOVE_END //HIDE_START jedis.close(); //HIDE_END } } ================================================ FILE: src/test/java/io/redis/examples/TopKExample.java ================================================ //EXAMPLE: topk_tutorial //HIDE_START package io.redis.examples; //HIDE_END //REMOVE_START import java.util.List; import org.junit.jupiter.api.Test; import redis.clients.jedis.RedisClient; //REMOVE_END public class TopKExample { @Test public void run() { //HIDE_START RedisClient jedis = RedisClient.create("redis://127.0.0.1:6379"); //HIDE_END //REMOVE_START jedis.del("bikes:keywords"); //REMOVE_END //STEP_START topk String res1 = jedis.topkReserve("bikes:keywords", 5L, 2000L, 7L, 0.925D); System.out.println(res1); // >>> True List res2 = jedis.topkAdd("bikes:keywords", "store", "seat", "handlebars", "handles", "pedals", "tires", "store", "seat"); System.out.println(res2); // >>> [None, None, None, None, None, 'handlebars', None, None] List res3 = jedis.topkList("bikes:keywords"); System.out.println(res3); // >>> ['store', 'seat', 'pedals', 'tires', 'handles'] List res4 = jedis.topkQuery("bikes:keywords", "store", "handlebars"); System.out.println(res4); // >>> [1, 0] //STEP_END //HIDE_START jedis.close(); //HIDE_END } } ================================================ FILE: src/test/java/io/redis/examples/VectorSetExample.java ================================================ // EXAMPLE: vecset_tutorial // REMOVE_START package io.redis.examples; import org.junit.jupiter.api.Test; import static org.junit.jupiter.api.Assertions.*; // REMOVE_END import redis.clients.jedis.RedisClient; import redis.clients.jedis.params.VAddParams; import redis.clients.jedis.params.VSimParams; import java.util.*; public class VectorSetExample { @Test public void run() { try (RedisClient jedis = RedisClient.create("redis://localhost:6379")) { // REMOVE_START jedis.del("points", "quantSetQ8", "quantSetNoQ", "quantSetBin", "setNotReduced", "setReduced"); // REMOVE_END // STEP_START vadd boolean res1 = jedis.vadd("points", new float[] { 1.0f, 1.0f }, "pt:A"); System.out.println(res1); // >>> true boolean res2 = jedis.vadd("points", new float[] { -1.0f, -1.0f }, "pt:B"); System.out.println(res2); // >>> true boolean res3 = jedis.vadd("points", new float[] { -1.0f, 1.0f }, "pt:C"); System.out.println(res3); // >>> true boolean res4 = jedis.vadd("points", new float[] { 1.0f, -1.0f }, "pt:D"); System.out.println(res4); // >>> true boolean res5 = jedis.vadd("points", new float[] { 1.0f, 0.0f }, "pt:E"); System.out.println(res5); // >>> true String res6 = jedis.type("points"); System.out.println(res6); // >>> vectorset // STEP_END // REMOVE_START assertTrue(res1); assertTrue(res2); assertTrue(res3); assertTrue(res4); assertTrue(res5); assertEquals("vectorset", res6); // REMOVE_END // STEP_START vcardvdim long res7 = jedis.vcard("points"); System.out.println(res7); // >>> 5 long res8 = jedis.vdim("points"); System.out.println(res8); // >>> 2 // STEP_END // REMOVE_START assertEquals(5L, res7); assertEquals(2L, res8); // REMOVE_END // STEP_START vemb List res9 = jedis.vemb("points", "pt:A"); System.out.println(res9); // >>> [0.9999999..., 0.9999999...] List res10 = jedis.vemb("points", "pt:B"); System.out.println(res10); // >>> [-0.9999999..., -0.9999999...] List res11 = jedis.vemb("points", "pt:C"); System.out.println(res11); // >>> [-0.9999999..., 0.9999999...] List res12 = jedis.vemb("points", "pt:D"); System.out.println(res12); // >>> [0.9999999..., -0.9999999...] List res13 = jedis.vemb("points", "pt:E"); System.out.println(res13); // >>> [1, 0] // STEP_END // REMOVE_START assertEquals(1, res9.get(0), 0.01); assertEquals(1, res9.get(1), 0.01); assertEquals(-1, res10.get(0), 0.01); assertEquals(-1, res10.get(1), 0.01); assertEquals(-1, res11.get(0), 0.01); assertEquals(1, res11.get(1), 0.01); assertEquals(1, res12.get(0), 0.01); assertEquals(-1, res12.get(1), 0.01); assertEquals(Arrays.asList(1.0, 0.0), res13); // REMOVE_END // STEP_START attr boolean res14 = jedis.vsetattr("points", "pt:A", "{\"name\":\"Point A\",\"description\":\"First point added\"}"); System.out.println(res14); // >>> true String res15 = jedis.vgetattr("points", "pt:A"); System.out.println(res15); // >>> {"name":"Point A","description":"First point added"} boolean res16 = jedis.vsetattr("points", "pt:A", ""); System.out.println(res16); // >>> true String res17 = jedis.vgetattr("points", "pt:A"); System.out.println(res17); // >>> null // STEP_END // REMOVE_START assertTrue(res14); assertTrue(res15.contains("\"name\":\"Point A\"")); assertTrue(res15.contains("\"description\":\"First point added\"")); assertTrue(res16); assertNull(res17); // REMOVE_END // STEP_START vrem boolean res18 = jedis.vadd("points", new float[] { 0.0f, 0.0f }, "pt:F"); System.out.println(res18); // >>> true long res19 = jedis.vcard("points"); System.out.println(res19); // >>> 6 boolean res20 = jedis.vrem("points", "pt:F"); System.out.println(res20); // >>> true long res21 = jedis.vcard("points"); System.out.println(res21); // >>> 5 // STEP_END // REMOVE_START assertTrue(res18); assertEquals(6L, res19); assertTrue(res20); assertEquals(5L, res21); // REMOVE_END // STEP_START vsim_basic List res22 = jedis.vsim("points", new float[] { 0.9f, 0.1f }); System.out.println(res22); // >>> ["pt:E", "pt:A", "pt:D", "pt:C", "pt:B"] // STEP_END // REMOVE_START assertEquals(Arrays.asList("pt:E", "pt:A", "pt:D", "pt:C", "pt:B"), res22); // REMOVE_END // STEP_START vsim_options Map res23 = jedis.vsimByElementWithScores("points", "pt:A", new VSimParams().count(4)); System.out.println(res23); // >>> {pt:A=1.0, pt:E≈0.85355, pt:D=0.5, pt:C=0.5} // STEP_END // REMOVE_START assertEquals(1.0, res23.get("pt:A"), 0.0001); assertEquals(0.5, res23.get("pt:C"), 0.0001); assertEquals(0.5, res23.get("pt:D"), 0.0001); assertTrue(Math.abs(res23.get("pt:E") - 0.8535) < 0.01); // REMOVE_END // STEP_START vsim_filter boolean res24 = jedis.vsetattr("points", "pt:A", "{\"size\":\"large\",\"price\":18.99}"); System.out.println(res24); // >>> true boolean res25 = jedis.vsetattr("points", "pt:B", "{\"size\":\"large\",\"price\":35.99}"); System.out.println(res25); // >>> true boolean res26 = jedis.vsetattr("points", "pt:C", "{\"size\":\"large\",\"price\":25.99}"); System.out.println(res26); // >>> true boolean res27 = jedis.vsetattr("points", "pt:D", "{\"size\":\"small\",\"price\":21.00}"); System.out.println(res27); // >>> true boolean res28 = jedis.vsetattr("points", "pt:E", "{\"size\":\"small\",\"price\":17.75}"); System.out.println(res28); // >>> true List res29 = jedis.vsimByElement("points", "pt:A", new VSimParams().filter(".size == \"large\"")); System.out.println(res29); // >>> ["pt:A", "pt:C", "pt:B"] List res30 = jedis.vsimByElement("points", "pt:A", new VSimParams().filter(".size == \"large\" && .price > 20.00")); System.out.println(res30); // >>> ["pt:C", "pt:B"] // STEP_END // REMOVE_START assertTrue(res24); assertTrue(res25); assertTrue(res26); assertTrue(res27); assertTrue(res28); assertEquals(Arrays.asList("pt:C", "pt:B"), res30); // REMOVE_END // STEP_START add_quant boolean res31 = jedis.vadd("quantSetQ8", new float[] { 1.262185f, 1.958231f }, "quantElement", new VAddParams().q8()); System.out.println(res31); // >>> true List res32 = jedis.vemb("quantSetQ8", "quantElement"); System.out.println("Q8: " + res32); // >>> Q8: [~1.264, ~1.958] boolean res33 = jedis.vadd("quantSetNoQ", new float[] { 1.262185f, 1.958231f }, "quantElement", new VAddParams().noQuant()); System.out.println(res33); // >>> true List res34 = jedis.vemb("quantSetNoQ", "quantElement"); System.out.println("NOQUANT: " + res34); // >>> NOQUANT: [~1.262185, ~1.958231] boolean res35 = jedis.vadd("quantSetBin", new float[] { 1.262185f, 1.958231f }, "quantElement", new VAddParams().bin()); System.out.println(res35); // >>> true List res36 = jedis.vemb("quantSetBin", "quantElement"); System.out.println("BIN: " + res36); // >>> BIN: [1, 1] // STEP_END // REMOVE_START assertTrue(res31); assertTrue(res33); assertTrue(res35); assertEquals(2, res32.size()); assertEquals(2, res34.size()); assertEquals(2, res36.size()); assertTrue(Math.abs(res32.get(0) - 1.26) < 0.05); assertTrue(Math.abs(res32.get(1) - 1.958) < 0.01); assertTrue(Math.abs(res34.get(0) - 1.2622) < 0.01); assertTrue(Math.abs(res34.get(1) - 1.9582) < 0.01); assertEquals(Arrays.asList(1.0, 1.0), res36); // REMOVE_END // STEP_START add_reduce float[] values = new float[300]; for (int i = 0; i < 300; i++) values[i] = i / 299.0f; boolean res37 = jedis.vadd("setNotReduced", values, "element"); System.out.println(res37); // >>> true long res38 = jedis.vdim("setNotReduced"); System.out.println(res38); // >>> 300 boolean res39 = jedis.vadd("setReduced", values, "element", 100, new VAddParams()); System.out.println(res39); // >>> true long res40 = jedis.vdim("setReduced"); System.out.println(res40); // >>> 100 // STEP_END // REMOVE_START assertTrue(res37); assertEquals(300L, res38); assertTrue(res39); assertEquals(100L, res40); // REMOVE_END // HIDE_START jedis.close(); } } } // HIDE_END ================================================ FILE: src/test/java/io/redis/test/annotations/ConditionalOnEnv.java ================================================ package io.redis.test.annotations; import java.lang.annotation.*; /** * Annotation to conditionally enable or disable tests based on the test environment. *

* This unified annotation replaces both {@code @EnabledOnEnv} and {@code @SkipOnEnv} annotations, * providing a single way to control test execution based on environment. *

* The annotation can be applied at both method and class level. Method-level annotations take * precedence over class-level annotations. *

* Example usage: * *

 * // Enable test only in a specific environment
 * @ConditionalOnEnv(value = TestEnvUtil.ENV_OSS_DOCKER, enabled = true)
 * public void testOnlyInDocker() {
 *   // This test only runs in Docker environment
 * }
 *
 * // Skip test in a specific environment
 * @ConditionalOnEnv(value = TestEnvUtil.ENV_REDIS_ENTERPRISE, enabled = false)
 * public void testNotInRedisEnterprise() {
 *   // This test is skipped in Redis Enterprise environment
 * }
 *
 * // Enable test in multiple environments
 * @ConditionalOnEnv(value = { TestEnvUtil.ENV_OSS_DOCKER,
 *     TestEnvUtil.ENV_OSS_SOURCE }, enabled = true)
 * public void testInDockerOrSource() {
 *   // This test runs in Docker or Source environments
 * }
 * 
*/ @Inherited @Retention(RetentionPolicy.RUNTIME) @Target({ ElementType.METHOD, ElementType.TYPE }) public @interface ConditionalOnEnv { /** * The environment(s) to match against. Valid values are defined in {@code TestEnvUtil} (e.g., * "oss-docker", "oss-source", "re"). * @return array of environment identifiers */ String[] value(); /** * Whether the test should be enabled or disabled when the current environment matches one of the * specified values. *

* When {@code enabled = true}: the test runs ONLY when the current environment matches one of the * specified values. *

* When {@code enabled = false}: the test is SKIPPED when the current environment matches one of * the specified values. * @return true to enable the test in matching environments, false to disable */ boolean enabled(); /** * Optional message to display when the test is disabled. * @return the reason message */ String message() default ""; } ================================================ FILE: src/test/java/io/redis/test/annotations/EnabledOnCommand.java ================================================ package io.redis.test.annotations; import java.lang.annotation.*; @Inherited @Retention(RetentionPolicy.RUNTIME) @Target({ElementType.METHOD, ElementType.TYPE}) public @interface EnabledOnCommand { String value(); String subCommand() default ""; } ================================================ FILE: src/test/java/io/redis/test/annotations/SinceRedisVersion.java ================================================ package io.redis.test.annotations; import java.lang.annotation.*; @Inherited @Retention(RetentionPolicy.RUNTIME) @Target({ElementType.METHOD, ElementType.TYPE}) public @interface SinceRedisVersion { String value(); String message() default ""; } ================================================ FILE: src/test/java/io/redis/test/utils/RedisInfo.java ================================================ package io.redis.test.utils; import java.util.HashMap; import java.util.Map; public class RedisInfo { private final Map infoMap; public RedisInfo() { this.infoMap = new HashMap<>(); } public void setField(String key, String value) { infoMap.put(key, value); } public String getField(String key) { return infoMap.get(key); } public String getRedisVersion() { return infoMap.get("redis_version"); } public String getOs() { return infoMap.get("os"); } public String getMode() { return infoMap.get("redis_mode"); } public String getPorts() { return infoMap.get("tcp_port"); // Assuming "tcp_port" is the key for ports } @Override public String toString() { return "RedisInfo{" + "infoMap=" + infoMap + '}'; } public static RedisInfo parseInfoServer(String infoOutput) { RedisInfo redisInfo = new RedisInfo(); String[] lines = infoOutput.split("\n"); for (String line : lines) { // Only parse lines that contain a colon (indicating a key-value pair) if (line.contains(":")) { String[] parts = line.split(":", 2); if (parts.length == 2) { redisInfo.setField(parts[0].trim(), parts[1].trim()); } } } // You can still check for required fields if necessary // Example: Ensure that specific fields are set if (redisInfo.getField("redis_version") == null || redisInfo.getField("redis_mode") == null) { throw new IllegalArgumentException("Missing required fields in Redis server info."); } return redisInfo; } } ================================================ FILE: src/test/java/io/redis/test/utils/RedisVersion.java ================================================ package io.redis.test.utils; public class RedisVersion implements Comparable { public static final RedisVersion V6_0_0 = RedisVersion.of("6.0.0"); public static final RedisVersion V7_0_0 = RedisVersion.of("7.0.0"); public static final RedisVersion V7_2_0 = RedisVersion.of("7.2.0"); public static final RedisVersion V7_4 = RedisVersion.of("7.4"); public static final RedisVersion V8_0_0_PRE = RedisVersion.of("7.9.0"); public static final RedisVersion V8_0_0 = RedisVersion.of("8.0.0"); public static final String V8_4_0_STRING = "8.4.0"; public static final RedisVersion V8_4_0 = RedisVersion.of("8.4.0"); public static final RedisVersion V8_6_0 = RedisVersion.of("8.6.0"); public static final RedisVersion V8_6_1 = RedisVersion.of("8.6.1"); private final int major; private final int minor; private final int patch; // Private constructor to enforce use of the static factory method private RedisVersion(int major, int minor, int patch) { this.major = major; this.minor = minor; this.patch = patch; } // Static method to create a RedisVersion from a version string public static RedisVersion of(String version) { // Check for the "redis_version:" prefix and remove it if present if (version.startsWith("redis_version:")) { version = version.substring("redis_version:".length()); } // Split the version string by the '.' character String[] parts = version.split("\\."); // Parse each component, setting defaults for missing parts int major = parts.length > 0 ? Integer.parseInt(parts[0]) : 0; int minor = parts.length > 1 ? Integer.parseInt(parts[1]) : 0; int patch = parts.length > 2 ? Integer.parseInt(parts[2]) : 0; return new RedisVersion(major, minor, patch); } public int getMajor() { return major; } public int getMinor() { return minor; } public int getPatch() { return patch; } @Override public String toString() { return "RedisVersion{" + "major=" + major + ", minor=" + minor + ", patch=" + patch + '}'; } @Override public int compareTo(RedisVersion other) { // Compare major, minor, and patch versions if (this.major != other.major) { return Integer.compare(this.major, other.major); } if (this.minor != other.minor) { return Integer.compare(this.minor, other.minor); } return Integer.compare(this.patch, other.patch); } public boolean is(RedisVersion other) { return this.compareTo(other) == 0; } public boolean isLessThanOrEqualTo(RedisVersion other) { return this.compareTo(other) <= 0; } public boolean isLessThan(RedisVersion other) { return this.compareTo(other) < 0; } public boolean isGreaterThanOrEqualTo(RedisVersion other) { return this.compareTo(other) >= 0; } public boolean isGreaterThan(RedisVersion other) { return this.compareTo(other) > 0; } // Static method to compare two RedisVersion instances public static int compare(RedisVersion v1, RedisVersion v2) { return v1.compareTo(v2); } } ================================================ FILE: src/test/java/redis/clients/jedis/ACLJedisPoolTest.java ================================================ package redis.clients.jedis; import static java.util.concurrent.TimeUnit.MILLISECONDS; import static org.awaitility.Awaitility.await; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertNull; import static org.junit.jupiter.api.Assertions.assertSame; import static org.junit.jupiter.api.Assertions.assertThrows; import static org.junit.jupiter.api.Assertions.assertTrue; import java.net.URI; import java.net.URISyntaxException; import java.time.Duration; import io.redis.test.annotations.ConditionalOnEnv; import io.redis.test.annotations.SinceRedisVersion; import org.apache.commons.pool2.impl.GenericObjectPoolConfig; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Tag; import org.junit.jupiter.api.extension.RegisterExtension; import redis.clients.jedis.exceptions.InvalidURIException; import redis.clients.jedis.exceptions.JedisAccessControlException; import redis.clients.jedis.exceptions.JedisException; import redis.clients.jedis.util.EnvCondition; import redis.clients.jedis.util.RedisVersionCondition; import redis.clients.jedis.util.TestEnvUtil; /** * This test class is a copy of {@link JedisPoolTest}. *

* This test is only executed when the server/cluster is Redis 6. or more. */ @SinceRedisVersion("6.0.0") @Tag("integration") @ConditionalOnEnv(value = TestEnvUtil.ENV_OSS_DOCKER, enabled = true) public class ACLJedisPoolTest { private static EndpointConfig endpoint; private static EndpointConfig endpointWithDefaultUser; @RegisterExtension public static EnvCondition envCondition = new EnvCondition(); @RegisterExtension public static RedisVersionCondition versionCondition = new RedisVersionCondition( () -> Endpoints.getRedisEndpoint("standalone0-acl")); @BeforeAll public static void prepare() { endpoint = Endpoints.getRedisEndpoint("standalone0-acl"); endpointWithDefaultUser = Endpoints.getRedisEndpoint("standalone0"); } @Test public void checkConnections() { JedisPool pool = new JedisPool(new JedisPoolConfig(), endpoint.getHost(), endpoint.getPort(), endpoint.getUsername(), endpoint.getPassword()); try (Jedis jedis = pool.getResource()) { jedis.set("foo", "bar"); assertEquals("bar", jedis.get("foo")); } pool.close(); assertTrue(pool.isClosed()); } @Test public void checkCloseableConnections() throws Exception { JedisPool pool = new JedisPool(endpoint.getHost(), endpoint.getPort(), endpoint.getUsername(), endpoint.getPassword()); try (Jedis jedis = pool.getResource()) { jedis.set("foo", "bar"); assertEquals("bar", jedis.get("foo")); } pool.close(); assertTrue(pool.isClosed()); } @Test public void checkResourceIsClosableAndReusable() { GenericObjectPoolConfig config = new GenericObjectPoolConfig<>(); config.setMaxTotal(1); config.setBlockWhenExhausted(false); try (JedisPool pool = new JedisPool(config, endpoint.getHost(), endpoint.getPort(), Protocol.DEFAULT_TIMEOUT, Protocol.DEFAULT_TIMEOUT, 0 /* infinite */, endpoint.getUsername(), endpoint.getPassword(), Protocol.DEFAULT_DATABASE, "closable-reusable-pool", false, null, null, null)) { Jedis jedis = pool.getResource(); jedis.set("hello", "jedis"); jedis.close(); Jedis jedis2 = pool.getResource(); assertEquals(jedis, jedis2); assertEquals("jedis", jedis2.get("hello")); jedis2.close(); } } @Test public void checkResourceWithConfigIsClosableAndReusable() { GenericObjectPoolConfig config = new GenericObjectPoolConfig<>(); config.setMaxTotal(1); config.setBlockWhenExhausted(false); try (JedisPool pool = new JedisPool(config, endpoint.getHostAndPort(), endpoint.getClientConfigBuilder().clientName("closable-reusable-pool") .build())) { Jedis jedis = pool.getResource(); jedis.set("hello", "jedis"); jedis.close(); Jedis jedis2 = pool.getResource(); assertEquals(jedis, jedis2); assertEquals("jedis", jedis2.get("hello")); assertEquals("closable-reusable-pool", jedis2.clientGetname()); jedis2.close(); } } @Test public void checkPoolRepairedWhenJedisIsBroken() { JedisPool pool = new JedisPool(new JedisPoolConfig(), endpoint.getHost(), endpoint.getPort(), Protocol.DEFAULT_TIMEOUT, Protocol.DEFAULT_TIMEOUT, 0 /* infinite */, endpoint.getUsername(), endpoint.getPassword(), Protocol.DEFAULT_DATABASE, "repairable-pool"); try (Jedis jedis = pool.getResource()) { jedis.set("foo", "0"); jedis.disconnect(); } try (Jedis jedis = pool.getResource()) { jedis.incr("foo"); } pool.close(); assertTrue(pool.isClosed()); } @Test public void checkPoolOverflow() { GenericObjectPoolConfig config = new GenericObjectPoolConfig<>(); config.setMaxTotal(1); config.setBlockWhenExhausted(false); try (JedisPool pool = new JedisPool(config, endpoint.getHost(), endpoint.getPort()); Jedis jedis = pool.getResource()) { jedis.auth(endpoint.getUsername(), endpoint.getPassword()); assertThrows(JedisException.class, () -> { try (Jedis jedis2 = pool.getResource()) { jedis2.auth(endpoint.getUsername(), endpoint.getPassword()); } }); } } @Test public void securePool() { JedisPoolConfig config = new JedisPoolConfig(); config.setTestOnBorrow(true); JedisPool pool = new JedisPool(config, endpoint.getHost(), endpoint.getPort(), 2000, endpoint.getUsername(), endpoint.getPassword()); try (Jedis jedis = pool.getResource()) { jedis.set("foo", "bar"); } pool.close(); assertTrue(pool.isClosed()); } @Test public void securePoolNonSSL() { JedisPoolConfig config = new JedisPoolConfig(); config.setTestOnBorrow(true); JedisPool pool = new JedisPool(config, endpoint.getHost(), endpoint.getPort(), 2000, endpoint.getUsername(), endpoint.getPassword(), false); try (Jedis jedis = pool.getResource()) { jedis.set("foo", "bar"); } pool.close(); assertTrue(pool.isClosed()); } @Test public void nonDefaultDatabase() { try (JedisPool pool0 = new JedisPool(new JedisPoolConfig(), endpoint.getHost(), endpoint.getPort(), 2000, endpoint.getUsername(), endpoint.getPassword()); Jedis jedis0 = pool0.getResource()) { jedis0.set("foo", "bar"); assertEquals("bar", jedis0.get("foo")); } try (JedisPool pool1 = new JedisPool(new JedisPoolConfig(), endpoint.getHost(), endpoint.getPort(), 2000, endpoint.getUsername(), endpoint.getPassword(), 1); Jedis jedis1 = pool1.getResource()) { assertNull(jedis1.get("foo")); } } @Test public void startWithUrlString() { try (Jedis j = new Jedis(endpoint.getHost(), endpoint.getPort())) { j.auth(endpoint.getUsername(), endpoint.getPassword()); j.select(2); j.set("foo", "bar"); } try (JedisPool pool = new JedisPool( endpoint.getURIBuilder().defaultCredentials().path("/2").build()); Jedis jedis = pool.getResource()) { assertEquals("bar", jedis.get("foo")); } } @Test public void startWithUrl() throws URISyntaxException { try (Jedis j = new Jedis(endpoint.getHost(), endpoint.getPort())) { j.auth(endpoint.getUsername(), endpoint.getPassword()); j.select(2); j.set("foo", "bar"); } try (JedisPool pool = new JedisPool( endpoint.getURIBuilder().defaultCredentials().path("/2").build()); Jedis jedis = pool.getResource()) { assertEquals("bar", jedis.get("foo")); } try (JedisPool pool = new JedisPool( endpointWithDefaultUser.getURIBuilder().defaultCredentials().path("/2").build()); Jedis jedis = pool.getResource()) { assertEquals("bar", jedis.get("foo")); } } @Test public void shouldThrowInvalidURIExceptionForInvalidURI() { assertThrows(InvalidURIException.class, () -> { new JedisPool(new URI("redis://localhost:")).close(); }); } @Test public void allowUrlWithNoDBAndNoPassword() throws URISyntaxException { new JedisPool(endpoint.getURI().toString()).close(); new JedisPool(endpoint.getURI()).close(); } @Test public void selectDatabaseOnActivation() { try (JedisPool pool = new JedisPool(new JedisPoolConfig(), endpoint.getHost(), endpoint.getPort(), 2000, endpoint.getUsername(), endpoint.getPassword())) { Jedis jedis0 = pool.getResource(); assertEquals(0, jedis0.getDB()); jedis0.select(1); assertEquals(1, jedis0.getDB()); jedis0.close(); Jedis jedis1 = pool.getResource(); assertSame(jedis1, jedis0); assertEquals(0, jedis1.getDB()); jedis1.close(); } } @Test public void customClientName() { try (JedisPool pool = new JedisPool(new JedisPoolConfig(), endpoint.getHost(), endpoint.getPort(), 2000, endpoint.getUsername(), endpoint.getPassword(), 0, "my_shiny_client_name"); Jedis jedis = pool.getResource()) { assertEquals("my_shiny_client_name", jedis.clientGetname()); } } @Test public void customClientNameNoSSL() { try (JedisPool pool0 = new JedisPool(new JedisPoolConfig(), endpoint.getHost(), endpoint.getPort(), 2000, endpoint.getUsername(), endpoint.getPassword(), 0, "my_shiny_client_name_no_ssl", false); Jedis jedis = pool0.getResource()) { assertEquals("my_shiny_client_name_no_ssl", jedis.clientGetname()); } } @Test public void testCloseConnectionOnMakeObject() { JedisPoolConfig config = new JedisPoolConfig(); config.setTestOnBorrow(true); try (JedisPool pool = new JedisPool(new JedisPoolConfig(), endpoint.getHost(), endpoint.getPort(), 2000, endpoint.getUsername(), "wrongpassword"); Jedis jedis = new Jedis(endpointWithDefaultUser.getURIBuilder() .credentials("", endpointWithDefaultUser.getPassword()).build())) { int currentClientCount = getClientCount(jedis.clientList()); assertThrows(JedisAccessControlException.class, pool::getResource); // wait for the redis server to close the connection await().pollDelay(Duration.ofMillis(10)).atMost(500, MILLISECONDS) .until(() -> getClientCount(jedis.clientList()) == currentClientCount); assertEquals(currentClientCount, getClientCount(jedis.clientList())); } } private int getClientCount(final String clientList) { return clientList.split("\n").length; } } ================================================ FILE: src/test/java/redis/clients/jedis/ACLJedisSentinelPoolTest.java ================================================ package redis.clients.jedis; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertSame; import static org.junit.jupiter.api.Assertions.assertThrows; import static org.junit.jupiter.api.Assertions.assertTrue; import java.util.HashSet; import java.util.Set; import java.util.stream.Collectors; import io.redis.test.annotations.SinceRedisVersion; import org.apache.commons.pool2.impl.GenericObjectPoolConfig; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Tag; import org.junit.jupiter.api.extension.RegisterExtension; import redis.clients.jedis.exceptions.JedisConnectionException; import redis.clients.jedis.exceptions.JedisException; import redis.clients.jedis.util.RedisVersionCondition; /** * This test class is mostly a copy of {@link JedisSentinelPoolTest}. *

* This tests are only executed when the server/cluster is Redis 6 or more. */ @SinceRedisVersion("6.0.0") @Tag("integration") public class ACLJedisSentinelPoolTest { private static final String MASTER_NAME = "aclmaster"; protected static HostAndPort sentinel1; protected Set sentinels = new HashSet<>(); @RegisterExtension public static RedisVersionCondition versionCondition = new RedisVersionCondition( () -> Endpoints.getRedisEndpoint("standalone0")); @BeforeAll public static void prepareEndpoint() { sentinel1 = Endpoints.getRedisEndpoint("sentinel-standalone0").getHostAndPort(); } @BeforeEach public void setUp() throws Exception { sentinels.clear(); sentinels.add(sentinel1); } private static Set toStrings(Set hostAndPorts) { return hostAndPorts.stream().map(HostAndPort::toString).collect(Collectors.toSet()); } @Test public void repeatedSentinelPoolInitialization() { for (int i = 0; i < 20; ++i) { GenericObjectPoolConfig config = new GenericObjectPoolConfig<>(); JedisSentinelPool pool = new JedisSentinelPool(MASTER_NAME, toStrings(sentinels), config, 1000, 1000, "acljedis", "fizzbuzz", 2, null, 1000, 1000, "sentinel", "foobared", null); pool.getResource().close(); pool.destroy(); } } @Test public void repeatedSentinelPoolInitializationWithConfig() { for (int i = 0; i < 20; ++i) { GenericObjectPoolConfig poolConfig = new GenericObjectPoolConfig<>(); JedisClientConfig masterConfig = DefaultJedisClientConfig.builder() .connectionTimeoutMillis(1000).socketTimeoutMillis(1000).database(2) .user("acljedis").password("fizzbuzz").build(); JedisClientConfig sentinelConfig = DefaultJedisClientConfig.builder() .connectionTimeoutMillis(1000).socketTimeoutMillis(1000) .user("sentinel").password("foobared").build(); JedisSentinelPool pool = new JedisSentinelPool(MASTER_NAME, sentinels, poolConfig, masterConfig, sentinelConfig); pool.getResource().close(); pool.destroy(); } } @Test public void initializeWithNotAvailableSentinelsShouldThrowException() { GenericObjectPoolConfig poolConfig = new GenericObjectPoolConfig<>(); JedisClientConfig masterConfig = DefaultJedisClientConfig.builder() .connectionTimeoutMillis(1000).socketTimeoutMillis(1000).database(2).user("acljedis") .password("fizzbuzz").build(); JedisClientConfig sentinelConfig = DefaultJedisClientConfig.builder() .connectionTimeoutMillis(1000).socketTimeoutMillis(1000).user("default") .password("wrongpassword").build(); assertThrows(JedisConnectionException.class, () -> { try (JedisSentinelPool ignored = new JedisSentinelPool(MASTER_NAME, sentinels, poolConfig, masterConfig, sentinelConfig)) { // do nothing } }); } @Test public void initializeWithNotMonitoredMasterNameShouldThrowException() { GenericObjectPoolConfig poolConfig = new GenericObjectPoolConfig<>(); JedisClientConfig masterConfig = DefaultJedisClientConfig.builder() .connectionTimeoutMillis(1000).socketTimeoutMillis(1000).database(2).user("acljedis") .password("fizzbuzz").build(); JedisClientConfig sentinelConfig = DefaultJedisClientConfig.builder() .connectionTimeoutMillis(1000).socketTimeoutMillis(1000).user("sentinel") .password("foobared").build(); assertThrows(JedisException.class, () -> { try (JedisSentinelPool ignored = new JedisSentinelPool("wrongMasterName", sentinels, poolConfig, masterConfig, sentinelConfig)) { // do nothing } }); } @Test public void checkCloseableConnections() throws Exception { GenericObjectPoolConfig config = new GenericObjectPoolConfig<>(); JedisSentinelPool pool = new JedisSentinelPool(MASTER_NAME, toStrings(sentinels), config, 1000, 1000, "acljedis", "fizzbuzz", 2, null, 1000, 1000, "sentinel", "foobared", null); try (Jedis jedis = pool.getResource()) { jedis.set("foo", "bar"); assertEquals("bar", jedis.get("foo")); } pool.close(); assertTrue(pool.isClosed()); } @Test public void returnResourceShouldResetState() { GenericObjectPoolConfig config = new GenericObjectPoolConfig<>(); config.setMaxTotal(1); config.setBlockWhenExhausted(false); try (JedisSentinelPool pool = new JedisSentinelPool(MASTER_NAME, toStrings(sentinels), config, 1000, 1000, "acljedis", "fizzbuzz", 2, null, 1000, 1000, "sentinel", "foobared", null)) { Jedis jedis; try (Jedis jedis1 = pool.getResource()) { jedis = jedis1; jedis1.set("hello", "jedis"); Transaction t = jedis1.multi(); t.set("hello", "world"); } try (Jedis jedis2 = pool.getResource()) { assertSame(jedis, jedis2); assertEquals("jedis", jedis2.get("hello")); } } } @Test public void checkResourceIsCloseable() { GenericObjectPoolConfig config = new GenericObjectPoolConfig<>(); config.setMaxTotal(1); config.setBlockWhenExhausted(false); try (JedisSentinelPool pool = new JedisSentinelPool(MASTER_NAME, toStrings(sentinels), config, 1000, 1000, "acljedis", "fizzbuzz", 2, null, 1000, 1000, "sentinel", "foobared", null)) { Jedis jedis; try (Jedis jedis1 = pool.getResource()) { jedis = jedis1; jedis1.set("hello", "jedis"); } try (Jedis jedis2 = pool.getResource()) { assertEquals(jedis, jedis2); } } } @Test public void customClientName() { GenericObjectPoolConfig config = new GenericObjectPoolConfig<>(); config.setMaxTotal(1); config.setBlockWhenExhausted(false); JedisSentinelPool pool = new JedisSentinelPool(MASTER_NAME, toStrings(sentinels), config, 1000, 1000, "acljedis", "fizzbuzz", 0, "my_shiny_master_client", 1000, 1000, "sentinel", "foobared", "my_shiny_sentinel_client"); try (Jedis jedis = pool.getResource()) { assertEquals("my_shiny_master_client", jedis.clientGetname()); } finally { pool.close(); } assertTrue(pool.isClosed()); } } ================================================ FILE: src/test/java/redis/clients/jedis/ACLJedisTest.java ================================================ package redis.clients.jedis; import static org.junit.jupiter.api.Assumptions.assumeTrue; import static org.junit.jupiter.api.Assertions.assertEquals; import static redis.clients.jedis.util.RedisVersionUtil.getRedisVersion; import io.redis.test.annotations.ConditionalOnEnv; import io.redis.test.utils.RedisVersion; import java.net.URISyntaxException; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.Test; import org.junit.jupiter.params.ParameterizedClass; import org.junit.jupiter.params.provider.MethodSource; import redis.clients.jedis.commands.jedis.JedisCommandsTestBase; import redis.clients.jedis.util.TestEnvUtil; /** * This test class is a copy of {@link JedisTest}. *

* This test is only executed when the server/cluster is Redis 6. or more. */ @ParameterizedClass @MethodSource("redis.clients.jedis.commands.CommandsTestsParameters#respVersions") public class ACLJedisTest extends JedisCommandsTestBase { protected static EndpointConfig endpoint; /** * Use to check if the ACL test should be ran. ACL are available only in 6.0 and later */ @BeforeAll public static void prepare() { endpoint = Endpoints.getRedisEndpoint("standalone0-acl"); assumeTrue(getRedisVersion(endpoint).isGreaterThanOrEqualTo(RedisVersion.of("6.0.0")), "Not running ACL test on this version of Redis"); } public ACLJedisTest(RedisProtocol redisProtocol) { super(redisProtocol); } @Test public void useWithoutConnecting() { try (Jedis j = new Jedis(endpoint.getHostAndPort())) { assertEquals("OK", j.auth(endpoint.getUsername(), endpoint.getPassword())); j.dbSize(); } } @Test public void connectWithConfig() { try (Jedis jedis = new Jedis(endpoint.getHostAndPort(), DefaultJedisClientConfig.builder().build())) { jedis.auth(endpoint.getUsername(), endpoint.getPassword()); assertEquals("PONG", jedis.ping()); } try (Jedis jedis = new Jedis(endpoint.getHostAndPort(), endpoint.getClientConfigBuilder().build())) { assertEquals("PONG", jedis.ping()); } } @Test public void connectWithConfigInterface() { try (Jedis jedis = new Jedis(endpoint.getHostAndPort(), new JedisClientConfig() { })) { jedis.auth(endpoint.getUsername(), endpoint.getPassword()); assertEquals("PONG", jedis.ping()); } try (Jedis jedis = new Jedis(endpoint.getHostAndPort(), new JedisClientConfig() { @Override public String getUser() { return endpoint.getUsername(); } @Override public String getPassword() { return endpoint.getPassword(); } })) { assertEquals("PONG", jedis.ping()); } } @Test @ConditionalOnEnv(value = TestEnvUtil.ENV_REDIS_ENTERPRISE, enabled = false) public void startWithUrl() { try (Jedis j = new Jedis(endpoint.getHostAndPort())) { assertEquals("OK", j.auth(endpoint.getUsername(), endpoint.getPassword())); assertEquals("OK", j.select(2)); j.set("foo", "bar"); } try (Jedis j2 = new Jedis( endpoint.getURIBuilder().defaultCredentials().path("/2").build().toString())) { assertEquals("PONG", j2.ping()); assertEquals("bar", j2.get("foo")); } } @Test @ConditionalOnEnv(value = TestEnvUtil.ENV_REDIS_ENTERPRISE, enabled = false) public void startWithUri() throws URISyntaxException { try (Jedis j = new Jedis(endpoint.getHostAndPort())) { assertEquals("OK", j.auth(endpoint.getUsername(), endpoint.getPassword())); assertEquals("OK", j.select(2)); j.set("foo", "bar"); } try (Jedis j1 = new Jedis(endpoint.getURIBuilder().defaultCredentials().path("/2").build())) { assertEquals("PONG", j1.ping()); assertEquals("bar", j1.get("foo")); } try (Jedis j2 = new Jedis(endpoint.getURIBuilder().defaultCredentials().path("/2").build())) { assertEquals("PONG", j2.ping()); assertEquals("bar", j2.get("foo")); } } } ================================================ FILE: src/test/java/redis/clients/jedis/BuilderTest.java ================================================ package redis.clients.jedis; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; import redis.clients.jedis.resps.StreamEntry; import redis.clients.jedis.resps.StreamEntryBinary; import redis.clients.jedis.util.RedisInputStream; import java.io.ByteArrayInputStream; import java.io.InputStream; import java.util.ArrayList; import java.util.List; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertNotNull; import static org.junit.jupiter.api.Assertions.assertTrue; public class BuilderTest { // Helper methods for building test data from RESP protocol strings private static Object parseRespResponse(String respResponse) { InputStream is = new ByteArrayInputStream(respResponse.getBytes()); return Protocol.read(new RedisInputStream(is)); } @SuppressWarnings("unchecked") private static ArrayList createStreamEntryData(String id, String fieldKey, String fieldValue, long millisElapsedFromDelivery, long deliveredCount) { String respResponse = "*4\r\n" + // Entry with 4 elements "$" + id.length() + "\r\n" + id + "\r\n" + // Entry ID "*2\r\n" + // 2 field-value pairs "$" + fieldKey.length() + "\r\n" + fieldKey + "\r\n" + // Field key "$" + fieldValue.length() + "\r\n" + fieldValue + "\r\n" + // Field value ":" + millisElapsedFromDelivery + "\r\n" + // millisElapsedFromDelivery ":" + deliveredCount + "\r\n"; // deliveredCount return (ArrayList) parseRespResponse(respResponse); } @SuppressWarnings("unchecked") private static ArrayList createStreamEntryBinaryData(String id, String fieldKey, byte[] fieldValue, long millisElapsedFromDelivery, long deliveredCount) { // For binary data, we need to construct the RESP response with the actual byte length String respResponse = "*4\r\n" + // Entry with 4 elements "$" + id.length() + "\r\n" + id + "\r\n" + // Entry ID "*2\r\n" + // 2 field-value pairs "$" + fieldKey.length() + "\r\n" + fieldKey + "\r\n" + // Field key "$" + fieldValue.length + "\r\n"; // Field value length // Manually construct the byte array with binary field value byte[] respBytes = respResponse.getBytes(); byte[] crLf = "\r\n".getBytes(); byte[] metadataBytes = (":" + millisElapsedFromDelivery + "\r\n" + ":" + deliveredCount + "\r\n").getBytes(); byte[] fullResponse = new byte[respBytes.length + fieldValue.length + crLf.length + metadataBytes.length]; System.arraycopy(respBytes, 0, fullResponse, 0, respBytes.length); System.arraycopy(fieldValue, 0, fullResponse, respBytes.length, fieldValue.length); System.arraycopy(crLf, 0, fullResponse, respBytes.length + fieldValue.length, crLf.length); System.arraycopy(metadataBytes, 0, fullResponse, respBytes.length + fieldValue.length + crLf.length, metadataBytes.length); InputStream is = new ByteArrayInputStream(fullResponse); return (ArrayList) Protocol.read(new RedisInputStream(is)); } @Test public void buildDouble() { Double build = BuilderFactory.DOUBLE.build("1.0".getBytes()); assertEquals(Double.valueOf(1.0), build); build = BuilderFactory.DOUBLE.build("inf".getBytes()); assertEquals(Double.valueOf(Double.POSITIVE_INFINITY), build); build = BuilderFactory.DOUBLE.build("+inf".getBytes()); assertEquals(Double.valueOf(Double.POSITIVE_INFINITY), build); build = BuilderFactory.DOUBLE.build("-inf".getBytes()); assertEquals(Double.valueOf(Double.NEGATIVE_INFINITY), build); try { BuilderFactory.DOUBLE.build("".getBytes()); Assertions.fail("Empty String should throw NumberFormatException."); } catch (NumberFormatException expected) { assertEquals("empty String", expected.getMessage()); } } @Test public void buildStreamEntryListWithClaimedEntryMetadata() { // Simulate Redis response for a single claimed entry with metadata // Format: [[id, [field, value], msSinceLastDelivery, redeliveryCount]] List data = new ArrayList<>(); data.add(createStreamEntryData("1234-12", "key", "value", 5000L, 2L)); List result = BuilderFactory.STREAM_ENTRY_LIST.build(data); assertNotNull(result); assertEquals(1, result.size()); StreamEntry streamEntry = result.get(0); assertEquals("1234-12", streamEntry.getID().toString()); assertEquals("value", streamEntry.getFields().get("key")); assertEquals(Long.valueOf(5000), streamEntry.getMillisElapsedFromDelivery()); assertEquals(Long.valueOf(2), streamEntry.getDeliveredCount()); } @Test public void buildStreamEntryListWithFreshEntryZeroRedeliveries() { // Simulate Redis response for a fresh entry (not claimed from PEL) // Format: [[id, [field, value], 0, 0]] List data = new ArrayList<>(); data.add(createStreamEntryData("1234-12", "key", "value", 1000L, 0L)); List result = BuilderFactory.STREAM_ENTRY_LIST.build(data); assertNotNull(result); assertEquals(1, result.size()); StreamEntry streamEntry = result.get(0); assertEquals("1234-12", streamEntry.getID().toString()); assertEquals(Long.valueOf(1000), streamEntry.getMillisElapsedFromDelivery()); assertEquals(Long.valueOf(0), streamEntry.getDeliveredCount()); } @Test public void buildStreamEntryListWithMixedBatchClaimedFirstThenFresh() { // Simulate Redis response with mixed entries: claimed entries first, then fresh entries List data = new ArrayList<>(); // Entry #1 (claimed, redeliveryCount=2) data.add(createStreamEntryData("1-0", "f1", "v1", 1500L, 2L)); // Entry #2 (claimed, redeliveryCount=1) data.add(createStreamEntryData("2-0", "f2", "v2", 1200L, 1L)); // Entry #3 (fresh, redeliveryCount=0) data.add(createStreamEntryData("3-0", "f3", "v3", 10L, 0L)); List result = BuilderFactory.STREAM_ENTRY_LIST.build(data); assertNotNull(result); assertEquals(3, result.size()); StreamEntry m1 = result.get(0); StreamEntry m2 = result.get(1); StreamEntry m3 = result.get(2); // Verify claimed entries assertTrue(m1.getDeliveredCount() > 0); assertTrue(m2.getDeliveredCount() > 0); assertEquals(Long.valueOf(2), m1.getDeliveredCount()); assertEquals(Long.valueOf(1), m2.getDeliveredCount()); // Verify fresh entry assertEquals(Long.valueOf(0), m3.getDeliveredCount()); } @Test public void buildStreamEntryBinaryListWithClaimedEntryMetadata() { // Test binary version with claimed entry metadata List data = new ArrayList<>(); data.add( createStreamEntryBinaryData("1234-12", "key", new byte[] { 0x00, 0x01, 0x02 }, 5000L, 2L)); List result = BuilderFactory.STREAM_ENTRY_BINARY_LIST.build(data); assertNotNull(result); assertEquals(1, result.size()); StreamEntryBinary streamEntry = result.get(0); assertEquals("1234-12", streamEntry.getID().toString()); assertEquals(Long.valueOf(5000), streamEntry.getMillisElapsedFromDelivery()); assertEquals(Long.valueOf(2), streamEntry.getDeliveredCount()); } } ================================================ FILE: src/test/java/redis/clients/jedis/ClusterCommandObjectsTest.java ================================================ package redis.clients.jedis; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertNotNull; import static org.junit.jupiter.api.Assertions.assertThrows; import static org.junit.jupiter.api.Assertions.assertTrue; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import redis.clients.jedis.args.Rawable; import redis.clients.jedis.params.SetParams; import redis.clients.jedis.util.JedisClusterCRC16; import redis.clients.jedis.util.SafeEncoder; /** * Unit tests for ClusterCommandObjects class. */ public class ClusterCommandObjectsTest { private ClusterCommandObjects clusterCommandObjects; @BeforeEach public void setUp() { clusterCommandObjects = new ClusterCommandObjects(); } @Test public void testGroupArgumentsByKeyValueHashSlot_singleSlot() { String[] keysValues = { "{user}:1", "value1", "{user}:2", "value2", "{user}:3", "value3" }; CommandArguments args = new CommandArguments(Protocol.Command.MSET); List result = clusterCommandObjects.groupArgumentsByKeyValueHashSlot(args, keysValues, null); assertEquals(1, result.size()); assertEquals(Protocol.Command.MSET, result.get(0).getCommand()); List argsStrings = extractArgsAsStrings(result.get(0)); assertTrue(argsStrings.contains("{user}:1")); assertTrue(argsStrings.contains("value1")); assertTrue(argsStrings.contains("{user}:2")); assertTrue(argsStrings.contains("value2")); } @Test public void testGroupArgumentsByKeyValueHashSlot_multipleSlots() { String[] keysValues = { "key1", "value1", "key2", "value2", "key3", "value3" }; CommandArguments args = new CommandArguments(Protocol.Command.MSET); List result = clusterCommandObjects.groupArgumentsByKeyValueHashSlot(args, keysValues, null); Map> expectedSlots = new HashMap<>(); for (int i = 0; i < keysValues.length; i += 2) { int slot = JedisClusterCRC16.getSlot(keysValues[i]); expectedSlots.computeIfAbsent(slot, k -> new ArrayList<>()).add(keysValues[i]); expectedSlots.get(slot).add(keysValues[i + 1]); } assertEquals(expectedSlots.size(), result.size()); for (CommandArguments cmdArgs : result) { assertEquals(Protocol.Command.MSET, cmdArgs.getCommand()); } } @Test public void testGroupArgumentsByKeyValueHashSlot_withParams() { String[] keysValues = { "{test}:key1", "value1", "{test}:key2", "value2" }; CommandArguments args = new CommandArguments(Protocol.Command.SET); SetParams params = SetParams.setParams().ex(100); List result = clusterCommandObjects.groupArgumentsByKeyValueHashSlot(args, keysValues, params); assertEquals(1, result.size()); List argsStrings = extractArgsAsStrings(result.get(0)); assertTrue(argsStrings.stream().anyMatch(s -> s.equalsIgnoreCase("EX"))); } @Test public void testGroupArgumentsByKeyValueHashSlot_emptyKeysValues() { String[] keysValues = {}; CommandArguments args = new CommandArguments(Protocol.Command.MSET); List result = clusterCommandObjects.groupArgumentsByKeyValueHashSlot(args, keysValues, null); assertEquals(0, result.size()); } @Test public void testGroupArgumentsByKeyValueHashSlot_oddNumberOfElements() { String[] keysValues = { "key1", "value1", "key2" }; CommandArguments args = new CommandArguments(Protocol.Command.MSET); assertThrows(IllegalArgumentException.class, () -> clusterCommandObjects.groupArgumentsByKeyValueHashSlot(args, keysValues, null)); } @Test public void testGroupArgumentsByKeyValueHashSlot_nullParams() { String[] keysValues = { "{slot}:key1", "value1" }; CommandArguments args = new CommandArguments(Protocol.Command.MSET); List result = clusterCommandObjects.groupArgumentsByKeyValueHashSlot(args, keysValues, null); assertNotNull(result); assertEquals(1, result.size()); } // Tests for byte[] version @Test public void testGroupArgumentsByKeyValueHashSlotBinary_singleSlot() { byte[][] keysValues = { "{user}:1".getBytes(), "value1".getBytes(), "{user}:2".getBytes(), "value2".getBytes(), "{user}:3".getBytes(), "value3".getBytes() }; CommandArguments args = new CommandArguments(Protocol.Command.MSET); List result = clusterCommandObjects.groupArgumentsByKeyValueHashSlot(args, keysValues, null); assertEquals(1, result.size()); assertEquals(Protocol.Command.MSET, result.get(0).getCommand()); List argsStrings = extractArgsAsStrings(result.get(0)); assertTrue(argsStrings.contains("{user}:1")); assertTrue(argsStrings.contains("value1")); } @Test public void testGroupArgumentsByKeyValueHashSlotBinary_multipleSlots() { byte[][] keysValues = { "key1".getBytes(), "value1".getBytes(), "key2".getBytes(), "value2".getBytes(), "key3".getBytes(), "value3".getBytes() }; CommandArguments args = new CommandArguments(Protocol.Command.MSET); List result = clusterCommandObjects.groupArgumentsByKeyValueHashSlot(args, keysValues, null); Map> expectedSlots = new HashMap<>(); for (int i = 0; i < keysValues.length; i += 2) { int slot = JedisClusterCRC16.getSlot(keysValues[i]); expectedSlots.computeIfAbsent(slot, k -> new ArrayList<>()).add(keysValues[i]); } assertEquals(expectedSlots.size(), result.size()); } @Test public void testGroupArgumentsByKeyValueHashSlotBinary_emptyKeysValues() { byte[][] keysValues = {}; CommandArguments args = new CommandArguments(Protocol.Command.MSET); List result = clusterCommandObjects.groupArgumentsByKeyValueHashSlot(args, keysValues, null); assertEquals(0, result.size()); } @Test public void testGroupArgumentsByKeyValueHashSlotBinary_oddNumberOfElements() { byte[][] keysValues = { "key1".getBytes(), "value1".getBytes(), "key2".getBytes() }; CommandArguments args = new CommandArguments(Protocol.Command.MSET); assertThrows(IllegalArgumentException.class, () -> clusterCommandObjects.groupArgumentsByKeyValueHashSlot(args, keysValues, null)); } // Tests for groupArgumentsByKeyHashSlot (String version) @Test public void testGroupArgumentsByKeyHashSlot_singleSlot() { String[] keys = { "{user}:1", "{user}:2", "{user}:3" }; CommandArguments args = new CommandArguments(Protocol.Command.DEL); List result = clusterCommandObjects.groupArgumentsByKeyHashSlot(args, keys, null); assertEquals(1, result.size()); assertEquals(Protocol.Command.DEL, result.get(0).getCommand()); List argsStrings = extractArgsAsStrings(result.get(0)); assertTrue(argsStrings.contains("{user}:1")); assertTrue(argsStrings.contains("{user}:2")); assertTrue(argsStrings.contains("{user}:3")); } @Test public void testGroupArgumentsByKeyHashSlot_multipleSlots() { String[] keys = { "key1", "key2", "key3" }; CommandArguments args = new CommandArguments(Protocol.Command.DEL); List result = clusterCommandObjects.groupArgumentsByKeyHashSlot(args, keys, null); Map> expectedSlots = new HashMap<>(); for (String key : keys) { int slot = JedisClusterCRC16.getSlot(key); expectedSlots.computeIfAbsent(slot, k -> new ArrayList<>()).add(key); } assertEquals(expectedSlots.size(), result.size()); for (CommandArguments cmdArgs : result) { assertEquals(Protocol.Command.DEL, cmdArgs.getCommand()); } } @Test public void testGroupArgumentsByKeyHashSlot_withParams() { String[] keys = { "{test}:key1", "{test}:key2" }; CommandArguments args = new CommandArguments(Protocol.Command.EXISTS); SetParams params = SetParams.setParams().ex(100); List result = clusterCommandObjects.groupArgumentsByKeyHashSlot(args, keys, params); assertEquals(1, result.size()); List argsStrings = extractArgsAsStrings(result.get(0)); assertTrue(argsStrings.stream().anyMatch(s -> s.equalsIgnoreCase("EX"))); } @Test public void testGroupArgumentsByKeyHashSlot_emptyKeys() { String[] keys = {}; CommandArguments args = new CommandArguments(Protocol.Command.DEL); List result = clusterCommandObjects.groupArgumentsByKeyHashSlot(args, keys, null); assertEquals(0, result.size()); } @Test public void testGroupArgumentsByKeyHashSlot_singleKey() { String[] keys = { "singleKey" }; CommandArguments args = new CommandArguments(Protocol.Command.DEL); List result = clusterCommandObjects.groupArgumentsByKeyHashSlot(args, keys, null); assertEquals(1, result.size()); List argsStrings = extractArgsAsStrings(result.get(0)); assertTrue(argsStrings.contains("singleKey")); } @Test public void testGroupArgumentsByKeyHashSlot_nullParams() { String[] keys = { "{slot}:key1" }; CommandArguments args = new CommandArguments(Protocol.Command.DEL); List result = clusterCommandObjects.groupArgumentsByKeyHashSlot(args, keys, null); assertNotNull(result); assertEquals(1, result.size()); } @Test public void testGroupArgumentsByKeyHashSlot_noValuesIncluded() { // Verify that key-only method doesn't include any extra elements String[] keys = { "{user}:1", "{user}:2" }; CommandArguments args = new CommandArguments(Protocol.Command.MGET); List result = clusterCommandObjects.groupArgumentsByKeyHashSlot(args, keys, null); assertEquals(1, result.size()); List argsStrings = extractArgsAsStrings(result.get(0)); // Should contain the command (MGET) and the 2 keys, no values assertEquals(3, argsStrings.size()); assertTrue(argsStrings.contains("{user}:1")); assertTrue(argsStrings.contains("{user}:2")); } // Tests for groupArgumentsByKeyHashSlot (byte[] version) @Test public void testGroupArgumentsByKeyHashSlotBinary_singleSlot() { byte[][] keys = { "{user}:1".getBytes(), "{user}:2".getBytes(), "{user}:3".getBytes() }; CommandArguments args = new CommandArguments(Protocol.Command.DEL); List result = clusterCommandObjects.groupArgumentsByKeyHashSlot(args, keys, null); assertEquals(1, result.size()); assertEquals(Protocol.Command.DEL, result.get(0).getCommand()); List argsStrings = extractArgsAsStrings(result.get(0)); assertTrue(argsStrings.contains("{user}:1")); assertTrue(argsStrings.contains("{user}:2")); assertTrue(argsStrings.contains("{user}:3")); } @Test public void testGroupArgumentsByKeyHashSlotBinary_multipleSlots() { byte[][] keys = { "key1".getBytes(), "key2".getBytes(), "key3".getBytes() }; CommandArguments args = new CommandArguments(Protocol.Command.DEL); List result = clusterCommandObjects.groupArgumentsByKeyHashSlot(args, keys, null); Map> expectedSlots = new HashMap<>(); for (byte[] key : keys) { int slot = JedisClusterCRC16.getSlot(key); expectedSlots.computeIfAbsent(slot, k -> new ArrayList<>()).add(key); } assertEquals(expectedSlots.size(), result.size()); } @Test public void testGroupArgumentsByKeyHashSlotBinary_emptyKeys() { byte[][] keys = {}; CommandArguments args = new CommandArguments(Protocol.Command.DEL); List result = clusterCommandObjects.groupArgumentsByKeyHashSlot(args, keys, null); assertEquals(0, result.size()); } @Test public void testGroupArgumentsByKeyHashSlotBinary_singleKey() { byte[][] keys = { "singleKey".getBytes() }; CommandArguments args = new CommandArguments(Protocol.Command.DEL); List result = clusterCommandObjects.groupArgumentsByKeyHashSlot(args, keys, null); assertEquals(1, result.size()); List argsStrings = extractArgsAsStrings(result.get(0)); assertTrue(argsStrings.contains("singleKey")); } @Test public void testGroupArgumentsByKeyHashSlotBinary_noValuesIncluded() { // Verify that key-only method doesn't include any extra elements byte[][] keys = { "{user}:1".getBytes(), "{user}:2".getBytes() }; CommandArguments args = new CommandArguments(Protocol.Command.MGET); List result = clusterCommandObjects.groupArgumentsByKeyHashSlot(args, keys, null); assertEquals(1, result.size()); List argsStrings = extractArgsAsStrings(result.get(0)); // Should contain the command (MGET) and the 2 keys, no values assertEquals(3, argsStrings.size()); assertTrue(argsStrings.contains("{user}:1")); assertTrue(argsStrings.contains("{user}:2")); } private List extractArgsAsStrings(CommandArguments cmdArgs) { List result = new ArrayList<>(); for (Rawable rawable : cmdArgs) { result.add(SafeEncoder.encode(rawable.getRaw())); } return result; } @Test public void testGetKeyHashSlots_withRawableFromByteArray() { CommandArguments args = new CommandArguments(Protocol.Command.GET); Rawable rawableKey = redis.clients.jedis.args.RawableFactory.from("testkey".getBytes()); // Using key() with a Rawable should store the Rawable object in the keys list args.key(rawableKey); // This should not throw an exception java.util.Set slots = args.getKeyHashSlots(); assertEquals(1, slots.size()); assertTrue(slots.contains(JedisClusterCRC16.getSlot("testkey".getBytes()))); } @Test public void testGetKeyHashSlots_withRawableFromString() { CommandArguments args = new CommandArguments(Protocol.Command.GET); Rawable rawableKey = redis.clients.jedis.args.RawableFactory.from("testkey"); args.key(rawableKey); // This should not throw an exception java.util.Set slots = args.getKeyHashSlots(); assertEquals(1, slots.size()); assertTrue(slots.contains(JedisClusterCRC16.getSlot("testkey"))); } @Test public void testGetKeyHashSlots_withMultipleRawableKeys() { CommandArguments args = new CommandArguments(Protocol.Command.MGET); Rawable rawableKey1 = redis.clients.jedis.args.RawableFactory.from("{user}:1".getBytes()); Rawable rawableKey2 = redis.clients.jedis.args.RawableFactory.from("{user}:2".getBytes()); args.key(rawableKey1); args.key(rawableKey2); // This should not throw an exception java.util.Set slots = args.getKeyHashSlots(); assertEquals(1, slots.size()); assertTrue(slots.contains(JedisClusterCRC16.getSlot("{user}:1"))); } @Test public void testGetKeyHashSlots_withMixedRawableAndStringKeys() { CommandArguments args = new CommandArguments(Protocol.Command.MGET); // First add a String key - this works fine args.key("stringKey"); // Then add a Rawable key Rawable rawableKey = redis.clients.jedis.args.RawableFactory.from("rawableKey".getBytes()); args.key(rawableKey); // This should not throw an exception java.util.Set slots = args.getKeyHashSlots(); assertEquals(2, slots.size()); } /** * Test that using a mix of Rawable and byte[] keys throws ClassCastException when computing hash * slots. */ @Test public void testGetKeyHashSlots_withMixedRawableAndByteArrayKeys() { CommandArguments args = new CommandArguments(Protocol.Command.MGET); // First add a byte[] key - this works fine args.key("byteKey".getBytes()); // Then add a Rawable key Rawable rawableKey = redis.clients.jedis.args.RawableFactory.from("rawableKey".getBytes()); args.key(rawableKey); // This should not throw an exception java.util.Set slots = args.getKeyHashSlots(); assertEquals(2, slots.size()); } /** * Verify that String keys work correctly (baseline test). */ @Test public void testGetKeyHashSlots_withStringKey_worksCorrectly() { CommandArguments args = new CommandArguments(Protocol.Command.GET); String key = "testkey"; args.key(key); // String keys should work correctly java.util.Set slots = args.getKeyHashSlots(); assertEquals(1, slots.size()); assertTrue(slots.contains(JedisClusterCRC16.getSlot(key))); } /** * Verify that byte[] keys work correctly (baseline test). */ @Test public void testGetKeyHashSlots_withByteArrayKey_worksCorrectly() { CommandArguments args = new CommandArguments(Protocol.Command.GET); byte[] key = "testkey".getBytes(); args.key(key); // byte[] keys should work correctly java.util.Set slots = args.getKeyHashSlots(); assertEquals(1, slots.size()); assertTrue(slots.contains(JedisClusterCRC16.getSlot(key))); } // ==================== Key Preprocessor Hash Slot Tests ==================== // These tests verify that the keyPreProcessor is properly accounted for when // calculating hash slots for multi-shard grouping. /** * Test that demonstrates the bug where groupArgumentsByKeyHashSlot calculates hash slots from the * original keys but the keyPreProcessor transforms them into different keys with different hash * slots. Bug: When a prefix like "{prefix}:" is added by keyPreProcessor, the original keys * "key1" and "key2" might hash to different slots, but after prefixing they become * "{prefix}:key1" and "{prefix}:key2" which hash to the same slot (due to the hash tag). The slot * calculation should use the preprocessed keys. */ @Test public void testGroupArgumentsByKeyHashSlot_withKeyPreProcessor_usesPreprocessedSlot() { // Create ClusterCommandObjects with a key preprocessor that adds a hash tag prefix ClusterCommandObjects clusterCmdObjects = new ClusterCommandObjects(); // The prefix "{sameSlot}:" ensures all keys hash to the same slot clusterCmdObjects.setKeyArgumentPreProcessor( new redis.clients.jedis.util.PrefixedKeyArgumentPreProcessor("{sameSlot}:")); // These keys hash to different slots without the prefix String[] keys = { "key1", "key2", "key3" }; int slot1 = JedisClusterCRC16.getSlot("key1"); int slot2 = JedisClusterCRC16.getSlot("key2"); int slot3 = JedisClusterCRC16.getSlot("key3"); // Verify the original keys hash to at least 2 different slots assertTrue(slot1 != slot2 || slot2 != slot3 || slot1 != slot3, "Test setup error: original keys should hash to different slots"); // After preprocessing, all keys should hash to the same slot because of the hash tag int expectedSlot = JedisClusterCRC16.getSlot("{sameSlot}:key1"); assertEquals(expectedSlot, JedisClusterCRC16.getSlot("{sameSlot}:key2"), "Preprocessed keys should hash to the same slot due to hash tag"); assertEquals(expectedSlot, JedisClusterCRC16.getSlot("{sameSlot}:key3"), "Preprocessed keys should hash to the same slot due to hash tag"); // Call groupArgumentsByKeyHashSlot CommandArguments args = new CommandArguments(Protocol.Command.DEL); List result = clusterCmdObjects.groupArgumentsByKeyHashSlot(args, keys, null); // BUG REPRODUCTION: With the bug, this would return multiple CommandArguments // because the slot calculation uses the original keys which hash to different slots. // The fix should make this return 1 CommandArguments because after preprocessing, // all keys hash to the same slot (due to the "{sameSlot}:" hash tag prefix). assertEquals(1, result.size(), "After preprocessing with hash tag prefix, all keys should group into one slot. " + "Got " + result.size() + " groups instead of 1, indicating slot calculation used original keys."); // Verify the preprocessed keys are in the result List argsStrings = extractArgsAsStrings(result.get(0)); assertTrue(argsStrings.contains("{sameSlot}:key1")); assertTrue(argsStrings.contains("{sameSlot}:key2")); assertTrue(argsStrings.contains("{sameSlot}:key3")); } /** * Test that groupArgumentsByKeyValueHashSlot also accounts for keyPreProcessor when calculating * hash slots for key-value pairs. */ @Test public void testGroupArgumentsByKeyValueHashSlot_withKeyPreProcessor_usesPreprocessedSlot() { ClusterCommandObjects clusterCmdObjects = new ClusterCommandObjects(); clusterCmdObjects.setKeyArgumentPreProcessor( new redis.clients.jedis.util.PrefixedKeyArgumentPreProcessor("{sameSlot}:")); // These key-value pairs have keys that hash to different slots without the prefix String[] keysValues = { "key1", "value1", "key2", "value2", "key3", "value3" }; // Verify original keys hash to different slots int slot1 = JedisClusterCRC16.getSlot("key1"); int slot2 = JedisClusterCRC16.getSlot("key2"); int slot3 = JedisClusterCRC16.getSlot("key3"); assertTrue(slot1 != slot2 || slot2 != slot3 || slot1 != slot3, "Test setup error: original keys should hash to different slots"); CommandArguments args = new CommandArguments(Protocol.Command.MSET); List result = clusterCmdObjects.groupArgumentsByKeyValueHashSlot(args, keysValues, null); // With the fix, all keys should group into one slot assertEquals(1, result.size(), "After preprocessing with hash tag prefix, all keys should group into one slot"); // Verify the preprocessed keys and values are in the result List argsStrings = extractArgsAsStrings(result.get(0)); assertTrue(argsStrings.contains("{sameSlot}:key1")); assertTrue(argsStrings.contains("value1")); assertTrue(argsStrings.contains("{sameSlot}:key2")); assertTrue(argsStrings.contains("value2")); assertTrue(argsStrings.contains("{sameSlot}:key3")); assertTrue(argsStrings.contains("value3")); } /** * Test the reverse case: keys that originally hash to the same slot but after preprocessing hash * to different slots. */ @Test public void testGroupArgumentsByKeyHashSlot_withKeyPreProcessor_differentSlotsAfterPreprocess() { ClusterCommandObjects clusterCmdObjects = new ClusterCommandObjects(); // This preprocessor changes the hash tag, causing keys to hash to different slots clusterCmdObjects.setKeyArgumentPreProcessor(key -> { String keyStr = (String) key; // Remove the hash tag, making keys hash based on their full content return keyStr.replace("{same}:", "different:"); }); // These keys all hash to the same slot due to the {same} hash tag String[] keys = { "{same}:key1", "{same}:key2", "{same}:key3" }; int originalSlot = JedisClusterCRC16.getSlot("{same}:key1"); assertEquals(originalSlot, JedisClusterCRC16.getSlot("{same}:key2")); assertEquals(originalSlot, JedisClusterCRC16.getSlot("{same}:key3")); // After preprocessing, keys become "different:key1", etc. which hash to different slots int slot1 = JedisClusterCRC16.getSlot("different:key1"); int slot2 = JedisClusterCRC16.getSlot("different:key2"); int slot3 = JedisClusterCRC16.getSlot("different:key3"); // At least some should be different (depending on actual hash values) assertTrue(slot1 != slot2 || slot2 != slot3 || slot1 != slot3, "Test setup: preprocessed keys should hash to different slots"); CommandArguments args = new CommandArguments(Protocol.Command.DEL); List result = clusterCmdObjects.groupArgumentsByKeyHashSlot(args, keys, null); // With the fix, keys should be grouped by their preprocessed slot values // which should result in multiple groups (not just 1 as with original keys) assertTrue(result.size() > 1, "After preprocessing removes hash tag, keys should be in different slots. " + "Got " + result.size() + " groups."); } /** * Test with byte[] keys and keyPreProcessor. */ @Test public void testGroupArgumentsByKeyHashSlot_withKeyPreProcessor_binaryKeys() { ClusterCommandObjects clusterCmdObjects = new ClusterCommandObjects(); clusterCmdObjects.setKeyArgumentPreProcessor( new redis.clients.jedis.util.PrefixedKeyArgumentPreProcessor("{sameSlot}:")); byte[][] keys = { "key1".getBytes(), "key2".getBytes(), "key3".getBytes() }; CommandArguments args = new CommandArguments(Protocol.Command.DEL); List result = clusterCmdObjects.groupArgumentsByKeyHashSlot(args, keys, null); // With the fix, all keys should group into one slot due to the hash tag prefix assertEquals(1, result.size(), "After preprocessing with hash tag prefix, all binary keys should group into one slot"); // Verify the preprocessed keys are in the result List argsStrings = extractArgsAsStrings(result.get(0)); assertTrue(argsStrings.contains("{sameSlot}:key1")); assertTrue(argsStrings.contains("{sameSlot}:key2")); assertTrue(argsStrings.contains("{sameSlot}:key3")); } } ================================================ FILE: src/test/java/redis/clients/jedis/ClusterPipeliningTest.java ================================================ package redis.clients.jedis; import static org.hamcrest.Matchers.contains; import static org.hamcrest.MatcherAssert.assertThat; import static org.junit.jupiter.api.Assertions.assertArrayEquals; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertNotNull; import static org.junit.jupiter.api.Assertions.assertNull; import static org.junit.jupiter.api.Assertions.assertThrows; import static org.junit.jupiter.api.Assertions.assertTrue; import static org.junit.jupiter.api.Assertions.fail; import static redis.clients.jedis.Protocol.CLUSTER_HASHSLOTS; import java.util.*; import org.hamcrest.MatcherAssert; import org.hamcrest.Matchers; import org.junit.jupiter.api.*; import redis.clients.jedis.args.*; import redis.clients.jedis.exceptions.JedisDataException; import redis.clients.jedis.params.*; import redis.clients.jedis.providers.ClusterConnectionProvider; import redis.clients.jedis.resps.GeoRadiusResponse; import redis.clients.jedis.resps.StreamEntry; import redis.clients.jedis.resps.Tuple; import redis.clients.jedis.util.AssertUtil; import redis.clients.jedis.util.GeoCoordinateMatcher; import redis.clients.jedis.util.GeoRadiusResponseMatcher; import redis.clients.jedis.util.JedisClusterTestUtil; import redis.clients.jedis.util.SafeEncoder; @Tag("integration") public class ClusterPipeliningTest { private static EndpointConfig endpoint; private static DefaultJedisClientConfig DEFAULT_CLIENT_CONFIG; private static Jedis node1; private static Jedis node2; private static Jedis node3; private static HostAndPort nodeInfo1; private static HostAndPort nodeInfo2; private static HostAndPort nodeInfo3; private static Set nodes; @BeforeAll public static void setUp() throws InterruptedException { endpoint = Endpoints.getRedisEndpoint("cluster-unbound"); DEFAULT_CLIENT_CONFIG = endpoint.getClientConfigBuilder().build(); nodeInfo1 = endpoint.getHostsAndPorts().get(0); nodeInfo2 = endpoint.getHostsAndPorts().get(1); nodeInfo3 = endpoint.getHostsAndPorts().get(2); nodes = new HashSet<>(Arrays.asList(nodeInfo1, nodeInfo2, nodeInfo3)); node1 = new Jedis(nodeInfo1); node1.auth(endpoint.getPassword()); node1.flushAll(); node2 = new Jedis(nodeInfo2); node2.auth(endpoint.getPassword()); node2.flushAll(); node3 = new Jedis(nodeInfo3); node3.auth(endpoint.getPassword()); node3.flushAll(); // add nodes to cluster node1.clusterMeet(nodeInfo2.getHost(), nodeInfo2.getPort()); node1.clusterMeet(nodeInfo2.getHost(), nodeInfo3.getPort()); // split available slots across the three nodes int slotsPerNode = CLUSTER_HASHSLOTS / 3; int[] node1Slots = new int[slotsPerNode]; int[] node2Slots = new int[slotsPerNode + 1]; int[] node3Slots = new int[slotsPerNode]; for (int i = 0, slot1 = 0, slot2 = 0, slot3 = 0; i < CLUSTER_HASHSLOTS; i++) { if (i < slotsPerNode) { node1Slots[slot1++] = i; } else if (i > slotsPerNode * 2) { node3Slots[slot3++] = i; } else { node2Slots[slot2++] = i; } } node1.clusterAddSlots(node1Slots); node2.clusterAddSlots(node2Slots); node3.clusterAddSlots(node3Slots); JedisClusterTestUtil.waitForClusterReady(node1, node2, node3); } @BeforeEach public void prepare() { node1.flushAll(); node2.flushAll(); node3.flushAll(); } @AfterEach public void cleanUp() { node1.flushDB(); node2.flushDB(); node3.flushDB(); } @AfterAll public static void tearDown() throws InterruptedException { if (node1 != null) node1.clusterReset(ClusterResetType.SOFT); if (node2 != null) node2.clusterReset(ClusterResetType.SOFT); if (node3 != null) node3.clusterReset(ClusterResetType.SOFT); } @Test public void constructorClientConfig() { try (ClusterPipeline pipe = new ClusterPipeline(nodes, DEFAULT_CLIENT_CONFIG)) { Response r1 = pipe.set("key1", "value1"); Response r2 = pipe.set("key2", "value2"); Response r3 = pipe.set("key3", "value3"); Response r4 = pipe.get("key1"); Response r5 = pipe.get("key2"); Response r6 = pipe.get("key3"); pipe.sync(); assertEquals("OK", r1.get()); assertEquals("OK", r2.get()); assertEquals("OK", r3.get()); assertEquals("value1", r4.get()); assertEquals("value2", r5.get()); assertEquals("value3", r6.get()); } } @Test public void constructorPoolConfig() { try (ClusterPipeline pipe = new ClusterPipeline(nodes, DEFAULT_CLIENT_CONFIG, new ConnectionPoolConfig())) { Response r1 = pipe.set("key1", "value1"); Response r2 = pipe.set("key2", "value2"); Response r3 = pipe.set("key3", "value3"); Response r4 = pipe.get("key1"); Response r5 = pipe.get("key2"); Response r6 = pipe.get("key3"); pipe.sync(); assertEquals("OK", r1.get()); assertEquals("OK", r2.get()); assertEquals("OK", r3.get()); assertEquals("value1", r4.get()); assertEquals("value2", r5.get()); assertEquals("value3", r6.get()); } } @Test public void constructorConnectionProvider() { try (ClusterConnectionProvider provider = new ClusterConnectionProvider(nodes, DEFAULT_CLIENT_CONFIG); ClusterPipeline pipeline = new ClusterPipeline(provider)) { Response r1 = pipeline.set("key1", "value1"); Response r2 = pipeline.set("key2", "value2"); Response r3 = pipeline.set("key3", "value3"); Response r4 = pipeline.get("key1"); Response r5 = pipeline.get("key2"); Response r6 = pipeline.get("key3"); pipeline.sync(); assertEquals("OK", r1.get()); assertEquals("OK", r2.get()); assertEquals("OK", r3.get()); assertEquals("value1", r4.get()); assertEquals("value2", r5.get()); assertEquals("value3", r6.get()); } } @Test public void clusterPipelined() { try (RedisClusterClient cluster = RedisClusterClient.builder().nodes(nodes).clientConfig(DEFAULT_CLIENT_CONFIG).build(); ClusterPipeline pipeline = cluster.pipelined()) { Response r1 = pipeline.set("key1", "value1"); Response r2 = pipeline.set("key2", "value2"); Response r3 = pipeline.set("key3", "value3"); Response r4 = pipeline.get("key1"); Response r5 = pipeline.get("key2"); Response r6 = pipeline.get("key3"); pipeline.sync(); assertEquals("OK", r1.get()); assertEquals("OK", r2.get()); assertEquals("OK", r3.get()); assertEquals("value1", r4.get()); assertEquals("value2", r5.get()); assertEquals("value3", r6.get()); } } @Test public void intermediateSync() { try (RedisClusterClient cluster = RedisClusterClient.builder().nodes(nodes).clientConfig(DEFAULT_CLIENT_CONFIG).build(); ClusterPipeline pipeline = cluster.pipelined()) { Response r1 = pipeline.set("key1", "value1"); Response r2 = pipeline.set("key2", "value2"); Response r3 = pipeline.set("key3", "value3"); pipeline.sync(); assertEquals("OK", r1.get()); assertEquals("OK", r2.get()); assertEquals("OK", r3.get()); Response r4 = pipeline.get("key1"); Response r5 = pipeline.get("key2"); Response r6 = pipeline.get("key3"); pipeline.sync(); assertEquals("value1", r4.get()); assertEquals("value2", r5.get()); assertEquals("value3", r6.get()); } } @Test public void intermediateSyncs() { try (RedisClusterClient cluster = RedisClusterClient.builder().nodes(nodes).clientConfig(DEFAULT_CLIENT_CONFIG).build(); ClusterPipeline pipeline = cluster.pipelined()) { Response r1 = pipeline.set("key1", "value1"); Response r2 = pipeline.set("key2", "value2"); Response r3 = pipeline.set("key3", "value3"); for (int i = 0; i < 100; i++) pipeline.sync(); assertEquals("OK", r1.get()); assertEquals("OK", r2.get()); assertEquals("OK", r3.get()); Response r4 = pipeline.get("key1"); Response r5 = pipeline.get("key2"); Response r6 = pipeline.get("key3"); for (int i = 0; i < 100; i++) pipeline.sync(); assertEquals("value1", r4.get()); assertEquals("value2", r5.get()); assertEquals("value3", r6.get()); } } @Test public void pipelineResponse() { try (RedisClusterClient jc = RedisClusterClient.builder().nodes(nodes).clientConfig(DEFAULT_CLIENT_CONFIG).build()) { jc.set("string", "foo"); jc.lpush("list", "foo"); jc.hset("hash", "foo", "bar"); jc.zadd("zset", 1, "foo"); jc.sadd("set", "foo"); jc.setrange("setrange", 0, "0123456789"); byte[] bytesForSetRange = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9}; jc.setrange("setrangebytes".getBytes(), 0, bytesForSetRange); } try (ClusterConnectionProvider provider = new ClusterConnectionProvider(nodes, DEFAULT_CLIENT_CONFIG)) { ClusterPipeline p = new ClusterPipeline(provider); Response string = p.get("string"); Response list = p.lpop("list"); Response hash = p.hget("hash", "foo"); Response> zset = p.zrange("zset", 0, -1); Response set = p.spop("set"); Response blist = p.exists("list"); Response zincrby = p.zincrby("zset", 1, "foo"); Response zcard = p.zcard("zset"); p.lpush("list", "bar"); Response> lrange = p.lrange("list", 0, -1); Response> hgetAll = p.hgetAll("hash"); p.sadd("set", "foo"); Response> smembers = p.smembers("set"); Response> zrangeWithScores = p.zrangeWithScores("zset", 0, -1); Response getrange = p.getrange("setrange", 1, 3); Response getrangeBytes = p.getrange("setrangebytes".getBytes(), 6, 8); p.sync(); assertEquals("foo", string.get()); assertEquals("foo", list.get()); assertEquals("bar", hash.get()); assertEquals("foo", zset.get().iterator().next()); assertEquals("foo", set.get()); assertEquals(false, blist.get()); assertEquals(Double.valueOf(2), zincrby.get()); assertEquals(Long.valueOf(1), zcard.get()); assertEquals(1, lrange.get().size()); assertNotNull(hgetAll.get().get("foo")); assertEquals(1, smembers.get().size()); assertEquals(1, zrangeWithScores.get().size()); assertEquals("123", getrange.get()); byte[] expectedGetRangeBytes = {6, 7, 8}; assertArrayEquals(expectedGetRangeBytes, getrangeBytes.get()); } } @Test public void pipelineBinarySafeHashCommands() { try (RedisClusterClient jc = RedisClusterClient.builder().nodes(nodes).clientConfig(DEFAULT_CLIENT_CONFIG).build()) { jc.hset("key".getBytes(), "f1".getBytes(), "v111".getBytes()); jc.hset("key".getBytes(), "f22".getBytes(), "v2222".getBytes()); } try (ClusterConnectionProvider provider = new ClusterConnectionProvider(nodes, DEFAULT_CLIENT_CONFIG)) { ClusterPipeline p = new ClusterPipeline(provider); Response> fmap = p.hgetAll("key".getBytes()); Response> fkeys = p.hkeys("key".getBytes()); Response> fordered = p.hmget("key".getBytes(), "f22".getBytes(), "f1".getBytes()); Response> fvals = p.hvals("key".getBytes()); p.sync(); assertNotNull(fmap.get()); // we have to do these strange contortions because byte[] is not a very good key for a java // Map. It only works with equality (you need the exact key object to retrieve the value). // I recommend we switch to using ByteBuffer or something similar: // http://stackoverflow.com/questions/1058149/using-a-byte-array-as-hashmap-key-java Map map = fmap.get(); Set mapKeys = map.keySet(); Iterator iterMap = mapKeys.iterator(); byte[] firstMapKey = iterMap.next(); byte[] secondMapKey = iterMap.next(); assertFalse(iterMap.hasNext()); verifyHasBothValues(firstMapKey, secondMapKey, "f1".getBytes(), "f22".getBytes()); byte[] firstMapValue = map.get(firstMapKey); byte[] secondMapValue = map.get(secondMapKey); verifyHasBothValues(firstMapValue, secondMapValue, "v111".getBytes(), "v2222".getBytes()); assertNotNull(fkeys.get()); Iterator iter = fkeys.get().iterator(); byte[] firstKey = iter.next(); byte[] secondKey = iter.next(); assertFalse(iter.hasNext()); verifyHasBothValues(firstKey, secondKey, "f1".getBytes(), "f22".getBytes()); assertNotNull(fordered.get()); assertArrayEquals("v2222".getBytes(), fordered.get().get(0)); assertArrayEquals("v111".getBytes(), fordered.get().get(1)); assertNotNull(fvals.get()); assertEquals(2, fvals.get().size()); byte[] firstValue = fvals.get().get(0); byte[] secondValue = fvals.get().get(1); verifyHasBothValues(firstValue, secondValue, "v111".getBytes(), "v2222".getBytes()); } } private void verifyHasBothValues(byte[] firstKey, byte[] secondKey, byte[] value1, byte[] value2) { assertFalse(Arrays.equals(firstKey, secondKey)); assertTrue(Arrays.equals(firstKey, value1) || Arrays.equals(firstKey, value2)); assertTrue(Arrays.equals(secondKey, value1) || Arrays.equals(secondKey, value2)); } @Test public void pipelineResponseWithinPipeline() { try (ClusterConnectionProvider provider = new ClusterConnectionProvider(nodes, DEFAULT_CLIENT_CONFIG)) { ClusterPipeline p = new ClusterPipeline(provider); Response string = p.get("string"); assertThrows(IllegalStateException.class,string::get); p.sync(); } } @Test public void pipelineWithPubSub() { try (ClusterConnectionProvider provider = new ClusterConnectionProvider(nodes, DEFAULT_CLIENT_CONFIG)) { ClusterPipeline pipelined = new ClusterPipeline(provider); Response p1 = pipelined.publish("foo", "bar"); Response p2 = pipelined.publish("foo".getBytes(), "bar".getBytes()); pipelined.sync(); assertEquals(0, p1.get().longValue()); assertEquals(0, p2.get().longValue()); } } @Test public void canRetrieveUnsetKey() { try (ClusterConnectionProvider provider = new ClusterConnectionProvider(nodes, DEFAULT_CLIENT_CONFIG)) { ClusterPipeline p = new ClusterPipeline(provider); Response shouldNotExist = p.get(UUID.randomUUID().toString()); p.sync(); assertNull(shouldNotExist.get()); } } @Test public void piplineWithError() { try (ClusterConnectionProvider provider = new ClusterConnectionProvider(nodes, DEFAULT_CLIENT_CONFIG)) { ClusterPipeline p = new ClusterPipeline(provider); p.set("foo", "bar"); Response> error = p.smembers("foo"); Response r = p.get("foo"); p.sync(); try { error.get(); fail(); } catch (JedisDataException e) { // that is fine we should be here } assertEquals(r.get(), "bar"); } } @Test public void getSetParams() { ClusterConnectionProvider provider = new ClusterConnectionProvider(nodes, DEFAULT_CLIENT_CONFIG); ClusterPipeline p = new ClusterPipeline(provider); Response r1 = p.set("key1", "value1"); Response r2 = p.set("key2", "value2"); Response r3 = p.set("key3", "value3"); Response r4 = p.set("key3", "value4", new SetParams().nx()); // Should not be updated Response r5 = p.get("key1"); Response r6 = p.get("key2"); Response r7 = p.get("key3"); p.sync(); assertEquals("OK", r1.get()); assertEquals("OK", r2.get()); assertEquals("OK", r3.get()); assertNull(r4.get()); assertEquals("value1", r5.get()); assertEquals("value2", r6.get()); assertEquals("value3", r7.get()); } @Test public void clusterPipelineSort() { List sorted = new ArrayList<>(); sorted.add("1"); sorted.add("2"); sorted.add("3"); sorted.add("4"); sorted.add("5"); ClusterConnectionProvider provider = new ClusterConnectionProvider(nodes, DEFAULT_CLIENT_CONFIG); ClusterPipeline p = new ClusterPipeline(provider); Response r1 = p.rpush("key1", "2", "3", "5", "1", "4"); Response> r2 = p.sort("key1"); Response r3 = p.sort("key1", "key1"); Response> r4 = p.lrange("key1", 0, 4); Response> r5 = p.sort("key1", new SortingParams().limit(0, 2)); Response r6 = p.sort("key1", new SortingParams().desc(), "key1"); Response> r7 = p.lrange("key1", 0, 4); p.sync(); assertEquals(Long.valueOf(5), r1.get()); assertEquals(sorted, r2.get()); assertEquals(Long.valueOf(5), r3.get()); assertEquals(sorted, r4.get()); assertEquals(2, r5.get().size()); assertEquals(Long.valueOf(5), r6.get()); Collections.reverse(sorted); assertEquals(sorted, r7.get()); } @Test public void clusterPipelineList() { List vals = new ArrayList<>(); vals.add("foobar"); ClusterConnectionProvider provider = new ClusterConnectionProvider(nodes, DEFAULT_CLIENT_CONFIG); ClusterPipeline p = new ClusterPipeline(provider); Response r1 = p.lpush("my{list}", "hello", "hello", "foo", "foo"); // ["foo", "foo", "hello", "hello"] Response r2 = p.rpush("my{newlist}", "hello", "hello", "foo", "foo"); // ["hello", "hello", "foo", "foo"] Response r3 = p.lpos("my{list}", "foo"); Response r4 = p.lpos("my{list}", "foo", new LPosParams().maxlen(1)); Response> r5 = p.lpos("my{list}", "foo", new LPosParams().maxlen(1), 2); Response r6 = p.ltrim("my{list}", 2, 3); // ["hello", "hello"] Response r7 = p.llen("my{list}"); Response r8 = p.lindex("my{list}", -1); Response r9 = p.lset("my{list}", 1, "foobar"); // ["hello", "foobar"] Response r10 = p.lrem("my{list}", 1, "hello"); // ["foobar"] Response> r11 = p.lrange("my{list}", 0, 10); Response r12 = p.rpop("my{newlist}"); // ["hello", "hello", "foo"] Response> r13 = p.lpop("my{list}", 1); // ["foobar"] Response> r14 = p.rpop("my{newlist}", 2); // ["hello"] Response r15 = p.linsert("my{newlist}", ListPosition.AFTER, "hello", "world"); // ["hello", "world"] Response r16 = p.lpushx("myother{newlist}", "foo", "bar"); Response r17 = p.rpushx("myother{newlist}", "foo", "bar"); Response r18 = p.rpoplpush("my{newlist}", "myother{newlist}"); Response r19 = p.lmove("my{newlist}", "myother{newlist}", ListDirection.LEFT, ListDirection.RIGHT); p.sync(); assertEquals(Long.valueOf(4), r1.get()); assertEquals(Long.valueOf(4), r2.get()); assertEquals(Long.valueOf(0), r3.get()); assertEquals(Long.valueOf(0), r4.get()); assertEquals(1, r5.get().size()); assertEquals("OK", r6.get()); assertEquals(Long.valueOf(2), r7.get()); assertEquals("hello", r8.get()); assertEquals("OK", r9.get()); assertEquals(Long.valueOf(1), r10.get()); assertEquals(vals, r11.get()); assertEquals("foo", r12.get()); assertEquals(vals, r13.get()); assertEquals(2, r14.get().size()); assertEquals(Long.valueOf(2), r15.get()); assertEquals(Long.valueOf(0), r16.get()); assertEquals(Long.valueOf(0), r17.get()); assertEquals("world", r18.get()); assertEquals("hello", r19.get()); } @Test public void clusterPipelineSet() { Set diff = new HashSet<>(); diff.add("bar"); diff.add("foo"); Set union = new HashSet<>(); union.add("hello"); union.add("world"); union.add("bar"); union.add("foo"); Set inter = new HashSet<>(); inter.add("world"); inter.add("hello"); ClusterConnectionProvider provider = new ClusterConnectionProvider(nodes, DEFAULT_CLIENT_CONFIG); ClusterPipeline p = new ClusterPipeline(provider); Response r1 = p.sadd("my{set}", "hello", "hello", "world", "foo", "bar"); p.sadd("mynew{set}", "hello", "hello", "world"); Response> r2 = p.sdiff("my{set}", "mynew{set}"); Response r3deprecated = p.sdiffStore("diffset{set}deprecated", "my{set}", "mynew{set}"); Response r3 = p.sdiffstore("diffset{set}", "my{set}", "mynew{set}"); Response> r4 = p.smembers("diffset{set}"); Response> r5 = p.sinter("my{set}", "mynew{set}"); Response r6 = p.sinterstore("interset{set}", "my{set}", "mynew{set}"); Response> r7 = p.smembers("interset{set}"); Response> r8 = p.sunion("my{set}", "mynew{set}"); Response r9 = p.sunionstore("unionset{set}", "my{set}", "mynew{set}"); Response> r10 = p.smembers("unionset{set}"); Response r11 = p.sismember("my{set}", "foo"); Response> r12 = p.smismember("my{set}", "foo", "foobar"); Response r13 = p.srem("my{set}", "foo"); Response> r14 = p.spop("my{set}", 1); Response r15 = p.scard("my{set}"); Response r16 = p.srandmember("my{set}"); Response> r17 = p.srandmember("my{set}", 2); // Response r18 = p.smove("my{set}", "mynew{set}", "hello"); p.sync(); assertEquals(Long.valueOf(4), r1.get()); assertEquals(diff, r2.get()); assertEquals(Long.valueOf(diff.size()), r3deprecated.get()); assertEquals(Long.valueOf(diff.size()), r3.get()); assertEquals(diff, r4.get()); assertEquals(inter, r5.get()); assertEquals(Long.valueOf(inter.size()), r6.get()); assertEquals(inter, r7.get()); assertEquals(union, r8.get()); assertEquals(Long.valueOf(union.size()), r9.get()); assertEquals(union, r10.get()); assertTrue(r11.get()); assertTrue(r12.get().get(0) && !r12.get().get(1)); assertEquals(Long.valueOf(1), r13.get()); assertTrue(union.containsAll(r14.get())); assertEquals(Long.valueOf(2), r15.get()); assertTrue(union.contains(r16.get())); assertTrue(union.containsAll(r17.get())); // assertEquals(Long.valueOf(1), r18.get()); } @Test public void clusterPipelineSortedSet() { Map hm = new HashMap<>(); hm.put("a1", 1d); hm.put("a2", 2d); hm.put("a3", 3d); Set members = new HashSet<>(hm.keySet()); Tuple max = new Tuple("a3", 3d); Tuple min = new Tuple("a1", 1d); ClusterConnectionProvider provider = new ClusterConnectionProvider(nodes, DEFAULT_CLIENT_CONFIG); ClusterPipeline p = new ClusterPipeline(provider); Response r1 = p.zadd("myset", hm); Response r2 = p.zrank("myset", "a3"); Response r3 = p.zrevrank("myset", "a3"); Response r4 = p.zrem("myset", "a1"); Response r5 = p.zadd("myset", 1d, "a1"); Response r6 = p.zadd("myotherset", 2d, "a1", new ZAddParams().nx()); Response r7 = p.zaddIncr("myset", 3d, "a4", new ZAddParams().xx()); // Should not update Response> r8 = p.zrevrange("myset", 0, 0); Response> r9 = p.zrevrangeWithScores("myset", 0, 0); Response r10 = p.zrandmember("myset"); Response> r11 = p.zrandmember("myset", 2); Response> r12 = p.zrandmemberWithScores("myset", 1); Response r13 = p.zscore("myset", "a1"); Response> r14 = p.zmscore("myset", "a1", "a2"); Response r15 = p.zpopmax("myset"); Response r16 = p.zpopmin("myset"); Response r17 = p.zcount("myotherset", 2, 5); Response r18 = p.zcount("myotherset", "(2", "5"); p.zadd("myset", hm, new ZAddParams().nx()); // return the elements that were popped Response> r19 = p.zpopmax("myset", 2); Response> r20 = p.zpopmin("myset", 1); p.sync(); assertEquals(Long.valueOf(3), r1.get()); assertEquals(Long.valueOf(2), r2.get()); assertEquals(Long.valueOf(0), r3.get()); assertEquals(Long.valueOf(1), r4.get()); assertEquals(Long.valueOf(1), r5.get()); assertEquals(Long.valueOf(1), r6.get()); assertNull(r7.get()); assertTrue(r8.get().size() == 1 && r8.get().contains("a3")); assertTrue(r9.get().size() == 1 && r9.get().contains(max)); assertTrue(members.contains(r10.get())); assertTrue(members.containsAll(r11.get())); assertEquals(1, r12.get().size()); assertEquals(Double.valueOf(1), r13.get()); assertTrue(hm.values().containsAll(r14.get())); assertEquals(max, r15.get()); assertEquals(min, r16.get()); assertEquals(Long.valueOf(1), r17.get()); assertEquals(Long.valueOf(0), r18.get()); assertTrue(r19.get().size() == 2 && r19.get().contains(max)); assertTrue(r20.get().size() == 1 && r20.get().contains(min)); } @Test public void clusterPipelineHash() { Map hm = new HashMap<>(); hm.put("field2", "2"); hm.put("field3", "5"); Set keys = new HashSet<>(); keys.add("field2"); List vals = new ArrayList<>(); vals.add("3.5"); List vals2 = new ArrayList<>(); vals2.add("hello"); vals2.add(null); ClusterConnectionProvider provider = new ClusterConnectionProvider(nodes, DEFAULT_CLIENT_CONFIG); ClusterPipeline p = new ClusterPipeline(provider); Response r1 = p.hset("myhash", "field1", "hello"); Response r2 = p.hsetnx("myhash", "field1", "hello"); Response r3 = p.hget("myhash", "field1"); Response r4 = p.hset("myotherhash", hm); Response r5 = p.hmset("mynewhash", hm); p.hincrBy("mynewhash", "field2", 1); Response r6 = p.hincrByFloat("mynewhash", "field2", 0.5); Response r7 = p.hlen("myhash"); Response r8 = p.hdel("mynewhash", "field3"); Response r9 = p.hexists("mynewhash", "field3"); Response> r10 = p.hkeys("mynewhash"); Response> r11 = p.hvals("mynewhash"); Response> r12 = p.hmget("myhash", "field1", "field2"); Response r13 = p.hrandfield("myotherhash"); Response> r14 = p.hrandfield("myotherhash", 4); Response> r15 = p.hrandfield("myotherhash", -4); Response r16 = p.hstrlen("myhash", "field1"); Response>> r17 = p.hrandfieldWithValues("myotherhash", 4); Response>> r18 = p.hrandfieldWithValues("myotherhash", -4); p.sync(); assertEquals(Long.valueOf(1), r1.get()); assertEquals(Long.valueOf(0), r2.get()); assertEquals("hello", r3.get()); assertEquals(Long.valueOf(2), r4.get()); assertEquals("OK", r5.get()); assertEquals(Double.valueOf(3.5), r6.get()); assertEquals(Long.valueOf(1), r7.get()); assertEquals(Long.valueOf(1), r8.get()); assertFalse(r9.get()); assertEquals(keys, r10.get()); assertEquals(vals, r11.get()); assertEquals(vals2, r12.get()); AssertUtil.assertCollectionContains(hm.keySet(), r13.get()); assertEquals(2, r14.get().size()); assertEquals(4, r15.get().size()); assertEquals(Long.valueOf(5), r16.get()); assertEquals(2, r17.get().size()); assertEquals(4, r18.get().size()); } @Test public void clusterPipelineGeo() { Map hm = new HashMap<>(); hm.put("place1", new GeoCoordinate(2.1909389952632, 41.433791470673)); hm.put("place2", new GeoCoordinate(2.1873744593677, 41.406342043777)); List hashValues = new ArrayList<>(); hashValues.add("sp3e9yg3kd0"); hashValues.add("sp3e9cbc3t0"); hashValues.add(null); GeoRadiusParam params = new GeoRadiusParam().withCoord().withHash().withDist(); GeoRadiusParam params2 = new GeoRadiusParam().count(1, true); GeoRadiusStoreParam storeParams = new GeoRadiusStoreParam().store("radius{#}"); ClusterConnectionProvider provider = new ClusterConnectionProvider(nodes, DEFAULT_CLIENT_CONFIG); ClusterPipeline p = new ClusterPipeline(provider); Response r1 = p.geoadd("barcelona", hm); p.geoadd("barcelona{#}", new GeoAddParams().nx(), hm); Response r2 = p.geodist("barcelona", "place1", "place2"); Response r3 = p.geodist("barcelona", "place1", "place2", GeoUnit.KM); Response> r4 = p.geohash("barcelona", "place1", "place2", "place3"); Response> r5 = p.geopos("barcelona", "place1", "place2"); Response> r6 = p.georadius("barcelona", 2.191, 41.433, 1000, GeoUnit.M); Response> r7 = p.georadiusReadonly("barcelona", 2.191, 41.433, 1000, GeoUnit.M); Response> r8 = p.georadius("barcelona", 2.191, 41.433, 1, GeoUnit.KM, params); Response> r9 = p.georadiusReadonly("barcelona", 2.191, 41.433, 1, GeoUnit.KM, params); Response r10 = p.georadiusStore("barcelona{#}", 2.191, 41.433, 1000, GeoUnit.M, params2, storeParams); Response> r11 = p.zrange("radius{#}", 0, -1); Response> r12 = p.georadiusByMember("barcelona", "place1", 4, GeoUnit.KM); Response> r13 = p.georadiusByMemberReadonly("barcelona", "place1", 4, GeoUnit.KM); Response> r14 = p.georadiusByMember("barcelona", "place1", 4, GeoUnit.KM, params2); Response> r15 = p.georadiusByMemberReadonly("barcelona", "place1", 4, GeoUnit.KM, params2); Response r16 = p.georadiusByMemberStore("barcelona{#}", "place1", 4, GeoUnit.KM, params2, storeParams); Response> r17 = p.zrange("radius{#}", 0, -1); p.sync(); assertEquals(Long.valueOf(2), r1.get()); assertEquals(Double.valueOf(3067.4157), r2.get()); assertEquals(Double.valueOf(3.0674), r3.get()); assertEquals(hashValues, r4.get()); assertThat(r5.get(), contains( GeoCoordinateMatcher.atCoordinates(2.19093829393386841, 41.43379028184083523), GeoCoordinateMatcher.atCoordinates(2.18737632036209106, 41.40634178640635099)) ); assertTrue(r6.get().size() == 1 && r6.get().get(0).getMemberByString().equals("place1")); assertTrue(r7.get().size() == 1 && r7.get().get(0).getMemberByString().equals("place1")); GeoRadiusResponse expectedResponse = new GeoRadiusResponse("place1".getBytes()); expectedResponse.setCoordinate(new GeoCoordinate(2.19093829393386841, 41.43379028184083523)); expectedResponse.setDistance(0.0881); expectedResponse.setRawScore(3471609698139488L); assertThat(r8.get().get(0), GeoRadiusResponseMatcher.ofResponse(expectedResponse)); assertThat(r9.get().get(0), GeoRadiusResponseMatcher.ofResponse(expectedResponse)); assertEquals(Long.valueOf(1), r10.get()); assertTrue(r11.get().size() == 1 && r11.get().contains("place1")); assertTrue(r12.get().size() == 2 && r12.get().get(0).getMemberByString().equals("place2")); assertTrue(r13.get().size() == 2 && r13.get().get(0).getMemberByString().equals("place2")); assertTrue(r14.get().size() == 1 && r14.get().get(0).getMemberByString().equals("place2")); assertTrue(r15.get().size() == 1 && r15.get().get(0).getMemberByString().equals("place2")); assertEquals(Long.valueOf(1), r16.get()); assertTrue(r17.get().size() == 1 && r17.get().contains("place2")); } @Test public void clusterPipelineHyperLogLog() { ClusterConnectionProvider provider = new ClusterConnectionProvider(nodes, DEFAULT_CLIENT_CONFIG); ClusterPipeline p = new ClusterPipeline(provider); Response r1 = p.pfadd("{hll}_1", "foo", "bar", "zap", "a"); Response r2 = p.pfadd("{hll}_2", "foo", "bar", "zap"); Response r3 = p.pfcount("{hll}_1", "{hll}_2"); Response r4 = p.pfmerge("{hll}3", "{hll}_1", "{hll}_2"); Response r5 = p.pfcount("{hll}3"); p.sync(); assertEquals(Long.valueOf(1), r1.get()); assertEquals(Long.valueOf(1), r2.get()); assertEquals(Long.valueOf(4), r3.get()); assertEquals("OK", r4.get()); assertEquals(Long.valueOf(4), r5.get()); } @Test public void clusterPipelineStringsAndBits() { List fieldRes = new ArrayList<>(); fieldRes.add(1L); fieldRes.add(0L); ClusterConnectionProvider provider = new ClusterConnectionProvider(nodes, DEFAULT_CLIENT_CONFIG); ClusterPipeline p = new ClusterPipeline(provider); Response r1 = p.set("{mykey}", "foobar"); // foobar = 66 6f 6f 62 61 72 p.set("my{otherkey}", "foo"); Response r2 = p.substr("{mykey}", 0, 2); Response r3 = p.strlen("{mykey}"); Response r4 = p.bitcount("my{otherkey}"); Response r5 = p.bitcount("my{otherkey}", 1, 1); Response r6 = p.bitpos("{mykey}", true); Response r7 = p.bitpos("{mykey}", false, new BitPosParams(1, 2)); Response> r8 = p.bitfield("mynew{key}", "INCRBY", "i5", "100", "1", "GET", "u4", "0"); Response> r9 = p.bitfieldReadonly("hello", "GET", "i8", "17"); p.set("myother{mykey}", "abcdef"); Response r10 = p.bitop(BitOP.AND, "dest{mykey}", "{mykey}", "myother{mykey}"); Response r11 = p.get("dest{mykey}"); Response r12 = p.setbit("my{otherkey}", 7, true); Response r13 = p.getbit("my{otherkey}", 7); p.sync(); assertEquals("OK", r1.get()); assertEquals("foo", r2.get()); assertEquals(Long.valueOf(6), r3.get()); assertEquals(Long.valueOf(16), r4.get()); assertEquals(Long.valueOf(6), r5.get()); assertEquals(Long.valueOf(1), r6.get()); assertEquals(Long.valueOf(8), r7.get()); assertEquals(fieldRes, r8.get()); assertEquals(fieldRes.subList(1, 2), r9.get()); assertEquals(Long.valueOf(6), r10.get()); assertEquals("`bc`ab", r11.get()); assertFalse(r12.get()); assertTrue(r13.get()); } @Test public void clusterPipelineStream() { Map hm = new HashMap<>(); hm.put("one", "one"); hm.put("two", "two"); hm.put("three", "three"); StreamEntryID streamId1 = new StreamEntryID("1638277876711-0"); StreamEntryID streamId2 = new StreamEntryID("1638277959731-0"); ClusterConnectionProvider provider = new ClusterConnectionProvider(nodes, DEFAULT_CLIENT_CONFIG); ClusterPipeline p = new ClusterPipeline(provider); Response r1 = p.xadd("mystream", streamId1, hm); Response r2 = p.xadd("mystream", new XAddParams().id(new StreamEntryID("1638277959731-0")).maxLen(2).approximateTrimming(), hm); Response r3 = p.xlen("mystream"); Response> r4 = p.xrange("mystream", streamId1, streamId2); Response> r5 = p.xrange("mystream", streamId1, streamId2, 1); Response> r6 = p.xrevrange("mystream", streamId2, streamId1); Response> r7 = p.xrevrange("mystream", streamId2, streamId1, 1); Response r8 = p.xgroupCreate("mystream", "group", streamId1, false); Response r9 = p.xgroupSetID("mystream", "group", streamId2); // More stream commands are missing p.sync(); assertEquals(streamId1, r1.get()); assertEquals(streamId2, r2.get()); assertEquals(Long.valueOf(2), r3.get()); assertTrue(r4.get().size() == 2 && r4.get().get(0).getID().compareTo(streamId1) == 0 && r4.get().get(1).getID().compareTo(streamId2) == 0); assertTrue(r5.get().size() == 1 && r5.get().get(0).getID().compareTo(streamId1) == 0); assertTrue(r6.get().size() == 2 && r6.get().get(1).getID().compareTo(streamId1) == 0 && r6.get().get(0).getID().compareTo(streamId2) == 0); assertTrue(r7.get().size() == 1 && r7.get().get(0).getID().compareTo(streamId2) == 0); assertEquals("OK", r8.get()); assertEquals("OK", r9.get()); } @Test public void testEval() { String script = "return 'success!'"; try (ClusterConnectionProvider provider = new ClusterConnectionProvider(nodes, DEFAULT_CLIENT_CONFIG)) { ClusterPipeline p = new ClusterPipeline(provider); Response result = p.eval(script); p.sync(); assertEquals("success!", result.get()); } } @Test public void testEvalWithBinary() { String script = "return 'success!'"; try (ClusterConnectionProvider provider = new ClusterConnectionProvider(nodes, DEFAULT_CLIENT_CONFIG)) { ClusterPipeline p = new ClusterPipeline(provider); Response result = p.eval(SafeEncoder.encode(script)); p.sync(); assertArrayEquals(SafeEncoder.encode("success!"), (byte[]) result.get()); } } @Test public void testEvalKeyAndArg() { String key = "test"; String arg = "3"; String script = "redis.call('INCRBY', KEYS[1], ARGV[1]) redis.call('INCRBY', KEYS[1], ARGV[1])"; try (ClusterConnectionProvider provider = new ClusterConnectionProvider(nodes, DEFAULT_CLIENT_CONFIG)) { ClusterPipeline p = new ClusterPipeline(provider); p.set(key, "0"); Response result0 = p.eval(script, Collections.singletonList(key), Collections.singletonList(arg)); p.incr(key); Response result1 = p.eval(script, Collections.singletonList(key), Collections.singletonList(arg)); Response result2 = p.get(key); p.sync(); assertNull(result0.get()); assertNull(result1.get()); assertEquals("13", result2.get()); } } @Test public void testEvalKeyAndArgWithBinary() { // binary byte[] bKey = SafeEncoder.encode("test"); byte[] bArg = SafeEncoder.encode("3"); byte[] bScript = SafeEncoder.encode("redis.call('INCRBY', KEYS[1], ARGV[1]) redis.call('INCRBY', KEYS[1], ARGV[1])"); try (ClusterConnectionProvider provider = new ClusterConnectionProvider(nodes, DEFAULT_CLIENT_CONFIG)) { ClusterPipeline bP = new ClusterPipeline(provider); bP.set(bKey, SafeEncoder.encode("0")); Response bResult0 = bP.eval(bScript, Collections.singletonList(bKey), Collections.singletonList(bArg)); bP.incr(bKey); Response bResult1 = bP.eval(bScript, Collections.singletonList(bKey), Collections.singletonList(bArg)); Response bResult2 = bP.get(bKey); bP.sync(); assertNull(bResult0.get()); assertNull(bResult1.get()); assertArrayEquals(SafeEncoder.encode("13"), bResult2.get()); } } @Test public void testEvalNestedLists() { String script = "return { {KEYS[1]} , {2} }"; try (ClusterConnectionProvider provider = new ClusterConnectionProvider(nodes, DEFAULT_CLIENT_CONFIG)) { ClusterPipeline p = new ClusterPipeline(provider); Response result = p.eval(script, 1, "key1"); p.sync(); List results = (List) result.get(); MatcherAssert.assertThat((List) results.get(0), Matchers.hasItem("key1")); MatcherAssert.assertThat((List) results.get(1), Matchers.hasItem(2L)); } } @Test public void testEvalNestedListsWithBinary() { byte[] bScript = SafeEncoder.encode("return { {KEYS[1]} , {2} }"); byte[] bKey = SafeEncoder.encode("key1"); try (ClusterConnectionProvider provider = new ClusterConnectionProvider(nodes, DEFAULT_CLIENT_CONFIG)) { ClusterPipeline p = new ClusterPipeline(provider); Response result = p.eval(bScript, 1, bKey); p.sync(); List results = (List) result.get(); MatcherAssert.assertThat((List) results.get(0), Matchers.hasItem(bKey)); MatcherAssert.assertThat((List) results.get(1), Matchers.hasItem(2L)); } } @Test public void testEvalsha() { String script = "return 'success!'"; String sha1; try (RedisClusterClient jc = RedisClusterClient.builder().nodes(nodes).clientConfig(DEFAULT_CLIENT_CONFIG).build()) { sha1 = jc.scriptLoad(script, "sampleKey"); assertTrue(jc.scriptExists(sha1, "sampleKey")); } try (ClusterConnectionProvider provider = new ClusterConnectionProvider(nodes, DEFAULT_CLIENT_CONFIG)) { ClusterPipeline p = new ClusterPipeline(provider); Response result = p.evalsha(sha1, 1, "sampleKey"); p.sync(); assertEquals("success!", result.get()); } } @Test public void testEvalshaKeyAndArg() { String key = "test"; String arg = "3"; String script = "redis.call('INCRBY', KEYS[1], ARGV[1]) redis.call('INCRBY', KEYS[1], ARGV[1])"; String sha1; try (RedisClusterClient jc = RedisClusterClient.builder().nodes(nodes).clientConfig(DEFAULT_CLIENT_CONFIG).build()) { sha1 = jc.scriptLoad(script, key); assertTrue(jc.scriptExists(sha1, key)); } try (ClusterConnectionProvider provider = new ClusterConnectionProvider(nodes, DEFAULT_CLIENT_CONFIG)) { ClusterPipeline p = new ClusterPipeline(provider); p.set(key, "0"); Response result0 = p.evalsha(sha1, Arrays.asList(key), Arrays.asList(arg)); p.incr(key); Response result1 = p.evalsha(sha1, Arrays.asList(key), Arrays.asList(arg)); Response result2 = p.get(key); p.sync(); assertNull(result0.get()); assertNull(result1.get()); assertEquals("13", result2.get()); } } @Test public void testEvalshaKeyAndArgWithBinary() { byte[] bKey = SafeEncoder.encode("test"); byte[] bArg = SafeEncoder.encode("3"); String script = "redis.call('INCRBY', KEYS[1], ARGV[1]) redis.call('INCRBY', KEYS[1], ARGV[1])"; byte[] bScript = SafeEncoder.encode(script); byte[] bSha1; try (RedisClusterClient jc = RedisClusterClient.builder().nodes(nodes).clientConfig(DEFAULT_CLIENT_CONFIG).build()) { bSha1 = jc.scriptLoad(bScript, bKey); assertTrue(jc.scriptExists(bSha1, bKey)); } try (ClusterConnectionProvider provider = new ClusterConnectionProvider(nodes, DEFAULT_CLIENT_CONFIG)) { ClusterPipeline p = new ClusterPipeline(provider); p.set(bKey, SafeEncoder.encode("0")); Response result0 = p.evalsha(bSha1, Arrays.asList(bKey), Arrays.asList(bArg)); p.incr(bKey); Response result1 = p.evalsha(bSha1, Arrays.asList(bKey), Arrays.asList(bArg)); Response result2 = p.get(bKey); p.sync(); assertNull(result0.get()); assertNull(result1.get()); assertArrayEquals(SafeEncoder.encode("13"), result2.get()); } } @Test public void spublishInPipeline() { try (RedisClusterClient jedis = RedisClusterClient.builder().nodes(nodes).clientConfig(DEFAULT_CLIENT_CONFIG).build()) { ClusterPipeline pipelined = jedis.pipelined(); Response p1 = pipelined.publish("foo", "bar"); Response p2 = pipelined.publish("foo".getBytes(), "bar".getBytes()); pipelined.sync(); assertEquals(0, p1.get().longValue()); assertEquals(0, p2.get().longValue()); } } @Test public void simple() { // TODO: move into 'redis.clients.jedis.commands.unified.cluster' package try (RedisClusterClient jedis = RedisClusterClient.builder().nodes(nodes).clientConfig(DEFAULT_CLIENT_CONFIG).build()) { final int count = 10; int totalCount = 0; for (int i = 0; i < count; i++) { jedis.set("foo" + i, "bar" + i); } totalCount += count; for (int i = 0; i < count; i++) { jedis.rpush("foobar" + i, "foo" + i, "bar" + i); } totalCount += count; List> responses = new ArrayList<>(totalCount); List expected = new ArrayList<>(totalCount); try (ClusterPipeline pipeline = jedis.pipelined()) { for (int i = 0; i < count; i++) { responses.add(pipeline.get("foo" + i)); expected.add("bar" + i); } for (int i = 0; i < count; i++) { responses.add(pipeline.lrange("foobar" + i, 0, -1)); expected.add(Arrays.asList("foo" + i, "bar" + i)); } } for (int i = 0; i < totalCount; i++) { assertEquals(expected.get(i), responses.get(i).get()); } } } @Test public void transaction() { try (RedisClusterClient cluster = RedisClusterClient.builder().nodes(nodes).clientConfig(DEFAULT_CLIENT_CONFIG).build()) { assertThrows(UnsupportedOperationException.class, () -> cluster.multi()); } } @Test @Timeout(10) public void multiple() { final int maxTotal = 100; ConnectionPoolConfig poolConfig = new ConnectionPoolConfig(); poolConfig.setMaxTotal(maxTotal); try (RedisClusterClient cluster = RedisClusterClient.builder().nodes(nodes).clientConfig(DEFAULT_CLIENT_CONFIG).maxAttempts(5).poolConfig(poolConfig).build()) { for (int i = 0; i < maxTotal; i++) { assertThreadsCount(); String s = Integer.toString(i); try (ClusterPipeline pipeline = cluster.pipelined()) { pipeline.set(s, s); pipeline.sync(); } assertThreadsCount(); } } } @Test public void testPipelineKeysAtSameNode() { try (RedisClusterClient cluster = RedisClusterClient.builder().nodes(nodes).clientConfig(DEFAULT_CLIENT_CONFIG).build()) { // test simple key cluster.set("foo", "bar"); try (ClusterPipeline pipeline = cluster.pipelined()) { Response foo = pipeline.get("foo"); pipeline.sync(); assertEquals("bar", foo.get()); } // test multi key but at same node int cnt = 3; String prefix = "{foo}:"; for (int i = 0; i < cnt; i++) { String key = prefix + i; cluster.set(key, String.valueOf(i)); } try (ClusterPipeline pipeline = cluster.pipelined()) { List> results = new ArrayList<>(); for (int i = 0; i < cnt; i++) { String key = prefix + i; results.add(pipeline.get(key)); } Response foo = pipeline.eval("return redis.call('get', KEYS[1])", Collections.singletonList("foo"), Collections.emptyList()); pipeline.sync(); int idx = 0; for (Response res : results) { assertEquals(String.valueOf(idx), res.get()); idx++; } assertEquals("bar", String.valueOf(foo.get())); } } } private static void assertThreadsCount() { // Get the root thread group final ThreadGroup rootGroup = Thread.currentThread().getThreadGroup().getParent(); // Create a buffer to store the thread information final Thread[] threads = new Thread[rootGroup.activeCount()]; // Enumerate all threads into the buffer rootGroup.enumerate(threads); // Assert information about threads final int count = (int) Arrays.stream(threads) .filter(thread -> thread != null && thread.getName() != null && thread.getName().startsWith("pool-")) .count(); MatcherAssert.assertThat(count, Matchers.lessThanOrEqualTo(20)); } @Test public void testAllShardsCommandRejected() { try (ClusterPipeline pipeline = new ClusterPipeline(nodes, DEFAULT_CLIENT_CONFIG)) { // KEYS has ALL_SHARDS policy with pattern argument (not a key), should be rejected UnsupportedOperationException ex = assertThrows( UnsupportedOperationException.class, () -> pipeline.keys("*")); assertTrue(ex.getMessage().contains("ALL_SHARDS"), "Error message should mention ALL_SHARDS policy"); assertTrue(ex.getMessage().contains("KEYS"), "Error message should mention the command name"); assertTrue(ex.getMessage().contains("no keys"), "Error message should mention that command has no keys"); } } @Test public void testAllShardsPolicyWithSampleKeyAllowed() { try (ClusterPipeline pipeline = new ClusterPipeline(nodes, DEFAULT_CLIENT_CONFIG)) { // SCRIPT EXISTS has ALL_SHARDS policy but with sampleKey it routes to single slot, should work String dummySha1 = "0000000000000000000000000000000000000000"; Response> existsResponse = pipeline.scriptExists("samplekey", dummySha1); pipeline.sync(); // The response should be valid (list with one boolean indicating if script exists) assertNotNull(existsResponse.get(), "SCRIPT EXISTS with sampleKey should be allowed in pipeline"); assertEquals(1, existsResponse.get().size(), "SCRIPT EXISTS should return list with one element"); assertFalse(existsResponse.get().get(0), "Dummy script should not exist"); } } @Test public void testMultiShardCommandRejected() { try (ClusterPipeline pipeline = new ClusterPipeline(nodes, DEFAULT_CLIENT_CONFIG)) { // MGET with keys in different slots should be rejected UnsupportedOperationException ex = assertThrows( UnsupportedOperationException.class, () -> pipeline.mget("key1", "key2", "key3")); assertTrue(ex.getMessage().contains("MULTI_SHARD"), "Error message should mention MULTI_SHARD policy"); } } @Test public void testDefaultPolicyCommandAllowed() { try (ClusterPipeline pipeline = new ClusterPipeline(nodes, DEFAULT_CLIENT_CONFIG)) { // SET with single key - DEFAULT policy, should work Response setResponse = pipeline.set("testkey", "testvalue"); Response getResponse = pipeline.get("testkey"); pipeline.sync(); assertEquals("OK", setResponse.get()); assertEquals("testvalue", getResponse.get()); } } @Test public void testMultiShardPolicyWithSingleKeyAllowed() { try (ClusterPipeline pipeline = new ClusterPipeline(nodes, DEFAULT_CLIENT_CONFIG)) { // EXISTS with single key has MULTI_SHARD policy but routes to single slot, should work pipeline.set("existskey", "value"); Response existsResponse = pipeline.exists("existskey"); pipeline.sync(); assertTrue(existsResponse.get(), "EXISTS with single key should be allowed in pipeline"); } } @Test public void testMultiShardPolicyWithMultipleKeysRejected() { try (ClusterPipeline pipeline = new ClusterPipeline(nodes, DEFAULT_CLIENT_CONFIG)) { // EXISTS with multiple keys in different slots has MULTI_SHARD policy, should be rejected UnsupportedOperationException ex = assertThrows( UnsupportedOperationException.class, () -> pipeline.exists("key1", "key2", "key3")); assertTrue(ex.getMessage().contains("MULTI_SHARD") || ex.getMessage().contains("multiple slots"), "Error message should mention MULTI_SHARD policy or multiple slots"); } } } ================================================ FILE: src/test/java/redis/clients/jedis/ConnectionTest.java ================================================ package redis.clients.jedis; import static org.hamcrest.MatcherAssert.assertThat; import static org.junit.jupiter.api.Assertions.assertThrows; import org.hamcrest.Matchers; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.Tag; import org.junit.jupiter.api.Test; import redis.clients.jedis.exceptions.JedisConnectionException; @Tag("integration") public class ConnectionTest { private static EndpointConfig endpoint; private Connection client; @BeforeAll public static void setUp() { endpoint = Endpoints.getRedisEndpoint("standalone0"); } @AfterEach public void tearDown() throws Exception { if (client != null) { client.close(); } } @Test public void checkUnknownHost() { client = new Connection("someunknownhost", Protocol.DEFAULT_PORT); assertThrows(JedisConnectionException.class, ()->client.connect()); } @Test public void checkWrongPort() { client = new Connection(Protocol.DEFAULT_HOST, 55665); assertThrows(JedisConnectionException.class, ()->client.connect()); } @Test public void connectIfNotConnectedWhenSettingTimeoutInfinite() { client = new Connection(endpoint.getHost(), endpoint.getPort()); client.setTimeoutInfinite(); } @Test public void checkCloseable() { client = new Connection(endpoint.getHost(), endpoint.getPort()); client.connect(); client.close(); } @Test public void checkIdentityString() { client = new Connection(endpoint.getHost(), endpoint.getPort()); String idString = "id: 0x" + Integer.toHexString(client.hashCode()).toUpperCase(); String identityString = client.toIdentityString(); assertThat(identityString, Matchers.startsWith("Connection{")); assertThat(identityString, Matchers.endsWith("}")); assertThat(identityString, Matchers.containsString(idString)); client.connect(); identityString = client.toIdentityString(); assertThat(identityString, Matchers.startsWith("Connection{")); assertThat(identityString, Matchers.endsWith("}")); assertThat(identityString, Matchers.containsString(idString)); assertThat(identityString, Matchers.containsString(", L:")); assertThat(identityString, Matchers.containsString(" - R:")); client.close(); identityString = client.toIdentityString(); assertThat(identityString, Matchers.startsWith("Connection{")); assertThat(identityString, Matchers.endsWith("}")); assertThat(identityString, Matchers.containsString(idString)); assertThat(identityString, Matchers.containsString(", L:")); assertThat(identityString, Matchers.containsString(" ! R:")); } } ================================================ FILE: src/test/java/redis/clients/jedis/DefaultJedisClientConfigTest.java ================================================ package redis.clients.jedis; import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.containsString; import static org.hamcrest.Matchers.equalTo; import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertNull; import static org.junit.jupiter.api.Assertions.assertThrows; import static org.junit.jupiter.api.Assertions.assertTrue; import java.net.URI; import org.junit.jupiter.api.Nested; import org.junit.jupiter.api.Test; class DefaultJedisClientConfigTest { @Nested class BuilderTests { @Test void builderFromUri_credentials() { URI uri = URI.create("redis://testuser:testpass@localhost:6379/0"); DefaultJedisClientConfig config = DefaultJedisClientConfig.builder(uri).build(); assertThat(config.getUser(), equalTo("testuser")); assertThat(config.getPassword(), equalTo("testpass")); assertThat(config.getDatabase(), equalTo(0)); } @Test void builderFromUri_database() { URI uri = URI.create("redis://localhost:6379/5"); DefaultJedisClientConfig config = DefaultJedisClientConfig.builder(uri).build(); assertThat(config.getDatabase(), equalTo(5)); } @Test void builderFromUri_ssl() { URI uri = URI.create("rediss://localhost:6380"); DefaultJedisClientConfig config = DefaultJedisClientConfig.builder(uri).build(); assertTrue(config.isSsl()); } @Test void builderFromUri_nossl() { URI uri = URI.create("redis://localhost:6380"); DefaultJedisClientConfig config = DefaultJedisClientConfig.builder(uri).build(); assertFalse(config.isSsl()); } @Test void builderFromUri_protocol() { URI uri = URI.create("redis://localhost:6379?protocol=3"); DefaultJedisClientConfig config = DefaultJedisClientConfig.builder(uri).build(); assertThat(config.getRedisProtocol(), equalTo(RedisProtocol.RESP3)); } @Test void builderFromUri_combined() { URI uri = URI.create("rediss://admin:secret123@localhost:6380/2?protocol=3"); DefaultJedisClientConfig config = DefaultJedisClientConfig.builder(uri).build(); assertThat(config.getUser(), equalTo("admin")); assertThat(config.getPassword(), equalTo("secret123")); assertThat(config.getDatabase(), equalTo(2)); assertThat(config.isSsl(), equalTo(true)); assertThat(config.getRedisProtocol(), equalTo(RedisProtocol.RESP3)); } @Test void builderFromUri_noCredentials() { URI uri = URI.create("redis://localhost:6379"); DefaultJedisClientConfig config = DefaultJedisClientConfig.builder(uri).build(); // Should have default/null credentials assertThat(config.getDatabase(), equalTo(Protocol.DEFAULT_DATABASE)); assertThat(config.isSsl(), equalTo(false)); } @Test void builderFromUri_passwordOnly() { URI uri = URI.create("redis://:password@localhost:6379"); assertThat(DefaultJedisClientConfig.builder(uri).build().getPassword(), equalTo("password")); assertNull(DefaultJedisClientConfig.builder(uri).build().getUser()); } @Test void builderFromUri_usernameOnlyThrowsException() { URI uri = URI.create("redis://onlyuser@localhost:6379"); IllegalArgumentException ex = assertThrows(IllegalArgumentException.class, () -> DefaultJedisClientConfig.builder(uri)); assertThat(ex.getMessage(), containsString("Password not provided in uri")); } @Test void builderFromUri_InvalidUri() { URI uri = URI.create("localhost:6379/0"); Exception ex = assertThrows(IllegalArgumentException.class, () -> DefaultJedisClientConfig.builder(uri).build()); assertThat(ex.getMessage(), containsString("Invalid Redis URI")); } } } ================================================ FILE: src/test/java/redis/clients/jedis/EndpointConfig.java ================================================ package redis.clients.jedis; import com.google.gson.FieldNamingPolicy; import com.google.gson.Gson; import com.google.gson.GsonBuilder; import com.google.gson.reflect.TypeToken; import redis.clients.jedis.util.JedisURIHelper; import redis.clients.jedis.util.TlsUtil; import java.io.FileReader; import java.net.URI; import java.nio.file.Path; import java.nio.file.Paths; import java.util.*; public class EndpointConfig { private final boolean tls; private final String username; private final String password; private final int bdbId; private final List endpoints; private final String tlsCertPath; public EndpointConfig(HostAndPort hnp, String username, String password, boolean tls, String tlsCertPath) { this.tls = tls; this.username = username; this.password = password; this.bdbId = 0; this.endpoints = Collections.singletonList( URI.create(getURISchema(tls) + hnp.getHost() + ":" + hnp.getPort())); this.tlsCertPath = tlsCertPath; } public HostAndPort getHostAndPort() { return JedisURIHelper.getHostAndPort(endpoints.get(0)); } public HostAndPort getHostAndPort(int index) { return JedisURIHelper.getHostAndPort(endpoints.get(index)); } public List getHostsAndPorts() { List result = new ArrayList<>(); for (URI endpoint : endpoints) { result.add(JedisURIHelper.getHostAndPort(endpoint)); } return result; } public String getPassword() { return password; } public String getUsername() { return username == null? "default" : username; } public String getHost() { return getHostAndPort().getHost(); } public int getPort() { return getHostAndPort().getPort(); } public Boolean isTls() { return tls; } public Path getCertificatesLocation() { return Paths.get(tlsCertPath); } public int getBdbId() { return bdbId; } public URI getURI() { return endpoints.get(0); } public class EndpointURIBuilder { private boolean tls; private String username; private String password; private String path; public EndpointURIBuilder() { this.username = ""; this.password = ""; this.path = ""; this.tls = EndpointConfig.this.tls; } public EndpointURIBuilder defaultCredentials() { this.username = EndpointConfig.this.username == null ? "" : getUsername(); this.password = EndpointConfig.this.getPassword(); return this; } public EndpointURIBuilder tls(boolean v) { this.tls = v; return this; } public EndpointURIBuilder path(String v) { this.path = v; return this; } public EndpointURIBuilder credentials(String u, String p) { this.username = u; this.password = p; return this; } public URI build() { String userInfo = !(this.username.isEmpty() && this.password.isEmpty()) ? this.username + ':' + this.password + '@' : ""; return URI.create( getURISchema(this.tls) + userInfo + getHost() + ":" + getPort() + this.path); } } public EndpointURIBuilder getURIBuilder() { return new EndpointURIBuilder(); } public DefaultJedisClientConfig.Builder getClientConfigBuilder() { DefaultJedisClientConfig.Builder builder = DefaultJedisClientConfig.builder() .password(password).ssl(tls); if (tls && tlsCertPath != null) { builder.sslSocketFactory(TlsUtil.sslSocketFactoryForEnv(Paths.get(tlsCertPath))); } if (username != null) { return builder.user(username); } return builder; } protected String getURISchema(boolean tls) { return (tls ? "rediss" : "redis") + "://"; } public static HashMap loadFromJSON(String filePath) throws Exception { Gson gson = new GsonBuilder().setFieldNamingPolicy( FieldNamingPolicy.LOWER_CASE_WITH_UNDERSCORES).create(); HashMap configs; try (FileReader reader = new FileReader(filePath)) { configs = gson.fromJson(reader, new TypeToken>() { }.getType()); } return configs; } } ================================================ FILE: src/test/java/redis/clients/jedis/Endpoints.java ================================================ package redis.clients.jedis; import org.opentest4j.TestAbortedException; import redis.clients.jedis.util.TestEnvUtil; import java.util.HashMap; public final class Endpoints { private static final HashMap endpointConfigs; static { try { endpointConfigs = EndpointConfig.loadFromJSON(TestEnvUtil.getEndpointsConfigPath()); } catch (Exception e) { throw new RuntimeException(e); } } public static EndpointConfig getRedisEndpoint(String endpointName) { if (!endpointConfigs.containsKey(endpointName)) { throw new TestAbortedException("Unavailable Redis endpoint: " + endpointName); } return endpointConfigs.get(endpointName); } private Endpoints() { throw new InstantiationError("Must not instantiate this class"); } } ================================================ FILE: src/test/java/redis/clients/jedis/HostAndPortTest.java ================================================ package redis.clients.jedis; import org.junit.jupiter.api.Test; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertThrows; public class HostAndPortTest { @Test public void checkFrom() throws Exception { String host = "2a11:1b1:0:111:e111:1f11:1111:1f1e:1999"; int port = 6379; HostAndPort hp = HostAndPort.from(host + ":" + Integer.toString(port)); assertEquals(host, hp.getHost()); assertEquals(port, hp.getPort()); } @Test public void checkFromWithoutPort() throws Exception { assertThrows(IllegalArgumentException.class, () -> HostAndPort.from("localhost:")); } } ================================================ FILE: src/test/java/redis/clients/jedis/JedisClusterCommandTest.java ================================================ //package redis.clients.jedis; // //import static org.junit.jupiter.api.Assertions.assertEquals; //import static org.junit.jupiter.api.Assertions.assertNotNull; //import static org.junit.jupiter.api.Assertions.assertTrue; //import static org.junit.jupiter.api.Assertions.fail; //import static org.mockito.ArgumentMatchers.any; //import static org.mockito.ArgumentMatchers.anyInt; //import static org.mockito.ArgumentMatchers.anyLong; //import static org.mockito.Mockito.doAnswer; //import static org.mockito.Mockito.inOrder; //import static org.mockito.Mockito.mock; //import static org.mockito.Mockito.times; //import static org.mockito.Mockito.when; // //import java.time.Duration; //import java.util.concurrent.atomic.AtomicLong; //import java.util.function.LongConsumer; //import org.junit.Test; //import org.mockito.InOrder; //import org.mockito.invocation.InvocationOnMock; //import org.mockito.stubbing.Answer; //import redis.clients.jedis.HostAndPort; //import redis.clients.jedis.Jedis; //import redis.clients.jedis.JedisClusterCommand; //import redis.clients.jedis.JedisClusterConnectionHandler; //import redis.clients.jedis.JedisSlotBasedConnectionHandler; //import redis.clients.jedis.exceptions.JedisAskDataException; //import redis.clients.jedis.exceptions.JedisClusterMaxAttemptsException; //import redis.clients.jedis.exceptions.JedisClusterOperationException; //import redis.clients.jedis.exceptions.JedisConnectionException; //import redis.clients.jedis.exceptions.JedisMovedDataException; //import redis.clients.jedis.exceptions.JedisNoReachableClusterNodeException; // //public class JedisClusterCommandTest { // // private static final Duration ONE_SECOND = Duration.ofSeconds(1); // // @Test // public void runSuccessfulExecute() { // JedisClusterConnectionHandler connectionHandler = mock(JedisClusterConnectionHandler.class); // JedisClusterCommand testMe = new JedisClusterCommand(connectionHandler, 10, // Duration.ZERO) { // @Override // public String execute(Jedis connection) { // return "foo"; // } // // @Override // protected void sleep(long ignored) { // throw new RuntimeException("This test should never sleep"); // } // }; // String actual = testMe.run(""); // assertEquals("foo", actual); // } // // @Test // public void runFailOnFirstExecSuccessOnSecondExec() { // JedisClusterConnectionHandler connectionHandler = mock(JedisClusterConnectionHandler.class); // // JedisClusterCommand testMe = new JedisClusterCommand(connectionHandler, 10, // ONE_SECOND) { // boolean isFirstCall = true; // // @Override // public String execute(Jedis connection) { // if (isFirstCall) { // isFirstCall = false; // throw new JedisConnectionException("Borkenz"); // } // // return "foo"; // } // // @Override // protected void sleep(long ignored) { // throw new RuntimeException("This test should never sleep"); // } // }; // // String actual = testMe.run(""); // assertEquals("foo", actual); // } // // @Test // public void runAlwaysFailing() { // JedisSlotBasedConnectionHandler connectionHandler = mock(JedisSlotBasedConnectionHandler.class); // // final LongConsumer sleep = mock(LongConsumer.class); // JedisClusterCommand testMe = new JedisClusterCommand(connectionHandler, 3, // ONE_SECOND) { // @Override // public String execute(Jedis connection) { // throw new JedisConnectionException("Connection failed"); // } // // @Override // protected void sleep(long sleepMillis) { // sleep.accept(sleepMillis); // } // }; // // try { // testMe.run(""); // fail("cluster command did not fail"); // } catch (JedisClusterMaxAttemptsException e) { // // expected // } // InOrder inOrder = inOrder(connectionHandler, sleep); // inOrder.verify(connectionHandler, times(2)).getConnectionFromSlot(anyInt()); // inOrder.verify(sleep).accept(anyLong()); // inOrder.verify(connectionHandler).renewSlotCache(); // inOrder.verify(connectionHandler).getConnectionFromSlot(anyInt()); // inOrder.verifyNoMoreInteractions(); // } // // @Test // public void runMovedSuccess() { // JedisSlotBasedConnectionHandler connectionHandler = mock(JedisSlotBasedConnectionHandler.class); // // final HostAndPort movedTarget = new HostAndPort(null, 0); // JedisClusterCommand testMe = new JedisClusterCommand(connectionHandler, 10, // ONE_SECOND) { // boolean isFirstCall = true; // // @Override // public String execute(Jedis connection) { // if (isFirstCall) { // isFirstCall = false; // // // Slot 0 moved // throw new JedisMovedDataException("", movedTarget, 0); // } // // return "foo"; // } // // @Override // protected void sleep(long ignored) { // throw new RuntimeException("This test should never sleep"); // } // }; // // String actual = testMe.run(""); // assertEquals("foo", actual); // // InOrder inOrder = inOrder(connectionHandler); // inOrder.verify(connectionHandler).getConnectionFromSlot(anyInt()); // inOrder.verify(connectionHandler).renewSlotCache(any()); // inOrder.verify(connectionHandler).getConnectionFromNode(movedTarget); // inOrder.verifyNoMoreInteractions(); // } // // @Test // public void runAskSuccess() { // JedisSlotBasedConnectionHandler connectionHandler = mock(JedisSlotBasedConnectionHandler.class); // Jedis connection = mock(Jedis.class); // final HostAndPort askTarget = new HostAndPort(null, 0); // when(connectionHandler.getConnectionFromNode(askTarget)).thenReturn(connection); // // JedisClusterCommand testMe = new JedisClusterCommand(connectionHandler, 10, // ONE_SECOND) { // boolean isFirstCall = true; // // @Override // public String execute(Jedis connection) { // if (isFirstCall) { // isFirstCall = false; // // // Slot 0 moved // throw new JedisAskDataException("", askTarget, 0); // } // // return "foo"; // } // // @Override // protected void sleep(long ignored) { // throw new RuntimeException("This test should never sleep"); // } // }; // // String actual = testMe.run(""); // assertEquals("foo", actual); // // InOrder inOrder = inOrder(connectionHandler, connection); // inOrder.verify(connectionHandler).getConnectionFromSlot(anyInt()); // inOrder.verify(connectionHandler).getConnectionFromNode(askTarget); // inOrder.verify(connection).asking(); // inOrder.verify(connection).close(); // From the finally clause in runWithRetries() // inOrder.verifyNoMoreInteractions(); // } // // @Test // public void runMovedThenAllNodesFailing() { // // Test: // // First attempt is a JedisMovedDataException() move, because we asked the wrong node. // // All subsequent attempts are JedisConnectionExceptions, because all nodes are now down. // // In response to the JedisConnectionExceptions, run() retries random nodes until maxAttempts is // // reached. // JedisSlotBasedConnectionHandler connectionHandler = mock(JedisSlotBasedConnectionHandler.class); // // final Jedis redirecter = mock(Jedis.class); // when(connectionHandler.getConnectionFromSlot(anyInt())).thenReturn(redirecter); // // final Jedis failer = mock(Jedis.class); // when(connectionHandler.getConnectionFromNode(any(HostAndPort.class))).thenReturn(failer); // doAnswer((Answer) (InvocationOnMock invocation) -> { // when(connectionHandler.getConnectionFromSlot(anyInt())).thenReturn(failer); // return null; // }).when(connectionHandler).renewSlotCache(); // // final LongConsumer sleep = mock(LongConsumer.class); // final HostAndPort movedTarget = new HostAndPort(null, 0); // JedisClusterCommand testMe = new JedisClusterCommand(connectionHandler, 5, // ONE_SECOND) { // @Override // public String execute(Jedis connection) { // if (redirecter == connection) { // // First attempt, report moved // throw new JedisMovedDataException("Moved", movedTarget, 0); // } // // if (failer == connection) { // // Second attempt in response to the move, report failure // throw new JedisConnectionException("Connection failed"); // } // // throw new IllegalStateException("Should have thrown jedis exception"); // } // // @Override // protected void sleep(long sleepMillis) { // sleep.accept(sleepMillis); // } // }; // // try { // testMe.run(""); // fail("cluster command did not fail"); // } catch (JedisClusterMaxAttemptsException e) { // // expected // } // InOrder inOrder = inOrder(connectionHandler, sleep); // inOrder.verify(connectionHandler).getConnectionFromSlot(anyInt()); // inOrder.verify(connectionHandler).renewSlotCache(redirecter); // inOrder.verify(connectionHandler, times(2)).getConnectionFromNode(movedTarget); // inOrder.verify(sleep).accept(anyLong()); // inOrder.verify(connectionHandler).renewSlotCache(); // inOrder.verify(connectionHandler, times(2)).getConnectionFromSlot(anyInt()); // inOrder.verify(sleep).accept(anyLong()); // inOrder.verify(connectionHandler).renewSlotCache(); // inOrder.verifyNoMoreInteractions(); // } // // @Test // public void runMasterFailingReplicaRecovering() { // // We have two nodes, master and replica, and master has just gone down permanently. // // // // Test: // // 1. We try to contact master => JedisConnectionException // // 2. We try to contact master => JedisConnectionException // // 3. sleep and renew // // 4. We try to contact replica => Success, because it has now failed over // // final Jedis master = mock(Jedis.class); // when(master.toString()).thenReturn("master"); // // final Jedis replica = mock(Jedis.class); // when(replica.toString()).thenReturn("replica"); // // JedisSlotBasedConnectionHandler connectionHandler = mock(JedisSlotBasedConnectionHandler.class); // // when(connectionHandler.getConnectionFromSlot(anyInt())).thenReturn(master); // // doAnswer((Answer) (InvocationOnMock invocation) -> { // when(connectionHandler.getConnectionFromSlot(anyInt())).thenReturn(replica); // return null; // }).when(connectionHandler).renewSlotCache(); // // final AtomicLong totalSleepMs = new AtomicLong(); // JedisClusterCommand testMe = new JedisClusterCommand(connectionHandler, 10, // ONE_SECOND) { // // @Override // public String execute(Jedis connection) { // assertNotNull(connection); // // if (connection == master) { // throw new JedisConnectionException("Master is down"); // } // // assert connection == replica; // // return "Success!"; // } // // @Override // protected void sleep(long sleepMillis) { // assert sleepMillis > 0; // totalSleepMs.addAndGet(sleepMillis); // } // }; // // assertEquals("Success!", testMe.run("")); // InOrder inOrder = inOrder(connectionHandler); // inOrder.verify(connectionHandler, times(2)).getConnectionFromSlot(anyInt()); // inOrder.verify(connectionHandler).renewSlotCache(); // inOrder.verify(connectionHandler).getConnectionFromSlot(anyInt()); // inOrder.verifyNoMoreInteractions(); // assertTrue(totalSleepMs.get() > 0); // } // // @Test(expected = JedisNoReachableClusterNodeException.class) // public void runRethrowsJedisNoReachableClusterNodeException() { // JedisSlotBasedConnectionHandler connectionHandler = mock(JedisSlotBasedConnectionHandler.class); // when(connectionHandler.getConnectionFromSlot(anyInt())).thenThrow( // JedisNoReachableClusterNodeException.class); // // JedisClusterCommand testMe = new JedisClusterCommand(connectionHandler, 10, // Duration.ZERO) { // @Override // public String execute(Jedis connection) { // return null; // } // // @Override // protected void sleep(long ignored) { // throw new RuntimeException("This test should never sleep"); // } // }; // // testMe.run(""); // } // // @Test // public void runStopsRetryingAfterTimeout() { // JedisSlotBasedConnectionHandler connectionHandler = mock(JedisSlotBasedConnectionHandler.class); // // final LongConsumer sleep = mock(LongConsumer.class); // JedisClusterCommand testMe = new JedisClusterCommand(connectionHandler, 3, // Duration.ZERO) { // @Override // public String execute(Jedis connection) { // try { // // exceed deadline // Thread.sleep(2L); // } catch (InterruptedException e) { // throw new RuntimeException(e); // } // throw new JedisConnectionException("Connection failed"); // } // // @Override // protected void sleep(long sleepMillis) { // sleep.accept(sleepMillis); // } // }; // // try { // testMe.run(""); // fail("cluster command did not fail"); // } catch (JedisClusterOperationException e) { // // expected // } // InOrder inOrder = inOrder(connectionHandler, sleep); // inOrder.verify(connectionHandler).getConnectionFromSlot(anyInt()); // inOrder.verifyNoMoreInteractions(); // } //} ================================================ FILE: src/test/java/redis/clients/jedis/JedisClusterInfoCacheTest.java ================================================ package redis.clients.jedis; import org.junit.jupiter.api.Tag; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; import org.mockito.Mock; import org.mockito.junit.jupiter.MockitoExtension; import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; import java.util.HashSet; import java.util.List; import java.util.Set; import java.util.stream.Collectors; import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.aMapWithSize; import static org.hamcrest.Matchers.equalTo; import static org.hamcrest.Matchers.hasEntry; import static org.hamcrest.Matchers.hasItem; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertNotNull; import static org.junit.jupiter.api.Assertions.assertNull; import static org.mockito.ArgumentMatchers.argThat; import static org.mockito.Mockito.when; import static redis.clients.jedis.JedisClusterInfoCache.getNodeKey; import static redis.clients.jedis.Protocol.Command.CLUSTER; import static redis.clients.jedis.util.CommandArgumentsMatchers.commandWithArgs; @Tag("unit") @ExtendWith(MockitoExtension.class) public class JedisClusterInfoCacheTest { private static final HostAndPort MASTER_HOST = new HostAndPort("127.0.0.1", 7000); private static final HostAndPort REPLICA_1_HOST = new HostAndPort("127.0.0.1", 7001); private static final HostAndPort REPLICA_2_HOST = new HostAndPort("127.0.0.1", 7002); private static final int TEST_SLOT = 0; @Mock private Connection mockConnection; @Test public void testReplicaNodeRemovalAndRediscovery() { // Create client config with read-only replicas enabled JedisClientConfig clientConfig = DefaultJedisClientConfig.builder() .readOnlyForRedisClusterReplicas().build(); Set startNodes = new HashSet<>(); startNodes.add(MASTER_HOST); JedisClusterInfoCache cache = new JedisClusterInfoCache(clientConfig, startNodes); // Mock the cluster slots responses when(mockConnection.executeCommand(argThat(commandWithArgs(CLUSTER, "SLOTS")))).thenReturn( masterReplicaSlotsResponse(MASTER_HOST, REPLICA_1_HOST)).thenReturn(masterOnlySlotsResponse()) .thenReturn(masterReplica2SlotsResponse()); // Initial discovery with one master and one replica (replica-1) cache.discoverClusterNodesAndSlots(mockConnection); assertMasterNodeAvailable(cache); assertReplicasAvailable(cache, REPLICA_1_HOST); // Simulate rediscovery - master only cache.discoverClusterNodesAndSlots(mockConnection); // Master should still be available // Replica should be cleared assertMasterNodeAvailable(cache); assertNoReplicasAvailable(cache); // Simulate rediscovery - another replica (replica-2) coming back cache.reset(); cache.discoverClusterNodesAndSlots(mockConnection); assertReplicasAvailable(cache, REPLICA_2_HOST); } @Test public void testResetWithReplicaSlots() { // This test verifies that reset() properly clears replica slots JedisClusterInfoCache cache = createCacheWithReplicasEnabled(); // Mock the cluster slots responses when(mockConnection.executeCommand(argThat(commandWithArgs(CLUSTER, "SLOTS")))).thenReturn( masterReplicaSlotsResponse(MASTER_HOST, REPLICA_1_HOST)); // Initial discovery cache.discoverClusterNodesAndSlots(mockConnection); assertReplicasAvailable(cache, REPLICA_1_HOST); // Call reset() - this should clear and nullify replica slots cache.reset(); assertNoReplicasAvailable(cache); // Rediscovery should work correctly cache.discoverClusterNodesAndSlots(mockConnection); assertReplicasAvailable(cache, REPLICA_1_HOST); } @Test public void getPrimaryNodesAfterReplicaNodeRemovalAndRediscovery() { // Create client config with read-only replicas enabled JedisClientConfig clientConfig = DefaultJedisClientConfig.builder() .readOnlyForRedisClusterReplicas().build(); Set startNodes = new HashSet<>(); startNodes.add(MASTER_HOST); JedisClusterInfoCache cache = new JedisClusterInfoCache(clientConfig, startNodes); // Mock the cluster slots responses when(mockConnection.executeCommand(argThat(commandWithArgs(CLUSTER, "SLOTS")))).thenReturn( masterReplicaSlotsResponse(MASTER_HOST, REPLICA_1_HOST)).thenReturn(masterOnlySlotsResponse()) .thenReturn(masterReplica2SlotsResponse()); // Initial discovery with one master and one replica (replica-1) cache.discoverClusterNodesAndSlots(mockConnection); assertThat(cache.getPrimaryNodes(),aMapWithSize(1)); assertThat(cache.getPrimaryNodes(), hasEntry(equalTo(getNodeKey(MASTER_HOST)), equalTo(cache.getNode(MASTER_HOST)))); // Simulate rediscovery - master only cache.discoverClusterNodesAndSlots(mockConnection); assertThat( cache.getPrimaryNodes(),aMapWithSize(1)); assertThat(cache.getPrimaryNodes(), hasEntry(equalTo(getNodeKey(MASTER_HOST)), equalTo(cache.getNode(MASTER_HOST)))); } @Test public void getPrimaryNodesAfterMasterReplicaFailover() { // Create client config with read-only replicas enabled JedisClientConfig clientConfig = DefaultJedisClientConfig.builder() .readOnlyForRedisClusterReplicas().build(); Set startNodes = new HashSet<>(); startNodes.add(MASTER_HOST); JedisClusterInfoCache cache = new JedisClusterInfoCache(clientConfig, startNodes); // Mock the cluster slots responses when(mockConnection.executeCommand(argThat(commandWithArgs(CLUSTER, "SLOTS")))) .thenReturn(masterReplicaSlotsResponse(MASTER_HOST, REPLICA_1_HOST)) .thenReturn(masterReplicaSlotsResponse(REPLICA_1_HOST, MASTER_HOST)); // Initial discovery with one master and one replica (replica-1) cache.discoverClusterNodesAndSlots(mockConnection); assertThat(cache.getPrimaryNodes(),aMapWithSize(1)); assertThat(cache.getPrimaryNodes(), hasEntry(equalTo(getNodeKey(MASTER_HOST)), equalTo(cache.getNode(MASTER_HOST)))); // Simulate rediscovery - master only cache.discoverClusterNodesAndSlots(mockConnection); assertThat( cache.getPrimaryNodes(),aMapWithSize(1)); assertThat(cache.getPrimaryNodes(), hasEntry(equalTo(getNodeKey(REPLICA_1_HOST)), equalTo(cache.getNode(REPLICA_1_HOST)))); } private List masterReplicaSlotsResponse(HostAndPort masterHost, HostAndPort replicaHost) { return createClusterSlotsResponse( new SlotRange.Builder(0, 16383).master(masterHost, masterHost.toString() + "-id") .replica(replicaHost, replicaHost.toString() + "-id").build()); } private List masterOnlySlotsResponse() { return createClusterSlotsResponse( new SlotRange.Builder(0, 16383).master(MASTER_HOST, "master-id-1").build()); } private List masterReplica2SlotsResponse() { return createClusterSlotsResponse( new SlotRange.Builder(0, 16383).master(MASTER_HOST, "master-id-1") .replica(REPLICA_2_HOST, "replica-id-2").build()); } private JedisClusterInfoCache createCacheWithReplicasEnabled() { JedisClientConfig clientConfig = DefaultJedisClientConfig.builder() .readOnlyForRedisClusterReplicas().build(); return new JedisClusterInfoCache(clientConfig, new HashSet<>(Collections.singletonList(MASTER_HOST))); } private void assertNoReplicasAvailable(JedisClusterInfoCache cache) { List caheReplicaNodePools = cache.getSlotReplicaPools(TEST_SLOT); assertNull(caheReplicaNodePools); } private void assertReplicasAvailable(JedisClusterInfoCache cache, HostAndPort... replicaNodes) { List caheReplicaNodePools = cache.getSlotReplicaPools(TEST_SLOT); assertEquals(replicaNodes.length, caheReplicaNodePools.size()); for (HostAndPort expectedReplica : replicaNodes) { ConnectionPool expectedNodePool = cache.getNode(expectedReplica); assertThat(caheReplicaNodePools, hasItem(expectedNodePool)); } } private void assertMasterNodeAvailable(JedisClusterInfoCache cache) { HostAndPort masterNode = cache.getSlotNode(TEST_SLOT); assertNotNull(masterNode); assertEquals(MASTER_HOST, masterNode); } /** * Helper method to create a cluster slots response with master and replica nodes */ private List createClusterSlotsResponse(SlotRange... slotRanges) { return Arrays.stream(slotRanges).map(this::clusterSlotRange).collect(Collectors.toList()); } private List clusterSlotRange(SlotRange slotRange) { List slotInfo = new ArrayList<>(); slotInfo.add((long) slotRange.start); slotInfo.add((long) slotRange.end); Node master = slotRange.master(); slotInfo.add( Arrays.asList(master.getHost().getBytes(), (long) master.getPort(), master.id.getBytes())); // Add replicas slotRange.replicas().forEach(r -> slotInfo.add( Arrays.asList(r.getHost().getBytes(), (long) r.getPort(), r.id.getBytes()))); return slotInfo; } static class SlotRange { private final int start; private final int end; private final List nodes; private SlotRange(int start, int end, List nodes) { this.start = start; this.end = end; this.nodes = nodes; } public SlotRange.Builder builder(int start, int end) { return new SlotRange.Builder(start, end); } public Node master() { return nodes.get(0); } public List replicas() { return nodes.subList(1, nodes.size()); } static class Builder { private final int start; private final int end; private final List nodes = new ArrayList<>(); public Builder(int start, int end) { this.start = start; this.end = end; } public Builder master(Node node) { if (!nodes.isEmpty()) { nodes.set(0, node); } else { nodes.add(node); } return this; } public Builder master(HostAndPort hostPort, String id) { return master(new Node(hostPort, id)); } public Builder replica(HostAndPort hostPort, String id) { return replica(new Node(hostPort, id)); } public Builder replica(Node node) { if (nodes.isEmpty()) { throw new IllegalStateException("Master node must be added before adding replicas"); } nodes.add(node); return this; } public SlotRange build() { return new SlotRange(start, end, nodes); } } } static class Node { private final HostAndPort hostPort; private final String id; public Node(HostAndPort hostPort, String id) { this.hostPort = hostPort; this.id = id; } public HostAndPort getHostPort() { return hostPort; } public String getHost() { return hostPort.getHost(); } public int getPort() { return hostPort.getPort(); } public String getId() { return id; } } } ================================================ FILE: src/test/java/redis/clients/jedis/JedisClusterWithoutSetupTest.java ================================================ package redis.clients.jedis; import static java.util.Collections.emptySet; import static org.junit.jupiter.api.Assertions.assertThrows; import static org.junit.jupiter.api.Assertions.assertEquals; import org.junit.jupiter.api.Test; import redis.clients.jedis.exceptions.JedisClusterOperationException; public class JedisClusterWithoutSetupTest { @Test public void noStartNodes() { JedisClusterOperationException operationException = assertThrows( JedisClusterOperationException.class, () -> new JedisCluster(emptySet())); assertEquals("No nodes to initialize cluster slots cache.", operationException.getMessage()); assertEquals(0, operationException.getSuppressed().length); } @Test public void uselessStartNodes() { JedisClusterOperationException operationException = assertThrows( JedisClusterOperationException.class, () -> new JedisCluster(new HostAndPort("localhost", 7378))); assertEquals("Could not initialize cluster slots cache.", operationException.getMessage()); assertEquals(1, operationException.getSuppressed().length); } } ================================================ FILE: src/test/java/redis/clients/jedis/JedisPoolTest.java ================================================ package redis.clients.jedis; import java.net.URI; import java.net.URISyntaxException; import java.time.Duration; import java.util.concurrent.atomic.AtomicInteger; import io.redis.test.annotations.ConditionalOnEnv; import org.apache.commons.pool2.PooledObject; import org.apache.commons.pool2.PooledObjectFactory; import org.apache.commons.pool2.impl.DefaultPooledObject; import org.apache.commons.pool2.impl.GenericObjectPoolConfig; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Tag; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.TestInfo; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.extension.RegisterExtension; import redis.clients.jedis.exceptions.InvalidURIException; import redis.clients.jedis.exceptions.JedisAccessControlException; import redis.clients.jedis.exceptions.JedisConnectionException; import redis.clients.jedis.exceptions.JedisException; import redis.clients.jedis.util.EnvCondition; import redis.clients.jedis.util.TestEnvUtil; import static java.util.concurrent.TimeUnit.MILLISECONDS; import static org.awaitility.Awaitility.await; import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.equalTo; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertInstanceOf; import static org.junit.jupiter.api.Assertions.assertNull; import static org.junit.jupiter.api.Assertions.assertSame; import static org.junit.jupiter.api.Assertions.assertThrows; import static org.junit.jupiter.api.Assertions.assertTrue; import static org.junit.jupiter.api.Assertions.fail; @Tag("integration") public class JedisPoolTest { @RegisterExtension public static EnvCondition envCondition = new EnvCondition(); private static EndpointConfig endpointStandalone0; private static EndpointConfig endpointStandalone1; private String testKey; private String testValue; @BeforeAll public static void prepareEndpoints() { endpointStandalone0 = Endpoints.getRedisEndpoint("standalone0"); endpointStandalone1 = Endpoints.getRedisEndpoint("standalone1"); } @BeforeEach public void setUpTestKey(TestInfo testInfo) { testKey = testInfo.getDisplayName() + "-key"; testValue = testInfo.getDisplayName() + "-value"; } @Test public void checkConnections() { JedisPool pool = new JedisPool(new JedisPoolConfig(), endpointStandalone0.getHost(), endpointStandalone0.getPort(), 2000); try (Jedis jedis = pool.getResource()) { jedis.auth(endpointStandalone0.getPassword()); jedis.set("foo", "bar"); assertEquals("bar", jedis.get("foo")); } pool.close(); assertTrue(pool.isClosed()); } @Test public void checkResourceWithConfig() { EndpointConfig endpoint = Endpoints.getRedisEndpoint("standalone7-with-lfu-policy"); try (JedisPool pool = new JedisPool(endpoint.getHostAndPort(), endpoint.getClientConfigBuilder().socketTimeoutMillis(5000).build())) { try (Jedis jedis = pool.getResource()) { assertEquals("PONG", jedis.ping()); assertEquals(5000, jedis.getClient().getSoTimeout()); } } } @Test public void checkCloseableConnections() throws Exception { JedisPool pool = new JedisPool(new JedisPoolConfig(), endpointStandalone0.getHost(), endpointStandalone0.getPort(), 2000); try (Jedis jedis = pool.getResource()) { jedis.auth(endpointStandalone0.getPassword()); jedis.set("foo", "bar"); assertEquals("bar", jedis.get("foo")); } pool.close(); assertTrue(pool.isClosed()); } @Test @ConditionalOnEnv(value = TestEnvUtil.ENV_REDIS_ENTERPRISE, enabled = false) public void checkConnectionWithDefaultHostAndPort() { JedisPool pool = new JedisPool(new JedisPoolConfig()); try (Jedis jedis = pool.getResource()) { jedis.auth(endpointStandalone0.getPassword()); jedis.set("foo", "bar"); assertEquals("bar", jedis.get("foo")); } pool.close(); assertTrue(pool.isClosed()); } @Test public void checkResourceIsClosableAndReusable() { GenericObjectPoolConfig config = new GenericObjectPoolConfig<>(); config.setMaxTotal(1); config.setBlockWhenExhausted(false); try (JedisPool pool = new JedisPool(config, endpointStandalone0.getHost(), endpointStandalone0.getPort(), 2000, endpointStandalone0.getPassword(), 0, "closable-reusable-pool", false, null, null, null)) { Jedis jedis = pool.getResource(); jedis.set("hello", "jedis"); jedis.close(); Jedis jedis2 = pool.getResource(); assertSame(jedis, jedis2); assertEquals("jedis", jedis2.get("hello")); jedis2.close(); } } @Test public void checkPoolRepairedWhenJedisIsBroken() { JedisPool pool = new JedisPool(new JedisPoolConfig(), endpointStandalone0.getHost(), endpointStandalone0.getPort()); try (Jedis jedis = pool.getResource()) { jedis.auth(endpointStandalone0.getPassword()); jedis.set("foo", "0"); jedis.disconnect(); } try (Jedis jedis = pool.getResource()) { jedis.auth(endpointStandalone0.getPassword()); jedis.incr("foo"); } pool.close(); assertTrue(pool.isClosed()); } @Test public void checkPoolOverflow() { GenericObjectPoolConfig config = new GenericObjectPoolConfig<>(); config.setMaxTotal(1); config.setBlockWhenExhausted(false); try (JedisPool pool = new JedisPool(config, endpointStandalone0.getHost(), endpointStandalone0.getPort()); Jedis jedis = pool.getResource()) { jedis.auth(endpointStandalone0.getPassword()); assertThrows(JedisException.class, pool::getResource); } } @Test public void securePool() { JedisPoolConfig config = new JedisPoolConfig(); config.setTestOnBorrow(true); JedisPool pool = new JedisPool(config, endpointStandalone0.getHost(), endpointStandalone0.getPort(), 2000, endpointStandalone0.getPassword()); try (Jedis jedis = pool.getResource()) { jedis.set("foo", "bar"); } pool.close(); assertTrue(pool.isClosed()); } @Test @ConditionalOnEnv(value = TestEnvUtil.ENV_REDIS_ENTERPRISE, enabled = false) public void nonDefaultDatabase() { try (JedisPool pool0 = new JedisPool(new JedisPoolConfig(), endpointStandalone0.getHost(), endpointStandalone0.getPort(), 2000, endpointStandalone0.getPassword()); Jedis jedis0 = pool0.getResource()) { jedis0.set("foo", "bar"); assertEquals("bar", jedis0.get("foo")); } try (JedisPool pool1 = new JedisPool(new JedisPoolConfig(), endpointStandalone0.getHost(), endpointStandalone0.getPort(), 2000, endpointStandalone0.getPassword(), 1); Jedis jedis1 = pool1.getResource()) { assertNull(jedis1.get("foo")); } } @Test @ConditionalOnEnv(value = TestEnvUtil.ENV_REDIS_ENTERPRISE, enabled = false) public void startWithUrlString() { try (Jedis j = new Jedis(endpointStandalone1.getHostAndPort())) { j.auth(endpointStandalone1.getPassword()); j.select(2); j.set("foo", "bar"); } try (JedisPool pool = new JedisPool( endpointStandalone1.getURIBuilder().credentials("", endpointStandalone1.getPassword()).path("/2").build()); Jedis jedis = pool.getResource()) { assertEquals("PONG", jedis.ping()); assertEquals("bar", jedis.get("foo")); } } @Test @ConditionalOnEnv(value = TestEnvUtil.ENV_REDIS_ENTERPRISE, enabled = false) public void startWithUrl() throws URISyntaxException { try (Jedis j = new Jedis(endpointStandalone1.getHostAndPort())) { j.auth(endpointStandalone1.getPassword()); j.select(2); j.set("foo", "bar"); } try (JedisPool pool = new JedisPool( endpointStandalone1.getURIBuilder().credentials("", endpointStandalone1.getPassword()).path("/2").build()); Jedis jedis = pool.getResource()) { assertEquals("bar", jedis.get("foo")); } } @Test public void shouldThrowInvalidURIExceptionForInvalidURI() throws URISyntaxException { assertThrows(InvalidURIException.class, ()->new JedisPool(new URI("localhost:6380")).close()); } @Test public void allowUrlWithNoDBAndNoPassword() throws URISyntaxException { new JedisPool(endpointStandalone1.getURI().toString()).close(); new JedisPool(endpointStandalone1.getURI()).close(); } @Test @ConditionalOnEnv(value = TestEnvUtil.ENV_REDIS_ENTERPRISE, enabled = false) public void selectDatabaseOnActivation() { try (JedisPool pool = new JedisPool(new JedisPoolConfig(), endpointStandalone0.getHost(), endpointStandalone0.getPort(), 2000, endpointStandalone0.getPassword())) { Jedis jedis0 = pool.getResource(); assertEquals(0, jedis0.getDB()); jedis0.select(1); assertEquals(1, jedis0.getDB()); jedis0.close(); Jedis jedis1 = pool.getResource(); assertSame(jedis1, jedis0); assertEquals(0, jedis1.getDB()); jedis1.close(); } } @Test public void customClientName() { try (JedisPool pool = new JedisPool(new JedisPoolConfig(), endpointStandalone0.getHost(), endpointStandalone0.getPort(), 2000, endpointStandalone0.getPassword(), 0, "my_shiny_client_name"); Jedis jedis = pool.getResource()) { assertEquals("my_shiny_client_name", jedis.clientGetname()); } } @Test public void invalidClientName() { try (JedisPool pool = new JedisPool(new JedisPoolConfig(), endpointStandalone0.getHost(), endpointStandalone0.getPort(), 2000, endpointStandalone0.getPassword(), 0, "invalid client name"); Jedis jedis = pool.getResource()) { } catch (Exception e) { if (!e.getMessage().startsWith("client info cannot contain space")) { fail("invalid client name test fail"); } } } @Test public void returnResourceDestroysResourceOnException() { class CrashingJedis extends Jedis { @Override public void resetState() { throw new RuntimeException(); } } final AtomicInteger destroyed = new AtomicInteger(0); class CrashingJedisPooledObjectFactory implements PooledObjectFactory { @Override public PooledObject makeObject() throws Exception { return new DefaultPooledObject(new CrashingJedis()); } @Override public void destroyObject(PooledObject p) throws Exception { destroyed.incrementAndGet(); } @Override public boolean validateObject(PooledObject p) { return true; } @Override public void activateObject(PooledObject p) throws Exception { } @Override public void passivateObject(PooledObject p) throws Exception { } } GenericObjectPoolConfig config = new GenericObjectPoolConfig<>(); config.setMaxTotal(1); JedisPool pool = new JedisPool(config, new CrashingJedisPooledObjectFactory()); Jedis crashingJedis = pool.getResource(); try { crashingJedis.close(); } catch (Exception ignored) { } assertEquals(1, destroyed.get()); } @Test public void returnResourceShouldResetState() { GenericObjectPoolConfig config = new GenericObjectPoolConfig<>(); config.setMaxTotal(1); config.setBlockWhenExhausted(false); JedisPool pool = new JedisPool(config, endpointStandalone0.getHost(), endpointStandalone0.getPort(), 2000, endpointStandalone0.getPassword()); Jedis jedis = pool.getResource(); try { jedis.set("hello", "jedis"); Transaction t = jedis.multi(); t.set("hello", "world"); } finally { jedis.close(); } try (Jedis jedis2 = pool.getResource()) { assertSame(jedis, jedis2); assertEquals("jedis", jedis2.get("hello")); } pool.close(); assertTrue(pool.isClosed()); } @Test public void getNumActiveWhenPoolIsClosed() { JedisPool pool = new JedisPool(new JedisPoolConfig(), endpointStandalone0.getHost(), endpointStandalone0.getPort(), 2000, endpointStandalone0.getPassword(), 0, "my_shiny_client_name"); try (Jedis j = pool.getResource()) { j.ping(); } pool.close(); assertEquals(0, pool.getNumActive()); } @Test public void getNumActiveReturnsTheCorrectNumber() { try (JedisPool pool = new JedisPool(new JedisPoolConfig(), endpointStandalone0.getHost(), endpointStandalone0.getPort(), 2000)) { Jedis jedis = pool.getResource(); jedis.auth(endpointStandalone0.getPassword()); jedis.set("foo", "bar"); assertEquals("bar", jedis.get("foo")); assertEquals(1, pool.getNumActive()); Jedis jedis2 = pool.getResource(); jedis.auth(endpointStandalone0.getPassword()); jedis.set("foo", "bar"); assertEquals(2, pool.getNumActive()); jedis.close(); assertEquals(1, pool.getNumActive()); jedis2.close(); assertEquals(0, pool.getNumActive()); } } @Test public void testAddObject() { try (JedisPool pool = new JedisPool(new JedisPoolConfig(), endpointStandalone0.getHost(), endpointStandalone0.getPort(), 2000)) { pool.addObjects(1); assertEquals(1, pool.getNumIdle()); } } @Test public void closeResourceTwice() { try (JedisPool pool = new JedisPool(new JedisPoolConfig(), endpointStandalone0.getHost(), endpointStandalone0.getPort(), 2000)) { Jedis j = pool.getResource(); j.auth(endpointStandalone0.getPassword()); j.ping(); j.close(); j.close(); } } @Test public void closeBrokenResourceTwice() { try (JedisPool pool = new JedisPool(new JedisPoolConfig(), endpointStandalone0.getHost(), endpointStandalone0.getPort(), 2000)) { Jedis j = pool.getResource(); try { // make connection broken j.getClient().getOne(); fail(); } catch (Exception e) { assertInstanceOf(JedisConnectionException.class, e); } assertTrue(j.isBroken()); j.close(); j.close(); } } @Test public void testCloseConnectionOnMakeObject() { JedisPoolConfig config = new JedisPoolConfig(); config.setTestOnBorrow(true); try (JedisPool pool = new JedisPool(new JedisPoolConfig(), endpointStandalone0.getHost(), endpointStandalone0.getPort(), 2000, "wrong pass"); Jedis jedis = new Jedis(endpointStandalone0.getURIBuilder().defaultCredentials().build())) { int currentClientCount = getClientCount(jedis.clientList()); assertThrows(JedisAccessControlException.class, pool::getResource); // wait for the redis server to close the connection await().pollDelay(Duration.ofMillis(10)).atMost(500, MILLISECONDS) .until(() -> getClientCount(jedis.clientList()) == currentClientCount); assertEquals(currentClientCount, getClientCount(jedis.clientList())); } } private int getClientCount(final String clientList) { return clientList.split("\n").length; } @Test public void testResetInvalidCredentials() { DefaultRedisCredentialsProvider credentialsProvider = new DefaultRedisCredentialsProvider(new DefaultRedisCredentials(null, endpointStandalone0.getPassword())); JedisFactory factory = new JedisFactory(endpointStandalone0.getHostAndPort(), DefaultJedisClientConfig.builder() .credentialsProvider(credentialsProvider).clientName("my_shiny_client_name").build()); try (JedisPool pool = new JedisPool(new JedisPoolConfig(), factory)) { Jedis obj1_ref; try (Jedis obj1_1 = pool.getResource()) { obj1_ref = obj1_1; obj1_1.set("foo", "bar"); assertEquals("bar", obj1_1.get("foo")); assertEquals(1, pool.getNumActive()); } assertEquals(0, pool.getNumActive()); try (Jedis obj1_2 = pool.getResource()) { assertSame(obj1_ref, obj1_2); assertEquals(1, pool.getNumActive()); credentialsProvider.setCredentials(new DefaultRedisCredentials(null, "wrong password")); try (Jedis obj2 = pool.getResource()) { fail("Should not get resource from pool"); } catch (JedisException e) { //ignore } assertEquals(1, pool.getNumActive()); } assertEquals(0, pool.getNumActive()); } } @Test public void testResetValidCredentials() { DefaultRedisCredentialsProvider credentialsProvider = new DefaultRedisCredentialsProvider(new DefaultRedisCredentials(null, "bad password")); JedisFactory factory = new JedisFactory(endpointStandalone0.getHostAndPort(), DefaultJedisClientConfig.builder() .credentialsProvider(credentialsProvider).clientName("my_shiny_client_name").build()); try (JedisPool pool = new JedisPool(new JedisPoolConfig(), factory)) { try (Jedis obj1 = pool.getResource()) { fail("Should not get resource from pool"); } catch (JedisException e) { //ignore } assertEquals(0, pool.getNumActive()); credentialsProvider.setCredentials(new DefaultRedisCredentials(null, endpointStandalone0.getPassword())); try (Jedis obj2 = pool.getResource()) { obj2.set("foo", "bar"); assertEquals("bar", obj2.get("foo")); } } } @Test public void testWithResource() { try (JedisPool pool = new JedisPool(new JedisPoolConfig(), endpointStandalone0.getHostAndPort(), endpointStandalone0.getClientConfigBuilder().build())) { pool.withResource(jedis -> { jedis.set(testKey, testValue); }); pool.withResource(jedis -> { assertEquals(testValue, jedis.get(testKey)); }); } } @Test public void testWithResourceReturnsConnectionToPool() { try (JedisPool pool = new JedisPool(new JedisPoolConfig(), endpointStandalone0.getHostAndPort(), endpointStandalone0.getClientConfigBuilder().build())) { pool.withResource(jedis -> { assertThat(pool.getNumActive(), equalTo(1)); jedis.set("foo", "bar"); }); assertThat(pool.getNumActive(), equalTo(0)); } } @Test public void testWithResourceGet() { try (JedisPool pool = new JedisPool(new JedisPoolConfig(), endpointStandalone0.getHostAndPort(), endpointStandalone0.getClientConfigBuilder().build())) { String result = pool.withResourceGet(jedis -> { jedis.set(testKey, testValue); return jedis.get(testKey); }); assertEquals(testValue, result); } } @Test public void testWithResourceGetReturnsConnectionToPool() { try (JedisPool pool = new JedisPool(new JedisPoolConfig(), endpointStandalone0.getHostAndPort(), endpointStandalone0.getClientConfigBuilder().build())) { String result = pool.withResourceGet(jedis -> { assertThat(pool.getNumActive(), equalTo(1)); jedis.set("foo", "bar"); return jedis.get("foo"); }); assertThat(result, equalTo("bar")); assertThat(pool.getNumActive(), equalTo(0)); } } } ================================================ FILE: src/test/java/redis/clients/jedis/JedisPoolUnitTest.java ================================================ package redis.clients.jedis; import org.apache.commons.pool2.PooledObjectFactory; import org.apache.commons.pool2.impl.DefaultPooledObject; import org.apache.commons.pool2.impl.GenericObjectPoolConfig; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.mockito.Mockito.*; public class JedisPoolUnitTest { private JedisPool pool; private Jedis mockJedis; @BeforeEach public void setUp() throws Exception { mockJedis = mock(Jedis.class); PooledObjectFactory mockFactory = mock(PooledObjectFactory.class); when(mockFactory.makeObject()).thenReturn(new DefaultPooledObject<>(mockJedis)); GenericObjectPoolConfig config = new GenericObjectPoolConfig<>(); config.setMaxTotal(1); pool = spy(new JedisPool(config, mockFactory)); } @AfterEach public void tearDown() { if (pool != null && !pool.isClosed()) { pool.close(); } } @Test public void testWithResourceClosesConnection() { pool.withResource(jedis -> assertEquals(mockJedis, jedis)); verify(mockJedis, times(1)).close(); } @Test public void testWithResourceGetClosesConnection() { String result = pool.withResourceGet(jedis -> "test-result"); verify(mockJedis, times(1)).close(); } @Test public void testWithResourceGetReturnsResult() { when(mockJedis.get(eq("test-key"))).thenReturn("test-result"); String result = pool.withResourceGet(jedis -> jedis.get("test-key")); verify(mockJedis, times(1)).close(); } } ================================================ FILE: src/test/java/redis/clients/jedis/JedisPubSubBaseTest.java ================================================ package redis.clients.jedis; import org.junit.jupiter.api.Test; import redis.clients.jedis.util.SafeEncoder; import java.util.Arrays; import java.util.List; import java.util.concurrent.CountDownLatch; import java.util.concurrent.TimeUnit; import static org.junit.jupiter.api.Assertions.assertTrue; import static org.junit.jupiter.api.Assertions.fail; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; import static redis.clients.jedis.Protocol.ResponseKeyword.MESSAGE; import static redis.clients.jedis.Protocol.ResponseKeyword.SUBSCRIBE; public class JedisPubSubBaseTest { @Test public void testProceed_givenThreadInterrupt_exitLoop() throws InterruptedException { // setup final JedisPubSubBase pubSub = new JedisPubSubBase() { @Override public void onMessage(String channel, String message) { fail("this should not happen when thread is interrupted"); } @Override protected String encode(byte[] raw) { return SafeEncoder.encode(raw); } }; final Connection mockConnection = mock(Connection.class); final List mockSubscribe = Arrays.asList( SUBSCRIBE.getRaw(), "channel".getBytes(), 1L ); final List mockResponse = Arrays.asList( MESSAGE.getRaw(), "channel".getBytes(), "message".getBytes() ); when(mockConnection.getUnflushedObject()). thenReturn(mockSubscribe, mockResponse); final CountDownLatch countDownLatch = new CountDownLatch(1); // action final Thread thread = new Thread(() -> { Thread.currentThread().interrupt(); pubSub.proceed(mockConnection, "channel"); countDownLatch.countDown(); }); thread.start(); assertTrue(countDownLatch.await(30, TimeUnit.MILLISECONDS)); } } ================================================ FILE: src/test/java/redis/clients/jedis/JedisSentinelPoolTest.java ================================================ package redis.clients.jedis; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertThrows; import static org.junit.jupiter.api.Assertions.assertTrue; import static org.junit.jupiter.api.Assertions.assertSame; import java.util.HashSet; import java.util.Set; import org.apache.commons.pool2.impl.GenericObjectPoolConfig; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Tag; import org.junit.jupiter.api.Test; import redis.clients.jedis.exceptions.JedisConnectionException; import redis.clients.jedis.exceptions.JedisException; @Tag("integration") public class JedisSentinelPoolTest { private static final String MASTER_NAME = "mymaster"; protected static HostAndPort sentinel1; protected static HostAndPort sentinel2; protected final Set sentinels = new HashSet<>(); @BeforeAll public static void prepare() { sentinel1 = Endpoints.getRedisEndpoint("sentinel-standalone2-1").getHostAndPort(); sentinel2 = Endpoints.getRedisEndpoint("sentinel-standalone2-3").getHostAndPort(); } @BeforeEach public void setUp() throws Exception { sentinels.clear(); sentinels.add(sentinel1.toString()); sentinels.add(sentinel2.toString()); } @Test public void repeatedSentinelPoolInitialization() { for (int i = 0; i < 20; ++i) { GenericObjectPoolConfig config = new GenericObjectPoolConfig<>(); JedisSentinelPool pool = new JedisSentinelPool(MASTER_NAME, sentinels, config, 1000, "foobared", 2); pool.getResource().close(); pool.destroy(); } } @Test public void initializeWithNotAvailableSentinelsShouldThrowException() { Set wrongSentinels = new HashSet(); wrongSentinels.add(new HostAndPort("localhost", 65432).toString()); wrongSentinels.add(new HostAndPort("localhost", 65431).toString()); assertThrows(JedisConnectionException.class, () -> new JedisSentinelPool(MASTER_NAME, wrongSentinels).close()); } @Test public void initializeWithNotMonitoredMasterNameShouldThrowException() { final String wrongMasterName = "wrongMasterName"; assertThrows(JedisException.class, ()-> new JedisSentinelPool(wrongMasterName, sentinels).close()); } @Test public void checkCloseableConnections() throws Exception { GenericObjectPoolConfig config = new GenericObjectPoolConfig<>(); JedisSentinelPool pool = new JedisSentinelPool(MASTER_NAME, sentinels, config, 1000, "foobared", 2); Jedis jedis = pool.getResource(); jedis.auth("foobared"); jedis.set("foo", "bar"); assertEquals("bar", jedis.get("foo")); jedis.close(); pool.close(); assertTrue(pool.isClosed()); } @Test public void returnResourceShouldResetState() { GenericObjectPoolConfig config = new GenericObjectPoolConfig<>(); config.setMaxTotal(1); config.setBlockWhenExhausted(false); try (JedisSentinelPool pool = new JedisSentinelPool(MASTER_NAME, sentinels, config, 1000, "foobared", 2)) { Jedis jedis = null; try (Jedis jedis1 = pool.getResource()) { jedis = jedis1; jedis1.set("hello", "jedis"); Transaction t = jedis1.multi(); t.set("hello", "world"); } try (Jedis jedis2 = pool.getResource()) { assertSame(jedis, jedis2); assertEquals("jedis", jedis2.get("hello")); } } } @Test public void checkResourceIsCloseable() { GenericObjectPoolConfig config = new GenericObjectPoolConfig<>(); config.setMaxTotal(1); config.setBlockWhenExhausted(false); JedisSentinelPool pool = new JedisSentinelPool(MASTER_NAME, sentinels, config, 1000, "foobared", 2); Jedis jedis = pool.getResource(); try { jedis.set("hello", "jedis"); } finally { jedis.close(); } Jedis jedis2 = pool.getResource(); try { assertEquals(jedis, jedis2); } finally { jedis2.close(); } } @Test public void customClientName() { GenericObjectPoolConfig config = new GenericObjectPoolConfig<>(); config.setMaxTotal(1); config.setBlockWhenExhausted(false); JedisSentinelPool pool = new JedisSentinelPool(MASTER_NAME, sentinels, config, 1000, "foobared", 0, "my_shiny_client_name"); Jedis jedis = pool.getResource(); try { assertEquals("my_shiny_client_name", jedis.clientGetname()); } finally { jedis.close(); pool.destroy(); } assertTrue(pool.isClosed()); } } ================================================ FILE: src/test/java/redis/clients/jedis/JedisSentinelTest.java ================================================ package redis.clients.jedis; import java.util.HashMap; import java.util.List; import java.util.Map; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Tag; import org.junit.jupiter.api.Test; import redis.clients.jedis.exceptions.JedisDataException; import redis.clients.jedis.util.JedisSentinelTestUtil; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertNotEquals; import static org.junit.jupiter.api.Assertions.assertTrue; import static org.junit.jupiter.api.Assertions.fail; @Tag("integration") public class JedisSentinelTest { private static final String MASTER_NAME = "mymaster"; private static final String MONITOR_MASTER_NAME = "mymastermonitor"; private static final String REMOVE_MASTER_NAME = "mymasterremove"; private static final String FAILOVER_MASTER_NAME = "mymasterfailover"; protected static EndpointConfig master; protected static HostAndPort sentinel; protected static HostAndPort sentinelForFailover; @BeforeAll public static void prepare() { master = Endpoints.getRedisEndpoint("standalone2-primary"); sentinel = Endpoints.getRedisEndpoint("sentinel-standalone2-1").getHostAndPort(); sentinelForFailover = Endpoints.getRedisEndpoint("sentinel-failover").getHostAndPort(); } @BeforeEach public void setup() throws InterruptedException { } @AfterEach public void clear() throws InterruptedException { // New Sentinel (after 2.8.1) // when slave promoted to master (slave of no one), New Sentinel force // to restore it (demote) // so, promote(slaveof) slave to master has no effect, not same to old // Sentinel's behavior ensureRemoved(MONITOR_MASTER_NAME); ensureRemoved(REMOVE_MASTER_NAME); } @Test public void sentinel() { Jedis j = new Jedis(sentinel); try { List> masters = j.sentinelMasters(); boolean inMasters = false; for (Map master : masters) if (MASTER_NAME.equals(master.get("name"))) inMasters = true; assertTrue(inMasters); List masterHostAndPort = j.sentinelGetMasterAddrByName(MASTER_NAME); HostAndPort masterFromSentinel = new HostAndPort(masterHostAndPort.get(0), Integer.parseInt(masterHostAndPort.get(1))); assertEquals(master.getPort(), masterFromSentinel.getPort()); List> slaves = j.sentinelReplicas(MASTER_NAME); assertFalse(slaves.isEmpty()); assertEquals(master.getPort(), Integer.parseInt(slaves.get(0).get("master-port"))); // DO NOT RE-RUN TEST TOO FAST, RESET TAKES SOME TIME TO... RESET assertEquals(Long.valueOf(1), j.sentinelReset(MASTER_NAME)); assertEquals(Long.valueOf(0), j.sentinelReset("woof" + MASTER_NAME)); } finally { j.close(); } } @Test public void sentinelFailover() throws InterruptedException { Jedis j = new Jedis(sentinelForFailover); Jedis j2 = new Jedis(sentinelForFailover); try { List masterHostAndPort = j.sentinelGetMasterAddrByName(FAILOVER_MASTER_NAME); HostAndPort currentMaster = new HostAndPort(masterHostAndPort.get(0), Integer.parseInt(masterHostAndPort.get(1))); JedisSentinelTestUtil.waitForNewPromotedMaster(FAILOVER_MASTER_NAME, j, j2); masterHostAndPort = j.sentinelGetMasterAddrByName(FAILOVER_MASTER_NAME); HostAndPort newMaster = new HostAndPort(masterHostAndPort.get(0), Integer.parseInt(masterHostAndPort.get(1))); assertNotEquals(newMaster, currentMaster); } finally { j.close(); j2.close(); } } @Test public void sentinelMonitor() { Jedis j = new Jedis(sentinel); try { // monitor new master String result = j.sentinelMonitor(MONITOR_MASTER_NAME, master.getHost(), master.getPort(), 1); assertEquals("OK", result); // already monitored try { j.sentinelMonitor(MONITOR_MASTER_NAME, master.getHost(), master.getPort(), 1); fail(); } catch (JedisDataException e) { // pass } } finally { j.close(); } } @Test public void sentinelRemove() { Jedis j = new Jedis(sentinel); try { ensureMonitored(sentinel, REMOVE_MASTER_NAME, master.getHost(), master.getPort(), 1); String result = j.sentinelRemove(REMOVE_MASTER_NAME); assertEquals("OK", result); // not exist try { result = j.sentinelRemove(REMOVE_MASTER_NAME); assertNotEquals("OK", result); fail(); } catch (JedisDataException e) { // pass } } finally { j.close(); } } @Test public void sentinelSet() { Jedis j = new Jedis(sentinel); try { Map parameterMap = new HashMap(); parameterMap.put("down-after-milliseconds", String.valueOf(1234)); parameterMap.put("parallel-syncs", String.valueOf(3)); parameterMap.put("quorum", String.valueOf(2)); j.sentinelSet(MASTER_NAME, parameterMap); List> masters = j.sentinelMasters(); for (Map master : masters) { if (master.get("name").equals(MASTER_NAME)) { assertEquals(1234, Integer.parseInt(master.get("down-after-milliseconds"))); assertEquals(3, Integer.parseInt(master.get("parallel-syncs"))); assertEquals(2, Integer.parseInt(master.get("quorum"))); } } parameterMap.put("quorum", String.valueOf(1)); j.sentinelSet(MASTER_NAME, parameterMap); } finally { j.close(); } } private void ensureMonitored(HostAndPort sentinel, String masterName, String ip, int port, int quorum) { try (Jedis j = new Jedis(sentinel)) { j.sentinelMonitor(masterName, ip, port, quorum); } catch (JedisDataException e) { // ignore } } private void ensureRemoved(String masterName) { Jedis j = new Jedis(sentinel); try { j.sentinelRemove(masterName); } catch (JedisDataException e) { } finally { j.close(); } } } ================================================ FILE: src/test/java/redis/clients/jedis/JedisShardedPubSubBaseTest.java ================================================ package redis.clients.jedis; import org.junit.jupiter.api.Test; import java.util.Arrays; import java.util.List; import java.util.concurrent.CountDownLatch; import java.util.concurrent.TimeUnit; import static org.junit.jupiter.api.Assertions.assertTrue; import static org.junit.jupiter.api.Assertions.fail; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; import static redis.clients.jedis.Protocol.ResponseKeyword.SMESSAGE; import static redis.clients.jedis.Protocol.ResponseKeyword.SSUBSCRIBE; public class JedisShardedPubSubBaseTest { @Test public void testProceed_givenThreadInterrupt_exitLoop() throws InterruptedException { // setup final JedisShardedPubSubBase pubSub = new JedisShardedPubSubBase() { @Override public void onSMessage(String channel, String message) { fail("this should not happen when thread is interrupted"); } @Override protected String encode(byte[] raw) { return new String(raw); } }; final Connection mockConnection = mock(Connection.class); final List mockSubscribe = Arrays.asList( SSUBSCRIBE.getRaw(), "channel".getBytes(), 1L ); final List mockResponse = Arrays.asList( SMESSAGE.getRaw(), "channel".getBytes(), "message".getBytes() ); when(mockConnection.getUnflushedObject()).thenReturn(mockSubscribe, mockResponse); final CountDownLatch countDownLatch = new CountDownLatch(1); // action final Thread thread = new Thread(() -> { Thread.currentThread().interrupt(); pubSub.proceed(mockConnection, "channel"); countDownLatch.countDown(); }); thread.start(); assertTrue(countDownLatch.await(100, TimeUnit.MILLISECONDS)); } } ================================================ FILE: src/test/java/redis/clients/jedis/JedisTest.java ================================================ package redis.clients.jedis; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertSame; import static org.junit.jupiter.api.Assertions.assertThrows; import static org.junit.jupiter.api.Assertions.assertTrue; import static org.junit.jupiter.api.Assertions.fail; import java.io.IOException; import java.net.ServerSocket; import java.net.SocketTimeoutException; import java.net.URI; import java.net.URISyntaxException; import java.time.Duration; import java.time.Instant; import java.util.HashMap; import java.util.Map; import io.redis.test.annotations.ConditionalOnEnv; import io.redis.test.annotations.SinceRedisVersion; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Tag; import org.junit.jupiter.params.ParameterizedClass; import org.junit.jupiter.params.provider.MethodSource; import redis.clients.jedis.exceptions.InvalidURIException; import redis.clients.jedis.exceptions.JedisConnectionException; import redis.clients.jedis.exceptions.JedisException; import redis.clients.jedis.commands.jedis.JedisCommandsTestBase; import redis.clients.jedis.util.SafeEncoder; import redis.clients.jedis.util.TestEnvUtil; @ParameterizedClass @MethodSource("redis.clients.jedis.commands.CommandsTestsParameters#respVersions") @Tag("integration") public class JedisTest extends JedisCommandsTestBase { public JedisTest(RedisProtocol protocol) { super(protocol); } @Test @ConditionalOnEnv(value = TestEnvUtil.ENV_REDIS_ENTERPRISE, enabled = false) public void useWithoutConnecting() { try (Jedis j = new Jedis()) { j.auth(endpoint.getPassword()); j.dbSize(); } } @Test public void checkBinaryData() { byte[] bigdata = new byte[1777]; for (int b = 0; b < bigdata.length; b++) { bigdata[b] = (byte) ((byte) b % 255); } Map hash = new HashMap<>(); hash.put("data", SafeEncoder.encode(bigdata)); assertEquals("OK", jedis.hmset("foo", hash)); assertEquals(hash, jedis.hgetAll("foo")); } @Test public void connectWithConfig() { try (Jedis jedis = new Jedis(endpoint.getHostAndPort(), DefaultJedisClientConfig.builder().build())) { jedis.auth(endpoint.getPassword()); assertEquals("PONG", jedis.ping()); } try (Jedis jedis = new Jedis(endpoint.getHostAndPort(), endpoint.getClientConfigBuilder().build())) { assertEquals("PONG", jedis.ping()); } } @Test public void connectWithEmptyConfigInterface() { try (Jedis jedis = new Jedis(endpoint.getHostAndPort(), new JedisClientConfig() { })) { jedis.auth(endpoint.getPassword()); assertEquals("PONG", jedis.ping()); } } @Test public void connectWithConfigInterface() { try (Jedis jedis = new Jedis(endpoint.getHostAndPort(), new JedisClientConfig() { @Override public String getPassword() { return endpoint.getPassword(); } })) { assertEquals("PONG", jedis.ping()); } } @Test public void connectOnResp3Protocol() { try (Jedis jedis = new Jedis(endpoint.getHostAndPort(), endpoint.getClientConfigBuilder().protocol(RedisProtocol.RESP3).build())) { assertEquals("PONG", jedis.ping()); assertEquals(RedisProtocol.RESP3, jedis.getConnection().getRedisProtocol()); } } @Test public void connectOnResp3ProtocolShortcut() { try (Jedis jedis = new Jedis(endpoint.getHostAndPort(), endpoint.getClientConfigBuilder().resp3().build())) { assertEquals("PONG", jedis.ping()); assertEquals(RedisProtocol.RESP3, jedis.getConnection().getRedisProtocol()); } } @Test @ConditionalOnEnv(value = TestEnvUtil.ENV_REDIS_ENTERPRISE, enabled = false) public void timeoutConnection() throws Exception { final String TIMEOUT_STR = "timeout"; Jedis jedis = new Jedis(endpoint.getHostAndPort(), endpoint.getClientConfigBuilder().timeoutMillis(15000).build()); // read current config final String timeout = jedis.configGet(TIMEOUT_STR).get(TIMEOUT_STR); try { jedis.configSet("timeout", "1"); Thread.sleep(5000); try { jedis.hmget("foobar", "foo"); fail("Operation should throw JedisConnectionException"); } catch (JedisConnectionException jce) { // expected } jedis.close(); } finally { // reset config jedis = new Jedis(endpoint.getHostAndPort(), endpoint.getClientConfigBuilder().build()); jedis.configSet(TIMEOUT_STR, timeout); jedis.close(); } } @Test public void infiniteTimeout() throws Exception { try (Jedis timeoutJedis = new Jedis(endpoint.getHost(), endpoint.getPort(), 200, 200, 200)) { timeoutJedis.auth(endpoint.getPassword()); try { timeoutJedis.blpop(0, "foo"); fail("SocketTimeoutException should occur"); } catch (JedisConnectionException jce) { assertSame(java.net.SocketTimeoutException.class, jce.getCause().getClass()); assertEquals("Read timed out", jce.getCause().getMessage()); assertTrue(timeoutJedis.isBroken()); } } } @Test public void failWhenSendingNullValues() { assertThrows(IllegalArgumentException.class, () -> jedis.set("foo", null)); } @Test public void shouldThrowInvalidURIExceptionForInvalidURI() { assertThrows(InvalidURIException.class, () -> new Jedis(new URI("redis://localhost")).close()); } // // @Test // public void shouldReconnectToSameDB() throws IOException { // jedis.select(1); // jedis.set("foo", "bar"); // jedis.getClient().getSocket().shutdownInput(); // jedis.getClient().getSocket().shutdownOutput(); // assertEquals("bar", jedis.get("foo")); // } @Test @ConditionalOnEnv(value = TestEnvUtil.ENV_REDIS_ENTERPRISE, enabled = false) public void connectWithUrl() { EndpointConfig endpoint = Endpoints.getRedisEndpoint("standalone1"); try (Jedis j = new Jedis(endpoint.getHostAndPort())) { j.auth(endpoint.getPassword()); j.select(2); j.set("foo", "bar"); } try (Jedis j2 = new Jedis( endpoint.getURIBuilder().defaultCredentials().path("/2").build().toString())) { assertEquals("PONG", j2.ping()); assertEquals("bar", j2.get("foo")); } } @Test @ConditionalOnEnv(value = TestEnvUtil.ENV_REDIS_ENTERPRISE, enabled = false) public void connectWithUri() throws URISyntaxException { EndpointConfig endpoint = Endpoints.getRedisEndpoint("standalone1"); try (Jedis j = new Jedis(endpoint.getHostAndPort())) { j.auth(endpoint.getPassword()); j.select(2); j.set("foo", "bar"); } try ( Jedis jedis = new Jedis(endpoint.getURIBuilder().defaultCredentials().path("/2").build())) { assertEquals("PONG", jedis.ping()); assertEquals("bar", jedis.get("foo")); } } @Test @ConditionalOnEnv(value = TestEnvUtil.ENV_REDIS_ENTERPRISE, enabled = false) public void connectWithUrlOnResp3() { EndpointConfig endpoint = Endpoints.getRedisEndpoint("standalone1"); try (Jedis j = new Jedis(endpoint.getHostAndPort())) { j.auth(endpoint.getPassword()); j.select(2); j.set("foo", "bar"); } try (Jedis j2 = new Jedis( endpoint.getURIBuilder().defaultCredentials().path("/2?protocol=3").build().toString())) { assertEquals("PONG", j2.ping()); assertEquals("bar", j2.get("foo")); } } @Test @ConditionalOnEnv(value = TestEnvUtil.ENV_REDIS_ENTERPRISE, enabled = false) public void connectWithUriOnResp3() throws URISyntaxException { EndpointConfig endpoint = Endpoints.getRedisEndpoint("standalone1"); try (Jedis j = new Jedis(endpoint.getHostAndPort())) { j.auth(endpoint.getPassword()); j.select(2); j.set("foo", "bar"); } try (Jedis jedis = new Jedis( endpoint.getURIBuilder().defaultCredentials().path("/2?protocol=3").build())) { assertEquals("PONG", jedis.ping()); assertEquals("bar", jedis.get("foo")); } } @Test public void shouldNotUpdateDbIndexIfSelectFails() { int currentDb = jedis.getDB(); try { int invalidDb = -1; jedis.select(invalidDb); fail("Should throw an exception if tried to select invalid db"); } catch (JedisException e) { assertEquals(currentDb, jedis.getDB()); } } @Test public void allowUrlWithNoDBAndNoPassword() { EndpointConfig endpointStandalone1 = Endpoints.getRedisEndpoint("standalone1"); try (Jedis j1 = new Jedis(endpointStandalone1.getURI().toString())) { j1.auth(endpointStandalone1.getPassword()); // assertEquals("localhost", j1.getClient().getHost()); // assertEquals(6380, j1.getClient().getPort()); assertEquals(0, j1.getDB()); } try (Jedis j2 = new Jedis(endpointStandalone1.getURI().toString())) { j2.auth(endpointStandalone1.getPassword()); // assertEquals("localhost", j2.getClient().getHost()); // assertEquals(6380, j2.getClient().getPort()); assertEquals(0, j2.getDB()); } } @Test public void uriWithDBindexShouldUseTimeout() throws URISyntaxException, IOException { int fakePort = 6378; int timeoutMillis = 3250; int deltaMillis = 500; URI uri = new URI(String.format("redis://localhost:%d/1", fakePort)); Instant start = Instant.now(); try (ServerSocket server = new ServerSocket(fakePort); Jedis jedis = new Jedis(uri, timeoutMillis)) { fail("Jedis should fail to connect to a fake port"); } catch (JedisConnectionException ex) { assertSame(SocketTimeoutException.class, ex.getCause().getClass()); assertEquals(timeoutMillis, Duration.between(start, Instant.now()).toMillis(), deltaMillis); } } @Test public void checkCloseable() { Jedis bj = new Jedis(); bj.close(); } @Test @ConditionalOnEnv(value = TestEnvUtil.ENV_REDIS_ENTERPRISE, enabled = false) public void checkCloseableAfterConnect() { Jedis bj = new Jedis(); bj.connect(); bj.close(); } @Test @ConditionalOnEnv(value = TestEnvUtil.ENV_REDIS_ENTERPRISE, enabled = false) public void checkCloseableAfterCommand() { Jedis bj = new Jedis(); bj.auth(endpoint.getPassword()); bj.close(); } @Test public void checkDisconnectOnQuit() { jedis.disconnect(); assertFalse(jedis.isConnected()); } @Test @SinceRedisVersion(value = "7.2.0", message = "see https://redis.io/docs/latest/commands/client-setinfo/") public void clientSetInfoDefault() { try (Jedis jedis = new Jedis(endpoint.getHostAndPort(), endpoint.getClientConfigBuilder() .clientSetInfoConfig(ClientSetInfoConfig.DEFAULT).build())) { assertEquals("PONG", jedis.ping()); String info = jedis.clientInfo(); assertTrue(info.contains("lib-name=" + JedisMetaInfo.getArtifactId())); assertTrue(info.contains("lib-ver=" + JedisMetaInfo.getVersion())); } } @Test public void clientSetInfoDisabled() { try (Jedis jedis = new Jedis(endpoint.getHostAndPort(), endpoint.getClientConfigBuilder() .clientSetInfoConfig(ClientSetInfoConfig.DISABLED).build())) { assertEquals("PONG", jedis.ping()); String info = jedis.clientInfo(); assertFalse(info.contains("lib-name=" + JedisMetaInfo.getArtifactId())); assertFalse(info.contains("lib-ver=" + JedisMetaInfo.getVersion())); } } @Test @SinceRedisVersion(value = "7.2.0", message = "@see https://redis.io/docs/latest/commands/client-setinfo/") public void clientSetInfoLibNameSuffix() { final String libNameSuffix = "for-redis"; ClientSetInfoConfig setInfoConfig = ClientSetInfoConfig.withLibNameSuffix(libNameSuffix); try (Jedis jedis = new Jedis(endpoint.getHostAndPort(), endpoint.getClientConfigBuilder().clientSetInfoConfig(setInfoConfig).build())) { assertEquals("PONG", jedis.ping()); String info = jedis.clientInfo(); assertTrue( info.contains("lib-name=" + JedisMetaInfo.getArtifactId() + '(' + libNameSuffix + ')')); assertTrue(info.contains("lib-ver=" + JedisMetaInfo.getVersion())); } } @Test @SinceRedisVersion(value = "7.2.0", message = "@see https://redis.io/docs/latest/commands/client-setinfo/") public void clientSetInfoWithUpstreamDriver() { DriverInfo driverInfo = DriverInfo.builder().addUpstreamDriver("spring-data-redis", "3.2.0") .build(); ClientSetInfoConfig setInfoConfig = new ClientSetInfoConfig(driverInfo); try (Jedis jedis = new Jedis(endpoint.getHostAndPort(), endpoint.getClientConfigBuilder().clientSetInfoConfig(setInfoConfig).build())) { assertEquals("PONG", jedis.ping()); String info = jedis.clientInfo(); assertTrue( info.contains("lib-name=" + JedisMetaInfo.getArtifactId() + "(spring-data-redis_v3.2.0)")); assertTrue(info.contains("lib-ver=" + JedisMetaInfo.getVersion())); } } @Test @SinceRedisVersion(value = "7.2.0", message = "@see https://redis.io/docs/latest/commands/client-setinfo/") public void clientSetInfoWithMultipleUpstreamDrivers() { DriverInfo driverInfo = DriverInfo.builder().addUpstreamDriver("spring-data-redis", "3.2.0") .addUpstreamDriver("lettuce-core", "6.4.1").addUpstreamDriver("redisson", "3.25.0").build(); ClientSetInfoConfig setInfoConfig = new ClientSetInfoConfig(driverInfo); try (Jedis jedis = new Jedis(endpoint.getHostAndPort(), endpoint.getClientConfigBuilder().clientSetInfoConfig(setInfoConfig).build())) { assertEquals("PONG", jedis.ping()); String info = jedis.clientInfo(); assertTrue(info.contains("lib-name=" + JedisMetaInfo.getArtifactId() + "(redisson_v3.25.0;lettuce-core_v6.4.1;spring-data-redis_v3.2.0)")); assertTrue(info.contains("lib-ver=" + JedisMetaInfo.getVersion())); } } } ================================================ FILE: src/test/java/redis/clients/jedis/ManagedConnectionProviderTest.java ================================================ package redis.clients.jedis; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Tag; import org.junit.jupiter.api.Test; import redis.clients.jedis.providers.ManagedConnectionProvider; import redis.clients.jedis.util.IOUtils; import static org.junit.jupiter.api.Assertions.assertNull; import static org.junit.jupiter.api.Assertions.fail; @Tag("integration") public class ManagedConnectionProviderTest { private Connection connection; @BeforeEach public void setUp() { EndpointConfig endpoint = Endpoints.getRedisEndpoint("standalone0"); connection = new Connection(endpoint.getHostAndPort(), endpoint.getClientConfigBuilder().build()); } @AfterEach public void tearDown() { IOUtils.closeQuietly(connection); } @Test public void test() { ManagedConnectionProvider managed = new ManagedConnectionProvider(); try (UnifiedJedis jedis = new UnifiedJedis(managed)) { try { jedis.get("any"); fail("Should get NPE."); } catch (NullPointerException npe) { } managed.setConnection(connection); assertNull(jedis.get("any")); } } } ================================================ FILE: src/test/java/redis/clients/jedis/MigratePipeliningTest.java ================================================ package redis.clients.jedis; import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.both; import static org.hamcrest.Matchers.contains; import static org.hamcrest.Matchers.containsString; import static org.hamcrest.Matchers.equalTo; import static org.hamcrest.Matchers.hasToString; import static org.hamcrest.Matchers.instanceOf; import static org.junit.jupiter.api.Assertions.assertArrayEquals; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertNull; import io.redis.test.annotations.ConditionalOnEnv; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.params.ParameterizedClass; import org.junit.jupiter.params.provider.MethodSource; import redis.clients.jedis.commands.jedis.JedisCommandsTestBase; import redis.clients.jedis.exceptions.JedisDataException; import redis.clients.jedis.params.MigrateParams; import redis.clients.jedis.util.TestEnvUtil; @ParameterizedClass @MethodSource("redis.clients.jedis.commands.CommandsTestsParameters#respVersions") @ConditionalOnEnv(value = TestEnvUtil.ENV_REDIS_ENTERPRISE, enabled = false) public class MigratePipeliningTest extends JedisCommandsTestBase { private static final byte[] bfoo = { 0x01, 0x02, 0x03 }; private static final byte[] bbar = { 0x04, 0x05, 0x06 }; private static final byte[] bfoo1 = { 0x07, 0x08, 0x01 }; private static final byte[] bbar1 = { 0x09, 0x00, 0x01 }; private static final byte[] bfoo2 = { 0x07, 0x08, 0x02 }; private static final byte[] bbar2 = { 0x09, 0x00, 0x02 }; private static final byte[] bfoo3 = { 0x07, 0x08, 0x03 }; private static final byte[] bbar3 = { 0x09, 0x00, 0x03 }; private static EndpointConfig destEndpoint; private static EndpointConfig destEndpointWithAuth; private static String host; private static int port; private static int portAuth; private static final int db = 2; private static final int dbAuth = 3; private static final int timeout = Protocol.DEFAULT_TIMEOUT; private Jedis dest; private Jedis destAuth; @BeforeAll public static void prepareEndpoints() { destEndpoint = Endpoints.getRedisEndpoint("standalone7-with-lfu-policy"); destEndpointWithAuth = Endpoints.getRedisEndpoint("standalone1"); host = destEndpoint.getHost(); port = destEndpoint.getPort(); portAuth = destEndpointWithAuth.getPort(); } public MigratePipeliningTest(RedisProtocol protocol) { super(protocol); } @BeforeEach @Override public void setUp() throws Exception { super.setUp(); dest = new Jedis(host, port, 500); dest.flushAll(); dest.select(db); destAuth = new Jedis(destEndpointWithAuth.getHostAndPort(), destEndpointWithAuth.getClientConfigBuilder().build()); destAuth.flushAll(); destAuth.select(dbAuth); } @AfterEach @Override public void tearDown() throws Exception { dest.close(); destAuth.close(); super.tearDown(); } @Test public void noKey() { Pipeline p = jedis.pipelined(); p.migrate(host, port, "foo", db, timeout); p.migrate(host, port, bfoo, db, timeout); p.migrate(host, port, db, timeout, new MigrateParams(), "foo1", "foo2", "foo3"); p.migrate(host, port, db, timeout, new MigrateParams(), bfoo1, bfoo2, bfoo3); assertThat(p.syncAndReturnAll(), contains("NOKEY", "NOKEY", "NOKEY", "NOKEY")); } @Test public void migrate() { assertNull(dest.get("foo")); Pipeline p = jedis.pipelined(); p.set("foo", "bar"); p.migrate(host, port, "foo", db, timeout); p.get("foo"); assertThat(p.syncAndReturnAll(), contains("OK", "OK", null)); assertEquals("bar", dest.get("foo")); } @Test public void migrateBinary() { assertNull(dest.get(bfoo)); Pipeline p = jedis.pipelined(); p.set(bfoo, bbar); p.migrate(host, port, bfoo, db, timeout); p.get(bfoo); assertThat(p.syncAndReturnAll(), contains("OK", "OK", null)); assertArrayEquals(bbar, dest.get(bfoo)); } @Test public void migrateEmptyParams() { assertNull(dest.get("foo")); Pipeline p = jedis.pipelined(); p.set("foo", "bar"); p.migrate(host, port, db, timeout, new MigrateParams(), "foo"); p.get("foo"); assertThat(p.syncAndReturnAll(), contains("OK", "OK", null)); assertEquals("bar", dest.get("foo")); } @Test public void migrateEmptyParamsBinary() { assertNull(dest.get(bfoo)); Pipeline p = jedis.pipelined(); p.set(bfoo, bbar); p.migrate(host, port, db, timeout, new MigrateParams(), bfoo); p.get(bfoo); assertThat(p.syncAndReturnAll(), contains("OK", "OK", null)); assertArrayEquals(bbar, dest.get(bfoo)); } @Test public void migrateCopy() { assertNull(dest.get("foo")); Pipeline p = jedis.pipelined(); p.set("foo", "bar"); p.migrate(host, port, db, timeout, new MigrateParams().copy(), "foo"); p.get("foo"); assertThat(p.syncAndReturnAll(), contains("OK", "OK", "bar")); assertEquals("bar", dest.get("foo")); } @Test public void migrateCopyBinary() { assertNull(dest.get(bfoo)); Pipeline p = jedis.pipelined(); p.set(bfoo, bbar); p.migrate(host, port, db, timeout, new MigrateParams().copy(), bfoo); p.get(bfoo); assertThat(p.syncAndReturnAll(), contains("OK", "OK", bbar)); assertArrayEquals(bbar, dest.get(bfoo)); } @Test public void migrateReplace() { dest.set("foo", "bar2"); assertEquals("bar2", dest.get("foo")); Pipeline p = jedis.pipelined(); p.set("foo", "bar1"); p.migrate(host, port, db, timeout, new MigrateParams().replace(), "foo"); p.get("foo"); assertThat(p.syncAndReturnAll(), contains("OK", "OK", null)); assertEquals("bar1", dest.get("foo")); } @Test public void migrateReplaceBinary() { dest.set(bfoo, bbar2); assertArrayEquals(bbar2, dest.get(bfoo)); Pipeline p = jedis.pipelined(); p.set(bfoo, bbar1); p.migrate(host, port, db, timeout, new MigrateParams().replace(), bfoo); p.get(bfoo); assertThat(p.syncAndReturnAll(), contains("OK", "OK", null)); assertArrayEquals(bbar1, dest.get(bfoo)); } @Test public void migrateCopyReplace() { dest.set("foo", "bar2"); assertEquals("bar2", dest.get("foo")); Pipeline p = jedis.pipelined(); p.set("foo", "bar1"); p.migrate(host, port, db, timeout, new MigrateParams().copy().replace(), "foo"); p.get("foo"); assertThat(p.syncAndReturnAll(), contains("OK", "OK", "bar1")); assertEquals("bar1", dest.get("foo")); } @Test public void migrateCopyReplaceBinary() { dest.set(bfoo, bbar2); assertArrayEquals(bbar2, dest.get(bfoo)); Pipeline p = jedis.pipelined(); p.set(bfoo, bbar1); p.migrate(host, port, db, timeout, new MigrateParams().copy().replace(), bfoo); p.get(bfoo); assertThat(p.syncAndReturnAll(), contains("OK", "OK", bbar1)); assertArrayEquals(bbar1, dest.get(bfoo)); } @Test public void migrateAuth() { assertNull(dest.get("foo")); Pipeline p = jedis.pipelined(); p.set("foo", "bar"); p.migrate(host, portAuth, dbAuth, timeout, new MigrateParams().auth(destEndpointWithAuth.getPassword()), "foo"); p.get("foo"); assertThat(p.syncAndReturnAll(), contains("OK", "OK", null)); assertEquals("bar", destAuth.get("foo")); } @Test public void migrateAuthBinary() { assertNull(dest.get(bfoo)); Pipeline p = jedis.pipelined(); p.set(bfoo, bbar); p.migrate(host, portAuth, dbAuth, timeout, new MigrateParams().auth(destEndpointWithAuth.getPassword()), bfoo); p.get(bfoo); assertThat(p.syncAndReturnAll(), contains("OK", "OK", null)); assertArrayEquals(bbar, destAuth.get(bfoo)); } @Test public void migrateAuth2() { assertNull(jedis.get("foo")); Pipeline p = destAuth.pipelined(); p.set("foo", "bar"); p.migrate(endpoint.getHost(), endpoint.getPort(), 0, timeout, new MigrateParams().auth2(endpoint.getUsername(), endpoint.getPassword()), "foo"); p.get("foo"); assertThat(p.syncAndReturnAll(), contains("OK", "OK", null)); assertEquals("bar", jedis.get("foo")); } @Test public void migrateAuth2Binary() { assertNull(jedis.get(bfoo)); Pipeline p = dest.pipelined(); p.set(bfoo, bbar); p.migrate(endpoint.getHost(), endpoint.getPort(), 0, timeout, new MigrateParams().auth2(endpoint.getUsername(), endpoint.getPassword()), bfoo); p.get(bfoo); assertThat(p.syncAndReturnAll(), contains("OK", "OK", null)); assertArrayEquals(bbar, jedis.get(bfoo)); } @Test public void migrateMulti() { assertNull(dest.get("foo1")); assertNull(dest.get("foo2")); assertNull(dest.get("foo3")); Pipeline p = jedis.pipelined(); p.mset("foo1", "bar1", "foo2", "bar2", "foo3", "bar3"); p.migrate(host, port, db, timeout, new MigrateParams(), "foo1", "foo2", "foo3"); assertThat(p.syncAndReturnAll(), contains("OK", "OK")); assertEquals("bar1", dest.get("foo1")); assertEquals("bar2", dest.get("foo2")); assertEquals("bar3", dest.get("foo3")); } @Test public void migrateMultiBinary() { assertNull(dest.get(bfoo1)); assertNull(dest.get(bfoo2)); assertNull(dest.get(bfoo3)); Pipeline p = jedis.pipelined(); p.mset(bfoo1, bbar1, bfoo2, bbar2, bfoo3, bbar3); p.migrate(host, port, db, timeout, new MigrateParams(), bfoo1, bfoo2, bfoo3); assertThat(p.syncAndReturnAll(), contains("OK", "OK")); assertArrayEquals(bbar1, dest.get(bfoo1)); assertArrayEquals(bbar2, dest.get(bfoo2)); assertArrayEquals(bbar3, dest.get(bfoo3)); } @Test public void migrateConflict() { dest.set("foo2", "bar"); assertNull(dest.get("foo1")); assertEquals("bar", dest.get("foo2")); assertNull(dest.get("foo3")); Pipeline p = jedis.pipelined(); p.mset("foo1", "bar1", "foo2", "bar2", "foo3", "bar3"); p.migrate(host, port, db, timeout, new MigrateParams(), "foo1", "foo2", "foo3"); assertThat(p.syncAndReturnAll(), contains( equalTo("OK"), both(instanceOf(JedisDataException.class)).and(hasToString(containsString("BUSYKEY"))) )); assertEquals("bar1", dest.get("foo1")); assertEquals("bar", dest.get("foo2")); assertEquals("bar3", dest.get("foo3")); } @Test public void migrateConflictBinary() { dest.set(bfoo2, bbar); assertNull(dest.get(bfoo1)); assertArrayEquals(bbar, dest.get(bfoo2)); assertNull(dest.get(bfoo3)); Pipeline p = jedis.pipelined(); p.mset(bfoo1, bbar1, bfoo2, bbar2, bfoo3, bbar3); p.migrate(host, port, db, timeout, new MigrateParams(), bfoo1, bfoo2, bfoo3); assertThat(p.syncAndReturnAll(), contains( equalTo("OK"), both(instanceOf(JedisDataException.class)).and(hasToString(containsString("BUSYKEY"))) )); assertArrayEquals(bbar1, dest.get(bfoo1)); assertArrayEquals(bbar, dest.get(bfoo2)); assertArrayEquals(bbar3, dest.get(bfoo3)); } } ================================================ FILE: src/test/java/redis/clients/jedis/MultiDbClientTest.java ================================================ package redis.clients.jedis; import eu.rekawek.toxiproxy.Proxy; import eu.rekawek.toxiproxy.ToxiproxyClient; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.Tag; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.AfterAll; import org.junit.jupiter.api.AfterEach; import static org.awaitility.Awaitility.await; import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.equalTo; import static org.hamcrest.Matchers.hasItems; import static org.hamcrest.Matchers.not; import static org.junit.jupiter.api.Assertions.*; import redis.clients.jedis.MultiDbConfig.DatabaseConfig; import redis.clients.jedis.exceptions.JedisValidationException; import redis.clients.jedis.mcf.DatabaseSwitchEvent; import redis.clients.jedis.mcf.SwitchReason; import java.io.IOException; import java.time.Duration; import java.util.ArrayList; import java.util.List; import java.util.function.Consumer; /** * Basic tests for MultiDbClient functionality. */ @Tag("integration") public class MultiDbClientTest { private MultiDbClient client; private static EndpointConfig endpoint1; private static EndpointConfig endpoint2; private static final ToxiproxyClient tp = new ToxiproxyClient("localhost", 8474); private static Proxy redisProxy1; private static Proxy redisProxy2; @BeforeAll public static void setupAdminClients() throws IOException { endpoint1 = Endpoints.getRedisEndpoint("redis-failover-1"); endpoint2 = Endpoints.getRedisEndpoint("redis-failover-2"); if (tp.getProxyOrNull("redis-1") != null) { tp.getProxy("redis-1").delete(); } if (tp.getProxyOrNull("redis-2") != null) { tp.getProxy("redis-2").delete(); } redisProxy1 = tp.createProxy("redis-1", "0.0.0.0:29379", "redis-failover-1:9379"); redisProxy2 = tp.createProxy("redis-2", "0.0.0.0:29380", "redis-failover-2:9380"); } @AfterAll public static void cleanupAdminClients() throws IOException { if (redisProxy1 != null) redisProxy1.delete(); if (redisProxy2 != null) redisProxy2.delete(); } @BeforeEach void setUp() { // Create a simple resilient client with mock endpoints for testing MultiDbConfig clientConfig = MultiDbConfig.builder() .database(endpoint1.getHostAndPort(), 100.0f, endpoint1.getClientConfigBuilder().build()) .database(endpoint2.getHostAndPort(), 50.0f, endpoint2.getClientConfigBuilder().build()) .build(); client = MultiDbClient.builder().multiDbConfig(clientConfig).build(); } @AfterEach void tearDown() { if (client != null) { client.close(); } } @Test void testAddRemoveDatabaseWithEndpointInterface() { Endpoint newEndpoint = new HostAndPort("unavailable", 6381); assertDoesNotThrow( () -> client.addDatabase(newEndpoint, 25.0f, DefaultJedisClientConfig.builder().build())); assertThat(client.getDatabaseEndpoints(), hasItems(newEndpoint)); assertDoesNotThrow(() -> client.removeDatabase(newEndpoint)); assertThat(client.getDatabaseEndpoints(), not(hasItems(newEndpoint))); } @Test void testAddRemoveDatabaseWithDatabaseConfig() { // todo : (@ggivo) Replace HostAndPort with Endpoint HostAndPort newEndpoint = new HostAndPort("unavailable", 6381); DatabaseConfig newConfig = DatabaseConfig .builder(newEndpoint, DefaultJedisClientConfig.builder().build()).weight(25.0f).build(); assertDoesNotThrow(() -> client.addDatabase(newConfig)); assertThat(client.getDatabaseEndpoints(), hasItems(newEndpoint)); assertDoesNotThrow(() -> client.removeDatabase(newEndpoint)); assertThat(client.getDatabaseEndpoints(), not(hasItems(newEndpoint))); } @Test void testSetActiveDatabase() { Endpoint endpoint = client.getActiveDatabaseEndpoint(); awaitIsHealthy(endpoint1.getHostAndPort()); awaitIsHealthy(endpoint2.getHostAndPort()); // Ensure we have a healthy endpoint to switch to Endpoint newEndpoint = client.getDatabaseEndpoints().stream() .filter(e -> e.equals(endpoint) && client.isHealthy(e)).findFirst().orElse(null); assertNotNull(newEndpoint); // Switch to the new endpoint client.setActiveDatabase(newEndpoint); assertEquals(newEndpoint, client.getActiveDatabaseEndpoint()); } @Test void testBuilderWithMultipleEndpointTypes() { MultiDbConfig clientConfig = MultiDbConfig.builder() .database(endpoint1.getHostAndPort(), 100.0f, DefaultJedisClientConfig.builder().build()) .database(DatabaseConfig .builder(endpoint2.getHostAndPort(), DefaultJedisClientConfig.builder().build()) .weight(50.0f).build()) .build(); try (MultiDbClient testClient = MultiDbClient.builder().multiDbConfig(clientConfig).build()) { assertThat(testClient.getDatabaseEndpoints().size(), equalTo(2)); assertThat(testClient.getDatabaseEndpoints(), hasItems(endpoint1.getHostAndPort(), endpoint2.getHostAndPort())); } } @Test public void testForceActiveDatabase() { Endpoint endpoint = client.getActiveDatabaseEndpoint(); // Ensure we have a healthy endpoint to switch to awaitIsHealthy(endpoint1.getHostAndPort()); awaitIsHealthy(endpoint2.getHostAndPort()); Endpoint newEndpoint = client.getDatabaseEndpoints().stream() .filter(e -> e.equals(endpoint) && client.isHealthy(e)).findFirst().orElse(null); assertNotNull(newEndpoint); // Force switch to the new endpoint for 10 seconds client.forceActiveDatabase(newEndpoint, Duration.ofMillis(100).toMillis()); // Verify the active endpoint has changed assertEquals(newEndpoint, client.getActiveDatabaseEndpoint()); } @Test public void testForceActiveDatabaseWithNonHealthyEndpoint() { Endpoint newEndpoint = new HostAndPort("unavailable", 6381); client.addDatabase(newEndpoint, 25.0f, DefaultJedisClientConfig.builder().build()); assertThrows(JedisValidationException.class, () -> client.forceActiveDatabase(newEndpoint, Duration.ofMillis(100).toMillis())); } @Test public void testForceActiveDatabaseWithNonExistingEndpoint() { Endpoint newEndpoint = new HostAndPort("unavailable", 6381); assertThrows(JedisValidationException.class, () -> client.forceActiveDatabase(newEndpoint, Duration.ofMillis(100).toMillis())); } @Test public void testWithDatabaseSwitchListener() { MultiDbConfig endpointsConfig = MultiDbConfig.builder() .database(DatabaseConfig .builder(endpoint1.getHostAndPort(), endpoint1.getClientConfigBuilder().build()) .weight(100.0f).build()) .database(DatabaseConfig .builder(endpoint2.getHostAndPort(), endpoint2.getClientConfigBuilder().build()) .weight(50.0f).build()) .build(); Consumer eventConsumer; List events = new ArrayList<>(); eventConsumer = events::add; try (MultiDbClient testClient = MultiDbClient.builder().databaseSwitchListener(eventConsumer) .multiDbConfig(endpointsConfig).build()) { assertThat(events.size(), equalTo(0)); awaitIsHealthy(endpoint2.getHostAndPort()); testClient.setActiveDatabase(endpoint2.getHostAndPort()); assertThat(events.size(), equalTo(1)); assertThat(events.get(0).getEndpoint(), equalTo(endpoint2.getHostAndPort())); assertThat(events.get(0).getReason(), equalTo(SwitchReason.FORCED)); } } @Test void testGetWeight() { // Verify we can get the initial weight set during configuration float weight1 = client.getWeight(endpoint1.getHostAndPort()); float weight2 = client.getWeight(endpoint2.getHostAndPort()); assertEquals(100.0f, weight1); assertEquals(50.0f, weight2); } @Test void testSetWeight() { Endpoint endpoint = endpoint1.getHostAndPort(); // Verify initial weight assertEquals(100.0f, client.getWeight(endpoint)); // Set a new weight client.setWeight(endpoint, 75.0f); // Verify the weight has changed assertEquals(75.0f, client.getWeight(endpoint)); } @Test void testSetWeightToZero() { Endpoint endpoint = endpoint2.getHostAndPort(); assertThrows(IllegalArgumentException.class, () -> client.setWeight(endpoint, 0.0f)); } @Test void testSetWeightMultipleTimes() { Endpoint endpoint = endpoint1.getHostAndPort(); // Set weight multiple times client.setWeight(endpoint, 25.0f); assertEquals(25.0f, client.getWeight(endpoint)); client.setWeight(endpoint, 80.0f); assertEquals(80.0f, client.getWeight(endpoint)); client.setWeight(endpoint, 1.0f); assertEquals(1.0f, client.getWeight(endpoint)); } private void awaitIsHealthy(HostAndPort hostAndPort) { await().atMost(Duration.ofSeconds(1)).until(() -> client.isHealthy(hostAndPort)); } } ================================================ FILE: src/test/java/redis/clients/jedis/PipeliningTest.java ================================================ package redis.clients.jedis; import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.equalTo; import static org.hamcrest.Matchers.hasItems; import static org.hamcrest.Matchers.instanceOf; import static org.hamcrest.Matchers.matchesPattern; import static org.junit.jupiter.api.Assertions.assertArrayEquals; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertNotNull; import static org.junit.jupiter.api.Assertions.assertNull; import static org.junit.jupiter.api.Assertions.assertThrows; import static org.junit.jupiter.api.Assertions.assertTrue; import static org.junit.jupiter.api.Assertions.fail; import java.io.IOException; import java.util.Arrays; import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.Set; import java.util.UUID; import java.util.concurrent.TimeUnit; import io.redis.test.annotations.ConditionalOnEnv; import org.hamcrest.Matcher; import org.hamcrest.Matchers; import org.junit.jupiter.api.Test; import org.junit.jupiter.params.ParameterizedClass; import org.junit.jupiter.params.provider.MethodSource; import org.awaitility.Awaitility; import redis.clients.jedis.commands.ProtocolCommand; import redis.clients.jedis.commands.jedis.JedisCommandsTestBase; import redis.clients.jedis.exceptions.JedisDataException; import redis.clients.jedis.params.SetParams; import redis.clients.jedis.resps.Tuple; import redis.clients.jedis.util.SafeEncoder; import redis.clients.jedis.util.TestEnvUtil; @ParameterizedClass @MethodSource("redis.clients.jedis.commands.CommandsTestsParameters#respVersions") public class PipeliningTest extends JedisCommandsTestBase { private static final byte[] bfoo = { 0x01, 0x02, 0x03, 0x04 }; private static final byte[] bfoo1 = { 0x01, 0x02, 0x03, 0x04, 0x11, 0x12, 0x13, 0x14 }; private static final byte[] bbar = { 0x05, 0x06, 0x07, 0x08 }; private static final byte[] bbaz = { 0x09, 0x0A, 0x0B, 0x0C }; public PipeliningTest(RedisProtocol protocol) { super(protocol); } @Test public void pipeline() { Pipeline p = jedis.pipelined(); p.set("foo", "bar"); p.get("foo"); List results = p.syncAndReturnAll(); assertEquals(2, results.size()); assertEquals("OK", results.get(0)); assertEquals("bar", results.get(1)); } @Test public void pipelineResponse() { jedis.set("string", "foo"); jedis.lpush("list", "foo"); jedis.hset("hash", "foo", "bar"); jedis.zadd("zset", 1, "foo"); jedis.sadd("set", "foo"); jedis.setrange("setrange", 0, "0123456789"); byte[] bytesForSetRange = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 }; jedis.setrange("setrangebytes".getBytes(), 0, bytesForSetRange); Pipeline p = jedis.pipelined(); Response string = p.get("string"); Response list = p.lpop("list"); Response hash = p.hget("hash", "foo"); Response> zset = p.zrange("zset", 0, -1); Response set = p.spop("set"); Response blist = p.exists("list"); Response zincrby = p.zincrby("zset", 1, "foo"); Response zcard = p.zcard("zset"); p.lpush("list", "bar"); Response> lrange = p.lrange("list", 0, -1); Response> hgetAll = p.hgetAll("hash"); p.sadd("set", "foo"); Response> smembers = p.smembers("set"); Response> zrangeWithScores = p.zrangeWithScores("zset", 0, -1); Response getrange = p.getrange("setrange", 1, 3); Response getrangeBytes = p.getrange("setrangebytes".getBytes(), 6, 8); p.sync(); assertEquals("foo", string.get()); assertEquals("foo", list.get()); assertEquals("bar", hash.get()); assertEquals("foo", zset.get().iterator().next()); assertEquals("foo", set.get()); assertEquals(false, blist.get()); assertEquals(Double.valueOf(2), zincrby.get()); assertEquals(Long.valueOf(1), zcard.get()); assertEquals(1, lrange.get().size()); assertNotNull(hgetAll.get().get("foo")); assertEquals(1, smembers.get().size()); assertEquals(1, zrangeWithScores.get().size()); assertEquals("123", getrange.get()); byte[] expectedGetRangeBytes = { 6, 7, 8 }; assertArrayEquals(expectedGetRangeBytes, getrangeBytes.get()); } @Test public void intermediateSyncs() { jedis.set("string", "foo"); jedis.lpush("list", "foo"); jedis.hset("hash", "foo", "bar"); jedis.zadd("zset", 1, "foo"); jedis.sadd("set", "foo"); jedis.setrange("setrange", 0, "0123456789"); byte[] bytesForSetRange = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 }; jedis.setrange("setrangebytes".getBytes(), 0, bytesForSetRange); Pipeline p = jedis.pipelined(); Response string = p.get("string"); Response list = p.lpop("list"); Response hash = p.hget("hash", "foo"); Response> zset = p.zrange("zset", 0, -1); Response set = p.spop("set"); Response blist = p.exists("list"); p.sync(); assertEquals("foo", string.get()); assertEquals("foo", list.get()); assertEquals("bar", hash.get()); assertEquals("foo", zset.get().iterator().next()); assertEquals("foo", set.get()); assertEquals(false, blist.get()); Response zincrby = p.zincrby("zset", 1, "foo"); Response zcard = p.zcard("zset"); p.lpush("list", "bar"); Response> lrange = p.lrange("list", 0, -1); Response> hgetAll = p.hgetAll("hash"); p.sadd("set", "foo"); p.sync(); assertEquals(Double.valueOf(2), zincrby.get()); assertEquals(Long.valueOf(1), zcard.get()); assertEquals(1, lrange.get().size()); assertNotNull(hgetAll.get().get("foo")); Response> smembers = p.smembers("set"); Response> zrangeWithScores = p.zrangeWithScores("zset", 0, -1); Response getrange = p.getrange("setrange", 1, 3); Response getrangeBytes = p.getrange("setrangebytes".getBytes(), 6, 8); p.sync(); assertEquals(1, smembers.get().size()); assertEquals(1, zrangeWithScores.get().size()); assertEquals("123", getrange.get()); byte[] expectedGetRangeBytes = { 6, 7, 8 }; assertArrayEquals(expectedGetRangeBytes, getrangeBytes.get()); } @Test public void pipelineResponseWithData() { jedis.zadd("zset", 1, "foo"); Pipeline p = jedis.pipelined(); Response score = p.zscore("zset", "foo"); p.sync(); assertNotNull(score.get()); } @Test public void pipelineBinarySafeHashCommands() { jedis.hset("key".getBytes(), "f1".getBytes(), "v111".getBytes()); jedis.hset("key".getBytes(), "f22".getBytes(), "v2222".getBytes()); Pipeline p = jedis.pipelined(); Response> fmap = p.hgetAll("key".getBytes()); Response> fkeys = p.hkeys("key".getBytes()); Response> fordered = p.hmget("key".getBytes(), "f22".getBytes(), "f1".getBytes()); Response> fvals = p.hvals("key".getBytes()); p.sync(); assertNotNull(fmap.get()); // we have to do these strange contortions because byte[] is not a very // good key // for a java Map. It only works with equality (you need the exact key // object to retrieve // the value) I recommend we switch to using ByteBuffer or something // similar: // http://stackoverflow.com/questions/1058149/using-a-byte-array-as-hashmap-key-java Map map = fmap.get(); Set mapKeys = map.keySet(); Iterator iterMap = mapKeys.iterator(); byte[] firstMapKey = iterMap.next(); byte[] secondMapKey = iterMap.next(); assertFalse(iterMap.hasNext()); verifyHasBothValues(firstMapKey, secondMapKey, "f1".getBytes(), "f22".getBytes()); byte[] firstMapValue = map.get(firstMapKey); byte[] secondMapValue = map.get(secondMapKey); verifyHasBothValues(firstMapValue, secondMapValue, "v111".getBytes(), "v2222".getBytes()); assertNotNull(fkeys.get()); Iterator iter = fkeys.get().iterator(); byte[] firstKey = iter.next(); byte[] secondKey = iter.next(); assertFalse(iter.hasNext()); verifyHasBothValues(firstKey, secondKey, "f1".getBytes(), "f22".getBytes()); assertNotNull(fordered.get()); assertArrayEquals("v2222".getBytes(), fordered.get().get(0)); assertArrayEquals("v111".getBytes(), fordered.get().get(1)); assertNotNull(fvals.get()); assertEquals(2, fvals.get().size()); byte[] firstValue = fvals.get().get(0); byte[] secondValue = fvals.get().get(1); verifyHasBothValues(firstValue, secondValue, "v111".getBytes(), "v2222".getBytes()); } private void verifyHasBothValues(byte[] firstKey, byte[] secondKey, byte[] value1, byte[] value2) { assertFalse(Arrays.equals(firstKey, secondKey)); assertTrue(Arrays.equals(firstKey, value1) || Arrays.equals(firstKey, value2)); assertTrue(Arrays.equals(secondKey, value1) || Arrays.equals(secondKey, value2)); } @Test @ConditionalOnEnv(value = TestEnvUtil.ENV_REDIS_ENTERPRISE, enabled = false) public void pipelineSelect() { jedis.set("foo", "bar"); jedis.swapDB(0, 1); Pipeline p = jedis.pipelined(); p.get("foo"); p.select(1); p.get("foo"); assertEquals(Arrays.asList(null, "OK", "bar"), p.syncAndReturnAll()); } @Test public void pipelineResponseWithoutData() { jedis.zadd("zset", 1, "foo"); Pipeline p = jedis.pipelined(); Response score = p.zscore("zset", "bar"); p.sync(); assertNull(score.get()); } @Test public void pipelineResponseWithinPipeline() { jedis.set("string", "foo"); Pipeline p = jedis.pipelined(); Response string = p.get("string"); assertThrows(IllegalStateException.class,string::get); p.sync(); } @Test public void publishInPipeline() { Pipeline pipelined = jedis.pipelined(); Response p1 = pipelined.publish("foo", "bar"); Response p2 = pipelined.publish("foo".getBytes(), "bar".getBytes()); pipelined.sync(); assertEquals(0, p1.get().longValue()); assertEquals(0, p2.get().longValue()); } @Test public void canRetrieveUnsetKey() { Pipeline p = jedis.pipelined(); Response shouldNotExist = p.get(UUID.randomUUID().toString()); p.sync(); assertNull(shouldNotExist.get()); } @Test public void piplineWithError() { Pipeline p = jedis.pipelined(); p.set("foo", "bar"); Response> error = p.smembers("foo"); Response r = p.get("foo"); p.sync(); try { error.get(); fail(); } catch (JedisDataException e) { // that is fine we should be here } assertEquals(r.get(), "bar"); } @Test public void testJedisThrowExceptionWhenInPipeline() { Pipeline pipeline = jedis.pipelined(); pipeline.set("foo", "3"); assertThrows(IllegalStateException.class, () -> jedis.get("somekey")); } @Test public void testReuseJedisWhenPipelineIsEmpty() { Pipeline pipeline = jedis.pipelined(); pipeline.set("foo", "3"); pipeline.sync(); String result = jedis.get("foo"); assertEquals(result, "3"); } @Test public void testResetStateWhenInPipeline() { Pipeline pipeline = jedis.pipelined(); pipeline.set("foo", "3"); jedis.resetState(); String result = jedis.get("foo"); assertEquals(result, "3"); } @Test public void waitReplicas() { EndpointConfig endpoint = Endpoints.getRedisEndpoint("standalone4-replica-of-standalone1"); Pipeline p = jedis.pipelined(); p.set("wait", "replicas"); p.waitReplicas(1, 10); p.sync(); try (Jedis j = new Jedis(endpoint.getHostAndPort())) { j.auth(endpoint.getPassword()); assertEquals("replicas", j.get("wait")); } } @Test public void waitAof() { EndpointConfig endpoint = Endpoints.getRedisEndpoint("standalone4-replica-of-standalone1"); Pipeline p = jedis.pipelined(); p.set("wait", "aof"); p.waitAOF(1L, 0L, 0L); p.sync(); try (Jedis j = new Jedis(endpoint.getHostAndPort())) { j.auth(endpoint.getPassword()); Awaitility.await().atMost(5, TimeUnit.SECONDS).pollInterval(50, TimeUnit.MILLISECONDS) .untilAsserted(() -> assertEquals("aof", j.get("wait"))); } } @Test public void setGet() { Pipeline p = jedis.pipelined(); Response _ok = p.set("hello", "world"); Response _world = p.setGet("hello", "jedis", SetParams.setParams()); Response _jedis = p.get("hello"); Response _null = p.setGet("key", "value", SetParams.setParams()); p.sync(); assertEquals("OK", _ok.get()); assertEquals("world", _world.get()); assertEquals("jedis", _jedis.get()); assertNull(_null.get()); } @Test public void setGetBinary() { Pipeline p = jedis.pipelined(); Response _ok = p.set("hello".getBytes(), "world".getBytes()); Response _world = p.setGet("hello".getBytes(), "jedis".getBytes(), SetParams.setParams()); Response _jedis = p.get("hello".getBytes()); Response _null = p.setGet("key".getBytes(), "value".getBytes(), SetParams.setParams()); p.sync(); assertEquals("OK", _ok.get()); assertArrayEquals("world".getBytes(), _world.get()); assertArrayEquals("jedis".getBytes(), _jedis.get()); assertNull(_null.get()); } @Test public void testEval() { String script = "return 'success!'"; Pipeline p = jedis.pipelined(); Response result = p.eval(script); p.sync(); assertEquals("success!", result.get()); } @Test public void testEvalWithBinary() { String script = "return 'success!'"; Pipeline p = jedis.pipelined(); Response result = p.eval(SafeEncoder.encode(script)); p.sync(); assertArrayEquals(SafeEncoder.encode("success!"), (byte[]) result.get()); } @Test public void testEvalKeyAndArg() { String key = "test"; String arg = "3"; String script = "redis.call('INCRBY', KEYS[1], ARGV[1]) redis.call('INCRBY', KEYS[1], ARGV[1])"; Pipeline p = jedis.pipelined(); p.set(key, "0"); Response result0 = p.eval(script, Arrays.asList(key), Arrays.asList(arg)); p.incr(key); Response result1 = p.eval(script, Arrays.asList(key), Arrays.asList(arg)); Response result2 = p.get(key); p.sync(); assertNull(result0.get()); assertNull(result1.get()); assertEquals("13", result2.get()); } @Test public void testEvalKeyAndArgWithBinary() { // binary byte[] bKey = SafeEncoder.encode("test"); byte[] bArg = SafeEncoder.encode("3"); byte[] bScript = SafeEncoder .encode("redis.call('INCRBY', KEYS[1], ARGV[1]) redis.call('INCRBY', KEYS[1], ARGV[1])"); Pipeline bP = jedis.pipelined(); bP.set(bKey, SafeEncoder.encode("0")); Response bResult0 = bP.eval(bScript, Arrays.asList(bKey), Arrays.asList(bArg)); bP.incr(bKey); Response bResult1 = bP.eval(bScript, Arrays.asList(bKey), Arrays.asList(bArg)); Response bResult2 = bP.get(bKey); bP.sync(); assertNull(bResult0.get()); assertNull(bResult1.get()); assertArrayEquals(SafeEncoder.encode("13"), bResult2.get()); } @Test public void testEvalNestedLists() { String script = "return { {KEYS[1]} , {2} }"; Pipeline p = jedis.pipelined(); Response result = p.eval(script, 1, "key1"); p.sync(); List results = (List) result.get(); assertThat((List) results.get(0), Matchers.hasItem("key1")); assertThat((List) results.get(1), Matchers.hasItem(2L)); } @Test public void testEvalNestedListsWithBinary() { byte[] bScript = SafeEncoder.encode("return { {KEYS[1]} , {2} }"); byte[] bKey = SafeEncoder.encode("key1"); Pipeline p = jedis.pipelined(); Response result = p.eval(bScript, 1, bKey); p.sync(); List results = (List) result.get(); assertThat((List) results.get(0), Matchers.hasItem(bKey)); assertThat((List) results.get(1), Matchers.hasItem(2L)); } @Test public void testEvalsha() { String script = "return 'success!'"; String sha1 = jedis.scriptLoad(script); assertTrue(jedis.scriptExists(sha1)); Pipeline p = jedis.pipelined(); Response result = p.evalsha(sha1); p.sync(); assertEquals("success!", result.get()); } @Test public void testEvalshaKeyAndArg() { String key = "test"; String arg = "3"; String script = "redis.call('INCRBY', KEYS[1], ARGV[1]) redis.call('INCRBY', KEYS[1], ARGV[1])"; String sha1 = jedis.scriptLoad(script); assertTrue(jedis.scriptExists(sha1)); Pipeline p = jedis.pipelined(); p.set(key, "0"); Response result0 = p.evalsha(sha1, Arrays.asList(key), Arrays.asList(arg)); p.incr(key); Response result1 = p.evalsha(sha1, Arrays.asList(key), Arrays.asList(arg)); Response result2 = p.get(key); p.sync(); assertNull(result0.get()); assertNull(result1.get()); assertEquals("13", result2.get()); } @Test public void testEvalshaKeyAndArgWithBinary() { byte[] bKey = SafeEncoder.encode("test"); byte[] bArg = SafeEncoder.encode("3"); String script = "redis.call('INCRBY', KEYS[1], ARGV[1]) redis.call('INCRBY', KEYS[1], ARGV[1])"; byte[] bScript = SafeEncoder.encode(script); byte[] bSha1 = jedis.scriptLoad(bScript); assertTrue(jedis.scriptExists(bSha1)); Pipeline p = jedis.pipelined(); p.set(bKey, SafeEncoder.encode("0")); Response result0 = p.evalsha(bSha1, Arrays.asList(bKey), Arrays.asList(bArg)); p.incr(bKey); Response result1 = p.evalsha(bSha1, Arrays.asList(bKey), Arrays.asList(bArg)); Response result2 = p.get(bKey); p.sync(); assertNull(result0.get()); assertNull(result1.get()); assertArrayEquals(SafeEncoder.encode("13"), result2.get()); } @Test public void testSyncWithNoCommandQueued() { // we need to test with fresh instance of Jedis Jedis jedis2 = new Jedis(endpoint.getHost(), endpoint.getPort(), 500); Pipeline pipeline = jedis2.pipelined(); pipeline.sync(); jedis2.close(); jedis2 = new Jedis(endpoint.getHost(), endpoint.getPort(), 500); pipeline = jedis2.pipelined(); List resp = pipeline.syncAndReturnAll(); assertTrue(resp.isEmpty()); jedis2.close(); } @Test public void testCloseable() throws IOException { // we need to test with fresh instance of Jedis Jedis jedis2 = new Jedis(endpoint.getHost(), endpoint.getPort(), 500); jedis2.auth(endpoint.getPassword()); Pipeline pipeline = jedis2.pipelined(); Response retFuture1 = pipeline.set("a", "1"); Response retFuture2 = pipeline.set("b", "2"); pipeline.close(); // it shouldn't meet any exception retFuture1.get(); retFuture2.get(); jedis2.close(); } @Test public void time() { Pipeline p = jedis.pipelined(); p.time(); // we get back one result, with two components: the seconds, and the microseconds, but encoded as strings Matcher timeResponseMatcher = hasItems(matchesPattern("\\d+"), matchesPattern("\\d+")); assertThat(p.syncAndReturnAll(), hasItems(timeResponseMatcher)); } @Test public void dbSize() { Pipeline p = jedis.pipelined(); p.dbSize(); p.set("foo", "bar"); p.dbSize(); assertThat(p.syncAndReturnAll(), hasItems(0L, "OK", 1L)); } @Test @ConditionalOnEnv(value = TestEnvUtil.ENV_REDIS_ENTERPRISE, enabled = false) public void move() { Pipeline p = jedis.pipelined(); p.move("foo", 1); p.set("foo", "bar"); p.move("foo", 1); p.get("foo"); p.select(1); p.get("foo"); assertThat(p.syncAndReturnAll(), hasItems(0L, "OK", 1L, null, "OK", "bar")); } @Test @ConditionalOnEnv(value = TestEnvUtil.ENV_REDIS_ENTERPRISE, enabled = false) public void moveBinary() { Pipeline p = jedis.pipelined(); p.move(bfoo, 1); p.set(bfoo, bbar); p.move(bfoo, 1); p.get(bfoo); p.select(1); p.get(bfoo); assertThat(p.syncAndReturnAll(), hasItems(0L, "OK", 1L, null, "OK", bbar)); } @Test @ConditionalOnEnv(value = TestEnvUtil.ENV_REDIS_ENTERPRISE, enabled = false) public void swapDb() { Pipeline p = jedis.pipelined(); p.set("foo", "bar"); p.get("foo"); p.select(1); p.get("foo"); p.swapDB(0, 1); p.select(0); p.get("foo"); p.select(1); p.get("foo"); assertThat(p.syncAndReturnAll(), hasItems("OK", "bar", "OK", null, "OK", "OK", null, "OK", "bar")); } @Test @ConditionalOnEnv(value = TestEnvUtil.ENV_REDIS_ENTERPRISE, enabled = false) public void copyToAnotherDb() { Pipeline p = jedis.pipelined(); p.copy("foo", "foo-copy", 1, false); p.set("foo", "bar"); p.copy("foo", "foo-copy", 1, false); p.get("foo"); p.select(1); p.get("foo-copy"); p.select(0); p.set("foo", "baz"); p.copy("foo", "foo-copy", 1, false); p.get("foo"); p.select(1); p.get("foo-copy"); assertThat(p.syncAndReturnAll(), hasItems(false, "OK", true, "bar", "OK", "bar", "OK", "OK", false, "baz", "bar")); } @Test @ConditionalOnEnv(value = TestEnvUtil.ENV_REDIS_ENTERPRISE, enabled = false) public void copyToAnotherDbBinary() { Pipeline p = jedis.pipelined(); p.copy(bfoo, bfoo1, 1, false); p.set(bfoo, bbar); p.copy(bfoo, bfoo1, 1, false); p.get(bfoo); p.select(1); p.get(bfoo1); p.select(0); p.set(bfoo, bbaz); p.copy(bfoo, bfoo1, 1, false); p.get(bfoo); p.select(1); p.get(bfoo1); assertThat(p.syncAndReturnAll(), hasItems(false, "OK", true, bbar, "OK", bbar, "OK", "OK", false, bbaz, bbar)); } enum Foo implements ProtocolCommand { FOO; @Override public byte[] getRaw() { return SafeEncoder.encode(name()); } } @Test public void errorInTheMiddle() { CommandObject invalidCommand = new CommandObject<>(new CommandObjects().commandArguments(Foo.FOO), BuilderFactory.STRING); Pipeline p = jedis.pipelined(); p.set("foo", "bar"); p.appendCommand(invalidCommand); p.get("foo"); assertThat(p.syncAndReturnAll(), hasItems( equalTo("OK"), instanceOf(JedisDataException.class), equalTo("bar") )); } } ================================================ FILE: src/test/java/redis/clients/jedis/ProtocolTest.java ================================================ package redis.clients.jedis; import org.junit.jupiter.api.Test; import redis.clients.jedis.util.FragmentedByteArrayInputStream; import static org.junit.jupiter.api.Assertions.assertArrayEquals; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertNull; import static org.junit.jupiter.api.Assertions.assertThrows; import static org.junit.jupiter.api.Assertions.fail; import static redis.clients.jedis.util.AssertUtil.assertByteArrayListEquals; import java.io.BufferedInputStream; import java.io.ByteArrayInputStream; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.io.PipedInputStream; import java.io.PipedOutputStream; import java.util.ArrayList; import java.util.List; import redis.clients.jedis.exceptions.JedisBusyException; import redis.clients.jedis.util.RedisInputStream; import redis.clients.jedis.util.RedisOutputStream; import redis.clients.jedis.util.SafeEncoder; public class ProtocolTest { @Test public void buildACommand() throws IOException { PipedInputStream pis = new PipedInputStream(); BufferedInputStream bis = new BufferedInputStream(pis); PipedOutputStream pos = new PipedOutputStream(pis); RedisOutputStream ros = new RedisOutputStream(pos); // Protocol.sendCommand(ros, Protocol.Command.GET, "SOMEKEY".getBytes(Protocol.CHARSET)); Protocol.sendCommand(ros, new CommandArguments(Protocol.Command.GET).add("SOMEKEY")); ros.flush(); pos.close(); String expectedCommand = "*2\r\n$3\r\nGET\r\n$7\r\nSOMEKEY\r\n"; int b; StringBuilder sb = new StringBuilder(); while ((b = bis.read()) != -1) { sb.append((char) b); } assertEquals(expectedCommand, sb.toString()); } @Test public void writeOverflow() throws IOException { RedisOutputStream ros = new RedisOutputStream(new OutputStream() { @Override public void write(int b) throws IOException { throw new IOException("thrown exception"); } }); ros.write(new byte[8191]); try { ros.write((byte) '*'); } catch (IOException ioe) { //ignore } assertThrows(IOException.class, ()-> ros.write((byte) '*')); } @Test public void bulkReply() { InputStream is = new ByteArrayInputStream("$6\r\nfoobar\r\n".getBytes()); byte[] response = (byte[]) Protocol.read(new RedisInputStream(is)); assertArrayEquals(SafeEncoder.encode("foobar"), response); } @Test public void fragmentedBulkReply() { FragmentedByteArrayInputStream fis = new FragmentedByteArrayInputStream( "$30\r\n012345678901234567890123456789\r\n".getBytes()); byte[] response = (byte[]) Protocol.read(new RedisInputStream(fis)); assertArrayEquals(SafeEncoder.encode("012345678901234567890123456789"), response); } @Test public void nullBulkReply() { InputStream is = new ByteArrayInputStream("$-1\r\n".getBytes()); String response = (String) Protocol.read(new RedisInputStream(is)); assertNull(response); } @Test public void singleLineReply() { InputStream is = new ByteArrayInputStream("+OK\r\n".getBytes()); byte[] response = (byte[]) Protocol.read(new RedisInputStream(is)); assertArrayEquals(SafeEncoder.encode("OK"), response); } @Test public void integerReply() { InputStream is = new ByteArrayInputStream(":123\r\n".getBytes()); long response = (Long) Protocol.read(new RedisInputStream(is)); assertEquals(123, response); } @SuppressWarnings("unchecked") @Test public void multiBulkReply() { InputStream is = new ByteArrayInputStream( "*4\r\n$3\r\nfoo\r\n$3\r\nbar\r\n$5\r\nHello\r\n$5\r\nWorld\r\n".getBytes()); List response = (List) Protocol.read(new RedisInputStream(is)); List expected = new ArrayList(); expected.add(SafeEncoder.encode("foo")); expected.add(SafeEncoder.encode("bar")); expected.add(SafeEncoder.encode("Hello")); expected.add(SafeEncoder.encode("World")); assertByteArrayListEquals(expected, response); } @SuppressWarnings("unchecked") @Test public void nullMultiBulkReply() { InputStream is = new ByteArrayInputStream("*-1\r\n".getBytes()); List response = (List) Protocol.read(new RedisInputStream(is)); assertNull(response); } @Test public void busyReply() { final String busyMessage = "BUSY Redis is busy running a script."; final InputStream is = new ByteArrayInputStream(('-' + busyMessage + "\r\n").getBytes()); try { Protocol.read(new RedisInputStream(is)); } catch (final JedisBusyException e) { assertEquals(busyMessage, e.getMessage()); return; } fail("Expected a JedisBusyException to be thrown."); } } ================================================ FILE: src/test/java/redis/clients/jedis/RedisClientTest.java ================================================ package redis.clients.jedis; import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.anything; import static org.hamcrest.Matchers.equalTo; import static org.hamcrest.Matchers.greaterThanOrEqualTo; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertInstanceOf; import static org.junit.jupiter.api.Assertions.assertThrows; import static org.junit.jupiter.api.Assertions.assertTrue; import static org.junit.jupiter.api.Assertions.fail; import java.net.URI; import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.atomic.AtomicInteger; import io.redis.test.annotations.ConditionalOnEnv; import org.apache.commons.pool2.impl.GenericObjectPoolConfig; import org.junit.jupiter.api.Tag; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.extension.RegisterExtension; import redis.clients.jedis.exceptions.JedisConnectionException; import redis.clients.jedis.exceptions.JedisException; import redis.clients.jedis.util.EnvCondition; import redis.clients.jedis.util.TestEnvUtil; @Tag("integration") public class RedisClientTest { @RegisterExtension public static EnvCondition envCondition = new EnvCondition(); private static EndpointConfig endpointStandalone7; private static EndpointConfig endpointStandalone1; // password protected @BeforeAll public static void prepareEndpoints() { endpointStandalone7 = Endpoints.getRedisEndpoint("standalone7-with-lfu-policy"); endpointStandalone1 = Endpoints.getRedisEndpoint("standalone1"); } @Test public void checkCloseableConnections() { RedisClient pool = RedisClient.builder() .hostAndPort(endpointStandalone7.getHost(), endpointStandalone7.getPort()) .clientConfig(endpointStandalone7.getClientConfigBuilder().timeoutMillis(2000).build()).build(); pool.set("foo", "bar"); assertEquals("bar", pool.get("foo")); pool.close(); assertTrue(pool.getPool().isClosed()); } @Test public void checkResourceWithConfig() { try (RedisClient pool = RedisClient.builder().hostAndPort(endpointStandalone7.getHostAndPort()) .clientConfig(endpointStandalone7.getClientConfigBuilder().socketTimeoutMillis(5000).build()) .build()) { try (Connection jedis = pool.getPool().getResource()) { assertTrue(jedis.ping()); assertEquals(5000, jedis.getSoTimeout()); } } } @Test public void checkPoolOverflow() { GenericObjectPoolConfig config = new GenericObjectPoolConfig<>(); config.setMaxTotal(1); config.setBlockWhenExhausted(false); try ( RedisClient pool = RedisClient.builder().hostAndPort(endpointStandalone7.getHostAndPort()) .poolConfig(config).build(); Connection jedis = pool.getPool().getResource()) { assertThrows(JedisException.class, () -> pool.getPool().getResource()); } } @Test @ConditionalOnEnv(value = TestEnvUtil.ENV_REDIS_ENTERPRISE, enabled = false) @SuppressWarnings("deprecation") public void startWithUrlString() { try (Jedis j = new Jedis(endpointStandalone1.getHostAndPort())) { j.auth(endpointStandalone1.getPassword()); j.select(2); j.set("foo", "bar"); } try (RedisClient pool = RedisClient.builder() .fromURI(endpointStandalone1.getURIBuilder() .credentials("", endpointStandalone1.getPassword()).path("/2").build().toString()) .build()) { assertEquals("bar", pool.get("foo")); } } @Test @ConditionalOnEnv(value = TestEnvUtil.ENV_REDIS_ENTERPRISE, enabled = false) @SuppressWarnings("deprecation") public void startWithUrl() { try (Jedis j = new Jedis(endpointStandalone1.getHostAndPort())) { j.auth(endpointStandalone1.getPassword()); j.select(2); j.set("foo", "bar"); } try (RedisClient pool = RedisClient.builder().fromURI(endpointStandalone1.getURIBuilder() .credentials("", endpointStandalone1.getPassword()).path("/2").build()).build()) { assertEquals("bar", pool.get("foo")); } } @Test @SuppressWarnings("deprecation") public void shouldThrowExceptionForInvalidURI() { assertThrows(Exception.class, () -> RedisClient.builder().fromURI(new URI("localhost:6380")).build()); } @Test @SuppressWarnings("deprecation") public void allowUrlWithNoDBAndNoPassword() { RedisClient.builder().fromURI(endpointStandalone1.getURI().toString()).build().close(); RedisClient.builder().fromURI(endpointStandalone1.getURI()).build().close(); } @Test public void customClientName() { try ( RedisClient pool = RedisClient.builder().hostAndPort(endpointStandalone7.getHostAndPort()) .clientConfig( endpointStandalone7.getClientConfigBuilder().clientName("my_shiny_client_name").build()) .build(); Connection jedis = pool.getPool().getResource()) { assertEquals("my_shiny_client_name", new Jedis(jedis).clientGetname()); } } @Test public void invalidClientName() { try ( RedisClient pool = RedisClient.builder().hostAndPort(endpointStandalone7.getHostAndPort()) .clientConfig( DefaultJedisClientConfig.builder().clientName("invalid client name").build()) .build(); Connection jedis = pool.getPool().getResource()) { } catch (Exception e) { if (!e.getMessage().startsWith("client info cannot contain space")) { fail("invalid client name test fail"); } } } @Test public void getNumActiveWhenPoolIsClosed() { RedisClient pool = RedisClient.builder().hostAndPort(endpointStandalone7.getHostAndPort()) .clientConfig(endpointStandalone7.getClientConfigBuilder().timeoutMillis(2000).build()) .build(); try (Connection j = pool.getPool().getResource()) { j.ping(); } pool.close(); assertEquals(0, pool.getPool().getNumActive()); } @Test public void getNumActiveReturnsTheCorrectNumber() { try (RedisClient pool = RedisClient.builder() .hostAndPort(endpointStandalone7.getHost(), endpointStandalone7.getPort()) .clientConfig(DefaultJedisClientConfig.builder().timeoutMillis(2000).build()) .poolConfig(new ConnectionPoolConfig()).build()) { Connection jedis = pool.getPool().getResource(); assertEquals(1, pool.getPool().getNumActive()); Connection jedis2 = pool.getPool().getResource(); assertEquals(2, pool.getPool().getNumActive()); jedis.close(); assertEquals(1, pool.getPool().getNumActive()); jedis2.close(); assertEquals(0, pool.getPool().getNumActive()); } } @Test public void closeResourceTwice() { try (RedisClient pool = RedisClient.builder() .hostAndPort(endpointStandalone7.getHost(), endpointStandalone7.getPort()) .clientConfig(endpointStandalone7.getClientConfigBuilder().timeoutMillis(2000).build()) .poolConfig(new ConnectionPoolConfig()).build()) { Connection j = pool.getPool().getResource(); j.ping(); j.close(); j.close(); } } @Test public void closeBrokenResourceTwice() { try (RedisClient pool = RedisClient.builder() .hostAndPort(endpointStandalone7.getHost(), endpointStandalone7.getPort()) .clientConfig(endpointStandalone7.getClientConfigBuilder().timeoutMillis(2000).build()) .poolConfig(new ConnectionPoolConfig()).build()) { Connection j = pool.getPool().getResource(); try { // make connection broken j.getOne(); fail(); } catch (Exception e) { assertInstanceOf(JedisConnectionException.class, e); } assertTrue(j.isBroken()); j.close(); j.close(); } } @Test public void testResetValidCredentials() { DefaultRedisCredentialsProvider credentialsProvider = new DefaultRedisCredentialsProvider(new DefaultRedisCredentials(null, "bad password")); try (RedisClient pool = RedisClient.builder().hostAndPort(endpointStandalone1.getHostAndPort()) .clientConfig( DefaultJedisClientConfig.builder().credentialsProvider(credentialsProvider).build()) .build()) { try { pool.get("foo"); fail("Should not get resource from pool"); } catch (JedisException e) { } assertEquals(0, pool.getPool().getNumActive()); credentialsProvider.setCredentials(new DefaultRedisCredentials(null, endpointStandalone1.getPassword())); assertThat(pool.get("foo"), anything()); } } @Test public void testCredentialsProvider() { final AtomicInteger prepareCount = new AtomicInteger(); final AtomicInteger cleanupCount = new AtomicInteger(); final AtomicBoolean validPassword = new AtomicBoolean(false); RedisCredentialsProvider credentialsProvider = new RedisCredentialsProvider() { @Override public void prepare() { prepareCount.incrementAndGet(); } @Override public RedisCredentials get() { if (!validPassword.get()) { return new RedisCredentials() { @Override public char[] getPassword() { return "invalidPass".toCharArray(); } }; } return new RedisCredentials() { @Override public String getUser() { return null; } @Override public char[] getPassword() { return endpointStandalone1.getPassword().toCharArray(); } }; } @Override public void cleanUp() { cleanupCount.incrementAndGet(); } }; // TODO: do it without the help of pool config; from Connection constructor? (configurable) force ping? GenericObjectPoolConfig poolConfig = new GenericObjectPoolConfig<>(); poolConfig.setMaxTotal(1); poolConfig.setTestOnBorrow(true); try (RedisClient pool = RedisClient.builder().hostAndPort(endpointStandalone1.getHostAndPort()) .clientConfig( DefaultJedisClientConfig.builder().credentialsProvider(credentialsProvider).build()) .poolConfig(poolConfig).build()) { try { pool.get("foo"); fail("Should not get resource from pool"); } catch (JedisException e) { //ignore } assertEquals(0, pool.getPool().getNumActive() + pool.getPool().getNumIdle() + pool.getPool().getNumWaiters()); assertThat(prepareCount.getAndSet(0), greaterThanOrEqualTo(1)); assertThat(cleanupCount.getAndSet(0), greaterThanOrEqualTo(1)); validPassword.set(true); assertThat(pool.get("foo"), anything()); assertThat(prepareCount.get(), equalTo(1)); assertThat(cleanupCount.get(), equalTo(1)); } } } ================================================ FILE: src/test/java/redis/clients/jedis/ReliableTransactionTest.java ================================================ package redis.clients.jedis; import static org.junit.jupiter.api.Assertions.assertArrayEquals; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertNull; import static org.junit.jupiter.api.Assertions.assertSame; import static org.junit.jupiter.api.Assertions.assertThrows; import static org.junit.jupiter.api.Assertions.assertTrue; import static org.junit.jupiter.api.Assertions.fail; import static redis.clients.jedis.Protocol.Command.INCR; import static redis.clients.jedis.Protocol.Command.GET; import static redis.clients.jedis.Protocol.Command.SET; import java.util.ArrayList; import java.util.List; import java.util.Set; import io.redis.test.annotations.ConditionalOnEnv; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Tag; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.extension.RegisterExtension; import redis.clients.jedis.exceptions.JedisDataException; import redis.clients.jedis.util.EnvCondition; import redis.clients.jedis.util.SafeEncoder; import redis.clients.jedis.util.TestEnvUtil; @Tag("integration") @ConditionalOnEnv(value = TestEnvUtil.ENV_REDIS_ENTERPRISE, enabled = false) public class ReliableTransactionTest { @RegisterExtension public static EnvCondition envCondition = new EnvCondition(); final byte[] bfoo = { 0x01, 0x02, 0x03, 0x04 }; final byte[] bbar = { 0x05, 0x06, 0x07, 0x08 }; final byte[] ba = { 0x0A }; final byte[] bb = { 0x0B }; final byte[] bmykey = { 0x42, 0x02, 0x03, 0x04 }; private static EndpointConfig endpoint; private Connection conn; private Jedis nj; @BeforeAll public static void prepareEndpoint() { endpoint = Endpoints.getRedisEndpoint("standalone0"); } @BeforeEach public void setUp() throws Exception { conn = new Connection(endpoint.getHostAndPort(), endpoint.getClientConfigBuilder().timeoutMillis(500).build()); nj = new Jedis(endpoint.getHostAndPort(), endpoint.getClientConfigBuilder().timeoutMillis(500).build()); nj.flushAll(); } @AfterEach public void tearDown() throws Exception { nj.close(); conn.close(); } @Test public void multi() { ReliableTransaction trans = new ReliableTransaction(conn); trans.sadd("foo", "a"); trans.sadd("foo", "b"); trans.scard("foo"); List response = trans.exec(); List expected = new ArrayList(); expected.add(1L); expected.add(1L); expected.add(2L); assertEquals(expected, response); // Binary trans = new ReliableTransaction(conn); trans.sadd(bfoo, ba); trans.sadd(bfoo, bb); trans.scard(bfoo); response = trans.exec(); expected = new ArrayList(); expected.add(1L); expected.add(1L); expected.add(2L); assertEquals(expected, response); } @Test public void watch() { ReliableTransaction t = new ReliableTransaction(conn, false); assertEquals("OK", t.watch("mykey", "somekey")); t.multi(); nj.set("mykey", "bar"); t.set("mykey", "foo"); List resp = t.exec(); assertNull(resp); assertEquals("bar", nj.get("mykey")); // Binary assertEquals("OK", t.watch(bmykey, "foobar".getBytes())); t.multi(); nj.set(bmykey, bbar); t.set(bmykey, bfoo); resp = t.exec(); assertNull(resp); assertArrayEquals(bbar, nj.get(bmykey)); } @Test public void unwatch() { ReliableTransaction t = new ReliableTransaction(conn, false); assertEquals("OK", t.watch("mykey")); String val = "foo"; assertEquals("OK", t.unwatch()); t.multi(); nj.set("mykey", "bar"); t.set("mykey", val); List resp = t.exec(); assertEquals(1, resp.size()); assertEquals("OK", resp.get(0)); // Binary t.watch(bmykey); byte[] bval = bfoo; assertEquals("OK", t.unwatch()); t.multi(); nj.set(bmykey, bbar); t.set(bmykey, bval); resp = t.exec(); assertEquals(1, resp.size()); assertEquals("OK", resp.get(0)); } @Test public void discard() { ReliableTransaction t = new ReliableTransaction(conn); String status = t.discard(); assertEquals("OK", status); } @Test public void transactionResponse() { nj.set("string", "foo"); nj.lpush("list", "foo"); nj.hset("hash", "foo", "bar"); nj.zadd("zset", 1, "foo"); nj.sadd("set", "foo"); ReliableTransaction t = new ReliableTransaction(conn); Response string = t.get("string"); Response list = t.lpop("list"); Response hash = t.hget("hash", "foo"); Response> zset = t.zrange("zset", 0, -1); Response set = t.spop("set"); t.exec(); assertEquals("foo", string.get()); assertEquals("foo", list.get()); assertEquals("bar", hash.get()); assertEquals("foo", zset.get().iterator().next()); assertEquals("foo", set.get()); } @Test public void transactionResponseBinary() { nj.set("string", "foo"); nj.lpush("list", "foo"); nj.hset("hash", "foo", "bar"); nj.zadd("zset", 1, "foo"); nj.sadd("set", "foo"); ReliableTransaction t = new ReliableTransaction(conn); Response string = t.get("string".getBytes()); Response list = t.lpop("list".getBytes()); Response hash = t.hget("hash".getBytes(), "foo".getBytes()); Response> zset = t.zrange("zset".getBytes(), 0, -1); Response set = t.spop("set".getBytes()); t.exec(); assertArrayEquals("foo".getBytes(), string.get()); assertArrayEquals("foo".getBytes(), list.get()); assertArrayEquals("bar".getBytes(), hash.get()); assertArrayEquals("foo".getBytes(), zset.get().iterator().next()); assertArrayEquals("foo".getBytes(), set.get()); } @Test public void transactionResponseWithinPipeline() { nj.set("string", "foo"); ReliableTransaction t = new ReliableTransaction(conn); Response string = t.get("string"); assertThrows(IllegalStateException.class, string::get); t.exec(); } @Test public void transactionResponseWithError() { ReliableTransaction t = new ReliableTransaction(conn); t.set("foo", "bar"); Response> error = t.smembers("foo"); Response r = t.get("foo"); List l = t.exec(); assertSame(JedisDataException.class, l.get(1).getClass()); try { error.get(); fail("We expect exception here!"); } catch (JedisDataException e) { // that is fine we should be here } assertEquals("bar", r.get()); } @Test public void testCloseable() { ReliableTransaction transaction = new ReliableTransaction(conn); transaction.set("a", "1"); transaction.set("b", "2"); transaction.close(); try { transaction.exec(); fail("close should discard transaction"); } catch (IllegalStateException e) { assertTrue(e.getMessage().contains("EXEC without MULTI")); // pass } } @Test public void testTransactionWithGeneralCommand() { ReliableTransaction t = new ReliableTransaction(conn); t.set("string", "foo"); t.lpush("list", "foo"); t.hset("hash", "foo", "bar"); t.zadd("zset", 1, "foo"); t.sendCommand(SET, "x", "1"); t.sadd("set", "foo"); t.sendCommand(INCR, "x"); Response string = t.get("string"); Response list = t.lpop("list"); Response hash = t.hget("hash", "foo"); Response> zset = t.zrange("zset", 0, -1); Response set = t.spop("set"); Response x = t.sendCommand(GET, "x"); t.exec(); assertEquals("foo", string.get()); assertEquals("foo", list.get()); assertEquals("bar", hash.get()); assertEquals("foo", zset.get().iterator().next()); assertEquals("foo", set.get()); assertEquals("2", SafeEncoder.encode((byte[]) x.get())); } @Test public void transactionResponseWithErrorWithGeneralCommand() { ReliableTransaction t = new ReliableTransaction(conn); t.set("foo", "bar"); t.sendCommand(SET, "x", "1"); Response> error = t.smembers("foo"); Response r = t.get("foo"); Response x = t.sendCommand(GET, "x"); t.sendCommand(INCR, "x"); List l = t.exec(); assertSame(JedisDataException.class, l.get(2).getClass()); try { error.get(); fail("We expect exception here!"); } catch (JedisDataException e) { // that is fine we should be here } assertEquals("bar", r.get()); assertEquals("1", SafeEncoder.encode((byte[]) x.get())); } } ================================================ FILE: src/test/java/redis/clients/jedis/SentineledConnectionProviderTest.java ================================================ package redis.clients.jedis; import java.util.HashSet; import java.util.Map; import java.util.Set; import org.apache.commons.pool2.impl.GenericObjectPoolConfig; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Tag; import org.junit.jupiter.api.Timeout; import org.junit.jupiter.api.BeforeAll; import redis.clients.jedis.exceptions.JedisConnectionException; import redis.clients.jedis.exceptions.JedisException; import redis.clients.jedis.providers.SentineledConnectionProvider; import redis.clients.jedis.util.ReflectionTestUtil; import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.equalTo; import static org.hamcrest.Matchers.hasKey; import static org.hamcrest.Matchers.sameInstance; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertSame; import static org.junit.jupiter.api.Assertions.assertThrows; import static org.junit.jupiter.api.Assertions.fail; /** * @see JedisSentinelPoolTest */ @Tag("integration") public class SentineledConnectionProviderTest { private static final String MASTER_NAME = "mymaster"; protected static HostAndPort sentinel1; protected static HostAndPort sentinel2; private static EndpointConfig primary; protected Set sentinels = new HashSet<>(); @BeforeAll public static void prepareEndpoints() { sentinel1 = Endpoints.getRedisEndpoint("sentinel-standalone2-1").getHostAndPort(); sentinel2 = Endpoints.getRedisEndpoint("sentinel-standalone2-3").getHostAndPort(); primary = Endpoints.getRedisEndpoint("standalone2-primary"); } @BeforeEach public void setUp() throws Exception { sentinels.clear(); sentinels.add(sentinel1); sentinels.add(sentinel2); } @Test public void repeatedSentinelPoolInitialization() { for (int i = 0; i < 20; ++i) { try (SentineledConnectionProvider provider = new SentineledConnectionProvider(MASTER_NAME, DefaultJedisClientConfig.builder().timeoutMillis(1000).password("foobared").database(2).build(), sentinels, DefaultJedisClientConfig.builder().build())) { provider.getConnection().close(); } } } /** * Ensure that getConnectionMap() does not cause connection leak. (#4323) */ @Test @Timeout( value = 1) public void getConnectionMapDoesNotCauseConnectionLeak() { ConnectionPoolConfig config = new ConnectionPoolConfig(); config.setMaxTotal(1); try (SentineledConnectionProvider sut = new SentineledConnectionProvider(MASTER_NAME, primary.getClientConfigBuilder().build(), config, sentinels, DefaultJedisClientConfig.builder().build())) { HostAndPort resolvedPrimary = sut.getCurrentMaster(); ConnectionPool pool = ReflectionTestUtil.getField(sut,"pool"); assertThat(pool.getNumActive(), equalTo(0)); Map cm = sut.getConnectionMap(); // exactly one entry for current primary // and no active connections assertThat(cm.size(), equalTo(1)); assertThat(cm, hasKey(resolvedPrimary)); assertThat(pool.getNumActive(), equalTo(0)); // primary did not change assertThat(ReflectionTestUtil.getField(sut,"pool"), sameInstance(pool)); } } /** * Ensure that getPrimaryNodesConnectionMap() does not cause connection leak. (#4323) */ @Test @Timeout( value = 1) public void getPrimaryNodesConnectionMapDoesNotCauseConnectionLeak() { ConnectionPoolConfig config = new ConnectionPoolConfig(); config.setMaxTotal(1); try (SentineledConnectionProvider sut = new SentineledConnectionProvider(MASTER_NAME, primary.getClientConfigBuilder().build(), config, sentinels, DefaultJedisClientConfig.builder().build())) { HostAndPort resolvedPrimary = sut.getCurrentMaster(); ConnectionPool pool = ReflectionTestUtil.getField(sut,"pool"); assertThat(pool.getNumActive(), equalTo(0)); Map cm = sut.getPrimaryNodesConnectionMap(); // exactly one entry for current primary // and no active connections assertThat(cm.size(), equalTo(1)); assertThat(cm, hasKey(resolvedPrimary)); assertThat(pool.getNumActive(), equalTo(0)); // primary did not change assertThat(ReflectionTestUtil.getField(sut,"pool"), sameInstance(pool)); } } @Test public void initializeWithNotAvailableSentinelsShouldThrowException() { Set wrongSentinels = new HashSet<>(); wrongSentinels.add(new HostAndPort("localhost", 65432)); wrongSentinels.add(new HostAndPort("localhost", 65431)); assertThrows(JedisConnectionException.class, () -> { try (SentineledConnectionProvider provider = new SentineledConnectionProvider(MASTER_NAME, DefaultJedisClientConfig.builder().build(), wrongSentinels, DefaultJedisClientConfig.builder().build())) { } }); } @Test public void initializeWithNotMonitoredMasterNameShouldThrowException() { final String wrongMasterName = "wrongMasterName"; assertThrows(JedisException.class, () -> { try (SentineledConnectionProvider provider = new SentineledConnectionProvider(wrongMasterName, DefaultJedisClientConfig.builder().build(), sentinels, DefaultJedisClientConfig.builder().build())) { } }); } @Test public void checkCloseableConnections() throws Exception { GenericObjectPoolConfig config = new GenericObjectPoolConfig<>(); try (RedisSentinelClient jedis = RedisSentinelClient .builder().masterName(MASTER_NAME).clientConfig(DefaultJedisClientConfig.builder() .timeoutMillis(1000).password("foobared").database(2).build()) .poolConfig(config).sentinels(sentinels).build()) { assertSame(SentineledConnectionProvider.class, jedis.provider.getClass()); jedis.set("foo", "bar"); assertEquals("bar", jedis.get("foo")); } } @Test public void checkResourceIsCloseable() { GenericObjectPoolConfig config = new GenericObjectPoolConfig<>(); config.setMaxTotal(1); config.setBlockWhenExhausted(false); try (RedisSentinelClient jedis = RedisSentinelClient .builder().masterName(MASTER_NAME).clientConfig(DefaultJedisClientConfig.builder() .timeoutMillis(1000).password("foobared").database(2).build()) .poolConfig(config).sentinels(sentinels).build()) { Connection conn = jedis.provider.getConnection(); try { conn.ping(); } finally { conn.close(); } Connection conn2 = jedis.provider.getConnection(); try { assertEquals(conn, conn2); } finally { conn2.close(); } } } @Test public void testResetInvalidPassword() { DefaultRedisCredentialsProvider credentialsProvider = new DefaultRedisCredentialsProvider(new DefaultRedisCredentials(null, "foobared")); try (RedisSentinelClient jedis = RedisSentinelClient.builder().masterName(MASTER_NAME) .clientConfig(DefaultJedisClientConfig.builder().timeoutMillis(2000) .credentialsProvider(credentialsProvider).database(2).clientName("my_shiny_client_name") .build()) .sentinels(sentinels).build()) { jedis.set("foo", "bar"); Connection conn1_ref; try (Connection conn1_1 = jedis.provider.getConnection()) { conn1_ref = conn1_1; assertEquals("bar", new Jedis(conn1_1).get("foo")); } credentialsProvider.setCredentials(new DefaultRedisCredentials(null, "wrong password")); try (Connection conn1_2 = jedis.provider.getConnection()) { assertSame(conn1_ref, conn1_2); try (Connection conn2 = jedis.provider.getConnection()) { fail("Should not get resource from pool"); } catch (JedisException e) { } } } } @Test public void testResetValidPassword() { DefaultRedisCredentialsProvider credentialsProvider = new DefaultRedisCredentialsProvider(new DefaultRedisCredentials(null, "wrong password")); try (RedisSentinelClient jedis = RedisSentinelClient.builder().masterName(MASTER_NAME) .clientConfig(DefaultJedisClientConfig.builder().timeoutMillis(2000) .credentialsProvider(credentialsProvider).database(2).clientName("my_shiny_client_name") .build()) .sentinels(sentinels).build()) { try (Connection conn1 = jedis.provider.getConnection()) { fail("Should not get resource from pool"); } catch (JedisException e) { } credentialsProvider.setCredentials(new DefaultRedisCredentials(null, "foobared")); try (Connection conn2 = jedis.provider.getConnection()) { new Jedis(conn2).set("foo", "bar"); assertEquals("bar", jedis.get("foo")); } } } } ================================================ FILE: src/test/java/redis/clients/jedis/StaticCommandFlagsRegistryTest.java ================================================ package redis.clients.jedis; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertNotNull; import static org.junit.jupiter.api.Assertions.assertTrue; import java.util.EnumSet; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import redis.clients.jedis.CommandFlagsRegistry.CommandFlag; import redis.clients.jedis.CommandFlagsRegistry.RequestPolicy; import redis.clients.jedis.CommandFlagsRegistry.ResponsePolicy; import redis.clients.jedis.commands.ProtocolCommand; import redis.clients.jedis.util.SafeEncoder; /** * Unit tests for StaticCommandFlagsRegistry. Tests the retrieval of command flags for various Redis * commands, including commands with subcommands. */ public class StaticCommandFlagsRegistryTest { private StaticCommandFlagsRegistry registry; @BeforeEach public void setUp() { registry = StaticCommandFlagsRegistry.registry(); } /** * Test that FUNCTION LOAD command returns the correct flags. FUNCTION LOAD should have: DENYOOM, * NOSCRIPT, WRITE flags. */ @Test public void testFunctionLoadCommandFlags() { // Create a CommandArguments for "FUNCTION LOAD" CommandArguments functionLoadArgs = new CommandArguments(Protocol.Command.FUNCTION).add("LOAD"); EnumSet flags = registry.getFlags(functionLoadArgs); assertNotNull(flags, "Flags should not be null"); assertFalse(flags.isEmpty(), "FUNCTION LOAD should have flags"); assertEquals(3, flags.size(), "FUNCTION LOAD should have exactly 3 flags"); assertTrue(flags.contains(CommandFlag.DENYOOM), "FUNCTION LOAD should have DENYOOM flag"); assertTrue(flags.contains(CommandFlag.NOSCRIPT), "FUNCTION LOAD should have NOSCRIPT flag"); assertTrue(flags.contains(CommandFlag.WRITE), "FUNCTION LOAD should have WRITE flag"); } /** * Test that FUNCTION DELETE command returns the correct flags. FUNCTION DELETE should have: * NOSCRIPT, WRITE flags. */ @Test public void testFunctionDeleteCommandFlags() { CommandArguments functionDeleteArgs = new CommandArguments(Protocol.Command.FUNCTION) .add("DELETE"); EnumSet flags = registry.getFlags(functionDeleteArgs); assertNotNull(flags, "Flags should not be null"); assertFalse(flags.isEmpty(), "FUNCTION DELETE should have flags"); assertEquals(2, flags.size(), "FUNCTION DELETE should have exactly 2 flags"); assertTrue(flags.contains(CommandFlag.NOSCRIPT), "FUNCTION DELETE should have NOSCRIPT flag"); assertTrue(flags.contains(CommandFlag.WRITE), "FUNCTION DELETE should have WRITE flag"); } /** * Test other subcommand examples: ACL SETUSER */ @Test public void testAclSetUserCommandFlags() { CommandArguments aclSetUserArgs = new CommandArguments(Protocol.Command.ACL).add("SETUSER"); EnumSet flags = registry.getFlags(aclSetUserArgs); assertNotNull(flags, "Flags should not be null"); assertFalse(flags.isEmpty(), "ACL SETUSER should have flags"); assertTrue(flags.contains(CommandFlag.ADMIN), "ACL SETUSER should have ADMIN flag"); assertTrue(flags.contains(CommandFlag.NOSCRIPT), "ACL SETUSER should have NOSCRIPT flag"); } /** * Test other subcommand examples: CONFIG GET */ @Test public void testConfigGetCommandFlags() { CommandArguments configGetArgs = new CommandArguments(Protocol.Command.CONFIG).add("GET"); EnumSet flags = registry.getFlags(configGetArgs); assertNotNull(flags, "Flags should not be null"); assertFalse(flags.isEmpty(), "CONFIG GET should have flags"); assertTrue(flags.contains(CommandFlag.ADMIN), "CONFIG GET should have ADMIN flag"); assertTrue(flags.contains(CommandFlag.LOADING), "CONFIG GET should have LOADING flag"); assertTrue(flags.contains(CommandFlag.STALE), "CONFIG GET should have STALE flag"); } /** * Test simple command without subcommands: GET */ @Test public void testGetCommandFlags() { CommandArguments getArgs = new CommandArguments(Protocol.Command.GET).add("key"); EnumSet flags = registry.getFlags(getArgs); assertNotNull(flags, "Flags should not be null"); assertFalse(flags.isEmpty(), "GET should have flags"); assertTrue(flags.contains(CommandFlag.READONLY), "GET should have READONLY flag"); assertTrue(flags.contains(CommandFlag.FAST), "GET should have FAST flag"); } /** * Test simple command without subcommands: SET */ @Test public void testSetCommandFlags() { CommandArguments setArgs = new CommandArguments(Protocol.Command.SET).add("key").add("value"); EnumSet flags = registry.getFlags(setArgs); assertNotNull(flags, "Flags should not be null"); assertFalse(flags.isEmpty(), "SET should have flags"); assertTrue(flags.contains(CommandFlag.WRITE), "SET should have WRITE flag"); assertTrue(flags.contains(CommandFlag.DENYOOM), "SET should have DENYOOM flag"); } /** * Test that unknown commands return empty flags */ @Test public void testUnknownCommandReturnsEmptyFlags() { ProtocolCommand unknownCommand = () -> SafeEncoder.encode("UNKNOWN_COMMAND_XYZ"); CommandArguments unknownArgs = new CommandArguments(unknownCommand); EnumSet flags = registry.getFlags(unknownArgs); assertNotNull(flags, "Flags should not be null"); assertTrue(flags.isEmpty(), "Unknown command should return empty flags"); } /** * Test case insensitivity - command names should be normalized to uppercase */ @Test public void testCaseInsensitivity() { ProtocolCommand functionCommand = () -> SafeEncoder.encode("function"); CommandArguments functionLoadArgs = new CommandArguments(functionCommand).add("load"); EnumSet flags = registry.getFlags(functionLoadArgs); assertNotNull(flags, "Flags should not be null"); assertFalse(flags.isEmpty(), "function load (lowercase) should have flags"); assertEquals(3, flags.size(), "function load should have exactly 3 flags"); assertTrue(flags.contains(CommandFlag.DENYOOM), "function load should have DENYOOM flag"); assertTrue(flags.contains(CommandFlag.NOSCRIPT), "function load should have NOSCRIPT flag"); assertTrue(flags.contains(CommandFlag.WRITE), "function load should have WRITE flag"); } /** * Test that unknown subcommands of parent commands fall back to parent command flags. If the * parent command also doesn't exist, it should return empty flags. */ @Test public void testUnknownSubcommandFallback() { // Create a CommandArguments for "FUNCTION UNKNOWN_SUBCOMMAND" // This subcommand doesn't exist, so it should fall back to "FUNCTION" parent flags // Since "FUNCTION" parent has empty flags, it should return empty flags CommandArguments unknownSubcommandArgs = new CommandArguments(Protocol.Command.FUNCTION) .add("UNKNOWN_SUBCOMMAND"); EnumSet flags = registry.getFlags(unknownSubcommandArgs); assertNotNull(flags, "Flags should not be null"); assertTrue(flags.isEmpty(), "Unknown FUNCTION subcommand should return empty flags (parent flags)"); } // ==================== Request Policy Tests ==================== /** * Test that getRequestPolicy returns DEFAULT for commands without a specific policy. Since the * current registry doesn't populate request policies, all commands should return DEFAULT. */ @Test public void testGetRequestPolicyReturnsDefault() { CommandArguments getArgs = new CommandArguments(Protocol.Command.GET).add("key"); RequestPolicy policy = registry.getRequestPolicy(getArgs); assertNotNull(policy, "Request policy should not be null"); assertEquals(RequestPolicy.DEFAULT, policy, "GET should have DEFAULT request policy"); } /** * Test that getRequestPolicy returns DEFAULT for unknown commands. */ @Test public void testGetRequestPolicyForUnknownCommand() { ProtocolCommand unknownCommand = () -> SafeEncoder.encode("UNKNOWN_COMMAND_XYZ"); CommandArguments unknownArgs = new CommandArguments(unknownCommand); RequestPolicy policy = registry.getRequestPolicy(unknownArgs); assertNotNull(policy, "Request policy should not be null"); assertEquals(RequestPolicy.DEFAULT, policy, "Unknown command should have DEFAULT request policy"); } /** * Test that getRequestPolicy works for commands with subcommands. */ @Test public void testGetRequestPolicyForSubcommand() { CommandArguments functionLoadArgs = new CommandArguments(Protocol.Command.FUNCTION).add("LOAD"); RequestPolicy policy = registry.getRequestPolicy(functionLoadArgs); assertNotNull(policy, "Request policy should not be null"); // Currently all commands return DEFAULT since policies aren't populated assertEquals(RequestPolicy.ALL_SHARDS, policy, "FUNCTION LOAD should have DEFAULT request policy"); } // ==================== Response Policy Tests ==================== /** * Test that getResponsePolicy returns DEFAULT for commands without a specific policy. Since the * current registry doesn't populate response policies, all commands should return DEFAULT. */ @Test public void testGetResponsePolicyReturnsDefault() { CommandArguments getArgs = new CommandArguments(Protocol.Command.GET).add("key"); ResponsePolicy policy = registry.getResponsePolicy(getArgs); assertNotNull(policy, "Response policy should not be null"); assertEquals(ResponsePolicy.DEFAULT, policy, "GET should have DEFAULT response policy"); } /** * Test that getResponsePolicy returns DEFAULT for unknown commands. */ @Test public void testGetResponsePolicyForUnknownCommand() { ProtocolCommand unknownCommand = () -> SafeEncoder.encode("UNKNOWN_COMMAND_XYZ"); CommandArguments unknownArgs = new CommandArguments(unknownCommand); ResponsePolicy policy = registry.getResponsePolicy(unknownArgs); assertNotNull(policy, "Response policy should not be null"); assertEquals(ResponsePolicy.DEFAULT, policy, "Unknown command should have DEFAULT response policy"); } /** * Test that getResponsePolicy works for commands with subcommands. */ @Test public void testGetResponsePolicyForSubcommand() { CommandArguments functionLoadArgs = new CommandArguments(Protocol.Command.FUNCTION).add("LOAD"); ResponsePolicy policy = registry.getResponsePolicy(functionLoadArgs); assertNotNull(policy, "Response policy should not be null"); // Currently all commands return DEFAULT since policies aren't populated assertEquals(ResponsePolicy.ALL_SUCCEEDED, policy, "FUNCTION LOAD should have DEFAULT response policy"); } /** * Test that RequestPolicy enum contains all expected values. */ @Test public void testRequestPolicyEnumValues() { RequestPolicy[] values = RequestPolicy.values(); assertEquals(5, values.length, "RequestPolicy should have 5 values"); // Verify all expected values exist assertNotNull(RequestPolicy.valueOf("DEFAULT")); assertNotNull(RequestPolicy.valueOf("ALL_NODES")); assertNotNull(RequestPolicy.valueOf("ALL_SHARDS")); assertNotNull(RequestPolicy.valueOf("MULTI_SHARD")); assertNotNull(RequestPolicy.valueOf("SPECIAL")); } /** * Test that ResponsePolicy enum contains all expected values. */ @Test public void testResponsePolicyEnumValues() { ResponsePolicy[] values = ResponsePolicy.values(); assertEquals(9, values.length, "ResponsePolicy should have 9 values"); // Verify all expected values exist assertNotNull(ResponsePolicy.valueOf("DEFAULT")); assertNotNull(ResponsePolicy.valueOf("ONE_SUCCEEDED")); assertNotNull(ResponsePolicy.valueOf("ALL_SUCCEEDED")); assertNotNull(ResponsePolicy.valueOf("AGG_LOGICAL_AND")); assertNotNull(ResponsePolicy.valueOf("AGG_LOGICAL_OR")); assertNotNull(ResponsePolicy.valueOf("AGG_MIN")); assertNotNull(ResponsePolicy.valueOf("AGG_MAX")); assertNotNull(ResponsePolicy.valueOf("AGG_SUM")); assertNotNull(ResponsePolicy.valueOf("SPECIAL")); } /** * Test that correct flags are stored for commands with subcommands. */ @Test public void testFlagsForCommandWithSubcommands() { // verify flags for COMMAND (top level) CommandArguments commandArgs = new CommandArguments(Protocol.Command.COMMAND); EnumSet commandFlags = registry.getFlags(commandArgs); EnumSet expectedCommandFlags = EnumSet.of(CommandFlag.LOADING, CommandFlag.STALE); assertEquals(expectedCommandFlags, commandFlags, "COMMAND should have expected flags"); // verify flags for COMMAND INFO (subcommand) CommandArguments commandInfoArgs = new CommandArguments(Protocol.Command.COMMAND).add("INFO"); EnumSet commandInfoflags = registry.getFlags(commandInfoArgs); EnumSet expectedCommandInfoFlags = EnumSet.of(CommandFlag.LOADING, CommandFlag.STALE); assertEquals(expectedCommandInfoFlags, commandInfoflags, "COMMAND INFO should have expected flags"); } /** * Test that HOTKEYS subcommands (START, STOP, GET, RESET) return the correct flags. All HOTKEYS * subcommands should have ADMIN and NOSCRIPT flags. */ @Test public void testHotkeysSubcommandFlags() { // Expected flags for all HOTKEYS subcommands: ADMIN, NOSCRIPT EnumSet expectedFlags = EnumSet.of(CommandFlag.ADMIN, CommandFlag.NOSCRIPT); // Test HOTKEYS START CommandArguments hotkeysStartArgs = new CommandArguments(Protocol.Command.HOTKEYS).add("START"); EnumSet startFlags = registry.getFlags(hotkeysStartArgs); assertNotNull(startFlags, "HOTKEYS START flags should not be null"); assertFalse(startFlags.isEmpty(), "HOTKEYS START should have flags"); assertEquals(expectedFlags, startFlags, "HOTKEYS START should have ADMIN and NOSCRIPT flags"); // Test HOTKEYS STOP CommandArguments hotkeysStopArgs = new CommandArguments(Protocol.Command.HOTKEYS).add("STOP"); EnumSet stopFlags = registry.getFlags(hotkeysStopArgs); assertNotNull(stopFlags, "HOTKEYS STOP flags should not be null"); assertFalse(stopFlags.isEmpty(), "HOTKEYS STOP should have flags"); assertEquals(expectedFlags, stopFlags, "HOTKEYS STOP should have ADMIN and NOSCRIPT flags"); // Test HOTKEYS GET CommandArguments hotkeysGetArgs = new CommandArguments(Protocol.Command.HOTKEYS).add("GET"); EnumSet getFlags = registry.getFlags(hotkeysGetArgs); assertNotNull(getFlags, "HOTKEYS GET flags should not be null"); assertFalse(getFlags.isEmpty(), "HOTKEYS GET should have flags"); assertEquals(expectedFlags, getFlags, "HOTKEYS GET should have ADMIN and NOSCRIPT flags"); // Test HOTKEYS RESET CommandArguments hotkeysResetArgs = new CommandArguments(Protocol.Command.HOTKEYS).add("RESET"); EnumSet resetFlags = registry.getFlags(hotkeysResetArgs); assertNotNull(resetFlags, "HOTKEYS RESET flags should not be null"); assertFalse(resetFlags.isEmpty(), "HOTKEYS RESET should have flags"); assertEquals(expectedFlags, resetFlags, "HOTKEYS RESET should have ADMIN and NOSCRIPT flags"); // Verify request policy for HOTKEYS GET (has SPECIAL request and response policy) RequestPolicy getRequestPolicy = registry.getRequestPolicy(hotkeysGetArgs); assertEquals(RequestPolicy.SPECIAL, getRequestPolicy, "HOTKEYS GET should have SPECIAL request policy"); ResponsePolicy getResponsePolicy = registry.getResponsePolicy(hotkeysGetArgs); assertEquals(ResponsePolicy.SPECIAL, getResponsePolicy, "HOTKEYS GET should have SPECIAL response policy"); // Verify request policy for HOTKEYS START (has SPECIAL request policy) RequestPolicy startRequestPolicy = registry.getRequestPolicy(hotkeysStartArgs); assertEquals(RequestPolicy.SPECIAL, startRequestPolicy, "HOTKEYS START should have SPECIAL request policy"); } } ================================================ FILE: src/test/java/redis/clients/jedis/TransactionV2Test.java ================================================ package redis.clients.jedis; import static org.hamcrest.Matchers.containsString; import static org.junit.jupiter.api.Assertions.assertArrayEquals; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertNotNull; import static org.junit.jupiter.api.Assertions.assertNull; import static org.junit.jupiter.api.Assertions.assertSame; import static org.junit.jupiter.api.Assertions.assertThrows; import static org.junit.jupiter.api.Assertions.assertTrue; import static org.junit.jupiter.api.Assertions.fail; import static redis.clients.jedis.Protocol.Command.INCR; import static redis.clients.jedis.Protocol.Command.GET; import static redis.clients.jedis.Protocol.Command.SET; import static redis.clients.jedis.util.AssertUtil.assertByteArrayListEquals; import java.util.ArrayList; import java.util.List; import java.util.Set; import io.redis.test.annotations.ConditionalOnEnv; import org.hamcrest.MatcherAssert; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Tag; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.RegisterExtension; import redis.clients.jedis.exceptions.JedisDataException; import redis.clients.jedis.util.EnvCondition; import redis.clients.jedis.util.SafeEncoder; import redis.clients.jedis.util.TestEnvUtil; @Tag("integration") public class TransactionV2Test { @RegisterExtension public static EnvCondition envCondition = new EnvCondition(); final byte[] bfoo = { 0x01, 0x02, 0x03, 0x04 }; final byte[] bbar = { 0x05, 0x06, 0x07, 0x08 }; final byte[] ba = { 0x0A }; final byte[] bb = { 0x0B }; final byte[] bc = { 0x0C }; final byte[] bmykey = { 0x42, 0x02, 0x03, 0x04 }; private static EndpointConfig endpoint; @BeforeAll public static void prepare() { endpoint = Endpoints.getRedisEndpoint("standalone0"); } private Connection conn; private Jedis nj; @BeforeEach public void setUp() throws Exception { conn = new Connection(endpoint.getHostAndPort(), endpoint.getClientConfigBuilder().timeoutMillis(500).build()); nj = new Jedis(endpoint.getHostAndPort(), endpoint.getClientConfigBuilder().timeoutMillis(500).build()); nj.flushAll(); } @AfterEach public void tearDown() throws Exception { nj.close(); conn.close(); } @Test public void multi() { Transaction trans = new Transaction(conn); trans.sadd("foo", "a"); trans.sadd("foo", "b"); trans.scard("foo"); List response = trans.exec(); List expected = new ArrayList(); expected.add(1L); expected.add(1L); expected.add(2L); assertEquals(expected, response); // Binary trans = new Transaction(conn); trans.sadd(bfoo, ba); trans.sadd(bfoo, bb); trans.scard(bfoo); response = trans.exec(); expected = new ArrayList(); expected.add(1L); expected.add(1L); expected.add(2L); assertEquals(expected, response); } @Test @ConditionalOnEnv(value = TestEnvUtil.ENV_REDIS_ENTERPRISE, enabled = false) public void watch() { Transaction t = new Transaction(conn, false); assertEquals("OK", t.watch("mykey", "somekey")); t.multi(); nj.set("mykey", "bar"); t.set("mykey", "foo"); List resp = t.exec(); assertNull(resp); assertEquals("bar", nj.get("mykey")); // Binary assertEquals("OK", t.watch(bmykey, "foobar".getBytes())); t.multi(); nj.set(bmykey, bbar); t.set(bmykey, bfoo); resp = t.exec(); assertNull(resp); assertArrayEquals(bbar, nj.get(bmykey)); } @Test public void unwatch() { Transaction t = new Transaction(conn, false); assertEquals("OK", t.watch("mykey")); String val = "foo"; assertEquals("OK", t.unwatch()); t.multi(); nj.set("mykey", "bar"); t.set("mykey", val); List resp = t.exec(); assertEquals(1, resp.size()); assertEquals("OK", resp.get(0)); // Binary t.watch(bmykey); byte[] bval = bfoo; assertEquals("OK", t.unwatch()); t.multi(); nj.set(bmykey, bbar); t.set(bmykey, bval); resp = t.exec(); assertEquals(1, resp.size()); assertEquals("OK", resp.get(0)); } @Test public void discard() { Transaction t = new Transaction(conn); String status = t.discard(); assertEquals("OK", status); } @Test @ConditionalOnEnv(value = TestEnvUtil.ENV_REDIS_ENTERPRISE, enabled = false) public void transactionResponse() { nj.set("string", "foo"); nj.lpush("list", "foo"); nj.hset("hash", "foo", "bar"); nj.zadd("zset", 1, "foo"); nj.sadd("set", "foo"); Transaction t = new Transaction(conn); Response string = t.get("string"); Response list = t.lpop("list"); Response hash = t.hget("hash", "foo"); Response> zset = t.zrange("zset", 0, -1); Response set = t.spop("set"); t.exec(); assertEquals("foo", string.get()); assertEquals("foo", list.get()); assertEquals("bar", hash.get()); assertEquals("foo", zset.get().iterator().next()); assertEquals("foo", set.get()); } @Test @ConditionalOnEnv(value = TestEnvUtil.ENV_REDIS_ENTERPRISE, enabled = false) public void transactionResponseBinary() { nj.set("string", "foo"); nj.lpush("list", "foo"); nj.hset("hash", "foo", "bar"); nj.zadd("zset", 1, "foo"); nj.sadd("set", "foo"); Transaction t = new Transaction(conn); Response string = t.get("string".getBytes()); Response list = t.lpop("list".getBytes()); Response hash = t.hget("hash".getBytes(), "foo".getBytes()); Response> zset = t.zrange("zset".getBytes(), 0, -1); Response set = t.spop("set".getBytes()); t.exec(); assertArrayEquals("foo".getBytes(), string.get()); assertArrayEquals("foo".getBytes(), list.get()); assertArrayEquals("bar".getBytes(), hash.get()); assertArrayEquals("foo".getBytes(), zset.get().iterator().next()); assertArrayEquals("foo".getBytes(), set.get()); } @Test public void transactionResponseWithinPipeline() { nj.set("string", "foo"); Transaction t = new Transaction(conn); Response string = t.get("string"); assertThrows(IllegalStateException.class, string::get); t.exec(); } @Test public void transactionResponseWithError() { Transaction t = new Transaction(conn); t.set("foo", "bar"); Response> error = t.smembers("foo"); Response r = t.get("foo"); List l = t.exec(); assertSame(JedisDataException.class, l.get(1).getClass()); try { error.get(); fail("We expect exception here!"); } catch (JedisDataException e) { // that is fine we should be here } assertEquals("bar", r.get()); } @Test public void transactionPropagatesErrorsBeforeExec() { // A command may fail to be queued, so there may be an error before EXEC is called. // For instance the command may be syntactically wrong (wrong number of arguments, wrong command name, ...) CommandObject invalidCommand = new CommandObject<>(new CommandObjects().commandArguments(SET), BuilderFactory.STRING); Transaction t = new Transaction(conn); t.appendCommand(invalidCommand); t.set("foo","bar"); JedisDataException exception = assertThrows(JedisDataException.class, t::exec); Throwable[] suppressed = exception.getSuppressed(); assertNotNull(suppressed, "Suppressed exceptions should not be null"); assertTrue(suppressed.length > 0, "There should be at least one suppressed exception"); MatcherAssert.assertThat(suppressed[0].getMessage(), containsString("ERR wrong number of arguments")); } @Test public void testCloseable() { Transaction transaction = new Transaction(conn); transaction.set("a", "1"); transaction.set("b", "2"); transaction.close(); try { transaction.exec(); fail("close should discard transaction"); } catch (IllegalStateException e) { assertTrue(e.getMessage().contains("EXEC without MULTI")); // pass } } @Test @ConditionalOnEnv(value = TestEnvUtil.ENV_REDIS_ENTERPRISE, enabled = false) public void testTransactionWithGeneralCommand() { Transaction t = new Transaction(conn); t.set("string", "foo"); t.lpush("list", "foo"); t.hset("hash", "foo", "bar"); t.zadd("zset", 1, "foo"); t.sendCommand(SET, "x", "1"); t.sadd("set", "foo"); t.sendCommand(INCR, "x"); Response string = t.get("string"); Response list = t.lpop("list"); Response hash = t.hget("hash", "foo"); Response> zset = t.zrange("zset", 0, -1); Response set = t.spop("set"); Response x = t.sendCommand(GET, "x"); t.exec(); assertEquals("foo", string.get()); assertEquals("foo", list.get()); assertEquals("bar", hash.get()); assertEquals("foo", zset.get().iterator().next()); assertEquals("foo", set.get()); assertEquals("2", SafeEncoder.encode((byte[]) x.get())); } @Test @ConditionalOnEnv(value = TestEnvUtil.ENV_REDIS_ENTERPRISE, enabled = false) public void transactionResponseWithErrorWithGeneralCommand() { Transaction t = new Transaction(conn); t.set("foo", "bar"); t.sendCommand(SET, "x", "1"); Response> error = t.smembers("foo"); Response r = t.get("foo"); Response x = t.sendCommand(GET, "x"); t.sendCommand(INCR, "x"); List l = t.exec(); assertSame(JedisDataException.class, l.get(2).getClass()); try { error.get(); fail("We expect exception here!"); } catch (JedisDataException e) { // that is fine we should be here } assertEquals("bar", r.get()); assertEquals("1", SafeEncoder.encode((byte[]) x.get())); } @Test public void zrevrangebyscore() { nj.zadd("foo", 1.0d, "a"); nj.zadd("foo", 2.0d, "b"); nj.zadd("foo", 3.0d, "c"); nj.zadd("foo", 4.0d, "d"); nj.zadd("foo", 5.0d, "e"); Transaction t = new Transaction(conn); Response> range = t.zrevrangeByScore("foo", 3d, Double.NEGATIVE_INFINITY, 0, 1); t.exec(); List expected = new ArrayList(); expected.add("c"); assertEquals(expected, range.get()); t = new Transaction(conn); range = t.zrevrangeByScore("foo", 3.5d, Double.NEGATIVE_INFINITY, 0, 2); t.exec(); expected = new ArrayList(); expected.add("c"); expected.add("b"); assertEquals(expected, range.get()); t = new Transaction(conn); range = t.zrevrangeByScore("foo", 3.5d, Double.NEGATIVE_INFINITY, 1, 1); t.exec(); expected = new ArrayList(); expected.add("b"); assertEquals(expected, range.get()); t = new Transaction(conn); range = t.zrevrangeByScore("foo", 4d, 2d); t.exec(); expected = new ArrayList(); expected.add("d"); expected.add("c"); expected.add("b"); assertEquals(expected, range.get()); t = new Transaction(conn); range = t.zrevrangeByScore("foo", "+inf", "(4"); t.exec(); expected = new ArrayList(); expected.add("e"); assertEquals(expected, range.get()); // Binary nj.zadd(bfoo, 1d, ba); nj.zadd(bfoo, 10d, bb); nj.zadd(bfoo, 0.1d, bc); nj.zadd(bfoo, 2d, ba); t = new Transaction(conn); Response> brange = t.zrevrangeByScore(bfoo, 2d, 0d); t.exec(); List bexpected = new ArrayList(); bexpected.add(ba); bexpected.add(bc); assertByteArrayListEquals(bexpected, brange.get()); t = new Transaction(conn); brange = t.zrevrangeByScore(bfoo, 2d, 0d, 0, 1); t.exec(); bexpected = new ArrayList(); bexpected.add(ba); assertByteArrayListEquals(bexpected, brange.get()); t = new Transaction(conn); Response> brange2 = t.zrevrangeByScore(bfoo, SafeEncoder.encode("+inf"), SafeEncoder.encode("(2")); t.exec(); bexpected = new ArrayList(); bexpected.add(bb); assertByteArrayListEquals(bexpected, brange2.get()); t = new Transaction(conn); brange = t.zrevrangeByScore(bfoo, 2d, 0d, 1, 1); t.exec(); bexpected = new ArrayList(); bexpected.add(bc); assertByteArrayListEquals(bexpected, brange.get()); } } ================================================ FILE: src/test/java/redis/clients/jedis/TupleSortedSetTest.java ================================================ package redis.clients.jedis; import java.util.ArrayList; import java.util.Collections; import java.util.List; import org.junit.jupiter.api.Test; import org.junit.jupiter.params.ParameterizedClass; import org.junit.jupiter.params.provider.MethodSource; import redis.clients.jedis.resps.Tuple; import redis.clients.jedis.commands.jedis.JedisCommandsTestBase; import static org.junit.jupiter.api.Assertions.assertEquals; @ParameterizedClass @MethodSource("redis.clients.jedis.commands.CommandsTestsParameters#respVersions") public class TupleSortedSetTest extends JedisCommandsTestBase { final byte[] bfoo = { 0x01, 0x02, 0x03, 0x04 }; final byte[] ba = { 0x0A }; final byte[] bb = { 0x0B }; final byte[] bc = { 0x0C }; final byte[] bd = { 0x0D }; final byte[] be = { 0x0E }; final byte[] bf = { 0x0F }; public TupleSortedSetTest(RedisProtocol protocol) { super(protocol); } @Test public void testBinary() { List array = new ArrayList(); jedis.zadd(bfoo, 0d, ba); array.add(new Tuple(ba, 0d)); jedis.zadd(bfoo, 1d, bb); array.add(new Tuple(bb, 1d)); List zrange = jedis.zrangeWithScores(bfoo, 0, -1); assertEquals(zrange, sorted(array)); jedis.zadd(bfoo, -0.3, bc); array.add(new Tuple(bc, -0.3)); jedis.zadd(bfoo, 0.3, bf); array.add(new Tuple(bf, 0.3)); jedis.zadd(bfoo, 0.3, be); array.add(new Tuple(be, 0.3)); jedis.zadd(bfoo, 0.3, bd); array.add(new Tuple(bd, 0.3)); zrange = jedis.zrangeWithScores(bfoo, 0, -1); assertEquals(zrange, sorted(array)); } @Test public void testString() { List array = new ArrayList(); jedis.zadd("foo", 0d, "a"); array.add(new Tuple("a", 0d)); jedis.zadd("foo", 1d, "b"); array.add(new Tuple("b", 1d)); List range = jedis.zrangeWithScores("foo", 0, -1); assertEquals(range, sorted(array)); jedis.zadd("foo", -0.3, "c"); array.add(new Tuple("c", -0.3)); jedis.zadd("foo", 0.3, "f"); array.add(new Tuple("f", 0.3)); jedis.zadd("foo", 0.3, "e"); array.add(new Tuple("e", 0.3)); jedis.zadd("foo", 0.3, "d"); array.add(new Tuple("d", 0.3)); range = jedis.zrangeWithScores("foo", 0, -1); assertEquals(range, sorted(array)); } private List sorted(List list) { List sort = new ArrayList<>(list); Collections.sort(sort); return sort; } } ================================================ FILE: src/test/java/redis/clients/jedis/UdsTest.java ================================================ package redis.clients.jedis; import java.io.File; import java.io.IOException; import java.net.Socket; import io.redis.test.annotations.ConditionalOnEnv; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.RegisterExtension; import org.newsclub.net.unix.AFUNIXSocket; import org.newsclub.net.unix.AFUNIXSocketAddress; import redis.clients.jedis.exceptions.JedisConnectionException; import redis.clients.jedis.util.EnvCondition; import redis.clients.jedis.util.TestEnvUtil; import static org.junit.jupiter.api.Assertions.assertEquals; @ConditionalOnEnv(value = TestEnvUtil.ENV_OSS_SOURCE, enabled = true) public class UdsTest { @RegisterExtension public static EnvCondition envCondition = new EnvCondition(); @Test public void jedisConnectsToUds() { try (Jedis jedis = new Jedis(new UdsJedisSocketFactory())) { assertEquals("PONG", jedis.ping()); } } @Test public void jedisConnectsToUdsResp3() { try (Jedis jedis = new Jedis(new UdsJedisSocketFactory(), DefaultJedisClientConfig.builder().resp3().build())) { assertEquals("PONG", jedis.ping()); } } @Test public void unifiedJedisConnectsToUds() { try (UnifiedJedis jedis = new UnifiedJedis(new UdsJedisSocketFactory())) { assertEquals("PONG", jedis.ping()); } } @Test public void unifiedJedisConnectsToUdsResp3() { try (UnifiedJedis jedis = new UnifiedJedis(new UdsJedisSocketFactory(), DefaultJedisClientConfig.builder().resp3().build())) { assertEquals("PONG", jedis.ping()); } } private static class UdsJedisSocketFactory implements JedisSocketFactory { private static final File UDS_SOCKET = new File("/tmp/redis_uds.sock"); @Override public Socket createSocket() throws JedisConnectionException { try { Socket socket = AFUNIXSocket.newStrictInstance(); socket.connect(new AFUNIXSocketAddress(UDS_SOCKET), Protocol.DEFAULT_TIMEOUT); return socket; } catch (IOException ioe) { throw new JedisConnectionException("Failed to create UDS connection.", ioe); } } } } ================================================ FILE: src/test/java/redis/clients/jedis/UnavailableConnectionTest.java ================================================ package redis.clients.jedis; import io.redis.test.annotations.ConditionalOnEnv; import org.apache.commons.pool2.impl.GenericObjectPoolConfig; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Timeout; import org.junit.jupiter.api.Tag; import org.junit.jupiter.api.extension.RegisterExtension; import redis.clients.jedis.exceptions.JedisConnectionException; import redis.clients.jedis.util.EnvCondition; import redis.clients.jedis.util.TestEnvUtil; import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertSame; import static org.junit.jupiter.api.Assertions.assertTrue; import static org.junit.jupiter.api.Assertions.fail; @Tag("integration") @ConditionalOnEnv(value = TestEnvUtil.ENV_OSS_SOURCE, enabled = true) public class UnavailableConnectionTest { @RegisterExtension public static EnvCondition envCondition = new EnvCondition(); private static final HostAndPort unavailableNode = new HostAndPort("localhost", 6400); @BeforeAll public static void setup() { setupAvoidQuitInDestroyObject(); try (Jedis j = new Jedis(unavailableNode)) { j.shutdown(); } } public static void cleanup() { cleanupAvoidQuitInDestroyObject(); } private static JedisPool poolForBrokenJedis1; private static Thread threadForBrokenJedis1; private static Jedis brokenJedis1; public static void setupAvoidQuitInDestroyObject() { GenericObjectPoolConfig config = new GenericObjectPoolConfig<>(); config.setMaxTotal(1); poolForBrokenJedis1 = new JedisPool(config, unavailableNode.getHost(), unavailableNode.getPort()); brokenJedis1 = poolForBrokenJedis1.getResource(); threadForBrokenJedis1 = new Thread(new Runnable() { @Override public void run() { brokenJedis1.blpop(0, "broken-key-1"); } }); threadForBrokenJedis1.start(); } @Test @Timeout(5) public void testAvoidQuitInDestroyObjectForBrokenConnection() throws InterruptedException { threadForBrokenJedis1.join(); assertFalse(threadForBrokenJedis1.isAlive()); assertTrue(brokenJedis1.isBroken()); brokenJedis1.close(); // we need capture/mock to test this properly try { poolForBrokenJedis1.getResource(); fail("Should not get connection from pool"); } catch (Exception ex) { assertSame(JedisConnectionException.class, ex.getClass()); } } public static void cleanupAvoidQuitInDestroyObject() { poolForBrokenJedis1.close(); } } ================================================ FILE: src/test/java/redis/clients/jedis/UnboundRedisClusterClientTest.java ================================================ package redis.clients.jedis; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertNotNull; import static org.junit.jupiter.api.Assertions.assertNull; import static org.junit.jupiter.api.Assertions.assertThrows; import static org.junit.jupiter.api.Assertions.assertTrue; import static org.junit.jupiter.api.Assertions.fail; import static redis.clients.jedis.Protocol.CLUSTER_HASHSLOTS; import java.io.IOException; import java.time.Duration; import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; import java.util.HashSet; import java.util.LinkedHashSet; import java.util.List; import java.util.Map; import java.util.Set; import java.util.concurrent.ArrayBlockingQueue; import java.util.concurrent.Callable; import java.util.concurrent.ExecutionException; import java.util.concurrent.Future; import java.util.concurrent.ThreadPoolExecutor; import java.util.concurrent.TimeUnit; import io.redis.test.annotations.SinceRedisVersion; import org.apache.commons.pool2.impl.GenericObjectPoolConfig; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Timeout; import org.junit.jupiter.api.Tag; import redis.clients.jedis.args.ClusterResetType; import redis.clients.jedis.exceptions.*; import redis.clients.jedis.util.ClientKillerUtil; import redis.clients.jedis.util.JedisClusterTestUtil; import redis.clients.jedis.util.JedisClusterCRC16; import redis.clients.jedis.util.Pool; @Tag("integration") public class UnboundRedisClusterClientTest extends UnboundRedisClusterClientTestBase { private static final int DEFAULT_TIMEOUT = 2000; //sec private static final int DEFAULT_REDIRECTIONS = 5; private static final ConnectionPoolConfig DEFAULT_POOL_CONFIG = new ConnectionPoolConfig(); private static final DefaultJedisClientConfig DEFAULT_CLIENT_CONFIG = endpoint.getClientConfigBuilder().build(); @Test public void testThrowMovedException() { assertThrows(JedisMovedDataException.class, ()->node1.set("foo", "bar")); } @Test public void testMovedExceptionParameters() { try { node1.set("foo", "bar"); } catch (JedisMovedDataException jme) { assertEquals(12182, jme.getSlot()); assertEquals(nodeInfo3, jme.getTargetNode()); return; } fail(); } @Test public void testThrowAskException() { int keySlot = JedisClusterCRC16.getSlot("test"); String node3Id = JedisClusterTestUtil.getNodeId(node3.clusterNodes()); node2.clusterSetSlotMigrating(keySlot, node3Id); assertThrows(JedisAskDataException.class, ()->node2.get("test")); } @Test public void testDiscoverNodesAutomatically() { Set jedisClusterNode = new HashSet<>(); jedisClusterNode.add(nodeInfo1); try (RedisClusterClient jc = RedisClusterClient.builder() .nodes(jedisClusterNode) .clientConfig(DefaultJedisClientConfig.builder() .connectionTimeoutMillis(DEFAULT_TIMEOUT) .socketTimeoutMillis(DEFAULT_TIMEOUT) .password(endpoint.getPassword()) .build()) .maxAttempts(DEFAULT_REDIRECTIONS) .poolConfig(DEFAULT_POOL_CONFIG) .build()) { assertEquals(3, jc.getClusterNodes().size()); } try (RedisClusterClient jc2 = RedisClusterClient.builder() .nodes(Collections.singleton(nodeInfo1)) .clientConfig(DefaultJedisClientConfig.builder() .connectionTimeoutMillis(DEFAULT_TIMEOUT) .socketTimeoutMillis(DEFAULT_TIMEOUT) .password(endpoint.getPassword()) .build()) .maxAttempts(DEFAULT_REDIRECTIONS) .poolConfig(DEFAULT_POOL_CONFIG) .build()) { assertEquals(3, jc2.getClusterNodes().size()); } } @Test public void testDiscoverNodesAutomaticallyWithSocketConfig() { HostAndPort hp = nodeInfo1; try (RedisClusterClient jc = RedisClusterClient.builder() .nodes(Collections.singleton(hp)) .clientConfig(DEFAULT_CLIENT_CONFIG) .maxAttempts(DEFAULT_REDIRECTIONS) .poolConfig(DEFAULT_POOL_CONFIG) .build()) { assertEquals(3, jc.getClusterNodes().size()); } try (RedisClusterClient jc = RedisClusterClient.builder() .nodes(Collections.singleton(hp)) .clientConfig(DEFAULT_CLIENT_CONFIG) .maxAttempts(DEFAULT_REDIRECTIONS) .poolConfig(DEFAULT_POOL_CONFIG) .build()) { assertEquals(3, jc.getClusterNodes().size()); } } @Test public void testSetClientName() { Set jedisClusterNode = new HashSet<>(); jedisClusterNode.add(nodeInfo1); String clientName = "myAppName"; try (RedisClusterClient jc = RedisClusterClient.builder() .nodes(jedisClusterNode) .clientConfig(DefaultJedisClientConfig.builder() .connectionTimeoutMillis(DEFAULT_TIMEOUT) .socketTimeoutMillis(DEFAULT_TIMEOUT) .password(endpoint.getPassword()) .clientName(clientName) .build()) .maxAttempts(DEFAULT_REDIRECTIONS) .poolConfig(DEFAULT_POOL_CONFIG) .build()) { for (Pool pool : jc.getClusterNodes().values()) { try (Jedis jedis = new Jedis(pool.getResource())) { assertEquals(clientName, jedis.clientGetname()); } } } } @Test public void testSetClientNameWithConfig() { HostAndPort hp = nodeInfo1; String clientName = "config-pattern-app"; try (RedisClusterClient jc = RedisClusterClient.builder() .nodes(Collections.singleton(hp)) .clientConfig(DefaultJedisClientConfig.builder().password(endpoint.getPassword()).clientName(clientName).build()) .maxAttempts(DEFAULT_REDIRECTIONS) .poolConfig(DEFAULT_POOL_CONFIG) .build()) { jc.getClusterNodes().values().forEach(pool -> { try (Jedis jedis = new Jedis(pool.getResource())) { assertEquals(clientName, jedis.clientGetname()); } }); } } @Test public void testCalculateConnectionPerSlot() { Set jedisClusterNode = new HashSet<>(); jedisClusterNode.add(nodeInfo1); try (RedisClusterClient jc = RedisClusterClient.builder() .nodes(jedisClusterNode) .clientConfig(DefaultJedisClientConfig.builder() .connectionTimeoutMillis(DEFAULT_TIMEOUT) .socketTimeoutMillis(DEFAULT_TIMEOUT) .password(endpoint.getPassword()) .build()) .maxAttempts(DEFAULT_REDIRECTIONS) .poolConfig(DEFAULT_POOL_CONFIG) .build()) { jc.set("foo", "bar"); jc.set("test", "test"); assertEquals("bar", node3.get("foo")); assertEquals("test", node2.get("test")); } try (RedisClusterClient jc2 = RedisClusterClient.builder() .nodes(Collections.singleton(nodeInfo1)) .clientConfig(DefaultJedisClientConfig.builder() .connectionTimeoutMillis(DEFAULT_TIMEOUT) .socketTimeoutMillis(DEFAULT_TIMEOUT) .password(endpoint.getPassword()) .build()) .maxAttempts(DEFAULT_REDIRECTIONS) .poolConfig(DEFAULT_POOL_CONFIG) .build()) { jc2.set("foo", "bar"); jc2.set("test", "test"); assertEquals("bar", node3.get("foo")); assertEquals("test", node2.get("test")); } } @Test public void testReadonlyAndReadwrite() throws Exception { node1.clusterMeet(LOCAL_IP, nodeInfoSlave2.getPort()); JedisClusterTestUtil.waitForClusterReady(node1, node2, node3, nodeSlave2); for (String nodeInfo : node2.clusterNodes().split("\n")) { if (nodeInfo.contains("myself")) { nodeSlave2.clusterReplicate(nodeInfo.split(" ")[0]); break; } } try { nodeSlave2.get("test"); fail(); } catch (JedisMovedDataException e) { } nodeSlave2.readonly(); nodeSlave2.get("test"); nodeSlave2.readwrite(); try { nodeSlave2.get("test"); fail(); } catch (JedisMovedDataException e) { } nodeSlave2.clusterReset(ClusterResetType.SOFT); nodeSlave2.flushDB(); } @Test public void testReadFromReplicas() throws Exception { node1.clusterMeet(LOCAL_IP, nodeInfoSlave2.getPort()); JedisClusterTestUtil.waitForClusterReady(node1, node2, node3, nodeSlave2); for (String nodeInfo : node2.clusterNodes().split("\n")) { if (nodeInfo.contains("myself")) { nodeSlave2.clusterReplicate(nodeInfo.split(" ")[0]); break; } } DefaultJedisClientConfig READ_REPLICAS_CLIENT_CONFIG = DefaultJedisClientConfig.builder() .password(endpoint.getPassword()).readOnlyForRedisClusterReplicas().build(); ClusterCommandObjects commandObjects = new ClusterCommandObjects(); try (RedisClusterClient jedisCluster = RedisClusterClient.builder() .nodes(Collections.singleton(nodeInfo1)) .clientConfig(READ_REPLICAS_CLIENT_CONFIG) .maxAttempts(DEFAULT_REDIRECTIONS) .poolConfig(DEFAULT_POOL_CONFIG) .build()) { assertEquals("OK", jedisCluster.set("test", "read-from-replicas")); assertEquals("read-from-replicas", jedisCluster.executeCommandToReplica(commandObjects.get("test"))); // TODO: ensure data being served from replica node(s) } nodeSlave2.clusterReset(ClusterResetType.SOFT); nodeSlave2.flushDB(); } /** * slot->nodes 15363 node3 e */ @Test public void testMigrate() { Set jedisClusterNode = new HashSet<>(); jedisClusterNode.add(nodeInfo1); try (RedisClusterClient jc = RedisClusterClient.builder() .nodes(jedisClusterNode) .clientConfig(DefaultJedisClientConfig.builder() .connectionTimeoutMillis(DEFAULT_TIMEOUT) .socketTimeoutMillis(DEFAULT_TIMEOUT) .password(endpoint.getPassword()) .build()) .maxAttempts(DEFAULT_REDIRECTIONS) .poolConfig(DEFAULT_POOL_CONFIG) .build()) { String node3Id = JedisClusterTestUtil.getNodeId(node3.clusterNodes()); String node2Id = JedisClusterTestUtil.getNodeId(node2.clusterNodes()); node3.clusterSetSlotMigrating(15363, node2Id); node2.clusterSetSlotImporting(15363, node3Id); try { node2.set("e", "e"); } catch (JedisMovedDataException jme) { assertEquals(15363, jme.getSlot()); assertEquals(new HostAndPort(LOCAL_IP, nodeInfo3.getPort()), jme.getTargetNode()); } try { node3.set("e", "e"); } catch (JedisAskDataException jae) { assertEquals(15363, jae.getSlot()); assertEquals(new HostAndPort(LOCAL_IP, nodeInfo2.getPort()), jae.getTargetNode()); } jc.set("e", "e"); try { node2.get("e"); } catch (JedisMovedDataException jme) { assertEquals(15363, jme.getSlot()); assertEquals(new HostAndPort(LOCAL_IP, nodeInfo3.getPort()), jme.getTargetNode()); } try { node3.get("e"); } catch (JedisAskDataException jae) { assertEquals(15363, jae.getSlot()); assertEquals(new HostAndPort(LOCAL_IP, nodeInfo2.getPort()), jae.getTargetNode()); } assertEquals("e", jc.get("e")); node2.clusterSetSlotNode(15363, node2Id); node3.clusterSetSlotNode(15363, node2Id); // assertEquals("e", jc.get("e")); assertEquals("e", node2.get("e")); // assertEquals("e", node3.get("e")); } } @Test public void testMigrateToNewNode() throws InterruptedException { Set jedisClusterNode = new HashSet<>(); jedisClusterNode.add(nodeInfo1); try (RedisClusterClient jc = RedisClusterClient.builder() .nodes(jedisClusterNode) .clientConfig(DefaultJedisClientConfig.builder() .connectionTimeoutMillis(DEFAULT_TIMEOUT) .socketTimeoutMillis(DEFAULT_TIMEOUT) .password(endpoint.getPassword()) .build()) .maxAttempts(DEFAULT_REDIRECTIONS) .poolConfig(DEFAULT_POOL_CONFIG) .build()) { node3.clusterMeet(LOCAL_IP, nodeInfo4.getPort()); String node3Id = JedisClusterTestUtil.getNodeId(node3.clusterNodes()); String node4Id = JedisClusterTestUtil.getNodeId(node4.clusterNodes()); JedisClusterTestUtil.waitForClusterReady(node4); node3.clusterSetSlotMigrating(15363, node4Id); node4.clusterSetSlotImporting(15363, node3Id); try { node4.set("e", "e"); } catch (JedisMovedDataException jme) { assertEquals(15363, jme.getSlot()); assertEquals(new HostAndPort(LOCAL_IP, nodeInfo3.getPort()), jme.getTargetNode()); } try { node3.set("e", "e"); } catch (JedisAskDataException jae) { assertEquals(15363, jae.getSlot()); assertEquals(new HostAndPort(LOCAL_IP, nodeInfo4.getPort()), jae.getTargetNode()); } try { node3.set("e", "e"); } catch (JedisAskDataException jae) { assertEquals(15363, jae.getSlot()); assertEquals(new HostAndPort(LOCAL_IP, nodeInfo4.getPort()), jae.getTargetNode()); } jc.set("e", "e"); try { node4.get("e"); } catch (JedisMovedDataException jme) { assertEquals(15363, jme.getSlot()); assertEquals(new HostAndPort(LOCAL_IP, nodeInfo3.getPort()), jme.getTargetNode()); } try { node3.get("e"); } catch (JedisAskDataException jae) { assertEquals(15363, jae.getSlot()); assertEquals(new HostAndPort(LOCAL_IP, nodeInfo4.getPort()), jae.getTargetNode()); } assertEquals("e", jc.get("e")); node4.clusterSetSlotNode(15363, node4Id); node3.clusterSetSlotNode(15363, node4Id); // assertEquals("e", jc.get("e")); assertEquals("e", node4.get("e")); // assertEquals("e", node3.get("e")); } } @Test public void testRecalculateSlotsWhenMoved() throws InterruptedException { Set jedisClusterNode = new HashSet<>(); jedisClusterNode.add(nodeInfo1); try (RedisClusterClient jc = RedisClusterClient.builder() .nodes(jedisClusterNode) .clientConfig(DefaultJedisClientConfig.builder() .connectionTimeoutMillis(DEFAULT_TIMEOUT) .socketTimeoutMillis(DEFAULT_TIMEOUT) .password(endpoint.getPassword()) .build()) .maxAttempts(DEFAULT_REDIRECTIONS) .poolConfig(DEFAULT_POOL_CONFIG) .build()) { int slot51 = JedisClusterCRC16.getSlot("51"); node2.clusterDelSlots(slot51); node3.clusterDelSlots(slot51); node3.clusterAddSlots(slot51); JedisClusterTestUtil.waitForClusterReady(node1, node2, node3); jc.set("51", "foo"); assertEquals("foo", jc.get("51")); } } @Test public void testAskResponse() { Set jedisClusterNode = new HashSet<>(); jedisClusterNode.add(nodeInfo1); try (RedisClusterClient jc = RedisClusterClient.builder() .nodes(jedisClusterNode) .clientConfig(DefaultJedisClientConfig.builder() .connectionTimeoutMillis(DEFAULT_TIMEOUT) .socketTimeoutMillis(DEFAULT_TIMEOUT) .password(endpoint.getPassword()) .build()) .maxAttempts(DEFAULT_REDIRECTIONS) .poolConfig(DEFAULT_POOL_CONFIG) .build()) { int slot51 = JedisClusterCRC16.getSlot("51"); node3.clusterSetSlotImporting(slot51, JedisClusterTestUtil.getNodeId(node2.clusterNodes())); node2.clusterSetSlotMigrating(slot51, JedisClusterTestUtil.getNodeId(node3.clusterNodes())); jc.set("51", "foo"); assertEquals("foo", jc.get("51")); } } @Test public void testAskResponseWithConfig() { HostAndPort hp = nodeInfo1; try (RedisClusterClient jc = RedisClusterClient.builder() .nodes(Collections.singleton(hp)) .clientConfig(DEFAULT_CLIENT_CONFIG) .maxAttempts(DEFAULT_REDIRECTIONS) .poolConfig(DEFAULT_POOL_CONFIG) .build()) { int slot51 = JedisClusterCRC16.getSlot("51"); node3.clusterSetSlotImporting(slot51, JedisClusterTestUtil.getNodeId(node2.clusterNodes())); node2.clusterSetSlotMigrating(slot51, JedisClusterTestUtil.getNodeId(node3.clusterNodes())); jc.set("51", "foo"); assertEquals("foo", jc.get("51")); } } // @Test(expected = JedisClusterMaxAttemptsException.class) @Test public void testRedisClusterMaxRedirections() { Set jedisClusterNode = new HashSet<>(); jedisClusterNode.add(nodeInfo1); assertThrows(JedisClusterOperationException.class,()-> { try (RedisClusterClient jc = RedisClusterClient.builder() .nodes(jedisClusterNode) .clientConfig(DefaultJedisClientConfig.builder() .connectionTimeoutMillis(DEFAULT_TIMEOUT) .socketTimeoutMillis(DEFAULT_TIMEOUT) .password(endpoint.getPassword()) .build()) .maxAttempts(DEFAULT_REDIRECTIONS) .poolConfig(DEFAULT_POOL_CONFIG) .build()) { int slot51 = JedisClusterCRC16.getSlot("51"); // This will cause an infinite redirection loop node2.clusterSetSlotMigrating(slot51, JedisClusterTestUtil.getNodeId(node3.clusterNodes())); jc.set("51", "foo"); } }); } // @Test(expected = JedisClusterMaxAttemptsException.class) @Test public void testRedisClusterMaxRedirectionsWithConfig() { HostAndPort hp = nodeInfo1; try (RedisClusterClient jc = RedisClusterClient.builder() .nodes(Collections.singleton(hp)) .clientConfig(DEFAULT_CLIENT_CONFIG) .maxAttempts(DEFAULT_REDIRECTIONS) .poolConfig(DEFAULT_POOL_CONFIG) .build()) { int slot51 = JedisClusterCRC16.getSlot("51"); // This will cause an infinite redirection loop node2.clusterSetSlotMigrating(slot51, JedisClusterTestUtil.getNodeId(node3.clusterNodes())); assertThrows(JedisClusterOperationException.class, ()->jc.set("51", "foo")); } } @Test public void testClusterForgetNode() { // at first, join node4 to cluster node1.clusterMeet(nodeInfo4.getHost(), nodeInfo4.getPort()); node2.clusterMeet(nodeInfo4.getHost(), nodeInfo4.getPort()); node3.clusterMeet(nodeInfo4.getHost(), nodeInfo4.getPort()); String node4Id = JedisClusterTestUtil.getNodeId(node4.clusterNodes()); JedisClusterTestUtil.assertNodeIsKnown(node1, node4Id, 1000); JedisClusterTestUtil.assertNodeIsKnown(node2, node4Id, 1000); JedisClusterTestUtil.assertNodeIsKnown(node3, node4Id, 1000); assertNodeHandshakeEnded(node1, 1000); assertNodeHandshakeEnded(node2, 1000); assertNodeHandshakeEnded(node3, 1000); assertEquals(4, node1.clusterNodes().split("\n").length); assertEquals(4, node2.clusterNodes().split("\n").length); assertEquals(4, node3.clusterNodes().split("\n").length); // do cluster forget node1.clusterForget(node4Id); node2.clusterForget(node4Id); node3.clusterForget(node4Id); JedisClusterTestUtil.assertNodeIsUnknown(node1, node4Id, 1000); JedisClusterTestUtil.assertNodeIsUnknown(node2, node4Id, 1000); JedisClusterTestUtil.assertNodeIsUnknown(node3, node4Id, 1000); assertEquals(3, node1.clusterNodes().split("\n").length); assertEquals(3, node2.clusterNodes().split("\n").length); assertEquals(3, node3.clusterNodes().split("\n").length); } @Test public void testClusterFlushSlots() { String slotRange = getNodeServingSlotRange(node1.clusterNodes()); assertNotNull(slotRange); try { node1.clusterFlushSlots(); assertNull(getNodeServingSlotRange(node1.clusterNodes())); } finally { // rollback String[] rangeInfo = slotRange.split("-"); int lower = Integer.parseInt(rangeInfo[0]); int upper = Integer.parseInt(rangeInfo[1]); int[] node1Slots = new int[upper - lower + 1]; for (int i = 0; lower <= upper;) { node1Slots[i++] = lower++; } node1.clusterAddSlots(node1Slots); } } @Test public void testClusterCountKeysInSlot() { Set jedisClusterNode = new HashSet<>(); jedisClusterNode.add(new HostAndPort(nodeInfo1.getHost(), nodeInfo1.getPort())); try (RedisClusterClient jc = RedisClusterClient.builder() .nodes(jedisClusterNode) .clientConfig(DefaultJedisClientConfig.builder() .connectionTimeoutMillis(DEFAULT_TIMEOUT) .socketTimeoutMillis(DEFAULT_TIMEOUT) .password(endpoint.getPassword()) .build()) .maxAttempts(DEFAULT_REDIRECTIONS) .poolConfig(DEFAULT_POOL_CONFIG) .build()) { int count = 5; for (int index = 0; index < count; index++) { jc.set("foo{bar}" + index, "hello"); } int slot = JedisClusterCRC16.getSlot("foo{bar}"); assertEquals(count, node1.clusterCountKeysInSlot(slot)); } } @Test public void testStableSlotWhenMigratingNodeOrImportingNodeIsNotSpecified() throws InterruptedException { Set jedisClusterNode = new HashSet<>(); jedisClusterNode.add(new HostAndPort(nodeInfo1.getHost(), nodeInfo1.getPort())); try (RedisClusterClient jc = RedisClusterClient.builder() .nodes(jedisClusterNode) .clientConfig(DefaultJedisClientConfig.builder() .connectionTimeoutMillis(DEFAULT_TIMEOUT) .socketTimeoutMillis(DEFAULT_TIMEOUT) .password(endpoint.getPassword()) .build()) .maxAttempts(DEFAULT_REDIRECTIONS) .poolConfig(DEFAULT_POOL_CONFIG) .build()) { int slot51 = JedisClusterCRC16.getSlot("51"); jc.set("51", "foo"); // node2 is responsible of taking care of slot51 (7186) node3.clusterSetSlotImporting(slot51, JedisClusterTestUtil.getNodeId(node2.clusterNodes())); assertEquals("foo", jc.get("51")); node3.clusterSetSlotStable(slot51); assertEquals("foo", jc.get("51")); node2.clusterSetSlotMigrating(slot51, JedisClusterTestUtil.getNodeId(node3.clusterNodes())); // assertEquals("foo", jc.get("51")); // it leads Max Redirections node2.clusterSetSlotStable(slot51); assertEquals("foo", jc.get("51")); } } @Test public void testIfPoolConfigAppliesToClusterPools() { GenericObjectPoolConfig config = new GenericObjectPoolConfig<>(); config.setMaxTotal(0); config.setMaxWait(Duration.ofMillis(DEFAULT_TIMEOUT)); Set jedisClusterNode = new HashSet<>(); jedisClusterNode.add(nodeInfo1); try (RedisClusterClient jc = RedisClusterClient.builder() .nodes(jedisClusterNode) .clientConfig(DefaultJedisClientConfig.builder() .connectionTimeoutMillis(DEFAULT_TIMEOUT) .socketTimeoutMillis(DEFAULT_TIMEOUT) .password(endpoint.getPassword()) .build()) .maxAttempts(DEFAULT_REDIRECTIONS) .poolConfig(config) .build()) { assertThrows(JedisException.class, ()->jc.set("52", "poolTestValue")); } } @Test public void testCloseable() throws IOException { Set jedisClusterNode = new HashSet<>(); jedisClusterNode.add(new HostAndPort(nodeInfo1.getHost(), nodeInfo1.getPort())); RedisClusterClient jc = RedisClusterClient.builder() .nodes(jedisClusterNode) .clientConfig(DefaultJedisClientConfig.builder() .connectionTimeoutMillis(DEFAULT_TIMEOUT) .socketTimeoutMillis(DEFAULT_TIMEOUT) .password(endpoint.getPassword()) .build()) .maxAttempts(DEFAULT_REDIRECTIONS) .poolConfig(DEFAULT_POOL_CONFIG) .build(); jc.set("51", "foo"); jc.close(); assertEquals(0, jc.getClusterNodes().size()); } @Test public void testCloseableWithConfig() { HostAndPort hp = nodeInfo1; try (RedisClusterClient jc = RedisClusterClient.builder() .nodes(Collections.singleton(hp)) .clientConfig(DEFAULT_CLIENT_CONFIG) .maxAttempts(DEFAULT_REDIRECTIONS) .poolConfig(DEFAULT_POOL_CONFIG) .build()) { jc.set("51", "foo"); jc.close(); assertEquals(0, jc.getClusterNodes().size()); } } @Test public void testJedisClusterTimeout() { Set jedisClusterNode = new HashSet<>(); jedisClusterNode.add(new HostAndPort(nodeInfo1.getHost(), nodeInfo1.getPort())); try (RedisClusterClient jc = RedisClusterClient.builder() .nodes(jedisClusterNode) .clientConfig(DefaultJedisClientConfig.builder() .connectionTimeoutMillis(4000) .socketTimeoutMillis(4000) .password(endpoint.getPassword()) .build()) .maxAttempts(DEFAULT_REDIRECTIONS) .poolConfig(DEFAULT_POOL_CONFIG) .build()) { for (Pool pool : jc.getClusterNodes().values()) { try (Connection conn = pool.getResource()) { assertEquals(4000, conn.getSoTimeout()); } } } } @Test public void testJedisClusterTimeoutWithConfig() { HostAndPort hp = nodeInfo1; try (RedisClusterClient jc = RedisClusterClient.builder() .nodes(Collections.singleton(hp)) .clientConfig(DefaultJedisClientConfig.builder() .connectionTimeoutMillis(4000).socketTimeoutMillis(4000).password(endpoint.getPassword()).build()) .maxAttempts(DEFAULT_REDIRECTIONS) .poolConfig(DEFAULT_POOL_CONFIG) .build()) { jc.getClusterNodes().values().forEach(pool -> { try (Connection conn = pool.getResource()) { assertEquals(4000, conn.getSoTimeout()); } }); } } @Test public void testJedisClusterRunsWithMultithreaded() throws InterruptedException, ExecutionException, IOException { Set jedisClusterNode = new HashSet<>(); jedisClusterNode.add(nodeInfo1); final RedisClusterClient jc = RedisClusterClient.builder() .nodes(jedisClusterNode) .clientConfig(DefaultJedisClientConfig.builder() .connectionTimeoutMillis(DEFAULT_TIMEOUT) .socketTimeoutMillis(DEFAULT_TIMEOUT) .password(endpoint.getPassword()) .build()) .maxAttempts(DEFAULT_REDIRECTIONS) .poolConfig(DEFAULT_POOL_CONFIG) .build(); jc.set("foo", "bar"); ThreadPoolExecutor executor = new ThreadPoolExecutor(10, 100, 0, TimeUnit.SECONDS, new ArrayBlockingQueue(10)); List> futures = new ArrayList>(); for (int i = 0; i < 50; i++) { executor.submit(new Callable() { @Override public String call() throws Exception { // FIXME : invalidate slot cache from JedisCluster to test // random connection also does work return jc.get("foo"); } }); } for (Future future : futures) { String value = future.get(); assertEquals("bar", value); } jc.close(); } @Test @Timeout(value = DEFAULT_TIMEOUT * 2, unit = TimeUnit.MILLISECONDS) public void testReturnConnectionOnJedisConnectionException() throws InterruptedException { Set jedisClusterNode = new HashSet<>(); jedisClusterNode.add(nodeInfo1); ConnectionPoolConfig config = new ConnectionPoolConfig(); config.setMaxTotal(1); try (RedisClusterClient jc = RedisClusterClient.builder() .nodes(jedisClusterNode) .clientConfig(DefaultJedisClientConfig.builder() .connectionTimeoutMillis(DEFAULT_TIMEOUT) .socketTimeoutMillis(DEFAULT_TIMEOUT) .password(endpoint.getPassword()) .build()) .maxAttempts(DEFAULT_REDIRECTIONS) .poolConfig(config) .build()) { try (Connection c = jc.getClusterNodes().get(nodeInfo2.toString()).getResource()) { Jedis j = new Jedis(c); ClientKillerUtil.tagClient(j, "DEAD"); ClientKillerUtil.killClient(j, "DEAD"); } jc.get("test"); } } @Test @Timeout(value = DEFAULT_TIMEOUT, unit = TimeUnit.MILLISECONDS) public void testReturnConnectionOnRedirection() { Set jedisClusterNode = new HashSet<>(); jedisClusterNode.add(nodeInfo1); ConnectionPoolConfig config = new ConnectionPoolConfig(); config.setMaxTotal(1); try (RedisClusterClient jc = RedisClusterClient.builder() .nodes(jedisClusterNode) .clientConfig(DefaultJedisClientConfig.builder() .connectionTimeoutMillis(DEFAULT_TIMEOUT) .socketTimeoutMillis(DEFAULT_TIMEOUT) .password(endpoint.getPassword()) .build()) .maxAttempts(DEFAULT_REDIRECTIONS) .poolConfig(config) .build()) { // This will cause an infinite redirection between node 2 and 3 node3.clusterSetSlotMigrating(15363, JedisClusterTestUtil.getNodeId(node2.clusterNodes())); assertThrows(JedisClusterOperationException.class, ()->jc.get("e")); } } @Test public void testLocalhostNodeNotAddedWhen127Present() { HostAndPort localhost = new HostAndPort("localhost", nodeInfo1.getPort()); Set jedisClusterNode = new HashSet<>(); // cluster node is defined as 127.0.0.1; adding localhost should work, // but shouldn't show up. jedisClusterNode.add(localhost); ConnectionPoolConfig config = new ConnectionPoolConfig(); config.setMaxTotal(1); try (RedisClusterClient jc = RedisClusterClient.builder() .nodes(jedisClusterNode) .clientConfig(DefaultJedisClientConfig.builder() .connectionTimeoutMillis(DEFAULT_TIMEOUT) .socketTimeoutMillis(DEFAULT_TIMEOUT) .password(endpoint.getPassword()) .build()) .maxAttempts(DEFAULT_REDIRECTIONS) .poolConfig(config) .build()) { Map clusterNodes = jc.getClusterNodes(); assertEquals(3, clusterNodes.size()); assertFalse(clusterNodes.containsKey(JedisClusterInfoCache.getNodeKey(localhost))); } } @Test public void testInvalidStartNodeNotAdded() { HostAndPort invalidHost = new HostAndPort("not-a-real-host", nodeInfo1.getPort()); Set jedisClusterNode = new LinkedHashSet(); jedisClusterNode.add(invalidHost); jedisClusterNode.add(nodeInfo1); ConnectionPoolConfig config = new ConnectionPoolConfig(); config.setMaxTotal(1); try (RedisClusterClient jc = RedisClusterClient.builder() .nodes(jedisClusterNode) .clientConfig(DefaultJedisClientConfig.builder() .connectionTimeoutMillis(DEFAULT_TIMEOUT) .socketTimeoutMillis(DEFAULT_TIMEOUT) .password(endpoint.getPassword()) .build()) .maxAttempts(DEFAULT_REDIRECTIONS) .poolConfig(config) .build()) { Map clusterNodes = jc.getClusterNodes(); assertEquals(3, clusterNodes.size()); assertFalse(clusterNodes.containsKey(JedisClusterInfoCache.getNodeKey(invalidHost))); } } @Test @SinceRedisVersion("7.0.0") public void clusterLinks2() { Set mapKeys = new HashSet<>(Arrays.asList("direction", "node", "create-time", "events", "send-buffer-allocated", "send-buffer-used")); List> links = node1.clusterLinks(); assertNotNull(links); assertTrue(links.size() >= 3); for (Map link : links) { assertEquals(6, link.size()); assertEquals(mapKeys, link.keySet()); } } @Test public void clusterRefreshNodes() throws Exception { Set jedisClusterNode = new HashSet<>(); jedisClusterNode.add(nodeInfo1); jedisClusterNode.add(nodeInfo2); jedisClusterNode.add(nodeInfo3); try (RedisClusterClient cluster = RedisClusterClient.builder() .nodes(jedisClusterNode) .clientConfig(DefaultJedisClientConfig.builder() .connectionTimeoutMillis(DEFAULT_TIMEOUT) .socketTimeoutMillis(DEFAULT_TIMEOUT) .password(endpoint.getPassword()) .build()) .maxAttempts(DEFAULT_REDIRECTIONS) .poolConfig(DEFAULT_POOL_CONFIG) .build()) { assertEquals(3, cluster.getClusterNodes().size()); cleanUp(); // cleanup and add node4 // at first, join node4 to cluster node1.clusterMeet(LOCAL_IP, nodeInfo2.getPort()); node1.clusterMeet(LOCAL_IP, nodeInfo3.getPort()); node1.clusterMeet(LOCAL_IP, nodeInfo4.getPort()); // split available slots across the three nodes int slotsPerNode = CLUSTER_HASHSLOTS / 4; int[] node1Slots = new int[slotsPerNode]; int[] node2Slots = new int[slotsPerNode]; int[] node3Slots = new int[slotsPerNode]; int[] node4Slots = new int[slotsPerNode]; for (int i = 0, slot1 = 0, slot2 = 0, slot3 = 0, slot4 = 0; i < CLUSTER_HASHSLOTS; i++) { if (i < slotsPerNode) { node1Slots[slot1++] = i; } else if (i >= slotsPerNode && i < slotsPerNode*2) { node2Slots[slot2++] = i; } else if (i >= slotsPerNode*2 && i < slotsPerNode*3) { node3Slots[slot3++] = i; } else { node4Slots[slot4++] = i; } } node1.clusterAddSlots(node1Slots); node2.clusterAddSlots(node2Slots); node3.clusterAddSlots(node3Slots); node4.clusterAddSlots(node4Slots); JedisClusterTestUtil.waitForClusterReady(node1, node2, node3, node4); // cluster.set("key", "value"); will get JedisMovedDataException and renewSlotCache cluster.set("key", "value"); assertEquals(4, cluster.getClusterNodes().size()); String nodeKey4 = LOCAL_IP + ":" + nodeInfo4.getPort(); assertTrue(cluster.getClusterNodes().keySet().contains(nodeKey4)); // make 4 nodes to 3 nodes cleanUp(); setUp(); // cluster.set("bar", "foo") will get JedisMovedDataException and renewSlotCache cluster.set("bar", "foo"); assertEquals(3, cluster.getClusterNodes().size()); } } @Test @Timeout(30) public void clusterPeriodTopologyRefreshTest() throws Exception { Set jedisClusterNode = new HashSet<>(); jedisClusterNode.add(nodeInfo1); jedisClusterNode.add(nodeInfo2); jedisClusterNode.add(nodeInfo3); // we set topologyRefreshPeriod is 1s Duration topologyRefreshPeriod = Duration.ofSeconds(1); try (RedisClusterClient cluster = RedisClusterClient.builder() .nodes(jedisClusterNode) .clientConfig(DEFAULT_CLIENT_CONFIG) .poolConfig(DEFAULT_POOL_CONFIG) .topologyRefreshPeriod(topologyRefreshPeriod) .maxAttempts(DEFAULT_REDIRECTIONS) .maxTotalRetriesDuration(Duration.ofSeconds(10)) .build()) { assertEquals(3, cluster.getClusterNodes().size()); cleanUp(); // cleanup and add node4 // at first, join node4 to cluster node1.clusterMeet(LOCAL_IP, nodeInfo2.getPort()); node1.clusterMeet(LOCAL_IP, nodeInfo3.getPort()); node1.clusterMeet(LOCAL_IP, nodeInfo4.getPort()); // split available slots across the three nodes int slotsPerNode = CLUSTER_HASHSLOTS / 4; int[] node1Slots = new int[slotsPerNode]; int[] node2Slots = new int[slotsPerNode]; int[] node3Slots = new int[slotsPerNode]; int[] node4Slots = new int[slotsPerNode]; for (int i = 0, slot1 = 0, slot2 = 0, slot3 = 0, slot4 = 0; i < CLUSTER_HASHSLOTS; i++) { if (i < slotsPerNode) { node1Slots[slot1++] = i; } else if (i >= slotsPerNode && i < slotsPerNode*2) { node2Slots[slot2++] = i; } else if (i >= slotsPerNode*2 && i < slotsPerNode*3) { node3Slots[slot3++] = i; } else { node4Slots[slot4++] = i; } } node1.clusterAddSlots(node1Slots); node2.clusterAddSlots(node2Slots); node3.clusterAddSlots(node3Slots); node4.clusterAddSlots(node4Slots); JedisClusterTestUtil.waitForClusterReady(node1, node2, node3, node4); // Now we just wait topologyRefreshPeriod * 3 (executor will delay) for cluster topology refresh (3 -> 4) Thread.sleep(topologyRefreshPeriod.toMillis() * 3); assertEquals(4, cluster.getClusterNodes().size()); String nodeKey4 = LOCAL_IP + ":" + nodeInfo4.getPort(); assertTrue(cluster.getClusterNodes().keySet().contains(nodeKey4)); // make 4 nodes to 3 nodes cleanUp(); setUp(); // Now we just wait topologyRefreshPeriod * 3 (executor will delay) for cluster topology refresh (4 -> 3) Thread.sleep(topologyRefreshPeriod.toMillis() * 3); assertEquals(3, cluster.getClusterNodes().size()); } } private static String getNodeServingSlotRange(String infoOutput) { // f4f3dc4befda352a4e0beccf29f5e8828438705d 127.0.0.1:7380 master - 0 // 1394372400827 0 connected 5461-10922 for (String infoLine : infoOutput.split("\n")) { if (infoLine.contains("myself")) { try { return infoLine.split(" ")[8]; } catch (ArrayIndexOutOfBoundsException e) { return null; } } } return null; } private void assertNodeHandshakeEnded(Jedis node, int timeoutMs) { int sleepInterval = 100; for (int sleepTime = 0; sleepTime <= timeoutMs; sleepTime += sleepInterval) { boolean isHandshaking = isAnyNodeHandshaking(node); if (!isHandshaking) return; try { Thread.sleep(sleepInterval); } catch (InterruptedException e) { } } throw new JedisException("Node handshaking is not ended"); } private boolean isAnyNodeHandshaking(Jedis node) { String infoOutput = node.clusterNodes(); for (String infoLine : infoOutput.split("\n")) { if (infoLine.contains("handshake")) { return true; } } return false; } } ================================================ FILE: src/test/java/redis/clients/jedis/UnboundRedisClusterClientTestBase.java ================================================ package redis.clients.jedis; import static redis.clients.jedis.Protocol.CLUSTER_HASHSLOTS; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Tag; import org.junit.jupiter.api.extension.RegisterExtension; import org.junit.jupiter.api.BeforeAll; import redis.clients.jedis.util.EnabledOnCommandCondition; import redis.clients.jedis.util.RedisVersionCondition; import redis.clients.jedis.args.ClusterResetType; import redis.clients.jedis.util.JedisClusterTestUtil; @Tag("integration") public abstract class UnboundRedisClusterClientTestBase { protected static EndpointConfig endpoint; protected static Jedis node1; protected static Jedis node2; protected static Jedis node3; protected static Jedis node4; protected static Jedis nodeSlave2; protected static HostAndPort nodeInfo1; protected static HostAndPort nodeInfo2; protected static HostAndPort nodeInfo3; protected static HostAndPort nodeInfo4; protected static HostAndPort nodeInfoSlave2; protected static final String LOCAL_IP = "127.0.0.1"; @RegisterExtension public RedisVersionCondition versionCondition = new RedisVersionCondition( () -> Endpoints.getRedisEndpoint("cluster-unbound")); @RegisterExtension public EnabledOnCommandCondition enabledOnCommandCondition = new EnabledOnCommandCondition( () -> Endpoints.getRedisEndpoint("cluster-unbound")); @BeforeAll public static void prepareEndpoint() { endpoint = Endpoints.getRedisEndpoint("cluster-unbound"); nodeInfo1 = endpoint.getHostsAndPorts().get(0); nodeInfo2 = endpoint.getHostsAndPorts().get(1); nodeInfo3 = endpoint.getHostsAndPorts().get(2); nodeInfo4 = endpoint.getHostsAndPorts().get(3); nodeInfoSlave2 = endpoint.getHostsAndPorts().get(4); } @BeforeEach public void setUp() throws InterruptedException { node1 = new Jedis(nodeInfo1); node1.auth(endpoint.getPassword()); node1.flushAll(); node2 = new Jedis(nodeInfo2); node2.auth(endpoint.getPassword()); node2.flushAll(); node3 = new Jedis(nodeInfo3); node3.auth(endpoint.getPassword()); node3.flushAll(); node4 = new Jedis(nodeInfo4); node4.auth(endpoint.getPassword()); node4.flushAll(); nodeSlave2 = new Jedis(nodeInfoSlave2); nodeSlave2.auth(endpoint.getPassword()); nodeSlave2.flushAll(); // ---- configure cluster // add nodes to cluster node1.clusterMeet(LOCAL_IP, nodeInfo2.getPort()); node1.clusterMeet(LOCAL_IP, nodeInfo3.getPort()); // split available slots across the three nodes int slotsPerNode = CLUSTER_HASHSLOTS / 3; int[] node1Slots = new int[slotsPerNode]; int[] node2Slots = new int[slotsPerNode + 1]; int[] node3Slots = new int[slotsPerNode]; for (int i = 0, slot1 = 0, slot2 = 0, slot3 = 0; i < CLUSTER_HASHSLOTS; i++) { if (i < slotsPerNode) { node1Slots[slot1++] = i; } else if (i > slotsPerNode * 2) { node3Slots[slot3++] = i; } else { node2Slots[slot2++] = i; } } node1.clusterAddSlots(node1Slots); node2.clusterAddSlots(node2Slots); node3.clusterAddSlots(node3Slots); JedisClusterTestUtil.waitForClusterReady(node1, node2, node3); } protected void cleanUp() { node1.flushDB(); node2.flushDB(); node3.flushDB(); node4.flushDB(); node1.clusterReset(ClusterResetType.HARD); node2.clusterReset(ClusterResetType.HARD); node3.clusterReset(ClusterResetType.HARD); node4.clusterReset(ClusterResetType.HARD); } @AfterEach public void tearDown() { cleanUp(); } } ================================================ FILE: src/test/java/redis/clients/jedis/UnifiedJedisCustomCommandsTest.java ================================================ package redis.clients.jedis; import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.equalTo; import static org.hamcrest.Matchers.sameInstance; import static org.mockito.ArgumentMatchers.any; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; import org.junit.jupiter.api.Test; import org.mockito.ArgumentCaptor; import redis.clients.jedis.commands.ProtocolCommand; import redis.clients.jedis.mocked.unified.UnifiedJedisMockedTestBase; /** * These tests are part of the mocked tests for {@link UnifiedJedis}, but, due to {@code protected} * visibility of some methods, they must reside in the same package as the tested class. */ public class UnifiedJedisCustomCommandsTest extends UnifiedJedisMockedTestBase { @Test public void testSendCommandWithProtocolCommand() { ProtocolCommand cmd = mock(ProtocolCommand.class); CommandArguments commandArguments = mock(CommandArguments.class); when(commandObjects.commandArguments(cmd)).thenReturn(commandArguments); when(commandExecutor.executeCommand(any())).thenReturn("OK"); Object result = jedis.sendCommand(cmd); ArgumentCaptor> argumentCaptor = ArgumentCaptor.forClass(CommandObject.class); verify(commandExecutor).executeCommand(argumentCaptor.capture()); CommandObject commandObject = argumentCaptor.getValue(); assertThat(commandObject.getArguments(), sameInstance(commandArguments)); assertThat(result, equalTo("OK")); verify(commandObjects).commandArguments(cmd); } @Test public void testSendCommandWithProtocolCommandAndByteArrayArgs() { ProtocolCommand cmd = mock(ProtocolCommand.class); byte[][] args = { "arg1".getBytes(), "arg2".getBytes() }; CommandArguments commandArguments = mock(CommandArguments.class); CommandArguments commandArgumentsWithArgs = mock(CommandArguments.class); when(commandArguments.addObjects((Object[]) args)).thenReturn(commandArgumentsWithArgs); when(commandObjects.commandArguments(cmd)).thenReturn(commandArguments); when(commandExecutor.executeCommand(any())).thenReturn("OK"); Object result = jedis.sendCommand(cmd, args); ArgumentCaptor> argumentCaptor = ArgumentCaptor.forClass(CommandObject.class); verify(commandExecutor).executeCommand(argumentCaptor.capture()); CommandObject commandObject = argumentCaptor.getValue(); assertThat(commandObject.getArguments(), sameInstance(commandArgumentsWithArgs)); assertThat(result, equalTo("OK")); verify(commandObjects).commandArguments(cmd); } @Test public void testSendBlockingCommandWithProtocolCommandAndByteArrayArgs() { ProtocolCommand cmd = mock(ProtocolCommand.class); byte[][] args = { "arg1".getBytes(), "arg2".getBytes() }; CommandArguments commandArguments = mock(CommandArguments.class); CommandArguments commandArgumentsBlocking = mock(CommandArguments.class); CommandArguments commandArgumentsWithArgs = mock(CommandArguments.class); when(commandArgumentsWithArgs.blocking()).thenReturn(commandArgumentsBlocking); when(commandArguments.addObjects((Object[]) args)).thenReturn(commandArgumentsWithArgs); when(commandObjects.commandArguments(cmd)).thenReturn(commandArguments); when(commandExecutor.executeCommand(any())).thenReturn("OK"); Object result = jedis.sendBlockingCommand(cmd, args); ArgumentCaptor> argumentCaptor = ArgumentCaptor.forClass(CommandObject.class); verify(commandExecutor).executeCommand(argumentCaptor.capture()); CommandObject commandObject = argumentCaptor.getValue(); assertThat(commandObject.getArguments(), sameInstance(commandArgumentsBlocking)); assertThat(result, equalTo("OK")); verify(commandObjects).commandArguments(cmd); } @Test public void testSendCommandWithProtocolCommandAndStringArgs() { ProtocolCommand cmd = mock(ProtocolCommand.class); String[] args = { "arg1", "arg2" }; CommandArguments commandArguments = mock(CommandArguments.class); CommandArguments commandArgumentsWithArgs = mock(CommandArguments.class); when(commandArguments.addObjects((Object[]) args)).thenReturn(commandArgumentsWithArgs); when(commandObjects.commandArguments(cmd)).thenReturn(commandArguments); when(commandExecutor.executeCommand(any())).thenReturn("OK"); Object result = jedis.sendCommand(cmd, args); ArgumentCaptor> argumentCaptor = ArgumentCaptor.forClass(CommandObject.class); verify(commandExecutor).executeCommand(argumentCaptor.capture()); CommandObject commandObject = argumentCaptor.getValue(); assertThat(commandObject.getArguments(), sameInstance(commandArgumentsWithArgs)); assertThat(result, equalTo("OK")); verify(commandObjects).commandArguments(cmd); } @Test public void testSendBlockingCommandWithProtocolCommandAndStringArgs() { ProtocolCommand cmd = mock(ProtocolCommand.class); String[] args = { "arg1", "arg2" }; CommandArguments commandArguments = mock(CommandArguments.class); CommandArguments commandArgumentsBlocking = mock(CommandArguments.class); CommandArguments commandArgumentsWithArgs = mock(CommandArguments.class); when(commandArgumentsWithArgs.blocking()).thenReturn(commandArgumentsBlocking); when(commandArguments.addObjects((Object[]) args)).thenReturn(commandArgumentsWithArgs); when(commandObjects.commandArguments(cmd)).thenReturn(commandArguments); when(commandExecutor.executeCommand(any())).thenReturn("OK"); Object result = jedis.sendBlockingCommand(cmd, args); ArgumentCaptor> argumentCaptor = ArgumentCaptor.forClass(CommandObject.class); verify(commandExecutor).executeCommand(argumentCaptor.capture()); CommandObject commandObject = argumentCaptor.getValue(); assertThat(commandObject.getArguments(), sameInstance(commandArgumentsBlocking)); assertThat(result, equalTo("OK")); verify(commandObjects).commandArguments(cmd); } @Test public void testSendCommandWithSampleKeyProtocolCommandAndByteArrayArgs() { byte[] sampleKey = "key".getBytes(); ProtocolCommand cmd = mock(ProtocolCommand.class); byte[][] args = { "arg1".getBytes(), "arg2".getBytes() }; CommandArguments commandArguments = mock(CommandArguments.class); CommandArguments commandArgumentsWithArgs = mock(CommandArguments.class); CommandArguments commandArgumentsWithKey = mock(CommandArguments.class); when(commandArguments.addObjects((Object[]) args)).thenReturn(commandArgumentsWithArgs); when(commandArgumentsWithArgs.addHashSlotKey(sampleKey)).thenReturn(commandArgumentsWithKey); when(commandObjects.commandArguments(cmd)).thenReturn(commandArguments); when(commandExecutor.executeCommand(any())).thenReturn("OK"); Object result = jedis.sendCommand(sampleKey, cmd, args); ArgumentCaptor> argumentCaptor = ArgumentCaptor.forClass(CommandObject.class); verify(commandExecutor).executeCommand(argumentCaptor.capture()); CommandObject commandObject = argumentCaptor.getValue(); assertThat(commandObject.getArguments(), sameInstance(commandArgumentsWithKey)); assertThat(result, equalTo("OK")); verify(commandObjects).commandArguments(cmd); } @Test public void testSendBlockingCommandWithSampleKeyProtocolCommandAndByteArrayArgs() { byte[] sampleKey = "key".getBytes(); ProtocolCommand cmd = mock(ProtocolCommand.class); byte[][] args = { "arg1".getBytes(), "arg2".getBytes() }; CommandArguments commandArguments = mock(CommandArguments.class); CommandArguments commandArgumentsWithArgs = mock(CommandArguments.class); CommandArguments commandArgumentsBlocking = mock(CommandArguments.class); CommandArguments commandArgumentsWithKey = mock(CommandArguments.class); when(commandArguments.addObjects((Object[]) args)).thenReturn(commandArgumentsWithArgs); when(commandArgumentsWithArgs.blocking()).thenReturn(commandArgumentsBlocking); when(commandArgumentsBlocking.addHashSlotKey(sampleKey)).thenReturn(commandArgumentsWithKey); when(commandObjects.commandArguments(cmd)).thenReturn(commandArguments); when(commandExecutor.executeCommand(any())).thenReturn("OK"); Object result = jedis.sendBlockingCommand(sampleKey, cmd, args); ArgumentCaptor> argumentCaptor = ArgumentCaptor.forClass(CommandObject.class); verify(commandExecutor).executeCommand(argumentCaptor.capture()); CommandObject commandObject = argumentCaptor.getValue(); assertThat(commandObject.getArguments(), sameInstance(commandArgumentsWithKey)); assertThat(result, equalTo("OK")); verify(commandObjects).commandArguments(cmd); } @Test public void testSendCommandWithStringSampleKeyProtocolCommandAndStringArgs() { String sampleKey = "key"; ProtocolCommand cmd = mock(ProtocolCommand.class); String[] args = { "arg1", "arg2" }; CommandArguments commandArguments = mock(CommandArguments.class); CommandArguments commandArgumentsWithArgs = mock(CommandArguments.class); CommandArguments commandArgumentsWithKey = mock(CommandArguments.class); when(commandArguments.addObjects((Object[]) args)).thenReturn(commandArgumentsWithArgs); when(commandArgumentsWithArgs.addHashSlotKey(sampleKey)).thenReturn(commandArgumentsWithKey); when(commandObjects.commandArguments(cmd)).thenReturn(commandArguments); when(commandExecutor.executeCommand(any())).thenReturn("OK"); Object result = jedis.sendCommand(sampleKey, cmd, args); ArgumentCaptor> argumentCaptor = ArgumentCaptor.forClass(CommandObject.class); verify(commandExecutor).executeCommand(argumentCaptor.capture()); CommandObject commandObject = argumentCaptor.getValue(); assertThat(commandObject.getArguments(), sameInstance(commandArgumentsWithKey)); assertThat(result, equalTo("OK")); verify(commandObjects).commandArguments(cmd); } @Test public void testSendBlockingCommandWithStringSampleKeyProtocolCommandAndStringArgs() { String sampleKey = "key"; ProtocolCommand cmd = mock(ProtocolCommand.class); String[] args = { "arg1", "arg2" }; CommandArguments commandArguments = mock(CommandArguments.class); CommandArguments commandArgumentsWithArgs = mock(CommandArguments.class); CommandArguments commandArgumentsBlocking = mock(CommandArguments.class); CommandArguments commandArgumentsWithKey = mock(CommandArguments.class); when(commandArguments.addObjects((Object[]) args)).thenReturn(commandArgumentsWithArgs); when(commandArgumentsWithArgs.blocking()).thenReturn(commandArgumentsBlocking); when(commandArgumentsBlocking.addHashSlotKey(sampleKey)).thenReturn(commandArgumentsWithKey); when(commandObjects.commandArguments(cmd)).thenReturn(commandArguments); when(commandExecutor.executeCommand(any())).thenReturn("OK"); Object result = jedis.sendBlockingCommand(sampleKey, cmd, args); ArgumentCaptor> argumentCaptor = ArgumentCaptor.forClass(CommandObject.class); verify(commandExecutor).executeCommand(argumentCaptor.capture()); CommandObject commandObject = argumentCaptor.getValue(); assertThat(commandObject.getArguments(), sameInstance(commandArgumentsWithKey)); assertThat(result, equalTo("OK")); verify(commandObjects).commandArguments(cmd); } } ================================================ FILE: src/test/java/redis/clients/jedis/authentication/EntraIDTestContext.java ================================================ package redis.clients.jedis.authentication; import java.io.ByteArrayInputStream; import java.security.KeyFactory; import java.security.PrivateKey; import java.security.cert.CertificateFactory; import java.security.cert.X509Certificate; import java.security.spec.PKCS8EncodedKeySpec; import java.util.Arrays; import java.util.Base64; import java.util.HashSet; import java.util.Set; public class EntraIDTestContext { private static final String AZURE_CLIENT_ID = "AZURE_CLIENT_ID"; private static final String AZURE_AUTHORITY = "AZURE_AUTHORITY"; private static final String AZURE_CLIENT_SECRET = "AZURE_CLIENT_SECRET"; private static final String AZURE_PRIVATE_KEY = "AZURE_PRIVATE_KEY"; private static final String AZURE_CERT = "AZURE_CERT"; private static final String AZURE_REDIS_SCOPES = "AZURE_REDIS_SCOPES"; private static final String AZURE_USER_ASSIGNED_MANAGED_ID = "AZURE_USER_ASSIGNED_MANAGED_ID"; private String clientId; private String authority; private String clientSecret; private PrivateKey privateKey; private X509Certificate cert; private Set redisScopes; private String userAssignedManagedIdentity; public static final EntraIDTestContext DEFAULT = new EntraIDTestContext(); private EntraIDTestContext() { clientId = System.getenv(AZURE_CLIENT_ID); authority = System.getenv(AZURE_AUTHORITY); clientSecret = System.getenv(AZURE_CLIENT_SECRET); userAssignedManagedIdentity = System.getenv(AZURE_USER_ASSIGNED_MANAGED_ID); } public EntraIDTestContext(String clientId, String authority, String clientSecret, PrivateKey privateKey, X509Certificate cert, Set redisScopes, String userAssignedManagedIdentity) { this.clientId = clientId; this.authority = authority; this.clientSecret = clientSecret; this.privateKey = privateKey; this.cert = cert; this.redisScopes = redisScopes; this.userAssignedManagedIdentity = userAssignedManagedIdentity; } public String getClientId() { return clientId; } public String getAuthority() { return authority; } public String getClientSecret() { return clientSecret; } public PrivateKey getPrivateKey() { if (privateKey == null) { this.privateKey = getPrivateKey(System.getenv(AZURE_PRIVATE_KEY)); } return privateKey; } public X509Certificate getCert() { if (cert == null) { this.cert = getCert(System.getenv(AZURE_CERT)); } return cert; } public Set getRedisScopes() { if (redisScopes == null) { String redisScopesEnv = System.getenv(AZURE_REDIS_SCOPES); this.redisScopes = new HashSet<>(Arrays.asList(redisScopesEnv.split(";"))); } return redisScopes; } public String getUserAssignedManagedIdentity() { return userAssignedManagedIdentity; } private PrivateKey getPrivateKey(String privateKey) { try { // Decode the base64 encoded key into a byte array byte[] decodedKey = Base64.getDecoder().decode(privateKey); // Generate the private key from the decoded byte array using PKCS8EncodedKeySpec PKCS8EncodedKeySpec keySpec = new PKCS8EncodedKeySpec(decodedKey); KeyFactory keyFactory = KeyFactory.getInstance("RSA"); // Use the correct algorithm (e.g., "RSA", "EC", "DSA") PrivateKey key = keyFactory.generatePrivate(keySpec); return key; } catch (Exception e) { e.printStackTrace(); throw new RuntimeException(e); } } private X509Certificate getCert(String cert) { try { // Convert the Base64 encoded string into a byte array byte[] encoded = java.util.Base64.getDecoder().decode(cert); // Create a CertificateFactory for X.509 certificates CertificateFactory certificateFactory = CertificateFactory.getInstance("X.509"); // Generate the certificate from the byte array X509Certificate certificate = (X509Certificate) certificateFactory .generateCertificate(new ByteArrayInputStream(encoded)); return certificate; } catch (Exception e) { e.printStackTrace(); throw new RuntimeException(e); } } } ================================================ FILE: src/test/java/redis/clients/jedis/authentication/RedisEntraIDClusterIntegrationTests.java ================================================ package redis.clients.jedis.authentication; import static org.hamcrest.Matchers.greaterThanOrEqualTo; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assumptions.assumeTrue; import static org.mockito.ArgumentMatchers.any; import static org.mockito.Mockito.atLeast; import static org.mockito.Mockito.doAnswer; import static org.mockito.Mockito.spy; import static org.mockito.Mockito.verify; import static org.awaitility.Awaitility.await; import static org.awaitility.Durations.*; import java.util.Collections; import java.util.List; import java.util.concurrent.CopyOnWriteArrayList; import java.util.concurrent.CountDownLatch; import java.util.concurrent.ExecutionException; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.Future; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.Test; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import redis.clients.authentication.core.TokenAuthConfig; import redis.clients.authentication.entraid.EntraIDTokenAuthConfigBuilder; import redis.clients.jedis.Connection; import redis.clients.jedis.ConnectionPoolConfig; import redis.clients.jedis.DefaultJedisClientConfig; import redis.clients.jedis.EndpointConfig; import redis.clients.jedis.HostAndPort; import redis.clients.jedis.Endpoints; import redis.clients.jedis.JedisClientConfig; import redis.clients.jedis.RedisClusterClient; public class RedisEntraIDClusterIntegrationTests { private static final Logger log = LoggerFactory .getLogger(RedisEntraIDClusterIntegrationTests.class); private static EntraIDTestContext testCtx; private static EndpointConfig endpointConfig; private static HostAndPort hnp; @BeforeAll public static void before() { try { testCtx = EntraIDTestContext.DEFAULT; endpointConfig = Endpoints.getRedisEndpoint("cluster-entraid-acl"); hnp = endpointConfig.getHostAndPort(); } catch (IllegalArgumentException e) { log.warn("Skipping test because no Redis endpoint is configured"); assumeTrue(false, "No Redis endpoint 'standalone-entraid-acl' is configured!"); } } @Test public void testClusterInitWithAuthXManager() { TokenAuthConfig tokenAuthConfig = EntraIDTokenAuthConfigBuilder.builder() .lowerRefreshBoundMillis(1000).clientId(testCtx.getClientId()) .secret(testCtx.getClientSecret()).authority(testCtx.getAuthority()) .scopes(testCtx.getRedisScopes()).build(); int defaultDirections = 5; JedisClientConfig config = DefaultJedisClientConfig.builder() .authXManager(new AuthXManager(tokenAuthConfig)).build(); ConnectionPoolConfig DEFAULT_POOL_CONFIG = new ConnectionPoolConfig(); try (RedisClusterClient jc = RedisClusterClient.builder() .nodes(Collections.singleton(hnp)) .clientConfig(config) .maxAttempts(defaultDirections) .poolConfig(DEFAULT_POOL_CONFIG) .build()) { assertEquals("OK", jc.set("foo", "bar")); assertEquals("bar", jc.get("foo")); assertEquals(1, jc.del("foo")); } } @Test public void testClusterWithReAuth() throws InterruptedException, ExecutionException { TokenAuthConfig tokenAuthConfig = EntraIDTokenAuthConfigBuilder.builder() // 0.00002F is to make it fit into 2 seconds, we need at least 2 attempt in 2 seconds // to trigger re-authentication. // For expiration time between 30 minutes to 12 hours // token renew will happen in from 36ms up to 864ms // If the received token has more than 12 hours to expire, this test will probably fail, and need to be adjusted. .expirationRefreshRatio(0.00002F).clientId(testCtx.getClientId()) .secret(testCtx.getClientSecret()).authority(testCtx.getAuthority()) .scopes(testCtx.getRedisScopes()).build(); AuthXManager authXManager = new AuthXManager(tokenAuthConfig); authXManager = spy(authXManager); List connections = new CopyOnWriteArrayList<>(); doAnswer(invocation -> { Connection connection = spy((Connection) invocation.getArgument(0)); invocation.getArguments()[0] = connection; connections.add(connection); return invocation.callRealMethod(); }).when(authXManager).addConnection(any(Connection.class)); JedisClientConfig config = DefaultJedisClientConfig.builder().authXManager(authXManager) .build(); ExecutorService executorService = Executors.newFixedThreadPool(2); CountDownLatch latch = new CountDownLatch(1); try (RedisClusterClient jc = RedisClusterClient.builder() .nodes(Collections.singleton(hnp)) .clientConfig(config) .build()) { Runnable task = () -> { while (latch.getCount() > 0) { assertEquals("OK", jc.set("foo", "bar")); } }; Future task1 = executorService.submit(task); Future task2 = executorService.submit(task); await().pollInterval(ONE_HUNDRED_MILLISECONDS).atMost(TWO_SECONDS) .until(connections::size, greaterThanOrEqualTo(2)); connections.forEach(conn -> { await().pollInterval(ONE_HUNDRED_MILLISECONDS).atMost(TWO_SECONDS) .untilAsserted(() -> verify(conn, atLeast(2)).reAuthenticate()); }); latch.countDown(); task1.get(); task2.get(); } finally { latch.countDown(); executorService.shutdown(); } } } ================================================ FILE: src/test/java/redis/clients/jedis/authentication/RedisEntraIDIntegrationTests.java ================================================ package redis.clients.jedis.authentication; import static org.awaitility.Awaitility.await; import static org.awaitility.Durations.TWO_SECONDS; import static org.awaitility.Durations.FIVE_SECONDS; import static org.awaitility.Durations.ONE_HUNDRED_MILLISECONDS; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertNotNull; import static org.junit.jupiter.api.Assertions.assertThrows; import static org.junit.jupiter.api.Assertions.assertTrue; import static org.junit.jupiter.api.Assertions.fail; import static org.junit.jupiter.api.Assumptions.assumeTrue; import static org.mockito.ArgumentMatchers.any; import static org.mockito.Mockito.atLeast; import static org.mockito.Mockito.doAnswer; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.mockConstruction; import static org.mockito.Mockito.spy; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; import static org.hamcrest.Matchers.in; import static org.hamcrest.Matchers.is; import java.io.IOException; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.UUID; import java.util.concurrent.ExecutionException; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.Future; import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.atomic.AtomicInteger; import java.util.concurrent.atomic.AtomicReference; import java.util.function.Consumer; import org.awaitility.Awaitility; import org.awaitility.Durations; import org.hamcrest.MatcherAssert; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.MethodOrderer; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.TestMethodOrder; import org.mockito.MockedConstruction; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import com.azure.identity.DefaultAzureCredential; import com.azure.identity.DefaultAzureCredentialBuilder; import redis.clients.authentication.core.IdentityProvider; import redis.clients.authentication.core.IdentityProviderConfig; import redis.clients.authentication.core.SimpleToken; import redis.clients.authentication.core.Token; import redis.clients.authentication.core.TokenAuthConfig; import redis.clients.authentication.entraid.AzureTokenAuthConfigBuilder; import redis.clients.authentication.entraid.EntraIDIdentityProvider; import redis.clients.authentication.entraid.EntraIDIdentityProviderConfig; import redis.clients.authentication.entraid.EntraIDTokenAuthConfigBuilder; import redis.clients.authentication.entraid.ServicePrincipalInfo; import redis.clients.jedis.Connection; import redis.clients.jedis.DefaultJedisClientConfig; import redis.clients.jedis.EndpointConfig; import redis.clients.jedis.HostAndPort; import redis.clients.jedis.Endpoints; import redis.clients.jedis.RedisClient; import redis.clients.jedis.exceptions.JedisAccessControlException; import redis.clients.jedis.exceptions.JedisConnectionException; import redis.clients.jedis.scenario.FaultInjectionClient; import redis.clients.jedis.scenario.FaultInjectionClient.TriggerActionResponse; @TestMethodOrder(MethodOrderer.MethodName.class) public class RedisEntraIDIntegrationTests { private static final Logger log = LoggerFactory.getLogger(RedisEntraIDIntegrationTests.class); private static EntraIDTestContext testCtx; private static EndpointConfig endpointConfig; private static HostAndPort hnp; private final FaultInjectionClient faultClient = new FaultInjectionClient(); @BeforeAll public static void before() { try { testCtx = EntraIDTestContext.DEFAULT; endpointConfig = Endpoints.getRedisEndpoint("standalone-entraid-acl"); hnp = endpointConfig.getHostAndPort(); } catch (IllegalArgumentException e) { log.warn("Skipping test because no Redis endpoint is configured"); assumeTrue(false, "No Redis endpoint 'standalone-entraid-acl' is configured!"); } } @Test public void testJedisConfig() { AtomicInteger counter = new AtomicInteger(0); try (MockedConstruction mockedConstructor = mockConstruction( EntraIDIdentityProvider.class, (mock, context) -> { ServicePrincipalInfo info = (ServicePrincipalInfo) context.arguments().get(0); assertEquals(testCtx.getClientId(), info.getClientId()); assertEquals(testCtx.getAuthority(), info.getAuthority()); assertEquals(testCtx.getClientSecret(), info.getSecret()); assertEquals(testCtx.getRedisScopes(), context.arguments().get(1)); assertNotNull(mock); doAnswer(invocation -> { counter.incrementAndGet(); return new SimpleToken("default", "token1", System.currentTimeMillis() + 5 * 60 * 1000, System.currentTimeMillis(), null); }).when(mock).requestToken(); })) { TokenAuthConfig tokenAuthConfig = EntraIDTokenAuthConfigBuilder.builder() .authority(testCtx.getAuthority()).clientId(testCtx.getClientId()) .secret(testCtx.getClientSecret()).scopes(testCtx.getRedisScopes()).build(); DefaultJedisClientConfig jedisConfig = DefaultJedisClientConfig.builder() .authXManager(new AuthXManager(tokenAuthConfig)).build(); RedisClient jedis = RedisClient.builder() .hostAndPort(new HostAndPort("localhost", 6379)) .clientConfig(jedisConfig) .build(); assertNotNull(jedis); assertEquals(1, counter.get()); } } // T.1.1 // Verify authentication using Azure AD with service principals @Test public void withSecret_azureServicePrincipalIntegrationTest() { TokenAuthConfig tokenAuthConfig = EntraIDTokenAuthConfigBuilder.builder() .clientId(testCtx.getClientId()).secret(testCtx.getClientSecret()) .authority(testCtx.getAuthority()).scopes(testCtx.getRedisScopes()).build(); DefaultJedisClientConfig jedisConfig = DefaultJedisClientConfig.builder() .authXManager(new AuthXManager(tokenAuthConfig)).build(); try (RedisClient jedis = RedisClient.builder() .hostAndPort(hnp) .clientConfig(jedisConfig) .build()) { String key = UUID.randomUUID().toString(); jedis.set(key, "value"); assertEquals("value", jedis.get(key)); jedis.del(key); } } // T.1.1 // Verify authentication using Azure AD with service principals @Test public void withCertificate_azureServicePrincipalIntegrationTest() { TokenAuthConfig tokenAuthConfig = EntraIDTokenAuthConfigBuilder.builder() .clientId(testCtx.getClientId()).secret(testCtx.getClientSecret()) .authority(testCtx.getAuthority()).scopes(testCtx.getRedisScopes()).build(); DefaultJedisClientConfig jedisConfig = DefaultJedisClientConfig.builder() .authXManager(new AuthXManager(tokenAuthConfig)).build(); try (RedisClient jedis = RedisClient.builder() .hostAndPort(hnp) .clientConfig(jedisConfig) .build()) { String key = UUID.randomUUID().toString(); jedis.set(key, "value"); assertEquals("value", jedis.get(key)); jedis.del(key); } } // T.2.2 // Test that the Redis client is not blocked/interrupted during token renewal. @Test public void renewalDuringOperationsTest() throws InterruptedException, ExecutionException { // set the stage with consecutive get/set operations with unique keys which keeps running with a jedispooled instace, // configure token manager to renew token approximately approximately every 10ms // wait till token was renewed at least 10 times after initial token acquisition // Additional note: Assumptions made on the time taken for token renewal and operations are based on the current implementation and may vary in future // Assumptions: // - TTL of token is 2 hour // - expirationRefreshRatio is 0.000001F // - renewal delay is 7 ms each time a token is acquired // - each auth command takes 40 ms in total to complete(considering the cloud test environments) // - each auth command would need to wait for an ongoing customer operation(GET/SET/DEL) to complete, which would take another 40 ms // - each renewal happens in 40+40+7 = 87 ms // - total number of renewals would take 87 * 10 = 870 ms TokenAuthConfig tokenAuthConfig = EntraIDTokenAuthConfigBuilder.builder() .clientId(testCtx.getClientId()).secret(testCtx.getClientSecret()) .authority(testCtx.getAuthority()).scopes(testCtx.getRedisScopes()) .expirationRefreshRatio(0.000001F).build(); AuthXManager authXManager = new AuthXManager(tokenAuthConfig); Consumer hook = mock(Consumer.class); authXManager.addPostAuthenticationHook(hook); DefaultJedisClientConfig jedisClientConfig = DefaultJedisClientConfig.builder() .authXManager(authXManager).build(); ExecutorService jedisExecutors = Executors.newFixedThreadPool(5); AtomicBoolean completed = new AtomicBoolean(false); ExecutorService runner = Executors.newSingleThreadExecutor(); runner.submit(() -> { try (RedisClient jedis = RedisClient.builder() .hostAndPort(hnp) .clientConfig(jedisClientConfig) .build()) { List> futures = new ArrayList<>(); for (int i = 0; i < 5; i++) { Future future = jedisExecutors.submit(() -> { while (!completed.get()) { String key = UUID.randomUUID().toString(); jedis.set(key, "value"); assertEquals("value", jedis.get(key)); jedis.del(key); } }); futures.add(future); } for (Future task : futures) { try { task.get(); } catch (InterruptedException | ExecutionException e) { e.printStackTrace(); } } } }); await().pollInterval(ONE_HUNDRED_MILLISECONDS).atMost(FIVE_SECONDS).untilAsserted(() -> { verify(hook, atLeast(10)).accept(any()); }); completed.set(true); runner.shutdown(); jedisExecutors.shutdown(); } // T.3.2 // Verify that all existing connections can be re-authenticated when a new token is received. @Test public void allConnectionsReauthTest() throws InterruptedException, ExecutionException { TokenAuthConfig tokenAuthConfig = EntraIDTokenAuthConfigBuilder.builder() .clientId(testCtx.getClientId()).secret(testCtx.getClientSecret()) .authority(testCtx.getAuthority()).scopes(testCtx.getRedisScopes()) .expirationRefreshRatio(0.000001F).build(); AuthXManager authXManager = new AuthXManager(tokenAuthConfig); authXManager = spy(authXManager); List connections = new ArrayList<>(); doAnswer(invocation -> { Connection connection = spy((Connection) invocation.getArgument(0)); invocation.getArguments()[0] = connection; connections.add(connection); Object result = invocation.callRealMethod(); return result; }).when(authXManager).addConnection(any(Connection.class)); DefaultJedisClientConfig jedisClientConfig = DefaultJedisClientConfig.builder() .authXManager(authXManager).build(); long startTime = System.currentTimeMillis(); List> futures = new ArrayList<>(); ExecutorService executor = Executors.newFixedThreadPool(5); try (RedisClient jedis = RedisClient.builder() .hostAndPort(hnp) .clientConfig(jedisClientConfig) .build()) { for (int i = 0; i < 5; i++) { Future future = executor.submit(() -> { for (; System.currentTimeMillis() - startTime < 2000;) { String key = UUID.randomUUID().toString(); jedis.set(key, "value"); assertEquals("value", jedis.get(key)); jedis.del(key); } }); futures.add(future); } for (Future task : futures) { task.get(); } connections.forEach(conn -> { verify(conn, atLeast(1)).reAuthenticate(); }); executor.shutdown(); } } // T.3.3 // Verify behavior when attempting to authenticate a single connection with an expired token. @Test public void connectionAuthWithExpiredTokenTest() { IdentityProvider idp = new EntraIDIdentityProviderConfig( new ServicePrincipalInfo(testCtx.getClientId(), testCtx.getClientSecret(), testCtx.getAuthority()), testCtx.getRedisScopes(), 1000).getProvider(); IdentityProvider mockIdentityProvider = mock(IdentityProvider.class); AtomicReference token = new AtomicReference<>(); doAnswer(invocation -> { if (token.get() == null) { token.set(idp.requestToken()); } return token.get(); }).when(mockIdentityProvider).requestToken(); IdentityProviderConfig idpConfig = mock(IdentityProviderConfig.class); when(idpConfig.getProvider()).thenReturn(mockIdentityProvider); TokenAuthConfig tokenAuthConfig = TokenAuthConfig.builder().tokenRequestExecTimeoutInMs(4000) .identityProviderConfig(idpConfig).expirationRefreshRatio(0.000001F).build(); AuthXManager authXManager = new AuthXManager(tokenAuthConfig); DefaultJedisClientConfig jedisClientConfig = DefaultJedisClientConfig.builder() .authXManager(authXManager).build(); try (RedisClient jedis = RedisClient.builder() .hostAndPort(hnp) .clientConfig(jedisClientConfig) .build()) { for (int i = 0; i < 50; i++) { String key = UUID.randomUUID().toString(); jedis.set(key, "value"); assertEquals("value", jedis.get(key)); jedis.del(key); } token.set(new SimpleToken(idp.requestToken().getUser(), "token1", System.currentTimeMillis() - 1, System.currentTimeMillis(), null)); JedisAccessControlException aclException = assertThrows(JedisAccessControlException.class, () -> { for (int i = 0; i < 50; i++) { String key = UUID.randomUUID().toString(); jedis.set(key, "value"); assertEquals("value", jedis.get(key)); jedis.del(key); } }); String expectedError = "WRONGPASS invalid username-password pair"; assertTrue(aclException.getMessage().startsWith(expectedError), "Expected '" + aclException.getMessage() + "' to start with '" + expectedError + "'"); } } // T.3.4 // Verify handling of reconnection and re-authentication after a network partition. (use cached token) @Test public void networkPartitionEvictionTest() { TokenAuthConfig tokenAuthConfig = EntraIDTokenAuthConfigBuilder.builder() .clientId(testCtx.getClientId()).secret(testCtx.getClientSecret()) .authority(testCtx.getAuthority()).scopes(testCtx.getRedisScopes()) .expirationRefreshRatio(0.5F).build(); AuthXManager authXManager = new AuthXManager(tokenAuthConfig); DefaultJedisClientConfig jedisClientConfig = DefaultJedisClientConfig.builder() .authXManager(authXManager).build(); try (RedisClient jedis = RedisClient.builder() .hostAndPort(hnp) .clientConfig(jedisClientConfig) .build()) { for (int i = 0; i < 5; i++) { String key = UUID.randomUUID().toString(); jedis.set(key, "value"); assertEquals("value", jedis.get(key)); jedis.del(key); } TriggerActionResponse actionResponse = triggerNetworkFailure(); JedisConnectionException aclException = assertThrows(JedisConnectionException.class, () -> { while (!actionResponse.isCompleted(ONE_HUNDRED_MILLISECONDS, TWO_SECONDS, FIVE_SECONDS)) { for (int i = 0; i < 50; i++) { String key = UUID.randomUUID().toString(); jedis.set(key, "value"); assertEquals("value", jedis.get(key)); jedis.del(key); } } }); String[] expectedMessages = new String[] { "Unexpected end of stream.", "java.net.SocketException: Connection reset" }; MatcherAssert.assertThat(aclException.getMessage(), is(in(expectedMessages))); Awaitility.await().pollDelay(Durations.ONE_HUNDRED_MILLISECONDS).atMost(Durations.TWO_SECONDS) .until(() -> { try { String key = UUID.randomUUID().toString(); jedis.set(key, "value"); assertEquals("value", jedis.get(key)); jedis.del(key); return true; } catch (Exception e) { log.debug("attempt to reconnect after network failure, connection has not been re-established yet:" + e.getMessage()); return false; } }); } } private TriggerActionResponse triggerNetworkFailure() { HashMap params = new HashMap<>(); params.put("bdb_id", endpointConfig.getBdbId()); TriggerActionResponse actionResponse = null; String action = "network_failure"; try { log.info("Triggering {}", action); actionResponse = faultClient.triggerAction(action, params); } catch (IOException e) { fail("Fault Injection Server error:" + e.getMessage()); } log.info("Action id: {}", actionResponse.getActionId()); return actionResponse; } @Test public void withDefaultCredentials_azureCredentialsIntegrationTest() { DefaultAzureCredential credential = new DefaultAzureCredentialBuilder().build(); TokenAuthConfig tokenAuthConfig = AzureTokenAuthConfigBuilder.builder() .defaultAzureCredential(credential).tokenRequestExecTimeoutInMs(2000) .build(); DefaultJedisClientConfig jedisConfig = DefaultJedisClientConfig.builder() .authXManager(new AuthXManager(tokenAuthConfig)).build(); try (RedisClient jedis = RedisClient.builder() .hostAndPort(hnp) .clientConfig(jedisConfig) .build()) { String key = UUID.randomUUID().toString(); jedis.set(key, "value"); assertEquals("value", jedis.get(key)); jedis.del(key); } } } ================================================ FILE: src/test/java/redis/clients/jedis/authentication/RedisEntraIDManagedIdentityIntegrationTests.java ================================================ package redis.clients.jedis.authentication; import java.util.Collections; import java.util.Set; import java.util.UUID; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.Test; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import redis.clients.authentication.core.TokenAuthConfig; import redis.clients.authentication.entraid.EntraIDTokenAuthConfigBuilder; import redis.clients.authentication.entraid.ManagedIdentityInfo.UserManagedIdentityType; import redis.clients.jedis.DefaultJedisClientConfig; import redis.clients.jedis.EndpointConfig; import redis.clients.jedis.HostAndPort; import redis.clients.jedis.Endpoints; import redis.clients.jedis.RedisClient; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assumptions.assumeTrue; public class RedisEntraIDManagedIdentityIntegrationTests { private static final Logger log = LoggerFactory.getLogger(RedisEntraIDIntegrationTests.class); private static EntraIDTestContext testCtx; private static EndpointConfig endpointConfig; private static HostAndPort hnp; private static Set managedIdentityAudience = Collections .singleton("https://redis.azure.com"); @BeforeAll public static void before() { try { testCtx = EntraIDTestContext.DEFAULT; endpointConfig = Endpoints.getRedisEndpoint("standalone-entraid-acl"); hnp = endpointConfig.getHostAndPort(); } catch (IllegalArgumentException e) { log.warn("Skipping test because no Redis endpoint is configured"); assumeTrue(false,"No Redis endpoint 'standalone-entraid-acl' is configured!"); } } // T.1.1 // Verify authentication using Azure AD with managed identities @Test public void withUserAssignedId_azureManagedIdentityIntegrationTest() { TokenAuthConfig tokenAuthConfig = EntraIDTokenAuthConfigBuilder.builder() .userAssignedManagedIdentity(UserManagedIdentityType.OBJECT_ID, testCtx.getUserAssignedManagedIdentity()) .scopes(managedIdentityAudience).build(); DefaultJedisClientConfig jedisConfig = DefaultJedisClientConfig.builder() .authXManager(new AuthXManager(tokenAuthConfig)).build(); try (RedisClient jedis = RedisClient.builder() .hostAndPort(hnp) .clientConfig(jedisConfig) .build()) { String key = UUID.randomUUID().toString(); jedis.set(key, "value"); assertEquals("value", jedis.get(key)); jedis.del(key); } } // T.1.1 // Verify authentication using Azure AD with managed identities @Test public void withSystemAssignedId_azureManagedIdentityIntegrationTest() { TokenAuthConfig tokenAuthConfig = EntraIDTokenAuthConfigBuilder.builder() .systemAssignedManagedIdentity().scopes(managedIdentityAudience).build(); DefaultJedisClientConfig jedisConfig = DefaultJedisClientConfig.builder() .authXManager(new AuthXManager(tokenAuthConfig)).build(); try (RedisClient jedis = RedisClient.builder() .hostAndPort(hnp) .clientConfig(jedisConfig) .build()) { String key = UUID.randomUUID().toString(); jedis.set(key, "value"); assertEquals("value", jedis.get(key)); jedis.del(key); } } } ================================================ FILE: src/test/java/redis/clients/jedis/authentication/TokenBasedAuthenticationClusterIntegrationTests.java ================================================ package redis.clients.jedis.authentication; import static org.hamcrest.Matchers.greaterThanOrEqualTo; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assumptions.assumeTrue; import static org.mockito.ArgumentMatchers.any; import static org.mockito.Mockito.atLeast; import static org.mockito.Mockito.doAnswer; import static org.mockito.Mockito.spy; import static org.mockito.Mockito.verify; import static org.awaitility.Awaitility.await; import static org.awaitility.Durations.*; import java.util.Collections; import java.util.List; import java.util.concurrent.CopyOnWriteArrayList; import java.util.concurrent.CountDownLatch; import java.util.concurrent.ExecutionException; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.Future; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.Test; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import redis.clients.authentication.core.IdentityProvider; import redis.clients.authentication.core.IdentityProviderConfig; import redis.clients.authentication.core.SimpleToken; import redis.clients.authentication.core.Token; import redis.clients.authentication.core.TokenAuthConfig; import redis.clients.jedis.Connection; import redis.clients.jedis.ConnectionPoolConfig; import redis.clients.jedis.DefaultJedisClientConfig; import redis.clients.jedis.EndpointConfig; import redis.clients.jedis.HostAndPort; import redis.clients.jedis.Endpoints; import redis.clients.jedis.JedisClientConfig; import redis.clients.jedis.RedisClusterClient; public class TokenBasedAuthenticationClusterIntegrationTests { private static final Logger log = LoggerFactory .getLogger(TokenBasedAuthenticationClusterIntegrationTests.class); private static EndpointConfig endpointConfig; private static HostAndPort hnp; @BeforeAll public static void before() { try { endpointConfig = Endpoints.getRedisEndpoint("cluster"); hnp = endpointConfig.getHostAndPort(); } catch (IllegalArgumentException e) { log.warn("Skipping test because no Redis endpoint is configured"); assumeTrue(false, "No Redis endpoint 'cluster' is configured!"); } } @Test public void testClusterInitWithAuthXManager() { IdentityProviderConfig idpConfig = new IdentityProviderConfig() { @Override public IdentityProvider getProvider() { return new IdentityProvider() { @Override public Token requestToken() { return new SimpleToken(endpointConfig.getUsername(), endpointConfig.getPassword() == null ? "" : endpointConfig.getPassword(), System.currentTimeMillis() + 5 * 1000, System.currentTimeMillis(), null); } }; } }; AuthXManager manager = new AuthXManager(TokenAuthConfig.builder() .lowerRefreshBoundMillis(1000).tokenRequestExecTimeoutInMs(3000) .identityProviderConfig(idpConfig).build()); int defaultDirections = 5; JedisClientConfig config = DefaultJedisClientConfig.builder().authXManager(manager).build(); ConnectionPoolConfig DEFAULT_POOL_CONFIG = new ConnectionPoolConfig(); try (RedisClusterClient jc = RedisClusterClient.builder() .nodes(Collections.singleton(hnp)) .clientConfig(config) .maxAttempts(defaultDirections) .poolConfig(DEFAULT_POOL_CONFIG) .build()) { assertEquals("OK", jc.set("foo", "bar")); assertEquals("bar", jc.get("foo")); assertEquals(1, jc.del("foo")); } } @Test public void testClusterWithReAuth() throws InterruptedException, ExecutionException { IdentityProviderConfig idpConfig = new IdentityProviderConfig() { @Override public IdentityProvider getProvider() { return new IdentityProvider() { @Override public Token requestToken() { return new SimpleToken(endpointConfig.getUsername(), endpointConfig.getPassword() == null ? "" : endpointConfig.getPassword(), System.currentTimeMillis() + 5 * 1000, System.currentTimeMillis(), null); } }; } }; AuthXManager authXManager = new AuthXManager(TokenAuthConfig.builder() .lowerRefreshBoundMillis(4600).tokenRequestExecTimeoutInMs(3000) .identityProviderConfig(idpConfig).build()); authXManager = spy(authXManager); List connections = new CopyOnWriteArrayList<>(); doAnswer(invocation -> { Connection connection = spy((Connection) invocation.getArgument(0)); invocation.getArguments()[0] = connection; connections.add(connection); return invocation.callRealMethod(); }).when(authXManager).addConnection(any(Connection.class)); JedisClientConfig config = DefaultJedisClientConfig.builder().authXManager(authXManager) .build(); ExecutorService executorService = Executors.newFixedThreadPool(2); CountDownLatch latch = new CountDownLatch(1); try (RedisClusterClient jc = RedisClusterClient.builder() .nodes(Collections.singleton(hnp)) .clientConfig(config) .build()) { Runnable task = () -> { while (latch.getCount() > 0) { assertEquals("OK", jc.set("foo", "bar")); } }; Future task1 = executorService.submit(task); Future task2 = executorService.submit(task); await().pollInterval(ONE_HUNDRED_MILLISECONDS).atMost(TWO_SECONDS) .until(connections::size, greaterThanOrEqualTo(2)); connections.forEach(conn -> { await().pollInterval(ONE_HUNDRED_MILLISECONDS).atMost(TWO_SECONDS) .untilAsserted(() -> verify(conn, atLeast(2)).reAuthenticate()); }); latch.countDown(); task1.get(); task2.get(); } finally { latch.countDown(); executorService.shutdown(); } } } ================================================ FILE: src/test/java/redis/clients/jedis/authentication/TokenBasedAuthenticationIntegrationTests.java ================================================ package redis.clients.jedis.authentication; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertThrows; import static org.junit.jupiter.api.Assumptions.assumeTrue; import static org.mockito.Mockito.when; import static org.mockito.ArgumentMatchers.any; import static org.mockito.Mockito.atLeast; import static org.mockito.Mockito.doAnswer; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.spy; import static org.mockito.Mockito.verify; import static org.awaitility.Awaitility.await; import static org.awaitility.Durations.ONE_HUNDRED_MILLISECONDS; import static org.awaitility.Durations.ONE_SECOND; import static org.hamcrest.Matchers.greaterThan; import static org.hamcrest.MatcherAssert.assertThat; import java.util.ArrayList; import java.util.List; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.atomic.AtomicBoolean; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.Test; import org.mockito.ArgumentCaptor; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import redis.clients.authentication.core.IdentityProvider; import redis.clients.authentication.core.IdentityProviderConfig; import redis.clients.authentication.core.SimpleToken; import redis.clients.authentication.core.TokenAuthConfig; import redis.clients.jedis.CommandArguments; import redis.clients.jedis.Connection; /* */ import redis.clients.jedis.DefaultJedisClientConfig; import redis.clients.jedis.EndpointConfig; import redis.clients.jedis.Endpoints; import redis.clients.jedis.JedisClientConfig; import redis.clients.jedis.JedisPubSub; import redis.clients.jedis.RedisClient; import redis.clients.jedis.RedisProtocol; import redis.clients.jedis.Protocol.Command; import redis.clients.jedis.exceptions.JedisException; public class TokenBasedAuthenticationIntegrationTests { private static final Logger log = LoggerFactory .getLogger(TokenBasedAuthenticationIntegrationTests.class); private static EndpointConfig endpointConfig; @BeforeAll public static void before() { try { endpointConfig = Endpoints.getRedisEndpoint("standalone0"); } catch (IllegalArgumentException e) { log.warn("Skipping test because no Redis endpoint is configured"); assumeTrue(false); } } @Test public void testClientForInitialAuth() { String user = "default"; String password = endpointConfig.getPassword(); IdentityProvider idProvider = mock(IdentityProvider.class); when(idProvider.requestToken()).thenReturn(new SimpleToken(user, password, System.currentTimeMillis() + 100000, System.currentTimeMillis(), null)); IdentityProviderConfig idProviderConfig = mock(IdentityProviderConfig.class); when(idProviderConfig.getProvider()).thenReturn(idProvider); TokenAuthConfig tokenAuthConfig = TokenAuthConfig.builder() .identityProviderConfig(idProviderConfig).expirationRefreshRatio(0.8F) .lowerRefreshBoundMillis(10000).tokenRequestExecTimeoutInMs(1000).build(); JedisClientConfig clientConfig = DefaultJedisClientConfig.builder() .authXManager(new AuthXManager(tokenAuthConfig)).build(); try (RedisClient jedis = RedisClient.builder() .hostAndPort(endpointConfig.getHostAndPort()) .clientConfig(clientConfig) .build()) { jedis.get("key1"); } } @Test public void testClientReauth() { String user = "default"; String password = endpointConfig.getPassword(); IdentityProvider idProvider = mock(IdentityProvider.class); when(idProvider.requestToken()).thenAnswer(invocation -> new SimpleToken(user, password, System.currentTimeMillis() + 5000, System.currentTimeMillis(), null)); IdentityProviderConfig idProviderConfig = mock(IdentityProviderConfig.class); when(idProviderConfig.getProvider()).thenReturn(idProvider); TokenAuthConfig tokenAuthConfig = TokenAuthConfig.builder() .identityProviderConfig(idProviderConfig).expirationRefreshRatio(0.8F) .lowerRefreshBoundMillis(4800).tokenRequestExecTimeoutInMs(1000).build(); AuthXManager authXManager = new AuthXManager(tokenAuthConfig); authXManager = spy(authXManager); List connections = new ArrayList<>(); doAnswer(invocation -> { Connection connection = spy((Connection) invocation.getArgument(0)); invocation.getArguments()[0] = connection; connections.add(connection); Object result = invocation.callRealMethod(); return result; }).when(authXManager).addConnection(any(Connection.class)); JedisClientConfig clientConfig = DefaultJedisClientConfig.builder().authXManager(authXManager) .build(); try (RedisClient jedis = RedisClient.builder() .hostAndPort(endpointConfig.getHostAndPort()) .clientConfig(clientConfig) .build()) { AtomicBoolean stop = new AtomicBoolean(false); ExecutorService executor = Executors.newSingleThreadExecutor(); executor.submit(() -> { while (!stop.get()) { jedis.get("key1"); } }); for (Connection connection : connections) { await().pollDelay(ONE_HUNDRED_MILLISECONDS).atMost(ONE_SECOND).untilAsserted(() -> { verify(connection, atLeast(3)).reAuthenticate(); }); } stop.set(true); executor.shutdown(); } } @Test public void testPubSubForInitialAuth() throws InterruptedException { String user = "default"; String password = endpointConfig.getPassword(); IdentityProvider idProvider = mock(IdentityProvider.class); when(idProvider.requestToken()).thenReturn(new SimpleToken(user, password, System.currentTimeMillis() + 100000, System.currentTimeMillis(), null)); IdentityProviderConfig idProviderConfig = mock(IdentityProviderConfig.class); when(idProviderConfig.getProvider()).thenReturn(idProvider); TokenAuthConfig tokenAuthConfig = TokenAuthConfig.builder() .identityProviderConfig(idProviderConfig).expirationRefreshRatio(0.8F) .lowerRefreshBoundMillis(10000).tokenRequestExecTimeoutInMs(1000).build(); JedisClientConfig clientConfig = DefaultJedisClientConfig.builder() .authXManager(new AuthXManager(tokenAuthConfig)).protocol(RedisProtocol.RESP3).build(); JedisPubSub pubSub = new JedisPubSub() { public void onSubscribe(String channel, int subscribedChannels) { this.unsubscribe(); } }; try (RedisClient jedis = RedisClient.builder() .hostAndPort(endpointConfig.getHostAndPort()) .clientConfig(clientConfig) .build()) { jedis.subscribe(pubSub, "channel1"); } } @Test public void testJedisPubSubReauth() { String user = "default"; String password = endpointConfig.getPassword(); IdentityProvider idProvider = mock(IdentityProvider.class); when(idProvider.requestToken()).thenAnswer(invocation -> new SimpleToken(user, password, System.currentTimeMillis() + 5000, System.currentTimeMillis(), null)); IdentityProviderConfig idProviderConfig = mock(IdentityProviderConfig.class); when(idProviderConfig.getProvider()).thenReturn(idProvider); TokenAuthConfig tokenAuthConfig = TokenAuthConfig.builder() .identityProviderConfig(idProviderConfig).expirationRefreshRatio(0.8F) .lowerRefreshBoundMillis(4800).tokenRequestExecTimeoutInMs(1000).build(); AuthXManager authXManager = new AuthXManager(tokenAuthConfig); authXManager = spy(authXManager); List connections = new ArrayList<>(); doAnswer(invocation -> { Connection connection = spy((Connection) invocation.getArgument(0)); invocation.getArguments()[0] = connection; connections.add(connection); Object result = invocation.callRealMethod(); return result; }).when(authXManager).addConnection(any(Connection.class)); JedisClientConfig clientConfig = DefaultJedisClientConfig.builder().authXManager(authXManager) .protocol(RedisProtocol.RESP3).build(); JedisPubSub pubSub = new JedisPubSub() { }; try (RedisClient jedis = RedisClient.builder() .hostAndPort(endpointConfig.getHostAndPort()) .clientConfig(clientConfig) .build()) { ExecutorService executor = Executors.newSingleThreadExecutor(); executor.submit(() -> { jedis.subscribe(pubSub, "channel1"); }); await().pollDelay(ONE_HUNDRED_MILLISECONDS).atMost(ONE_SECOND) .until(pubSub::getSubscribedChannels, greaterThan(0)); assertEquals(1, connections.size()); for (Connection connection : connections) { await().pollDelay(ONE_HUNDRED_MILLISECONDS).atMost(ONE_SECOND).untilAsserted(() -> { ArgumentCaptor captor = ArgumentCaptor.forClass(CommandArguments.class); verify(connection, atLeast(3)).sendCommand(captor.capture()); assertThat(captor.getAllValues().stream() .filter((item) -> item.getCommand() == Command.AUTH).count(), greaterThan(3L)); }); } pubSub.unsubscribe(); executor.shutdown(); } } @Test public void testJedisPubSubWithResp2() { String user = "default"; String password = endpointConfig.getPassword(); IdentityProvider idProvider = mock(IdentityProvider.class); when(idProvider.requestToken()).thenReturn(new SimpleToken(user, password, System.currentTimeMillis() + 100000, System.currentTimeMillis(), null)); IdentityProviderConfig idProviderConfig = mock(IdentityProviderConfig.class); when(idProviderConfig.getProvider()).thenReturn(idProvider); TokenAuthConfig tokenAuthConfig = TokenAuthConfig.builder() .identityProviderConfig(idProviderConfig).expirationRefreshRatio(0.8F) .lowerRefreshBoundMillis(10000).tokenRequestExecTimeoutInMs(1000).build(); JedisClientConfig clientConfig = DefaultJedisClientConfig.builder() .authXManager(new AuthXManager(tokenAuthConfig)).build(); try (RedisClient jedis = RedisClient.builder() .hostAndPort(endpointConfig.getHostAndPort()) .clientConfig(clientConfig) .build()) { JedisPubSub pubSub = new JedisPubSub() {}; JedisException e = assertThrows(JedisException.class, () -> jedis.subscribe(pubSub, "channel1")); assertEquals( "Blocking pub/sub operations are not supported on token-based authentication enabled connections with RESP2 protocol!", e.getMessage()); } } } ================================================ FILE: src/test/java/redis/clients/jedis/authentication/TokenBasedAuthenticationUnitTests.java ================================================ package redis.clients.jedis.authentication; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertThrows; import static org.mockito.ArgumentMatchers.any; import static org.mockito.Mockito.*; import static org.awaitility.Awaitility.await; import static org.awaitility.Durations.*; import static org.hamcrest.CoreMatchers.either; import static org.hamcrest.CoreMatchers.is; import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.lessThanOrEqualTo; import java.util.Collections; import java.util.concurrent.CountDownLatch; import java.util.concurrent.ExecutionException; import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeoutException; import java.util.concurrent.atomic.AtomicInteger; import org.hamcrest.Matchers; import org.junit.jupiter.api.Test; import org.mockito.ArgumentCaptor; import org.mockito.MockedConstruction; import redis.clients.authentication.core.IdentityProvider; import redis.clients.authentication.core.IdentityProviderConfig; import redis.clients.authentication.core.SimpleToken; import redis.clients.authentication.core.Token; import redis.clients.authentication.core.TokenAuthConfig; import redis.clients.authentication.core.TokenListener; import redis.clients.authentication.core.TokenManager; import redis.clients.authentication.core.TokenManagerConfig; import redis.clients.authentication.core.TokenManagerConfig.RetryPolicy; import redis.clients.jedis.ConnectionPool; import redis.clients.jedis.EndpointConfig; import redis.clients.jedis.HostAndPort; public class TokenBasedAuthenticationUnitTests { private HostAndPort hnp = new HostAndPort("localhost", 6379); private EndpointConfig endpoint = new EndpointConfig(hnp, null, null, false, null); @Test public void testJedisAuthXManagerInstance() { TokenManagerConfig tokenManagerConfig = mock(TokenManagerConfig.class); IdentityProviderConfig identityProviderConfig = mock(IdentityProviderConfig.class); IdentityProvider identityProvider = mock(IdentityProvider.class); when(identityProviderConfig.getProvider()).thenReturn(identityProvider); try (MockedConstruction mockedConstructor = mockConstruction(TokenManager.class, (mock, context) -> { assertEquals(identityProvider, context.arguments().get(0)); assertEquals(tokenManagerConfig, context.arguments().get(1)); })) { new AuthXManager(new TokenAuthConfig(tokenManagerConfig, identityProviderConfig)); } } @Test public void withExpirationRefreshRatio_testJedisAuthXManagerTriggersEvict() throws Exception { IdentityProvider idProvider = mock(IdentityProvider.class); when(idProvider.requestToken()) .thenReturn(new SimpleToken("default", "password", System.currentTimeMillis() + 1000, System.currentTimeMillis(), Collections.singletonMap("oid", "default"))); TokenManager tokenManager = new TokenManager(idProvider, new TokenManagerConfig(0.4F, 100, 1000, new RetryPolicy(1, 1))); AuthXManager jedisAuthXManager = new AuthXManager(tokenManager); AtomicInteger numberOfEvictions = new AtomicInteger(0); try (ConnectionPool pool = new ConnectionPool(hnp, endpoint.getClientConfigBuilder().authXManager(jedisAuthXManager).build()) { @Override public void evict() throws Exception { numberOfEvictions.incrementAndGet(); super.evict(); } }) { await().pollInterval(ONE_HUNDRED_MILLISECONDS).atMost(FIVE_HUNDRED_MILLISECONDS) .until(numberOfEvictions::get, Matchers.greaterThanOrEqualTo(1)); } } public void withLowerRefreshBounds_testJedisAuthXManagerTriggersEvict() throws Exception { IdentityProvider idProvider = mock(IdentityProvider.class); when(idProvider.requestToken()) .thenReturn(new SimpleToken("default", "password", System.currentTimeMillis() + 1000, System.currentTimeMillis(), Collections.singletonMap("oid", "default"))); TokenManager tokenManager = new TokenManager(idProvider, new TokenManagerConfig(0.9F, 600, 1000, new RetryPolicy(1, 1))); AuthXManager jedisAuthXManager = new AuthXManager(tokenManager); AtomicInteger numberOfEvictions = new AtomicInteger(0); try (ConnectionPool pool = new ConnectionPool(endpoint.getHostAndPort(), endpoint.getClientConfigBuilder().authXManager(jedisAuthXManager).build()) { @Override public void evict() throws Exception { numberOfEvictions.incrementAndGet(); super.evict(); } }) { await().pollInterval(ONE_HUNDRED_MILLISECONDS).atMost(FIVE_HUNDRED_MILLISECONDS) .until(numberOfEvictions::get, Matchers.greaterThanOrEqualTo(1)); } } public static class TokenManagerConfigWrapper extends TokenManagerConfig { int lower; float ratio; public TokenManagerConfigWrapper() { super(0, 0, 0, null); } @Override public int getLowerRefreshBoundMillis() { return lower; } @Override public float getExpirationRefreshRatio() { return ratio; } @Override public RetryPolicy getRetryPolicy() { return new RetryPolicy(1, 1); } } @Test public void testCalculateRenewalDelay() { long delay = 0; long duration = 0; long issueDate; long expireDate; TokenManagerConfigWrapper config = new TokenManagerConfigWrapper(); TokenManager manager = new TokenManager(() -> null, config); duration = 5000; config.lower = 2000; config.ratio = 0.5F; issueDate = System.currentTimeMillis(); expireDate = issueDate + duration; delay = manager.calculateRenewalDelay(expireDate, issueDate); assertThat(delay, lessThanOrEqualTo(Math.min(duration - config.lower, (long) (duration * config.ratio)))); duration = 10000; config.lower = 8000; config.ratio = 0.2F; issueDate = System.currentTimeMillis(); expireDate = issueDate + duration; delay = manager.calculateRenewalDelay(expireDate, issueDate); assertThat(delay, lessThanOrEqualTo(Math.min(duration - config.lower, (long) (duration * config.ratio)))); duration = 10000; config.lower = 10000; config.ratio = 0.2F; issueDate = System.currentTimeMillis(); expireDate = issueDate + duration; delay = manager.calculateRenewalDelay(expireDate, issueDate); assertEquals(0, delay); duration = 0; config.lower = 5000; config.ratio = 0.2F; issueDate = System.currentTimeMillis(); expireDate = issueDate + duration; delay = manager.calculateRenewalDelay(expireDate, issueDate); assertEquals(0, delay); duration = 10000; config.lower = 1000; config.ratio = 0.00001F; issueDate = System.currentTimeMillis(); expireDate = issueDate + duration; delay = manager.calculateRenewalDelay(expireDate, issueDate); assertEquals(0, delay); duration = 10000; config.lower = 1000; config.ratio = 0.0001F; issueDate = System.currentTimeMillis(); expireDate = issueDate + duration; delay = manager.calculateRenewalDelay(expireDate, issueDate); assertThat(delay, either(is(0L)).or(is(1L))); } @Test public void testAuthXManagerReceivesNewToken() throws InterruptedException, ExecutionException, TimeoutException { IdentityProvider identityProvider = () -> new SimpleToken("user1", "tokenVal", System.currentTimeMillis() + 5 * 1000, System.currentTimeMillis(), Collections.singletonMap("oid", "user1")); TokenManager tokenManager = new TokenManager(identityProvider, new TokenManagerConfig(0.7F, 200, 2000, new RetryPolicy(1, 1))); AuthXManager manager = spy(new AuthXManager(tokenManager)); final Token[] tokenHolder = new Token[1]; doAnswer(invocation -> { Object[] args = invocation.getArguments(); tokenHolder[0] = (Token) args[0]; return null; }).when(manager).authenticateConnections(any()); try { manager.start(); assertEquals(tokenHolder[0].getValue(), "tokenVal"); } finally { manager.stop(); } } @Test public void testBlockForInitialTokenWhenException() { String exceptionMessage = "Test exception from identity provider!"; IdentityProvider identityProvider = () -> { throw new RuntimeException(exceptionMessage); }; TokenManager tokenManager = new TokenManager(identityProvider, new TokenManagerConfig(0.7F, 200, 2000, new TokenManagerConfig.RetryPolicy(5, 100))); AuthXManager manager = new AuthXManager(tokenManager); JedisAuthenticationException e = assertThrows(JedisAuthenticationException.class, manager::start); assertEquals(exceptionMessage, e.getCause().getCause().getMessage()); } @Test public void testBlockForInitialTokenWhenHangs() { String exceptionMessage = "AuthXManager failed to start!"; CountDownLatch latch = new CountDownLatch(1); IdentityProvider identityProvider = () -> { try { latch.await(); } catch (InterruptedException e) { // Ignore } return null; }; TokenManager tokenManager = new TokenManager(identityProvider, new TokenManagerConfig(0.7F, 200, 1000, new TokenManagerConfig.RetryPolicy(2, 100))); AuthXManager manager = new AuthXManager(tokenManager); JedisAuthenticationException e = assertThrows(JedisAuthenticationException.class, manager::start); latch.countDown(); assertEquals(exceptionMessage, e.getMessage()); } @Test public void testTokenManagerWithFailingTokenRequest() throws InterruptedException, ExecutionException, TimeoutException { int numberOfRetries = 5; CountDownLatch requesLatch = new CountDownLatch(numberOfRetries); IdentityProvider identityProvider = mock(IdentityProvider.class); when(identityProvider.requestToken()).thenAnswer(invocation -> { requesLatch.countDown(); if (requesLatch.getCount() > 0) { throw new RuntimeException("Test exception from identity provider!"); } return new SimpleToken("user1", "tokenValX", System.currentTimeMillis() + 50 * 1000, System.currentTimeMillis(), Collections.singletonMap("oid", "user1")); }); ArgumentCaptor argument = ArgumentCaptor.forClass(Token.class); TokenManager tokenManager = new TokenManager(identityProvider, new TokenManagerConfig(0.7F, 200, 2000, new TokenManagerConfig.RetryPolicy(numberOfRetries - 1, 100))); try { TokenListener listener = mock(TokenListener.class); tokenManager.start(listener, false); requesLatch.await(); await().pollDelay(ONE_HUNDRED_MILLISECONDS).atMost(FIVE_HUNDRED_MILLISECONDS) .untilAsserted(() -> verify(listener).onTokenRenewed(argument.capture())); verify(identityProvider, times(numberOfRetries)).requestToken(); verify(listener, never()).onError(any()); assertEquals("tokenValX", argument.getValue().getValue()); } finally { tokenManager.stop(); } } @Test public void testTokenManagerWithHangingTokenRequest() throws InterruptedException, ExecutionException, TimeoutException { int sleepDuration = 200; int executionTimeout = 100; int tokenLifetime = 50 * 1000; int numberOfRetries = 5; CountDownLatch requesLatch = new CountDownLatch(numberOfRetries); IdentityProvider identityProvider = () -> { requesLatch.countDown(); if (requesLatch.getCount() > 0) { try { Thread.sleep(sleepDuration); } catch (InterruptedException e) { // Ignore } return null; } return new SimpleToken("user1", "tokenValX", System.currentTimeMillis() + tokenLifetime, System.currentTimeMillis(), Collections.singletonMap("oid", "user1")); }; TokenManager tokenManager = new TokenManager(identityProvider, new TokenManagerConfig(0.7F, 200, executionTimeout, new TokenManagerConfig.RetryPolicy(numberOfRetries, 100))); AuthXManager manager = spy(new AuthXManager(tokenManager)); try { AuthXEventListener listener = mock(AuthXEventListener.class); manager.setListener(listener); manager.start(); requesLatch.await(); verify(listener, never()).onIdentityProviderError(any()); verify(listener, never()).onConnectionAuthenticationError(any()); await().atMost(2, TimeUnit.SECONDS).untilAsserted(() -> { verify(manager, times(1)).authenticateConnections(any()); }); } finally { manager.stop(); } } } ================================================ FILE: src/test/java/redis/clients/jedis/benchmark/CRC16Benchmark.java ================================================ package redis.clients.jedis.benchmark; import java.util.Calendar; import redis.clients.jedis.util.JedisClusterCRC16; public class CRC16Benchmark { private static final int TOTAL_OPERATIONS = 100000000; private static String[] TEST_SET = {"", "123456789", "sfger132515", "hae9Napahngaikeethievubaibogiech", "AAAAAAAAAAAAAAAAAAAAAA", "Hello, World!"}; public static void main(String[] args) { long begin = Calendar.getInstance().getTimeInMillis(); for (int n = 0; n <= TOTAL_OPERATIONS; n++) { JedisClusterCRC16.getSlot(TEST_SET[n % TEST_SET.length]); } long elapsed = Calendar.getInstance().getTimeInMillis() - begin; System.out.println(((1000 * TOTAL_OPERATIONS) / elapsed) + " ops"); } } ================================================ FILE: src/test/java/redis/clients/jedis/benchmark/GetSetBenchmark.java ================================================ package redis.clients.jedis.benchmark; import java.io.IOException; import java.net.UnknownHostException; import java.util.Calendar; import redis.clients.jedis.EndpointConfig; import redis.clients.jedis.Jedis; import redis.clients.jedis.Endpoints; public class GetSetBenchmark { private static EndpointConfig endpoint = Endpoints.getRedisEndpoint("standalone0"); private static final int TOTAL_OPERATIONS = 100000; public static void main(String[] args) throws UnknownHostException, IOException { Jedis jedis = new Jedis(endpoint.getHostAndPort()); jedis.connect(); jedis.auth(endpoint.getPassword()); jedis.flushAll(); long begin = Calendar.getInstance().getTimeInMillis(); for (int n = 0; n <= TOTAL_OPERATIONS; n++) { String key = "foo" + n; jedis.set(key, "bar" + n); jedis.get(key); } long elapsed = Calendar.getInstance().getTimeInMillis() - begin; jedis.disconnect(); System.out.println(((1000 * 2 * TOTAL_OPERATIONS) / elapsed) + " ops"); } } ================================================ FILE: src/test/java/redis/clients/jedis/benchmark/PipelinedGetSetBenchmark.java ================================================ package redis.clients.jedis.benchmark; import java.io.IOException; import java.net.UnknownHostException; import java.util.Calendar; import redis.clients.jedis.*; public class PipelinedGetSetBenchmark { private static EndpointConfig endpoint = Endpoints.getRedisEndpoint("standalone0"); private static final int TOTAL_OPERATIONS = 200000; public static void main(String[] args) throws UnknownHostException, IOException { Jedis jedis = new Jedis(endpoint.getHostAndPort()); jedis.connect(); jedis.auth(endpoint.getPassword()); jedis.flushAll(); long begin = Calendar.getInstance().getTimeInMillis(); Pipeline p = jedis.pipelined(); for (int n = 0; n <= TOTAL_OPERATIONS; n++) { String key = "foo" + n; p.set(key, "bar" + n); p.get(key); } p.sync(); long elapsed = Calendar.getInstance().getTimeInMillis() - begin; jedis.disconnect(); System.out.println(((1000 * 2 * TOTAL_OPERATIONS) / elapsed) + " ops"); } } ================================================ FILE: src/test/java/redis/clients/jedis/benchmark/PoolBenchmark.java ================================================ package redis.clients.jedis.benchmark; import java.util.ArrayList; import java.util.List; import java.util.concurrent.atomic.AtomicInteger; import org.apache.commons.pool2.impl.GenericObjectPoolConfig; import redis.clients.jedis.*; public class PoolBenchmark { private static EndpointConfig endpoint = Endpoints.getRedisEndpoint("standalone0"); private static final int TOTAL_OPERATIONS = 100000; public static void main(String[] args) throws Exception { Jedis j = new Jedis(endpoint.getHostAndPort()); j.connect(); j.auth(endpoint.getPassword()); j.flushAll(); j.disconnect(); long t = System.currentTimeMillis(); // withoutPool(); withPool(); long elapsed = System.currentTimeMillis() - t; System.out.println(((1000 * 2 * TOTAL_OPERATIONS) / elapsed) + " ops"); } private static void withPool() throws Exception { final JedisPool pool = new JedisPool(new GenericObjectPoolConfig(), endpoint.getHost(), endpoint.getPort(), 2000, endpoint.getPassword()); List tds = new ArrayList(); final AtomicInteger ind = new AtomicInteger(); for (int i = 0; i < 50; i++) { Thread hj = new Thread(new Runnable() { public void run() { for (int i = 0; (i = ind.getAndIncrement()) < TOTAL_OPERATIONS;) { try { Jedis j = pool.getResource(); final String key = "foo" + i; j.set(key, key); j.get(key); j.close(); } catch (Exception e) { e.printStackTrace(); } } } }); tds.add(hj); hj.start(); } for (Thread t : tds) { t.join(); } pool.destroy(); } } ================================================ FILE: src/test/java/redis/clients/jedis/benchmark/ProtocolBenchmark.java ================================================ package redis.clients.jedis.benchmark; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.IOException; import java.io.InputStream; import java.util.concurrent.TimeUnit; import redis.clients.jedis.CommandArguments; import redis.clients.jedis.Protocol; import redis.clients.jedis.util.RedisInputStream; import redis.clients.jedis.util.RedisOutputStream; /** * Copyright (c) 2014 */ public class ProtocolBenchmark { private static final int TOTAL_OPERATIONS = 500000; public static void main(String[] args) throws Exception, IOException { long total = 0; for (int at = 0; at != 10; ++at) { long elapsed = measureInputMulti(); long ops = ((1000 * 2 * TOTAL_OPERATIONS) / TimeUnit.NANOSECONDS.toMillis(elapsed)); if (at >= 5) { total += ops; } } System.out.println((total / 5) + " avg"); total = 0; for (int at = 0; at != 10; ++at) { long elapsed = measureInputStatus(); long ops = ((1000 * 2 * TOTAL_OPERATIONS) / TimeUnit.NANOSECONDS.toMillis(elapsed)); if (at >= 5) { total += ops; } } System.out.println((total / 5) + " avg"); total = 0; for (int at = 0; at != 10; ++at) { long elapsed = measureCommand(); long ops = ((1000 * 2 * TOTAL_OPERATIONS) / TimeUnit.NANOSECONDS.toMillis(elapsed)); if (at >= 5) { total += ops; } } System.out.println((total / 5) + " avg"); } private static long measureInputMulti() throws Exception { long duration = 0; InputStream is = new ByteArrayInputStream( "*4\r\n$3\r\nfoo\r\n$13\r\nbarbarbarfooz\r\n$5\r\nHello\r\n$5\r\nWorld\r\n".getBytes()); RedisInputStream in = new RedisInputStream(is); for (int n = 0; n <= TOTAL_OPERATIONS; n++) { long start = System.nanoTime(); Protocol.read(in); duration += (System.nanoTime() - start); in.reset(); } return duration; } private static long measureInputStatus() throws Exception { long duration = 0; InputStream is = new ByteArrayInputStream("+OK\r\n".getBytes()); RedisInputStream in = new RedisInputStream(is); for (int n = 0; n <= TOTAL_OPERATIONS; n++) { long start = System.nanoTime(); Protocol.read(in); duration += (System.nanoTime() - start); in.reset(); } return duration; } private static long measureCommand() throws Exception { long duration = 0; byte[] KEY = "123456789".getBytes(); byte[] VAL = "FooBar".getBytes(); for (int n = 0; n <= TOTAL_OPERATIONS; n++) { RedisOutputStream out = new RedisOutputStream(new ByteArrayOutputStream(8192)); long start = System.nanoTime(); // Protocol.sendCommand(out, Protocol.Command.SET, KEY, VAL); Protocol.sendCommand(out, new CommandArguments(Protocol.Command.SET).key(KEY).add(VAL)); duration += (System.nanoTime() - start); } return duration; } } ================================================ FILE: src/test/java/redis/clients/jedis/benchmark/RedisClientBenchmark.java ================================================ package redis.clients.jedis.benchmark; import java.util.ArrayList; import java.util.List; import java.util.concurrent.atomic.AtomicInteger; import redis.clients.jedis.*; public class RedisClientBenchmark { private static EndpointConfig endpoint = Endpoints.getRedisEndpoint("standalone0"); private static final int TOTAL_OPERATIONS = 100000; public static void main(String[] args) throws Exception { try (Jedis j = new Jedis(endpoint.getHost(), endpoint.getPort())) { j.auth(endpoint.getPassword()); j.flushAll(); j.disconnect(); } long t = System.currentTimeMillis(); withPool(); long elapsed = System.currentTimeMillis() - t; System.out.println(((1000 * 2 * TOTAL_OPERATIONS) / elapsed) + " ops"); } private static void withPool() throws Exception { final RedisClient j = RedisClient.builder() .hostAndPort(endpoint.getHost(), endpoint.getPort()) .clientConfig(DefaultJedisClientConfig.builder() .password(endpoint.getPassword()) .build()) .build(); List tds = new ArrayList<>(); final AtomicInteger ind = new AtomicInteger(); for (int i = 0; i < 50; i++) { Thread hj = new Thread(new Runnable() { @Override public void run() { for (int i = 0; (i = ind.getAndIncrement()) < TOTAL_OPERATIONS;) { try { final String key = "foo" + i; j.set(key, key); j.get(key); } catch (Exception e) { e.printStackTrace(); } } } }); tds.add(hj); hj.start(); } for (Thread t : tds) { t.join(); } j.close(); } } ================================================ FILE: src/test/java/redis/clients/jedis/benchmark/RedisClientCSCBenchmark.java ================================================ package redis.clients.jedis.benchmark; import java.util.ArrayList; import java.util.List; import java.util.concurrent.atomic.AtomicInteger; import redis.clients.jedis.*; import redis.clients.jedis.csc.Cache; import redis.clients.jedis.csc.TestCache; public class RedisClientCSCBenchmark { private static EndpointConfig endpoint = Endpoints.getRedisEndpoint("standalone0"); private static final int TOTAL_OPERATIONS = 1000000; private static final int NUMBER_OF_THREADS = 50; public static void main(String[] args) throws Exception { try (Jedis j = new Jedis(endpoint.getHost(), endpoint.getPort())) { j.auth(endpoint.getPassword()); j.flushAll(); j.disconnect(); } int totalRounds = 50; long withoutCache = 0; long withCache = 0; for (int i = 0; i < totalRounds; i++) { withoutCache += runBenchmark(null); withCache += runBenchmark(new TestCache()); } for (int i = 0; i < totalRounds; i++) { } System.out.println(String.format("after %d rounds withoutCache: %d ms, withCache: %d ms", totalRounds, withoutCache, withCache)); System.out.println("execution time ratio: " + (double) withCache / withoutCache); } private static long runBenchmark(Cache cache) throws Exception { long start = System.currentTimeMillis(); withPool(cache); long elapsed = System.currentTimeMillis() - start; System.out.println(String.format("%s round elapsed: %d ms", cache == null ? "no cache" : "cached", elapsed)); return elapsed; } private static void withPool(Cache cache) throws Exception { JedisClientConfig config = DefaultJedisClientConfig.builder().protocol(RedisProtocol.RESP3) .password(endpoint.getPassword()).build(); List tds = new ArrayList<>(); final AtomicInteger ind = new AtomicInteger(); try (RedisClient jedis = RedisClient.builder() .hostAndPort(endpoint.getHostAndPort()) .clientConfig(config) .cache(cache) .build()) { for (int i = 0; i < NUMBER_OF_THREADS; i++) { Thread hj = new Thread(new Runnable() { @Override public void run() { for (int i = 0; (i = ind.getAndIncrement()) < TOTAL_OPERATIONS;) { try { final String key = "foo" + i; jedis.set(key, key); jedis.get(key); } catch (Exception e) { e.printStackTrace(); throw e; } } } }); tds.add(hj); hj.start(); } for (Thread t : tds) { t.join(); } } } } ================================================ FILE: src/test/java/redis/clients/jedis/benchmark/SafeEncoderBenchmark.java ================================================ package redis.clients.jedis.benchmark; import java.io.IOException; import java.net.UnknownHostException; import java.util.Calendar; import redis.clients.jedis.util.SafeEncoder; public class SafeEncoderBenchmark { private static final int TOTAL_OPERATIONS = 10000000; public static void main(String[] args) throws UnknownHostException, IOException { long begin = Calendar.getInstance().getTimeInMillis(); for (int n = 0; n <= TOTAL_OPERATIONS; n++) { SafeEncoder.encode("foo bar!"); } long elapsed = Calendar.getInstance().getTimeInMillis() - begin; System.out.println(((1000 * TOTAL_OPERATIONS) / elapsed) + " ops to build byte[]"); begin = Calendar.getInstance().getTimeInMillis(); byte[] bytes = "foo bar!".getBytes(); for (int n = 0; n <= TOTAL_OPERATIONS; n++) { SafeEncoder.encode(bytes); } elapsed = Calendar.getInstance().getTimeInMillis() - begin; System.out.println(((1000 * TOTAL_OPERATIONS) / elapsed) + " ops to build Strings"); } } ================================================ FILE: src/test/java/redis/clients/jedis/builders/ClientBuilderTest.java ================================================ package redis.clients.jedis.builders; import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.contains; import static org.hamcrest.Matchers.containsString; import static org.hamcrest.Matchers.equalTo; import static org.junit.jupiter.api.Assertions.assertNull; import static org.junit.jupiter.api.Assertions.assertThrows; import static org.mockito.Mockito.*; import java.nio.charset.StandardCharsets; import java.util.ArrayList; import java.util.List; import java.util.Collections; import redis.clients.jedis.util.ReflectionTestUtil; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; import org.mockito.ArgumentCaptor; import org.mockito.Captor; import org.mockito.Mock; import org.mockito.junit.jupiter.MockitoExtension; import redis.clients.jedis.*; import redis.clients.jedis.args.Rawable; import redis.clients.jedis.util.CompareCondition; import redis.clients.jedis.csc.Cache; import redis.clients.jedis.executors.CommandExecutor; import redis.clients.jedis.params.SetParams; import redis.clients.jedis.providers.ConnectionProvider; import redis.clients.jedis.json.JsonObjectMapper; import redis.clients.jedis.json.Path2; import redis.clients.jedis.search.FTSearchParams; @ExtendWith(MockitoExtension.class) class ClientBuilderTest { @Mock CommandExecutor exec; @Mock ConnectionProvider provider; @Captor ArgumentCaptor> cap; private static List argsToStrings(CommandObject co) { List out = new ArrayList<>(); for (Rawable r : co.getArguments()) { out.add(new String(r.getRaw(), StandardCharsets.UTF_8)); } return out; } @Test void appliesKeyPreprocessorToCommandObjects() { try (RedisClient client = RedisClient.builder().commandExecutor(exec) .connectionProvider(provider).keyPreProcessor(k -> "prefix:" + k).build()) { client.set("key", "v"); } verify(exec).executeCommand(cap.capture()); assertThat(argsToStrings(cap.getValue()), contains("SET", "prefix:key", "v")); } @Test void appliesJsonObjectMapper() { JsonObjectMapper mapper = mock(JsonObjectMapper.class); when(mapper.toJson(any())).thenReturn("JSON:{a=1}"); try (RedisClient client = RedisClient.builder().commandExecutor(exec) .connectionProvider(provider).jsonObjectMapper(mapper).build()) { client.jsonSetWithEscape("k", Path2.ROOT_PATH, Collections.singletonMap("a", 1)); } verify(exec).executeCommand(cap.capture()); assertThat(argsToStrings(cap.getValue()), contains("JSON.SET", "k", "$", "JSON:{a=1}")); } @Test void appliesSearchDialect() { try (RedisClient client = RedisClient.builder().commandExecutor(exec) .connectionProvider(provider).searchDialect(3).build()) { client.ftSearch("idx", "q", new FTSearchParams()); } verify(exec, atLeastOnce()).executeCommand(cap.capture()); List args = argsToStrings(cap.getValue()); assertThat(args, contains("FT.SEARCH", "idx", "q", "DIALECT", "3")); } @Test void cacheRequiresRESP3() { Cache cache = mock(Cache.class); IllegalArgumentException ex = assertThrows(IllegalArgumentException.class, () -> RedisClient .builder().commandExecutor(exec).connectionProvider(provider).cache(cache).build(), "Cache requires RESP3"); assertThat(ex.getMessage(), containsString("Client-side caching is only supported with RESP3")); } @Test void standaloneValidateHostPortRequired() { IllegalArgumentException ex = assertThrows(IllegalArgumentException.class, () -> RedisClient.builder().hostAndPort(null).build()); assertThat(ex.getMessage(), containsString("Either URI or host/port must be specified")); } @Test void sentinelValidateMasterAndSentinels() { IllegalArgumentException ex = assertThrows(IllegalArgumentException.class, () -> RedisSentinelClient.builder().build()); assertThat(ex.getMessage(), containsString("Master name is required for Sentinel mode")); ex = assertThrows(IllegalArgumentException.class, () -> RedisSentinelClient.builder().masterName("mymaster").build()); assertThat(ex.getMessage(), containsString("At least one sentinel must be specified for Sentinel mode")); ex = assertThrows(IllegalArgumentException.class, () -> RedisSentinelClient.builder() .masterName("mymaster").sentinels(Collections.emptySet()).build()); assertThat(ex.getMessage(), containsString("At least one sentinel must be specified for Sentinel mode")); } @Test void setWithValueCondition() { try (JedisPooled client = JedisPooled.builder().commandExecutor(exec) .connectionProvider(provider).build()) { client.set("key", "value", SetParams.setParams().xx().condition(CompareCondition.valueEq("oldValue"))); } verify(exec).executeCommand(cap.capture()); assertThat(argsToStrings(cap.getValue()), contains("SET", "key", "value", "IFEQ", "oldValue", "XX")); } @Test void setWithDigestCondition() { try (JedisPooled client = JedisPooled.builder().commandExecutor(exec) .connectionProvider(provider).build()) { client.set("key", "value", SetParams.setParams().nx().ex(100) .condition(CompareCondition.digestEq("0123456789abcdef"))); } verify(exec).executeCommand(cap.capture()); assertThat(argsToStrings(cap.getValue()), contains("SET", "key", "value", "IFDEQ", "0123456789abcdef", "NX", "EX", "100")); } @Test void delexWithValueCondition() { when(exec.executeCommand(any())).thenReturn(1L); try (JedisPooled client = JedisPooled.builder().commandExecutor(exec) .connectionProvider(provider).build()) { client.delex("key", CompareCondition.valueNe("value")); } verify(exec).executeCommand(cap.capture()); assertThat(argsToStrings(cap.getValue()), contains("DELEX", "key", "IFNE", "value")); } @Test void delexWithDigestCondition() { when(exec.executeCommand(any())).thenReturn(1L); try (JedisPooled client = JedisPooled.builder().commandExecutor(exec) .connectionProvider(provider).build()) { client.delex("key", CompareCondition.digestNe("fedcba9876543210")); } verify(exec).executeCommand(cap.capture()); assertThat(argsToStrings(cap.getValue()), contains("DELEX", "key", "IFDNE", "fedcba9876543210")); } @Test void digestKey() { try (JedisPooled client = JedisPooled.builder().commandExecutor(exec) .connectionProvider(provider).build()) { client.digestKey("key"); } verify(exec).executeCommand(cap.capture()); assertThat(argsToStrings(cap.getValue()), contains("DIGEST", "key")); } @SuppressWarnings("deprecation") @Test void fromURI_withInvalidURI_throwsException() { // URI with credentials IllegalArgumentException ex = assertThrows(IllegalArgumentException.class, () -> RedisClient.builder().fromURI("uriuser:uripass@localhost:6379")); assertThat(ex.getMessage(), containsString("Invalid Redis URI")); } @Test @SuppressWarnings("deprecation") void fromUri_existingClientConfigIsPreserved() { JedisClientConfig config = DefaultJedisClientConfig.builder().user("testuser") .password("testpass").connectionTimeoutMillis(5000).build(); // URI without credentials - should preserve config credentials StandaloneClientBuilder builder = RedisClient.builder().clientConfig(config) .fromURI("redis://localhost:6379"); JedisClientConfig resultConfig = getClientConfig(builder); assertThat(resultConfig.getUser(), equalTo("testuser")); assertThat(resultConfig.getPassword(), equalTo("testpass")); assertThat(resultConfig.getConnectionTimeoutMillis(), equalTo(5000)); } @Test @SuppressWarnings("deprecation") void fromURI_withCredentials_overridesClientConfigCredentials() { JedisClientConfig config = DefaultJedisClientConfig.builder().user("olduser") .password("oldpass").build(); // URI with credentials should override config credentials StandaloneClientBuilder builder = RedisClient.builder().clientConfig(config) .fromURI("redis://newuser:newpass@localhost:6379"); JedisClientConfig resultConfig = getClientConfig(builder); assertThat(resultConfig.getUser(), equalTo("newuser")); assertThat(resultConfig.getPassword(), equalTo("newpass")); } @Test @SuppressWarnings("deprecation") void fromURI_ThenClientConfig_configWins() { // URI with credentials StandaloneClientBuilder builder = RedisClient.builder() .fromURI("redis://uriuser:uripass@localhost:6379").clientConfig( DefaultJedisClientConfig.builder().user("configuser").password("configpass").build()); JedisClientConfig resultConfig = getClientConfig(builder); assertThat(resultConfig.getUser(), equalTo("configuser")); assertThat(resultConfig.getPassword(), equalTo("configpass")); } @Test @SuppressWarnings("deprecation") void fromUri_multipleFromURICalls_lastWins() { StandaloneClientBuilder builder = RedisClient.builder() .fromURI("redis://user1:pass1@localhost:6379/1") .fromURI("redis://user2:pass2@localhost:6380/2"); JedisClientConfig resultConfig = getClientConfig(builder); assertThat(resultConfig.getUser(), equalTo("user2")); assertThat(resultConfig.getPassword(), equalTo("pass2")); assertThat(resultConfig.getDatabase(), equalTo(2)); } @Test @SuppressWarnings("deprecation") void fromUri_partialURIOverride_preservesNonURIValues() { // Real-world scenario from issue #4416 JedisClientConfig config = DefaultJedisClientConfig.builder().user("mark").password("secret") .connectionTimeoutMillis(5000).socketTimeoutMillis(3000).build(); // URI without credentials, only host/port StandaloneClientBuilder builder = RedisClient.builder().clientConfig(config) .fromURI("redis://localhost:6379"); JedisClientConfig resultConfig = getClientConfig(builder); // Should preserve credentials and timeouts from config assertThat(resultConfig.getUser(), equalTo("mark")); assertThat(resultConfig.getPassword(), equalTo("secret")); assertThat(resultConfig.getConnectionTimeoutMillis(), equalTo(5000)); assertThat(resultConfig.getSocketTimeoutMillis(), equalTo(3000)); } @Test void fromUri_withProtocol_OverridesClientConfig() { JedisClientConfig config = DefaultJedisClientConfig.builder().protocol(RedisProtocol.RESP2) .build(); StandaloneClientBuilder builder = RedisClient.builder().clientConfig(config) .fromURI("redis://localhost:6379?protocol=3"); JedisClientConfig resultConfig = getClientConfig(builder); assertThat(resultConfig.getRedisProtocol(), equalTo(RedisProtocol.RESP3)); } @Test void fromUri_noProtocol_PreservesClientConfig() { JedisClientConfig config = DefaultJedisClientConfig.builder().protocol(RedisProtocol.RESP2) .build(); StandaloneClientBuilder builder = RedisClient.builder().clientConfig(config) .fromURI("redis://localhost:6379"); JedisClientConfig resultConfig = getClientConfig(builder); assertThat(resultConfig.getRedisProtocol(), equalTo(RedisProtocol.RESP2)); } @Test void fromUri_noProtocol_preservesDefault() { StandaloneClientBuilder builder = RedisClient.builder() .fromURI("redis://localhost:6379"); JedisClientConfig resultConfig = getClientConfig(builder); assertNull(resultConfig.getRedisProtocol()); } /** * Helper method to access the protected clientConfig field from the builder using reflection. */ private JedisClientConfig getClientConfig( redis.clients.jedis.builders.AbstractClientBuilder builder) { return ReflectionTestUtil.getField(builder, "clientConfig"); } } ================================================ FILE: src/test/java/redis/clients/jedis/builders/ClusterClientBuilderTest.java ================================================ package redis.clients.jedis.builders; import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.containsString; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertThrows; import static org.mockito.Mockito.atLeastOnce; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; import java.nio.charset.StandardCharsets; import java.time.Duration; import java.util.ArrayList; import java.util.Collections; import java.util.HashSet; import java.util.List; import java.util.Set; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; import org.mockito.ArgumentCaptor; import org.mockito.Captor; import org.mockito.Mock; import org.mockito.MockedConstruction; import org.mockito.Mockito; import org.mockito.junit.jupiter.MockitoExtension; import redis.clients.jedis.CommandObject; import redis.clients.jedis.DefaultJedisClientConfig; import redis.clients.jedis.HostAndPort; import redis.clients.jedis.RedisClusterClient; import redis.clients.jedis.args.Rawable; import redis.clients.jedis.executors.ClusterCommandExecutor; import redis.clients.jedis.executors.CommandExecutor; import redis.clients.jedis.json.JsonObjectMapper; import redis.clients.jedis.json.Path2; import redis.clients.jedis.providers.ClusterConnectionProvider; import redis.clients.jedis.providers.ConnectionProvider; import redis.clients.jedis.search.FTSearchParams; @ExtendWith(MockitoExtension.class) class ClusterClientBuilderTest { @Mock CommandExecutor exec; @Mock ConnectionProvider provider; @Captor ArgumentCaptor> cap; private static List argsToStrings(CommandObject co) { List out = new ArrayList<>(); for (Rawable r : co.getArguments()) { out.add(new String(r.getRaw(), StandardCharsets.UTF_8)); } return out; } private static Set someNodes() { Set nodes = new HashSet<>(); nodes.add(new HostAndPort("127.0.0.1", 7000)); return nodes; } @Test void clusterNodesEmptyShouldThrow() { IllegalArgumentException ex = assertThrows(IllegalArgumentException.class, () -> RedisClusterClient.builder().nodes(new HashSet<>()).build()); assertThat(ex.getMessage(), containsString("At least one cluster node must be specified for cluster mode")); } @Test void negativeMaxTotalRetriesDurationShouldThrow() { IllegalArgumentException ex = assertThrows(IllegalArgumentException.class, () -> RedisClusterClient.builder().nodes(someNodes()) .maxTotalRetriesDuration(Duration.ofMillis(-1)).build()); assertThat(ex.getMessage(), containsString("Max total retries duration cannot be negative for cluster mode")); } @Test void negativeTopologyRefreshShouldThrow() { IllegalArgumentException ex = assertThrows(IllegalArgumentException.class, () -> RedisClusterClient.builder().nodes(someNodes()) .topologyRefreshPeriod(Duration.ofMillis(-1)).build()); assertThat(ex.getMessage(), containsString("Topology refresh period cannot be negative for cluster mode")); } @Test void buildWithPositiveDurationsAndConfig_usesProvidedExecAndProvider() { try (RedisClusterClient client = RedisClusterClient.builder().nodes(someNodes()) .clientConfig(redis.clients.jedis.DefaultJedisClientConfig.builder().build()).maxAttempts(3) .maxTotalRetriesDuration(Duration.ofMillis(10)).topologyRefreshPeriod(Duration.ofMillis(50)) .connectionProvider(provider).commandExecutor(exec).build()) { client.ping(); } verify(exec, atLeastOnce()).executeCommand(cap.capture()); assertThat(argsToStrings(cap.getValue()).get(0), containsString("PING")); } @Test void nodesNotProvidedShouldThrow() { IllegalArgumentException ex = assertThrows(IllegalArgumentException.class, () -> RedisClusterClient.builder().build()); assertThat(ex.getMessage(), containsString("At least one cluster node must be specified for cluster mode")); } @Test void searchDialectZeroShouldThrow() { IllegalArgumentException ex = assertThrows(IllegalArgumentException.class, () -> RedisClusterClient.builder().searchDialect(0)); assertThat(ex.getMessage(), containsString("DIALECT=0 cannot be set.")); } @Test void keyPreprocessorAppliedInCluster() { try (RedisClusterClient client = RedisClusterClient.builder().nodes(someNodes()) .connectionProvider(provider).commandExecutor(exec).keyPreProcessor(k -> "prefix:" + k) .build()) { client.set("k", "v"); verify(exec).executeCommand(cap.capture()); List args = argsToStrings(cap.getValue()); // SET prefix:k v assertThat(args.get(0), containsString("SET")); assertEquals("prefix:k", args.get(1)); assertEquals("v", args.get(2)); } } @Test void jsonObjectMapperAppliedInCluster() { JsonObjectMapper mapper = Mockito.mock(JsonObjectMapper.class); when(mapper.toJson(Mockito.any())).thenReturn("JSON:obj"); try (RedisClusterClient client = RedisClusterClient.builder().nodes(someNodes()) .connectionProvider(provider).commandExecutor(exec).jsonObjectMapper(mapper).build()) { client.jsonSetWithEscape("k", Path2.ROOT_PATH, Collections.singletonMap("a", 1)); verify(exec).executeCommand(cap.capture()); List args = argsToStrings(cap.getValue()); // JSON.SET k $ JSON:obj assertEquals("JSON.SET", args.get(0)); assertEquals("k", args.get(1)); assertEquals("$", args.get(2)); assertEquals("JSON:obj", args.get(3)); } } @Test void searchDialectAppliedInCluster() { try (RedisClusterClient client = RedisClusterClient.builder().nodes(someNodes()) .connectionProvider(provider).commandExecutor(exec).searchDialect(3).build()) { client.ftSearch("idx", "q", new FTSearchParams()); verify(exec, atLeastOnce()).executeCommand(cap.capture()); List args = argsToStrings(cap.getValue()); // FT.SEARCH idx q DIALECT 3 assertEquals("FT.SEARCH", args.get(0)); assertEquals("idx", args.get(1)); assertEquals("q", args.get(2)); assertEquals("DIALECT", args.get(3)); assertEquals("3", args.get(4)); } } @Test void maxTotalRetriesDurationUsesDefaultValues() { // Test that when nothing is explicitly set, it uses default values // Default socketTimeout = 2000ms (Protocol.DEFAULT_TIMEOUT) // Default maxAttempts = 5 (JedisCluster.DEFAULT_MAX_ATTEMPTS) // Expected maxTotalRetriesDuration = 2000 * 5 = 10000ms int defaultSocketTimeout = 2000; // Protocol.DEFAULT_TIMEOUT int defaultMaxAttempts = 5; // JedisCluster.DEFAULT_MAX_ATTEMPTS Duration expectedMaxTotalRetriesDuration = Duration .ofMillis((long) defaultSocketTimeout * defaultMaxAttempts); // 10000ms ClusterConnectionProvider mockProvider = Mockito.mock(ClusterConnectionProvider.class); // Use MockedConstruction to capture ClusterCommandExecutor constructor arguments try (MockedConstruction mockedExecutor = Mockito .mockConstruction(ClusterCommandExecutor.class, (mock, context) -> { // Verify constructor was called with default values assertEquals(4, context.arguments().size(), "ClusterCommandExecutor should have 4 constructor arguments"); assertEquals(mockProvider, context.arguments().get(0), "First argument should be the connection provider"); assertEquals(defaultMaxAttempts, context.arguments().get(1), "Second argument should be default maxAttempts (5)"); assertEquals(expectedMaxTotalRetriesDuration, context.arguments().get(2), "Third argument should be calculated from default values (2000ms * 5 = 10000ms)"); })) { try (RedisClusterClient client = RedisClusterClient.builder().nodes(someNodes()) .connectionProvider(mockProvider).build()) { // Verify that ClusterCommandExecutor was constructed assertEquals(1, mockedExecutor.constructed().size(), "ClusterCommandExecutor should have been constructed once"); } } } @Test void maxTotalRetriesDurationCalculatedFromSocketTimeoutAndMaxAttempts() { // Test that when maxTotalRetriesDuration is not explicitly set, // it is calculated as socketTimeout * maxAttempts int socketTimeout = 5000; // 5 seconds int maxAttempts = 10; Duration expectedMaxTotalRetriesDuration = Duration .ofMillis((long) socketTimeout * maxAttempts); // 50000ms DefaultJedisClientConfig clientConfig = DefaultJedisClientConfig.builder() .socketTimeoutMillis(socketTimeout).build(); ClusterConnectionProvider mockProvider = Mockito.mock(ClusterConnectionProvider.class); // Use MockedConstruction to capture ClusterCommandExecutor constructor arguments try (MockedConstruction mockedExecutor = Mockito .mockConstruction(ClusterCommandExecutor.class, (mock, context) -> { // Verify constructor was called with correct arguments assertEquals(4, context.arguments().size(), "ClusterCommandExecutor should have 4 constructor arguments"); assertEquals(mockProvider, context.arguments().get(0), "First argument should be the connection provider"); assertEquals(maxAttempts, context.arguments().get(1), "Second argument should be maxAttempts"); assertEquals(expectedMaxTotalRetriesDuration, context.arguments().get(2), "Third argument should be calculated maxTotalRetriesDuration (socketTimeout * maxAttempts)"); })) { try (RedisClusterClient client = RedisClusterClient.builder().nodes(someNodes()) .clientConfig(clientConfig).maxAttempts(maxAttempts).connectionProvider(mockProvider) .build()) { // Verify that ClusterCommandExecutor was constructed assertEquals(1, mockedExecutor.constructed().size(), "ClusterCommandExecutor should have been constructed once"); } } } @Test void maxTotalRetriesDurationUsesExplicitValueWhenSet() { // Test that when maxTotalRetriesDuration is explicitly set, // it uses that value instead of calculating from socketTimeout * maxAttempts int socketTimeout = 5000; // 5 seconds int maxAttempts = 10; Duration explicitMaxTotalRetriesDuration = Duration.ofSeconds(100); // 100 seconds DefaultJedisClientConfig clientConfig = DefaultJedisClientConfig.builder() .socketTimeoutMillis(socketTimeout).build(); ClusterConnectionProvider mockProvider = Mockito.mock(ClusterConnectionProvider.class); // Use MockedConstruction to capture ClusterCommandExecutor constructor arguments try (MockedConstruction mockedExecutor = Mockito .mockConstruction(ClusterCommandExecutor.class, (mock, context) -> { // Verify constructor was called with the explicitly set maxTotalRetriesDuration assertEquals(4, context.arguments().size(), "ClusterCommandExecutor should have 4 constructor arguments"); assertEquals(mockProvider, context.arguments().get(0), "First argument should be the connection provider"); assertEquals(maxAttempts, context.arguments().get(1), "Second argument should be maxAttempts"); assertEquals(explicitMaxTotalRetriesDuration, context.arguments().get(2), "Third argument should be the explicitly set maxTotalRetriesDuration"); })) { try (RedisClusterClient client = RedisClusterClient.builder().nodes(someNodes()) .clientConfig(clientConfig).maxAttempts(maxAttempts) .maxTotalRetriesDuration(explicitMaxTotalRetriesDuration).connectionProvider(mockProvider) .build()) { // Verify that ClusterCommandExecutor was constructed assertEquals(1, mockedExecutor.constructed().size(), "ClusterCommandExecutor should have been constructed once"); } } } } ================================================ FILE: src/test/java/redis/clients/jedis/builders/JedisClusterConstructorReflectionTest.java ================================================ package redis.clients.jedis.builders; import static org.junit.jupiter.api.Assertions.*; import java.lang.reflect.Constructor; import java.time.Duration; import java.util.Set; import javax.net.ssl.HostnameVerifier; import javax.net.ssl.SSLParameters; import javax.net.ssl.SSLSocketFactory; import org.apache.commons.pool2.PooledObjectFactory; import org.apache.commons.pool2.impl.GenericObjectPoolConfig; import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.condition.EnabledIfSystemProperty; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import redis.clients.jedis.*; import redis.clients.jedis.HostAndPortMapper; import redis.clients.jedis.csc.Cache; import redis.clients.jedis.csc.CacheConfig; import redis.clients.jedis.executors.CommandExecutor; import redis.clients.jedis.providers.ClusterConnectionProvider; import redis.clients.jedis.providers.ConnectionProvider; /** * Reflection-based coverage test for JedisCluster constructors against builder API. The test * verifies each constructor parameter can be provided via: - ClusterClientBuilder methods, or - * DefaultJedisClientConfig.Builder methods, or - Custom ConnectionProvider (manual coverage) */ @EnabledIfSystemProperty(named = "with-param-names", matches = "true") public class JedisClusterConstructorReflectionTest { private static final Logger log = LoggerFactory .getLogger(JedisClusterConstructorReflectionTest.class); @Test @DisplayName("Builder coverage of JedisCluster constructors (reflection)") void testConstructorParameterCoverageReport() { Constructor[] ctors = JedisCluster.class.getConstructors(); int total = 0, covered = 0; StringBuilder uncoveredReport = new StringBuilder(); for (Constructor ctor : ctors) { total++; java.lang.reflect.Parameter[] params = ctor.getParameters(); boolean[] paramCovered = new boolean[params.length]; String[] paramCoverageBy = new String[params.length]; String[] paramWhyMissing = new String[params.length]; for (int i = 0; i < params.length; i++) { java.lang.reflect.Parameter p = params[i]; Class t = p.getType(); String name = safeName(p); if (t == Set.class) { paramCovered[i] = true; paramCoverageBy[i] = "JedisCluster.builder().nodes(Set)"; } else if (t == HostAndPort.class) { paramCovered[i] = true; paramCoverageBy[i] = "JedisCluster.builder().nodes(Set.of(HostAndPort))"; } else if (t == JedisClientConfig.class) { paramCovered[i] = true; paramCoverageBy[i] = "JedisCluster.builder().clientConfig(DefaultJedisClientConfig...)"; } else if (t == GenericObjectPoolConfig.class) { paramCovered[i] = true; paramCoverageBy[i] = "JedisCluster.builder().poolConfig(...)"; } else if (t == Cache.class) { paramCovered[i] = true; paramCoverageBy[i] = "JedisCluster.builder().cache(...)"; } else if (t == CacheConfig.class) { paramCovered[i] = true; paramCoverageBy[i] = "JedisCluster.builder().cacheConfig(...)"; } else if (t == Duration.class) { // Either maxTotalRetriesDuration or topologyRefreshPeriod paramCovered[i] = true; paramCoverageBy[i] = "JedisCluster.builder().maxTotalRetriesDuration(..)/topologyRefreshPeriod(..)"; } else if (t == HostAndPortMapper.class) { paramCovered[i] = true; paramCoverageBy[i] = "DefaultJedisClientConfig.builder().hostAndPortMapper(...)"; } else if (t == int.class || t == Integer.class) { String lname = name.toLowerCase(); if (lname.contains("attempt")) { paramCovered[i] = true; paramCoverageBy[i] = "JedisCluster.builder().maxAttempts(...)"; } else if (lname.contains("port")) { // part of HostAndPort in string,int combos -> handled by string/host mapping below paramCoverageBy[i] = "JedisCluster.builder().nodes(Set.of(new HostAndPort(host,port)))"; } else if (lname.contains("db") || lname.contains("database")) { paramCovered[i] = true; paramCoverageBy[i] = "DefaultJedisClientConfig.builder().database(...)"; } else if (lname.contains("conn")) { paramCovered[i] = true; paramCoverageBy[i] = "DefaultJedisClientConfig.builder().connectionTimeoutMillis(...)"; } else if ((lname.contains("so") && lname.contains("timeout")) || lname.equals("sockettimeout")) { paramCovered[i] = true; paramCoverageBy[i] = "DefaultJedisClientConfig.builder().socketTimeoutMillis(...)"; } else if (lname.contains("infinite")) { paramCovered[i] = true; paramCoverageBy[i] = "DefaultJedisClientConfig.builder().blockingSocketTimeoutMillis(...)"; } else if (lname.contains("timeout")) { paramCovered[i] = true; paramCoverageBy[i] = "DefaultJedisClientConfig.builder().connectionTimeoutMillis(...)+socketTimeoutMillis(...)"; } } else if (t == boolean.class || t == Boolean.class) { paramCovered[i] = true; paramCoverageBy[i] = "DefaultJedisClientConfig.builder().ssl(...)"; } else if (t == String.class) { String lname = name.toLowerCase(); if (lname.contains("host")) { paramCoverageBy[i] = "JedisCluster.builder().nodes(Set.of(new HostAndPort(host,port)))"; } else if (lname.contains("user")) { paramCovered[i] = true; paramCoverageBy[i] = "DefaultJedisClientConfig.builder().user(...)"; } else if (lname.contains("pass")) { paramCovered[i] = true; paramCoverageBy[i] = "DefaultJedisClientConfig.builder().password(...)"; } else if (lname.contains("client") && lname.contains("name")) { paramCovered[i] = true; paramCoverageBy[i] = "DefaultJedisClientConfig.builder().clientName(...)"; } else { // could be password; mark to client config paramCovered[i] = true; paramCoverageBy[i] = "DefaultJedisClientConfig.builder().password/clientName/..."; } } else if (t == SSLSocketFactory.class) { paramCovered[i] = true; paramCoverageBy[i] = "DefaultJedisClientConfig.builder().sslSocketFactory(...)"; } else if (t == SSLParameters.class) { paramCovered[i] = true; paramCoverageBy[i] = "DefaultJedisClientConfig.builder().sslParameters(...)"; } else if (t == HostnameVerifier.class) { paramCovered[i] = true; paramCoverageBy[i] = "DefaultJedisClientConfig.builder().hostnameVerifier(...)"; } else if (t == ConnectionProvider.class || t == ClusterConnectionProvider.class || t == PooledObjectFactory.class) { paramCovered[i] = true; paramCoverageBy[i] = "Custom ConnectionProvider via builder.connectionProvider(...)"; } else if (t == CommandExecutor.class || t == CommandObjects.class) { paramCovered[i] = true; paramCoverageBy[i] = t == CommandExecutor.class ? "builder.commandExecutor(...)" : "Internal (builder-provided CommandObjects)"; } else if (t == RedisProtocol.class) { paramCovered[i] = true; paramCoverageBy[i] = "builder.redisProtocol(...)"; } else { paramCovered[i] = false; paramWhyMissing[i] = "No known builder mapping for type: " + t.getSimpleName(); } } // Pair host/port pattern for (String,int,...) convenience ctors int hostIdx = findPotentialHostStringIndex(params); int portIdx = findPortIndex(params); if (hostIdx != -1 && portIdx != -1) { paramCovered[hostIdx] = true; if (paramCoverageBy[hostIdx] == null) paramCoverageBy[hostIdx] = "JedisCluster.builder().nodes(Set.of(new HostAndPort(host,port)))"; paramCovered[portIdx] = true; if (paramCoverageBy[portIdx] == null) paramCoverageBy[portIdx] = "JedisCluster.builder().nodes(Set.of(new HostAndPort(host,port)))"; } boolean ctorCovered = true; StringBuilder missingParams = new StringBuilder(); for (int i = 0; i < params.length; i++) { if (!paramCovered[i]) { ctorCovered = false; missingParams.append(" - ").append(prettyParam(params[i])).append(" -> ") .append(paramWhyMissing[i] != null ? paramWhyMissing[i] : "unknown").append("\n"); } } if (!ctorCovered) { uncoveredReport.append("\nUncovered constructor: ").append(prettySignature(ctor)) .append("\n"); uncoveredReport.append("Missing parameters:\n").append(missingParams); } else { covered++; } } log.info("Analyzed {} constructors; fully covered: {}", total, covered); if (covered < total) { log.warn("Uncovered constructors detected:{}", uncoveredReport); } assertEquals(total, covered, "Expected all constructors to be covered by builders"); assertTrue(total > 0, "Expected at least one constructor to analyze"); } // ===== Helpers copied from pooled test, adapted ===== private static boolean hasType(java.lang.reflect.Parameter[] params, Class type) { for (java.lang.reflect.Parameter p : params) if (p.getType() == type) return true; return false; } private static int findPortIndex(java.lang.reflect.Parameter[] params) { for (int i = 0; i < params.length; i++) { Class t = params[i].getType(); if (t == int.class || t == Integer.class) { String n = safeName(params[i]).toLowerCase(); if (n.contains("port")) return i; } } int hostIdx = findPotentialHostStringIndex(params); if (hostIdx != -1) { for (int i = 0; i < params.length; i++) if (params[i].getType() == int.class || params[i].getType() == Integer.class) return i; } return -1; } private static int findPotentialHostStringIndex(java.lang.reflect.Parameter[] params) { for (int i = 0; i < params.length; i++) { if (params[i].getType() == String.class) { String n = safeName(params[i]).toLowerCase(); if (n.contains("host")) return i; } } boolean hasInt = false; int stringCount = 0; int stringIdx = -1; for (int i = 0; i < params.length; i++) { Class t = params[i].getType(); if (t == int.class || t == Integer.class) hasInt = true; if (t == String.class) { stringCount++; stringIdx = i; } } if (hasInt && stringCount == 1) return stringIdx; return -1; } private static String prettySignature(Constructor ctor) { StringBuilder sb = new StringBuilder(); sb.append(ctor.getDeclaringClass().getSimpleName()).append('('); java.lang.reflect.Parameter[] ps = ctor.getParameters(); for (int i = 0; i < ps.length; i++) { if (i > 0) sb.append(", "); sb.append(prettyParam(ps[i])); } sb.append(')'); return sb.toString(); } private static String prettyParam(java.lang.reflect.Parameter p) { return p.getType().getSimpleName() + " " + safeName(p); } private static String safeName(java.lang.reflect.Parameter p) { try { String n = p.getName(); return n != null ? n : ("arg" + p.getParameterizedType().getTypeName().hashCode()); } catch (Exception e) { return "arg"; } } } ================================================ FILE: src/test/java/redis/clients/jedis/builders/JedisPooledConstructorReflectionTest.java ================================================ package redis.clients.jedis.builders; import static org.junit.jupiter.api.Assertions.*; import java.lang.reflect.Constructor; import java.net.URI; import javax.net.ssl.HostnameVerifier; import javax.net.ssl.SSLParameters; import javax.net.ssl.SSLSocketFactory; import org.apache.commons.pool2.PooledObjectFactory; import org.apache.commons.pool2.impl.GenericObjectPoolConfig; import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.condition.EnabledIfSystemProperty; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import redis.clients.jedis.*; import redis.clients.jedis.csc.Cache; import redis.clients.jedis.csc.CacheConfig; import redis.clients.jedis.executors.CommandExecutor; import redis.clients.jedis.providers.ConnectionProvider; import redis.clients.jedis.providers.PooledConnectionProvider; /** * Reflection-based coverage test that iterates over all public JedisPooled constructors and * validates that each constructor parameter can be provided by either * DefaultJedisClientConfig.builder() or JedisPooled.builder() (directly or via a custom * ConnectionProvider). It reports any uncovered constructors and missing parameters. */ @EnabledIfSystemProperty(named = "with-param-names", matches = "true") public class JedisPooledConstructorReflectionTest { private static final Logger log = LoggerFactory .getLogger(JedisPooledConstructorReflectionTest.class); @Test @DisplayName("Builder coverage of JedisPooled constructors (reflection)") void testConstructorParameterCoverageReport() { Constructor[] ctors = JedisPooled.class.getConstructors(); int total = 0, covered = 0; StringBuilder uncoveredReport = new StringBuilder(); for (Constructor ctor : ctors) { log.info("Testing constructor: {}", ctor); total++; java.lang.reflect.Parameter[] params = ctor.getParameters(); boolean[] paramCovered = new boolean[params.length]; String[] paramCoverageBy = new String[params.length]; String[] paramWhyMissing = new String[params.length]; // Pass 1: mark simple, unambiguous mappings by type/name for (int i = 0; i < params.length; i++) { java.lang.reflect.Parameter p = params[i]; Class t = p.getType(); String name = safeName(p); if (t == HostAndPort.class) { paramCovered[i] = true; paramCoverageBy[i] = "JedisPooled.builder().hostAndPort(HostAndPort)"; } else if (t == URI.class) { paramCovered[i] = true; paramCoverageBy[i] = "JedisPooled.builder().fromURI(URI)"; } else if (t == GenericObjectPoolConfig.class) { paramCovered[i] = true; paramCoverageBy[i] = "JedisPooled.builder().poolConfig(...)"; } else if (t == JedisClientConfig.class) { paramCovered[i] = true; paramCoverageBy[i] = "JedisPooled.builder().clientConfig(DefaultJedisClientConfig...)"; } else if (t == SSLSocketFactory.class) { paramCovered[i] = true; paramCoverageBy[i] = "DefaultJedisClientConfig.builder().sslSocketFactory(...)"; } else if (t == SSLParameters.class) { paramCovered[i] = true; paramCoverageBy[i] = "DefaultJedisClientConfig.builder().sslParameters(...)"; } else if (t == HostnameVerifier.class) { paramCovered[i] = true; paramCoverageBy[i] = "DefaultJedisClientConfig.builder().hostnameVerifier(...)"; } else if (t == Cache.class) { paramCovered[i] = true; paramCoverageBy[i] = "JedisPooled.builder().cache(...)"; } else if (t == CacheConfig.class) { paramCovered[i] = true; paramCoverageBy[i] = "JedisPooled.builder().cacheConfig(...)"; } else if (t == CommandExecutor.class) { paramCovered[i] = true; paramCoverageBy[i] = "JedisPooled.builder().commandExecutor(...)"; } else if (t == RedisProtocol.class) { paramCovered[i] = true; paramCoverageBy[i] = "JedisPooled.builder().redisProtocol(...)"; } else if (t == ConnectionProvider.class || t == PooledConnectionProvider.class || t == PooledObjectFactory.class || t == JedisSocketFactory.class) { // Considered covered via custom provider path paramCovered[i] = true; paramCoverageBy[i] = "Custom ConnectionProvider via JedisPooled.builder().connectionProvider(...)"; } else if (t == String.class) { String lname = name.toLowerCase(); if (lname.contains("url") || lname.contains("uri")) { paramCovered[i] = true; paramCoverageBy[i] = "JedisPooled.builder().fromURI(String)"; } else if (lname.contains("host")) { // Will associate with a port int in pass 2 paramCoverageBy[i] = "JedisPooled.builder().hostAndPort(host,int)"; } else if (lname.contains("user")) { paramCovered[i] = true; paramCoverageBy[i] = "DefaultJedisClientConfig.builder().user(...)"; } else if (lname.contains("pass")) { paramCovered[i] = true; paramCoverageBy[i] = "DefaultJedisClientConfig.builder().password(...)"; } else if (lname.contains("client") && lname.contains("name")) { paramCovered[i] = true; paramCoverageBy[i] = "DefaultJedisClientConfig.builder().clientName(...)"; } else { // Heuristic: if any int param exists, assume this is host to pair with port if (hasType(params, int.class) || hasType(params, Integer.class)) { paramCoverageBy[i] = "JedisPooled.builder().hostAndPort(host,int)"; } else { // Otherwise assume URL paramCovered[i] = true; paramCoverageBy[i] = "JedisPooled.builder().fromURI(String)"; } } } else if (t == int.class || t == Integer.class) { String lname = name.toLowerCase(); if (lname.contains("port")) { // Will be paired with host paramCoverageBy[i] = "JedisPooled.builder().hostAndPort(host,int)"; } else if (lname.contains("db") || lname.contains("database")) { paramCovered[i] = true; paramCoverageBy[i] = "DefaultJedisClientConfig.builder().database(...)"; } else if (lname.contains("conn")) { paramCovered[i] = true; paramCoverageBy[i] = "DefaultJedisClientConfig.builder().connectionTimeoutMillis(...)"; } else if ((lname.contains("so") && lname.contains("timeout")) || lname.equals("sockettimeout")) { paramCovered[i] = true; paramCoverageBy[i] = "DefaultJedisClientConfig.builder().socketTimeoutMillis(...)"; } else if (lname.contains("infinite")) { paramCovered[i] = true; paramCoverageBy[i] = "DefaultJedisClientConfig.builder().blockingSocketTimeoutMillis(...)"; } else if (lname.contains("timeout")) { // Legacy single timeout: maps to both connection and socket timeouts paramCovered[i] = true; paramCoverageBy[i] = "DefaultJedisClientConfig.builder().connectionTimeoutMillis(...)+socketTimeoutMillis(...)"; } else { // Generic: map the first int after host to port if not yet paired, else treat as // timeout paramCoverageBy[i] = "DefaultJedisClientConfig.builder().connectionTimeoutMillis/socketTimeoutMillis(...)"; } } else if (t == boolean.class || t == Boolean.class) { paramCovered[i] = true; paramCoverageBy[i] = "DefaultJedisClientConfig.builder().ssl(...)"; } else if (t == CommandObjects.class) { // Provided internally by builder, not directly configurable paramCovered[i] = true; paramCoverageBy[i] = "Internal (builder-provided CommandObjects)"; } else { paramCovered[i] = false; paramWhyMissing[i] = "No known builder mapping for type: " + t.getSimpleName(); } } // Pass 2: pair host/port if needed int hostIdx = findPotentialHostStringIndex(params); int portIdx = findPortIndex(params); if (hostIdx != -1 && portIdx != -1) { // Both are coverable via hostAndPort paramCovered[hostIdx] = true; if (paramCoverageBy[hostIdx] == null) paramCoverageBy[hostIdx] = "JedisPooled.builder().hostAndPort(host,int)"; paramCovered[portIdx] = true; if (paramCoverageBy[portIdx] == null) paramCoverageBy[portIdx] = "JedisPooled.builder().hostAndPort(host,int)"; } // Evaluate constructor coverage boolean ctorCovered = true; StringBuilder missingParams = new StringBuilder(); for (int i = 0; i < params.length; i++) { if (!paramCovered[i]) { ctorCovered = false; missingParams.append(" - ").append(prettyParam(params[i])).append(" -> ") .append(paramWhyMissing[i] != null ? paramWhyMissing[i] : "unknown").append("\n"); } } if (!ctorCovered) { uncoveredReport.append("\nUncovered constructor: ").append(prettySignature(ctor)) .append("\n"); uncoveredReport.append("Missing parameters:\n").append(missingParams); } else { covered++; } } log.info("Analyzed {} constructors; fully covered: {}", total, covered); if (covered < total) { log.warn("Uncovered constructors detected:{}", uncoveredReport); } assertEquals(total, covered, "Expected all constructors to be covered by builders"); assertTrue(total > 0, "Expected at least one constructor to analyze"); // This test reports coverage; it does not fail on gaps, but logs them clearly. } // ===== Mapping helpers for coverage test ===== private static boolean hasType(java.lang.reflect.Parameter[] params, Class type) { for (java.lang.reflect.Parameter p : params) if (p.getType() == type) return true; return false; } private static int findPortIndex(java.lang.reflect.Parameter[] params) { for (int i = 0; i < params.length; i++) { Class t = params[i].getType(); if (t == int.class || t == Integer.class) { String n = safeName(params[i]).toLowerCase(); if (n.contains("port")) return i; } } // fallback: choose the first int when there is also a host string present int hostIdx = findPotentialHostStringIndex(params); if (hostIdx != -1) { for (int i = 0; i < params.length; i++) if (params[i].getType() == int.class || params[i].getType() == Integer.class) return i; } return -1; } private static int findPotentialHostStringIndex(java.lang.reflect.Parameter[] params) { for (int i = 0; i < params.length; i++) { if (params[i].getType() == String.class) { String n = safeName(params[i]).toLowerCase(); if (n.contains("host")) return i; } } // fallback: if there is an int parameter and exactly one String among (String,int,...), treat // that String as host boolean hasInt = false; int stringCount = 0; int stringIdx = -1; for (int i = 0; i < params.length; i++) { Class t = params[i].getType(); if (t == int.class || t == Integer.class) hasInt = true; if (t == String.class) { stringCount++; stringIdx = i; } } if (hasInt && stringCount == 1) return stringIdx; return -1; } private static String prettySignature(Constructor ctor) { StringBuilder sb = new StringBuilder(); sb.append(ctor.getDeclaringClass().getSimpleName()).append('('); java.lang.reflect.Parameter[] ps = ctor.getParameters(); for (int i = 0; i < ps.length; i++) { if (i > 0) sb.append(", "); sb.append(prettyParam(ps[i])); } sb.append(')'); return sb.toString(); } private static String prettyParam(java.lang.reflect.Parameter p) { return p.getType().getSimpleName() + " " + safeName(p); } private static String safeName(java.lang.reflect.Parameter p) { try { String n = p.getName(); return n != null ? n : ("arg" + p.getParameterizedType().getTypeName().hashCode()); } catch (Exception e) { return "arg"; } } } ================================================ FILE: src/test/java/redis/clients/jedis/builders/JedisSentineledConstructorReflectionTest.java ================================================ package redis.clients.jedis.builders; import static org.junit.jupiter.api.Assertions.*; import java.lang.reflect.Constructor; import java.util.Set; import javax.net.ssl.HostnameVerifier; import javax.net.ssl.SSLParameters; import javax.net.ssl.SSLSocketFactory; import org.apache.commons.pool2.PooledObjectFactory; import org.apache.commons.pool2.impl.GenericObjectPoolConfig; import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.condition.EnabledIfSystemProperty; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import redis.clients.jedis.*; import redis.clients.jedis.csc.Cache; import redis.clients.jedis.csc.CacheConfig; import redis.clients.jedis.executors.CommandExecutor; import redis.clients.jedis.providers.ConnectionProvider; import redis.clients.jedis.providers.SentineledConnectionProvider; /** * Reflection-based coverage test for JedisSentineled constructors against builder API. */ @EnabledIfSystemProperty(named = "with-param-names", matches = "true") public class JedisSentineledConstructorReflectionTest { private static final Logger log = LoggerFactory .getLogger(JedisSentineledConstructorReflectionTest.class); @Test @DisplayName("Builder coverage of JedisSentineled constructors (reflection)") void testConstructorParameterCoverageReport() { Constructor[] ctors = JedisSentineled.class.getConstructors(); int total = 0, covered = 0; StringBuilder uncoveredReport = new StringBuilder(); for (Constructor ctor : ctors) { total++; java.lang.reflect.Parameter[] params = ctor.getParameters(); boolean[] paramCovered = new boolean[params.length]; String[] paramCoverageBy = new String[params.length]; String[] paramWhyMissing = new String[params.length]; for (int i = 0; i < params.length; i++) { java.lang.reflect.Parameter p = params[i]; Class t = p.getType(); String name = safeName(p); if (t == String.class) { String lname = name.toLowerCase(); if (lname.contains("master")) { paramCovered[i] = true; paramCoverageBy[i] = "JedisSentineled.builder().masterName(String)"; } else if (lname.contains("user")) { paramCovered[i] = true; paramCoverageBy[i] = "DefaultJedisClientConfig.builder().user(...)"; } else if (lname.contains("pass")) { paramCovered[i] = true; paramCoverageBy[i] = "DefaultJedisClientConfig.builder().password(...)"; } else if (lname.contains("client") && lname.contains("name")) { paramCovered[i] = true; paramCoverageBy[i] = "DefaultJedisClientConfig.builder().clientName(...)"; } else { paramCovered[i] = true; paramCoverageBy[i] = "DefaultJedisClientConfig.builder().password/clientName/..."; } } else if (t == Set.class) { paramCovered[i] = true; paramCoverageBy[i] = "JedisSentineled.builder().sentinels(Set)"; } else if (t == JedisClientConfig.class) { paramCovered[i] = true; paramCoverageBy[i] = "JedisSentineled.builder().masterClientConfig(...)/sentinelClientConfig(...)"; } else if (t == GenericObjectPoolConfig.class) { paramCovered[i] = true; paramCoverageBy[i] = "JedisSentineled.builder().poolConfig(...)"; } else if (t == Cache.class) { paramCovered[i] = true; paramCoverageBy[i] = "JedisSentineled.builder().cache(...)"; } else if (t == CacheConfig.class) { paramCovered[i] = true; paramCoverageBy[i] = "JedisSentineled.builder().cacheConfig(...)"; } else if (t == int.class || t == Integer.class) { String lname = name.toLowerCase(); if (lname.contains("db") || lname.contains("database")) { paramCovered[i] = true; paramCoverageBy[i] = "DefaultJedisClientConfig.builder().database(...)"; } else if (lname.contains("conn")) { paramCovered[i] = true; paramCoverageBy[i] = "DefaultJedisClientConfig.builder().connectionTimeoutMillis(...)"; } else if ((lname.contains("so") && lname.contains("timeout")) || lname.equals("sockettimeout")) { paramCovered[i] = true; paramCoverageBy[i] = "DefaultJedisClientConfig.builder().socketTimeoutMillis(...)"; } else if (lname.contains("infinite")) { paramCovered[i] = true; paramCoverageBy[i] = "DefaultJedisClientConfig.builder().blockingSocketTimeoutMillis(...)"; } else if (lname.contains("timeout")) { paramCovered[i] = true; paramCoverageBy[i] = "DefaultJedisClientConfig.builder().connectionTimeoutMillis(...)+socketTimeoutMillis(...)"; } } else if (t == boolean.class || t == Boolean.class) { paramCovered[i] = true; paramCoverageBy[i] = "DefaultJedisClientConfig.builder().ssl(...)"; } else if (t == SSLSocketFactory.class) { paramCovered[i] = true; paramCoverageBy[i] = "DefaultJedisClientConfig.builder().sslSocketFactory(...)"; } else if (t == SSLParameters.class) { paramCovered[i] = true; paramCoverageBy[i] = "DefaultJedisClientConfig.builder().sslParameters(...)"; } else if (t == HostnameVerifier.class) { paramCovered[i] = true; paramCoverageBy[i] = "DefaultJedisClientConfig.builder().hostnameVerifier(...)"; } else if (t == ConnectionProvider.class || t == SentineledConnectionProvider.class || t == PooledObjectFactory.class) { paramCovered[i] = true; paramCoverageBy[i] = "Custom ConnectionProvider via builder.connectionProvider(...)"; } else if (t == CommandExecutor.class || t == CommandObjects.class) { paramCovered[i] = true; paramCoverageBy[i] = t == CommandExecutor.class ? "builder.commandExecutor(...)" : "Internal (builder-provided CommandObjects)"; } else if (t == RedisProtocol.class) { paramCovered[i] = true; paramCoverageBy[i] = "builder.redisProtocol(...)"; } else { paramCovered[i] = false; paramWhyMissing[i] = "No known builder mapping for type: " + t.getSimpleName(); } } boolean ctorCovered = true; StringBuilder missingParams = new StringBuilder(); for (int i = 0; i < params.length; i++) { if (!paramCovered[i]) { ctorCovered = false; missingParams.append(" - ").append(prettyParam(params[i])).append(" -> ") .append(paramWhyMissing[i] != null ? paramWhyMissing[i] : "unknown").append("\n"); } } if (!ctorCovered) { uncoveredReport.append("\nUncovered constructor: ").append(prettySignature(ctor)) .append("\n"); uncoveredReport.append("Missing parameters:\n").append(missingParams); } else { covered++; } } log.info("Analyzed {} constructors; fully covered: {}", total, covered); if (covered < total) { log.warn("Uncovered constructors detected:{}", uncoveredReport); } assertEquals(total, covered, "Expected all constructors to be covered by builders"); assertTrue(total > 0, "Expected at least one constructor to analyze"); } private static String prettySignature(Constructor ctor) { StringBuilder sb = new StringBuilder(); sb.append(ctor.getDeclaringClass().getSimpleName()).append('('); java.lang.reflect.Parameter[] ps = ctor.getParameters(); for (int i = 0; i < ps.length; i++) { if (i > 0) sb.append(", "); sb.append(prettyParam(ps[i])); } sb.append(')'); return sb.toString(); } private static String prettyParam(java.lang.reflect.Parameter p) { return p.getType().getSimpleName() + " " + safeName(p); } private static String safeName(java.lang.reflect.Parameter p) { try { String n = p.getName(); return n != null ? n : ("arg" + p.getParameterizedType().getTypeName().hashCode()); } catch (Exception e) { return "arg"; } } } ================================================ FILE: src/test/java/redis/clients/jedis/builders/RedisClusterClientMigrationIntegrationTest.java ================================================ package redis.clients.jedis.builders; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertNotNull; import java.util.HashSet; import java.util.Set; import org.apache.commons.pool2.impl.GenericObjectPoolConfig; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Tag; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.BeforeAll; import redis.clients.jedis.*; /** * Integration test that verifies migration compatibility from the legacy JedisCluster constructor * to the new RedisClusterClient.Builder pattern. *

* This test demonstrates that the following legacy JedisCluster constructor: * *

 * public JedisCluster(Set<HostAndPort> clusterNodes, JedisClientConfig clientConfig, 
 *                     int maxAttempts, GenericObjectPoolConfig<Connection> poolConfig)
 * 
* * can be replaced with the RedisClusterClient.Builder pattern while maintaining the same * functionality. */ @Tag("integration") public class RedisClusterClientMigrationIntegrationTest { private static EndpointConfig endpoint; private static Set CLUSTER_NODES; private static String PASSWORD; private static final int MAX_ATTEMPTS = 3; private JedisCluster legacyCluster; private RedisClusterClient newCluster; @BeforeAll public static void prepareEndpoint() { endpoint = Endpoints.getRedisEndpoint("cluster-stable"); CLUSTER_NODES = new HashSet<>(endpoint.getHostsAndPorts()); PASSWORD = endpoint.getPassword(); } @BeforeEach public void setUp() { // Clean up any existing data before each test cleanClusterData(); } @AfterEach public void tearDown() { // Close connections if (legacyCluster != null) { legacyCluster.close(); legacyCluster = null; } if (newCluster != null) { newCluster.close(); newCluster = null; } // Clean up data after tests cleanClusterData(); } /** * Test that verifies both approaches can handle cluster operations correctly. Tests constructor: * JedisCluster(Set<HostAndPort>, JedisClientConfig, int, * GenericObjectPoolConfig<Connection>) */ @Test public void testSpringDataRedisConstructor() { // Prepare common configuration JedisClientConfig clientConfig = DefaultJedisClientConfig.builder().password(PASSWORD) .socketTimeoutMillis(2000).connectionTimeoutMillis(2000).build(); GenericObjectPoolConfig poolConfig = new ConnectionPoolConfig(); // Create both cluster clients legacyCluster = new JedisCluster(CLUSTER_NODES, clientConfig, MAX_ATTEMPTS, poolConfig); newCluster = RedisClusterClient.builder().nodes(CLUSTER_NODES).clientConfig(clientConfig) .maxAttempts(MAX_ATTEMPTS).poolConfig(poolConfig).build(); verifyBothClients(legacyCluster, newCluster); } /** * Test migration from constructor with username, password, clientName, and SSL. Tests * constructor: JedisCluster(Set<HostAndPort>, int, int, int, String, String, String, * GenericObjectPoolConfig<Connection>, boolean) */ @Test public void testConstructorWithUsernamePasswordClientNameAndSsl() { int connectionTimeout = 2000; int socketTimeout = 2000; String username = "default"; String clientName = "test-client"; boolean ssl = false; // SSL requires special cluster setup GenericObjectPoolConfig poolConfig = new ConnectionPoolConfig(); // Legacy constructor with username legacyCluster = new JedisCluster(CLUSTER_NODES, connectionTimeout, socketTimeout, MAX_ATTEMPTS, username, PASSWORD, clientName, poolConfig, ssl); // New Builder pattern JedisClientConfig clientConfig = DefaultJedisClientConfig.builder() .connectionTimeoutMillis(connectionTimeout).socketTimeoutMillis(socketTimeout) .user(username).password(PASSWORD).clientName(clientName).ssl(ssl).build(); newCluster = RedisClusterClient.builder().nodes(CLUSTER_NODES).clientConfig(clientConfig) .maxAttempts(MAX_ATTEMPTS).poolConfig(poolConfig).build(); verifyBothClients(legacyCluster, newCluster); } /** * Test migration from constructor with password, clientName, and SSL (no username). Tests * constructor: JedisCluster(Set<HostAndPort>, int, int, int, String, String, * GenericObjectPoolConfig<Connection>, boolean) */ @Test public void testConstructorWithPasswordClientNameAndSsl() { int connectionTimeout = 2000; int socketTimeout = 2000; String clientName = "test-client"; boolean ssl = false; // SSL requires special cluster setup GenericObjectPoolConfig poolConfig = new ConnectionPoolConfig(); // Legacy constructor without username legacyCluster = new JedisCluster(CLUSTER_NODES, connectionTimeout, socketTimeout, MAX_ATTEMPTS, PASSWORD, clientName, poolConfig, ssl); // New Builder pattern JedisClientConfig clientConfig = DefaultJedisClientConfig.builder() .connectionTimeoutMillis(connectionTimeout).socketTimeoutMillis(socketTimeout) .password(PASSWORD).clientName(clientName).ssl(ssl).build(); newCluster = RedisClusterClient.builder().nodes(CLUSTER_NODES).clientConfig(clientConfig) .maxAttempts(MAX_ATTEMPTS).poolConfig(poolConfig).build(); verifyBothClients(legacyCluster, newCluster); } /** * Test migration from simple constructor with just nodes and poolConfig. Tests constructor: * JedisCluster(Set<HostAndPort>, GenericObjectPoolConfig<Connection>) */ @Test public void testConstructorWithNodesAndPoolConfig() { GenericObjectPoolConfig poolConfig = new ConnectionPoolConfig(); poolConfig.setMaxTotal(8); poolConfig.setMaxIdle(8); JedisClientConfig clientConfig = DefaultJedisClientConfig.builder().password(PASSWORD).build(); // Legacy constructor legacyCluster = new JedisCluster(CLUSTER_NODES, clientConfig, poolConfig); // New Builder pattern - need to add password via clientConfig newCluster = RedisClusterClient.builder().nodes(CLUSTER_NODES).clientConfig(clientConfig) .poolConfig(poolConfig).build(); verifyBothClients(legacyCluster, newCluster); } /** * Test migration from constructor with connection timeout, socket timeout, maxAttempts, password, * and poolConfig. Tests constructor: JedisCluster(Set<HostAndPort>, int, int, int, String, * GenericObjectPoolConfig<Connection>) */ @Test public void testConstructorWithTimeoutsPasswordAndPoolConfig() { int connectionTimeout = 2000; int socketTimeout = 2000; GenericObjectPoolConfig poolConfig = new ConnectionPoolConfig(); // Legacy constructor legacyCluster = new JedisCluster(CLUSTER_NODES, connectionTimeout, socketTimeout, MAX_ATTEMPTS, PASSWORD, poolConfig); // New Builder pattern JedisClientConfig clientConfig = DefaultJedisClientConfig.builder() .connectionTimeoutMillis(connectionTimeout).socketTimeoutMillis(socketTimeout) .password(PASSWORD).build(); newCluster = RedisClusterClient.builder().nodes(CLUSTER_NODES).clientConfig(clientConfig) .maxAttempts(MAX_ATTEMPTS).poolConfig(poolConfig).build(); verifyBothClients(legacyCluster, newCluster); } /** * Test migration from constructor with timeout, maxAttempts, and poolConfig. Tests constructor: * JedisCluster(Set<HostAndPort>, int, int, GenericObjectPoolConfig<Connection>) */ @Test public void testConstructorWithTimeoutMaxAttemptsAndPoolConfig() { int timeout = 2000; GenericObjectPoolConfig poolConfig = new ConnectionPoolConfig(); JedisClientConfig clientConfig = DefaultJedisClientConfig.builder().timeoutMillis(timeout) .password(PASSWORD).build(); // Legacy constructor - uses same timeout for connection and socket legacyCluster = new JedisCluster(CLUSTER_NODES, timeout, timeout, MAX_ATTEMPTS, PASSWORD, poolConfig); // New Builder pattern newCluster = RedisClusterClient.builder().nodes(CLUSTER_NODES).clientConfig(clientConfig) .maxAttempts(MAX_ATTEMPTS).poolConfig(poolConfig).build(); verifyBothClients(legacyCluster, newCluster); } /** * Test migration from single HostAndPort constructor with poolConfig. Tests constructor: * JedisCluster(HostAndPort, GenericObjectPoolConfig<Connection>) */ @Test public void testConstructorWithSingleNodeAndPoolConfig() { int timeout = 2000; HostAndPort singleNode = endpoint.getHostsAndPorts().get(0); GenericObjectPoolConfig poolConfig = new ConnectionPoolConfig(); JedisClientConfig clientConfig = DefaultJedisClientConfig.builder().password(PASSWORD).build(); // Legacy constructor with single node legacyCluster = new JedisCluster(singleNode, timeout, timeout, MAX_ATTEMPTS, PASSWORD, poolConfig); // New Builder pattern - need to add password and wrap single node in a Set Set singleNodeSet = new HashSet<>(); singleNodeSet.add(singleNode); newCluster = RedisClusterClient.builder().nodes(singleNodeSet).clientConfig(clientConfig) .poolConfig(poolConfig).build(); verifyBothClients(legacyCluster, newCluster); } protected void verifyBothClients(JedisCluster legacyCluster, RedisClusterClient newCluster) { // Verify both discovered the cluster nodes assertNotNull(legacyCluster.getClusterNodes(), "Legacy cluster should discover cluster nodes"); assertNotNull(newCluster.getClusterNodes(), "New cluster should discover cluster nodes"); // Both should have discovered the same number of nodes assertEquals(legacyCluster.getClusterNodes().size(), newCluster.getClusterNodes().size(), "Both approaches should discover the same number of cluster nodes"); // Test basic string operations String key1 = "test-string-key"; legacyCluster.set(key1, "value1"); assertEquals("value1", newCluster.get(key1)); // Test increment operations String key2 = "test-counter-key"; legacyCluster.set(key2, "0"); newCluster.incr(key2); assertEquals("1", legacyCluster.get(key2)); // Clean up newCluster.del(key1); newCluster.del(key2); } /** * Helper method to clean up cluster data before and after tests. */ private void cleanClusterData() { // Connect to each stable cluster node and flush data for (HostAndPort node : endpoint.getHostsAndPorts()) { try (redis.clients.jedis.Jedis jedis = new redis.clients.jedis.Jedis(node)) { jedis.auth(PASSWORD); jedis.flushDB(); } catch (Exception e) { // Ignore errors during cleanup } } } } ================================================ FILE: src/test/java/redis/clients/jedis/builders/UnifiedJedisConstructorReflectionTest.java ================================================ package redis.clients.jedis.builders; import static org.junit.jupiter.api.Assertions.*; import java.lang.reflect.Constructor; import java.net.URI; import java.util.Set; import javax.net.ssl.HostnameVerifier; import javax.net.ssl.SSLParameters; import javax.net.ssl.SSLSocketFactory; import org.apache.commons.pool2.PooledObjectFactory; import org.apache.commons.pool2.impl.GenericObjectPoolConfig; import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.condition.EnabledIfSystemProperty; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import redis.clients.jedis.*; import redis.clients.jedis.csc.Cache; import redis.clients.jedis.csc.CacheConfig; import redis.clients.jedis.executors.CommandExecutor; import redis.clients.jedis.providers.ConnectionProvider; /** * Reflection-based coverage test for UnifiedJedis constructors against builder API. Since * UnifiedJedis is the base, we treat many low-level components as covered via custom * ConnectionProvider or builder-provided internals. */ @EnabledIfSystemProperty(named = "with-param-names", matches = "true") public class UnifiedJedisConstructorReflectionTest { private static final Logger log = LoggerFactory .getLogger(UnifiedJedisConstructorReflectionTest.class); @Test @DisplayName("Builder coverage of UnifiedJedis constructors (reflection)") void testConstructorParameterCoverageReport() { Constructor[] ctors = UnifiedJedis.class.getConstructors(); int total = 0, covered = 0; StringBuilder uncoveredReport = new StringBuilder(); for (Constructor ctor : ctors) { if (isUnsafeConstructor(ctor) || clusterConstructorThatShouldBeDeprecatedAndRemoved(ctor) || retriesConstructorThatShouldBeIncorporatedIntoBuilderAsDefault(ctor) || multiDbConnectionProviderShouldBeReplacedWithMultiDbClient(ctor)) { // Exclude unsafe constructors from analysis as requested continue; } total++; java.lang.reflect.Parameter[] params = ctor.getParameters(); boolean[] paramCovered = new boolean[params.length]; String[] paramCoverageBy = new String[params.length]; String[] paramWhyMissing = new String[params.length]; for (int i = 0; i < params.length; i++) { java.lang.reflect.Parameter p = params[i]; Class t = p.getType(); String name = safeName(p); if (t == HostAndPort.class) { paramCovered[i] = true; paramCoverageBy[i] = "JedisPooled.builder().hostAndPort(HostAndPort)"; } else if (t == URI.class) { paramCovered[i] = true; paramCoverageBy[i] = "builder().fromURI(URI)"; } else if (t == ConnectionProvider.class) { paramCovered[i] = true; paramCoverageBy[i] = "Custom ConnectionProvider via builder.connectionProvider(...)"; } else if (t == GenericObjectPoolConfig.class) { paramCovered[i] = true; paramCoverageBy[i] = "builder.poolConfig(...) (in concrete builders)"; } else if (t == JedisClientConfig.class) { paramCovered[i] = true; paramCoverageBy[i] = "builder.clientConfig(DefaultJedisClientConfig...)"; } else if (t == Cache.class || t == CacheConfig.class) { paramCovered[i] = true; paramCoverageBy[i] = t == Cache.class ? "builder.cache(...)" : "builder.cacheConfig(...)"; } else if (t == CommandExecutor.class || t == CommandObjects.class) { paramCovered[i] = true; paramCoverageBy[i] = t == CommandExecutor.class ? "builder.commandExecutor(...)" : "Internal (builder-provided CommandObjects)"; } else if (t == RedisProtocol.class) { paramCovered[i] = true; paramCoverageBy[i] = "builder.redisProtocol(...)"; } else if (t == SSLSocketFactory.class) { paramCovered[i] = true; paramCoverageBy[i] = "DefaultJedisClientConfig.builder().sslSocketFactory(...)"; } else if (t == SSLParameters.class) { paramCovered[i] = true; paramCoverageBy[i] = "DefaultJedisClientConfig.builder().sslParameters(...)"; } else if (t == HostnameVerifier.class) { paramCovered[i] = true; paramCoverageBy[i] = "DefaultJedisClientConfig.builder().hostnameVerifier(...)"; } else if (t == String.class) { String lname = name.toLowerCase(); if (lname.contains("url") || lname.contains("uri")) { paramCovered[i] = true; paramCoverageBy[i] = "JedisPooled.builder().fromURI(String)"; } else { paramCovered[i] = false; paramWhyMissing[i] = "No known builder mapping for type: " + t.getSimpleName(); } } else if (t == int.class || t == Integer.class) { String lname = name.toLowerCase(); if (lname.contains("timeout")) { paramCovered[i] = true; paramCoverageBy[i] = "DefaultJedisClientConfig.builder().connectionTimeoutMillis/socketTimeoutMillis/blockingSocketTimeoutMillis(...)"; } else if (lname.contains("attempt")) { paramCovered[i] = true; paramCoverageBy[i] = "Cluster/Sentinel builders manage attempts"; } else if (lname.contains("db") || lname.contains("database")) { paramCovered[i] = true; paramCoverageBy[i] = "DefaultJedisClientConfig.builder().database(...)"; } } else if (t == boolean.class || t == Boolean.class) { paramCovered[i] = true; paramCoverageBy[i] = "DefaultJedisClientConfig.builder().ssl(...)"; } else if (t == PooledObjectFactory.class) { paramCovered[i] = true; paramCoverageBy[i] = "Custom provider via PooledObjectFactory in concrete builders"; } else { paramCovered[i] = false; paramWhyMissing[i] = "No known builder mapping for type: " + t.getSimpleName(); } } boolean ctorCovered = true; StringBuilder missingParams = new StringBuilder(); for (int i = 0; i < params.length; i++) { if (!paramCovered[i]) { ctorCovered = false; missingParams.append(" - ").append(prettyParam(params[i])).append(" -> ") .append(paramWhyMissing[i] != null ? paramWhyMissing[i] : "unknown").append("\n"); } } if (!ctorCovered) { uncoveredReport.append("\nUncovered constructor: ").append(prettySignature(ctor)) .append("\n"); uncoveredReport.append("Missing parameters:\n").append(missingParams); } else { covered++; } } log.info("Analyzed {} constructors; fully covered: {}", total, covered); if (covered < total) { log.warn("Uncovered constructors detected:{}", uncoveredReport); } assertEquals(total, covered, "Expected all constructors to be covered by builders"); assertTrue(total > 0, "Expected at least one constructor to analyze"); } private static boolean isUnsafeConstructor(Constructor ctor) { Class[] types = ctor.getParameterTypes(); for (Class t : types) { if (t == Connection.class || t == JedisSocketFactory.class) { return true; } } return false; } // FIXME: Remove this when we use command executor with retries by default private static boolean retriesConstructorThatShouldBeIncorporatedIntoBuilderAsDefault( Constructor ctor) { return ctor.toString().equals( "public redis.clients.jedis.UnifiedJedis(redis.clients.jedis.providers.ConnectionProvider,int,java.time.Duration)"); } // FIXME: Remove this when we remove the deprecated Cluster-related constructors from UnifiedJedis private static boolean clusterConstructorThatShouldBeDeprecatedAndRemoved(Constructor ctor) { Class[] types = ctor.getParameterTypes(); return types.length > 1 && ((types[0] == Set.class && types[1] == JedisClientConfig.class) || types[0].getSimpleName().equals("ClusterConnectionProvider")); } // FIXME: Remove this when we add convince class and builder for ResilientClient private static boolean multiDbConnectionProviderShouldBeReplacedWithMultiDbClient( Constructor ctor) { Class[] types = ctor.getParameterTypes(); return types.length == 1 && types[0].getSimpleName().equals("MultiDbConnectionProvider"); } private static String prettySignature(Constructor ctor) { StringBuilder sb = new StringBuilder(); sb.append(ctor.getDeclaringClass().getSimpleName()).append('('); java.lang.reflect.Parameter[] ps = ctor.getParameters(); for (int i = 0; i < ps.length; i++) { if (i > 0) sb.append(", "); sb.append(prettyParam(ps[i])); } sb.append(')'); return sb.toString(); } private static String prettyParam(java.lang.reflect.Parameter p) { return p.getType().getSimpleName() + " " + safeName(p); } private static String safeName(java.lang.reflect.Parameter p) { try { String n = p.getName(); return n != null ? n : ("arg" + p.getParameterizedType().getTypeName().hashCode()); } catch (Exception e) { return "arg"; } } } ================================================ FILE: src/test/java/redis/clients/jedis/codegen/CommandFlagsRegistryGenerator.java ================================================ package redis.clients.jedis.codegen; import com.google.gson.Gson; import com.google.gson.reflect.TypeToken; import redis.clients.jedis.Endpoints; import redis.clients.jedis.Jedis; import redis.clients.jedis.Module; import redis.clients.jedis.exceptions.JedisConnectionException; import redis.clients.jedis.resps.CommandInfo; import java.io.IOException; import java.lang.reflect.Type; import java.nio.charset.StandardCharsets; import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.Paths; import java.util.*; import java.util.stream.Collectors; /** * Code generator for StaticCommandFlagsRegistry. This generator connects to a Redis server, * retrieves all command metadata using the COMMAND command, and automatically generates the * StaticCommandFlagsRegistry class that implements CommandFlagsRegistry interface. *

* Usage: * *

 * java -cp ... redis.clients.jedis.codegen.CommandFlagsRegistryGenerator [host] [port]
 * 
*

* Arguments: *

    *
  • host - Redis server hostname (default: localhost)
  • *
  • port - Redis server port (default: 6379)
  • *
*

* Note: This is a code generation tool and should NOT be executed as part of regular tests. */ public class CommandFlagsRegistryGenerator { private static final String JAVA_FILE = "src/main/java/redis/clients/jedis/StaticCommandFlagsRegistryInitializer.java"; private static final String BACKUP_JSON_FILE = "redis_commands_metadata.json"; private final String redisHost; private final int redisPort; // Server metadata collected during generation private ServerMetadata serverMetadata; // Map JSON flag names to Java enum names private static final Map FLAG_MAPPING = new LinkedHashMap<>(); static { FLAG_MAPPING.put("readonly", "READONLY"); FLAG_MAPPING.put("write", "WRITE"); FLAG_MAPPING.put("denyoom", "DENYOOM"); FLAG_MAPPING.put("admin", "ADMIN"); FLAG_MAPPING.put("pubsub", "PUBSUB"); FLAG_MAPPING.put("noscript", "NOSCRIPT"); FLAG_MAPPING.put("random", "RANDOM"); FLAG_MAPPING.put("sort_for_script", "SORT_FOR_SCRIPT"); FLAG_MAPPING.put("loading", "LOADING"); FLAG_MAPPING.put("stale", "STALE"); FLAG_MAPPING.put("skip_monitor", "SKIP_MONITOR"); FLAG_MAPPING.put("skip_slowlog", "SKIP_SLOWLOG"); FLAG_MAPPING.put("asking", "ASKING"); FLAG_MAPPING.put("fast", "FAST"); FLAG_MAPPING.put("movablekeys", "MOVABLEKEYS"); FLAG_MAPPING.put("module", "MODULE"); FLAG_MAPPING.put("blocking", "BLOCKING"); FLAG_MAPPING.put("no_auth", "NO_AUTH"); FLAG_MAPPING.put("no_async_loading", "NO_ASYNC_LOADING"); FLAG_MAPPING.put("no_multi", "NO_MULTI"); FLAG_MAPPING.put("no_mandatory_keys", "NO_MANDATORY_KEYS"); FLAG_MAPPING.put("allow_busy", "ALLOW_BUSY"); } /** * Manual command flag overrides that take precedence over Redis server metadata. These overrides * allow defining custom flag combinations, request policies, and response policies for specific * commands when the server-provided metadata is incorrect or needs customization. *

* Key: Command name (uppercase, e.g., "KEYS" or "ACL CAT" for subcommands) Value: CommandMetadata * with the override values *

* To add a new override, add an entry to this map in the static initializer block. */ private static final Map MANUAL_OVERRIDES = new LinkedHashMap<>(); static { // Override INFO: change request policy from ALL_SHARDS to DEFAULT // Reason: SPECIAL response policy not yet supported in the library and defaults to return // single result INFO should be executed on a single node, not broadcast to all shards MANUAL_OVERRIDES.put("INFO", new CommandMetadata(Arrays.asList("loading", "stale"), "default", "special")); // Override FUNCTION STATS: change request policy from ALL_SHARDS to DEFAULT // Reason: SPECIAL response policy not yet supported in the library and defaults to return // single result FUNCTION STATS should be executed on a single node, not broadcast to all shards MANUAL_OVERRIDES.put("FUNCTION STATS", new CommandMetadata(Arrays.asList("noscript", "allow_busy"), "default", "special")); } /** * Known parent commands that have subcommands. When the generator encounters a command name * containing a space (e.g., "FUNCTION LOAD"), the parent part (e.g., "FUNCTION") must be listed * here for proper subcommand registration. *

* If a new Redis command with subcommands is added, add the parent command name to this set. The * generator will fail with a helpful error message if an unknown parent command is encountered. */ private static final Set KNOWN_PARENT_COMMANDS = new HashSet<>(Arrays.asList("ACL", "CLIENT", "CLUSTER", "COMMAND", "CONFIG", "FUNCTION", "HOTKEYS", "LATENCY", "MEMORY", "MODULE", "OBJECT", "PUBSUB", "SCRIPT", "SLOWLOG", "XGROUP", "XINFO", "FT.CONFIG", "FT.CURSOR")); public CommandFlagsRegistryGenerator(String host, int port) { this.redisHost = host; this.redisPort = port; } public static void main(String[] args) { printLine(); System.out.println("StaticCommandFlagsRegistry Generator"); printLine(); // Parse command line arguments String host = args.length > 0 ? args[0] : "localhost"; int port = args.length > 1 ? Integer.parseInt(args[1]) : 6379; System.out.println("Redis server: " + host + ":" + port); System.out.println(); try { CommandFlagsRegistryGenerator generator = new CommandFlagsRegistryGenerator(host, port); generator.generate(); System.out.println(); printLine(); System.out.println("✓ Code generation completed successfully!"); printLine(); } catch (Exception e) { System.err.println(); printLine(); System.err.println("✗ Code generation failed!"); printLine(); e.printStackTrace(); System.exit(1); } } private static void printLine() { for (int i = 0; i < 80; i++) { System.out.print("="); } System.out.println(); } public void generate() throws IOException { Map commandsMetadata; // Step 1: Retrieve commands from Redis System.out.println("\nStep 1: Connecting to Redis at " + redisHost + ":" + redisPort + "..."); try { commandsMetadata = retrieveCommandsFromRedis(); System.out.println("✓ Retrieved " + commandsMetadata.size() + " commands from Redis"); // Save to backup JSON file saveToJsonFile(commandsMetadata); } catch (JedisConnectionException e) { System.err.println("✗ Failed to connect to Redis: " + e.getMessage()); System.out.println("\nAttempting to use backup JSON file: " + BACKUP_JSON_FILE); commandsMetadata = readJsonFile(); System.out.println("✓ Loaded " + commandsMetadata.size() + " commands from backup file"); } // Step 2: Process commands and group by metadata combinations System.out.println("\nStep 2: Processing commands and grouping by metadata..."); Map> metadataCombinations = groupByMetadata(commandsMetadata); System.out.println("✓ Found " + metadataCombinations.size() + " unique metadata combinations"); // Step 3: Generate StaticCommandFlagsRegistry class System.out.println("\nStep 3: Generating StaticCommandFlagsRegistry class..."); String classContent = generateRegistryClass(metadataCombinations); System.out.println("✓ Generated " + classContent.split("\n").length + " lines of code"); // Step 4: Write StaticCommandFlagsRegistry.java System.out.println("\nStep 4: Writing " + JAVA_FILE + "..."); writeJavaFile(classContent); System.out.println("✓ Successfully created StaticCommandFlagsRegistry.java"); } private Map retrieveCommandsFromRedis() { Map result = new LinkedHashMap<>(); try (Jedis jedis = new Jedis(redisHost, redisPort)) { jedis.connect(); jedis.auth(Endpoints.getRedisEndpoint("standalone0").getPassword()); // Collect server metadata String infoServer = jedis.info("server"); String version = extractInfoValue(infoServer, "redis_version"); String mode = extractInfoValue(infoServer, "redis_mode"); // Get loaded modules List modules = new ArrayList<>(); try { List moduleList = jedis.moduleList(); for (Module module : moduleList) { modules.add(module.getName()); } } catch (Exception e) { // Module list might not be available in all Redis versions System.out.println(" Note: Could not retrieve module list: " + e.getMessage()); } serverMetadata = new ServerMetadata(version, mode, modules); // Get all commands using COMMAND Map commands = jedis.command(); for (Map.Entry entry : commands.entrySet()) { CommandInfo cmdInfo = entry.getValue(); String commandName = normalizeCommandName(cmdInfo.getName()); // Check for subcommands Map subcommands = cmdInfo.getSubcommands(); if (subcommands != null && !subcommands.isEmpty()) { // This command has subcommands - add parent command with its flags first if (!shouldExcludeCommand(commandName)) { CommandMetadata parentMetadata = extractCommandMetadata(cmdInfo); result.put(commandName, parentMetadata); } // Then process subcommands for (Map.Entry subEntry : subcommands.entrySet()) { CommandInfo subCmdInfo = subEntry.getValue(); String subCommandName = normalizeCommandName(subCmdInfo.getName()); // Filter out unwanted commands if (shouldExcludeCommand(subCommandName)) { continue; } CommandMetadata metadata = extractCommandMetadata(subCmdInfo); result.put(subCommandName, metadata); } } else { // Regular command without subcommands // Filter out unwanted commands if (!shouldExcludeCommand(commandName)) { CommandMetadata metadata = extractCommandMetadata(cmdInfo); result.put(commandName, metadata); } } } } // Ignore close errors return result; } /** * Extract command metadata (flags, request_policy, response_policy) from CommandInfo. */ private CommandMetadata extractCommandMetadata(CommandInfo cmdInfo) { // Get flags List flags = new ArrayList<>(); if (cmdInfo.getFlags() != null) { for (String flag : cmdInfo.getFlags()) { flags.add(flag.toLowerCase()); } } // Extract request_policy and response_policy from tips String requestPolicy = null; String responsePolicy = null; List tips = cmdInfo.getTips(); if (tips != null) { for (String tip : tips) { String tipLower = tip.toLowerCase(); if (tipLower.startsWith("request_policy:")) { requestPolicy = tipLower.substring("request_policy:".length()); } else if (tipLower.startsWith("response_policy:")) { responsePolicy = tipLower.substring("response_policy:".length()); } } } return new CommandMetadata(flags, requestPolicy, responsePolicy); } /** * Normalize command name: replace pipe separators with spaces and convert to uppercase. Redis * returns command names like "acl|help" but Jedis uses "ACL HELP". */ private String normalizeCommandName(String commandName) { return commandName.replace('|', ' ').toUpperCase(); } /** * Check if a command should be excluded from the registry. *

* Exclusion rules: *

    *
  • All HELP subcommands (e.g., "ACL HELP", "CONFIG HELP", "XINFO HELP")
  • *
  • All FT.DEBUG subcommands (e.g., "FT.DEBUG DUMP_TERMS", "FT.DEBUG GIT_SHA")
  • *
  • All _FT.DEBUG subcommands (internal RediSearch debug commands)
  • *
*/ private boolean shouldExcludeCommand(String commandName) { // Exclude all HELP subcommands if (commandName.endsWith(" HELP")) { return true; } // Exclude FT.DEBUG and _FT.DEBUG subcommands return commandName.startsWith("FT.DEBUG ") || commandName.startsWith("_FT.DEBUG ") || commandName.startsWith("_FT.CONFIG "); } private String extractInfoValue(String info, String key) { String[] lines = info.split("\n"); for (String line : lines) { if (line.startsWith(key + ":")) { return line.substring(key.length() + 1).trim(); } } return "unknown"; } private void saveToJsonFile(Map commandsMetadata) throws IOException { Gson gson = new Gson(); String json = gson.toJson(commandsMetadata); Path jsonPath = Paths.get(BACKUP_JSON_FILE); Files.write(jsonPath, json.getBytes(StandardCharsets.UTF_8)); System.out.println("✓ Saved backup to " + BACKUP_JSON_FILE); } private Map readJsonFile() throws IOException { Path jsonPath = Paths.get(BACKUP_JSON_FILE); if (!Files.exists(jsonPath)) { throw new IOException("Backup file not found: " + BACKUP_JSON_FILE); } // JDK 8 compatible: read file as bytes and convert to string byte[] bytes = Files.readAllBytes(jsonPath); String jsonContent = new String(bytes, StandardCharsets.UTF_8); Gson gson = new Gson(); // Parse JSON with proper type Type type = new TypeToken>() { }.getType(); Map parsed = gson.fromJson(jsonContent, type); return new LinkedHashMap<>(parsed); } private Map> groupByMetadata( Map commandsMetadata) { Map> result = new LinkedHashMap<>(); for (Map.Entry entry : commandsMetadata.entrySet()) { String command = entry.getKey(); String commandUpper = command.toUpperCase(); // Check for manual override first - overrides take precedence over server metadata CommandMetadata metadata; if (MANUAL_OVERRIDES.containsKey(commandUpper)) { metadata = MANUAL_OVERRIDES.get(commandUpper); System.out.println(" Applying manual override for command: " + commandUpper); } else { metadata = entry.getValue(); } // Convert JSON flags to Java enum names and sort List javaFlags = metadata.flags.stream().map(f -> FLAG_MAPPING.get(f.toLowerCase())) .filter(Objects::nonNull).sorted().collect(Collectors.toList()); // Convert request and response policies to Java enum names (uppercase) String requestPolicy = metadata.requestPolicy != null ? metadata.requestPolicy.toUpperCase() : null; String responsePolicy = metadata.responsePolicy != null ? metadata.responsePolicy.toUpperCase() : null; MetadataKey key = new MetadataKey(javaFlags, requestPolicy, responsePolicy); result.computeIfAbsent(key, k -> new ArrayList<>()).add(commandUpper); } return result; } private String generateRegistryClass(Map> metadataCombinations) { StringBuilder sb = new StringBuilder(); // Package and imports sb.append("package redis.clients.jedis;\n\n"); sb.append("import java.util.EnumSet;\n"); sb.append("import static redis.clients.jedis.StaticCommandFlagsRegistry.EMPTY_FLAGS;\n"); sb.append("import static redis.clients.jedis.CommandFlagsRegistry.CommandFlag;\n"); sb.append("import static redis.clients.jedis.CommandFlagsRegistry.RequestPolicy;\n"); sb.append("import static redis.clients.jedis.CommandFlagsRegistry.ResponsePolicy;\n"); // Class javadoc sb.append("/**\n"); sb.append( " * Static implementation of CommandFlagsRegistry. This class is auto-generated by\n"); sb.append(" * CommandFlagsRegistryGenerator. DO NOT EDIT MANUALLY.\n"); // Add server metadata if available if (serverMetadata != null) { sb.append(" *

Generated from Redis Server:\n"); sb.append(" *

    \n"); sb.append(" *
  • Version: ").append(serverMetadata.version).append("
  • \n"); sb.append(" *
  • Mode: ").append(serverMetadata.mode).append("
  • \n"); if (!serverMetadata.modules.isEmpty()) { sb.append(" *
  • Loaded Modules: ").append(String.join(", ", serverMetadata.modules)) .append("
  • \n"); } else { sb.append(" *
  • Loaded Modules: none
  • \n"); } sb.append(" *
  • Generated at: ").append(serverMetadata.generatedAt).append("
  • \n"); sb.append(" *
\n"); } sb.append(" */\n"); sb.append("final class StaticCommandFlagsRegistryInitializer {\n\n"); // Static initializer block sb.append(" static void initialize(StaticCommandFlagsRegistry.Builder builder) {\n"); // Organize commands into parent commands and simple commands Map> parentCommands = new LinkedHashMap<>(); Map simpleCommands = new LinkedHashMap<>(); // Categorize commands for (Map.Entry> entry : metadataCombinations.entrySet()) { MetadataKey metadataKey = entry.getKey(); for (String command : entry.getValue()) { int spaceIndex = command.indexOf(' '); if (spaceIndex > 0) { // This is a compound command (e.g., "FUNCTION LOAD") String parent = command.substring(0, spaceIndex); String subcommand = command.substring(spaceIndex + 1); if (KNOWN_PARENT_COMMANDS.contains(parent)) { parentCommands.computeIfAbsent(parent, k -> new LinkedHashMap<>()).put(subcommand, metadataKey); } else { // Unknown parent command with subcommands - fail with helpful error message throw new IllegalStateException(String.format( "Unknown command with subcommands encountered: '%s' (parent: '%s', subcommand: '%s'). " + "Command names cannot contain spaces unless the parent command is registered. " + "To fix this, add '%s' to the 'KNOWN_PARENT_COMMANDS' set in CommandFlagsRegistryGenerator.java.", command, parent, subcommand, parent)); } } else { // Simple command without subcommands simpleCommands.put(command, metadataKey); } } } // Generate parent command registries for (String parent : KNOWN_PARENT_COMMANDS) { // Use parent command's actual metadata if available, otherwise use EMPTY_FLAGS MetadataKey parentMetadata = simpleCommands.remove(parent); if (parentMetadata != null) { sb.append(generateRegisterCall(parent, null, parentMetadata)); } else { sb.append(String.format(" builder.register(\"%s\", EMPTY_FLAGS);\n", parent)); } Map subcommands = parentCommands.get(parent); if (subcommands != null && !subcommands.isEmpty()) { sb.append(String.format(" // %s subcommands\n", parent)); // Add subcommands List sortedSubcommands = new ArrayList<>(subcommands.keySet()); Collections.sort(sortedSubcommands); for (String subcommand : sortedSubcommands) { MetadataKey metadataKey = subcommands.get(subcommand); sb.append(generateRegisterCall(parent, subcommand, metadataKey)); } } } // Generate simple commands grouped by metadata Map> simpleCommandsByMetadata = new LinkedHashMap<>(); for (Map.Entry entry : simpleCommands.entrySet()) { simpleCommandsByMetadata.computeIfAbsent(entry.getValue(), k -> new ArrayList<>()) .add(entry.getKey()); } // Sort by flag count, then alphabetically List>> sortedEntries = simpleCommandsByMetadata.entrySet() .stream() .sorted( Comparator.comparing((Map.Entry> e) -> e.getKey().flags.size()) .thenComparing(e -> e.getKey().toString())) .collect(Collectors.toList()); for (Map.Entry> entry : sortedEntries) { MetadataKey metadataKey = entry.getKey(); List commands = entry.getValue(); Collections.sort(commands); // Add comment describing the metadata sb.append(String.format(" // %d command(s) with: %s\n", commands.size(), metadataKey.toDescription())); // Add registry entries for (String command : commands) { sb.append(generateRegisterCall(command, null, metadataKey)); } sb.append("\n"); } // Close initializer block sb.append(" }\n\n"); // Close class sb.append("}\n"); return sb.toString(); } /** * Generate a builder.register() call for a command with its metadata. */ private String generateRegisterCall(String command, String subcommand, MetadataKey metadataKey) { String enumSetExpr = createEnumSetExpression(metadataKey.flags); String requestPolicyExpr = metadataKey.requestPolicy != null ? "RequestPolicy." + metadataKey.requestPolicy : "null"; String responsePolicyExpr = metadataKey.responsePolicy != null ? "ResponsePolicy." + metadataKey.responsePolicy : "null"; // Check if we need to use the extended register method (with policies) boolean hasPolicies = metadataKey.requestPolicy != null || metadataKey.responsePolicy != null; if (subcommand != null) { // Subcommand registration if (hasPolicies) { return String.format(" builder.register(\"%s\", \"%s\", %s, %s, %s);\n", command, subcommand, enumSetExpr, requestPolicyExpr, responsePolicyExpr); } else { return String.format(" builder.register(\"%s\", \"%s\", %s);\n", command, subcommand, enumSetExpr); } } else { // Simple command registration if (hasPolicies) { return String.format(" builder.register(\"%s\", %s, %s, %s);\n", command, enumSetExpr, requestPolicyExpr, responsePolicyExpr); } else { return String.format(" builder.register(\"%s\", %s);\n", command, enumSetExpr); } } } private String createEnumSetExpression(List flags) { if (flags.isEmpty()) { return "EMPTY_FLAGS"; } else if (flags.size() == 1) { return "EnumSet.of(CommandFlag." + flags.get(0) + ")"; } else { String flagsList = flags.stream().map(f -> "CommandFlag." + f) .collect(Collectors.joining(", ")); return "EnumSet.of(" + flagsList + ")"; } } private void writeJavaFile(String classContent) throws IOException { Path javaPath = Paths.get(JAVA_FILE); // JDK 8 compatible: write string as bytes Files.write(javaPath, classContent.getBytes(StandardCharsets.UTF_8)); } /** * Holds command metadata extracted from Redis (flags, request_policy, response_policy). Used for * JSON serialization/deserialization. */ private static class CommandMetadata { final List flags; final String requestPolicy; final String responsePolicy; CommandMetadata(List flags, String requestPolicy, String responsePolicy) { this.flags = flags != null ? flags : new ArrayList<>(); this.requestPolicy = requestPolicy; this.responsePolicy = responsePolicy; } } /** * Represents a unique combination of flags, request policy, and response policy for grouping * commands. */ private static class MetadataKey { final List flags; final String requestPolicy; final String responsePolicy; final int hashCode; MetadataKey(List flags, String requestPolicy, String responsePolicy) { this.flags = new ArrayList<>(flags); this.requestPolicy = requestPolicy; this.responsePolicy = responsePolicy; this.hashCode = Objects.hash(this.flags, requestPolicy, responsePolicy); } @Override public boolean equals(Object o) { if (this == o) return true; if (!(o instanceof MetadataKey)) return false; MetadataKey that = (MetadataKey) o; return flags.equals(that.flags) && Objects.equals(requestPolicy, that.requestPolicy) && Objects.equals(responsePolicy, that.responsePolicy); } @Override public int hashCode() { return hashCode; } @Override public String toString() { return String.format("flags=%s, request=%s, response=%s", flags, requestPolicy, responsePolicy); } /** * Generate a human-readable description for comments. */ String toDescription() { StringBuilder sb = new StringBuilder(); if (flags.isEmpty()) { sb.append("no flags"); } else { sb.append(flags.stream().map(String::toLowerCase).collect(Collectors.joining(", "))); } if (requestPolicy != null) { sb.append("; request_policy=").append(requestPolicy.toLowerCase()); } if (responsePolicy != null) { sb.append("; response_policy=").append(responsePolicy.toLowerCase()); } return sb.toString(); } } /** * Holds metadata about the Redis server used for generation */ private static class ServerMetadata { final String version; final String mode; final List modules; final String generatedAt; ServerMetadata(String version, String mode, List modules) { this.version = version; this.mode = mode; this.modules = modules; this.generatedAt = new java.text.SimpleDateFormat("yyyy-MM-dd HH:mm:ss z") .format(new java.util.Date()); } } } ================================================ FILE: src/test/java/redis/clients/jedis/codegen/README.md ================================================ # Code Generators This package contains code generation tools for the Jedis project. These are **not tests** and should not be executed as part of the test suite. ## CommandFlagsRegistryGenerator Automatically generates and updates the static flags registry in `CommandObject.java` by retrieving command metadata from a running Redis server. ### Purpose The `CommandObject` class uses a static registry to map Redis command names to their flags. ### How It Works 1. **Connects** to a Redis server (default: localhost:6379) 2. **Retrieves** all command metadata using the `COMMAND` command 3. **Processes** commands and subcommands, extracting their flags 4. **Groups** commands by their flag combinations 5. **Generates** a static initializer block with inline `EnumSet` creation 6. **Updates** `CommandObject.java` automatically using regex pattern matching 7. **Saves** a backup JSON file for offline use ### Prerequisites - A running Redis server (version 7.0+ recommended for full command metadata) - The Redis server should have all modules loaded if you want to include module commands ### When to Run Run this generator whenever: - Upgrading to a new Redis version - New Redis modules are added to your server - Command flags are modified in Redis - You want to ensure the registry is up-to-date with your Redis server ### Fallback Mode If the generator cannot connect to Redis, it will automatically fall back to using the backup JSON file (`redis_commands_flags.json`) if available. ### Output The generator will: - ✓ Connect to Redis and retrieve command metadata - ✓ Process commands and subcommands - ✓ Group commands by flag combinations - ✓ Generate the complete static initializer block - ✓ Update `src/main/java/redis/clients/jedis/CommandObject.java` in-place - ✓ Save a backup JSON file for offline use - ✓ Preserve original command names (with spaces, dots, hyphens) ================================================ FILE: src/test/java/redis/clients/jedis/collections/JedisByteHashMapTest.java ================================================ package redis.clients.jedis.collections; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.ObjectInputStream; import java.io.ObjectOutputStream; import java.util.Arrays; import java.util.HashMap; import java.util.Map; import java.util.Map.Entry; import java.util.Set; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import redis.clients.jedis.util.JedisByteHashMap; import redis.clients.jedis.util.JedisByteMap; import static org.junit.jupiter.api.Assertions.assertArrayEquals; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertTrue; public class JedisByteHashMapTest { private static JedisByteHashMap map = new JedisByteHashMap(); private static JedisByteMap map2 = new JedisByteMap<>(); private byte[][] keys = { { 'k', 'e', 'y', '1' }, { 'k', 'e', 'y', '2' }, { 'k', 'e', 'y', '3' } }; private byte[][] vals = { { 'v', 'a', 'l', '1' }, { 'v', 'a', 'l', '2' }, { 'v', 'a', 'l', '3' } }; @BeforeEach public void before() throws Exception { map.clear(); map2.clear(); } private boolean arrayContainsKey(byte[][] arr, byte[] key) { for (byte[] anArr : arr) { if (Arrays.equals(anArr, key)) { return true; } } return false; } private boolean entryContainsKV(Set> s, byte[] key, byte[] value) { for (Map.Entry en : s) { if (Arrays.equals(en.getKey(), key) && Arrays.equals(en.getValue(), value)) { return true; } } return false; } private boolean entrySetSame(Set> s1, Set> s2) { for (Map.Entry en1 : s1) { if (!entryContainsKV(s2, en1.getKey(), en1.getValue())) { return false; } } for (Map.Entry en2 : s2) { if (!entryContainsKV(s1, en2.getKey(), en2.getValue())) { return false; } } return true; } @Test public void mapOperations() { // put map.put(keys[0], vals[0]); assertEquals(1, map.size()); // putAll Map kvMap = new HashMap<>(); kvMap.put(keys[1], vals[1]); kvMap.put(keys[2], vals[2]); map.putAll(kvMap); assertEquals(3, map.size()); // containsKey assertTrue(map.containsKey(keys[0])); // containsValue assertTrue(map.containsValue(vals[0])); // entrySet Set> entries = map.entrySet(); assertEquals(3, entries.size()); for (Entry entry : entries) { assertTrue(arrayContainsKey(keys, entry.getKey())); assertTrue(arrayContainsKey(vals, entry.getValue())); } // get assertArrayEquals(vals[0], map.get(keys[0])); // isEmpty assertFalse(map.isEmpty()); // keySet for (byte[] key : map.keySet()) { assertTrue(arrayContainsKey(keys, key)); } // values for (byte[] value : map.values()) { assertTrue(arrayContainsKey(vals, value)); } // remove map.remove(keys[0]); assertEquals(2, map.size()); // clear map.clear(); assertEquals(0, map.size()); } @Test public void serialize() throws Exception { for (int i = 0; i < keys.length; i++) { map.put(keys[i], vals[i]); } ByteArrayOutputStream byteOut = new ByteArrayOutputStream(); ObjectOutputStream objOut = new ObjectOutputStream(byteOut); objOut.writeObject(map); ByteArrayInputStream byteIn = new ByteArrayInputStream(byteOut.toByteArray()); ObjectInputStream objIn = new ObjectInputStream(byteIn); JedisByteHashMap mapRead = (JedisByteHashMap) objIn.readObject(); assertTrue(entrySetSame(map.entrySet(), mapRead.entrySet())); } @Test public void map2Operations() { // put map2.put(keys[0], vals[0]); assertEquals(1, map2.size()); // putAll Map kvMap = new HashMap<>(); kvMap.put(keys[1], vals[1]); kvMap.put(keys[2], vals[2]); map2.putAll(kvMap); assertEquals(3, map2.size()); // containsKey assertTrue(map2.containsKey(keys[0])); // containsValue assertTrue(map2.containsValue(vals[0])); // entrySet Set> entries = map2.entrySet(); assertEquals(3, entries.size()); for (Map.Entry entry : entries) { assertTrue(arrayContainsKey(keys, entry.getKey())); assertTrue(arrayContainsKey(vals, entry.getValue())); } // get assertArrayEquals(vals[0], map2.get(keys[0])); // isEmpty assertFalse(map2.isEmpty()); // keySet for (byte[] key : map2.keySet()) { assertTrue(arrayContainsKey(keys, key)); } // values for (byte[] value : map2.values()) { assertTrue(arrayContainsKey(vals, value)); } // remove map2.remove(keys[0]); assertEquals(2, map2.size()); // clear map2.clear(); assertEquals(0, map2.size()); } @Test public void serialize2() throws Exception { for (int i = 0; i < keys.length; i++) { map2.put(keys[i], vals[i]); } ByteArrayOutputStream byteOut = new ByteArrayOutputStream(); ObjectOutputStream objOut = new ObjectOutputStream(byteOut); objOut.writeObject(map2); ByteArrayInputStream byteIn = new ByteArrayInputStream(byteOut.toByteArray()); ObjectInputStream objIn = new ObjectInputStream(byteIn); JedisByteMap mapRead = (JedisByteMap) objIn.readObject(); assertTrue(entrySetSame(map2.entrySet(), mapRead.entrySet())); } } ================================================ FILE: src/test/java/redis/clients/jedis/collections/SetFromListTest.java ================================================ package redis.clients.jedis.collections; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.Test; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.ObjectInputStream; import java.io.ObjectOutputStream; import java.lang.reflect.Method; import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; import java.util.HashSet; import java.util.List; import java.util.Set; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertTrue; public class SetFromListTest { private static Method method; @BeforeAll public static void beforeClass() throws Exception { Class clazz = Class.forName("redis.clients.jedis.BuilderFactory$SetFromList"); method = clazz.getDeclaredMethod("of", List.class); method.setAccessible(true); } /** * Instantiate SetFromList class by reflection because it is protected static inner class of * BinaryJedis. */ @SuppressWarnings("unchecked") private Set setFromList(List list) throws Exception { return (Set) method.invoke(null, list); } @Test public void setOperations() throws Exception { // add Set cut = setFromList(new ArrayList()); cut.add("A"); cut.add("B"); cut.add("A"); assertEquals(2, cut.size()); // remove cut.remove("A"); assertEquals(1, cut.size()); cut.remove("C"); assertEquals(1, cut.size()); // contains assertTrue(cut.contains("B")); assertFalse(cut.contains("A")); cut.add("C"); cut.add("D"); // containsAll assertTrue(cut.containsAll(cut)); // retainAll cut.retainAll(Arrays.asList("C", "D")); assertEquals(2, cut.size()); assertTrue(cut.contains("C")); assertTrue(cut.contains("D")); // removeAll cut.removeAll(Arrays.asList("C")); assertEquals(1, cut.size()); assertTrue(cut.contains("D")); // clear cut.clear(); assertTrue(cut.isEmpty()); } @Test public void iteration() throws Exception { List list = a2z(); Set cut = setFromList(list); // ordering guarantee int i = 0; for (String x : cut) { assertEquals(list.get(i++), x); } } @Test public void equals() throws Exception { List list = a2z(); Set hashSet = new HashSet(list); Set cut = setFromList(list); assertEquals(hashSet, cut); } @Test public void serialize() throws Exception { Set set = setFromList(a2z()); ByteArrayOutputStream byteOut = new ByteArrayOutputStream(); ObjectOutputStream objOut = new ObjectOutputStream(byteOut); objOut.writeObject(set); ByteArrayInputStream byteIn = new ByteArrayInputStream(byteOut.toByteArray()); ObjectInputStream objIn = new ObjectInputStream(byteIn); Set setRead = (Set) objIn.readObject(); assertEquals(set, setRead); } private List a2z() { List list = new ArrayList(); for (int i = 'a'; i <= 'z'; i++) { list.add(String.valueOf((char) i)); } Collections.shuffle(list); return list; } } ================================================ FILE: src/test/java/redis/clients/jedis/commands/CommandsTestsParameters.java ================================================ package redis.clients.jedis.commands; import java.util.Arrays; import java.util.Collection; import redis.clients.jedis.RedisProtocol; public class CommandsTestsParameters { /** * RESP protocol versions we want our commands related tests to run against. * {@code null} means to use the default protocol which is assumed to be RESP2. */ public static Collection respVersions() { return Arrays.asList( new Object[]{ null }, new Object[]{ RedisProtocol.RESP2 }, new Object[]{ RedisProtocol.RESP3 } ); } } ================================================ FILE: src/test/java/redis/clients/jedis/commands/commandobjects/CommandObjectsBitmapCommandsTest.java ================================================ package redis.clients.jedis.commands.commandobjects; import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.contains; import static org.hamcrest.Matchers.equalTo; import java.util.List; import io.redis.test.annotations.SinceRedisVersion; import io.redis.test.annotations.ConditionalOnEnv; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Tag; import redis.clients.jedis.RedisProtocol; import redis.clients.jedis.args.BitCountOption; import redis.clients.jedis.args.BitOP; import redis.clients.jedis.params.BitPosParams; import redis.clients.jedis.util.TestEnvUtil; /** * Tests related to Bitmap commands. */ @Tag("integration") public class CommandObjectsBitmapCommandsTest extends CommandObjectsStandaloneTestBase { public CommandObjectsBitmapCommandsTest(RedisProtocol protocol) { super(protocol); } @Test public void testSetbitAndGetbit() { String key = "bitKey"; long offset = 10; Boolean initialValue = exec(commandObjects.getbit(key, offset)); assertThat(initialValue, equalTo(false)); Boolean setbit = exec(commandObjects.setbit(key, offset, true)); assertThat(setbit, equalTo(false)); // original value returned Boolean finalValue = exec(commandObjects.getbit(key, offset)); assertThat(finalValue, equalTo(true)); } @Test public void testSetbitAndGetbitBinary() { byte[] key = "bitKeyBytes".getBytes(); long offset = 10; Boolean initialValue = exec(commandObjects.getbit(key, offset)); assertThat(initialValue, equalTo(false)); Boolean setbit = exec(commandObjects.setbit(key, offset, true)); assertThat(setbit, equalTo(false)); // original value returned Boolean finalValue = exec(commandObjects.getbit(key, offset)); assertThat(finalValue, equalTo(true)); } @Test @SinceRedisVersion(value = "7.0.0", message = "Starting with Redis version 7.0.0: Added the BYTE|BIT option.") public void testBitcount() { String key = "bitcountKey"; byte[] keyBytes = key.getBytes(); // Set some bits exec(commandObjects.setbit(key, 1, true)); exec(commandObjects.setbit(key, 2, true)); exec(commandObjects.setbit(key, 7, true)); // This makes 1 byte with 3 bits set exec(commandObjects.setbit(key, 8, true)); // Next byte, first bit set Long bitcountFullString = exec(commandObjects.bitcount(key)); assertThat(bitcountFullString, equalTo(4L)); Long bitcountFirstByte = exec(commandObjects.bitcount(key, 0, 0)); assertThat(bitcountFirstByte, equalTo(3L)); Long bitcountFullStringBinary = exec(commandObjects.bitcount(keyBytes)); assertThat(bitcountFullStringBinary, equalTo(4L)); Long bitcountFirstByteBinary = exec(commandObjects.bitcount(keyBytes, 0, 0)); assertThat(bitcountFirstByteBinary, equalTo(3L)); Long bitcountFirstSixBits = exec(commandObjects.bitcount(key, 0, 5, BitCountOption.BIT)); assertThat(bitcountFirstSixBits, equalTo(2L)); Long bitcountFirstSixBitsBinary = exec(commandObjects.bitcount(keyBytes, 0, 5, BitCountOption.BIT)); assertThat(bitcountFirstSixBitsBinary, equalTo(2L)); } @Test @SinceRedisVersion(value = "7.0.0", message="Starting with Redis version 7.0.0: Added the BYTE|BIT option.") public void testBitpos() { String key = "bitposKey"; byte[] keyBytes = key.getBytes(); // Set some bits exec(commandObjects.setbit(key, 10, true)); exec(commandObjects.setbit(key, 22, true)); exec(commandObjects.setbit(key, 30, true)); Long firstSetBit = exec(commandObjects.bitpos(key, true)); assertThat(firstSetBit, equalTo(10L)); Long firstUnsetBit = exec(commandObjects.bitpos(key, false)); assertThat(firstUnsetBit, equalTo(0L)); BitPosParams params = new BitPosParams(15, 25).modifier(BitCountOption.BIT); Long firstSetBitInRange = exec(commandObjects.bitpos(key, true, params)); assertThat(firstSetBitInRange, equalTo(22L)); Long firstUnsetBitInRange = exec(commandObjects.bitpos(key, false, params)); assertThat(firstUnsetBitInRange, equalTo(15L)); Long firstSetBitBinary = exec(commandObjects.bitpos(keyBytes, true)); assertThat(firstSetBitBinary, equalTo(10L)); Long firstUnsetBitBinary = exec(commandObjects.bitpos(keyBytes, false)); assertThat(firstUnsetBitBinary, equalTo(0L)); Long firstSetBitInRangeBinary = exec(commandObjects.bitpos(keyBytes, true, params)); assertThat(firstSetBitInRangeBinary, equalTo(22L)); Long firstUnsetBitInRangeBinary = exec(commandObjects.bitpos(keyBytes, false, params)); assertThat(firstUnsetBitInRangeBinary, equalTo(15L)); } @Test public void testBitfield() { String key = "bitfieldKey"; List bitfieldResult = exec(commandObjects.bitfield( key, "INCRBY", "i5", "100", "7", "GET", "i5", "100")); // Contains the result of the INCRBY operation, and the result of the GET operation. assertThat(bitfieldResult, contains(7L, 7L)); List bitfieldRoResult = exec(commandObjects.bitfieldReadonly( key, "GET", "i4", "100")); assertThat(bitfieldRoResult, contains(3L)); } @Test public void testBitfieldBinary() { byte[] key = "bitfieldKeyBytes".getBytes(); List bitfieldResult = exec(commandObjects.bitfield(key, "INCRBY".getBytes(), "i5".getBytes(), "100".getBytes(), "7".getBytes(), "GET".getBytes(), "i5".getBytes(), "100".getBytes())); // Contains the result of the INCRBY operation, and the result of the GET operation. assertThat(bitfieldResult, contains(7L, 7L)); List bitfieldRoResult = exec(commandObjects.bitfieldReadonly(key, "GET".getBytes(), "i4".getBytes(), "100".getBytes())); assertThat(bitfieldRoResult, contains(3L)); } @Test @ConditionalOnEnv(value = TestEnvUtil.ENV_REDIS_ENTERPRISE, enabled = false) public void testBitop() { String srcKey1 = "srcKey1"; String srcKey2 = "srcKey2"; String destKey = "destKey"; // Set some bits exec(commandObjects.setbit(srcKey1, 1, true)); exec(commandObjects.setbit(srcKey1, 2, true)); exec(commandObjects.setbit(srcKey1, 3, true)); exec(commandObjects.setbit(srcKey2, 1, true)); exec(commandObjects.setbit(srcKey2, 3, true)); Long bitopResult = exec(commandObjects.bitop(BitOP.AND, destKey, srcKey1, srcKey2)); assertThat(bitopResult, equalTo(1L)); // 1 byte stored assertThat(exec(commandObjects.getbit(destKey, 1)), equalTo(true)); assertThat(exec(commandObjects.getbit(destKey, 2)), equalTo(false)); assertThat(exec(commandObjects.getbit(destKey, 3)), equalTo(true)); } @Test @ConditionalOnEnv(value = TestEnvUtil.ENV_REDIS_ENTERPRISE, enabled = false) public void testBitopBinary() { byte[] srcKey1 = "srcKey1".getBytes(); byte[] srcKey2 = "srcKey2".getBytes(); byte[] destKey = "destKey".getBytes(); // Set some bits exec(commandObjects.setbit(srcKey1, 1, true)); exec(commandObjects.setbit(srcKey1, 2, true)); exec(commandObjects.setbit(srcKey1, 3, true)); exec(commandObjects.setbit(srcKey2, 1, true)); exec(commandObjects.setbit(srcKey2, 3, true)); Long bitopResult = exec(commandObjects.bitop(BitOP.XOR, destKey, srcKey1, srcKey2)); assertThat(bitopResult, equalTo(1L)); // 1 byte stored assertThat(exec(commandObjects.getbit(new String(destKey), 1)), equalTo(false)); assertThat(exec(commandObjects.getbit(new String(destKey), 2)), equalTo(true)); assertThat(exec(commandObjects.getbit(new String(destKey), 3)), equalTo(false)); } } ================================================ FILE: src/test/java/redis/clients/jedis/commands/commandobjects/CommandObjectsBloomFilterCommandsTest.java ================================================ package redis.clients.jedis.commands.commandobjects; import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.contains; import static org.hamcrest.Matchers.equalTo; import static org.hamcrest.Matchers.hasEntry; import static org.hamcrest.Matchers.notNullValue; import java.util.List; import java.util.Map; import org.junit.jupiter.api.Test; import redis.clients.jedis.RedisProtocol; import redis.clients.jedis.bloom.BFInsertParams; import redis.clients.jedis.bloom.BFReserveParams; /** * Tests related to Bloom Filter commands. */ public class CommandObjectsBloomFilterCommandsTest extends CommandObjectsModulesTestBase { public CommandObjectsBloomFilterCommandsTest(RedisProtocol protocol) { super(protocol); } @Test public void testBfAddAndExists() { String key = "testBf"; String reserve = exec(commandObjects.bfReserve(key, 0.01, 1000)); assertThat(reserve, equalTo("OK")); boolean add = exec(commandObjects.bfAdd(key, "item1")); assertThat(add, equalTo(true)); boolean exists = exec(commandObjects.bfExists(key, "item1")); assertThat(exists, equalTo(true)); boolean notExists = exec(commandObjects.bfExists(key, "item2")); assertThat(notExists, equalTo(false)); } @Test public void testBfInsert() { String key = "testBf"; String reserve = exec(commandObjects.bfReserve(key, 0.01, 1000)); assertThat(reserve, equalTo("OK")); List insert = exec(commandObjects.bfInsert(key, "item1", "item2")); assertThat(insert, contains(true, true)); BFInsertParams insertParams = new BFInsertParams().noCreate().capacity(1000); List insertWithParams = exec(commandObjects.bfInsert(key, insertParams, "item1", "item2")); assertThat(insertWithParams, contains(false, false)); assertThat(exec(commandObjects.bfExists(key, "item1")), equalTo(true)); assertThat(exec(commandObjects.bfExists(key, "item2")), equalTo(true)); assertThat(exec(commandObjects.bfExists(key, "item3")), equalTo(false)); } @Test public void testBfMAddMExistsAndCard() { String key = "testBf"; String reserve = exec(commandObjects.bfReserve(key, 0.01, 1000)); assertThat(reserve, equalTo("OK")); List mAdd = exec(commandObjects.bfMAdd(key, "item1", "item2", "item3")); assertThat(mAdd, contains(true, true, true)); List mExists = exec(commandObjects.bfMExists(key, "item1", "item2", "item3", "item4")); assertThat(mExists, contains(true, true, true, false)); Long card = exec(commandObjects.bfCard(key)); assertThat(card, equalTo(3L)); } @Test public void testBfScanDumpAndLoadChunk() { String key = "test"; String reserve = exec(commandObjects.bfReserve(key, 0.01, 5000)); assertThat(reserve, equalTo("OK")); for (int i = 0; i < 1000; i++) { Boolean add = exec(commandObjects.bfAdd(key, "item" + i)); assertThat(add, equalTo(true)); } String newKey = "testBfLoadChunk"; long iterator = 0; do { Map.Entry scanDumpResult = exec(commandObjects.bfScanDump(key, iterator)); iterator = scanDumpResult.getKey(); if (iterator > 0) { byte[] data = scanDumpResult.getValue(); assertThat(data, notNullValue()); String loadChunk = exec(commandObjects.bfLoadChunk(newKey, iterator, data)); assertThat(loadChunk, equalTo("OK")); } } while (iterator != 0); // verify destination for (int i = 0; i < 1000; i++) { Boolean exists = exec(commandObjects.bfExists(newKey, "item" + i)); assertThat(exists, equalTo(true)); } Boolean missingItem = exec(commandObjects.bfExists(newKey, "item1001")); assertThat(missingItem, equalTo(false)); } @Test public void testBfInfo() { String key = "testBf"; double errorRate = 0.01; long capacity = 1000; BFReserveParams reserveParams = new BFReserveParams().expansion(2); String reserve = exec(commandObjects.bfReserve(key, errorRate, capacity, reserveParams)); assertThat(reserve, equalTo("OK")); Boolean add = exec(commandObjects.bfAdd(key, "item1")); assertThat(add, equalTo(true)); Map info = exec(commandObjects.bfInfo(key)); assertThat(info, hasEntry("Capacity", 1000L)); assertThat(info, hasEntry("Number of items inserted", 1L)); } } ================================================ FILE: src/test/java/redis/clients/jedis/commands/commandobjects/CommandObjectsCountMinSketchCommandsTest.java ================================================ package redis.clients.jedis.commands.commandobjects; import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.containsInAnyOrder; import static org.hamcrest.Matchers.equalTo; import static org.hamcrest.Matchers.greaterThanOrEqualTo; import static org.hamcrest.Matchers.hasEntry; import static org.hamcrest.Matchers.hasSize; import static org.hamcrest.Matchers.lessThanOrEqualTo; import static org.hamcrest.Matchers.notNullValue; import java.util.HashMap; import java.util.List; import java.util.Map; import org.junit.jupiter.api.Test; import redis.clients.jedis.RedisProtocol; /** * Tests related to Count-min sketch commands. */ public class CommandObjectsCountMinSketchCommandsTest extends CommandObjectsModulesTestBase { public CommandObjectsCountMinSketchCommandsTest(RedisProtocol protocol) { super(protocol); } @Test public void testIncrByAndQuery() { String key = "testCMS"; String init = exec(commandObjects.cmsInitByDim(key, 10000, 5)); assertThat(init, equalTo("OK")); Map itemIncrements = new HashMap<>(); itemIncrements.put("apple", 30L); itemIncrements.put("banana", 20L); itemIncrements.put("carrot", 10L); List incrBy = exec(commandObjects.cmsIncrBy(key, itemIncrements)); // due to Map's unpredictable order, we can't assert ordering of the result assertThat(incrBy, containsInAnyOrder(10L, 20L, 30L)); List query = exec(commandObjects.cmsQuery(key, "apple", "banana", "carrot", "date")); assertThat(query, notNullValue()); assertThat(query.size(), equalTo(4)); assertThat(query.get(0), greaterThanOrEqualTo(30L)); // apple assertThat(query.get(1), greaterThanOrEqualTo(20L)); // banana assertThat(query.get(2), greaterThanOrEqualTo(10L)); // carrot assertThat(query.get(3), lessThanOrEqualTo(1L)); // date, in practice, could be >0 due to estimation error } @Test public void testCMSInitByProb() { String key = "testCMS"; String init = exec(commandObjects.cmsInitByProb(key, 0.01, 0.99)); assertThat(init, equalTo("OK")); Map itemIncrements = new HashMap<>(); itemIncrements.put("apple", 5L); itemIncrements.put("banana", 3L); itemIncrements.put("carrot", 8L); List incrBy = exec(commandObjects.cmsIncrBy(key, itemIncrements)); assertThat(incrBy, containsInAnyOrder(3L, 5L, 8L)); List query = exec(commandObjects.cmsQuery(key, "apple", "banana", "carrot", "dragonfruit")); assertThat(query, notNullValue()); assertThat(query.size(), equalTo(4)); assertThat(query.get(0), greaterThanOrEqualTo(5L)); // apple assertThat(query.get(1), greaterThanOrEqualTo(3L)); // banana assertThat(query.get(2), greaterThanOrEqualTo(8L)); // carrot // "dragonfruit" was not incremented, its count should be minimal, but due to the probabilistic nature of CMS, it might not be exactly 0. assertThat(query.get(3), lessThanOrEqualTo(1L)); } @Test public void testCMSMerge() { String cmsKey1 = "testCMS1"; String cmsKey2 = "testCMS2"; String cmsDestKey = "testCMSMerged"; long width = 10000; long depth = 5; String init1 = exec(commandObjects.cmsInitByDim(cmsKey1, width, depth)); assertThat(init1, equalTo("OK")); String init2 = exec(commandObjects.cmsInitByDim(cmsKey2, width, depth)); assertThat(init2, equalTo("OK")); Map itemIncrements1 = new HashMap<>(); itemIncrements1.put("apple", 2L); itemIncrements1.put("banana", 3L); List incrBy1 = exec(commandObjects.cmsIncrBy(cmsKey1, itemIncrements1)); assertThat(incrBy1, containsInAnyOrder(2L, 3L)); Map itemIncrements2 = new HashMap<>(); itemIncrements2.put("carrot", 5L); itemIncrements2.put("date", 4L); List incrBy2 = exec(commandObjects.cmsIncrBy(cmsKey2, itemIncrements2)); assertThat(incrBy2, containsInAnyOrder(4L, 5L)); String init3 = exec(commandObjects.cmsInitByDim(cmsDestKey, width, depth)); assertThat(init3, equalTo("OK")); String merge = exec(commandObjects.cmsMerge(cmsDestKey, cmsKey1, cmsKey2)); assertThat(merge, equalTo("OK")); List query = exec(commandObjects.cmsQuery(cmsDestKey, "apple", "banana", "carrot", "date")); assertThat(query, notNullValue()); assertThat(query.size(), equalTo(4)); assertThat(query.get(0), greaterThanOrEqualTo(2L)); // apple assertThat(query.get(1), greaterThanOrEqualTo(3L)); // banana assertThat(query.get(2), greaterThanOrEqualTo(5L)); // carrot assertThat(query.get(3), greaterThanOrEqualTo(4L)); // date } @Test public void testCMSMergeWithWeights() { String cmsKey1 = "testCMS1"; String cmsKey2 = "testCMS2"; String cmsDestKey = "testCMSMerged"; long width = 10000; long depth = 5; String init1 = exec(commandObjects.cmsInitByDim(cmsKey1, width, depth)); assertThat(init1, equalTo("OK")); String init2 = exec(commandObjects.cmsInitByDim(cmsKey2, width, depth)); assertThat(init2, equalTo("OK")); Map itemIncrements1 = new HashMap<>(); itemIncrements1.put("apple", 2L); itemIncrements1.put("banana", 3L); List incrBy1 = exec(commandObjects.cmsIncrBy(cmsKey1, itemIncrements1)); assertThat(incrBy1, containsInAnyOrder(2L, 3L)); Map itemIncrements2 = new HashMap<>(); itemIncrements2.put("carrot", 5L); itemIncrements2.put("date", 4L); List incrBy2 = exec(commandObjects.cmsIncrBy(cmsKey2, itemIncrements2)); assertThat(incrBy2, containsInAnyOrder(4L, 5L)); String init3 = exec(commandObjects.cmsInitByDim(cmsDestKey, width, depth)); assertThat(init3, equalTo("OK")); // Weights for the CMS keys to be merged Map keysAndWeights = new HashMap<>(); keysAndWeights.put(cmsKey1, 1L); keysAndWeights.put(cmsKey2, 2L); String merge = exec(commandObjects.cmsMerge(cmsDestKey, keysAndWeights)); assertThat(merge, equalTo("OK")); List query = exec(commandObjects.cmsQuery(cmsDestKey, "apple", "banana", "carrot", "date")); assertThat(query, notNullValue()); assertThat(query.size(), equalTo(4)); assertThat(query.get(0), greaterThanOrEqualTo(2L)); // apple, weight of 1 assertThat(query.get(1), greaterThanOrEqualTo(3L)); // banana, weight of 1 assertThat(query.get(2), greaterThanOrEqualTo(10L)); // carrot, weight of 2, so 5 * 2 assertThat(query.get(3), greaterThanOrEqualTo(8L)); // date, weight of 2, so 4 * 2 } @Test public void testCMSInfo() { String key = "testCMS"; long width = 10000; long depth = 5; String init = exec(commandObjects.cmsInitByDim(key, width, depth)); assertThat(init, equalTo("OK")); Map itemIncrements = new HashMap<>(); itemIncrements.put("apple", 3L); itemIncrements.put("banana", 2L); itemIncrements.put("carrot", 1L); List incrBy = exec(commandObjects.cmsIncrBy(key, itemIncrements)); assertThat(incrBy, hasSize(3)); Map info = exec(commandObjects.cmsInfo(key)); assertThat(info, hasEntry("width", 10000L)); assertThat(info, hasEntry("depth", 5L)); assertThat(info, hasEntry("count", 6L)); // 3 + 2 + 1 } } ================================================ FILE: src/test/java/redis/clients/jedis/commands/commandobjects/CommandObjectsCuckooFilterCommandsTest.java ================================================ package redis.clients.jedis.commands.commandobjects; import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.contains; import static org.hamcrest.Matchers.empty; import static org.hamcrest.Matchers.equalTo; import static org.hamcrest.Matchers.everyItem; import static org.hamcrest.Matchers.greaterThanOrEqualTo; import static org.hamcrest.Matchers.hasEntry; import static org.hamcrest.Matchers.not; import static org.hamcrest.Matchers.notNullValue; import java.util.List; import java.util.Map; import org.junit.jupiter.api.Test; import redis.clients.jedis.RedisProtocol; import redis.clients.jedis.bloom.CFInsertParams; import redis.clients.jedis.bloom.CFReserveParams; /** * Tests related to Cuckoo filter commands. */ public class CommandObjectsCuckooFilterCommandsTest extends CommandObjectsModulesTestBase { public CommandObjectsCuckooFilterCommandsTest(RedisProtocol protocol) { super(protocol); } @Test public void testCuckooFilterAdd() { String key = "testCuckooFilter"; String reserve = exec(commandObjects.cfReserve(key, 1000)); assertThat(reserve, equalTo("OK")); Boolean add = exec(commandObjects.cfAdd(key, "apple")); assertThat(add, equalTo(true)); Boolean addNx = exec(commandObjects.cfAddNx(key, "apple")); assertThat(addNx, equalTo(false)); // "apple" already exists, NX makes this fail Boolean addNx2 = exec(commandObjects.cfAddNx(key, "banana")); assertThat(addNx2, equalTo(true)); Long count = exec(commandObjects.cfCount(key, "apple")); assertThat(count, greaterThanOrEqualTo(1L)); } @Test public void testCuckooFilterReserveInsertAndCount() { String key = "testCuckooFilterAdvanced"; CFReserveParams reserveParams = new CFReserveParams() .bucketSize(4).maxIterations(500).expansion(1); String reserve = exec(commandObjects.cfReserve(key, 5000, reserveParams)); assertThat(reserve, equalTo("OK")); List insert = exec(commandObjects.cfInsert( key, "apple", "banana", "carrot", "date")); assertThat(insert, everyItem(equalTo(true))); CFInsertParams insertParams = new CFInsertParams().noCreate(); List insertWithParams = exec(commandObjects.cfInsert( key, insertParams, "eggplant", "fig", "grape", "apple")); assertThat(insertWithParams, everyItem(equalTo(true))); Long countApple = exec(commandObjects.cfCount(key, "apple")); assertThat(countApple, greaterThanOrEqualTo(2L)); Long countBanana = exec(commandObjects.cfCount(key, "banana")); assertThat(countBanana, greaterThanOrEqualTo(1L)); Long countNonExisting = exec(commandObjects.cfCount(key, "watermelon")); assertThat(countNonExisting, equalTo(0L)); } @Test public void testCuckooFilterInsertNx() { String key = "testCf"; String[] items = { "item1", "item2", "item3" }; CFInsertParams insertParams = new CFInsertParams().capacity(1000L).noCreate(); List insertNx1 = exec(commandObjects.cfInsertNx(key, items)); assertThat(insertNx1, not(empty())); assertThat(insertNx1, everyItem(equalTo(true))); long countAfterFirstInsert = exec(commandObjects.cfCount(key, "item1")); assertThat(countAfterFirstInsert, greaterThanOrEqualTo(1L)); List insertNx2 = exec(commandObjects.cfInsertNx(key, insertParams, items)); assertThat(insertNx2, not(empty())); assertThat(insertNx2, everyItem(equalTo(false))); long countAfterSecondInsert = exec(commandObjects.cfCount(key, "item1")); assertThat(countAfterSecondInsert, greaterThanOrEqualTo(1L)); // count should remain the same } @Test public void testCuckooFilterExistsAndDel() { String key = "testCf"; String item = "item1"; boolean existsBeforeInsert = exec(commandObjects.cfExists(key, item)); assertThat(existsBeforeInsert, equalTo(false)); Boolean add = exec(commandObjects.cfAdd(key, item)); assertThat(add, equalTo(true)); boolean existsAfterInsert = exec(commandObjects.cfExists(key, item)); assertThat(existsAfterInsert, equalTo(true)); boolean delete = exec(commandObjects.cfDel(key, item)); assertThat(delete, equalTo(true)); boolean existsAfterDelete = exec(commandObjects.cfExists(key, item)); assertThat(existsAfterDelete, equalTo(false)); } @Test public void testCuckooFilterMExists() { String key = "testCf"; exec(commandObjects.cfInsert(key, "item1", "item2", "item3")); List mExists = exec(commandObjects.cfMExists( key, "item1", "item2", "item3", "item4", "item5")); assertThat(mExists, contains(true, true, true, false, false)); } @Test public void testCuckooFilterScanDumpAndLoadChunk() { long capacity = 5000; CFReserveParams reserveParams = new CFReserveParams() .bucketSize(4).maxIterations(500).expansion(1); String key = "testCf"; String reserve = exec(commandObjects.cfReserve(key, capacity, reserveParams)); assertThat(reserve, equalTo("OK")); // add some items to the source for (int i = 0; i < 1000; i++) { exec(commandObjects.cfAdd(key, "item" + i)); } String newKey = "testCfLoadChunk"; // scandump and load long iterator = 0; do { Map.Entry scanDumpResult = exec(commandObjects.cfScanDump(key, iterator)); iterator = scanDumpResult.getKey(); if (iterator > 0) { byte[] data = scanDumpResult.getValue(); assertThat(data, notNullValue()); String loadChunk = exec(commandObjects.cfLoadChunk(newKey, iterator, data)); assertThat(loadChunk, equalTo("OK")); } } while (iterator != 0); // verify destination for (int i = 0; i < 1000; i++) { boolean exists = exec(commandObjects.cfExists(newKey, "item" + i)); assertThat(exists, equalTo(true)); } boolean missingItem = exec(commandObjects.cfExists(newKey, "item1001")); assertThat(missingItem, equalTo(false)); } @Test public void testCuckooFilterInfo() { String key = "testCfInfo"; exec(commandObjects.cfReserve(key, 1000)); exec(commandObjects.cfAdd(key, "item1")); Map info = exec(commandObjects.cfInfo(key)); assertThat(info, hasEntry("Number of items inserted", 1L)); } } ================================================ FILE: src/test/java/redis/clients/jedis/commands/commandobjects/CommandObjectsGenericCommandsTest.java ================================================ package redis.clients.jedis.commands.commandobjects; import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.anyOf; import static org.hamcrest.Matchers.contains; import static org.hamcrest.Matchers.containsInAnyOrder; import static org.hamcrest.Matchers.empty; import static org.hamcrest.Matchers.equalTo; import static org.hamcrest.Matchers.greaterThan; import static org.hamcrest.Matchers.greaterThanOrEqualTo; import static org.hamcrest.Matchers.hasItem; import static org.hamcrest.Matchers.hasItems; import static org.hamcrest.Matchers.not; import static org.hamcrest.Matchers.notNullValue; import static org.hamcrest.Matchers.nullValue; import java.util.Arrays; import java.util.HashSet; import java.util.List; import java.util.Set; import io.redis.test.annotations.SinceRedisVersion; import io.redis.test.annotations.ConditionalOnEnv; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Tag; import redis.clients.jedis.Jedis; import redis.clients.jedis.RedisProtocol; import redis.clients.jedis.args.ExpiryOption; import redis.clients.jedis.params.RestoreParams; import redis.clients.jedis.params.ScanParams; import redis.clients.jedis.params.SortingParams; import redis.clients.jedis.resps.ScanResult; import redis.clients.jedis.util.TestEnvUtil; /** * Tests related to Generic commands. */ @Tag("integration") public class CommandObjectsGenericCommandsTest extends CommandObjectsStandaloneTestBase { public CommandObjectsGenericCommandsTest(RedisProtocol protocol) { super(protocol); } @Test public void testExists() { String key1 = "existsKey1"; String key2 = "existsKey2"; String value = "value"; exec(commandObjects.set(key1, value)); exec(commandObjects.set(key2, value)); Boolean existsSingle = exec(commandObjects.exists(key1)); assertThat(existsSingle, equalTo(true)); Long existsMultiple = exec(commandObjects.exists(key1, key2, "nonExistingKey")); assertThat(existsMultiple, equalTo(2L)); Boolean existsSingleByte = exec(commandObjects.exists(key1.getBytes())); assertThat(existsSingleByte, equalTo(true)); Long existsMultipleByte = exec(commandObjects.exists(key1.getBytes(), key2.getBytes(), "nonExistingKey".getBytes())); assertThat(existsMultipleByte, equalTo(2L)); Boolean existsNonExisting = exec(commandObjects.exists("nonExistingKey")); assertThat(existsNonExisting, equalTo(false)); Boolean existsNonExistingBytes = exec(commandObjects.exists("nonExistingKey".getBytes())); assertThat(existsNonExistingBytes, equalTo(false)); } @Test public void testPersist() { String key1 = "persistKey1"; byte[] key2 = "persistKey2".getBytes(); String value = "value"; int expireTime = 10; // seconds exec(commandObjects.setex(key1, expireTime, value)); exec(commandObjects.setex(key2, expireTime, value.getBytes())); Long ttlBeforePersist1 = exec(commandObjects.ttl(key1)); assertThat(ttlBeforePersist1, greaterThan(0L)); Long ttlBeforePersist2 = exec(commandObjects.ttl(key2)); assertThat(ttlBeforePersist2, greaterThan(0L)); Long persist1 = exec(commandObjects.persist(key1)); assertThat(persist1, equalTo(1L)); Long persist2 = exec(commandObjects.persist(key2)); assertThat(persist2, equalTo(1L)); Long ttlAfterPersist1 = exec(commandObjects.ttl(key1)); assertThat(ttlAfterPersist1, equalTo(-1L)); Long ttlAfterPersist2 = exec(commandObjects.ttl(key2)); assertThat(ttlAfterPersist2, equalTo(-1L)); } @Test public void testType() { String stringKey = "stringKey"; String listKey = "listKey"; byte[] hashKey = "hashKey".getBytes(); exec(commandObjects.set(stringKey, "value")); exec(commandObjects.rpush(listKey, "value")); exec(commandObjects.hset(hashKey, "field".getBytes(), "hvalue".getBytes())); String stringKeyType = exec(commandObjects.type(stringKey)); assertThat(stringKeyType, equalTo("string")); String listKeyType = exec(commandObjects.type(listKey)); assertThat(listKeyType, equalTo("list")); String hashKeyType = exec(commandObjects.type(hashKey)); assertThat(hashKeyType, equalTo("hash")); String nonExistingKeyType = exec(commandObjects.type("nonExistingKey")); assertThat(nonExistingKeyType, equalTo("none")); } @Test public void testDumpAndRestore() { String key = "dumpRestoreKey"; String value = "value"; exec(commandObjects.set(key, value)); byte[] dumpedValue = exec(commandObjects.dump(key)); assertThat(dumpedValue, notNullValue()); exec(commandObjects.del(key)); Boolean existsAfterDel = exec(commandObjects.exists(key)); assertThat(existsAfterDel, equalTo(false)); String restore = exec(commandObjects.restore(key, 0, dumpedValue)); assertThat(restore, equalTo("OK")); String restoredValue = exec(commandObjects.get(key)); assertThat(restoredValue, equalTo(value)); Long ttlAfterRestore = exec(commandObjects.pttl(key)); assertThat(ttlAfterRestore, equalTo(-1L)); exec(commandObjects.del(key)); Boolean existsAfterSecondDel = exec(commandObjects.exists(key)); assertThat(existsAfterSecondDel, equalTo(false)); long ttl = 5000; // milliseconds RestoreParams params = new RestoreParams().idleTime(500); String restoreWithParams = exec(commandObjects.restore(key, ttl, dumpedValue, params)); assertThat(restoreWithParams, equalTo("OK")); String secondRestoredValue = exec(commandObjects.get(key)); assertThat(secondRestoredValue, equalTo(value)); Long ttlAfterSecondRestore = exec(commandObjects.pttl(key)); assertThat(ttlAfterSecondRestore, greaterThan(0L)); } @Test public void testDumpAndRestoreBinary() { byte[] key = "dumpRestoreKey".getBytes(); byte[] value = "value".getBytes(); exec(commandObjects.set(key, value)); byte[] dumpedValue = exec(commandObjects.dump(key)); assertThat(dumpedValue, notNullValue()); exec(commandObjects.del(key)); Boolean existsAfterDel = exec(commandObjects.exists(key)); assertThat(existsAfterDel, equalTo(false)); String restore = exec(commandObjects.restore(key, 0, dumpedValue)); assertThat(restore, equalTo("OK")); byte[] restoredValue = exec(commandObjects.get(key)); assertThat(restoredValue, equalTo(value)); Long ttlAfterRestore = exec(commandObjects.pttl(key)); assertThat(ttlAfterRestore, equalTo(-1L)); exec(commandObjects.del(key)); Boolean existsAfterSecondDel = exec(commandObjects.exists(key)); assertThat(existsAfterSecondDel, equalTo(false)); long ttl = 5000; // milliseconds RestoreParams params = new RestoreParams().idleTime(500); String restoreWithParams = exec(commandObjects.restore(key, ttl, dumpedValue, params)); assertThat(restoreWithParams, equalTo("OK")); byte[] secondRestoredValue = exec(commandObjects.get(key)); assertThat(secondRestoredValue, equalTo(value)); Long ttlAfterSecondRestore = exec(commandObjects.pttl(key)); assertThat(ttlAfterSecondRestore, greaterThan(0L)); } @Test @SinceRedisVersion(value = "7.0.0") public void testExpireAndExpireTime() { String key = "expireKey"; String value = "value"; exec(commandObjects.set(key, value)); Long expireTimeBefore = exec(commandObjects.expireTime(key)); assertThat(expireTimeBefore, equalTo(-1L)); long seconds = 60; Long expire = exec(commandObjects.expire(key, seconds)); assertThat(expire, equalTo(1L)); Long expireTimeAfter = exec(commandObjects.expireTime(key)); assertThat(expireTimeAfter, greaterThan(System.currentTimeMillis() / 1000)); } @Test @SinceRedisVersion(value = "7.0.0") public void testExpireAndExpireTimeBinary() { byte[] key = "expireKey".getBytes(); byte[] value = "value".getBytes(); exec(commandObjects.set(key, value)); Long expireTimeBefore = exec(commandObjects.expireTime(key)); assertThat(expireTimeBefore, equalTo(-1L)); long seconds = 60; Long expire = exec(commandObjects.expire(key, seconds)); assertThat(expire, equalTo(1L)); Long expireTimeAfter = exec(commandObjects.expireTime(key)); assertThat(expireTimeAfter, greaterThan(System.currentTimeMillis() / 1000)); } @Test @SinceRedisVersion(value = "7.0.0") public void testExpireWithExpiryOption() { String key = "expireWithOptionKey"; String value = "value"; exec(commandObjects.set(key, value)); Long expireTimeBefore = exec(commandObjects.expireTime(key)); assertThat(expireTimeBefore, equalTo(-1L)); long seconds = 120; ExpiryOption expiryOptionNX = ExpiryOption.NX; Long expireNx = exec(commandObjects.expire(key, seconds, expiryOptionNX)); assertThat(expireNx, equalTo(1L)); Long expireNxAgain = exec(commandObjects.expire(key, seconds, expiryOptionNX)); assertThat(expireNxAgain, equalTo(0L)); Long expireTimeAfter = exec(commandObjects.expireTime(key)); assertThat(expireTimeAfter, greaterThan(System.currentTimeMillis() / 1000)); } @Test @SinceRedisVersion(value = "7.0.0") public void testExpireWithExpiryOptionTimeBinary() { byte[] key = "expireWithOptionKey".getBytes(); byte[] value = "value".getBytes(); exec(commandObjects.set(key, value)); Long expireTimeBefore = exec(commandObjects.expireTime(key)); assertThat(expireTimeBefore, equalTo(-1L)); long seconds = 120; ExpiryOption expiryOptionNX = ExpiryOption.NX; Long expireNx = exec(commandObjects.expire(key, seconds, expiryOptionNX)); assertThat(expireNx, equalTo(1L)); Long expireNxAgain = exec(commandObjects.expire(key, seconds, expiryOptionNX)); assertThat(expireNxAgain, equalTo(0L)); Long expireTimeAfter = exec(commandObjects.expireTime(key)); assertThat(expireTimeAfter, greaterThan(System.currentTimeMillis() / 1000)); } @Test @SinceRedisVersion(value = "7.0.0") public void testPexpireAndPexpireTime() { String key = "pexpireKey"; String value = "value"; exec(commandObjects.set(key, value)); long expireTimeMillis = 15000; // 15 seconds Long pexpireTimeBefore = exec(commandObjects.pexpireTime(key)); assertThat(pexpireTimeBefore, equalTo(-1L)); Long pexpire = exec(commandObjects.pexpire(key, expireTimeMillis)); assertThat(pexpire, equalTo(1L)); Long pexpireTimeAfter = exec(commandObjects.pexpireTime(key)); assertThat(pexpireTimeAfter, greaterThan(System.currentTimeMillis())); } @Test @SinceRedisVersion(value = "7.0.0") public void testPexpireAndPexpireTimeBinary() { byte[] key = "pexpireKey".getBytes(); byte[] value = "value".getBytes(); exec(commandObjects.set(key, value)); long expireTimeMillis = 15000; // 15 seconds Long pexpireTimeBefore = exec(commandObjects.pexpireTime(key)); assertThat(pexpireTimeBefore, equalTo(-1L)); Long pexpire = exec(commandObjects.pexpire(key, expireTimeMillis)); assertThat(pexpire, equalTo(1L)); Long pexpireTimeAfter = exec(commandObjects.pexpireTime(key)); assertThat(pexpireTimeAfter, greaterThan(System.currentTimeMillis())); } @Test @SinceRedisVersion(value = "7.0.0") public void testPexpireWithOptionsAndPexpireTime() { String key = "pexpireWithOptionsKey"; String value = "value"; exec(commandObjects.set(key, value)); long expireTimeMillis = 20000; // 20 seconds Long pexpireTimeBefore = exec(commandObjects.pexpireTime(key)); assertThat(pexpireTimeBefore, equalTo(-1L)); Long pexpire = exec(commandObjects.pexpire(key, expireTimeMillis, ExpiryOption.NX)); assertThat(pexpire, equalTo(1L)); Long pexpireTimeAfterSet = exec(commandObjects.pexpireTime(key)); assertThat(pexpireTimeAfterSet, greaterThan(System.currentTimeMillis())); Long pexpireWithNx = exec(commandObjects.pexpire(key, expireTimeMillis, ExpiryOption.NX)); assertThat(pexpireWithNx, equalTo(0L)); Long pexpireWithXx = exec(commandObjects.pexpire(key, expireTimeMillis, ExpiryOption.XX)); assertThat(pexpireWithXx, equalTo(1L)); } @Test @SinceRedisVersion(value = "7.0.0", message = "Starting with Redis version 7.0.0: Added options: NX, XX, GT and LT.") public void testPexpireWithOptionsAndPexpireTimeBinary() { byte[] key = "pexpireWithOptionsKey".getBytes(); byte[] value = "value".getBytes(); exec(commandObjects.set(key, value)); long expireTimeMillis = 20000; // 20 seconds Long pexpireTimeBefore = exec(commandObjects.pexpireTime(key)); assertThat(pexpireTimeBefore, equalTo(-1L)); Long pexpire = exec(commandObjects.pexpire(key, expireTimeMillis, ExpiryOption.NX)); assertThat(pexpire, equalTo(1L)); Long pexpireTimeAfterSet = exec(commandObjects.pexpireTime(key)); assertThat(pexpireTimeAfterSet, greaterThan(System.currentTimeMillis())); Long pexpireWithNx = exec(commandObjects.pexpire(key, expireTimeMillis, ExpiryOption.NX)); assertThat(pexpireWithNx, equalTo(0L)); Long pexpireWithXx = exec(commandObjects.pexpire(key, expireTimeMillis, ExpiryOption.XX)); assertThat(pexpireWithXx, equalTo(1L)); } @Test @SinceRedisVersion(value = "7.0.0") public void testExpireAtAndExpireTime() { String key = "expireAtKey"; String value = "value"; exec(commandObjects.set(key, value)); long futureExpireTime = System.currentTimeMillis() / 1000 + 10; // 10 seconds from now // Setting expire at in the future Long expireAt = exec(commandObjects.expireAt(key, futureExpireTime)); assertThat(expireAt, equalTo(1L)); Long expireTime = exec(commandObjects.expireTime(key)); assertThat(expireTime, equalTo(futureExpireTime)); // Setting expire at in the past should delete the key long pastExpireTime = System.currentTimeMillis() / 1000 - 10; Long expireAtPast = exec(commandObjects.expireAt(key, pastExpireTime)); assertThat(expireAtPast, equalTo(1L)); Long expireTimeAfterPast = exec(commandObjects.expireTime(key)); assertThat(expireTimeAfterPast, equalTo(-2L)); // Key does not exist } @Test @SinceRedisVersion(value = "7.0.0") public void testExpireAtAndExpireTimeBinary() { byte[] key = "expireAtKey".getBytes(); byte[] value = "value".getBytes(); exec(commandObjects.set(key, value)); long futureExpireTime = System.currentTimeMillis() / 1000 + 10; // 10 seconds from now // Setting expire at in the future Long expireAt = exec(commandObjects.expireAt(key, futureExpireTime)); assertThat(expireAt, equalTo(1L)); Long expireTime = exec(commandObjects.expireTime(key)); assertThat(expireTime, equalTo(futureExpireTime)); // Setting expire at in the past should delete the key long pastExpireTime = System.currentTimeMillis() / 1000 - 10; Long expireAtPast = exec(commandObjects.expireAt(key, pastExpireTime)); assertThat(expireAtPast, equalTo(1L)); Long expireTimeAfterPast = exec(commandObjects.expireTime(key)); assertThat(expireTimeAfterPast, equalTo(-2L)); // Key does not exist } @Test @SinceRedisVersion(value = "7.0.0") public void testExpireAtWithOptionsAndExpireTime() { String key = "expireAtWithOptionsKey"; String value = "value"; exec(commandObjects.set(key, value)); long futureExpireTime = System.currentTimeMillis() / 1000 + 20; // 20 seconds from now // Setting expire at in the future, with NX Long expireAtNx = exec(commandObjects.expireAt(key, futureExpireTime, ExpiryOption.NX)); assertThat(expireAtNx, equalTo(1L)); Long expireTimeAfterNx = exec(commandObjects.expireTime(key)); assertThat(expireTimeAfterNx, equalTo(futureExpireTime)); // Update expire at in the future, with XX long laterFutureExpireTime = futureExpireTime + 10; Long expireAtXx = exec(commandObjects.expireAt(key, laterFutureExpireTime, ExpiryOption.XX)); assertThat(expireAtXx, equalTo(1L)); Long expireTimeAfterXx = exec(commandObjects.expireTime(key)); assertThat(expireTimeAfterXx, equalTo(laterFutureExpireTime)); // Try to reset with NX, should fail Long expireAtNxAgain = exec(commandObjects.expireAt(key, futureExpireTime, ExpiryOption.NX)); assertThat(expireAtNxAgain, equalTo(0L)); Long expireTimeAfterNxAgain = exec(commandObjects.expireTime(key)); assertThat(expireTimeAfterNxAgain, equalTo(laterFutureExpireTime)); } @Test @SinceRedisVersion(value = "7.0.0") public void testExpireAtWithOptionsAndExpireTimeBinary() { byte[] key = "expireAtWithOptionsKey".getBytes(); byte[] value = "value".getBytes(); exec(commandObjects.set(key, value)); long futureExpireTime = System.currentTimeMillis() / 1000 + 20; // 20 seconds from now // Setting expire at in the future, with NX Long expireAtNx = exec(commandObjects.expireAt(key, futureExpireTime, ExpiryOption.NX)); assertThat(expireAtNx, equalTo(1L)); Long expireTimeAfterNx = exec(commandObjects.expireTime(key)); assertThat(expireTimeAfterNx, equalTo(futureExpireTime)); // Update expire at in the future, with XX long laterFutureExpireTime = futureExpireTime + 10; Long expireAtXx = exec(commandObjects.expireAt(key, laterFutureExpireTime, ExpiryOption.XX)); assertThat(expireAtXx, equalTo(1L)); Long expireTime = exec(commandObjects.expireTime(key)); assertThat(expireTime, equalTo(laterFutureExpireTime)); // Try to reset with NX, should fail Long expireAtNxAgain = exec(commandObjects.expireAt(key, futureExpireTime, ExpiryOption.NX)); assertThat(expireAtNxAgain, equalTo(0L)); Long expireTimeAfterNxAgain = exec(commandObjects.expireTime(key)); assertThat(expireTimeAfterNxAgain, equalTo(laterFutureExpireTime)); } @Test @SinceRedisVersion(value = "7.0.0") public void testPexpireAtAndPexpireTime() { String key = "pexpireAtKey"; String value = "value"; exec(commandObjects.set(key, value)); long futureTimestampMillis = System.currentTimeMillis() + 20000; // 20 seconds from now Long pexpireAt = exec(commandObjects.pexpireAt(key, futureTimestampMillis)); assertThat(pexpireAt, equalTo(1L)); Long pexpireTime = exec(commandObjects.pexpireTime(key)); assertThat(pexpireTime, equalTo(futureTimestampMillis)); // Setting pexpire at a past timestamp should delete the key long pastTimestampMillis = System.currentTimeMillis() - 20000; Long pexpireAtPast = exec(commandObjects.pexpireAt(key, pastTimestampMillis)); assertThat(pexpireAtPast, equalTo(1L)); Long pexpireTimeAfterPast = exec(commandObjects.pexpireTime(key)); assertThat(pexpireTimeAfterPast, equalTo(-2L)); // Key does not exist } @Test @SinceRedisVersion(value = "7.0.0") public void testPexpireAtAndPexpireTimeBinary() { byte[] key = "pexpireAtKey".getBytes(); byte[] value = "value".getBytes(); exec(commandObjects.set(key, value)); long futureTimestampMillis = System.currentTimeMillis() + 20000; // 20 seconds from now Long pexpireAt = exec(commandObjects.pexpireAt(key, futureTimestampMillis)); assertThat(pexpireAt, equalTo(1L)); Long pexpireTime = exec(commandObjects.pexpireTime(key)); assertThat(pexpireTime, equalTo(futureTimestampMillis)); // Setting pexpire at a past timestamp should delete the key long pastTimestampMillis = System.currentTimeMillis() - 20000; Long pexpireAtPast = exec(commandObjects.pexpireAt(key, pastTimestampMillis)); assertThat(pexpireAtPast, equalTo(1L)); Long pexpireTimeAfterPast = exec(commandObjects.pexpireTime(key)); assertThat(pexpireTimeAfterPast, equalTo(-2L)); // Key does not exist } @Test @SinceRedisVersion(value = "7.0.0", message = "Starting with Redis version 7.0.0: Added options: NX, XX, GT and LT.") public void testPexpireAtWithOptionsAndPexpireTime() { String key = "pexpireAtWithOptionsKey"; String value = "value"; exec(commandObjects.set(key, value)); long futureTimestampMillis = System.currentTimeMillis() + 30000; // 30 seconds from now // Setting with NX Long pexpireAtNx = exec(commandObjects.pexpireAt(key, futureTimestampMillis, ExpiryOption.NX)); assertThat(pexpireAtNx, equalTo(1L)); Long pexpireTimeAfterNx = exec(commandObjects.pexpireTime(key)); assertThat(pexpireTimeAfterNx, equalTo(futureTimestampMillis)); // Updating with XX long laterFutureTimestampMillis = futureTimestampMillis + 10000; // Further 10 seconds in the future Long pexpireAtXx = exec(commandObjects.pexpireAt(key, laterFutureTimestampMillis, ExpiryOption.XX)); assertThat(pexpireAtXx, equalTo(1L)); Long pexpireTimeAfterXx = exec(commandObjects.pexpireTime(key)); assertThat(pexpireTimeAfterXx, equalTo(laterFutureTimestampMillis)); // Updating with NX fails Long pexpireAtNxAgain = exec(commandObjects.pexpireAt(key, futureTimestampMillis, ExpiryOption.NX)); assertThat(pexpireAtNxAgain, equalTo(0L)); Long pexpireTimeAfterNxAgain = exec(commandObjects.pexpireTime(key)); assertThat(pexpireTimeAfterNxAgain, equalTo(laterFutureTimestampMillis)); } @Test @SinceRedisVersion(value = "7.0.0") public void testPexpireAtWithOptionsAndPexpireTimeBinary() { byte[] key = "pexpireAtWithOptionsKey".getBytes(); byte[] value = "value".getBytes(); exec(commandObjects.set(key, value)); long futureTimestampMillis = System.currentTimeMillis() + 30000; // 30 seconds from now // Setting with NX Long pexpireAtNx = exec(commandObjects.pexpireAt(key, futureTimestampMillis, ExpiryOption.NX)); assertThat(pexpireAtNx, equalTo(1L)); Long pexpireTimeAfterNx = exec(commandObjects.pexpireTime(key)); assertThat(pexpireTimeAfterNx, equalTo(futureTimestampMillis)); // Updating with XX long laterFutureTimestampMillis = futureTimestampMillis + 10000; // Further 10 seconds in the future Long pexpireAtXx = exec(commandObjects.pexpireAt(key, laterFutureTimestampMillis, ExpiryOption.XX)); assertThat(pexpireAtXx, equalTo(1L)); Long pexpireTimeAfterXx = exec(commandObjects.pexpireTime(key)); assertThat(pexpireTimeAfterXx, equalTo(laterFutureTimestampMillis)); // Updating with NX fails Long pexpireAtNxAgain = exec(commandObjects.pexpireAt(key, futureTimestampMillis, ExpiryOption.NX)); assertThat(pexpireAtNxAgain, equalTo(0L)); Long pexpireTimeAfterNxAgain = exec(commandObjects.pexpireTime(key)); assertThat(pexpireTimeAfterNxAgain, equalTo(laterFutureTimestampMillis)); } @Test public void testTtl() { String key = "ttlKey"; String value = "value"; long seconds = 10; exec(commandObjects.set(key, value)); exec(commandObjects.expire(key, seconds)); Long ttl = exec(commandObjects.ttl(key)); assertThat(ttl, greaterThan(0L)); } @Test public void testTtlBinary() { byte[] key = "ttlKey".getBytes(); byte[] value = "value".getBytes(); long seconds = 10; exec(commandObjects.set(key, value)); exec(commandObjects.expire(key, seconds)); Long ttl = exec(commandObjects.ttl(key)); assertThat(ttl, greaterThan(1L)); } @Test public void testPttl() { String key = "pttlKey"; String value = "value"; long milliseconds = 10000; // 10 seconds exec(commandObjects.set(key, value)); exec(commandObjects.pexpire(key, milliseconds)); Long pttl = exec(commandObjects.pttl(key)); assertThat(pttl, greaterThan(0L)); } @Test public void testPttlBinary() { byte[] key = "pttlKey".getBytes(); byte[] value = "value".getBytes(); long milliseconds = 10000; // 10 seconds exec(commandObjects.set(key, value)); exec(commandObjects.pexpire(key, milliseconds)); Long pttl = exec(commandObjects.pttl(key)); assertThat(pttl, greaterThan(1L)); } @Test public void testTouch() { String key = "touchKey"; exec(commandObjects.set(key, "value")); Long touchExisting = exec(commandObjects.touch(key)); assertThat(touchExisting, equalTo(1L)); Long touchNonExistent = exec(commandObjects.touch("nonExistentKey")); assertThat(touchNonExistent, equalTo(0L)); } @Test public void testTouchBinary() { byte[] key = "touchKey".getBytes(); exec(commandObjects.set(key, "value".getBytes())); Long touchExisting = exec(commandObjects.touch(key)); assertThat(touchExisting, equalTo(1L)); Long touchNonExistent = exec(commandObjects.touch("nonExistentKey".getBytes())); assertThat(touchNonExistent, equalTo(0L)); } @Test public void testTouchMultiple() { String key1 = "touchMultiKey1"; String key2 = "touchMultiKey2"; String key3 = "nonExistentKey"; exec(commandObjects.set(key1, "value1")); exec(commandObjects.set(key2, "value2")); Long touch = exec(commandObjects.touch(key1, key2, key3)); assertThat(touch, equalTo(2L)); } @Test public void testTouchMultipleBinary() { byte[] key1 = "touchMultiKey1".getBytes(); byte[] key2 = "touchMultiKey2".getBytes(); byte[] key3 = "nonExistentKey".getBytes(); exec(commandObjects.set(key1, "value1".getBytes())); exec(commandObjects.set(key2, "value2".getBytes())); Long touch = exec(commandObjects.touch(key1, key2, key3)); assertThat(touch, equalTo(2L)); } @Test public void testSort() { String listKey = "sortList"; exec(commandObjects.lpush(listKey, "3", "1", "2")); List sorted = exec(commandObjects.sort(listKey)); assertThat(sorted, contains("1", "2", "3")); } @Test public void testSortBinary() { byte[] listKey = "sortList".getBytes(); exec(commandObjects.lpush(listKey, "3".getBytes(), "1".getBytes(), "2".getBytes())); List sorted = exec(commandObjects.sort(listKey)); assertThat(sorted, contains("1".getBytes(), "2".getBytes(), "3".getBytes())); } @Test public void testSortWithSortingParams() { String listKey = "sortListParams"; exec(commandObjects.lpush(listKey, "item3", "item1", "item2")); SortingParams sortingParams = new SortingParams().alpha().limit(0, 2); List sorted = exec(commandObjects.sort(listKey, sortingParams)); assertThat(sorted, contains("item1", "item2")); } @Test public void testSortBinaryWithSortingParams() { byte[] listKey = "sortListParams".getBytes(); exec(commandObjects.lpush(listKey, "item3".getBytes(), "item1".getBytes(), "item2".getBytes())); SortingParams sortingParams = new SortingParams().alpha().limit(0, 2); List sorted = exec(commandObjects.sort(listKey, sortingParams)); assertThat(sorted, contains("item1".getBytes(), "item2".getBytes())); } @Test @ConditionalOnEnv(value = TestEnvUtil.ENV_REDIS_ENTERPRISE, enabled = false) public void testSortAndStore() { String listKey = "sortStoreList"; String destinationKey = "sortedList"; exec(commandObjects.lpush(listKey, "9", "3", "6")); Long sort = exec(commandObjects.sort(listKey, destinationKey)); assertThat(sort, equalTo(3L)); List sorted = exec(commandObjects.lrange(destinationKey, 0, -1)); assertThat(sorted, contains("3", "6", "9")); } @Test @ConditionalOnEnv(value = TestEnvUtil.ENV_REDIS_ENTERPRISE, enabled = false) public void testSortAndStoreBinary() { byte[] listKey = "sortStoreList".getBytes(); byte[] destinationKey = "sortedList".getBytes(); exec(commandObjects.lpush(listKey, "9".getBytes(), "3".getBytes(), "6".getBytes())); Long sort = exec(commandObjects.sort(listKey, destinationKey)); assertThat(sort, equalTo(3L)); List sorted = exec(commandObjects.lrange(destinationKey, 0, -1)); assertThat(sorted, contains("3".getBytes(), "6".getBytes(), "9".getBytes())); } @Test @ConditionalOnEnv(value = TestEnvUtil.ENV_REDIS_ENTERPRISE, enabled = false) public void testSortWithParamsAndStore() { String listKey = "sortParamsStoreList"; String destinationKey = "sortedParamsList"; exec(commandObjects.lpush(listKey, "item3", "item1", "item2")); SortingParams sortingParams = new SortingParams().alpha().limit(0, 2); Long sort = exec(commandObjects.sort(listKey, sortingParams, destinationKey)); assertThat(sort, equalTo(2L)); List sorted = exec(commandObjects.lrange(destinationKey, 0, -1)); assertThat(sorted, contains("item1", "item2")); } @Test @ConditionalOnEnv(value = TestEnvUtil.ENV_REDIS_ENTERPRISE, enabled = false) public void testSortWithParamsAndStoreBinary() { byte[] listKey = "sortParamsStoreList".getBytes(); byte[] destinationKey = "sortedParamsList".getBytes(); exec(commandObjects.lpush(listKey, "item3".getBytes(), "item1".getBytes(), "item2".getBytes())); SortingParams sortingParams = new SortingParams().alpha().limit(0, 2); Long sort = exec(commandObjects.sort(listKey, sortingParams, destinationKey)); assertThat(sort, equalTo(2L)); List sorted = exec(commandObjects.lrange(destinationKey, 0, -1)); assertThat(sorted, contains("item1".getBytes(), "item2".getBytes())); } @Test @SinceRedisVersion(value = "7.0.0") public void testSortReadonly() { String listKey = "readonlySortList"; exec(commandObjects.lpush(listKey, "3", "1", "2")); SortingParams sortingParams = new SortingParams().desc(); List sorted = exec(commandObjects.sortReadonly(listKey, sortingParams)); assertThat(sorted, contains("3", "2", "1")); } @Test @SinceRedisVersion(value = "7.0.0") public void testSortReadonlyBinary() { byte[] listKey = "readonlySortList".getBytes(); exec(commandObjects.lpush(listKey, "3".getBytes(), "1".getBytes(), "2".getBytes())); SortingParams sortingParams = new SortingParams().desc(); List sorted = exec(commandObjects.sortReadonly(listKey, sortingParams)); assertThat(sorted, contains("3".getBytes(), "2".getBytes(), "1".getBytes())); } @Test public void testDel() { String key = "delKey"; String value = "value"; exec(commandObjects.set(key, value)); String getBeforeDel = exec(commandObjects.get(key)); assertThat(getBeforeDel, equalTo(value)); Long del = exec(commandObjects.del(key)); assertThat(del, equalTo(1L)); String getAfterDel = exec(commandObjects.get(key)); assertThat(getAfterDel, nullValue()); } @Test public void testDelBinary() { byte[] key = "delKey".getBytes(); byte[] value = "value".getBytes(); exec(commandObjects.set(key, value)); byte[] getBeforeDel = exec(commandObjects.get(key)); assertThat(getBeforeDel, equalTo(value)); Long del = exec(commandObjects.del(key)); assertThat(del, equalTo(1L)); byte[] getAfterDel = exec(commandObjects.get(key)); assertThat(getAfterDel, nullValue()); } @Test public void testDelMultiple() { String key1 = "key1"; String key2 = "key2"; exec(commandObjects.set(key1, "value")); exec(commandObjects.set(key2, "value")); Long del = exec(commandObjects.del(key1, key2, "nonExistingKey")); assertThat(del, equalTo(2L)); Long exists = exec(commandObjects.exists(key1, key2)); assertThat(exists, equalTo(0L)); } @Test public void testDelMultipleBinary() { byte[] key1 = "key1".getBytes(); byte[] key2 = "key2".getBytes(); exec(commandObjects.set(key1, "value".getBytes())); exec(commandObjects.set(key2, "value".getBytes())); Long del = exec(commandObjects.del(key1, key2, "nonExistingKey".getBytes())); assertThat(del, equalTo(2L)); Long exists = exec(commandObjects.exists(key1, key2)); assertThat(exists, equalTo(0L)); } @Test public void testUnlink() { String key = "unlinkKey"; exec(commandObjects.set(key, "value")); Long unlink = exec(commandObjects.unlink(key)); assertThat(unlink, equalTo(1L)); Boolean exists = exec(commandObjects.exists(key)); assertThat(exists, equalTo(false)); } @Test public void testUnlinkBinary() { byte[] key = "unlinkKey".getBytes(); exec(commandObjects.set(key, "value".getBytes())); Long unlink = exec(commandObjects.unlink(key)); assertThat(unlink, equalTo(1L)); Boolean exists = exec(commandObjects.exists(key)); assertThat(exists, equalTo(false)); } @Test public void testUnlinkMultiple() { String key1 = "key1ToUnlink"; String key2 = "key2ToUnlink"; exec(commandObjects.set(key1, "value")); exec(commandObjects.set(key2, "value")); Long unlink = exec(commandObjects.unlink(key1, key2, "nonExistingKey")); assertThat(unlink, equalTo(2L)); Long exists = exec(commandObjects.exists(key1, key2)); assertThat(exists, equalTo(0L)); } @Test public void testUnlinkMultipleBinary() { byte[] key1 = "key1ToUnlink".getBytes(); byte[] key2 = "key2ToUnlink".getBytes(); exec(commandObjects.set(key1, "value".getBytes())); exec(commandObjects.set(key2, "value".getBytes())); Long unlink = exec(commandObjects.unlink(key1, key2, "nonExistingKey".getBytes())); assertThat(unlink, equalTo(2L)); Long exists = exec(commandObjects.exists(key1, key2)); assertThat(exists, equalTo(0L)); } @Test @ConditionalOnEnv(value = TestEnvUtil.ENV_REDIS_ENTERPRISE, enabled = false) public void testCopyWithStringKeys() { String srcKey = "sourceKey"; String dstKey = "destinationKey"; String value = "value"; String otherValue = "otherValue"; exec(commandObjects.set(srcKey, value)); String initialValue = exec(commandObjects.get(srcKey)); assertThat(initialValue, equalTo(value)); String dstBeforeCopy = exec(commandObjects.get(dstKey)); assertThat(dstBeforeCopy, nullValue()); Boolean copy = exec(commandObjects.copy(srcKey, dstKey, false)); assertThat(copy, equalTo(true)); String dstAfterCopy = exec(commandObjects.get(dstKey)); assertThat(dstAfterCopy, equalTo(value)); exec(commandObjects.set(srcKey, otherValue)); Boolean copyFail = exec(commandObjects.copy(srcKey, dstKey, false)); assertThat(copyFail, equalTo(false)); String dstAfterFailedCopy = exec(commandObjects.get(dstKey)); assertThat(dstAfterFailedCopy, equalTo(value)); Boolean copyReplace = exec(commandObjects.copy(srcKey, dstKey, true)); assertThat(copyReplace, equalTo(true)); String dstAfterReplace = exec(commandObjects.get(dstKey)); assertThat(dstAfterReplace, equalTo(otherValue)); } @Test @ConditionalOnEnv(value = TestEnvUtil.ENV_REDIS_ENTERPRISE, enabled = false) public void testCopyWithBinaryKeys() { byte[] srcKey = "sourceKey".getBytes(); byte[] dstKey = "destinationKey".getBytes(); byte[] value = "value".getBytes(); byte[] otherValue = "otherValue".getBytes(); exec(commandObjects.set(srcKey, value)); byte[] initialValue = exec(commandObjects.get(srcKey)); assertThat(initialValue, equalTo(value)); byte[] dstBeforeCopy = exec(commandObjects.get(dstKey)); assertThat(dstBeforeCopy, nullValue()); Boolean copy = exec(commandObjects.copy(srcKey, dstKey, false)); assertThat(copy, equalTo(true)); byte[] dstAfterCopy = exec(commandObjects.get(dstKey)); assertThat(dstAfterCopy, equalTo(value)); exec(commandObjects.set(srcKey, otherValue)); Boolean copyFail = exec(commandObjects.copy(srcKey, dstKey, false)); assertThat(copyFail, equalTo(false)); byte[] dstAfterFailedCopy = exec(commandObjects.get(dstKey)); assertThat(dstAfterFailedCopy, equalTo(value)); Boolean copyReplace = exec(commandObjects.copy(srcKey, dstKey, true)); assertThat(copyReplace, equalTo(true)); byte[] dstAfterReplace = exec(commandObjects.get(dstKey)); assertThat(dstAfterReplace, equalTo(otherValue)); } @Test @ConditionalOnEnv(value = TestEnvUtil.ENV_REDIS_ENTERPRISE, enabled = false) public void testCopyToDb() { String srcKey = "sourceKey"; String dstKey = "destinationKey"; int dstDB = 1; exec(commandObjects.set(srcKey, "initialValue")); Boolean existsAfterSet = exec(commandObjects.exists(srcKey)); assertThat(existsAfterSet, equalTo(true)); Boolean copy = exec(commandObjects.copy(srcKey, dstKey, dstDB, true)); assertThat(copy, equalTo(true)); assertKeyExists(dstDB, dstKey, "initialValue"); // Update source exec(commandObjects.set(srcKey, "newValue")); // Copy again without replace, it fails since dstKey already exists Boolean secondCopy = exec(commandObjects.copy(srcKey, dstKey, dstDB, false)); assertThat(secondCopy, equalTo(false)); assertKeyExists(dstDB, dstKey, "initialValue"); } @Test @ConditionalOnEnv(value = TestEnvUtil.ENV_REDIS_ENTERPRISE, enabled = false) public void testCopyToDbBinary() { String srcKey = "sourceKey"; String dstKey = "destinationKey"; int dstDB = 1; exec(commandObjects.set(srcKey, "initialValue")); Boolean existsAfterSet = exec(commandObjects.exists(srcKey)); assertThat(existsAfterSet, equalTo(true)); Boolean copy = exec(commandObjects.copy( srcKey.getBytes(), dstKey.getBytes(), dstDB, true)); assertThat(copy, equalTo(true)); assertKeyExists(dstDB, dstKey, "initialValue"); // Update source exec(commandObjects.set(srcKey, "newValue")); // Copy again without replace, it will fail Boolean secondCopy = exec(commandObjects.copy(srcKey.getBytes(), dstKey.getBytes(), dstDB, false)); assertThat(secondCopy, equalTo(false)); assertKeyExists(dstDB, dstKey, "initialValue"); } private void assertKeyExists(int dstDb, String key, Object expectedValue) { // Cheat and use Jedis, it gives us access to any db. try (Jedis jedis = new Jedis(endpoint.getHostAndPort())) { jedis.auth(endpoint.getPassword()); jedis.select(dstDb); assertThat(jedis.get(key), equalTo(expectedValue)); } } @Test @ConditionalOnEnv(value = TestEnvUtil.ENV_REDIS_ENTERPRISE, enabled = false) public void testRenameWithStringKeys() { String oldKey = "oldKeyName"; String newKey = "newKeyName"; String value = "value"; exec(commandObjects.set(oldKey, value)); String oldValue = exec(commandObjects.get(oldKey)); assertThat(oldValue, equalTo(value)); String newKeyBeforeRename = exec(commandObjects.get(newKey)); assertThat(newKeyBeforeRename, nullValue()); String rename = exec(commandObjects.rename(oldKey, newKey)); assertThat(rename, equalTo("OK")); String oldKeyAfterRename = exec(commandObjects.get(oldKey)); assertThat(oldKeyAfterRename, nullValue()); String newValue = exec(commandObjects.get(newKey)); assertThat(newValue, equalTo(value)); } @Test @ConditionalOnEnv(value = TestEnvUtil.ENV_REDIS_ENTERPRISE, enabled = false) public void testRenameWithBinaryKeys() { byte[] oldKey = "oldKeyName".getBytes(); byte[] newKey = "newKeyName".getBytes(); byte[] value = "value".getBytes(); exec(commandObjects.set(oldKey, value)); byte[] oldValue = exec(commandObjects.get(oldKey)); assertThat(oldValue, equalTo(value)); byte[] newKeyBeforeRename = exec(commandObjects.get(newKey)); assertThat(newKeyBeforeRename, nullValue()); String rename = exec(commandObjects.rename(oldKey, newKey)); assertThat(rename, equalTo("OK")); byte[] oldKeyAfterRename = exec(commandObjects.get(oldKey)); assertThat(oldKeyAfterRename, nullValue()); byte[] newValue = exec(commandObjects.get(newKey)); assertThat(newValue, equalTo(value)); } @Test @ConditionalOnEnv(value = TestEnvUtil.ENV_REDIS_ENTERPRISE, enabled = false) public void testRenamenx() { String oldKey = "oldKeyToRenameNX"; String newKey = "newKeyForRenameNX"; String anotherKey = "anotherKey"; String value = "value"; exec(commandObjects.set(oldKey, value)); exec(commandObjects.set(anotherKey, value)); String newKeyBefore = exec(commandObjects.get(newKey)); assertThat(newKeyBefore, nullValue()); Long renamenx = exec(commandObjects.renamenx(oldKey, newKey)); assertThat(renamenx, equalTo(1L)); String newValue = exec(commandObjects.get(newKey)); assertThat(newValue, equalTo(value)); Long renamenxFail = exec(commandObjects.renamenx(anotherKey, newKey)); assertThat(renamenxFail, equalTo(0L)); String anotherKeyStillExists = exec(commandObjects.get(anotherKey)); assertThat(anotherKeyStillExists, equalTo(value)); } @Test @ConditionalOnEnv(value = TestEnvUtil.ENV_REDIS_ENTERPRISE, enabled = false) public void testRenamenxBinary() { byte[] oldKey = "oldKeyToRenameNX".getBytes(); byte[] newKey = "newKeyForRenameNX".getBytes(); byte[] anotherKey = "anotherKey".getBytes(); byte[] value = "value".getBytes(); exec(commandObjects.set(oldKey, value)); exec(commandObjects.set(anotherKey, value)); byte[] newKeyBefore = exec(commandObjects.get(newKey)); assertThat(newKeyBefore, nullValue()); Long renamenx = exec(commandObjects.renamenx(oldKey, newKey)); assertThat(renamenx, equalTo(1L)); byte[] newValue = exec(commandObjects.get(newKey)); assertThat(newValue, equalTo(value)); Long renamenxFail = exec(commandObjects.renamenx(anotherKey, newKey)); assertThat(renamenxFail, equalTo(0L)); byte[] anotherKeyStillExists = exec(commandObjects.get(anotherKey)); assertThat(anotherKeyStillExists, equalTo(value)); } @Test public void testDbSize() { Long initialSize = exec(commandObjects.dbSize()); assertThat(initialSize, greaterThanOrEqualTo(0L)); String key = "testKey"; exec(commandObjects.set(key, "testValue")); Long newSize = exec(commandObjects.dbSize()); assertThat(newSize, equalTo(initialSize + 1)); exec(commandObjects.del(key)); Long finalSize = exec(commandObjects.dbSize()); assertThat(finalSize, equalTo(initialSize)); } @Test public void testKeysWithStringPattern() { String pattern = "testKey:*"; String matchingKey1 = "testKey:1"; String matchingKey2 = "testKey:2"; String value = "value"; exec(commandObjects.set(matchingKey1, value)); exec(commandObjects.set(matchingKey2, value)); exec(commandObjects.set("otherKey", value)); Set keys = exec(commandObjects.keys(pattern)); assertThat(keys, containsInAnyOrder(matchingKey1, matchingKey2)); exec(commandObjects.del(matchingKey1, matchingKey2)); Set keysAfterDeletion = exec(commandObjects.keys(pattern)); assertThat(keysAfterDeletion, empty()); } @Test public void testKeysWithBinaryPattern() { byte[] pattern = "testKey:*".getBytes(); byte[] matchingKey1 = "testKey:1".getBytes(); byte[] matchingKey2 = "testKey:2".getBytes(); byte[] value = "value".getBytes(); exec(commandObjects.set(matchingKey1, value)); exec(commandObjects.set(matchingKey2, value)); exec(commandObjects.set("otherKey".getBytes(), value)); Set keys = exec(commandObjects.keys(pattern)); assertThat(keys, containsInAnyOrder(matchingKey1, matchingKey2)); exec(commandObjects.del(matchingKey1, matchingKey2)); Set keysAfterDeletion = exec(commandObjects.keys(pattern)); assertThat(keysAfterDeletion, empty()); } @Test public void testScan() { String key1 = "scanKey1"; String key2 = "scanKey2"; exec(commandObjects.set(key1, "value")); exec(commandObjects.set(key2, "value")); Set collectedKeys = new HashSet<>(); ScanResult scanResult; String nextCursor = "0"; do { scanResult = exec(commandObjects.scan(nextCursor)); nextCursor = scanResult.getCursor(); collectedKeys.addAll(scanResult.getResult()); } while (!"0".equals(nextCursor)); assertThat(collectedKeys, hasItems(key1, key2)); } @Test public void testScanBinary() { byte[] key1 = "scanKey1".getBytes(); byte[] key2 = "scanKey2".getBytes(); exec(commandObjects.set(key1, "value".getBytes())); exec(commandObjects.set(key2, "value".getBytes())); Set collectedKeys = new HashSet<>(); ScanResult scanResult; byte[] cursor = "0".getBytes(); do { scanResult = exec(commandObjects.scan(cursor)); cursor = scanResult.getCursorAsBytes(); collectedKeys.addAll(scanResult.getResult()); } while (!Arrays.equals("0".getBytes(), cursor)); assertThat(collectedKeys, hasItems(key1, key2)); } @Test public void testScanWithParams() { String matchingKey1 = "user:123"; String matchingKey2 = "user:456"; String nonMatchingKey = "config:123"; exec(commandObjects.set(matchingKey1, "testValue")); exec(commandObjects.set(matchingKey2, "testValue")); exec(commandObjects.set(nonMatchingKey, "testValue")); ScanParams params = new ScanParams().match("user:*").count(2); Set collectedKeys = new HashSet<>(); ScanResult scanResult; String cursor = "0"; do { scanResult = exec(commandObjects.scan(cursor, params)); collectedKeys.addAll(scanResult.getResult()); cursor = scanResult.getCursor(); } while (!"0".equals(scanResult.getCursor())); assertThat(collectedKeys, hasItems(matchingKey1, matchingKey2)); assertThat(collectedKeys, not(hasItem(nonMatchingKey))); } @Test public void testScanWithParamsBinary() { byte[] matchingKey1 = "user:123".getBytes(); byte[] matchingKey2 = "user:456".getBytes(); byte[] nonMatchingKey = "config:123".getBytes(); exec(commandObjects.set(matchingKey1, "testValue".getBytes())); exec(commandObjects.set(matchingKey2, "testValue".getBytes())); exec(commandObjects.set(nonMatchingKey, "testValue".getBytes())); ScanParams params = new ScanParams().match("user:*").count(2); Set collectedKeys = new HashSet<>(); ScanResult scanResult; byte[] cursor = "0".getBytes(); do { scanResult = exec(commandObjects.scan(cursor, params)); collectedKeys.addAll(scanResult.getResult()); cursor = scanResult.getCursorAsBytes(); } while (!Arrays.equals("0".getBytes(), cursor)); assertThat(collectedKeys, hasItems(matchingKey1, matchingKey2)); assertThat(collectedKeys, not(hasItem(nonMatchingKey))); } @Test public void testScanWithParamsAndType() { String stringKey = "user:string:1"; String listKey = "user:list:1"; exec(commandObjects.set(stringKey, "value")); exec(commandObjects.rpush(listKey, "value1", "value2")); ScanParams params = new ScanParams().match("user:*"); Set collectedKeys = new HashSet<>(); ScanResult scanResult; String cursor = "0"; do { scanResult = exec(commandObjects.scan(cursor, params, "string")); collectedKeys.addAll(scanResult.getResult()); cursor = scanResult.getCursor(); } while (!"0".equals(scanResult.getCursor())); assertThat(collectedKeys, hasItem(stringKey)); assertThat(collectedKeys, not(hasItem(listKey))); } @Test public void testScanWithParamsAndTypeBinary() { byte[] stringKey = "user:string:1".getBytes(); byte[] listKey = "user:list:1".getBytes(); exec(commandObjects.set(stringKey, "value".getBytes())); exec(commandObjects.rpush(listKey, "value1".getBytes(), "value2".getBytes())); ScanParams params = new ScanParams().match("user:*".getBytes()); Set collectedKeys = new HashSet<>(); ScanResult scanResult; byte[] cursor = "0".getBytes(); do { scanResult = exec(commandObjects.scan(cursor, params, "string".getBytes())); collectedKeys.addAll(scanResult.getResult()); cursor = scanResult.getCursorAsBytes(); } while (!Arrays.equals("0".getBytes(), cursor)); assertThat(collectedKeys, hasItem(stringKey)); assertThat(collectedKeys, not(hasItem(listKey))); } @Test public void testRandomKey() { String key1 = "testKey1"; String key2 = "testKey2"; exec(commandObjects.set(key1, "value")); exec(commandObjects.set(key2, "value")); String randomKey = exec(commandObjects.randomKey()); assertThat(randomKey, anyOf(equalTo(key1), equalTo(key2))); } @Test public void testRandomBinaryKey() { byte[] key1 = "testKey1".getBytes(); byte[] key2 = "testKey2".getBytes(); exec(commandObjects.set(key1, "value".getBytes())); exec(commandObjects.set(key2, "value".getBytes())); byte[] randomBinaryKey = exec(commandObjects.randomBinaryKey()); assertThat(randomBinaryKey, anyOf(equalTo(key1), equalTo(key2))); } } ================================================ FILE: src/test/java/redis/clients/jedis/commands/commandobjects/CommandObjectsGeospatialCommandsTest.java ================================================ package redis.clients.jedis.commands.commandobjects; import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.closeTo; import static org.hamcrest.Matchers.contains; import static org.hamcrest.Matchers.containsInAnyOrder; import static org.hamcrest.Matchers.empty; import static org.hamcrest.Matchers.equalTo; import static org.hamcrest.Matchers.notNullValue; import static org.hamcrest.Matchers.nullValue; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.stream.Collectors; import io.redis.test.annotations.ConditionalOnEnv; import org.junit.jupiter.api.Test; import redis.clients.jedis.GeoCoordinate; import redis.clients.jedis.RedisProtocol; import redis.clients.jedis.args.GeoUnit; import redis.clients.jedis.params.GeoAddParams; import redis.clients.jedis.params.GeoRadiusParam; import redis.clients.jedis.params.GeoRadiusStoreParam; import redis.clients.jedis.params.GeoSearchParam; import redis.clients.jedis.resps.GeoRadiusResponse; import redis.clients.jedis.util.TestEnvUtil; /** * Tests related to Geospatial commands. */ public class CommandObjectsGeospatialCommandsTest extends CommandObjectsStandaloneTestBase { // Some coordinates for testing public static final String CATANIA = "Catania"; public static final double CATANIA_LATITUDE = 37.502669; public static final double CATANIA_LONGITUDE = 15.087269; public static final String PALERMO = "Palermo"; public static final double PALERMO_LONGITUDE = 13.361389; public static final double PALERMO_LATITUDE = 38.115556; public static final String SYRACUSE = "Syracuse"; public static final double SYRACUSE_LONGITUDE = 15.293331; public static final double SYRACUSE_LATITUDE = 37.075474; public static final String AGRIGENTO = "Agrigento"; public static final double AGRIGENTO_LONGITUDE = 13.583333; public static final double AGRIGENTO_LATITUDE = 37.316667; public CommandObjectsGeospatialCommandsTest(RedisProtocol protocol) { super(protocol); } @Test public void testGeoAddAndRadius() { String key = "locations"; Map cataniaCoordinates = new HashMap<>(); cataniaCoordinates.put(CATANIA, new GeoCoordinate(CATANIA_LONGITUDE, CATANIA_LATITUDE)); Map syracuseCoordinates = new HashMap<>(); syracuseCoordinates.put(SYRACUSE, new GeoCoordinate(SYRACUSE_LONGITUDE, SYRACUSE_LATITUDE)); Long addPalermo = exec(commandObjects.geoadd(key, PALERMO_LONGITUDE, PALERMO_LATITUDE, PALERMO)); assertThat(addPalermo, equalTo(1L)); List radiusFromPalermo = exec(commandObjects.georadius( key, PALERMO_LONGITUDE, PALERMO_LATITUDE, 100, GeoUnit.KM)); assertThat(radiusFromPalermo.stream().map(GeoRadiusResponse::getMemberByString).collect(Collectors.toList()), contains(equalTo(PALERMO))); Long addCatania = exec(commandObjects.geoadd(key, cataniaCoordinates)); assertThat(addCatania, equalTo(1L)); List radiusFromCatania = exec(commandObjects.georadius( key, CATANIA_LONGITUDE, CATANIA_LATITUDE, 100, GeoUnit.KM)); assertThat(radiusFromCatania.stream().map(GeoRadiusResponse::getMemberByString).collect(Collectors.toList()), contains(equalTo(CATANIA))); Long addSyracuse = exec(commandObjects.geoadd(key, GeoAddParams.geoAddParams().nx(), syracuseCoordinates)); assertThat(addSyracuse, equalTo(1L)); List radiusEverything = exec(commandObjects.georadius( key, 15, 37, 200, GeoUnit.KM)); assertThat(radiusEverything.stream().map(GeoRadiusResponse::getMemberByString).collect(Collectors.toList()), containsInAnyOrder(equalTo(CATANIA), equalTo(SYRACUSE), equalTo(PALERMO))); } @Test public void testGeoAddAndRadiusBinary() { byte[] key = "locations".getBytes(); Map cataniaCoordinates = new HashMap<>(); cataniaCoordinates.put(CATANIA.getBytes(), new GeoCoordinate(CATANIA_LONGITUDE, CATANIA_LATITUDE)); Map syracuseCoordinates = new HashMap<>(); syracuseCoordinates.put(SYRACUSE.getBytes(), new GeoCoordinate(SYRACUSE_LONGITUDE, SYRACUSE_LATITUDE)); Long addPalermo = exec(commandObjects.geoadd(key, PALERMO_LONGITUDE, PALERMO_LATITUDE, PALERMO.getBytes())); assertThat(addPalermo, equalTo(1L)); List radiusFromPalermo = exec(commandObjects.georadius( key, PALERMO_LONGITUDE, PALERMO_LATITUDE, 100, GeoUnit.KM)); assertThat(radiusFromPalermo.stream().map(GeoRadiusResponse::getMember).collect(Collectors.toList()), contains(equalTo(PALERMO.getBytes()))); Long addCatania = exec(commandObjects.geoadd(key, cataniaCoordinates)); assertThat(addCatania, equalTo(1L)); List radiusFromCatania = exec(commandObjects.georadius( key, CATANIA_LONGITUDE, CATANIA_LATITUDE, 100, GeoUnit.KM)); assertThat(radiusFromCatania.stream().map(GeoRadiusResponse::getMember).collect(Collectors.toList()), contains(equalTo(CATANIA.getBytes()))); Long addSyracuse = exec(commandObjects.geoadd(key, GeoAddParams.geoAddParams().nx(), syracuseCoordinates)); assertThat(addSyracuse, equalTo(1L)); List radiusEverything = exec(commandObjects.georadius( key, 15, 37, 200, GeoUnit.KM)); assertThat(radiusEverything.stream().map(GeoRadiusResponse::getMember).collect(Collectors.toList()), containsInAnyOrder(equalTo(CATANIA.getBytes()), equalTo(PALERMO.getBytes()), equalTo(SYRACUSE.getBytes()))); } @Test public void testGeoDist() { String key = "locations"; byte[] binaryKey = key.getBytes(); // Add locations to calculate distance exec(commandObjects.geoadd(key, CATANIA_LONGITUDE, CATANIA_LATITUDE, CATANIA)); exec(commandObjects.geoadd(key, PALERMO_LONGITUDE, PALERMO_LATITUDE, PALERMO)); Double distance = exec(commandObjects.geodist(key, CATANIA, PALERMO)); // This is in meters, we don't try to accurately assert it. We refer to it later. assertThat(distance, notNullValue()); Double distanceWithUnit = exec(commandObjects.geodist(key, CATANIA, PALERMO, GeoUnit.KM)); assertThat(distanceWithUnit, closeTo(distance / 1000, 0.001)); Double binaryDistance = exec(commandObjects.geodist(binaryKey, CATANIA.getBytes(), PALERMO.getBytes())); assertThat(binaryDistance, closeTo(distance, 0.001)); Double binaryDistanceWithUnit = exec(commandObjects.geodist(binaryKey, CATANIA.getBytes(), PALERMO.getBytes(), GeoUnit.KM)); assertThat(binaryDistanceWithUnit, closeTo(distance / 1000, 0.001)); } @Test public void testGeoHash() { String key = "locations"; byte[] binaryKey = key.getBytes(); exec(commandObjects.geoadd(key, CATANIA_LONGITUDE, CATANIA_LATITUDE, CATANIA)); exec(commandObjects.geoadd(key, PALERMO_LONGITUDE, PALERMO_LATITUDE, PALERMO)); List hashes = exec(commandObjects.geohash(key, CATANIA, PALERMO)); assertThat(hashes, contains(notNullValue(), notNullValue())); List binaryHashes = exec(commandObjects.geohash(binaryKey, CATANIA.getBytes(), PALERMO.getBytes())); assertThat(binaryHashes, contains(hashes.get(0).getBytes(), hashes.get(1).getBytes())); } @Test public void testGeoPos() { String key = "locations"; byte[] binaryKey = key.getBytes(); exec(commandObjects.geoadd(key, CATANIA_LONGITUDE, CATANIA_LATITUDE, CATANIA)); exec(commandObjects.geoadd(key, PALERMO_LONGITUDE, PALERMO_LATITUDE, PALERMO)); List positions = exec(commandObjects.geopos(key, CATANIA, PALERMO)); assertThat(positions.size(), equalTo(2)); assertThat(positions.get(0), notNullValue()); assertThat(positions.get(0).getLongitude(), closeTo(CATANIA_LONGITUDE, 0.001)); assertThat(positions.get(0).getLatitude(), closeTo(CATANIA_LATITUDE, 0.001)); assertThat(positions.get(1), notNullValue()); assertThat(positions.get(1).getLongitude(), closeTo(PALERMO_LONGITUDE, 0.001)); assertThat(positions.get(1).getLatitude(), closeTo(PALERMO_LATITUDE, 0.001)); List binaryPositions = exec(commandObjects.geopos(binaryKey, CATANIA.getBytes(), PALERMO.getBytes())); assertThat(binaryPositions.size(), equalTo(2)); assertThat(binaryPositions.get(0), notNullValue()); assertThat(binaryPositions.get(0).getLongitude(), closeTo(CATANIA_LONGITUDE, 0.001)); assertThat(binaryPositions.get(0).getLatitude(), closeTo(CATANIA_LATITUDE, 0.001)); assertThat(binaryPositions.get(1), notNullValue()); assertThat(binaryPositions.get(1).getLongitude(), closeTo(PALERMO_LONGITUDE, 0.001)); assertThat(binaryPositions.get(1).getLatitude(), closeTo(PALERMO_LATITUDE, 0.001)); } @Test public void testGeoRadius() { String key = "locations"; byte[] binaryKey = key.getBytes(); GeoRadiusParam param = GeoRadiusParam.geoRadiusParam().withCoord().withDist(); exec(commandObjects.geoadd(key, CATANIA_LONGITUDE, CATANIA_LATITUDE, CATANIA)); exec(commandObjects.geoadd(key, PALERMO_LONGITUDE, PALERMO_LATITUDE, PALERMO)); List responses = exec(commandObjects.georadius(key, CATANIA_LONGITUDE, CATANIA_LATITUDE, 200, GeoUnit.KM)); // we got distances, but no coordinates assertThat(responses.stream().map(GeoRadiusResponse::getMemberByString).collect(Collectors.toList()), containsInAnyOrder(CATANIA, PALERMO)); assertThat(responses.stream().map(GeoRadiusResponse::getDistance).collect(Collectors.toList()), containsInAnyOrder(notNullValue(), notNullValue())); assertThat(responses.stream().map(GeoRadiusResponse::getCoordinate).collect(Collectors.toList()), containsInAnyOrder(nullValue(), nullValue())); List responsesWithParam = exec(commandObjects.georadius(key, CATANIA_LONGITUDE, CATANIA_LATITUDE, 200, GeoUnit.KM, param)); // we got distances, and coordinates assertThat(responsesWithParam.stream().map(GeoRadiusResponse::getMemberByString).collect(Collectors.toList()), containsInAnyOrder(CATANIA, PALERMO)); assertThat(responsesWithParam.stream().map(GeoRadiusResponse::getDistance).collect(Collectors.toList()), containsInAnyOrder(notNullValue(), notNullValue())); assertThat(responsesWithParam.stream().map(GeoRadiusResponse::getCoordinate).collect(Collectors.toList()), containsInAnyOrder(notNullValue(), notNullValue())); assertThat(responsesWithParam.stream().map(GeoRadiusResponse::getCoordinate).map(GeoCoordinate::getLatitude).collect(Collectors.toList()), containsInAnyOrder(closeTo(PALERMO_LATITUDE, 0.001), closeTo(CATANIA_LATITUDE, 0.001))); assertThat(responsesWithParam.stream().map(GeoRadiusResponse::getCoordinate).map(GeoCoordinate::getLongitude).collect(Collectors.toList()), containsInAnyOrder(closeTo(PALERMO_LONGITUDE, 0.001), closeTo(CATANIA_LONGITUDE, 0.001))); List binaryResponses = exec(commandObjects.georadius(binaryKey, CATANIA_LONGITUDE, CATANIA_LATITUDE, 200, GeoUnit.KM)); // distances, but no coordinates assertThat(binaryResponses.stream().map(GeoRadiusResponse::getMemberByString).collect(Collectors.toList()), containsInAnyOrder(CATANIA, PALERMO)); assertThat(binaryResponses.stream().map(GeoRadiusResponse::getDistance).collect(Collectors.toList()), containsInAnyOrder(notNullValue(), notNullValue())); assertThat(binaryResponses.stream().map(GeoRadiusResponse::getCoordinate).collect(Collectors.toList()), containsInAnyOrder(nullValue(), nullValue())); List binaryResponsesWithParam = exec(commandObjects.georadius(binaryKey, CATANIA_LONGITUDE, CATANIA_LATITUDE, 200, GeoUnit.KM, param)); // distances, and coordinates assertThat(binaryResponsesWithParam.stream().map(GeoRadiusResponse::getMember).collect(Collectors.toList()), containsInAnyOrder(CATANIA.getBytes(), PALERMO.getBytes())); assertThat(binaryResponsesWithParam.stream().map(GeoRadiusResponse::getDistance).collect(Collectors.toList()), containsInAnyOrder(notNullValue(), notNullValue())); assertThat(binaryResponsesWithParam.stream().map(GeoRadiusResponse::getCoordinate).collect(Collectors.toList()), containsInAnyOrder(notNullValue(), notNullValue())); assertThat(binaryResponsesWithParam.stream().map(GeoRadiusResponse::getCoordinate).map(GeoCoordinate::getLatitude).collect(Collectors.toList()), containsInAnyOrder(closeTo(PALERMO_LATITUDE, 0.001), closeTo(CATANIA_LATITUDE, 0.001))); assertThat(binaryResponsesWithParam.stream().map(GeoRadiusResponse::getCoordinate).map(GeoCoordinate::getLongitude).collect(Collectors.toList()), containsInAnyOrder(closeTo(PALERMO_LONGITUDE, 0.001), closeTo(CATANIA_LONGITUDE, 0.001))); } @Test public void testGeoRadiusReadonly() { String key = "locations"; byte[] binaryKey = key.getBytes(); GeoRadiusParam param = GeoRadiusParam.geoRadiusParam().withCoord().withDist(); exec(commandObjects.geoadd(key, CATANIA_LONGITUDE, CATANIA_LATITUDE, CATANIA)); exec(commandObjects.geoadd(key, PALERMO_LONGITUDE, PALERMO_LATITUDE, PALERMO)); List responses = exec(commandObjects.georadiusReadonly(key, CATANIA_LONGITUDE, CATANIA_LATITUDE, 200, GeoUnit.KM)); // we got distances, but no coordinates assertThat(responses.stream().map(GeoRadiusResponse::getMemberByString).collect(Collectors.toList()), containsInAnyOrder(CATANIA, PALERMO)); assertThat(responses.stream().map(GeoRadiusResponse::getDistance).collect(Collectors.toList()), containsInAnyOrder(notNullValue(), notNullValue())); assertThat(responses.stream().map(GeoRadiusResponse::getCoordinate).collect(Collectors.toList()), containsInAnyOrder(nullValue(), nullValue())); List responsesWithParam = exec(commandObjects.georadiusReadonly(key, CATANIA_LONGITUDE, CATANIA_LATITUDE, 200, GeoUnit.KM, param)); // we got distances, and coordinates assertThat(responsesWithParam.stream().map(GeoRadiusResponse::getMemberByString).collect(Collectors.toList()), containsInAnyOrder(CATANIA, PALERMO)); assertThat(responsesWithParam.stream().map(GeoRadiusResponse::getDistance).collect(Collectors.toList()), containsInAnyOrder(notNullValue(), notNullValue())); assertThat(responsesWithParam.stream().map(GeoRadiusResponse::getCoordinate).collect(Collectors.toList()), containsInAnyOrder(notNullValue(), notNullValue())); assertThat(responsesWithParam.stream().map(GeoRadiusResponse::getCoordinate).map(GeoCoordinate::getLatitude).collect(Collectors.toList()), containsInAnyOrder(closeTo(PALERMO_LATITUDE, 0.001), closeTo(CATANIA_LATITUDE, 0.001))); assertThat(responsesWithParam.stream().map(GeoRadiusResponse::getCoordinate).map(GeoCoordinate::getLongitude).collect(Collectors.toList()), containsInAnyOrder(closeTo(PALERMO_LONGITUDE, 0.001), closeTo(CATANIA_LONGITUDE, 0.001))); List binaryResponses = exec(commandObjects.georadiusReadonly(binaryKey, CATANIA_LONGITUDE, CATANIA_LATITUDE, 200, GeoUnit.KM)); // distances, but no coordinates assertThat(binaryResponses.stream().map(GeoRadiusResponse::getMemberByString).collect(Collectors.toList()), containsInAnyOrder(CATANIA, PALERMO)); assertThat(binaryResponses.stream().map(GeoRadiusResponse::getDistance).collect(Collectors.toList()), containsInAnyOrder(notNullValue(), notNullValue())); assertThat(binaryResponses.stream().map(GeoRadiusResponse::getCoordinate).collect(Collectors.toList()), containsInAnyOrder(nullValue(), nullValue())); List binaryResponsesWithParam = exec(commandObjects.georadiusReadonly(binaryKey, CATANIA_LONGITUDE, CATANIA_LATITUDE, 200, GeoUnit.KM, param)); // distances, and coordinates assertThat(binaryResponsesWithParam.stream().map(GeoRadiusResponse::getMember).collect(Collectors.toList()), containsInAnyOrder(CATANIA.getBytes(), PALERMO.getBytes())); assertThat(binaryResponsesWithParam.stream().map(GeoRadiusResponse::getDistance).collect(Collectors.toList()), containsInAnyOrder(notNullValue(), notNullValue())); assertThat(binaryResponsesWithParam.stream().map(GeoRadiusResponse::getCoordinate).collect(Collectors.toList()), containsInAnyOrder(notNullValue(), notNullValue())); assertThat(binaryResponsesWithParam.stream().map(GeoRadiusResponse::getCoordinate).map(GeoCoordinate::getLatitude).collect(Collectors.toList()), containsInAnyOrder(closeTo(PALERMO_LATITUDE, 0.001), closeTo(CATANIA_LATITUDE, 0.001))); assertThat(binaryResponsesWithParam.stream().map(GeoRadiusResponse::getCoordinate).map(GeoCoordinate::getLongitude).collect(Collectors.toList()), containsInAnyOrder(closeTo(PALERMO_LONGITUDE, 0.001), closeTo(CATANIA_LONGITUDE, 0.001))); } @Test public void testGeoRadiusByMember() { String key = "locations"; byte[] binaryKey = key.getBytes(); GeoRadiusParam param = GeoRadiusParam.geoRadiusParam().withCoord().withDist(); exec(commandObjects.geoadd(key, CATANIA_LONGITUDE, CATANIA_LATITUDE, CATANIA)); exec(commandObjects.geoadd(key, PALERMO_LONGITUDE, PALERMO_LATITUDE, PALERMO)); exec(commandObjects.geoadd(key, AGRIGENTO_LONGITUDE, AGRIGENTO_LATITUDE, AGRIGENTO)); List responses = exec(commandObjects.georadiusByMember(key, AGRIGENTO, 100, GeoUnit.KM)); assertThat(responses.stream().map(GeoRadiusResponse::getMemberByString).collect(Collectors.toList()), containsInAnyOrder(AGRIGENTO, PALERMO)); assertThat(responses.stream().map(GeoRadiusResponse::getDistance).collect(Collectors.toList()), containsInAnyOrder(notNullValue(), notNullValue())); assertThat(responses.stream().map(GeoRadiusResponse::getCoordinate).collect(Collectors.toList()), containsInAnyOrder(nullValue(), nullValue())); List responsesWithParam = exec(commandObjects.georadiusByMember(key, AGRIGENTO, 100, GeoUnit.KM, param)); assertThat(responsesWithParam.stream().map(GeoRadiusResponse::getMemberByString).collect(Collectors.toList()), containsInAnyOrder(AGRIGENTO, PALERMO)); assertThat(responsesWithParam.stream().map(GeoRadiusResponse::getDistance).collect(Collectors.toList()), containsInAnyOrder(notNullValue(), notNullValue())); assertThat(responsesWithParam.stream().map(GeoRadiusResponse::getCoordinate).collect(Collectors.toList()), containsInAnyOrder(notNullValue(), notNullValue())); assertThat(responsesWithParam.stream().map(GeoRadiusResponse::getCoordinate).map(GeoCoordinate::getLatitude).collect(Collectors.toList()), containsInAnyOrder(closeTo(PALERMO_LATITUDE, 0.001), closeTo(AGRIGENTO_LATITUDE, 0.001))); assertThat(responsesWithParam.stream().map(GeoRadiusResponse::getCoordinate).map(GeoCoordinate::getLongitude).collect(Collectors.toList()), containsInAnyOrder(closeTo(PALERMO_LONGITUDE, 0.001), closeTo(AGRIGENTO_LONGITUDE, 0.001))); List binaryResponses = exec(commandObjects.georadiusByMember(binaryKey, AGRIGENTO.getBytes(), 100, GeoUnit.KM)); assertThat(binaryResponses.stream().map(GeoRadiusResponse::getMemberByString).collect(Collectors.toList()), containsInAnyOrder(AGRIGENTO, PALERMO)); assertThat(binaryResponses.stream().map(GeoRadiusResponse::getDistance).collect(Collectors.toList()), containsInAnyOrder(notNullValue(), notNullValue())); assertThat(binaryResponses.stream().map(GeoRadiusResponse::getCoordinate).collect(Collectors.toList()), containsInAnyOrder(nullValue(), nullValue())); List binaryResponsesWithParam = exec(commandObjects.georadiusByMember(binaryKey, AGRIGENTO.getBytes(), 100, GeoUnit.KM, param)); assertThat(binaryResponsesWithParam.stream().map(GeoRadiusResponse::getMember).collect(Collectors.toList()), containsInAnyOrder(AGRIGENTO.getBytes(), PALERMO.getBytes())); assertThat(binaryResponsesWithParam.stream().map(GeoRadiusResponse::getDistance).collect(Collectors.toList()), containsInAnyOrder(notNullValue(), notNullValue())); assertThat(binaryResponsesWithParam.stream().map(GeoRadiusResponse::getCoordinate).collect(Collectors.toList()), containsInAnyOrder(notNullValue(), notNullValue())); assertThat(binaryResponsesWithParam.stream().map(GeoRadiusResponse::getCoordinate).map(GeoCoordinate::getLatitude).collect(Collectors.toList()), containsInAnyOrder(closeTo(PALERMO_LATITUDE, 0.001), closeTo(AGRIGENTO_LATITUDE, 0.001))); assertThat(binaryResponsesWithParam.stream().map(GeoRadiusResponse::getCoordinate).map(GeoCoordinate::getLongitude).collect(Collectors.toList()), containsInAnyOrder(closeTo(PALERMO_LONGITUDE, 0.001), closeTo(AGRIGENTO_LONGITUDE, 0.001))); } @Test public void testGeoRadiusByMemberReadonly() { String key = "locations"; byte[] binaryKey = key.getBytes(); GeoRadiusParam param = GeoRadiusParam.geoRadiusParam().withCoord().withDist(); exec(commandObjects.geoadd(key, CATANIA_LONGITUDE, CATANIA_LATITUDE, CATANIA)); exec(commandObjects.geoadd(key, PALERMO_LONGITUDE, PALERMO_LATITUDE, PALERMO)); exec(commandObjects.geoadd(key, AGRIGENTO_LONGITUDE, AGRIGENTO_LATITUDE, AGRIGENTO)); List responses = exec(commandObjects.georadiusByMemberReadonly(key, AGRIGENTO, 100, GeoUnit.KM)); assertThat(responses.stream().map(GeoRadiusResponse::getMemberByString).collect(Collectors.toList()), containsInAnyOrder(AGRIGENTO, PALERMO)); assertThat(responses.stream().map(GeoRadiusResponse::getDistance).collect(Collectors.toList()), containsInAnyOrder(notNullValue(), notNullValue())); assertThat(responses.stream().map(GeoRadiusResponse::getCoordinate).collect(Collectors.toList()), containsInAnyOrder(nullValue(), nullValue())); List responsesWithParam = exec(commandObjects.georadiusByMemberReadonly(key, AGRIGENTO, 100, GeoUnit.KM, param)); assertThat(responsesWithParam.stream().map(GeoRadiusResponse::getMemberByString).collect(Collectors.toList()), containsInAnyOrder(AGRIGENTO, PALERMO)); assertThat(responsesWithParam.stream().map(GeoRadiusResponse::getDistance).collect(Collectors.toList()), containsInAnyOrder(notNullValue(), notNullValue())); assertThat(responsesWithParam.stream().map(GeoRadiusResponse::getCoordinate).collect(Collectors.toList()), containsInAnyOrder(notNullValue(), notNullValue())); assertThat(responsesWithParam.stream().map(GeoRadiusResponse::getCoordinate).map(GeoCoordinate::getLatitude).collect(Collectors.toList()), containsInAnyOrder(closeTo(PALERMO_LATITUDE, 0.001), closeTo(AGRIGENTO_LATITUDE, 0.001))); assertThat(responsesWithParam.stream().map(GeoRadiusResponse::getCoordinate).map(GeoCoordinate::getLongitude).collect(Collectors.toList()), containsInAnyOrder(closeTo(PALERMO_LONGITUDE, 0.001), closeTo(AGRIGENTO_LONGITUDE, 0.001))); List binaryResponses = exec(commandObjects.georadiusByMemberReadonly(binaryKey, AGRIGENTO.getBytes(), 100, GeoUnit.KM)); assertThat(binaryResponses.stream().map(GeoRadiusResponse::getMemberByString).collect(Collectors.toList()), containsInAnyOrder(AGRIGENTO, PALERMO)); assertThat(binaryResponses.stream().map(GeoRadiusResponse::getDistance).collect(Collectors.toList()), containsInAnyOrder(notNullValue(), notNullValue())); assertThat(binaryResponses.stream().map(GeoRadiusResponse::getCoordinate).collect(Collectors.toList()), containsInAnyOrder(nullValue(), nullValue())); List binaryResponsesWithParam = exec(commandObjects.georadiusByMemberReadonly(binaryKey, AGRIGENTO.getBytes(), 100, GeoUnit.KM, param)); assertThat(binaryResponsesWithParam.stream().map(GeoRadiusResponse::getMember).collect(Collectors.toList()), containsInAnyOrder(AGRIGENTO.getBytes(), PALERMO.getBytes())); assertThat(binaryResponsesWithParam.stream().map(GeoRadiusResponse::getDistance).collect(Collectors.toList()), containsInAnyOrder(notNullValue(), notNullValue())); assertThat(binaryResponsesWithParam.stream().map(GeoRadiusResponse::getCoordinate).collect(Collectors.toList()), containsInAnyOrder(notNullValue(), notNullValue())); assertThat(binaryResponsesWithParam.stream().map(GeoRadiusResponse::getCoordinate).map(GeoCoordinate::getLatitude).collect(Collectors.toList()), containsInAnyOrder(closeTo(PALERMO_LATITUDE, 0.001), closeTo(AGRIGENTO_LATITUDE, 0.001))); assertThat(binaryResponsesWithParam.stream().map(GeoRadiusResponse::getCoordinate).map(GeoCoordinate::getLongitude).collect(Collectors.toList()), containsInAnyOrder(closeTo(PALERMO_LONGITUDE, 0.001), closeTo(AGRIGENTO_LONGITUDE, 0.001))); } @Test @ConditionalOnEnv(value = TestEnvUtil.ENV_REDIS_ENTERPRISE, enabled = false) public void testGeoradiusStore() { String key = "locations"; byte[] binaryKey = key.getBytes(); String destinationKey = "result"; String binaryDestinationKey = "resultBinary"; GeoRadiusParam param = GeoRadiusParam.geoRadiusParam().sortAscending(); exec(commandObjects.geoadd(key, PALERMO_LONGITUDE, PALERMO_LATITUDE, PALERMO)); exec(commandObjects.geoadd(key, CATANIA_LONGITUDE, CATANIA_LATITUDE, CATANIA)); GeoRadiusStoreParam storeParam = GeoRadiusStoreParam.geoRadiusStoreParam().store(destinationKey); Long store = exec(commandObjects.georadiusStore(key, PALERMO_LONGITUDE, PALERMO_LATITUDE, 200, GeoUnit.KM, param, storeParam)); assertThat(store, equalTo(2L)); List destination = exec(commandObjects.zrange(destinationKey, 0, -1)); assertThat(destination, containsInAnyOrder(PALERMO, CATANIA)); GeoRadiusStoreParam storeParamForBinary = GeoRadiusStoreParam.geoRadiusStoreParam().store(binaryDestinationKey); Long storeBinary = exec(commandObjects.georadiusStore(binaryKey, PALERMO_LONGITUDE, PALERMO_LATITUDE, 200, GeoUnit.KM, param, storeParamForBinary)); assertThat(storeBinary, equalTo(2L)); destination = exec(commandObjects.zrange(binaryDestinationKey, 0, -1)); assertThat(destination, containsInAnyOrder(PALERMO, CATANIA)); } @Test @ConditionalOnEnv(value = TestEnvUtil.ENV_REDIS_ENTERPRISE, enabled = false) public void testGeoradiusByMemberStore() { String key = "locations"; byte[] binaryKey = key.getBytes(); String destinationKey = "result"; String binaryDestinationKey = "resultBinary"; GeoRadiusParam param = GeoRadiusParam.geoRadiusParam().sortAscending(); exec(commandObjects.geoadd(key, PALERMO_LONGITUDE, PALERMO_LATITUDE, PALERMO)); exec(commandObjects.geoadd(key, CATANIA_LONGITUDE, CATANIA_LATITUDE, CATANIA)); GeoRadiusStoreParam storeParam = GeoRadiusStoreParam.geoRadiusStoreParam().store(destinationKey); Long store = exec(commandObjects.georadiusByMemberStore(key, PALERMO, 200, GeoUnit.KM, param, storeParam)); assertThat(store, equalTo(2L)); List storedResults = exec(commandObjects.zrange(destinationKey, 0, -1)); assertThat(storedResults, containsInAnyOrder(PALERMO, CATANIA)); GeoRadiusStoreParam storeParamForBinary = GeoRadiusStoreParam.geoRadiusStoreParam().store(binaryDestinationKey); Long storeBinary = exec(commandObjects.georadiusByMemberStore(binaryKey, PALERMO.getBytes(), 200, GeoUnit.KM, param, storeParamForBinary)); assertThat(storeBinary, equalTo(2L)); storedResults = exec(commandObjects.zrange(binaryDestinationKey, 0, -1)); assertThat(storedResults, containsInAnyOrder(PALERMO, CATANIA)); } @Test public void testGeosearch() { String key = "locations"; GeoCoordinate palermoCoord = new GeoCoordinate(PALERMO_LONGITUDE, PALERMO_LATITUDE); exec(commandObjects.geoadd(key, PALERMO_LONGITUDE, PALERMO_LATITUDE, PALERMO)); exec(commandObjects.geoadd(key, CATANIA_LONGITUDE, CATANIA_LATITUDE, CATANIA)); List resultsByMember = exec(commandObjects.geosearch(key, PALERMO, 200, GeoUnit.KM)); assertThat(resultsByMember.stream().map(GeoRadiusResponse::getMemberByString).collect(Collectors.toList()), containsInAnyOrder(PALERMO, CATANIA)); List resultsByCoord = exec(commandObjects.geosearch(key, palermoCoord, 200, GeoUnit.KM)); assertThat(resultsByCoord.stream().map(GeoRadiusResponse::getMemberByString).collect(Collectors.toList()), containsInAnyOrder(PALERMO, CATANIA)); List resultsByMemberBox = exec(commandObjects.geosearch(key, PALERMO, 200, 200, GeoUnit.KM)); assertThat(resultsByMemberBox.stream().map(GeoRadiusResponse::getMemberByString).collect(Collectors.toList()), containsInAnyOrder(PALERMO)); List resultsByCoordBox = exec(commandObjects.geosearch(key, palermoCoord, 200, 200, GeoUnit.KM)); assertThat(resultsByCoordBox.stream().map(GeoRadiusResponse::getMemberByString).collect(Collectors.toList()), containsInAnyOrder(PALERMO)); GeoSearchParam params = GeoSearchParam.geoSearchParam() .byRadius(200, GeoUnit.KM).withCoord().withDist().fromMember(PALERMO); List resultsWithParams = exec(commandObjects.geosearch(key, params)); assertThat(resultsWithParams.stream().map(GeoRadiusResponse::getMemberByString).collect(Collectors.toList()), containsInAnyOrder(PALERMO, CATANIA)); List resultsInvalidKey = exec(commandObjects.geosearch("invalidKey", PALERMO, 100, GeoUnit.KM)); assertThat(resultsInvalidKey, empty()); } @Test public void testGeosearchBinary() { byte[] key = "locations".getBytes(); GeoCoordinate palermoCoord = new GeoCoordinate(PALERMO_LONGITUDE, PALERMO_LATITUDE); exec(commandObjects.geoadd(key, PALERMO_LONGITUDE, PALERMO_LATITUDE, PALERMO.getBytes())); exec(commandObjects.geoadd(key, CATANIA_LONGITUDE, CATANIA_LATITUDE, CATANIA.getBytes())); List resultsByMember = exec(commandObjects.geosearch(key, PALERMO.getBytes(), 200, GeoUnit.KM)); assertThat(resultsByMember.stream().map(GeoRadiusResponse::getMemberByString).collect(Collectors.toList()), containsInAnyOrder(PALERMO, CATANIA)); List resultsByCoord = exec(commandObjects.geosearch(key, palermoCoord, 200, GeoUnit.KM)); assertThat(resultsByCoord.stream().map(GeoRadiusResponse::getMemberByString).collect(Collectors.toList()), containsInAnyOrder(PALERMO, CATANIA)); List resultsByMemberBox = exec(commandObjects.geosearch(key, PALERMO.getBytes(), 200, 200, GeoUnit.KM)); assertThat(resultsByMemberBox.stream().map(GeoRadiusResponse::getMemberByString).collect(Collectors.toList()), containsInAnyOrder(PALERMO)); List resultsByCoordBox = exec(commandObjects.geosearch(key, palermoCoord, 200, 200, GeoUnit.KM)); assertThat(resultsByCoordBox.stream().map(GeoRadiusResponse::getMemberByString).collect(Collectors.toList()), containsInAnyOrder(PALERMO)); GeoSearchParam params = GeoSearchParam.geoSearchParam() .byRadius(200, GeoUnit.KM).withCoord().withDist().fromMember(PALERMO); List resultsWithParams = exec(commandObjects.geosearch(key, params)); assertThat(resultsWithParams.stream().map(GeoRadiusResponse::getMemberByString).collect(Collectors.toList()), containsInAnyOrder(PALERMO, CATANIA)); List resultsInvalidKey = exec(commandObjects.geosearch("invalidKey".getBytes(), PALERMO.getBytes(), 100, GeoUnit.KM)); assertThat(resultsInvalidKey, empty()); } @Test @ConditionalOnEnv(value = TestEnvUtil.ENV_REDIS_ENTERPRISE, enabled = false) public void testGeosearchStore() { String srcKey = "locations"; String destKey = "locationsStore"; GeoCoordinate palermoCoord = new GeoCoordinate(PALERMO_LONGITUDE, PALERMO_LATITUDE); exec(commandObjects.geoadd(srcKey, PALERMO_LONGITUDE, PALERMO_LATITUDE, PALERMO)); exec(commandObjects.geoadd(srcKey, CATANIA_LONGITUDE, CATANIA_LATITUDE, CATANIA)); Long storeByMember = exec(commandObjects.geosearchStore(destKey, srcKey, PALERMO, 200, GeoUnit.KM)); assertThat(storeByMember, equalTo(2L)); List storedResultsByMember = exec(commandObjects.zrange(destKey, 0, -1)); assertThat(storedResultsByMember, containsInAnyOrder(PALERMO, CATANIA)); // Reset exec(commandObjects.del(destKey)); Long storeByCoord = exec(commandObjects.geosearchStore(destKey, srcKey, palermoCoord, 200, GeoUnit.KM)); assertThat(storeByCoord, equalTo(2L)); List storedResultsByCoord = exec(commandObjects.zrange(destKey, 0, -1)); assertThat(storedResultsByCoord, containsInAnyOrder(PALERMO, CATANIA)); exec(commandObjects.del(destKey)); Long storeByMemberBox = exec(commandObjects.geosearchStore(destKey, srcKey, PALERMO, 200, 200, GeoUnit.KM)); assertThat(storeByMemberBox, equalTo(1L)); List storedResultsByMemberBox = exec(commandObjects.zrange(destKey, 0, -1)); assertThat(storedResultsByMemberBox, containsInAnyOrder(PALERMO)); exec(commandObjects.del(destKey)); Long storeByCoordBox = exec(commandObjects.geosearchStore(destKey, srcKey, palermoCoord, 200, 200, GeoUnit.KM)); assertThat(storeByCoordBox, equalTo(1L)); List storedResultsByCoordBox = exec(commandObjects.zrange(destKey, 0, -1)); assertThat(storedResultsByCoordBox, containsInAnyOrder(PALERMO)); exec(commandObjects.del(destKey)); GeoSearchParam params = GeoSearchParam.geoSearchParam() .byRadius(200, GeoUnit.KM).fromMember(PALERMO); Long storeWithParams = exec(commandObjects.geosearchStore(destKey, srcKey, params)); assertThat(storeWithParams, equalTo(2L)); List storedResultsWithParams = exec(commandObjects.zrange(destKey, 0, -1)); assertThat(storedResultsWithParams, containsInAnyOrder(PALERMO, CATANIA)); } @Test @ConditionalOnEnv(value = TestEnvUtil.ENV_REDIS_ENTERPRISE, enabled = false) public void testGeosearchStoreBinary() { byte[] srcKey = "locations".getBytes(); byte[] destKey = "locationsStore".getBytes(); GeoCoordinate palermoCoord = new GeoCoordinate(PALERMO_LONGITUDE, PALERMO_LATITUDE); exec(commandObjects.geoadd(srcKey, PALERMO_LONGITUDE, PALERMO_LATITUDE, PALERMO.getBytes())); exec(commandObjects.geoadd(srcKey, CATANIA_LONGITUDE, CATANIA_LATITUDE, CATANIA.getBytes())); Long storeByMember = exec(commandObjects.geosearchStore(destKey, srcKey, PALERMO.getBytes(), 200, GeoUnit.KM)); assertThat(storeByMember, equalTo(2L)); List storedResultsByMember = exec(commandObjects.zrange(destKey, 0, -1)); assertThat(storedResultsByMember, containsInAnyOrder(PALERMO.getBytes(), CATANIA.getBytes())); // Reset exec(commandObjects.del(destKey)); Long storeByCoord = exec(commandObjects.geosearchStore(destKey, srcKey, palermoCoord, 200, GeoUnit.KM)); assertThat(storeByCoord, equalTo(2L)); List storedResultsByCoord = exec(commandObjects.zrange(destKey, 0, -1)); assertThat(storedResultsByCoord, containsInAnyOrder(PALERMO.getBytes(), CATANIA.getBytes())); exec(commandObjects.del(destKey)); Long storeByMemberBox = exec(commandObjects.geosearchStore(destKey, srcKey, PALERMO.getBytes(), 200, 200, GeoUnit.KM)); assertThat(storeByMemberBox, equalTo(1L)); List storedResultsByMemberBox = exec(commandObjects.zrange(destKey, 0, -1)); assertThat(storedResultsByMemberBox, containsInAnyOrder(PALERMO.getBytes())); exec(commandObjects.del(destKey)); Long storeByCoordBox = exec(commandObjects.geosearchStore(destKey, srcKey, palermoCoord, 200, 200, GeoUnit.KM)); assertThat(storeByCoordBox, equalTo(1L)); List storedResultsByCoordBox = exec(commandObjects.zrange(destKey, 0, -1)); assertThat(storedResultsByCoordBox, containsInAnyOrder(PALERMO.getBytes())); exec(commandObjects.del(destKey)); GeoSearchParam params = GeoSearchParam.geoSearchParam() .byRadius(200, GeoUnit.KM).fromMember(PALERMO); Long storeWithParams = exec(commandObjects.geosearchStore(destKey, srcKey, params)); assertThat(storeWithParams, equalTo(2L)); List storedResultsWithParams = exec(commandObjects.zrange(destKey, 0, -1)); assertThat(storedResultsWithParams, containsInAnyOrder(PALERMO.getBytes(), CATANIA.getBytes())); } @Test @ConditionalOnEnv(value = TestEnvUtil.ENV_REDIS_ENTERPRISE, enabled = false) public void testGeosearchStoreStoreDist() { String srcKey = "locations"; byte[] srcKeyBytes = srcKey.getBytes(); String destKey = "resultKey"; byte[] destKeyBytes = destKey.getBytes(); exec(commandObjects.geoadd(srcKey, PALERMO_LONGITUDE, PALERMO_LATITUDE, PALERMO)); exec(commandObjects.geoadd(srcKey, CATANIA_LONGITUDE, CATANIA_LATITUDE, CATANIA)); exec(commandObjects.geoadd(srcKey, SYRACUSE_LONGITUDE, SYRACUSE_LATITUDE, SYRACUSE)); GeoSearchParam params = new GeoSearchParam() .byRadius(100, GeoUnit.KM).fromLonLat(15, 37); Long store = exec(commandObjects.geosearchStoreStoreDist(destKey, srcKey, params)); assertThat(store, equalTo(2L)); List dstContent = exec(commandObjects.zrange(destKey, 0, -1)); assertThat(dstContent, containsInAnyOrder(CATANIA, SYRACUSE)); exec(commandObjects.del(destKey)); Long storeWithBytes = exec(commandObjects.geosearchStoreStoreDist(destKeyBytes, srcKeyBytes, params)); assertThat(storeWithBytes, equalTo(2L)); List dstContentWithBytes = exec(commandObjects.zrange(destKeyBytes, 0, -1)); assertThat(dstContentWithBytes, containsInAnyOrder(CATANIA.getBytes(), SYRACUSE.getBytes())); } } ================================================ FILE: src/test/java/redis/clients/jedis/commands/commandobjects/CommandObjectsHashCommandsTest.java ================================================ package redis.clients.jedis.commands.commandobjects; import static java.util.Arrays.asList; import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.allOf; import static org.hamcrest.Matchers.anyOf; import static org.hamcrest.Matchers.both; import static org.hamcrest.Matchers.contains; import static org.hamcrest.Matchers.containsInAnyOrder; import static org.hamcrest.Matchers.equalTo; import static org.hamcrest.Matchers.everyItem; import static org.hamcrest.Matchers.greaterThan; import static org.hamcrest.Matchers.greaterThanOrEqualTo; import static org.hamcrest.Matchers.hasEntry; import static org.hamcrest.Matchers.hasSize; import static org.hamcrest.Matchers.lessThanOrEqualTo; import static org.hamcrest.Matchers.nullValue; import static redis.clients.jedis.util.AssertUtil.assertByteArrayListEquals; import static org.hamcrest.CoreMatchers.is; import java.util.Collections; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Set; import io.redis.test.annotations.SinceRedisVersion; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Tag; import redis.clients.jedis.RedisProtocol; import redis.clients.jedis.args.ExpiryOption; import redis.clients.jedis.params.HGetExParams; import redis.clients.jedis.params.HSetExParams; import redis.clients.jedis.params.ScanParams; import redis.clients.jedis.resps.ScanResult; /** * Tests related to Hash commands. */ @Tag("integration") public class CommandObjectsHashCommandsTest extends CommandObjectsStandaloneTestBase { private final byte[] bfoo = { 0x01, 0x02, 0x03, 0x04 }; private final byte[] bcar = { 0x09, 0x0A, 0x0B, 0x0C }; private final byte[] bbar1 = { 0x05, 0x06, 0x07, 0x08, 0x0A }; private final byte[] bbar2 = { 0x05, 0x06, 0x07, 0x08, 0x0B }; private final byte[] bbar3 = { 0x05, 0x06, 0x07, 0x08, 0x0C }; public CommandObjectsHashCommandsTest(RedisProtocol protocol) { super(protocol); } @Test public void testHashSetGet() { String key = "hashKey"; String field = "name"; String value = "John"; String getInitial = exec(commandObjects.hget(key, field)); assertThat(getInitial, nullValue()); Long set = exec(commandObjects.hset(key, field, value)); assertThat(set, equalTo(1L)); String get = exec(commandObjects.hget(key, field)); assertThat(get, equalTo(value)); } @Test public void testHashSetGetBinary() { byte[] key = "hashKeyBytes".getBytes(); byte[] field = "field".getBytes(); byte[] value = "value".getBytes(); byte[] getInitial = exec(commandObjects.hget(key, field)); assertThat(getInitial, nullValue()); Long set = exec(commandObjects.hset(key, field, value)); assertThat(set, equalTo(1L)); byte[] get = exec(commandObjects.hget(key, field)); assertThat(get, equalTo(value)); } @Test @SinceRedisVersion("7.9.0") public void testHgetex() { String key = "hashKey"; String field = "name"; String value = "John"; long seconds = 20; exec(commandObjects.hset(key, field, value)); List fieldValues = exec( commandObjects.hgetex(key, new HGetExParams().ex(seconds), field)); assertThat(fieldValues, is(Collections.singletonList(value))); List ttlList = exec(commandObjects.httl(key, field)); Long ttl = ttlList.get(0); assertThat(ttl, greaterThanOrEqualTo(seconds - 1)); } @Test @SinceRedisVersion("7.9.0") public void testHgetexBinary() { byte[] key = "hashKeyBytes".getBytes(); byte[] field = "name".getBytes(); byte[] value = "John".getBytes(); long seconds = 20; exec(commandObjects.hset(key, field, value)); List get = exec(commandObjects.hgetex(key, new HGetExParams().ex(seconds), field)); assertByteArrayListEquals(get, Collections.singletonList(value)); List ttlList = exec(commandObjects.httl(key, field)); Long ttl = ttlList.get(0); assertThat(ttl, greaterThanOrEqualTo(seconds - 1)); assertThat(ttl, greaterThanOrEqualTo(seconds - 1)); } @Test @SinceRedisVersion("7.9.0") public void testHsetex() { String key = "hashKey"; String field = "name"; String value = "John"; long seconds = 20; Long set = exec(commandObjects.hsetex(key, new HSetExParams().ex(seconds), field, value)); assertThat(set, equalTo(1L)); String get = exec(commandObjects.hget(key, field)); assertThat(get, equalTo(value)); List ttlList = exec(commandObjects.httl(key, field)); Long ttl = ttlList.get(0); assertThat(ttl, greaterThanOrEqualTo(seconds - 1)); } @Test @SinceRedisVersion("7.9.0") public void testHsetexBinary() { byte[] key = "hashKeyBytes".getBytes(); byte[] field = "name".getBytes(); byte[] value = "John".getBytes(); long seconds = 20; Long set = exec(commandObjects.hsetex(key, new HSetExParams().ex(seconds), field, value)); assertThat(set, equalTo(1L)); byte[] get = exec(commandObjects.hget(key, field)); assertThat(get, equalTo(value)); List ttlList = exec(commandObjects.httl(key, field)); Long ttl = ttlList.get(0); assertThat(ttl, greaterThanOrEqualTo(seconds - 1)); assertThat(ttl, greaterThanOrEqualTo(seconds - 1)); } @Test @SinceRedisVersion("7.9.0") public void testHgetdel() { String key = "hashKey"; String field = "name"; String value = "John"; exec(commandObjects.hset(key, field, value)); List getDel = exec(commandObjects.hgetdel(key, field)); assertThat(getDel, is(Collections.singletonList(value))); String getAfterDel = exec(commandObjects.hget(key, field)); assertThat(getAfterDel, nullValue()); } @Test @SinceRedisVersion("7.9.0") public void testHgetdelBinary() { byte[] key = "hashKeyBytes".getBytes(); byte[] field = "field".getBytes(); byte[] value = "value".getBytes(); exec(commandObjects.hset(key, field, value)); List getDel = exec(commandObjects.hgetdel(key, field)); assertByteArrayListEquals(Collections.singletonList(value), getDel); byte[] getAfterDel = exec(commandObjects.hget(key, field)); assertThat(getAfterDel, nullValue()); } @Test public void testHashBulkSet() { String key = "hashKey"; Map hash = new HashMap<>(); hash.put("field1", "value1"); hash.put("field2", "value2"); Long set = exec(commandObjects.hset(key, hash)); assertThat(set, equalTo((long) hash.size())); List mget = exec(commandObjects.hmget(key, "field1", "field2")); assertThat(mget, contains("value1", "value2")); } @Test public void testHashBulkSetBinary() { byte[] key = "hashKey".getBytes(); Map hash = new HashMap<>(); hash.put("field1".getBytes(), "value1".getBytes()); hash.put("field2".getBytes(), "value2".getBytes()); Long set = exec(commandObjects.hset(key, hash)); assertThat(set, equalTo((long) hash.size())); List mget = exec(commandObjects.hmget(key, "field1".getBytes(), "field2".getBytes())); assertThat(mget, contains("value1".getBytes(), "value2".getBytes())); } @Test public void testHashMsetMget() { String key = "bulkHashKey"; Map hash = new HashMap<>(); hash.put("field1", "value1"); hash.put("field2", "value2"); String mset = exec(commandObjects.hmset(key, hash)); assertThat(mset, equalTo("OK")); List mget = exec(commandObjects.hmget(key, "field1", "field2")); assertThat(mget, contains("value1", "value2")); } @Test public void testHashMsetMgetBinary() { byte[] key = "hashKey".getBytes(); Map hash = new HashMap<>(); hash.put("field1".getBytes(), "value1".getBytes()); hash.put("field2".getBytes(), "value2".getBytes()); String mset = exec(commandObjects.hmset(key, hash)); assertThat(mset, equalTo("OK")); List mget = exec(commandObjects.hmget(key, "field1".getBytes(), "field2".getBytes())); assertThat(mget, contains("value1".getBytes(), "value2".getBytes())); } @Test public void testHsetnx() { String key = "hashKey"; String field = "field"; String value = "value"; String initialGet = exec(commandObjects.hget(key, field)); assertThat(initialGet, nullValue()); Long initialSet = exec(commandObjects.hsetnx(key, field, value)); assertThat(initialSet, equalTo(1L)); String get = exec(commandObjects.hget(key, field)); assertThat(get, equalTo(value)); Long secondSet = exec(commandObjects.hsetnx(key, field, "newValue")); assertThat(secondSet, equalTo(0L)); String secondGet = exec(commandObjects.hget(key, field)); assertThat(secondGet, equalTo(value)); } @Test public void testHsetnxBinary() { byte[] key = "hashKey".getBytes(); byte[] field = "field".getBytes(); byte[] value = "value".getBytes(); byte[] initialGet = exec(commandObjects.hget(key, field)); assertThat(initialGet, nullValue()); Long set = exec(commandObjects.hsetnx(key, field, value)); assertThat(set, equalTo(1L)); byte[] get = exec(commandObjects.hget(key, field)); assertThat(get, equalTo(value)); Long secondSet = exec(commandObjects.hsetnx(key, field, "newValue".getBytes())); assertThat(secondSet, equalTo(0L)); byte[] secondGet = exec(commandObjects.hget(key, field)); assertThat(secondGet, equalTo(value)); } @Test public void testHincrBy() { String key = "incrementHashKey"; String field = "incrementField"; Long initialSet = exec(commandObjects.hset(key, field, "0")); assertThat(initialSet, equalTo(1L)); String initialGet = exec(commandObjects.hget(key, field)); assertThat(initialGet, equalTo("0")); Long incrByLong = exec(commandObjects.hincrBy(key, field, 10L)); assertThat(incrByLong, equalTo(10L)); String getAfterIncrByLong = exec(commandObjects.hget(key, field)); assertThat(getAfterIncrByLong, equalTo("10")); Double incrByFloat = exec(commandObjects.hincrByFloat(key, field, 2.5)); assertThat(incrByFloat, equalTo(12.5)); String getAfterIncrByFloat = exec(commandObjects.hget(key, field)); assertThat(getAfterIncrByFloat, equalTo("12.5")); } @Test public void testHincrByBinary() { byte[] key = "key".getBytes(); byte[] field = "field".getBytes(); Long initialSet = exec(commandObjects.hset(key, field, "0".getBytes())); assertThat(initialSet, equalTo(1L)); byte[] initialGet = exec(commandObjects.hget(key, field)); assertThat(initialGet, equalTo("0".getBytes())); Long incrByLong = exec(commandObjects.hincrBy(key, field, 10L)); assertThat(incrByLong, equalTo(10L)); byte[] getAfterIncrByLong = exec(commandObjects.hget(key, field)); assertThat(getAfterIncrByLong, equalTo("10".getBytes())); Double incrByDouble = exec(commandObjects.hincrByFloat(key, field, 2.5)); assertThat(incrByDouble, equalTo(12.5)); byte[] getAfterIncrByDouble = exec(commandObjects.hget(key, field)); assertThat(getAfterIncrByDouble, equalTo("12.5".getBytes())); } @Test public void testHashExistsDel() { String key = "key"; String field1 = "field1"; String field2 = "field2"; String value = "value"; exec(commandObjects.hset(key, field1, value)); exec(commandObjects.hset(key, field2, value)); Boolean exists = exec(commandObjects.hexists(key, field1)); assertThat(exists, equalTo(true)); Long len = exec(commandObjects.hlen(key)); assertThat(len, equalTo(2L)); Long del = exec(commandObjects.hdel(key, field1)); assertThat(del, equalTo(1L)); Boolean existsAfterDel = exec(commandObjects.hexists(key, field1)); assertThat(existsAfterDel, equalTo(false)); Long lenAfterDel = exec(commandObjects.hlen(key)); assertThat(lenAfterDel, equalTo(1L)); } @Test public void testHashExistsDelBinary() { byte[] key = "key".getBytes(); byte[] field1 = "field1".getBytes(); byte[] field2 = "field2".getBytes(); byte[] value = "value".getBytes(); exec(commandObjects.hset(key, field1, value)); exec(commandObjects.hset(key, field2, value)); Boolean exists = exec(commandObjects.hexists(key, field1)); assertThat(exists, equalTo(true)); Long len = exec(commandObjects.hlen(key)); assertThat(len, equalTo(2L)); Long del = exec(commandObjects.hdel(key, field1)); assertThat(del, equalTo(1L)); Boolean existsAfterDel = exec(commandObjects.hexists(key, field1)); assertThat(existsAfterDel, equalTo(false)); Long lenAfterDel = exec(commandObjects.hlen(key)); assertThat(lenAfterDel, equalTo(1L)); } @Test public void testHashKeysValsGetAll() { String key = "hashKey"; byte[] keyBinary = key.getBytes(); String field1 = "field1"; String field2 = "field2"; String value1 = "value1"; String value2 = "value2"; exec(commandObjects.hset(key, field1, value1)); exec(commandObjects.hset(key, field2, value2)); Set keys = exec(commandObjects.hkeys(key)); assertThat(keys, containsInAnyOrder(field1, field2)); List values = exec(commandObjects.hvals(key)); assertThat(values, containsInAnyOrder(value1, value2)); Map hash = exec(commandObjects.hgetAll(key)); assertThat(hash, allOf( hasEntry(field1, value1), hasEntry(field2, value2))); // binary Set keysBinary = exec(commandObjects.hkeys(keyBinary)); assertThat(keysBinary, containsInAnyOrder(field1.getBytes(), field2.getBytes())); List valuesBinary = exec(commandObjects.hvals(keyBinary)); assertThat(valuesBinary, containsInAnyOrder(value1.getBytes(), value2.getBytes())); Map hashBinary = exec(commandObjects.hgetAll(keyBinary)); assertThat(hashBinary, allOf( hasEntry(field1.getBytes(), value1.getBytes()), hasEntry(field2.getBytes(), value2.getBytes()))); } @Test public void testHashRandfield() { String key = "testHash"; byte[] bkey = key.getBytes(); exec(commandObjects.hset(key, "field1", "value1")); exec(commandObjects.hset(key, "field2", "value2")); String singleField = exec(commandObjects.hrandfield(key)); assertThat(singleField, anyOf(equalTo("field1"), equalTo("field2"))); List fields = exec(commandObjects.hrandfield(key, 2)); assertThat(fields, containsInAnyOrder("field1", "field2")); List> fieldsWithValues = exec(commandObjects.hrandfieldWithValues(key, 2)); assertThat(fieldsWithValues, hasSize(2)); fieldsWithValues.forEach(entry -> assertThat(entry.getValue(), anyOf(equalTo("value1"), equalTo("value2")))); // binary byte[] singleFieldBinary = exec(commandObjects.hrandfield(bkey)); assertThat(singleFieldBinary, anyOf(equalTo("field1".getBytes()), equalTo("field2".getBytes()))); List fieldsBinary = exec(commandObjects.hrandfield(bkey, 2)); assertThat(fieldsBinary, containsInAnyOrder("field1".getBytes(), "field2".getBytes())); List> fieldsWithValuesBinary = exec(commandObjects.hrandfieldWithValues(bkey, 2)); assertThat(fieldsWithValuesBinary, hasSize(2)); fieldsWithValuesBinary.forEach(entry -> assertThat(entry.getValue(), anyOf(equalTo("value1".getBytes()), equalTo("value2".getBytes())))); } @Test @SinceRedisVersion("7.4.0") public void testHscan() { String key = "testHashScan"; byte[] bkey = key.getBytes(); exec(commandObjects.hset(key, "field1", "value1")); exec(commandObjects.hset(key, "field2", "value2")); ScanParams params = new ScanParams().count(2); ScanResult> scanResult = exec(commandObjects.hscan(key, ScanParams.SCAN_POINTER_START, params)); assertThat(scanResult.getResult(), hasSize(lessThanOrEqualTo(2))); scanResult.getResult().forEach(entry -> assertThat(entry.getKey(), anyOf(equalTo("field1"), equalTo("field2")))); scanResult.getResult().forEach(entry -> assertThat(entry.getValue(), anyOf(equalTo("value1"), equalTo("value2")))); ScanResult scanResultNoValues = exec(commandObjects.hscanNoValues(key, ScanParams.SCAN_POINTER_START, params)); assertThat(scanResultNoValues.getResult(), hasSize(lessThanOrEqualTo(2))); assertThat(scanResultNoValues.getResult(), everyItem(anyOf(equalTo("field1"), equalTo("field2")))); // binary ScanResult> bscanResult = exec(commandObjects.hscan(bkey, ScanParams.SCAN_POINTER_START_BINARY, params)); assertThat(bscanResult.getResult(), hasSize(lessThanOrEqualTo(2))); bscanResult.getResult().forEach(entry -> assertThat(entry.getKey(), anyOf(equalTo("field1".getBytes()), equalTo("field2".getBytes())))); bscanResult.getResult().forEach(entry -> assertThat(entry.getValue(), anyOf(equalTo("value1".getBytes()), equalTo("value2".getBytes())))); ScanResult bscanResultNoValues = exec(commandObjects.hscanNoValues(bkey, ScanParams.SCAN_POINTER_START_BINARY, params)); assertThat(bscanResultNoValues.getResult(), hasSize(lessThanOrEqualTo(2))); assertThat(bscanResultNoValues.getResult(), everyItem(anyOf(equalTo("field1".getBytes()), equalTo("field2".getBytes())))); } @Test public void testHashStrlen() { String key = "testHashStrlen"; byte[] bkey = key.getBytes(); exec(commandObjects.hset(key, "field1", "value1")); Long strlen = exec(commandObjects.hstrlen(key, "field1")); assertThat(strlen, equalTo(6L)); Long strlenNonExistingField = exec(commandObjects.hstrlen(key, "nonExistingField")); assertThat(strlenNonExistingField, equalTo(0L)); // binary Long strlenBinary = exec(commandObjects.hstrlen(bkey, "field1".getBytes())); assertThat(strlenBinary, equalTo(6L)); Long strlenNonExistingFieldBinary = exec(commandObjects.hstrlen(bkey, "nonExistingField".getBytes())); assertThat(strlenNonExistingFieldBinary, equalTo(0L)); } @Test @SinceRedisVersion("7.4.0") public void hexpireAndHttl() { long seconds1 = 20; long seconds2 = 10; exec(commandObjects.hset("foo", "bar", "car")); exec(commandObjects.hset("foo", "bare", "care")); assertThat(exec(commandObjects.hexpire("foo", seconds1, "bar", "bared")), equalTo(asList(1L, -2L))); exec(commandObjects.hset("foo", "bared", "cared")); assertThat(exec(commandObjects.hexpire("foo", seconds2, ExpiryOption.NX, "bar", "bared")), equalTo(asList(0L, 1L))); assertThat(exec(commandObjects.httl("foo", "bar", "bare", "bared")), contains(greaterThanOrEqualTo(seconds1 - 1), equalTo(-1L), both(lessThanOrEqualTo(seconds2)).and(greaterThanOrEqualTo(seconds2 - 1)))); } @Test @SinceRedisVersion("7.4.0") public void hexpireAndHttlBinary() { long seconds1 = 20; long seconds2 = 10; exec(commandObjects.hset(bfoo, bbar1, bcar)); exec(commandObjects.hset(bfoo, bbar2, bcar)); assertThat(exec(commandObjects.hexpire(bfoo, seconds1, bbar1, bbar3)), equalTo(asList(1L, -2L))); exec(commandObjects.hset(bfoo, bbar3, bcar)); assertThat(exec(commandObjects.hexpire(bfoo, seconds2, ExpiryOption.NX, bbar1, bbar3)), equalTo(asList(0L, 1L))); assertThat(exec(commandObjects.httl(bfoo, bbar1, bbar2, bbar3)), contains(greaterThanOrEqualTo(seconds1 - 1), equalTo(-1L), both(lessThanOrEqualTo(seconds2)).and(greaterThanOrEqualTo(seconds2 - 1)))); } @Test @SinceRedisVersion("7.4.0") public void hpexpireAndHpttl() { long millis1 = 20_000; long millis2 = 10_000; exec(commandObjects.hset("foo", "bar", "car")); assertThat(exec(commandObjects.hpexpire("foo", millis1, "bar", "bared")), equalTo(asList(1L, -2L))); exec(commandObjects.hset("foo", "bared", "cared")); assertThat(exec(commandObjects.hpexpire("foo", millis2, ExpiryOption.XX, "bar", "bared")), equalTo(asList(1L, 0L))); assertThat(exec(commandObjects.hpttl("foo", "bar", "bare", "bared")), contains(both(lessThanOrEqualTo(millis2)).and(greaterThan(millis2 - 1000)), equalTo(-2L), equalTo(-1L))); } @Test @SinceRedisVersion("7.4.0") public void hpexpireAndHpttlBinary() { long millis1 = 20_000; long millis2 = 10_000; exec(commandObjects.hset(bfoo, bbar1, bcar)); assertThat(exec(commandObjects.hpexpire(bfoo, millis1, bbar1, bbar3)), equalTo(asList(1L, -2L))); exec(commandObjects.hset(bfoo, bbar3, bcar)); assertThat(exec(commandObjects.hpexpire(bfoo, millis2, ExpiryOption.XX, bbar1, bbar3)), equalTo(asList(1L, 0L))); assertThat(exec(commandObjects.hpttl(bfoo, bbar1, bbar2, bbar3)), contains(both(lessThanOrEqualTo(millis2)).and(greaterThan(millis2 - 1000)), equalTo(-2L), equalTo(-1L))); } @Test @SinceRedisVersion("7.4.0") public void hexpireAtAndExpireTime() { long currSeconds = System.currentTimeMillis() / 1000; long seconds1 = currSeconds + 20; long seconds2 = currSeconds + 10; exec(commandObjects.hset("foo", "bar", "car")); exec(commandObjects.hset("foo", "bare", "care")); assertThat(exec(commandObjects.hexpireAt("foo", seconds1, "bar", "bared")), equalTo(asList(1L, -2L))); exec(commandObjects.hset("foo", "bared", "cared")); assertThat(exec(commandObjects.hexpireAt("foo", seconds2, ExpiryOption.LT, "bar", "bared")), equalTo(asList(1L, 1L))); assertThat(exec(commandObjects.hexpireTime("foo", "bar", "bare", "bared")), contains(both(lessThanOrEqualTo(seconds2)).and(greaterThanOrEqualTo(seconds2 - 1)), equalTo(-1L), both(lessThanOrEqualTo(seconds2)).and(greaterThanOrEqualTo(seconds2 - 1)))); } @Test @SinceRedisVersion("7.4.0") public void hexpireAtAndExpireTimeBinary() { long currSeconds = System.currentTimeMillis() / 1000; long seconds1 = currSeconds + 20; long seconds2 = currSeconds + 10; exec(commandObjects.hset(bfoo, bbar1, bcar)); exec(commandObjects.hset(bfoo, bbar2, bcar)); assertThat(exec(commandObjects.hexpireAt(bfoo, seconds1, bbar1, bbar3)), equalTo(asList(1L, -2L))); exec(commandObjects.hset(bfoo, bbar3, bcar)); assertThat(exec(commandObjects.hexpireAt(bfoo, seconds2, ExpiryOption.LT, bbar1, bbar3)), equalTo(asList(1L, 1L))); assertThat(exec(commandObjects.hexpireTime(bfoo, bbar1, bbar2, bbar3)), contains(both(lessThanOrEqualTo(seconds2)).and(greaterThanOrEqualTo(seconds2 - 1)), equalTo(-1L), both(lessThanOrEqualTo(seconds2)).and(greaterThanOrEqualTo(seconds2 - 1)))); } @Test @SinceRedisVersion("7.4.0") public void hpexpireAtAndPexpireTime() { long currMillis = System.currentTimeMillis(); long unixMillis = currMillis + 20_000; exec(commandObjects.hset("foo", "bar", "car")); assertThat(exec(commandObjects.hpexpireAt("foo", unixMillis - 100, "bar", "bared")), equalTo(asList(1L, -2L))); exec(commandObjects.hset("foo", "bared", "cared")); assertThat(exec(commandObjects.hpexpireAt("foo", unixMillis, ExpiryOption.GT, "bar", "bared")), equalTo(asList(1L, 0L))); assertThat(exec(commandObjects.hpexpireTime("foo", "bar", "bare", "bared")), contains(equalTo(unixMillis), equalTo(-2L), equalTo(-1L))); } @Test @SinceRedisVersion("7.4.0") public void hpexpireAtAndPexpireTimeBinary() { long currMillis = System.currentTimeMillis(); long unixMillis = currMillis + 20_000; exec(commandObjects.hset(bfoo, bbar1, bcar)); assertThat(exec(commandObjects.hpexpireAt(bfoo, unixMillis - 100, bbar1, bbar3)), equalTo(asList(1L, -2L))); exec(commandObjects.hset(bfoo, bbar3, bcar)); assertThat(exec(commandObjects.hpexpireAt(bfoo, unixMillis, ExpiryOption.GT, bbar1, bbar3)), equalTo(asList(1L, 0L))); assertThat(exec(commandObjects.hpexpireTime(bfoo, bbar1, bbar2, bbar3)), contains(equalTo(unixMillis), equalTo(-2L), equalTo(-1L))); } @Test @SinceRedisVersion("7.4.0") public void hpersist() { long seconds = 20; exec(commandObjects.hset("foo", "bar", "car")); exec(commandObjects.hset("foo", "bare", "care")); assertThat(exec(commandObjects.hexpire("foo", seconds, "bar", "bared")), equalTo(asList(1L, -2L))); assertThat(exec(commandObjects.hpersist("foo", "bar", "bare", "bared")), equalTo(asList(1L, -1L, -2L))); assertThat(exec(commandObjects.httl("foo", "bar", "bare", "bared")), contains(equalTo(-1L), equalTo(-1L), equalTo(-2L))); } @Test @SinceRedisVersion("7.4.0") public void hpersistBinary() { long seconds = 20; exec(commandObjects.hset(bfoo, bbar1, bcar)); exec(commandObjects.hset(bfoo, bbar2, bcar)); assertThat(exec(commandObjects.hexpire(bfoo, seconds, bbar1, bbar3)), equalTo(asList(1L, -2L))); assertThat(exec(commandObjects.hpersist(bfoo, bbar1, bbar2, bbar3)), equalTo(asList(1L, -1L, -2L))); assertThat(exec(commandObjects.httl(bfoo, bbar1, bbar2, bbar3)), contains(equalTo(-1L), equalTo(-1L), equalTo(-2L))); } } ================================================ FILE: src/test/java/redis/clients/jedis/commands/commandobjects/CommandObjectsHyperloglogCommandsTest.java ================================================ package redis.clients.jedis.commands.commandobjects; import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.equalTo; import static org.hamcrest.Matchers.greaterThan; import static org.hamcrest.Matchers.greaterThanOrEqualTo; import io.redis.test.annotations.ConditionalOnEnv; import org.junit.jupiter.api.Test; import redis.clients.jedis.RedisProtocol; import redis.clients.jedis.util.TestEnvUtil; /** * Tests related to HyperLogLog commands. */ public class CommandObjectsHyperloglogCommandsTest extends CommandObjectsStandaloneTestBase { public CommandObjectsHyperloglogCommandsTest(RedisProtocol protocol) { super(protocol); } @Test public void testPfaddAndCount() { String key = "hyperloglogKey"; Long add = exec(commandObjects.pfadd(key, "element1", "element2", "element3")); assertThat(add, equalTo(1L)); Long count = exec(commandObjects.pfcount(key)); assertThat(count, greaterThanOrEqualTo(3L)); // approximate, expect at least 3 Long addNewElement = exec(commandObjects.pfadd(key, "element4")); assertThat(addNewElement, equalTo(1L)); Long countWithNewElement = exec(commandObjects.pfcount(key)); assertThat(countWithNewElement, greaterThan(count)); } @Test public void testPfaddAndCountBinary() { byte[] key = "hyperloglogKey".getBytes(); Long add = exec(commandObjects.pfadd(key, "element1".getBytes(), "element2".getBytes(), "element3".getBytes())); assertThat(add, equalTo(1L)); Long count = exec(commandObjects.pfcount(key)); assertThat(count, greaterThanOrEqualTo(3L)); } @Test @ConditionalOnEnv(value = TestEnvUtil.ENV_REDIS_ENTERPRISE, enabled = false) public void testPfmerge() { String key1 = "hyperloglog1"; String key2 = "hyperloglog2"; exec(commandObjects.pfadd(key1, "elementA", "elementB")); exec(commandObjects.pfadd(key2, "elementC", "elementD")); String destKey = "mergedHyperloglog"; byte[] destKeyBytes = "mergedHyperloglogBytes".getBytes(); String mergeResultWithString = exec(commandObjects.pfmerge(destKey, key1, key2)); assertThat(mergeResultWithString, equalTo("OK")); Long countAfterMergeWithString = exec(commandObjects.pfcount(destKey)); assertThat(countAfterMergeWithString, greaterThanOrEqualTo(4L)); // binary String mergeResultWithBytes = exec(commandObjects.pfmerge(destKeyBytes, key1.getBytes(), key2.getBytes())); assertThat(mergeResultWithBytes, equalTo("OK")); Long countAfterMergeWithBytes = exec(commandObjects.pfcount(destKeyBytes)); assertThat(countAfterMergeWithBytes, greaterThanOrEqualTo(4L)); } @Test @ConditionalOnEnv(value = TestEnvUtil.ENV_REDIS_ENTERPRISE, enabled = false) public void testPfcount() { String key1 = "hyperloglogCount1"; String key2 = "hyperloglogCount2"; exec(commandObjects.pfadd(key1, "element1", "element2", "element3")); exec(commandObjects.pfadd(key2, "element4", "element5", "element6")); Long countForKey1 = exec(commandObjects.pfcount(key1)); assertThat(countForKey1, greaterThanOrEqualTo(3L)); Long countForBothKeys = exec(commandObjects.pfcount(key1, key2)); assertThat(countForBothKeys, greaterThanOrEqualTo(6L)); // binary Long countForKey1Binary = exec(commandObjects.pfcount(key1.getBytes())); assertThat(countForKey1Binary, greaterThanOrEqualTo(3L)); Long countForBothKeysBinary = exec(commandObjects.pfcount(key1.getBytes(), key2.getBytes())); assertThat(countForBothKeysBinary, greaterThanOrEqualTo(6L)); } } ================================================ FILE: src/test/java/redis/clients/jedis/commands/commandobjects/CommandObjectsJsonCommandsTest.java ================================================ package redis.clients.jedis.commands.commandobjects; import static net.javacrumbs.jsonunit.JsonMatchers.jsonEquals; import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.contains; import static org.hamcrest.Matchers.containsInAnyOrder; import static org.hamcrest.Matchers.equalTo; import static org.hamcrest.Matchers.greaterThan; import static org.hamcrest.Matchers.hasEntry; import static org.hamcrest.Matchers.instanceOf; import static org.hamcrest.Matchers.not; import static org.hamcrest.Matchers.notNullValue; import static org.hamcrest.Matchers.nullValue; import static org.junit.jupiter.api.Assumptions.assumeTrue; import java.util.HashMap; import java.util.List; import java.util.Map; import org.json.JSONArray; import org.json.JSONObject; import org.junit.jupiter.api.Test; import redis.clients.jedis.RedisProtocol; import redis.clients.jedis.json.JsonSetParams; import redis.clients.jedis.json.Path; import redis.clients.jedis.json.Path2; /** * Tests related to JSON commands. */ public class CommandObjectsJsonCommandsTest extends CommandObjectsModulesTestBase { public CommandObjectsJsonCommandsTest(RedisProtocol protocol) { super(protocol); } @Test public void testJsonSetAndJsonGet() { String key = "jsonKey"; JSONObject person = new JSONObject(); person.put("name", "John Doe"); person.put("age", 30); String setRoot = exec(commandObjects.jsonSet(key, Path2.ROOT_PATH, person)); assertThat(setRoot, equalTo("OK")); Object getRoot = exec(commandObjects.jsonGet(key, Path2.ROOT_PATH)); assertThat(getRoot, jsonEquals(new JSONArray().put(person))); JSONObject details = new JSONObject(); details.put("city", "New York"); String setDeep = exec(commandObjects.jsonSet(key, new Path2("$.details"), details)); assertThat(setDeep, equalTo("OK")); Object getDeep = exec(commandObjects.jsonGet(key, new Path2("$.details"))); assertThat(getDeep, jsonEquals(new JSONArray().put(details))); Object getFull = exec(commandObjects.jsonGet(key, Path2.ROOT_PATH)); person.put("details", details); assertThat(getFull, jsonEquals(new JSONArray().put(person))); } @Test public void testJsonSetWithEscape() { String key = "jsonKey"; Map book = new HashMap<>(); book.put("title", "Learning JSON"); String setRoot = exec(commandObjects.jsonSetWithEscape(key, Path2.ROOT_PATH, book)); assertThat(setRoot, equalTo("OK")); Object getRoot = exec(commandObjects.jsonGet(key, Path2.ROOT_PATH)); JSONArray expected = new JSONArray().put(new JSONObject(book)); assertThat(getRoot, jsonEquals(expected)); } @Test @Deprecated public void testJsonSetJsonGetOldPath() { String key = "jsonKey"; Map book = new HashMap<>(); book.put("author", "Jane Doe"); book.put("title", "Advanced JSON Techniques"); String setRoot = exec(commandObjects.jsonSet(key, Path.ROOT_PATH, book)); assertThat(setRoot, equalTo("OK")); Object getRoot = exec(commandObjects.jsonGet(key, Path.ROOT_PATH)); assertThat(getRoot, instanceOf(Map.class)); @SuppressWarnings("unchecked") Map getRootMap = (Map) getRoot; assertThat(getRootMap, hasEntry("author", "Jane Doe")); assertThat(getRootMap, hasEntry("title", "Advanced JSON Techniques")); } @Test @Deprecated public void testJsonSetWithPlainString() { String key = "jsonKey"; String jsonString = "{\"name\":\"John\"}"; String setRoot = exec(commandObjects.jsonSetWithPlainString(key, Path.ROOT_PATH, jsonString)); assertThat(setRoot, equalTo("OK")); Object getRoot = exec(commandObjects.jsonGet(key, Path.ROOT_PATH)); assertThat(getRoot, instanceOf(Map.class)); @SuppressWarnings("unchecked") Map getRootMap = (Map) getRoot; assertThat(getRootMap, hasEntry("name", "John")); } @Test public void testJsonSetWithParams() { String key = "jsonKey"; JSONObject book = new JSONObject(); book.put("author", "Jane Doe"); book.put("title", "Advanced JSON Techniques"); JsonSetParams params = new JsonSetParams().nx(); String setRoot = exec(commandObjects.jsonSet(key, Path2.ROOT_PATH, book, params)); assertThat(setRoot, equalTo("OK")); Object getRoot = exec(commandObjects.jsonGet(key, Path2.ROOT_PATH)); JSONArray expected = new JSONArray().put(book); assertThat(getRoot, jsonEquals(expected)); } @Test public void testJsonSetWithEscapeAndParams() { String key = "jsonKey"; Map book = new HashMap<>(); book.put("author", "John Smith"); book.put("title", "JSON Escaping 101"); JsonSetParams params = new JsonSetParams().nx(); String setRoot = exec(commandObjects.jsonSetWithEscape(key, Path2.ROOT_PATH, book, params)); assertThat(setRoot, equalTo("OK")); Object getRoot = exec(commandObjects.jsonGet(key, Path2.ROOT_PATH)); JSONArray expected = new JSONArray().put(new JSONObject(book)); assertThat(getRoot, jsonEquals(expected)); } @Test @Deprecated public void testJsonSetOldPathWithParams() { String key = "jsonKey"; Map user = new HashMap<>(); user.put("username", "johndoe"); user.put("accountType", "premium"); JsonSetParams params = new JsonSetParams().nx(); String setRoot = exec(commandObjects.jsonSet(key, Path.ROOT_PATH, user, params)); assertThat(setRoot, equalTo("OK")); Object getRoot = exec(commandObjects.jsonGet(key, Path.ROOT_PATH)); assertThat(getRoot, instanceOf(Map.class)); @SuppressWarnings("unchecked") Map readResultMap = (Map) getRoot; assertThat(readResultMap, hasEntry("username", "johndoe")); assertThat(readResultMap, hasEntry("accountType", "premium")); } @Test public void testJsonMerge() { String key = "jsonKey"; JSONObject initialUser = new JSONObject(); initialUser.put("name", "John Doe"); initialUser.put("age", 30); exec(commandObjects.jsonSet(key, Path2.ROOT_PATH, initialUser)); JSONObject mergeUser = new JSONObject(); mergeUser.put("occupation", "Software Developer"); mergeUser.put("age", 31); // Assuming we're updating the age as well String mergeRoot = exec(commandObjects.jsonMerge(key, Path2.ROOT_PATH, mergeUser)); assertThat(mergeRoot, equalTo("OK")); Object getRoot = exec(commandObjects.jsonGet(key, Path2.ROOT_PATH)); assertThat(getRoot, notNullValue()); JSONObject expectedUser = new JSONObject(); expectedUser.put("name", "John Doe"); expectedUser.put("age", 31); expectedUser.put("occupation", "Software Developer"); JSONArray expected = new JSONArray().put(expectedUser); assertThat(getRoot, jsonEquals(expected)); } @Test @Deprecated public void testJsonMergeOldPath() { String key = "jsonKey"; Map initialUser = new HashMap<>(); initialUser.put("name", "Jane Doe"); exec(commandObjects.jsonSet(key, Path.ROOT_PATH, initialUser)); Map mergeUser = new HashMap<>(); mergeUser.put("occupation", "Data Scientist"); mergeUser.put("name", "Jane Smith"); // update the name as well String mergeRoot = exec(commandObjects.jsonMerge(key, Path.ROOT_PATH, mergeUser)); assertThat(mergeRoot, equalTo("OK")); Object getRoot = exec(commandObjects.jsonGet(key, Path.ROOT_PATH)); assertThat(getRoot, instanceOf(Map.class)); @SuppressWarnings("unchecked") Map resultMap = (Map) getRoot; assertThat(resultMap, hasEntry("name", "Jane Smith")); assertThat(resultMap, hasEntry("occupation", "Data Scientist")); } @Test @Deprecated public void testJsonGenericObject() { String key = "user:1000"; Person person = new Person(); person.setName("John Doe"); person.setAge(30); String setRoot = exec(commandObjects.jsonSet(key, Path.ROOT_PATH, person)); assertThat(setRoot, equalTo("OK")); Object getRoot = exec(commandObjects.jsonGet(key)); assertThat(getRoot, instanceOf(Map.class)); @SuppressWarnings("unchecked") Map resultMap = (Map) getRoot; assertThat(resultMap, hasEntry("name", "John Doe")); assertThat(resultMap, hasEntry("age", 30.0)); } @Test @Deprecated public void testJsonGetWithClass() { assumeTrue(protocol != RedisProtocol.RESP3); String key = "user:2000"; String jsonObject = "{\"name\":\"Jane Doe\",\"age\":25}"; exec(commandObjects.jsonSetWithPlainString(key, Path.ROOT_PATH, jsonObject)); Person getRoot = exec(commandObjects.jsonGet(key, Person.class)); assertThat(getRoot.getName(), equalTo("Jane Doe")); assertThat(getRoot.getAge(), equalTo(25)); } @Test public void testJsonMGet() { String keyBob = "user:bob"; String keyCharlie = "user:charlie"; JSONObject bob = new JSONObject(); bob.put("name", "Bob"); bob.put("age", 30); JSONObject charlie = new JSONObject(); charlie.put("name", "Charlie"); charlie.put("age", 25); String setBobRoot = exec(commandObjects.jsonSet(keyBob, Path2.ROOT_PATH, bob)); assertThat(setBobRoot, equalTo("OK")); String setCharlieRoot = exec(commandObjects.jsonSet(keyCharlie, Path2.ROOT_PATH, charlie)); assertThat(setCharlieRoot, equalTo("OK")); List getNames = exec(commandObjects.jsonMGet(Path2.of("name"), keyBob, keyCharlie)); assertThat(getNames, contains( jsonEquals(new JSONArray().put("Bob")), jsonEquals(new JSONArray().put("Charlie")) )); List getRoots = exec(commandObjects.jsonMGet(Path2.ROOT_PATH, keyBob, keyCharlie)); assertThat(getRoots, contains( jsonEquals(new JSONArray().put(bob)), jsonEquals(new JSONArray().put(charlie)) )); } @Test @Deprecated public void testJsonMGetOldPath() { String keyBob = "user:bob"; String keyCharlie = "user:charlie"; JSONObject bob = new JSONObject(); bob.put("name", "Bob"); bob.put("age", 30); JSONObject charlie = new JSONObject(); charlie.put("name", "Charlie"); charlie.put("age", 25); String setBobRoot = exec(commandObjects.jsonSet(keyBob, Path2.ROOT_PATH, bob)); assertThat(setBobRoot, equalTo("OK")); String setCharlieRoot = exec(commandObjects.jsonSet(keyCharlie, Path2.ROOT_PATH, charlie)); assertThat(setCharlieRoot, equalTo("OK")); List getNamesTyped = exec(commandObjects.jsonMGet(Path.of("name"), String.class, keyBob, keyCharlie)); assertThat(getNamesTyped, contains("Bob", "Charlie")); List getPersonsTyped = exec(commandObjects.jsonMGet(Path.ROOT_PATH, Person.class, keyBob, keyCharlie)); assertThat(getPersonsTyped, contains( new Person("Bob", 30), new Person("Charlie", 25) )); } @Test @Deprecated public void testJsonGetAsPlainString() { String key = "user:3000"; Person person = new Person("John Smith", 30); exec(commandObjects.jsonSet(key, Path.ROOT_PATH, person)); String getName = exec(commandObjects.jsonGetAsPlainString(key, Path.of(".name"))); assertThat(getName, equalTo("\"John Smith\"")); String getRoot = exec(commandObjects.jsonGetAsPlainString(key, Path.ROOT_PATH)); assertThat(getRoot, jsonEquals(person)); } @Test @Deprecated public void testJsonGetWithPathAndClass() { String key = "user:4000"; String jsonObject = "{\"person\":{\"name\":\"Alice Johnson\",\"age\":28}}"; String setRoot = exec(commandObjects.jsonSetWithPlainString(key, Path.ROOT_PATH, jsonObject)); assertThat(setRoot, equalTo("OK")); Person getPerson = exec(commandObjects.jsonGet(key, Person.class, Path.of(".person"))); assertThat(getPerson.getName(), equalTo("Alice Johnson")); assertThat(getPerson.getAge(), equalTo(28)); } @Test public void testJsonDel() { String key = "user:11000"; JSONObject person = new JSONObject(); person.put("name", "Gina"); person.put("age", 29); exec(commandObjects.jsonSet(key, Path2.ROOT_PATH, person)); Object preCheck = exec(commandObjects.jsonGet(key, Path2.ROOT_PATH)); assertThat(preCheck, jsonEquals(new JSONArray().put(person))); Long del = exec(commandObjects.jsonDel(key)); assertThat(del, equalTo(1L)); Object postCheck = exec(commandObjects.jsonGet(key, Path2.ROOT_PATH)); assertThat(postCheck, nullValue()); } @Test public void testJsonDelPath() { String key = "user:11000"; JSONObject person = new JSONObject(); person.put("name", "Gina"); person.put("age", 29); exec(commandObjects.jsonSet(key, Path2.ROOT_PATH, person)); Object preCheck = exec(commandObjects.jsonGet(key, Path2.ROOT_PATH)); assertThat(preCheck, jsonEquals(new JSONArray().put(person))); Long delAge = exec(commandObjects.jsonDel(key, Path2.of(".age"))); assertThat(delAge, equalTo(1L)); Object postCheck = exec(commandObjects.jsonGet(key, Path2.ROOT_PATH)); JSONObject expected = new JSONObject(); expected.put("name", "Gina"); assertThat(postCheck, jsonEquals(new JSONArray().put(expected))); } @Test @Deprecated public void testJsonDelOldPath() { String key = "user:11000"; JSONObject person = new JSONObject(); person.put("name", "Gina"); person.put("age", 29); exec(commandObjects.jsonSet(key, Path2.ROOT_PATH, person)); Object preCheck = exec(commandObjects.jsonGet(key, Path2.ROOT_PATH)); assertThat(preCheck, jsonEquals(new JSONArray().put(person))); Long delAge = exec(commandObjects.jsonDel(key, Path.of(".age"))); assertThat(delAge, equalTo(1L)); Object postCheck = exec(commandObjects.jsonGet(key, Path2.ROOT_PATH)); JSONObject expected = new JSONObject(); expected.put("name", "Gina"); assertThat(postCheck, jsonEquals(new JSONArray().put(expected))); } @Test public void testJsonClear() { String key = "user:11000"; JSONObject person = new JSONObject(); person.put("name", "Gina"); person.put("age", 29); exec(commandObjects.jsonSet(key, Path2.ROOT_PATH, person)); Object preCheck = exec(commandObjects.jsonGet(key, Path2.ROOT_PATH)); assertThat(preCheck, jsonEquals(new JSONArray().put(person))); Long clear = exec(commandObjects.jsonClear(key)); assertThat(clear, equalTo(1L)); Object postCheck = exec(commandObjects.jsonGet(key, Path2.ROOT_PATH)); JSONArray expected = new JSONArray().put(new JSONObject()); assertThat(postCheck, jsonEquals(expected)); } @Test public void testJsonClearPath() { String key = "user:11000"; JSONObject person = new JSONObject(); person.put("name", "Gina"); person.put("age", 29); person.put("occupations", new JSONArray().put("Data Scientist").put("Developer")); exec(commandObjects.jsonSet(key, Path2.ROOT_PATH, person)); Object preCheck = exec(commandObjects.jsonGet(key, Path2.ROOT_PATH)); assertThat(preCheck, jsonEquals(new JSONArray().put(person))); Long clearOccupations = exec(commandObjects.jsonClear(key, Path2.of(".occupations"))); assertThat(clearOccupations, equalTo(1L)); Object postCheck = exec(commandObjects.jsonGet(key, Path2.ROOT_PATH)); JSONObject expected = new JSONObject(); expected.put("name", "Gina"); expected.put("age", 29); expected.put("occupations", new JSONArray()); assertThat(postCheck, jsonEquals(new JSONArray().put(expected))); } @Test @Deprecated public void testJsonClearOldPath() { String key = "user:11000"; JSONObject person = new JSONObject(); person.put("name", "Gina"); person.put("age", 29); person.put("occupations", new JSONArray().put("Data Scientist").put("Developer")); exec(commandObjects.jsonSet(key, Path2.ROOT_PATH, person)); Object preCheck = exec(commandObjects.jsonGet(key, Path2.ROOT_PATH)); assertThat(preCheck, jsonEquals(new JSONArray().put(person))); Long clearOccupations = exec(commandObjects.jsonClear(key, Path.of(".occupations"))); assertThat(clearOccupations, equalTo(1L)); Object postCheck = exec(commandObjects.jsonGet(key, Path2.ROOT_PATH)); JSONObject expected = new JSONObject(); expected.put("name", "Gina"); expected.put("age", 29); expected.put("occupations", new JSONArray()); assertThat(postCheck, jsonEquals(new JSONArray().put(expected))); } @Test public void testJsonToggle() { String key = "user:13000"; JSONObject item = new JSONObject(); item.put("active", true); exec(commandObjects.jsonSet(key, Path2.ROOT_PATH, item)); Object preCheck = exec(commandObjects.jsonGet(key, Path2.ROOT_PATH)); assertThat(preCheck, jsonEquals(new JSONArray().put(item))); List toggle = exec(commandObjects.jsonToggle(key, Path2.of(".active"))); assertThat(toggle, contains(false)); Object postCheck = exec(commandObjects.jsonGet(key, Path2.ROOT_PATH)); JSONObject expected = new JSONObject(); expected.put("active", false); assertThat(postCheck, jsonEquals(new JSONArray().put(expected))); } @Test public void testJsonType() { String key = "jsonKey"; JSONObject item = new JSONObject(); item.put("active", true); exec(commandObjects.jsonSet(key, Path2.ROOT_PATH, item)); List> type = exec(commandObjects.jsonType(key, Path2.of(".active"))); assertThat(type, contains(boolean.class)); } @Test @Deprecated public void testJsonTypeOldPath() { assumeTrue(protocol != RedisProtocol.RESP3); String key = "jsonKey"; JSONObject item = new JSONObject(); item.put("active", true); exec(commandObjects.jsonSet(key, Path2.ROOT_PATH, item)); Class type = exec(commandObjects.jsonType(key, Path.of(".active"))); assertThat(type, equalTo(boolean.class)); } @Test public void testJsonStrAppend() { String key = "user:1000"; JSONObject person = new JSONObject(); person.put("name", "Gina"); person.put("age", 29); exec(commandObjects.jsonSet(key, Path2.ROOT_PATH, person)); Object preCheck = exec(commandObjects.jsonGet(key, Path2.ROOT_PATH)); assertThat(preCheck, jsonEquals(new JSONArray().put(person))); List strAppend = exec(commandObjects.jsonStrAppend(key, Path2.of(".name"), " Smith")); assertThat(strAppend, contains(10L)); Object postCheck = exec(commandObjects.jsonGet(key, Path2.ROOT_PATH)); JSONObject expected = new JSONObject(); expected.put("name", "Gina Smith"); expected.put("age", 29); assertThat(postCheck, jsonEquals(new JSONArray().put(expected))); } @Test @Deprecated public void testJsonStrAppendOldPath() { String key = "user:1000"; JSONObject person = new JSONObject(); person.put("name", "Gina"); person.put("age", 29); exec(commandObjects.jsonSet(key, Path2.ROOT_PATH, person)); Object preCheck = exec(commandObjects.jsonGet(key, Path2.ROOT_PATH)); assertThat(preCheck, jsonEquals(new JSONArray().put(person))); Long strAppend = exec(commandObjects.jsonStrAppend(key, Path.of(".name"), " Smith")); assertThat(strAppend, equalTo(10L)); Object postCheck = exec(commandObjects.jsonGet(key, Path2.ROOT_PATH)); JSONObject expected = new JSONObject(); expected.put("name", "Gina Smith"); expected.put("age", 29); assertThat(postCheck, jsonEquals(new JSONArray().put(expected))); } @Test @Deprecated public void testJsonStrAppendRootPath() { assumeTrue(protocol != RedisProtocol.RESP3); String key = "user:1000"; String setRoot = exec(commandObjects.jsonSetWithPlainString(key, Path.ROOT_PATH, "\"John\"")); assertThat(setRoot, equalTo("OK")); Object getBefore = exec(commandObjects.jsonGet(key, Path2.ROOT_PATH)); assertThat(getBefore, jsonEquals(new JSONArray().put("John"))); Long strAppend = exec(commandObjects.jsonStrAppend(key, " Doe")); assertThat(strAppend, equalTo(8L)); Object getAfter = exec(commandObjects.jsonGet(key, Path2.ROOT_PATH)); assertThat(getAfter, jsonEquals(new JSONArray().put("John Doe"))); } @Test @Deprecated public void testJsonStrLenRootPath() { assumeTrue(protocol != RedisProtocol.RESP3); String key = "user:1001"; String setRoot = exec(commandObjects.jsonSetWithPlainString(key, Path.ROOT_PATH, "\"Hello World\"")); assertThat(setRoot, equalTo("OK")); Long strLen = exec(commandObjects.jsonStrLen(key)); assertThat(strLen, equalTo(11L)); // "Hello World" length } @Test public void testJsonStrLen() { String key = "user:1002"; JSONObject item = new JSONObject(); item.put("message", "Hello, Redis!"); String setResponse = exec(commandObjects.jsonSet(key, Path2.ROOT_PATH, item)); assertThat(setResponse, equalTo("OK")); List strLenResponse = exec(commandObjects.jsonStrLen(key, Path2.of(".message"))); assertThat(strLenResponse, contains(13L)); // "Hello, Redis!" length } @Test @Deprecated public void testJsonStrLenOldPath() { String key = "user:1003"; JSONObject item = new JSONObject(); item.put("message", "Hello, Redis!"); String setResponse = exec(commandObjects.jsonSet(key, Path2.ROOT_PATH, item)); assertThat(setResponse, equalTo("OK")); Long strLenResponse = exec(commandObjects.jsonStrLen(key, Path.of(".message"))); assertThat(strLenResponse, equalTo(13L)); // "Hello, Redis!" length } @Test public void testJsonNumIncrBy() { String key = "user:12000"; JSONObject item = new JSONObject(); item.put("balance", 100); exec(commandObjects.jsonSet(key, Path2.ROOT_PATH, item)); Object preCheck = exec(commandObjects.jsonGet(key, Path2.ROOT_PATH)); assertThat(preCheck, jsonEquals(new JSONArray().put(item))); Object numIncrBy = exec(commandObjects.jsonNumIncrBy(key, Path2.of("$.balance"), 50.0)); assertThat(numIncrBy, jsonEquals(new JSONArray().put(150.0))); Object postCheck = exec(commandObjects.jsonGet(key, Path2.ROOT_PATH)); JSONObject expected = new JSONObject(); expected.put("balance", 150.0); assertThat(postCheck, jsonEquals(new JSONArray().put(expected))); } @Test public void testJsonArrAppendWithEscape() { String key = "json"; JSONArray data = new JSONArray() .put("Elixir") .put("Swift"); exec(commandObjects.jsonSet(key, Path2.ROOT_PATH, data)); Object preCheck = exec(commandObjects.jsonGet(key, Path2.ROOT_PATH)); assertThat(preCheck, jsonEquals(new JSONArray().put(data))); List arrAppend = exec(commandObjects.jsonArrAppendWithEscape( key, Path2.ROOT_PATH, "Kotlin", "TypeScript")); assertThat(arrAppend, contains(4L)); Object postCheck = exec(commandObjects.jsonGet(key, Path2.ROOT_PATH)); JSONArray expected = new JSONArray() .put("Elixir") .put("Swift") .put("Kotlin") .put("TypeScript"); assertThat(postCheck, jsonEquals(new JSONArray().put(expected))); } @Test public void testJsonArrAppend() { String key = "json"; JSONArray data = new JSONArray() .put("Java") .put("Python"); exec(commandObjects.jsonSet(key, Path2.ROOT_PATH, data)); JSONObject person = new JSONObject(); person.put("name", "John"); List arrAppend = exec(commandObjects.jsonArrAppend(key, Path2.ROOT_PATH, "\"C++\"", "\"JavaScript\"", person)); assertThat(arrAppend, contains(5L)); Object postCheck = exec(commandObjects.jsonGet(key, Path2.ROOT_PATH)); JSONArray expected = new JSONArray() .put("Java") .put("Python") .put("C++") .put("JavaScript") .put(person); assertThat(postCheck, jsonEquals(new JSONArray().put(expected))); } @Test @Deprecated public void testJsonArrAppendOldPath() { String key = "json"; JSONArray data = new JSONArray() .put(new JSONArray() .put("Java") .put("Python")) .put(1); exec(commandObjects.jsonSet(key, Path2.ROOT_PATH, data)); Person person = new Person("John", 45); Long arrAppend = exec( commandObjects.jsonArrAppend(key, Path.of(".[0]"), "Swift", "Go", person)); assertThat(arrAppend, equalTo(5L)); Object postCheck = exec(commandObjects.jsonGet(key, Path2.ROOT_PATH)); JSONArray expected = new JSONArray() .put(new JSONArray() .put("Java") .put("Python") .put("Swift") .put("Go") .put(new JSONObject() .put("name", "John") .put("age", 45))) .put(1); assertThat(postCheck, jsonEquals(new JSONArray().put(expected))); } @Test public void testJsonArrIndex() { String key = "json"; JSONArray data = new JSONArray() .put("Java") .put("Python") .put("Java"); // duplicate exec(commandObjects.jsonSet(key, Path2.ROOT_PATH, data)); List arrIndex = exec(commandObjects.jsonArrIndex(key, Path2.ROOT_PATH, "\"Java\"")); assertThat(arrIndex, contains(0L)); List arrIndexNotFound = exec(commandObjects.jsonArrIndex(key, Path2.ROOT_PATH, "\"C++\"")); assertThat(arrIndexNotFound, contains(-1L)); } @Test public void testJsonArrIndexWithEscape() { String key = "json"; JSONArray data = new JSONArray() .put("Java") .put("Python") .put("Java"); // duplicate exec(commandObjects.jsonSet(key, Path2.ROOT_PATH, data)); List arrIndex = exec(commandObjects.jsonArrIndexWithEscape(key, Path2.ROOT_PATH, "Java")); assertThat(arrIndex, contains(0L)); List arrIndexNotFound = exec(commandObjects.jsonArrIndexWithEscape(key, Path2.ROOT_PATH, "Go")); assertThat(arrIndexNotFound, contains(-1L)); } @Test @Deprecated public void testJsonArrIndexDeprecated() { String key = "json"; JSONArray data = new JSONArray() .put(new JSONArray() .put("Java") .put("Python") .put("Java")); // duplicate exec(commandObjects.jsonSet(key, Path2.ROOT_PATH, data)); Long arrIndex = exec(commandObjects.jsonArrIndex(key, Path.of(".[0]"), "Java")); assertThat(arrIndex, equalTo(0L)); Long arrIndexNotFound = exec(commandObjects.jsonArrIndex(key, Path.of(".[0]"), "Swift")); assertThat(arrIndexNotFound, equalTo(-1L)); } @Test public void testJsonArrInsert() { String key = "json"; JSONArray data = new JSONArray() .put("Java") .put("Python"); exec(commandObjects.jsonSet(key, Path2.ROOT_PATH, data)); Object preCheck = exec(commandObjects.jsonGet(key, Path2.ROOT_PATH)); assertThat(preCheck, jsonEquals(new JSONArray().put(data))); List arrInsert = exec( commandObjects.jsonArrInsert(key, Path2.ROOT_PATH, 1, "\"C++\"")); assertThat(arrInsert, contains(3L)); Object postCheck = exec(commandObjects.jsonGet(key, Path2.ROOT_PATH)); JSONArray expected = new JSONArray() .put("Java") .put("C++") .put("Python"); assertThat(postCheck, jsonEquals(new JSONArray().put(expected))); } @Test public void testJsonArrInsertWithEscape() { String key = "json"; JSONArray data = new JSONArray() .put("Java") .put("Python"); exec(commandObjects.jsonSet(key, Path2.ROOT_PATH, data)); Object preCheck = exec(commandObjects.jsonGet(key, Path2.ROOT_PATH)); assertThat(preCheck, jsonEquals(new JSONArray().put(data))); List arrInsert = exec(commandObjects.jsonArrInsertWithEscape(key, Path2.ROOT_PATH, 1, "Go")); assertThat(arrInsert, contains(3L)); Object postCheck = exec(commandObjects.jsonGet(key, Path2.ROOT_PATH)); JSONArray expected = new JSONArray() .put("Java") .put("Go") .put("Python"); assertThat(postCheck, jsonEquals(new JSONArray().put(expected))); } @Test @Deprecated public void testJsonArrInsertOldPath() { String key = "json"; JSONArray data = new JSONArray() .put(1) .put(new JSONArray() .put("Scala") .put("Kotlin")); exec(commandObjects.jsonSet(key, Path2.ROOT_PATH, data)); Object preCheck = exec(commandObjects.jsonGet(key, Path2.ROOT_PATH)); assertThat(preCheck, jsonEquals(new JSONArray().put(data))); Long arrInsert = exec(commandObjects.jsonArrInsert(key, Path.of(".[1]"), 1, "Swift")); assertThat(arrInsert, equalTo(3L)); Object postCheck = exec(commandObjects.jsonGet(key, Path2.ROOT_PATH)); JSONArray expected = new JSONArray() .put(1) .put(new JSONArray() .put("Scala") .put("Swift") .put("Kotlin")); assertThat(postCheck, jsonEquals(new JSONArray().put(expected))); } @Test @Deprecated public void testJsonArrPopRoot() { String key = "json"; JSONArray data = new JSONArray() .put("apple") .put("banana") .put("cherry"); exec(commandObjects.jsonSet(key, Path2.ROOT_PATH, data)); Object preCheck = exec(commandObjects.jsonGet(key, Path2.ROOT_PATH)); assertThat(preCheck, jsonEquals(new JSONArray().put(data))); Object arrPop = exec(commandObjects.jsonArrPop(key)); assertThat(arrPop, equalTo("cherry")); Object postCheck = exec(commandObjects.jsonGet(key, Path2.ROOT_PATH)); JSONArray expected = new JSONArray() .put("apple") .put("banana"); assertThat(postCheck, jsonEquals(new JSONArray().put(expected))); } @Test public void testJsonArrPopWithPath2() { String key = "json"; JSONObject data = new JSONObject() .put("fruits", new JSONArray() .put("apple") .put("banana") .put("cherry")); exec(commandObjects.jsonSet(key, Path2.ROOT_PATH, data)); Object preCheck = exec(commandObjects.jsonGet(key, Path2.ROOT_PATH)); assertThat(preCheck, jsonEquals(new JSONArray().put(data))); List arrPop = exec(commandObjects.jsonArrPop(key, Path2.of(".fruits"))); assertThat(arrPop, contains("cherry")); Object postCheck = exec(commandObjects.jsonGet(key, Path2.ROOT_PATH)); JSONObject expected = new JSONObject() .put("fruits", new JSONArray() .put("apple") .put("banana")); assertThat(postCheck, jsonEquals(new JSONArray().put(expected))); } @Test @Deprecated public void testJsonArrPopOldPath() { String key = "json"; JSONObject data = new JSONObject() .put("fruits", new JSONArray() .put("apple") .put("banana") .put("cherry")); exec(commandObjects.jsonSet(key, Path2.ROOT_PATH, data)); Object preCheck = exec(commandObjects.jsonGet(key, Path2.ROOT_PATH)); assertThat(preCheck, jsonEquals(new JSONArray().put(data))); Object arrPop = exec(commandObjects.jsonArrPop(key, Path.of(".fruits"))); assertThat(arrPop, equalTo("cherry")); Object postCheck = exec(commandObjects.jsonGet(key, Path2.ROOT_PATH)); JSONObject expected = new JSONObject() .put("fruits", new JSONArray() .put("apple") .put("banana")); assertThat(postCheck, jsonEquals(new JSONArray().put(expected))); } @Test @Deprecated public void testJsonArrPopRootWithType() { String key = "json"; JSONArray data = new JSONArray() .put(1) .put(2) .put(3); exec(commandObjects.jsonSet(key, Path2.ROOT_PATH, data)); Object preCheck = exec(commandObjects.jsonGet(key, Path2.ROOT_PATH)); assertThat(preCheck, jsonEquals(new JSONArray().put(data))); Integer arrPop = exec(commandObjects.jsonArrPop(key, Integer.class)); assertThat(arrPop, equalTo(3)); Object postCheck = exec(commandObjects.jsonGet(key, Path2.ROOT_PATH)); JSONArray expected = new JSONArray() .put(1) .put(2); assertThat(postCheck, jsonEquals(new JSONArray().put(expected))); } @Test @Deprecated public void testJsonArrPopWithOldPathAndType() { String key = "json"; JSONObject data = new JSONObject() .put("numbers", new JSONArray() .put(10) .put(20) .put(30)); exec(commandObjects.jsonSet(key, Path2.ROOT_PATH, data)); Object preCheck = exec(commandObjects.jsonGet(key, Path2.ROOT_PATH)); assertThat(preCheck, jsonEquals(new JSONArray().put(data))); Integer arrPop = exec(commandObjects.jsonArrPop(key, Integer.class, Path.of(".numbers"))); assertThat(arrPop, equalTo(30)); Object postCheck = exec(commandObjects.jsonGet(key, Path2.ROOT_PATH)); JSONObject expected = new JSONObject() .put("numbers", new JSONArray() .put(10) .put(20)); assertThat(postCheck, jsonEquals(new JSONArray().put(expected))); } @Test @Deprecated public void testJsonArrPopWithOldPathTypeAndIndex() { String key = "json"; JSONObject data = new JSONObject() .put("numbers", new JSONArray() .put(10) .put(20) .put(30)); exec(commandObjects.jsonSet(key, Path2.ROOT_PATH, data)); Object preCheck = exec(commandObjects.jsonGet(key, Path2.ROOT_PATH)); assertThat(preCheck, jsonEquals(new JSONArray().put(data))); Integer arrPop = exec(commandObjects.jsonArrPop(key, Integer.class, Path.of(".numbers"), 1)); assertThat(arrPop, equalTo(20)); Object postCheck = exec(commandObjects.jsonGet(key, Path2.ROOT_PATH)); JSONObject expected = new JSONObject() .put("numbers", new JSONArray() .put(10) .put(30)); assertThat(postCheck, jsonEquals(new JSONArray().put(expected))); } @Test public void testJsonArrPopWithPathAndIndex() { String key = "json"; JSONObject data = new JSONObject() .put("numbers", new JSONArray() .put(10) .put(20) .put(30)); exec(commandObjects.jsonSet(key, Path2.ROOT_PATH, data)); Object preCheck = exec(commandObjects.jsonGet(key, Path2.ROOT_PATH)); assertThat(preCheck, jsonEquals(new JSONArray().put(data))); List arrPop = exec(commandObjects.jsonArrPop(key, Path2.of(".numbers"), 1)); assertThat(arrPop, contains(20.0)); Object postCheck = exec(commandObjects.jsonGet(key, Path2.ROOT_PATH)); JSONObject expected = new JSONObject() .put("numbers", new JSONArray() .put(10) .put(30)); assertThat(postCheck, jsonEquals(new JSONArray().put(expected))); } @Test @Deprecated public void testJsonArrPopOldPathAndIndex() { String key = "json"; JSONObject data = new JSONObject() .put("numbers", new JSONArray() .put(10) .put(20) .put(30)); exec(commandObjects.jsonSet(key, Path2.ROOT_PATH, data)); Object preCheck = exec(commandObjects.jsonGet(key, Path2.ROOT_PATH)); assertThat(preCheck, jsonEquals(new JSONArray().put(data))); Object arrPop = exec(commandObjects.jsonArrPop(key, Path.of(".numbers"), 1)); assertThat(arrPop, equalTo(20.0)); Object postCheck = exec(commandObjects.jsonGet(key, Path2.ROOT_PATH)); JSONObject expected = new JSONObject() .put("numbers", new JSONArray() .put(10) .put(30)); assertThat(postCheck, jsonEquals(new JSONArray().put(expected))); } @Test public void testJsonArrTrimWithPath() { String key = "json"; JSONObject data = new JSONObject() .put("fruits", new JSONArray() .put("apple") .put("banana") .put("cherry") .put("date") .put("fig")); exec(commandObjects.jsonSet(key, Path2.ROOT_PATH, data)); Object preCheck = exec(commandObjects.jsonGet(key, Path2.ROOT_PATH)); assertThat(preCheck, jsonEquals(new JSONArray().put(data))); List arrTrim = exec(commandObjects.jsonArrTrim(key, Path2.of(".fruits"), 1, 3)); assertThat(arrTrim, contains(3L)); Object postCheck = exec(commandObjects.jsonGet(key, Path2.ROOT_PATH)); JSONObject expected = new JSONObject() .put("fruits", new JSONArray() .put("banana") .put("cherry") .put("date")); assertThat(postCheck, jsonEquals(new JSONArray().put(expected))); } @Test @Deprecated public void testJsonArrTrimOldPath() { String key = "json"; JSONObject data = new JSONObject() .put("fruits", new JSONArray() .put("apple") .put("banana") .put("cherry") .put("date") .put("fig")); exec(commandObjects.jsonSet(key, Path2.ROOT_PATH, data)); Object preCheck = exec(commandObjects.jsonGet(key, Path2.ROOT_PATH)); assertThat(preCheck, jsonEquals(new JSONArray().put(data))); Long arrTrim = exec(commandObjects.jsonArrTrim(key, Path.of(".fruits"), 1, 3)); assertThat(arrTrim, equalTo(3L)); Object postCheck = exec(commandObjects.jsonGet(key, Path2.ROOT_PATH)); JSONObject expected = new JSONObject() .put("fruits", new JSONArray() .put("banana") .put("cherry") .put("date")); assertThat(postCheck, jsonEquals(new JSONArray().put(expected))); } @Test @Deprecated public void testJsonArrLenRoot() { assumeTrue(protocol != RedisProtocol.RESP3); String key = "json"; JSONArray data = new JSONArray() .put("apple") .put("banana") .put("cherry") .put("date") .put("fig"); exec(commandObjects.jsonSet(key, Path2.ROOT_PATH, data)); Long arrLen = exec(commandObjects.jsonArrLen(key)); assertThat(arrLen, equalTo(5L)); } @Test public void testJsonArrLenWithPath() { String key = "json"; JSONObject data = new JSONObject() .put("fruits", new JSONArray() .put("apple") .put("banana") .put("cherry") .put("date") .put("fig")); exec(commandObjects.jsonSet(key, Path2.ROOT_PATH, data)); List arrLen = exec(commandObjects.jsonArrLen(key, Path2.of(".fruits"))); assertThat(arrLen, contains(5L)); } @Test @Deprecated public void testJsonArrLenOldPath() { String key = "json"; JSONObject data = new JSONObject() .put("fruits", new JSONArray() .put("apple") .put("banana") .put("cherry") .put("date") .put("fig")); exec(commandObjects.jsonSet(key, Path2.ROOT_PATH, data)); Long arrLen = exec(commandObjects.jsonArrLen(key, Path.of(".fruits"))); assertThat(arrLen, equalTo(5L)); } @Test @Deprecated public void testJsonObjLenRoot() { assumeTrue(protocol != RedisProtocol.RESP3); String key = "json"; JSONObject data = new JSONObject(); data.put("name", "John"); data.put("age", 30); String setResponse = exec(commandObjects.jsonSet(key, Path2.ROOT_PATH, data)); assertThat(setResponse, equalTo("OK")); Long objLen = exec(commandObjects.jsonObjLen(key)); assertThat(objLen, equalTo(2L)); // 2 keys: "name" and "age" } @Test @Deprecated public void testJsonObjLenOldPath() { String key = "json"; JSONObject data = new JSONObject().put("user", new JSONObject() .put("name", "John") .put("age", 30)); String setResponse = exec(commandObjects.jsonSet(key, Path2.ROOT_PATH, data)); assertThat(setResponse, equalTo("OK")); Long objLen = exec(commandObjects.jsonObjLen(key, Path.of(".user"))); assertThat(objLen, equalTo(2L)); } @Test public void testJsonObjLenWithPath2() { String key = "json"; JSONObject data = new JSONObject().put("user", new JSONObject() .put("name", "John") .put("age", 30)); String setResponse = exec(commandObjects.jsonSet(key, Path2.ROOT_PATH, data)); assertThat(setResponse, equalTo("OK")); List objLen = exec(commandObjects.jsonObjLen(key, Path2.of(".user"))); assertThat(objLen, contains(2L)); } @Test @Deprecated public void testJsonObjKeysRoot() { assumeTrue(protocol != RedisProtocol.RESP3); String key = "json"; JSONObject data = new JSONObject(); data.put("name", "John"); data.put("age", 30); String setResponse = exec(commandObjects.jsonSet(key, Path2.ROOT_PATH, data)); assertThat(setResponse, equalTo("OK")); List keys = exec(commandObjects.jsonObjKeys(key)); assertThat(keys, containsInAnyOrder("name", "age")); } @Test @Deprecated public void testJsonObjKeysOldPath() { String key = "json"; JSONObject data = new JSONObject().put("user", new JSONObject() .put("name", "John") .put("age", 30)); String setResponse = exec(commandObjects.jsonSet(key, Path2.ROOT_PATH, data)); assertThat(setResponse, equalTo("OK")); List keys = exec(commandObjects.jsonObjKeys(key, Path.of(".user"))); assertThat(keys, containsInAnyOrder("name", "age")); } @Test public void testJsonObjKeysWithPath() { String key = "json"; JSONObject data = new JSONObject().put("user", new JSONObject() .put("name", "John") .put("age", 30)); String setResponse = exec(commandObjects.jsonSet(key, Path2.ROOT_PATH, data)); assertThat(setResponse, equalTo("OK")); List> keys = exec(commandObjects.jsonObjKeys(key, Path2.of(".user"))); assertThat(keys, contains(containsInAnyOrder("name", "age"))); } @Test @Deprecated public void testJsonDebugMemoryRoot() { assumeTrue(protocol != RedisProtocol.RESP3); String key = "json"; JSONObject data = new JSONObject() .put("name", "John") .put("age", 30); String setResponse = exec(commandObjects.jsonSet(key, Path2.ROOT_PATH, data)); assertThat(setResponse, equalTo("OK")); Long memoryUsage = exec(commandObjects.jsonDebugMemory(key)); assertThat(memoryUsage, notNullValue()); assertThat(memoryUsage, greaterThan(0L)); } @Test @Deprecated public void testJsonDebugMemoryOldPath() { String key = "json"; JSONObject data = new JSONObject().put("user", new JSONObject() .put("name", "John") .put("age", 30)); String setResponse = exec(commandObjects.jsonSet(key, Path2.ROOT_PATH, data)); assertThat(setResponse, equalTo("OK")); Long memoryUsage = exec(commandObjects.jsonDebugMemory(key, Path.of(".user"))); assertThat(memoryUsage, notNullValue()); assertThat(memoryUsage, greaterThan(0L)); } @Test public void testJsonDebugMemoryWithPath2() { String key = "json"; JSONObject data = new JSONObject().put("user", new JSONObject() .put("name", "John") .put("age", 30)); String setResponse = exec(commandObjects.jsonSet(key, Path2.ROOT_PATH, data)); assertThat(setResponse, equalTo("OK")); List memoryUsages = exec(commandObjects.jsonDebugMemory(key, Path2.of(".user"))); assertThat(memoryUsages, contains(greaterThan(0L))); } } ================================================ FILE: src/test/java/redis/clients/jedis/commands/commandobjects/CommandObjectsListCommandsTest.java ================================================ package redis.clients.jedis.commands.commandobjects; import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.anyOf; import static org.hamcrest.Matchers.contains; import static org.hamcrest.Matchers.empty; import static org.hamcrest.Matchers.equalTo; import static org.hamcrest.Matchers.nullValue; import java.util.List; import io.redis.test.annotations.SinceRedisVersion; import io.redis.test.annotations.ConditionalOnEnv; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Tag; import redis.clients.jedis.RedisProtocol; import redis.clients.jedis.args.ListDirection; import redis.clients.jedis.args.ListPosition; import redis.clients.jedis.params.LPosParams; import redis.clients.jedis.util.KeyValue; import redis.clients.jedis.util.TestEnvUtil; /** * Tests related to List commands. */ @Tag("integration") public class CommandObjectsListCommandsTest extends CommandObjectsStandaloneTestBase { public CommandObjectsListCommandsTest(RedisProtocol protocol) { super(protocol); } @Test public void testPushCommands() { String key = "list"; Long rpush = exec(commandObjects.rpush(key, "hello", "world")); assertThat(rpush, equalTo(2L)); Long lpush = exec(commandObjects.lpush(key, "hello", "world")); assertThat(lpush, equalTo(4L)); List lrange = exec(commandObjects.lrange(key, 0, -1)); assertThat(lrange, contains("world", "hello", "hello", "world")); } @Test public void testPushCommandsBinary() { String keyStr = "list"; byte[] key = keyStr.getBytes(); Long rpush = exec(commandObjects.rpush(key, "hello".getBytes(), "world".getBytes())); assertThat(rpush, equalTo(2L)); Long lpush = exec(commandObjects.lpush(key, "hello".getBytes(), "world".getBytes())); assertThat(lpush, equalTo(4L)); List lrange = exec(commandObjects.lrange(keyStr, 0, -1)); assertThat(lrange, contains("world", "hello", "hello", "world")); } @Test public void testLlen() { String key = "list"; Long initialLength = exec(commandObjects.llen(key)); assertThat(initialLength, equalTo(0L)); exec(commandObjects.rpush(key, "value", "value")); Long llen = exec(commandObjects.llen(key)); assertThat(llen, equalTo(2L)); Long llenBinary = exec(commandObjects.llen(key.getBytes())); assertThat(llenBinary, equalTo(2L)); } @Test public void testLrange() { String key = "list"; String value1 = "first"; String value2 = "second"; String value3 = "third"; exec(commandObjects.rpush(key, value1, value2, value3)); List lrange = exec(commandObjects.lrange(key, 0, -1)); assertThat(lrange, contains(value1, value2, value3)); List lrangeBinary = exec(commandObjects.lrange(key.getBytes(), 0, -1)); assertThat(lrangeBinary, contains(value1.getBytes(), value2.getBytes(), value3.getBytes())); List partialRange = exec(commandObjects.lrange(key, 1, 2)); assertThat(partialRange, contains(value2, value3)); List emptyRange = exec(commandObjects.lrange(key, 4, 5)); assertThat(emptyRange, empty()); } @Test public void testLtrim() { String key = "list"; exec(commandObjects.rpush(key, "one", "two", "three", "four")); String trim = exec(commandObjects.ltrim(key, 1, 2)); assertThat(trim, equalTo("OK")); List lrange = exec(commandObjects.lrange(key, 0, -1)); assertThat(lrange, contains("two", "three")); } @Test public void testLtrimBinary() { byte[] key = "list".getBytes(); exec(commandObjects.rpush(key, "one".getBytes(), "two".getBytes(), "three".getBytes(), "four".getBytes())); String trim = exec(commandObjects.ltrim(key, 1, 2)); assertThat(trim, equalTo("OK")); List lrange = exec(commandObjects.lrange(key, 0, -1)); assertThat(lrange, contains("two".getBytes(), "three".getBytes())); } @Test public void testLindexCommands() { String key = "list"; exec(commandObjects.rpush(key, "alpha", "beta", "gamma")); String lindex = exec(commandObjects.lindex(key, 1)); assertThat(lindex, equalTo("beta")); byte[] lindexBinary = exec(commandObjects.lindex(key.getBytes(), 2)); assertThat(lindexBinary, equalTo("gamma".getBytes())); String lindexOufOfRange = exec(commandObjects.lindex(key, 5)); assertThat(lindexOufOfRange, nullValue()); byte[] lindexLastPositionBinary = exec(commandObjects.lindex(key.getBytes(), -1)); assertThat(lindexLastPositionBinary, equalTo("gamma".getBytes())); } @Test public void testLset() { String key = "list"; String initialValue = "initial"; String updatedValue = "updated"; exec(commandObjects.rpush(key, initialValue)); String lindexBefore = exec(commandObjects.lindex(key, 0)); assertThat(lindexBefore, equalTo(initialValue)); String lset = exec(commandObjects.lset(key, 0, updatedValue)); assertThat(lset, equalTo("OK")); String lindexAfter = exec(commandObjects.lindex(key, 0)); assertThat(lindexAfter, equalTo(updatedValue)); } @Test public void testLsetBinary() { byte[] keyBytes = "list".getBytes(); String initialValue = "initial"; String updatedValue = "updated"; exec(commandObjects.rpush(keyBytes, initialValue.getBytes())); byte[] lindexBefore = exec(commandObjects.lindex(keyBytes, 0)); assertThat(lindexBefore, equalTo(initialValue.getBytes())); String lset = exec(commandObjects.lset(keyBytes, 0, updatedValue.getBytes())); assertThat(lset, equalTo("OK")); byte[] lindexAfter = exec(commandObjects.lindex(keyBytes, 0)); assertThat(lindexAfter, equalTo(updatedValue.getBytes())); } @Test public void testLrem() { String key = "remList"; exec(commandObjects.rpush(key, "duplicate", "duplicate", "unique")); List lrangeInitial = exec(commandObjects.lrange(key, 0, -1)); assertThat(lrangeInitial, contains("duplicate", "duplicate", "unique")); Long lrem = exec(commandObjects.lrem(key, 1, "duplicate")); assertThat(lrem, equalTo(1L)); List lrangeAfterLremSingle = exec(commandObjects.lrange(key, 0, -1)); assertThat(lrangeAfterLremSingle, contains("duplicate", "unique")); Long lremNonExistent = exec(commandObjects.lrem(key, 0, "nonexistent")); assertThat(lremNonExistent, equalTo(0L)); List lrangeAfterLremNonExistent = exec(commandObjects.lrange(key, 0, -1)); assertThat(lrangeAfterLremNonExistent, contains("duplicate", "unique")); } @Test public void testLremBinary() { byte[] keyBytes = "remList".getBytes(); exec(commandObjects.rpush(keyBytes, "duplicate".getBytes(), "duplicate".getBytes(), "unique".getBytes())); List lrangeBefore = exec(commandObjects.lrange(keyBytes, 0, -1)); assertThat(lrangeBefore, contains("duplicate".getBytes(), "duplicate".getBytes(), "unique".getBytes())); Long lremMultiple = exec(commandObjects.lrem(keyBytes, 0, "duplicate".getBytes())); assertThat(lremMultiple, equalTo(2L)); List lrangeAfter = exec(commandObjects.lrange(keyBytes, 0, -1)); assertThat(lrangeAfter, contains("unique".getBytes())); } @Test public void testPopCommands() { String key = "popList"; exec(commandObjects.rpush(key, "first", "second", "third", "first", "second", "third")); String lpop = exec(commandObjects.lpop(key)); assertThat(lpop, equalTo("first")); String rpop = exec(commandObjects.rpop(key)); assertThat(rpop, equalTo("third")); List lpopMultiple = exec(commandObjects.lpop(key, 2)); assertThat(lpopMultiple, contains("second", "third")); List rpopMultiple = exec(commandObjects.rpop(key, 2)); assertThat(rpopMultiple, contains("second", "first")); } @Test public void testPopCommandsBinary() { byte[] key = "popList".getBytes(); exec(commandObjects.rpush(key, "first".getBytes(), "second".getBytes(), "third".getBytes(), "first".getBytes(), "second".getBytes(), "third".getBytes())); byte[] lpop = exec(commandObjects.lpop(key)); assertThat(lpop, equalTo("first".getBytes())); byte[] rpop = exec(commandObjects.rpop(key)); assertThat(rpop, equalTo("third".getBytes())); List lpopMultiple = exec(commandObjects.lpop(key, 2)); assertThat(lpopMultiple, contains("second".getBytes(), "third".getBytes())); List rpopMultiple = exec(commandObjects.rpop(key, 2)); assertThat(rpopMultiple, contains("second".getBytes(), "first".getBytes())); } @Test public void testLpos() { String key = "list"; String value = "target"; String nonExistentValue = "ghost"; exec(commandObjects.rpush(key, "start", value, "middle", value, "end")); Long lposFirst = exec(commandObjects.lpos(key, value)); assertThat(lposFirst, equalTo(1L)); Long lposFirstBinary = exec(commandObjects.lpos(key.getBytes(), value.getBytes())); assertThat(lposFirstBinary, equalTo(1L)); LPosParams params = LPosParams.lPosParams().rank(-1); Long lposLast = exec(commandObjects.lpos(key, value, params)); assertThat(lposLast, equalTo(3L)); Long lposLastBinary = exec(commandObjects.lpos(key.getBytes(), value.getBytes(), params)); assertThat(lposLastBinary, equalTo(3L)); List lposMultiple = exec(commandObjects.lpos(key, value, params, 2)); assertThat(lposMultiple, contains(3L, 1L)); List lposMultipleBinary = exec(commandObjects.lpos(key.getBytes(), value.getBytes(), params, 2)); assertThat(lposMultipleBinary, contains(3L, 1L)); Long lposNonExistent = exec(commandObjects.lpos(key, nonExistentValue)); assertThat(lposNonExistent, nullValue()); Long lposNonExistentBinary = exec(commandObjects.lpos(key.getBytes(), nonExistentValue.getBytes())); assertThat(lposNonExistentBinary, nullValue()); } @Test public void testLinsert() { String key = "insertList"; String pivot = "pivot"; String valueBefore = "beforePivot"; String valueAfter = "afterPivot"; exec(commandObjects.rpush(key, pivot)); Long linsertBefore = exec(commandObjects.linsert(key, ListPosition.BEFORE, pivot, valueBefore)); assertThat(linsertBefore, equalTo(2L)); Long linsertAfter = exec(commandObjects.linsert(key, ListPosition.AFTER, pivot, valueAfter)); assertThat(linsertAfter, equalTo(3L)); List lrange = exec(commandObjects.lrange(key, 0, -1)); assertThat(lrange, contains(valueBefore, pivot, valueAfter)); } @Test public void testLinsertBinary() { byte[] key = "insertList".getBytes(); byte[] pivot = "pivot".getBytes(); byte[] valueBefore = "valueBefore".getBytes(); byte[] valueAfter = "valueAfter".getBytes(); exec(commandObjects.rpush(key, pivot)); Long linsertBefore = exec(commandObjects.linsert(key, ListPosition.BEFORE, pivot, valueBefore)); assertThat(linsertBefore, equalTo(2L)); Long linsertAfter = exec(commandObjects.linsert(key, ListPosition.AFTER, pivot, valueAfter)); assertThat(linsertAfter, equalTo(3L)); List lrange = exec(commandObjects.lrange(key, 0, -1)); assertThat(lrange, contains(valueBefore, pivot, valueAfter)); } @Test public void testPushxCommands() { String key = "pushxList"; String value1 = "first"; String value2 = "second"; Long lpushxInitial = exec(commandObjects.lpushx(key, value1)); assertThat(lpushxInitial, equalTo(0L)); Long rpushxInitial = exec(commandObjects.rpushx(key, value1)); assertThat(rpushxInitial, equalTo(0L)); Boolean exists = exec(commandObjects.exists(key)); assertThat(exists, equalTo(false)); exec(commandObjects.lpush(key, "init")); Long lpushx = exec(commandObjects.lpushx(key, value1, value2)); assertThat(lpushx, equalTo(3L)); // new size returned Long rpushx = exec(commandObjects.rpushx(key, value1, value2)); assertThat(rpushx, equalTo(5L)); List lrange = exec(commandObjects.lrange(key, 0, -1)); assertThat(lrange, contains(value2, value1, "init", value1, value2)); } @Test public void testPushxCommandsBinary() { byte[] key = "pushxList".getBytes(); byte[] value1 = "first".getBytes(); byte[] value2 = "second".getBytes(); Long lpushxInitial = exec(commandObjects.lpushx(key, value1)); assertThat(lpushxInitial, equalTo(0L)); Long rpushxInitial = exec(commandObjects.rpushx(key, value1)); assertThat(rpushxInitial, equalTo(0L)); Boolean exists = exec(commandObjects.exists(key)); assertThat(exists, equalTo(false)); exec(commandObjects.lpush(key, "init".getBytes())); Long lpushx = exec(commandObjects.lpushx(key, value1, value2)); assertThat(lpushx, equalTo(3L)); Long rpushx = exec(commandObjects.rpushx(key, value1, value2)); assertThat(rpushx, equalTo(5L)); List lrange = exec(commandObjects.lrange(key, 0, -1)); assertThat(lrange, contains(value2, value1, "init".getBytes(), value1, value2)); } @Test @ConditionalOnEnv(value = TestEnvUtil.ENV_REDIS_ENTERPRISE, enabled = false) public void testBlpop() { String key1 = "list1"; String key2 = "list2"; String value1 = "value1"; String value2 = "value2"; exec(commandObjects.lpush(key1, value1)); List blpop = exec(commandObjects.blpop(1, key1)); assertThat(blpop, contains(key1, value1)); exec(commandObjects.lpush(key1, value1)); exec(commandObjects.lpush(key2, value2)); List blpopMultiple = exec(commandObjects.blpop(1, key1, key2)); assertThat(blpopMultiple, anyOf(contains(key1, value1), contains(key2, value2))); exec(commandObjects.lpush(key1, value1)); KeyValue blpopDoubleTimeout = exec(commandObjects.blpop(1.0, key1)); assertThat(blpopDoubleTimeout.getKey(), equalTo(key1)); assertThat(blpopDoubleTimeout.getValue(), equalTo(value1)); exec(commandObjects.lpush(key1, value1)); exec(commandObjects.lpush(key2, value2)); KeyValue blpopDoubleTimeoutMultiple = exec(commandObjects.blpop(1.0, key1, key2)); assertThat(blpopDoubleTimeoutMultiple.getKey(), anyOf(equalTo(key1), equalTo(key2))); assertThat(blpopDoubleTimeoutMultiple.getValue(), anyOf(equalTo(value1), equalTo(value2))); } @Test @ConditionalOnEnv(value = TestEnvUtil.ENV_REDIS_ENTERPRISE, enabled = false) public void testBlpopBinary() { byte[] key1 = "list1".getBytes(); byte[] key2 = "list2".getBytes(); byte[] value1 = "value1".getBytes(); byte[] value2 = "value2".getBytes(); exec(commandObjects.lpush(key1, value1)); List blpop = exec(commandObjects.blpop(1, key1)); assertThat(blpop.get(0), equalTo(key1)); assertThat(blpop.get(1), equalTo(value1)); exec(commandObjects.lpush(key1, value1)); exec(commandObjects.lpush(key2, value2)); List blpopMultiple = exec(commandObjects.blpop(1, key1, key2)); assertThat(blpopMultiple, anyOf(contains(key1, value1), contains(key2, value2))); exec(commandObjects.lpush(key1, value1)); KeyValue blpopDoubleTimeout = exec(commandObjects.blpop(1.0, key1)); assertThat(blpopDoubleTimeout.getKey(), equalTo(key1)); assertThat(blpopDoubleTimeout.getValue(), equalTo(value1)); exec(commandObjects.lpush(key1, value1)); exec(commandObjects.lpush(key2, value2)); KeyValue blpopDoubleTimeoutMultiple = exec(commandObjects.blpop(1.0, key1, key2)); assertThat(blpopDoubleTimeoutMultiple.getKey(), anyOf(equalTo(key1), equalTo(key2))); assertThat(blpopDoubleTimeoutMultiple.getValue(), anyOf(equalTo(value1), equalTo(value2))); } @Test @ConditionalOnEnv(value = TestEnvUtil.ENV_REDIS_ENTERPRISE, enabled = false) public void testBrpop() { String key1 = "list1"; String key2 = "list2"; String value1 = "value1"; String value2 = "value2"; exec(commandObjects.lpush(key1, value1)); List brpop = exec(commandObjects.brpop(1, key1)); assertThat(brpop, contains(key1, value1)); exec(commandObjects.lpush(key1, value1)); exec(commandObjects.lpush(key2, value2)); List brpopMultiple = exec(commandObjects.brpop(1, key1, key2)); assertThat(brpopMultiple, anyOf(contains(key1, value1), contains(key2, value2))); exec(commandObjects.lpush(key1, value1)); KeyValue brpopDoubleTimeout = exec(commandObjects.brpop(1.0, key1)); assertThat(brpopDoubleTimeout.getKey(), equalTo(key1)); assertThat(brpopDoubleTimeout.getValue(), equalTo(value1)); exec(commandObjects.lpush(key1, value1)); exec(commandObjects.lpush(key2, value2)); KeyValue brpopDoubleTimeoutMultiple = exec(commandObjects.brpop(1.0, key1, key2)); assertThat(brpopDoubleTimeoutMultiple.getKey(), anyOf(equalTo(key1), equalTo(key2))); assertThat(brpopDoubleTimeoutMultiple.getValue(), anyOf(equalTo(value1), equalTo(value2))); } @Test @ConditionalOnEnv(value = TestEnvUtil.ENV_REDIS_ENTERPRISE, enabled = false) public void testBrpopBinary() { byte[] key1 = "list1".getBytes(); byte[] key2 = "list2".getBytes(); byte[] value1 = "value1".getBytes(); byte[] value2 = "value2".getBytes(); exec(commandObjects.lpush(key1, value1)); List brpop = exec(commandObjects.brpop(1, key1)); assertThat(brpop.get(0), equalTo(key1)); assertThat(brpop.get(1), equalTo(value1)); exec(commandObjects.lpush(key1, value1)); exec(commandObjects.lpush(key2, value2)); List brpopMultiple = exec(commandObjects.brpop(1, key1, key2)); assertThat(brpopMultiple, anyOf(contains(key1, value1), contains(key2, value2))); exec(commandObjects.lpush(key1, value1)); KeyValue brpopDoubleTimeout = exec(commandObjects.brpop(1.0, key1)); assertThat(brpopDoubleTimeout.getKey(), equalTo(key1)); assertThat(brpopDoubleTimeout.getValue(), equalTo(value1)); exec(commandObjects.lpush(key1, value1)); exec(commandObjects.lpush(key2, value2)); KeyValue brpopDoubleTimeoutMultiple = exec(commandObjects.brpop(1.0, key1, key2)); assertThat(brpopDoubleTimeoutMultiple.getKey(), anyOf(equalTo(key1), equalTo(key2))); assertThat(brpopDoubleTimeoutMultiple.getValue(), anyOf(equalTo(value1), equalTo(value2))); } @Test @ConditionalOnEnv(value = TestEnvUtil.ENV_REDIS_ENTERPRISE, enabled = false) public void testRpoplpushAndBrpoplpush() { String srcKey = "sourceList"; String dstKey = "destinationList"; String value1 = "value1"; String value2 = "value2"; String noResult = exec(commandObjects.rpoplpush(srcKey, dstKey)); assertThat(noResult, nullValue()); exec(commandObjects.lpush(srcKey, value1)); String result = exec(commandObjects.rpoplpush(srcKey, dstKey)); assertThat(result, equalTo(value1)); List dstList = exec(commandObjects.lrange(dstKey, 0, -1)); assertThat(dstList, contains(value1)); exec(commandObjects.lpush(srcKey, value2)); String bResult = exec(commandObjects.brpoplpush(srcKey, dstKey, 1)); assertThat(bResult, equalTo(value2)); dstList = exec(commandObjects.lrange(dstKey, 0, -1)); assertThat(dstList, contains(value2, value1)); } @Test @ConditionalOnEnv(value = TestEnvUtil.ENV_REDIS_ENTERPRISE, enabled = false) public void testRpoplpushAndBrpoplpushBinary() { byte[] srcKey = "sourceList".getBytes(); byte[] dstKey = "destinationList".getBytes(); byte[] value1 = "value1".getBytes(); byte[] value2 = "value2".getBytes(); exec(commandObjects.lpush(srcKey, value1)); byte[] result = exec(commandObjects.rpoplpush(srcKey, dstKey)); assertThat(result, equalTo(value1)); List dstList = exec(commandObjects.lrange(dstKey, 0, -1)); assertThat(dstList, contains(equalTo(value1))); exec(commandObjects.lpush(srcKey, value2)); byte[] bResult = exec(commandObjects.brpoplpush(srcKey, dstKey, 1)); assertThat(bResult, equalTo(value2)); dstList = exec(commandObjects.lrange(dstKey, 0, -1)); assertThat(dstList, contains(equalTo(value2), equalTo(value1))); } @Test @ConditionalOnEnv(value = TestEnvUtil.ENV_REDIS_ENTERPRISE, enabled = false) public void testLmoveAndBlmove() { String srcKey = "sourceList"; String dstKey = "destinationList"; String value1 = "value1"; String value2 = "value2"; exec(commandObjects.lpush(srcKey, value1)); String result = exec(commandObjects.lmove(srcKey, dstKey, ListDirection.LEFT, ListDirection.RIGHT)); assertThat(result, equalTo(value1)); List dstList = exec(commandObjects.lrange(dstKey, 0, -1)); assertThat(dstList, contains(value1)); exec(commandObjects.lpush(srcKey, value2)); String bResult = exec(commandObjects.blmove(srcKey, dstKey, ListDirection.LEFT, ListDirection.LEFT, 1.0)); assertThat(bResult, equalTo(value2)); dstList = exec(commandObjects.lrange(dstKey, 0, -1)); assertThat(dstList, contains(value2, value1)); } @Test @ConditionalOnEnv(value = TestEnvUtil.ENV_REDIS_ENTERPRISE, enabled = false) public void testLmoveAndBlmoveBinary() { byte[] srcKey = "sourceList".getBytes(); byte[] dstKey = "destinationList".getBytes(); byte[] value1 = "value1".getBytes(); byte[] value2 = "value2".getBytes(); exec(commandObjects.lpush(srcKey, value1)); byte[] result = exec(commandObjects.lmove(srcKey, dstKey, ListDirection.LEFT, ListDirection.RIGHT)); assertThat(result, equalTo(value1)); List dstList = exec(commandObjects.lrange(dstKey, 0, -1)); assertThat(dstList.get(0), equalTo(value1)); exec(commandObjects.lpush(srcKey, value2)); byte[] bResult = exec(commandObjects.blmove(srcKey, dstKey, ListDirection.LEFT, ListDirection.LEFT, 1.0)); assertThat(bResult, equalTo(value2)); dstList = exec(commandObjects.lrange(dstKey, 0, -1)); assertThat(dstList, contains(equalTo(value2), equalTo(value1))); } @Test @SinceRedisVersion(value = "7.0.0") @ConditionalOnEnv(value = TestEnvUtil.ENV_REDIS_ENTERPRISE, enabled = false) public void testLmpopAndBlmpop() { String key1 = "list1"; String key2 = "list2"; String value1 = "value1"; String value2 = "value2"; exec(commandObjects.lpush(key1, value1, value1, value1, value1, value1, value1)); exec(commandObjects.lpush(key2, value2, value2, value2, value2, value2, value2)); KeyValue> lmpop = exec(commandObjects.lmpop(ListDirection.LEFT, key1, key2)); assertThat(lmpop.getKey(), anyOf(equalTo(key1), equalTo(key2))); assertThat(lmpop.getValue(), anyOf(contains(value1), contains(value2))); KeyValue> lmpopMultiple = exec(commandObjects.lmpop(ListDirection.LEFT, 2, key1, key2)); assertThat(lmpopMultiple.getKey(), anyOf(equalTo(key1), equalTo(key2))); assertThat(lmpopMultiple.getValue(), anyOf(contains(value1, value1), contains(value2, value2))); KeyValue> blmpop = exec(commandObjects.blmpop(1.0, ListDirection.LEFT, key1, key2)); assertThat(blmpop.getKey(), anyOf(equalTo(key1), equalTo(key2))); assertThat(blmpop.getValue(), anyOf(contains(value1), contains(value2))); KeyValue> blmpopMultiple = exec(commandObjects.blmpop(1.0, ListDirection.LEFT, 2, key1, key2)); assertThat(blmpopMultiple.getKey(), anyOf(equalTo(key1), equalTo(key2))); assertThat(blmpopMultiple.getValue(), anyOf(contains(value1, value1), contains(value2, value2))); } @Test @SinceRedisVersion(value = "7.0.0") @ConditionalOnEnv(value = TestEnvUtil.ENV_REDIS_ENTERPRISE, enabled = false) public void testLmpopAndBlmpopBinary() { byte[] key1 = "list1".getBytes(); byte[] key2 = "list2".getBytes(); byte[] value1 = "value1".getBytes(); byte[] value2 = "value2".getBytes(); exec(commandObjects.lpush(key1, value1, value1, value1, value1, value1, value1)); exec(commandObjects.lpush(key2, value2, value2, value2, value2, value2, value2)); KeyValue> lmpop = exec(commandObjects.lmpop(ListDirection.LEFT, key1, key2)); assertThat(lmpop.getKey(), anyOf(equalTo(key1), equalTo(key2))); assertThat(lmpop.getValue(), anyOf(contains(equalTo(value1)), contains(equalTo(value2)))); KeyValue> lmpopMultiple = exec(commandObjects.lmpop(ListDirection.LEFT, 2, key1, key2)); assertThat(lmpopMultiple.getKey(), anyOf(equalTo(key1), equalTo(key2))); assertThat(lmpopMultiple.getValue(), anyOf(contains(equalTo(value1), equalTo(value1)), contains(equalTo(value2), equalTo(value2)))); KeyValue> blmpop = exec(commandObjects.blmpop(1.0, ListDirection.LEFT, key1, key2)); assertThat(blmpop.getKey(), anyOf(equalTo(key1), equalTo(key2))); assertThat(blmpop.getValue(), anyOf(contains(equalTo(value1)), contains(equalTo(value2)))); KeyValue> blmpopMultiple = exec(commandObjects.blmpop(1.0, ListDirection.LEFT, 2, key1, key2)); assertThat(blmpopMultiple.getKey(), anyOf(equalTo(key1), equalTo(key2))); assertThat(blmpopMultiple.getValue(), anyOf(contains(equalTo(value1), equalTo(value1)), contains(equalTo(value2), equalTo(value2)))); } } ================================================ FILE: src/test/java/redis/clients/jedis/commands/commandobjects/CommandObjectsModulesTestBase.java ================================================ package redis.clients.jedis.commands.commandobjects; import org.junit.jupiter.api.extension.RegisterExtension; import redis.clients.jedis.Endpoints; import redis.clients.jedis.RedisProtocol; import redis.clients.jedis.util.EnvCondition; /** * Base class for tests that need a Redis Stack server. */ public abstract class CommandObjectsModulesTestBase extends CommandObjectsTestBase { @RegisterExtension static EnvCondition envCondition = new EnvCondition(); public CommandObjectsModulesTestBase(RedisProtocol protocol) { super(protocol, Endpoints.getRedisEndpoint("modules-docker")); } } ================================================ FILE: src/test/java/redis/clients/jedis/commands/commandobjects/CommandObjectsScriptingCommandsTest.java ================================================ package redis.clients.jedis.commands.commandobjects; import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.contains; import static org.hamcrest.Matchers.containsString; import static org.hamcrest.Matchers.empty; import static org.hamcrest.Matchers.equalTo; import static org.hamcrest.Matchers.hasEntry; import static org.hamcrest.Matchers.hasKey; import static org.hamcrest.Matchers.hasSize; import static org.hamcrest.Matchers.notNullValue; import static org.hamcrest.Matchers.nullValue; import static org.junit.jupiter.api.Assertions.assertThrows; import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; import java.util.List; import java.util.Map; import io.redis.test.annotations.SinceRedisVersion; import io.redis.test.annotations.ConditionalOnEnv; import io.redis.test.utils.RedisVersion; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Tag; import redis.clients.jedis.RedisProtocol; import redis.clients.jedis.args.FlushMode; import redis.clients.jedis.args.FunctionRestorePolicy; import redis.clients.jedis.exceptions.JedisException; import redis.clients.jedis.resps.FunctionStats; import redis.clients.jedis.resps.LibraryInfo; import redis.clients.jedis.util.RedisVersionUtil; import redis.clients.jedis.util.TestEnvUtil; /** * Tests related to Scripting commands. */ @Tag("integration") public class CommandObjectsScriptingCommandsTest extends CommandObjectsStandaloneTestBase { public CommandObjectsScriptingCommandsTest(RedisProtocol protocol) { super(protocol); } @BeforeEach @Override public void setUp() { super.setUp(); if (RedisVersionUtil.getRedisVersion(endpoint).isGreaterThanOrEqualTo(RedisVersion.V7_0_0)) { assertThat(exec(commandObjects.functionFlush(FlushMode.SYNC)), equalTo("OK")); } } @Test public void testEvalWithOnlyScript() { String set = exec(commandObjects.set("foo", "bar")); assertThat(set, equalTo("OK")); String script = "return redis.call('get', 'foo')"; Object eval = exec(commandObjects.eval(script)); assertThat(eval, equalTo("bar")); Object evalBinary = exec(commandObjects.eval(script.getBytes())); assertThat(evalBinary, equalTo("bar".getBytes())); // eval with incorrect script assertThrows(JedisException.class, () -> exec(commandObjects.eval("return x"))); } @Test public void testEvalWithScriptAndSampleKey() { String set = exec(commandObjects.set("foo", "bar")); assertThat(set, equalTo("OK")); String script = "return redis.call('get', 'foo');"; Object eval = exec(commandObjects.eval(script, "sampleKey")); assertThat(eval, equalTo("bar")); Object evalBinary = exec(commandObjects.eval(script.getBytes(), "sampleKey".getBytes())); assertThat(evalBinary, equalTo("bar".getBytes())); } @Test @ConditionalOnEnv(value = TestEnvUtil.ENV_REDIS_ENTERPRISE, enabled = false) public void testEvalWithScriptKeyCountAndParams() { exec(commandObjects.set("key1", "value1")); exec(commandObjects.set("key2", "value2")); // Script to get values of two keys and compare them String script = "if redis.call('get', KEYS[1]) == ARGV[1] and redis.call('get', KEYS[2]) == ARGV[2] then return 'true' else return 'false' end"; Object evalTrue = exec(commandObjects.eval( script, 2, "key1", "key2", "value1", "value2")); assertThat(evalTrue, equalTo("true")); Object evalTrueBinary = exec(commandObjects.eval( script.getBytes(), 2, "key1".getBytes(), "key2".getBytes(), "value1".getBytes(), "value2".getBytes())); assertThat(evalTrueBinary, equalTo("true".getBytes())); Object evalFalse = exec(commandObjects.eval( script, 2, "key1", "key2", "value1", "value3")); assertThat(evalFalse, equalTo("false")); Object evalFalseBinary = exec(commandObjects.eval( script.getBytes(), 2, "key1".getBytes(), "key2".getBytes(), "value1".getBytes(), "value3".getBytes())); assertThat(evalFalseBinary, equalTo("false".getBytes())); // Incorrect number of keys specified assertThrows(JedisException.class, () -> exec(commandObjects.eval(script, 1, "key1", "value1", "value2"))); } @Test @ConditionalOnEnv(value = TestEnvUtil.ENV_REDIS_ENTERPRISE, enabled = false) public void testEvalWithScriptKeysAndArgsList() { exec(commandObjects.hset("fruits", "apples", "5")); exec(commandObjects.hset("fruits", "bananas", "3")); exec(commandObjects.hset("fruits", "oranges", "4")); // Script to sum the values for the fruits provided as args. The hash name is provided as key. // The sum is written to a string value whose name is also provided as keys. String script = "local sum = 0\n" + "for i, fruitKey in ipairs(ARGV) do\n" + " local value = redis.call('HGET', KEYS[1], fruitKey)\n" + " if value then\n" + " sum = sum + tonumber(value)\n" + " end\n" + "end\n" + "redis.call('SET', KEYS[2], sum)\n" + "return sum"; String initialTotal = exec(commandObjects.get("total")); assertThat(initialTotal, nullValue()); Object eval = exec(commandObjects.eval(script, Arrays.asList("fruits", "total"), Arrays.asList("apples", "bananas", "oranges"))); assertThat(eval, equalTo(12L)); String totalAfterEval = exec(commandObjects.get("total")); assertThat(totalAfterEval, equalTo("12")); // reset assertThat(exec(commandObjects.del("total")), equalTo(1L)); // binary String initialTotalBinary = exec(commandObjects.get("total")); assertThat(initialTotalBinary, nullValue()); Object evalBinary = exec(commandObjects.eval(script.getBytes(), Arrays.asList("fruits".getBytes(), "total".getBytes()), Arrays.asList("apples".getBytes(), "bananas".getBytes(), "oranges".getBytes()))); assertThat(evalBinary, equalTo(12L)); String totalAfterEvalBinary = exec(commandObjects.get("total")); assertThat(totalAfterEvalBinary, equalTo("12")); } @Test @SinceRedisVersion(value = "7.0.0") @ConditionalOnEnv(value = TestEnvUtil.ENV_REDIS_ENTERPRISE, enabled = false) public void testEvalReadonlyWithScriptKeysAndArgsList() { exec(commandObjects.set("readonlyKey1", "readonlyValue1")); exec(commandObjects.set("readonlyKey2", "readonlyValue2")); // Script to retrieve values for provided keys, concatenates String script = "return redis.call('get', KEYS[1]) .. redis.call('get', KEYS[2])"; Object eval = exec(commandObjects.evalReadonly( script, Arrays.asList("readonlyKey1", "readonlyKey2"), Collections.emptyList())); assertThat(eval, equalTo("readonlyValue1readonlyValue2")); Object evalBinary = exec(commandObjects.evalReadonly( script.getBytes(), Arrays.asList("readonlyKey1".getBytes(), "readonlyKey2".getBytes()), Collections.emptyList())); assertThat(evalBinary, equalTo("readonlyValue1readonlyValue2".getBytes())); } @Test public void testEvalshaWithSha1() { String script = "return 42"; String sha1 = exec(commandObjects.scriptLoad(script)); assertThat(sha1, notNullValue()); Object eval = exec(commandObjects.evalsha(sha1)); assertThat(eval, equalTo(42L)); Object evalBinary = exec(commandObjects.evalsha(sha1.getBytes())); assertThat(evalBinary, equalTo(42L)); // incorrect SHA1 hash assertThrows(JedisException.class, () -> exec(commandObjects.evalsha("incorrectSha1"))); } @Test public void testEvalshaWithSha1AndSampleKey() { String script = "return redis.call('get', 'foo')"; String sha1 = exec(commandObjects.scriptLoad(script)); assertThat(sha1, notNullValue()); exec(commandObjects.set("foo", "bar")); Object eval = exec(commandObjects.evalsha(sha1, "sampleKey")); assertThat(eval, equalTo("bar")); Object evalBinary = exec(commandObjects.evalsha(sha1.getBytes(), "sampleKey".getBytes())); assertThat(evalBinary, equalTo("bar".getBytes())); } @Test @ConditionalOnEnv(value = TestEnvUtil.ENV_REDIS_ENTERPRISE, enabled = false) public void testEvalWithScriptKeyCountAndParamsSha() { exec(commandObjects.set("key1", "value1")); exec(commandObjects.set("key2", "value2")); // Script to get values of two keys and compare them with expected values String script = "if redis.call('get', KEYS[1]) == ARGV[1] and redis.call('get', KEYS[2]) == ARGV[2] then return 'true' else return 'false' end"; String sha1 = exec(commandObjects.scriptLoad(script)); assertThat(sha1, notNullValue()); Object evalTrue = exec(commandObjects.evalsha( sha1, 2, "key1", "key2", "value1", "value2")); assertThat(evalTrue, equalTo("true")); Object evalTrueBinary = exec(commandObjects.evalsha( sha1.getBytes(), 2, "key1".getBytes(), "key2".getBytes(), "value1".getBytes(), "value2".getBytes())); assertThat(evalTrueBinary, equalTo("true".getBytes())); Object evalFalse = exec(commandObjects.evalsha( sha1, 2, "key1", "key2", "value1", "value3")); assertThat(evalFalse, equalTo("false")); Object evalFalseBinary = exec(commandObjects.evalsha( sha1.getBytes(), 2, "key1".getBytes(), "key2".getBytes(), "value1".getBytes(), "value3".getBytes())); assertThat(evalFalseBinary, equalTo("false".getBytes())); // Incorrect number of keys assertThrows(JedisException.class, () -> exec(commandObjects.evalsha(sha1, 1, "key1", "value1", "value2"))); } @Test @ConditionalOnEnv(value = TestEnvUtil.ENV_REDIS_ENTERPRISE, enabled = false) public void testEvalWithScriptKeysAndArgsListSha() { exec(commandObjects.hset("fruits", "apples", "5")); exec(commandObjects.hset("fruits", "bananas", "3")); exec(commandObjects.hset("fruits", "oranges", "4")); // Sums the values for given fruits, stores the result, and returns it String script = "local sum = 0\n" + "for i, fruitKey in ipairs(ARGV) do\n" + " local value = redis.call('HGET', KEYS[1], fruitKey)\n" + " if value then\n" + " sum = sum + tonumber(value)\n" + " end\n" + "end\n" + "redis.call('SET', KEYS[2], sum)\n" + "return sum"; String sha1 = exec(commandObjects.scriptLoad(script)); assertThat(sha1, notNullValue()); String initialTotal = exec(commandObjects.get("total")); assertThat(initialTotal, nullValue()); Object eval = exec(commandObjects.evalsha( sha1, Arrays.asList("fruits", "total"), Arrays.asList("apples", "bananas", "oranges"))); assertThat(eval, equalTo(12L)); String totalAfterEval = exec(commandObjects.get("total")); assertThat(totalAfterEval, equalTo("12")); // reset assertThat(exec(commandObjects.del("total")), equalTo(1L)); // binary String initialTotalBinary = exec(commandObjects.get("total")); assertThat(initialTotalBinary, nullValue()); Object evalBinary = exec(commandObjects.evalsha( sha1.getBytes(), Arrays.asList("fruits".getBytes(), "total".getBytes()), Arrays.asList("apples".getBytes(), "bananas".getBytes(), "oranges".getBytes()))); assertThat(evalBinary, equalTo(12L)); String totalAfterEvalBinary = exec(commandObjects.get("total")); assertThat(totalAfterEvalBinary, equalTo("12")); } @Test @SinceRedisVersion(value = "7.0.0") @ConditionalOnEnv(value = TestEnvUtil.ENV_REDIS_ENTERPRISE, enabled = false) public void testEvalReadonlyWithScriptKeysAndArgsListSha() { exec(commandObjects.set("readonlyKey1", "readonlyValue1")); exec(commandObjects.set("readonlyKey2", "readonlyValue2")); // Script to retrieve values for provided keys, concatenated String script = "return redis.call('get', KEYS[1]) .. redis.call('get', KEYS[2])"; String sha1 = exec(commandObjects.scriptLoad(script)); assertThat(sha1, notNullValue()); Object eval = exec(commandObjects.evalshaReadonly( sha1, Arrays.asList("readonlyKey1", "readonlyKey2"), Collections.emptyList())); assertThat(eval, equalTo("readonlyValue1readonlyValue2")); Object evalBinary = exec(commandObjects.evalshaReadonly( sha1.getBytes(), Arrays.asList("readonlyKey1".getBytes(), "readonlyKey2".getBytes()), Collections.emptyList())); assertThat(evalBinary, equalTo("readonlyValue1readonlyValue2".getBytes())); } @Test public void testScriptExists() { String script = "return 'test script'"; String sha1 = exec(commandObjects.scriptLoad(script)); assertThat(sha1, notNullValue()); List exists = exec(commandObjects.scriptExists(Collections.singletonList(sha1))); assertThat(exists, contains(true)); // Load another script to test with multiple SHA1 hashes String anotherScript = "return 'another test script'"; String anotherSha1 = exec(commandObjects.scriptLoad(anotherScript)); assertThat(anotherSha1, notNullValue()); String nonExistingSha1 = "nonexistentsha1"; List existsMultiple = exec(commandObjects.scriptExists( "sampleKey", sha1, anotherSha1, nonExistingSha1)); assertThat(existsMultiple, contains(true, true, false)); List existsMultipleBinary = exec(commandObjects.scriptExists( "sampleKey".getBytes(), sha1.getBytes(), anotherSha1.getBytes(), nonExistingSha1.getBytes())); assertThat(existsMultipleBinary, contains(true, true, false)); } @Test public void testScriptLoadAndRun() { String script = "return 'Hello, Redis!'"; String sha1 = exec(commandObjects.scriptLoad(script)); assertThat(sha1, notNullValue()); Object scriptResponse1 = exec(commandObjects.evalsha(sha1)); assertThat(scriptResponse1, equalTo("Hello, Redis!")); } @Test public void testScriptLoadAndRunSampleKey() { String anotherScript = "return redis.call('get', 'testKey')"; String sampleKey = "testKey"; exec(commandObjects.set(sampleKey, "sampleValue")); // Set a value for the sampleKey String anotherSha1 = exec(commandObjects.scriptLoad(anotherScript, sampleKey)); assertThat(anotherSha1, notNullValue()); Object scriptResponse2 = exec(commandObjects.evalsha(anotherSha1, sampleKey)); assertThat(scriptResponse2, equalTo("sampleValue")); } @Test public void testScriptLoadAndRunSampleKeyBinary() { String anotherScript = "return redis.call('get', 'testKey')"; String sampleKey = "testKey"; exec(commandObjects.set(sampleKey, "sampleValue")); // Set a value for the sampleKey byte[] anotherSha1 = exec(commandObjects.scriptLoad(anotherScript.getBytes(), sampleKey.getBytes())); assertThat(anotherSha1, notNullValue()); Object scriptResponse2 = exec(commandObjects.evalsha(anotherSha1, sampleKey.getBytes())); assertThat(scriptResponse2, equalTo("sampleValue".getBytes())); } @Test public void testScriptFlush() { String script = "return 'test script flush'"; String sha1 = exec(commandObjects.scriptLoad(script)); assertThat(sha1, notNullValue()); List existsBefore = exec(commandObjects.scriptExists(Collections.singletonList(sha1))); assertThat(existsBefore, contains(true)); String flush = exec(commandObjects.scriptFlush()); assertThat(flush, equalTo("OK")); List existsAfter = exec(commandObjects.scriptExists(Collections.singletonList(sha1))); assertThat(existsAfter, contains(false)); } @Test public void testScriptFlushSampleKeyAndMode() { String script = "return 'test script flush'"; String sha1 = exec(commandObjects.scriptLoad(script)); assertThat(sha1, notNullValue()); List existsBefore = exec(commandObjects.scriptExists(Collections.singletonList(sha1))); assertThat(existsBefore, contains(true)); String flush = exec(commandObjects.scriptFlush("anyKey", FlushMode.SYNC)); assertThat(flush, equalTo("OK")); List existsAfter = exec(commandObjects.scriptExists(Collections.singletonList(sha1))); assertThat(existsAfter, contains(false)); } @Test public void testScriptFlushSampleKey() { String script = "return 'test script flush'"; String sha1 = exec(commandObjects.scriptLoad(script)); assertThat(sha1, notNullValue()); List existsBefore = exec(commandObjects.scriptExists(Collections.singletonList(sha1))); assertThat(existsBefore, contains(true)); String flush = exec(commandObjects.scriptFlush("anyKey")); assertThat(flush, equalTo("OK")); List existsAfter = exec(commandObjects.scriptExists(Collections.singletonList(sha1))); assertThat(existsAfter, contains(false)); } @Test public void testScriptFlushBinary() { String script = "return 'test script flush'"; String sha1 = exec(commandObjects.scriptLoad(script)); assertThat(sha1, notNullValue()); List existsBefore = exec(commandObjects.scriptExists(Collections.singletonList(sha1))); assertThat(existsBefore, contains(true)); String flush = exec(commandObjects.scriptFlush("anyKey".getBytes())); assertThat(flush, equalTo("OK")); List existsAfter = exec(commandObjects.scriptExists(Collections.singletonList(sha1))); assertThat(existsAfter, contains(false)); } @Test public void testScriptFlushSampleKeyAndModeBinary() { String script = "return 'test script flush'"; String sha1 = exec(commandObjects.scriptLoad(script)); assertThat(sha1, notNullValue()); List existsBefore = exec(commandObjects.scriptExists(Collections.singletonList(sha1))); assertThat(existsBefore, contains(true)); String flush = exec(commandObjects.scriptFlush("anyKey".getBytes(), FlushMode.SYNC)); assertThat(flush, equalTo("OK")); List existsAfter = exec(commandObjects.scriptExists(Collections.singletonList(sha1))); assertThat(existsAfter, contains(false)); } @Test public void testScriptKill() { JedisException e = assertThrows(JedisException.class, () -> exec(commandObjects.scriptKill())); assertThat(e.getMessage(), containsString("No scripts in execution right now.")); e = assertThrows(JedisException.class, () -> exec(commandObjects.scriptKill("anyKey"))); assertThat(e.getMessage(), containsString("No scripts in execution right now.")); e = assertThrows(JedisException.class, () -> exec(commandObjects.scriptKill("anyKey".getBytes()))); assertThat(e.getMessage(), containsString("No scripts in execution right now.")); } @Test @SinceRedisVersion(value = "7.0.0") @ConditionalOnEnv(value = TestEnvUtil.ENV_REDIS_ENTERPRISE, enabled = false) public void testSumValuesFunction() { String luaScript = "#!lua name=mylib\n" + "redis.register_function('sumValues', function(keys, args)\n" + "local sum = 0\n" + "for _, key in ipairs(keys) do\n" + "local val = redis.call('GET', key)\n" + "if val then sum = sum + tonumber(val) end\n" + "end\n" + "redis.call('SET', 'total', sum)\n" + "return sum\n" + "end)"; String functionLoad = exec(commandObjects.functionLoad(luaScript)); assertThat(functionLoad, equalTo("mylib")); exec(commandObjects.set("key1", "10")); exec(commandObjects.set("key2", "20")); exec(commandObjects.set("key3", "30")); String initialTotal = exec(commandObjects.get("total")); assertThat(initialTotal, nullValue()); Object fcall = exec(commandObjects.fcall( "sumValues", Arrays.asList("key1", "key2", "key3"), new ArrayList<>())); assertThat(fcall, equalTo(60L)); String totalAfterFcall = exec(commandObjects.get("total")); assertThat(totalAfterFcall, equalTo("60")); // reset exec(commandObjects.del("total")); String totalAfterRest = exec(commandObjects.get("total")); assertThat(totalAfterRest, nullValue()); Object fcallBinary = exec(commandObjects.fcall( "sumValues".getBytes(), Arrays.asList("key1".getBytes(), "key2".getBytes(), "key3".getBytes()), new ArrayList<>())); assertThat(fcallBinary, equalTo(60L)); String totalAfterFcallBinary = exec(commandObjects.get("total")); assertThat(totalAfterFcallBinary, equalTo("60")); } @Test @SinceRedisVersion(value = "7.0.0") @ConditionalOnEnv(value = TestEnvUtil.ENV_REDIS_ENTERPRISE, enabled = false) public void testSumValuesFunctionReadonly() { String luaScript = "#!lua name=mylib\n" + "redis.register_function{function_name='sumValues', callback=function(keys, args)\n" + "local sum = 0\n" + "for _, key in ipairs(keys) do\n" + "local val = redis.call('GET', key)\n" + "if val then sum = sum + tonumber(val) end\n" + "end\n" + "return sum\n" + "end, flags={'no-writes'}}"; String functionLoad = exec(commandObjects.functionLoad(luaScript)); assertThat(functionLoad, equalTo("mylib")); exec(commandObjects.set("key1", "10")); exec(commandObjects.set("key2", "20")); exec(commandObjects.set("key3", "30")); Object fcall = exec(commandObjects.fcallReadonly( "sumValues", Arrays.asList("key1", "key2", "key3"), new ArrayList<>())); assertThat(fcall, equalTo(60L)); Object fcallBinary = exec(commandObjects.fcallReadonly( "sumValues".getBytes(), Arrays.asList("key1".getBytes(), "key2".getBytes(), "key3".getBytes()), new ArrayList<>())); assertThat(fcallBinary, equalTo(60L)); } @Test @SinceRedisVersion(value = "7.0.0") public void testFunctionDeletion() { String luaScript = "#!lua name=mylib\n" + "redis.register_function('sumValues', function(keys, args) return 42 end)"; exec(commandObjects.functionLoad(luaScript)); String libraryName = "mylib"; List listResponse = exec(commandObjects.functionList()); assertThat(listResponse, hasSize(1)); assertThat(listResponse.get(0).getLibraryName(), equalTo(libraryName)); assertThat(listResponse.get(0).getFunctions(), hasSize(1)); assertThat(listResponse.get(0).getFunctions().get(0), hasEntry("name", "sumValues")); String delete = exec(commandObjects.functionDelete(libraryName)); assertThat(delete, equalTo("OK")); listResponse = exec(commandObjects.functionList()); assertThat(listResponse, empty()); } @Test @SinceRedisVersion(value = "7.0.0") public void testFunctionDeletionBinary() { String luaScript = "#!lua name=mylib\n" + "redis.register_function('sumValues', function(keys, args) return 42 end)"; exec(commandObjects.functionLoad(luaScript)); String libraryName = "mylib"; List listResponse = exec(commandObjects.functionList()); assertThat(listResponse, hasSize(1)); assertThat(listResponse.get(0).getLibraryName(), equalTo(libraryName)); assertThat(listResponse.get(0).getFunctions(), hasSize(1)); assertThat(listResponse.get(0).getFunctions().get(0), hasEntry("name", "sumValues")); String deleteBinary = exec(commandObjects.functionDelete(libraryName.getBytes())); assertThat(deleteBinary, equalTo("OK")); listResponse = exec(commandObjects.functionList()); assertThat(listResponse, empty()); } @Test @SinceRedisVersion(value = "7.0.0") public void testFunctionListing() { String luaScript = "#!lua name=mylib\n" + "redis.register_function('sumValues', function(keys, args) return 42 end)"; exec(commandObjects.functionLoad(luaScript)); String libraryName = "mylib"; List list = exec(commandObjects.functionList()); assertThat(list, hasSize(1)); assertThat(list.get(0).getLibraryName(), equalTo(libraryName)); assertThat(list.get(0).getFunctions(), hasSize(1)); assertThat(list.get(0).getFunctions().get(0), hasEntry("name", "sumValues")); assertThat(list.get(0).getLibraryCode(), nullValue()); List listBinary = exec(commandObjects.functionListBinary()); assertThat(listBinary, hasSize(1)); List listLibrary = exec(commandObjects.functionList(libraryName)); assertThat(listLibrary, hasSize(1)); assertThat(listLibrary.get(0).getLibraryName(), equalTo(libraryName)); assertThat(listLibrary.get(0).getFunctions(), hasSize(1)); assertThat(listLibrary.get(0).getFunctions().get(0), hasEntry("name", "sumValues")); assertThat(listLibrary.get(0).getLibraryCode(), nullValue()); List listLibraryBinary = exec(commandObjects.functionList(libraryName.getBytes())); assertThat(listLibraryBinary, hasSize(1)); List listWithCode = exec(commandObjects.functionListWithCode()); assertThat(listWithCode, hasSize(1)); assertThat(listWithCode.get(0).getLibraryName(), equalTo(libraryName)); assertThat(listWithCode.get(0).getFunctions(), hasSize(1)); assertThat(listWithCode.get(0).getFunctions().get(0), hasEntry("name", "sumValues")); assertThat(listWithCode.get(0).getLibraryCode(), notNullValue()); List listWithCodeBinary = exec(commandObjects.functionListWithCodeBinary()); assertThat(listWithCodeBinary, hasSize(1)); List listWithCodeLibrary = exec(commandObjects.functionListWithCode(libraryName)); assertThat(listWithCodeLibrary, hasSize(1)); assertThat(listWithCodeLibrary.get(0).getLibraryName(), equalTo(libraryName)); assertThat(listWithCodeLibrary.get(0).getFunctions(), hasSize(1)); assertThat(listWithCodeLibrary.get(0).getFunctions().get(0), hasEntry("name", "sumValues")); assertThat(listWithCodeLibrary.get(0).getLibraryCode(), notNullValue()); List listWithCodeLibraryBinary = exec(commandObjects.functionListWithCode(libraryName.getBytes())); assertThat(listWithCodeLibraryBinary, hasSize(1)); } @Test @SinceRedisVersion(value = "7.0.0") public void testFunctionReload() { String luaScript = "#!lua name=mylib\n" + "redis.register_function('dummy', function(keys, args) return 42 end)"; String loadResult = exec(commandObjects.functionLoad(luaScript)); assertThat(loadResult, equalTo("mylib")); Object result = exec(commandObjects.fcall( "dummy".getBytes(), new ArrayList<>(), new ArrayList<>())); assertThat(result, equalTo(42L)); String luaScriptChanged = "#!lua name=mylib\n" + "redis.register_function('dummy', function(keys, args) return 52 end)"; String replaceResult = exec(commandObjects.functionLoadReplace(luaScriptChanged)); assertThat(replaceResult, equalTo("mylib")); Object resultAfter = exec(commandObjects.fcall( "dummy".getBytes(), new ArrayList<>(), new ArrayList<>())); assertThat(resultAfter, equalTo(52L)); } @Test @SinceRedisVersion(value = "7.0.0") public void testFunctionReloadBinary() { String luaScript = "#!lua name=mylib\n" + "redis.register_function('dummy', function(keys, args) return 42 end)"; String loadResult = exec(commandObjects.functionLoad(luaScript.getBytes())); assertThat(loadResult, equalTo("mylib")); Object result = exec(commandObjects.fcall(( "dummy").getBytes(), new ArrayList<>(), new ArrayList<>())); assertThat(result, equalTo(42L)); String luaScriptChanged = "#!lua name=mylib\n" + "redis.register_function('dummy', function(keys, args) return 52 end)"; String replaceResult = exec(commandObjects.functionLoadReplace(luaScriptChanged.getBytes())); assertThat(replaceResult, equalTo("mylib")); Object resultAfter = exec(commandObjects.fcall( "dummy".getBytes(), new ArrayList<>(), new ArrayList<>())); assertThat(resultAfter, equalTo(52L)); } @Test @SinceRedisVersion(value = "7.0.0") public void testFunctionStats() { String luaScript = "#!lua name=mylib\n" + "redis.register_function('dummy', function(keys, args) return 42 end)"; String loadResult = exec(commandObjects.functionLoad(luaScript)); assertThat(loadResult, equalTo("mylib")); for (int i = 0; i < 5; i++) { Object result = exec(commandObjects.fcall( "dummy".getBytes(), new ArrayList<>(), new ArrayList<>())); assertThat(result, equalTo(42L)); } FunctionStats stats = exec(commandObjects.functionStats()); assertThat(stats, notNullValue()); assertThat(stats.getEngines(), hasKey("LUA")); Map luaStats = stats.getEngines().get("LUA"); assertThat(luaStats, hasEntry("libraries_count", 1L)); assertThat(luaStats, hasEntry("functions_count", 1L)); Object statsBinary = exec(commandObjects.functionStatsBinary()); assertThat(statsBinary, notNullValue()); } @Test @SinceRedisVersion(value = "7.0.0") public void testFunctionDumpFlushRestore() { String luaScript = "#!lua name=mylib\n" + "redis.register_function('sumValues', function(keys, args) return 42 end)"; exec(commandObjects.functionLoad(luaScript)); String libraryName = "mylib"; List list = exec(commandObjects.functionList()); assertThat(list, hasSize(1)); assertThat(list.get(0).getLibraryName(), equalTo(libraryName)); assertThat(list.get(0).getFunctions(), hasSize(1)); assertThat(list.get(0).getFunctions().get(0), hasEntry("name", "sumValues")); byte[] dump = exec(commandObjects.functionDump()); assertThat(dump, notNullValue()); String flush = exec(commandObjects.functionFlush()); assertThat(flush, equalTo("OK")); list = exec(commandObjects.functionList()); assertThat(list, empty()); String restore = exec(commandObjects.functionRestore(dump)); assertThat(restore, equalTo("OK")); list = exec(commandObjects.functionList()); assertThat(list, hasSize(1)); assertThat(list.get(0).getLibraryName(), equalTo(libraryName)); assertThat(list.get(0).getFunctions(), hasSize(1)); assertThat(list.get(0).getFunctions().get(0), hasEntry("name", "sumValues")); } @Test @SinceRedisVersion(value = "7.0.0") public void testFunctionDumpFlushRestoreWithPolicy() { String luaScript = "#!lua name=mylib\n" + "redis.register_function('sumValues', function(keys, args) return 42 end)"; exec(commandObjects.functionLoad(luaScript)); String libraryName = "mylib"; List list = exec(commandObjects.functionList()); assertThat(list, hasSize(1)); assertThat(list.get(0).getLibraryName(), equalTo(libraryName)); assertThat(list.get(0).getFunctions(), hasSize(1)); assertThat(list.get(0).getFunctions().get(0), hasEntry("name", "sumValues")); byte[] dump = exec(commandObjects.functionDump()); assertThat(dump, notNullValue()); String flush = exec(commandObjects.functionFlush()); assertThat(flush, equalTo("OK")); list = exec(commandObjects.functionList()); assertThat(list, empty()); String restore = exec(commandObjects.functionRestore(dump, FunctionRestorePolicy.REPLACE)); assertThat(restore, equalTo("OK")); list = exec(commandObjects.functionList()); assertThat(list, hasSize(1)); assertThat(list.get(0).getLibraryName(), equalTo(libraryName)); assertThat(list.get(0).getFunctions(), hasSize(1)); assertThat(list.get(0).getFunctions().get(0), hasEntry("name", "sumValues")); } @Test @SinceRedisVersion(value = "7.0.0") public void testFunctionFlushWithMode() { String luaScript = "#!lua name=mylib\n" + "redis.register_function('sumValues', function(keys, args) return 42 end)"; exec(commandObjects.functionLoad(luaScript)); String libraryName = "mylib"; List list = exec(commandObjects.functionList()); assertThat(list, hasSize(1)); assertThat(list.get(0).getLibraryName(), equalTo(libraryName)); assertThat(list.get(0).getFunctions(), hasSize(1)); assertThat(list.get(0).getFunctions().get(0), hasEntry("name", "sumValues")); String flush = exec(commandObjects.functionFlush(FlushMode.SYNC)); assertThat(flush, equalTo("OK")); list = exec(commandObjects.functionList()); assertThat(list, empty()); } @Test @SinceRedisVersion(value = "7.0.0") public void testFunctionKill() { JedisException e = assertThrows(JedisException.class, () -> exec(commandObjects.functionKill())); assertThat(e.getMessage(), containsString("No scripts in execution right now")); } } ================================================ FILE: src/test/java/redis/clients/jedis/commands/commandobjects/CommandObjectsSearchAndQueryCommandsTest.java ================================================ package redis.clients.jedis.commands.commandobjects; import static net.javacrumbs.jsonunit.JsonMatchers.jsonEquals; import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.anEmptyMap; import static org.hamcrest.Matchers.contains; import static org.hamcrest.Matchers.containsInAnyOrder; import static org.hamcrest.Matchers.empty; import static org.hamcrest.Matchers.emptyOrNullString; import static org.hamcrest.Matchers.equalTo; import static org.hamcrest.Matchers.equalToIgnoringCase; import static org.hamcrest.Matchers.hasEntry; import static org.hamcrest.Matchers.hasKey; import static org.hamcrest.Matchers.hasSize; import static org.hamcrest.Matchers.instanceOf; import static org.hamcrest.Matchers.not; import static org.hamcrest.Matchers.notNullValue; import static org.hamcrest.Matchers.nullValue; import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Set; import java.util.stream.Collectors; import org.json.JSONObject; import org.junit.jupiter.api.Test; import redis.clients.jedis.RedisProtocol; import redis.clients.jedis.args.SortingOrder; import redis.clients.jedis.json.Path2; import redis.clients.jedis.resps.Tuple; import redis.clients.jedis.search.Document; import redis.clients.jedis.search.FTCreateParams; import redis.clients.jedis.search.FTSearchParams; import redis.clients.jedis.search.FTSpellCheckParams; import redis.clients.jedis.search.IndexDataType; import redis.clients.jedis.search.IndexDefinition; import redis.clients.jedis.search.IndexOptions; import redis.clients.jedis.search.Query; import redis.clients.jedis.search.Schema; import redis.clients.jedis.search.SearchResult; import redis.clients.jedis.search.aggr.AggregationBuilder; import redis.clients.jedis.search.aggr.AggregationResult; import redis.clients.jedis.search.aggr.Reducers; import redis.clients.jedis.search.schemafields.NumericField; import redis.clients.jedis.search.schemafields.SchemaField; import redis.clients.jedis.search.schemafields.TagField; import redis.clients.jedis.search.schemafields.TextField; /** * Tests related to Search and query commands. */ public class CommandObjectsSearchAndQueryCommandsTest extends CommandObjectsModulesTestBase { public CommandObjectsSearchAndQueryCommandsTest(RedisProtocol protocol) { super(protocol); } @Test public void testFtSearchHash() { String indexName = "booksIndex"; IndexDefinition indexDefinition = new IndexDefinition(IndexDefinition.Type.HASH).setPrefixes("books:"); IndexOptions indexOptions = IndexOptions.defaultOptions().setDefinition(indexDefinition); Schema schema = new Schema() .addField(new Schema.Field("title", Schema.FieldType.TEXT)) .addField(new Schema.Field("price", Schema.FieldType.NUMERIC)); String create = exec(commandObjects.ftCreate(indexName, indexOptions, schema)); assertThat(create, equalTo("OK")); // Set individual fields. String book1000 = "books:1000"; Long hset = exec(commandObjects.hsetObject(book1000, "title", "Redis in Action")); assertThat(hset, equalTo(1L)); hset = exec(commandObjects.hsetObject(book1000, "price", 17.99)); assertThat(hset, equalTo(1L)); hset = exec(commandObjects.hsetObject(book1000, "author", "John Doe")); assertThat(hset, equalTo(1L)); // Set multiple fields. Map hash = new HashMap<>(); hash.put("title", "Redis Essentials"); hash.put("price", 19.99); hash.put("author", "Jane Doe"); String book1200 = "books:1200"; Long hsetMultiple = exec(commandObjects.hsetObject(book1200, hash)); assertThat(hsetMultiple, equalTo(3L)); // Text search. SearchResult search = exec(commandObjects.ftSearch(indexName, "Action")); assertThat(search.getTotalResults(), equalTo(1L)); assertThat(search.getDocuments(), hasSize(1)); Document document = search.getDocuments().get(0); assertThat(document.getId(), equalTo(book1000)); assertThat(document.get("title"), equalTo("Redis in Action")); assertThat(document.get("price"), equalTo("17.99")); assertThat(document.get("author"), equalTo("John Doe")); // Price range search. SearchResult searchByPrice = exec(commandObjects.ftSearch(indexName, "@price:[19 +inf]")); assertThat(searchByPrice.getTotalResults(), equalTo(1L)); assertThat(searchByPrice.getDocuments(), hasSize(1)); Document documentByPrice = searchByPrice.getDocuments().get(0); assertThat(documentByPrice.getId(), equalTo(book1200)); assertThat(documentByPrice.get("title"), equalTo("Redis Essentials")); assertThat(documentByPrice.get("price"), equalTo("19.99")); assertThat(documentByPrice.get("author"), equalTo("Jane Doe")); // Price range search with sorting. FTSearchParams ftSearchParams = new FTSearchParams().sortBy("price", SortingOrder.DESC); SearchResult searchByPriceWithParams = exec(commandObjects.ftSearch(indexName, "@price:[10 20]", ftSearchParams)); assertThat(searchByPriceWithParams.getTotalResults(), equalTo(2L)); assertThat(searchByPriceWithParams.getDocuments(), hasSize(2)); assertThat(searchByPriceWithParams.getDocuments().stream().map(Document::getId).collect(Collectors.toList()), contains(book1200, book1000)); Query query = new Query() .addFilter(new Query.NumericFilter("price", 19.0, 20.0)) .returnFields("price", "title"); SearchResult searchByPriceWithQuery = exec(commandObjects.ftSearch(indexName, query)); assertThat(searchByPriceWithQuery.getTotalResults(), equalTo(1L)); assertThat(searchByPriceWithQuery.getDocuments(), hasSize(1)); Document documentByPriceWithQuery = searchByPriceWithQuery.getDocuments().get(0); assertThat(documentByPriceWithQuery.getId(), equalTo(book1200)); assertThat(documentByPriceWithQuery.get("title"), equalTo("Redis Essentials")); assertThat(documentByPriceWithQuery.get("price"), equalTo("19.99")); assertThat(documentByPriceWithQuery.get("author"), nullValue()); } @Test public void testFtSearchJson() { String indexName = "testIndex"; IndexDefinition indexDefinition = new IndexDefinition(IndexDefinition.Type.JSON) .setPrefixes("books:"); IndexOptions indexOptions = IndexOptions.defaultOptions().setDefinition(indexDefinition); Schema schema = new Schema() .addField(new Schema.Field("$.title", Schema.FieldType.TEXT)) .addField(new Schema.Field("$.price", Schema.FieldType.NUMERIC)); String create = exec(commandObjects.ftCreate(indexName, indexOptions, schema)); assertThat(create, equalTo("OK")); Map hash = new HashMap<>(); hash.put("title", "Redis in Action"); hash.put("price", 17.99); hash.put("author", "John Doe"); String jsonSet = exec(commandObjects.jsonSet("books:1000", Path2.ROOT_PATH, new JSONObject(hash))); assertThat(jsonSet, equalTo("OK")); Map hash2 = new HashMap<>(); hash2.put("title", "Redis Essentials"); hash2.put("price", 19.99); hash2.put("author", "Jane Doe"); String jsonSet2 = exec(commandObjects.jsonSet("books:1200", Path2.ROOT_PATH, new JSONObject(hash2))); assertThat(jsonSet2, equalTo("OK")); SearchResult searchResult = exec(commandObjects.ftSearch(indexName, "Action")); assertThat(searchResult.getTotalResults(), equalTo(1L)); assertThat(searchResult.getDocuments(), hasSize(1)); Document document = searchResult.getDocuments().get(0); assertThat(document.getId(), equalTo("books:1000")); assertThat(document.get("$"), equalTo("{\"title\":\"Redis in Action\",\"price\":17.99,\"author\":\"John Doe\"}")); } @Test public void testFtCreateWithParams() { String indexName = "booksIndex"; SchemaField[] schema = { TextField.of("$.title").as("title"), NumericField.of("$.price").as("price") }; FTCreateParams createParams = FTCreateParams.createParams() .on(IndexDataType.JSON) .addPrefix("books:"); String createResult = exec(commandObjects.ftCreate(indexName, createParams, Arrays.asList(schema))); assertThat(createResult, equalTo("OK")); JSONObject bookRedisInAction = new JSONObject(); bookRedisInAction.put("title", "Redis in Action"); bookRedisInAction.put("price", 17.99); bookRedisInAction.put("author", "John Doe"); String jsonSet = exec(commandObjects.jsonSet("books:1000", Path2.ROOT_PATH, bookRedisInAction)); assertThat(jsonSet, equalTo("OK")); JSONObject bookRedisEssentials = new JSONObject(); bookRedisEssentials.put("title", "Redis Essentials"); bookRedisEssentials.put("price", 19.99); bookRedisEssentials.put("author", "Jane Doe"); String jsonSet2 = exec(commandObjects.jsonSet("books:1200", Path2.ROOT_PATH, bookRedisEssentials)); assertThat(jsonSet2, equalTo("OK")); SearchResult searchResult = exec(commandObjects.ftSearch(indexName, "Action")); assertThat(searchResult.getTotalResults(), equalTo(1L)); assertThat(searchResult.getDocuments(), hasSize(1)); Document document = searchResult.getDocuments().get(0); assertThat(document.getId(), equalTo("books:1000")); Object documentRoot = document.get("$"); assertThat(documentRoot, instanceOf(String.class)); // Unparsed! assertThat(documentRoot, jsonEquals(bookRedisInAction)); } @Test public void testFtAlterWithParams() throws InterruptedException { String indexName = "booksIndex"; List schema = new ArrayList<>(); schema.add(TextField.of("$.title").as("title")); schema.add(NumericField.of("$.price").as("price")); FTCreateParams createParams = FTCreateParams.createParams() .on(IndexDataType.JSON) .addPrefix("books:"); String createResult = exec(commandObjects.ftCreate(indexName, createParams, schema)); assertThat(createResult, equalTo("OK")); JSONObject bookRedisInAction = new JSONObject(); bookRedisInAction.put("title", "Redis in Action"); bookRedisInAction.put("price", 17.99); bookRedisInAction.put("author", "John Doe"); String jsonSet = exec(commandObjects.jsonSet("books:1000", Path2.ROOT_PATH, bookRedisInAction)); assertThat(jsonSet, equalTo("OK")); JSONObject bookRedisEssentials = new JSONObject(); bookRedisEssentials.put("title", "Redis Essentials"); bookRedisEssentials.put("price", 19.99); bookRedisEssentials.put("author", "Jane Doe"); String jsonSet2 = exec(commandObjects.jsonSet("books:1200", Path2.ROOT_PATH, bookRedisEssentials)); assertThat(jsonSet2, equalTo("OK")); SearchResult searchNotInIndex = exec(commandObjects.ftSearch(indexName, "John")); assertThat(searchNotInIndex.getTotalResults(), equalTo(0L)); assertThat(searchNotInIndex.getDocuments(), empty()); List schemaExtension = new ArrayList<>(); schemaExtension.add(TextField.of("$.author").as("author")); String alter = exec(commandObjects.ftAlter(indexName, schemaExtension)); assertThat(alter, equalTo("OK")); Thread.sleep(300); // wait for index to be updated SearchResult searchInIndex = exec(commandObjects.ftSearch(indexName, "John")); assertThat(searchInIndex.getTotalResults(), equalTo(1L)); assertThat(searchInIndex.getDocuments(), hasSize(1)); Document document = searchInIndex.getDocuments().get(0); assertThat(document.getId(), equalTo("books:1000")); Object documentRoot = document.get("$"); assertThat(documentRoot, instanceOf(String.class)); // Unparsed! assertThat(documentRoot, jsonEquals(bookRedisInAction)); } @Test public void testFtExplain() { String indexName = "booksIndex"; IndexDefinition indexDefinition = new IndexDefinition(IndexDefinition.Type.HASH).setPrefixes("books:"); IndexOptions indexOptions = IndexOptions.defaultOptions().setDefinition(indexDefinition); Schema schema = new Schema() .addField(new Schema.Field("title", Schema.FieldType.TEXT)) .addField(new Schema.Field("price", Schema.FieldType.NUMERIC)); String createResult = exec(commandObjects.ftCreate(indexName, indexOptions, schema)); assertThat(createResult, equalTo("OK")); // Add a book to the index String bookId = "books:123"; Map bookFields = new HashMap<>(); bookFields.put("title", "Redis for Dummies"); bookFields.put("price", 29.99); Long hsetResult = exec(commandObjects.hsetObject(bookId, bookFields)); assertThat(hsetResult, equalTo(2L)); Query query = new Query("Redis").returnFields("title", "price"); String explanation = exec(commandObjects.ftExplain(indexName, query)); assertThat(explanation, not(emptyOrNullString())); List explanationCli = exec(commandObjects.ftExplainCLI(indexName, query)); assertThat(explanationCli, not(empty())); } @Test public void testFtAggregate() { String indexName = "booksIndex"; IndexDefinition indexDefinition = new IndexDefinition(IndexDefinition.Type.HASH).setPrefixes("books:"); IndexOptions indexOptions = IndexOptions.defaultOptions().setDefinition(indexDefinition); Schema schema = new Schema() .addField(new Schema.Field("title", Schema.FieldType.TEXT)) .addField(new Schema.Field("price", Schema.FieldType.NUMERIC)) .addField(new Schema.Field("genre", Schema.FieldType.TAG)); String createResult = exec(commandObjects.ftCreate(indexName, indexOptions, schema)); assertThat(createResult, equalTo("OK")); // Add books to the index Map book1Fields = new HashMap<>(); book1Fields.put("title", "Redis for Dummies"); book1Fields.put("price", 20.99); book1Fields.put("genre", "Technology"); String book1Id = "books:101"; exec(commandObjects.hsetObject(book1Id, book1Fields)); Map book2Fields = new HashMap<>(); book2Fields.put("title", "Advanced Redis"); book2Fields.put("price", 25.99); book2Fields.put("genre", "Technology"); String book2Id = "books:102"; exec(commandObjects.hsetObject(book2Id, book2Fields)); // Aggregation: average price of books in the 'Technology' genre AggregationBuilder aggr = new AggregationBuilder() .groupBy("@genre", Reducers.avg("@price").as("avgPrice")) .filter("@genre=='Technology'"); AggregationResult aggregationResult = exec(commandObjects.ftAggregate(indexName, aggr)); assertThat(aggregationResult, notNullValue()); assertThat(aggregationResult.getResults(), hasSize(1)); Map result = aggregationResult.getResults().get(0); assertThat(result, hasEntry("genre", "Technology")); assertThat(result, hasEntry("avgPrice", "23.49")); } @Test public void testSpellCheck() { // Add some terms to an index String indexName = "techArticles"; List schemaFields = Collections.singletonList(TextField.of("$.technology")); exec(commandObjects.ftCreate(indexName, FTCreateParams.createParams().on(IndexDataType.JSON), schemaFields)); exec(commandObjects.jsonSet("articles:02", Path2.ROOT_PATH, new JSONObject().put("technology", "Flutter"))); exec(commandObjects.jsonSet("articles:03", Path2.ROOT_PATH, new JSONObject().put("technology", "Rust"))); exec(commandObjects.jsonSet("articles:04", Path2.ROOT_PATH, new JSONObject().put("technology", "Angular"))); SearchResult searchInIndex = exec(commandObjects.ftSearch(indexName, "Flutter")); assertThat(searchInIndex.getTotalResults(), equalTo(1L)); String query = "Fluter JavaScrit Pyhton Rust"; // Spellcheck based on index only Map> indexOnly = exec(commandObjects.ftSpellCheck(indexName, query)); assertThat(indexOnly.get("fluter"), hasKey(equalToIgnoringCase("Flutter"))); assertThat(indexOnly.get("javascrit"), anEmptyMap()); assertThat(indexOnly.get("pyhton"), anEmptyMap()); // Add more terms to a dictionary String dictionary = "techDict"; Long addResult = exec(commandObjects.ftDictAdd(dictionary, "JavaScript", "Python")); assertThat(addResult, equalTo(2L)); // Spellcheck based on index and dictionary FTSpellCheckParams paramsWithDict = new FTSpellCheckParams().includeTerm(dictionary); Map> indexAndDictionary = exec(commandObjects.ftSpellCheck(indexName, query, paramsWithDict)); assertThat(indexAndDictionary.get("fluter"), hasKey(equalToIgnoringCase("Flutter"))); assertThat(indexAndDictionary.get("javascrit"), hasKey("JavaScript")); assertThat(indexAndDictionary.get("pyhton"), anEmptyMap()); // Increase Levenshtein distance, to allow for misspelled letter FTSpellCheckParams paramsWithDictAndDist = new FTSpellCheckParams().includeTerm(dictionary).distance(2); Map> indexAndDictionaryWithDist = exec(commandObjects.ftSpellCheck(indexName, query, paramsWithDictAndDist)); assertThat(indexAndDictionaryWithDist.get("fluter"), hasKey(equalToIgnoringCase("Flutter"))); assertThat(indexAndDictionaryWithDist.get("javascrit"), hasKey("JavaScript")); assertThat(indexAndDictionaryWithDist.get("pyhton"), hasKey("Python")); } @Test public void testFtDictAddDelAndDump() { String dictionary = "programmingLanguages"; Long addResult = exec(commandObjects.ftDictAdd(dictionary, "Java", "Python", "JavaScript", "Rust")); assertThat(addResult, equalTo(4L)); Set dumpResultAfterAdd = exec(commandObjects.ftDictDump(dictionary)); assertThat(dumpResultAfterAdd, containsInAnyOrder("Java", "Python", "JavaScript", "Rust")); Long delResult = exec(commandObjects.ftDictDel(dictionary, "Rust")); assertThat(delResult, equalTo(1L)); Set dumpResultAfterDel = exec(commandObjects.ftDictDump(dictionary)); assertThat(dumpResultAfterDel, containsInAnyOrder("Java", "Python", "JavaScript")); } @Test public void testFtDictAddDelAndDumpWithSampleKeys() { String index = "index"; // not used actually, but needed for the command String dictionary = "programmingLanguages"; Long addResult = exec(commandObjects.ftDictAddBySampleKey(index, dictionary, "Java", "Python", "JavaScript", "Rust")); assertThat(addResult, equalTo(4L)); Set dumpResultAfterAdd = exec(commandObjects.ftDictDumpBySampleKey(index, dictionary)); assertThat(dumpResultAfterAdd, containsInAnyOrder("Java", "Python", "JavaScript", "Rust")); Long delResult = exec(commandObjects.ftDictDelBySampleKey(index, dictionary, "Rust")); assertThat(delResult, equalTo(1L)); Set dumpResultAfterDel = exec(commandObjects.ftDictDumpBySampleKey(index, dictionary)); assertThat(dumpResultAfterDel, containsInAnyOrder("Java", "Python", "JavaScript")); } @Test public void testFtTags() { String indexName = "booksIndex"; SchemaField[] schema = { TextField.of("$.title"), TagField.of("$.genre").as("genre").separator(',') }; FTCreateParams createParams = FTCreateParams.createParams() .on(IndexDataType.JSON) .addPrefix("books:"); String createResult = exec(commandObjects.ftCreate(indexName, createParams, Arrays.asList(schema))); assertThat(createResult, equalTo("OK")); JSONObject bookDune = new JSONObject(); bookDune.put("title", "Dune"); bookDune.put("genre", "Science Fiction, Fantasy, Adventure"); String jsonSet = exec(commandObjects.jsonSet("books:1000", Path2.ROOT_PATH, bookDune)); assertThat(jsonSet, equalTo("OK")); JSONObject bookTheFoundation = new JSONObject(); bookTheFoundation.put("title", "The Foundation"); bookTheFoundation.put("genre", "Technical, Novel, Essential"); String jsonSet2 = exec(commandObjects.jsonSet("books:1200", Path2.ROOT_PATH, bookTheFoundation)); assertThat(jsonSet2, equalTo("OK")); Set tagVals = exec(commandObjects.ftTagVals(indexName, "genre")); assertThat(tagVals, containsInAnyOrder( "science fiction", "fantasy", "adventure", "technical", "novel", "essential")); SearchResult searchSimple = exec(commandObjects.ftSearch(indexName, "Fantasy")); assertThat(searchSimple.getTotalResults(), equalTo(0L)); assertThat(searchSimple.getDocuments(), empty()); SearchResult searchSpecialSyntax = exec(commandObjects.ftSearch(indexName, "@genre:{ fantasy }")); assertThat(searchSpecialSyntax.getTotalResults(), equalTo(1L)); assertThat(searchSpecialSyntax.getDocuments(), hasSize(1)); Document document = searchSpecialSyntax.getDocuments().get(0); assertThat(document.getId(), equalTo("books:1000")); Object documentRoot = document.get("$"); assertThat(documentRoot, instanceOf(String.class)); // Unparsed! assertThat(documentRoot, jsonEquals(bookDune)); } @Test public void testFtInfo() { String indexName = "booksIndex"; SchemaField[] schema = { TextField.of("$.title"), TagField.of("$.genre").as("genre").separator(',') }; FTCreateParams createParams = FTCreateParams.createParams() .on(IndexDataType.JSON) .addPrefix("books:"); String createResult = exec(commandObjects.ftCreate(indexName, createParams, Arrays.asList(schema))); assertThat(createResult, equalTo("OK")); JSONObject bookDune = new JSONObject(); bookDune.put("title", "Dune"); bookDune.put("genre", "Science Fiction, Fantasy, Adventure"); String jsonSet = exec(commandObjects.jsonSet("books:1000", Path2.ROOT_PATH, bookDune)); assertThat(jsonSet, equalTo("OK")); JSONObject bookTheFoundation = new JSONObject(); bookTheFoundation.put("title", "The Foundation"); bookTheFoundation.put("genre", "Technical, Novel, Essential"); String jsonSet2 = exec(commandObjects.jsonSet("books:1200", Path2.ROOT_PATH, bookTheFoundation)); assertThat(jsonSet2, equalTo("OK")); Map infoResult = exec(commandObjects.ftInfo(indexName)); assertThat(infoResult, hasEntry("index_name", indexName)); } @Test public void testFtSugAddAndGet() { String key = "autocomplete"; // Round 1: single suggestion with weight 2.0 Long sugAdd1 = exec(commandObjects.ftSugAdd(key, "Redis", 2.0)); assertThat(sugAdd1, equalTo(1L)); List suggestionsOneOption = exec(commandObjects.ftSugGet(key, "Re")); assertThat(suggestionsOneOption, contains("Redis")); List suggestionsWithScoresOneOption = exec(commandObjects.ftSugGetWithScores(key, "Re")); assertThat(suggestionsWithScoresOneOption, contains( new Tuple("Redis", 1.0))); // Round 2: two suggestions with weights 2.0 and 1.0 Long sugAdd2 = exec(commandObjects.ftSugAdd(key, "Redux", 1.0)); assertThat(sugAdd2, equalTo(2L)); List suggestionsTwoOptions = exec(commandObjects.ftSugGet(key, "Re")); assertThat(suggestionsTwoOptions, contains("Redis", "Redux")); List suggestionsWithScoresTwoOptions = exec(commandObjects.ftSugGetWithScores(key, "Re")); assertThat(suggestionsWithScoresTwoOptions, contains( new Tuple("Redis", 1.0), new Tuple("Redux", 0.5))); // Round 2: same two suggestions with weights 2.0 and 3.0 Long sugAddIncr = exec(commandObjects.ftSugAddIncr(key, "Redux", 2.0)); assertThat(sugAddIncr, equalTo(2L)); List suggestionsAfterScoreChange = exec(commandObjects.ftSugGet(key, "Re")); assertThat(suggestionsAfterScoreChange, contains("Redux", "Redis")); List suggestionsWithScoresAfterChange = exec(commandObjects.ftSugGetWithScores(key, "Re")); assertThat(suggestionsWithScoresAfterChange, contains( new Tuple("Redux", 1.5), new Tuple("Redis", 1.0))); } } ================================================ FILE: src/test/java/redis/clients/jedis/commands/commandobjects/CommandObjectsServerManagementCommandsTest.java ================================================ package redis.clients.jedis.commands.commandobjects; import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.containsString; import static org.hamcrest.Matchers.equalTo; import static org.hamcrest.Matchers.greaterThan; import static org.hamcrest.Matchers.greaterThanOrEqualTo; import org.junit.jupiter.api.Test; import redis.clients.jedis.CommandObject; import redis.clients.jedis.RedisProtocol; /** * Tests related to Server management commands. */ public class CommandObjectsServerManagementCommandsTest extends CommandObjectsStandaloneTestBase { public CommandObjectsServerManagementCommandsTest(RedisProtocol protocol) { super(protocol); } @Test public void testSlowLogReset() { String reset = exec(commandObjects.slowlogReset()); assertThat(reset, equalTo("OK")); } @Test public void testMemoryUsage() { String key = "key"; int samples = 5; exec(commandObjects.set(key, "value")); CommandObject memoryUsage = commandObjects.memoryUsage(key); assertThat(exec(memoryUsage), greaterThan(0L)); CommandObject memoryUsageWithSamples = commandObjects.memoryUsage(key, samples); assertThat(exec(memoryUsageWithSamples), greaterThan(0L)); CommandObject memoryUsageBinary = commandObjects.memoryUsage(key.getBytes()); assertThat(exec(memoryUsageBinary), greaterThan(0L)); CommandObject memoryUsageBinaryWithSamples = commandObjects.memoryUsage(key.getBytes(), samples); assertThat(exec(memoryUsageBinaryWithSamples), greaterThan(0L)); } @Test public void testObjectRefcount() { String key = "refcountKey"; exec(commandObjects.set(key, "value")); Long refcount = exec(commandObjects.objectRefcount(key)); assertThat(refcount, greaterThanOrEqualTo(1L)); Long refcountBinary = exec(commandObjects.objectRefcount(key.getBytes())); assertThat(refcountBinary, greaterThanOrEqualTo(1L)); } @Test public void testObjectEncoding() { exec(commandObjects.lpush("lst", "Hello, Redis!")); String encoding = exec(commandObjects.objectEncoding("lst")); assertThat(encoding, containsString("list")); byte[] encodingBinary = exec(commandObjects.objectEncoding("lst".getBytes())); assertThat(new String(encodingBinary), containsString("list")); } @Test public void testObjectIdletime() throws InterruptedException { String key = "idleTestString"; String value = "Idle value test"; exec(commandObjects.set(key, value)); // A small delay to simulate idle time Thread.sleep(1000); Long idleTime = exec(commandObjects.objectIdletime(key)); assertThat(idleTime, greaterThan(0L)); Long idleTimeBinary = exec(commandObjects.objectIdletime(key.getBytes())); assertThat(idleTimeBinary, greaterThan(0L)); } } ================================================ FILE: src/test/java/redis/clients/jedis/commands/commandobjects/CommandObjectsSetCommandsTest.java ================================================ package redis.clients.jedis.commands.commandobjects; import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.anyOf; import static org.hamcrest.Matchers.contains; import static org.hamcrest.Matchers.containsInAnyOrder; import static org.hamcrest.Matchers.equalTo; import static org.hamcrest.Matchers.everyItem; import static org.hamcrest.Matchers.hasSize; import static org.hamcrest.Matchers.lessThanOrEqualTo; import static org.hamcrest.Matchers.not; import java.util.List; import java.util.Set; import io.redis.test.annotations.ConditionalOnEnv; import io.redis.test.annotations.SinceRedisVersion; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Tag; import redis.clients.jedis.RedisProtocol; import redis.clients.jedis.params.ScanParams; import redis.clients.jedis.resps.ScanResult; import redis.clients.jedis.util.TestEnvUtil; /** * Tests related to Set commands. */ @Tag("integration") public class CommandObjectsSetCommandsTest extends CommandObjectsStandaloneTestBase { public CommandObjectsSetCommandsTest(RedisProtocol protocol) { super(protocol); } @Test public void testSetCommands() { String key = "testSet"; String member1 = "member1"; String member2 = "member2"; String member3 = "member3"; Long sadd = exec(commandObjects.sadd(key, member1, member2, member3)); assertThat(sadd, equalTo(3L)); Set members = exec(commandObjects.smembers(key)); assertThat(members, containsInAnyOrder(member1, member2, member3)); Long srem = exec(commandObjects.srem(key, member1)); assertThat(srem, equalTo(1L)); Set membersAfterSrem = exec(commandObjects.smembers(key)); assertThat(membersAfterSrem, containsInAnyOrder(member2, member3)); } @Test public void testSetCommandsBinary() { byte[] key = "testSetB".getBytes(); byte[] member1 = "member1".getBytes(); byte[] member2 = "member2".getBytes(); byte[] member3 = "member3".getBytes(); Long sadd = exec(commandObjects.sadd(key, member1, member2, member3)); assertThat(sadd, equalTo(3L)); Set members = exec(commandObjects.smembers(key)); assertThat(members, containsInAnyOrder(member1, member2, member3)); Long srem = exec(commandObjects.srem(key, member1)); assertThat(srem, equalTo(1L)); Set membersAfterSrem = exec(commandObjects.smembers(key)); assertThat(membersAfterSrem, containsInAnyOrder(member2, member3)); } @Test public void testSpop() { String key = "testSetPop"; String member1 = "member1"; String member2 = "member2"; String member3 = "member3"; Long sadd = exec(commandObjects.sadd(key, member1, member2, member3)); assertThat(sadd, equalTo(3L)); String spop = exec(commandObjects.spop(key)); assertThat(spop, anyOf(equalTo(member1), equalTo(member2), equalTo(member3))); Set spopMultiple = exec(commandObjects.spop(key, 2)); assertThat(spopMultiple, hasSize(2)); assertThat(spopMultiple, everyItem(anyOf(equalTo(member1), equalTo(member2), equalTo(member3)))); assertThat(spopMultiple, not(contains(spop))); } @Test public void testSpopBinary() { byte[] bkey = "testSetPopB".getBytes(); byte[] member1 = "member1".getBytes(); byte[] member2 = "member2".getBytes(); byte[] member3 = "member3".getBytes(); Long sadd = exec(commandObjects.sadd(bkey, member1, member2, member3)); assertThat(sadd, equalTo(3L)); byte[] spop = exec(commandObjects.spop(bkey)); assertThat(spop, anyOf(equalTo(member1), equalTo(member2), equalTo(member3))); Set spopMultiple = exec(commandObjects.spop(bkey, 2)); assertThat(spopMultiple, hasSize(2)); assertThat(spopMultiple, everyItem(anyOf(equalTo(member1), equalTo(member2), equalTo(member3)))); assertThat(spopMultiple, not(contains(spop))); } @Test public void testSetMembershipCommands() { String key = "testSetMembership"; String member1 = "member1"; String member2 = "member2"; exec(commandObjects.sadd(key, member1, member2)); Long scard = exec(commandObjects.scard(key)); assertThat(scard, equalTo(2L)); Long scardBinary = exec(commandObjects.scard(key.getBytes())); assertThat(scardBinary, equalTo(2L)); Boolean isMember = exec(commandObjects.sismember(key, member1)); assertThat(isMember, equalTo(true)); Boolean isMemberBinary = exec(commandObjects.sismember(key.getBytes(), member1.getBytes())); assertThat(isMemberBinary, equalTo(true)); List mIsMember = exec(commandObjects.smismember(key, member1, "nonMember")); assertThat(mIsMember, contains(true, false)); List mIsMemberBinary = exec(commandObjects.smismember(key.getBytes(), member1.getBytes(), "nonMember".getBytes())); assertThat(mIsMemberBinary, contains(true, false)); } @Test public void testSrandmemberCommands() { String key = "testSetRandomMember"; String member1 = "member1"; String member2 = "member2"; String member3 = "member3"; exec(commandObjects.sadd(key, member1, member2, member3)); String randomMember = exec(commandObjects.srandmember(key)); assertThat(randomMember, anyOf(equalTo(member1), equalTo(member2), equalTo(member3))); byte[] randomMemberBinary = exec(commandObjects.srandmember(key.getBytes())); assertThat(new String(randomMemberBinary), anyOf(equalTo(member1), equalTo(member2), equalTo(member3))); List randomMembers = exec(commandObjects.srandmember(key, 2)); assertThat(randomMembers, hasSize(2)); assertThat(randomMembers, everyItem(anyOf(equalTo(member1), equalTo(member2), equalTo(member3)))); assertThat(randomMembers, not(contains(randomMember))); List randomMembersBinary = exec(commandObjects.srandmember(key.getBytes(), 2)); assertThat(randomMembersBinary, hasSize(2)); assertThat(randomMembersBinary, everyItem(anyOf(equalTo(member1.getBytes()), equalTo(member2.getBytes()), equalTo(member3.getBytes())))); assertThat(randomMembersBinary, not(contains(randomMemberBinary))); } @Test public void testSscanCommands() { String key = "testSetScan"; String member1 = "member1"; String member2 = "member2"; String member3 = "member3"; exec(commandObjects.sadd(key, member1, member2, member3)); ScanParams params = new ScanParams().count(2); ScanResult scan = exec(commandObjects.sscan(key, ScanParams.SCAN_POINTER_START, params)); assertThat(scan.getResult(), hasSize(lessThanOrEqualTo(3))); assertThat(scan.getResult(), everyItem(anyOf(equalTo(member1), equalTo(member2), equalTo(member3)))); ScanResult scanBinary = exec(commandObjects.sscan(key.getBytes(), ScanParams.SCAN_POINTER_START_BINARY, params)); assertThat(scanBinary.getResult(), hasSize(lessThanOrEqualTo(3))); assertThat(scanBinary.getResult(), everyItem(anyOf(equalTo(member1.getBytes()), equalTo(member2.getBytes()), equalTo(member3.getBytes())))); } @Test @ConditionalOnEnv(value = TestEnvUtil.ENV_REDIS_ENTERPRISE, enabled = false) public void testSdiff() { String key1 = "testSet1"; String key2 = "testSet2"; exec(commandObjects.sadd(key1, "member1", "member2", "member3")); exec(commandObjects.sadd(key2, "member2", "member3", "member4")); Set diff = exec(commandObjects.sdiff(key1, key2)); assertThat(diff, contains("member1")); Set diffBinary = exec(commandObjects.sdiff(key1.getBytes(), key2.getBytes())); assertThat(diffBinary, contains("member1".getBytes())); } @Test @ConditionalOnEnv(value = TestEnvUtil.ENV_REDIS_ENTERPRISE, enabled = false) public void testSdiffstore() { String key1 = "testSet1"; String key2 = "testSet2"; String dstKey = "testSetDiff"; exec(commandObjects.sadd(key1, "member1", "member2", "member3")); exec(commandObjects.sadd(key2, "member2", "member3", "member4")); Long diffStore = exec(commandObjects.sdiffstore(dstKey, key1, key2)); assertThat(diffStore, equalTo(1L)); Set dstSet = exec(commandObjects.smembers(dstKey)); assertThat(dstSet, contains("member1")); } @Test @ConditionalOnEnv(value = TestEnvUtil.ENV_REDIS_ENTERPRISE, enabled = false) public void testSdiffstoreBinary() { byte[] key1 = "testSet1".getBytes(); byte[] key2 = "testSet2".getBytes(); byte[] dstKey = "testSetDiff".getBytes(); exec(commandObjects.sadd(key1, "member1".getBytes(), "member2".getBytes(), "member3".getBytes())); exec(commandObjects.sadd(key2, "member2".getBytes(), "member3".getBytes(), "member4".getBytes())); Long diffStore = exec(commandObjects.sdiffstore(dstKey, key1, key2)); assertThat(diffStore, equalTo(1L)); Set dstSet = exec(commandObjects.smembers(dstKey)); assertThat(dstSet, contains("member1".getBytes())); } @Test @SinceRedisVersion(value = "7.0.0") @ConditionalOnEnv(value = TestEnvUtil.ENV_REDIS_ENTERPRISE, enabled = false) public void testSinterAndSinterCard() { String key1 = "testSetInter1"; String key2 = "testSetInter2"; exec(commandObjects.sadd(key1, "member1", "member2", "member3")); exec(commandObjects.sadd(key2, "member2", "member3", "member4")); Set inter = exec(commandObjects.sinter(key1, key2)); assertThat(inter, containsInAnyOrder("member2", "member3")); Set interBinary = exec(commandObjects.sinter(key1.getBytes(), key2.getBytes())); assertThat(interBinary, containsInAnyOrder("member2".getBytes(), "member3".getBytes())); Long interCard = exec(commandObjects.sintercard(key1, key2)); assertThat(interCard, equalTo(2L)); Long interCardBinary = exec(commandObjects.sintercard(key1.getBytes(), key2.getBytes())); assertThat(interCardBinary, equalTo(2L)); Long interCardLimited = exec(commandObjects.sintercard(1, key1, key2)); assertThat(interCardLimited, equalTo(1L)); Long interCardLimitedBinary = exec(commandObjects.sintercard(1, key1.getBytes(), key2.getBytes())); assertThat(interCardLimitedBinary, equalTo(1L)); } @Test @ConditionalOnEnv(value = TestEnvUtil.ENV_REDIS_ENTERPRISE, enabled = false) public void testSinterstore() { String key1 = "testSetInter1"; String key2 = "testSetInter2"; String dstKey = "testSetInterResult"; exec(commandObjects.sadd(key1, "member1", "member2", "member3")); exec(commandObjects.sadd(key2, "member2", "member3", "member4")); Long interStore = exec(commandObjects.sinterstore(dstKey, key1, key2)); assertThat(interStore, equalTo(2L)); Set dstSet = exec(commandObjects.smembers(dstKey)); assertThat(dstSet, containsInAnyOrder("member2", "member3")); } @Test @ConditionalOnEnv(value = TestEnvUtil.ENV_REDIS_ENTERPRISE, enabled = false) public void testSinterstoreBinary() { byte[] key1 = "testSetInter1B".getBytes(); byte[] key2 = "testSetInter2B".getBytes(); byte[] dstKey = "testSetInterResultB".getBytes(); exec(commandObjects.sadd(key1, "member1".getBytes(), "member2".getBytes(), "member3".getBytes())); exec(commandObjects.sadd(key2, "member2".getBytes(), "member3".getBytes(), "member4".getBytes())); Long interStore = exec(commandObjects.sinterstore(dstKey, key1, key2)); assertThat(interStore, equalTo(2L)); Set dstSet = exec(commandObjects.smembers(dstKey)); assertThat(dstSet, containsInAnyOrder("member2".getBytes(), "member3".getBytes())); } @Test @ConditionalOnEnv(value = TestEnvUtil.ENV_REDIS_ENTERPRISE, enabled = false) public void testSunion() { String key1 = "testSetUnion1"; String key2 = "testSetUnion2"; exec(commandObjects.sadd(key1, "member1", "member2", "member3")); exec(commandObjects.sadd(key2, "member3", "member4", "member5")); Set unionResult = exec(commandObjects.sunion(key1, key2)); assertThat(unionResult, containsInAnyOrder( "member1", "member2", "member3", "member4", "member5")); Set bunionResult = exec(commandObjects.sunion(key1.getBytes(), key2.getBytes())); assertThat(bunionResult, containsInAnyOrder( "member1".getBytes(), "member2".getBytes(), "member3".getBytes(), "member4".getBytes(), "member5".getBytes())); } @Test @ConditionalOnEnv(value = TestEnvUtil.ENV_REDIS_ENTERPRISE, enabled = false) public void testSunionstore() { String key1 = "testSetUnion1"; String key2 = "testSetUnion2"; String dstKey = "testSetUnionResult"; exec(commandObjects.sadd(key1, "member1", "member2", "member3")); exec(commandObjects.sadd(key2, "member3", "member4", "member5")); Long unionStore = exec(commandObjects.sunionstore(dstKey, key1, key2)); assertThat(unionStore, equalTo(5L)); Set dstSet = exec(commandObjects.smembers(dstKey)); assertThat(dstSet, containsInAnyOrder( "member1", "member2", "member3", "member4", "member5")); } @Test @ConditionalOnEnv(value = TestEnvUtil.ENV_REDIS_ENTERPRISE, enabled = false) public void testSunionstoreBinary() { byte[] key1 = "testSetUnion1".getBytes(); byte[] key2 = "testSetUnion2".getBytes(); byte[] dstKey = "testSetUnionResult".getBytes(); exec(commandObjects.sadd(key1, "member1".getBytes(), "member2".getBytes(), "member3".getBytes())); exec(commandObjects.sadd(key2, "member3".getBytes(), "member4".getBytes(), "member5".getBytes())); Long unionStore = exec(commandObjects.sunionstore(dstKey, key1, key2)); assertThat(unionStore, equalTo(5L)); Set dstSet = exec(commandObjects.smembers(dstKey)); assertThat(dstSet, containsInAnyOrder( "member1".getBytes(), "member2".getBytes(), "member3".getBytes(), "member4".getBytes(), "member5".getBytes())); } @Test @ConditionalOnEnv(value = TestEnvUtil.ENV_REDIS_ENTERPRISE, enabled = false) public void testSmove() { String srcKey = "testSetSrc"; String dstKey = "testSetDst"; String member = "memberToMove"; exec(commandObjects.sadd(srcKey, member)); Long smove = exec(commandObjects.smove(srcKey, dstKey, member)); assertThat(smove, equalTo(1L)); Set dstSet = exec(commandObjects.smembers(dstKey)); assertThat(dstSet, contains(member)); } @Test @ConditionalOnEnv(value = TestEnvUtil.ENV_REDIS_ENTERPRISE, enabled = false) public void testSmoveBinary() { byte[] srcKey = "testSetSrc".getBytes(); byte[] dstKey = "testSetDst".getBytes(); byte[] member = "memberToMove".getBytes(); exec(commandObjects.sadd(srcKey, member)); Long smove = exec(commandObjects.smove(srcKey, dstKey, member)); assertThat(smove, equalTo(1L)); Set dstSet = exec(commandObjects.smembers(dstKey)); assertThat(dstSet, contains(member)); } } ================================================ FILE: src/test/java/redis/clients/jedis/commands/commandobjects/CommandObjectsSortedSetCommandsTest.java ================================================ package redis.clients.jedis.commands.commandobjects; import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.anyOf; import static org.hamcrest.Matchers.closeTo; import static org.hamcrest.Matchers.contains; import static org.hamcrest.Matchers.containsInAnyOrder; import static org.hamcrest.Matchers.either; import static org.hamcrest.Matchers.empty; import static org.hamcrest.Matchers.equalTo; import static org.hamcrest.Matchers.hasItem; import static org.hamcrest.Matchers.hasItems; import static org.hamcrest.Matchers.hasSize; import static org.hamcrest.Matchers.notNullValue; import java.util.HashMap; import java.util.List; import java.util.Map; import io.redis.test.annotations.ConditionalOnEnv; import io.redis.test.annotations.SinceRedisVersion; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Tag; import redis.clients.jedis.RedisProtocol; import redis.clients.jedis.args.SortedSetOption; import redis.clients.jedis.params.ScanParams; import redis.clients.jedis.params.ZAddParams; import redis.clients.jedis.params.ZIncrByParams; import redis.clients.jedis.params.ZParams; import redis.clients.jedis.params.ZRangeParams; import redis.clients.jedis.resps.ScanResult; import redis.clients.jedis.resps.Tuple; import redis.clients.jedis.util.KeyValue; import redis.clients.jedis.util.TestEnvUtil; /** * Tests related to Sorted set commands. */ @Tag("integration") public class CommandObjectsSortedSetCommandsTest extends CommandObjectsStandaloneTestBase { public CommandObjectsSortedSetCommandsTest(RedisProtocol protocol) { super(protocol); } @Test public void testZaddAndZcard() { String key = "zset"; String member = "member1"; double score = 1.0; Map scoreMembers = new HashMap<>(); scoreMembers.put("member2", 2.0); scoreMembers.put("member3", 3.0); ZAddParams params = ZAddParams.zAddParams().nx(); Long zadd = exec(commandObjects.zadd(key, score, member)); assertThat(zadd, equalTo(1L)); Long zaddParams = exec(commandObjects.zadd(key, score, member, params)); assertThat(zaddParams, equalTo(0L)); Long zaddMultiple = exec(commandObjects.zadd(key, scoreMembers)); assertThat(zaddMultiple, equalTo(2L)); Long zaddMultipleParams = exec(commandObjects.zadd(key, scoreMembers, params)); assertThat(zaddMultipleParams, equalTo(0L)); Long zcard = exec(commandObjects.zcard(key)); assertThat(zcard, equalTo(3L)); } @Test public void testZaddAndZcardBinary() { byte[] key = "zset".getBytes(); byte[] member = "member1".getBytes(); double score = 1.0; Map binaryScoreMembers = new HashMap<>(); binaryScoreMembers.put("member2".getBytes(), 2.0); binaryScoreMembers.put("member3".getBytes(), 3.0); ZAddParams params = ZAddParams.zAddParams().nx(); Long zadd = exec(commandObjects.zadd(key, score, member)); assertThat(zadd, equalTo(1L)); Long zaddParams = exec(commandObjects.zadd(key, score, member, params)); assertThat(zaddParams, equalTo(0L)); Long zaddMultiple = exec(commandObjects.zadd(key, binaryScoreMembers)); assertThat(zaddMultiple, equalTo(2L)); Long zaddMultipleParams = exec(commandObjects.zadd(key, binaryScoreMembers, params)); assertThat(zaddMultipleParams, equalTo(0L)); Long zcard = exec(commandObjects.zcard(key)); assertThat(zcard, equalTo(3L)); } @Test public void testZIncrAndZincrBy() { String key = "zset"; String member = "member"; double initialScore = 1.0; double increment = 2.0; ZAddParams zAddParams = ZAddParams.zAddParams().xx(); ZIncrByParams zIncrByParams = ZIncrByParams.zIncrByParams().xx(); Long zadd = exec(commandObjects.zadd(key, initialScore, member)); assertThat(zadd, equalTo(1L)); Double zaddIncr = exec(commandObjects.zaddIncr(key, increment, member, zAddParams)); assertThat(zaddIncr, closeTo(initialScore + increment, 0.001)); Double zscoreAfterZaddincr = exec(commandObjects.zscore(key, member)); assertThat(zscoreAfterZaddincr, closeTo(initialScore + increment, 0.001)); Double zincrBy = exec(commandObjects.zincrby(key, increment, member)); assertThat(zincrBy, closeTo(initialScore + increment * 2, 0.001)); Double zscoreAfterZincrBy = exec(commandObjects.zscore(key, member)); assertThat(zscoreAfterZincrBy, closeTo(initialScore + increment * 2, 0.001)); Double zincrByParams = exec(commandObjects.zincrby(key, increment, member, zIncrByParams)); assertThat(zincrByParams, closeTo(initialScore + increment * 3, 0.001)); Double zscoreAfterZincrByParams = exec(commandObjects.zscore(key, member)); assertThat(zscoreAfterZincrByParams, closeTo(initialScore + increment * 3, 0.001)); } @Test public void testZIncrAndZincrByBinary() { byte[] key = "zset".getBytes(); byte[] member = "member".getBytes(); double initialScore = 1.0; double increment = 2.0; ZAddParams zAddParams = ZAddParams.zAddParams().xx(); ZIncrByParams zIncrByParams = ZIncrByParams.zIncrByParams().xx(); Long zadd = exec(commandObjects.zadd(key, initialScore, member)); assertThat(zadd, equalTo(1L)); Double zaddIncr = exec(commandObjects.zaddIncr(key, increment, member, zAddParams)); assertThat(zaddIncr, closeTo(initialScore + increment, 0.001)); Double zscoreAfterZaddIncr = exec(commandObjects.zscore(key, member)); assertThat(zscoreAfterZaddIncr, closeTo(initialScore + increment, 0.001)); Double zincrBy = exec(commandObjects.zincrby(key, increment, member)); assertThat(zincrBy, closeTo(initialScore + increment * 2, 0.001)); Double zscoreAfterZincrBy = exec(commandObjects.zscore(key, member)); assertThat(zscoreAfterZincrBy, closeTo(initialScore + increment * 2, 0.001)); Double zincrByParams = exec(commandObjects.zincrby(key, increment, member, zIncrByParams)); assertThat(zincrByParams, closeTo(initialScore + increment * 3, 0.001)); Double zscoreAfterZincrByParams = exec(commandObjects.zscore(key, member)); assertThat(zscoreAfterZincrByParams, closeTo(initialScore + increment * 3, 0.001)); } @Test public void testZrem() { String key = "zset"; String member1 = "one"; String member2 = "two"; double score1 = 1.0; double score2 = 2.0; exec(commandObjects.zadd(key, score1, member1)); exec(commandObjects.zadd(key, score2, member2)); List zrangeBefore = exec(commandObjects.zrange(key, 0, -1)); assertThat(zrangeBefore, containsInAnyOrder(member1, member2)); Long removedCount = exec(commandObjects.zrem(key, member1)); assertThat(removedCount, equalTo(1L)); List zrangeAfter = exec(commandObjects.zrange(key, 0, -1)); assertThat(zrangeAfter, containsInAnyOrder(member2)); } @Test public void testZremBinary() { byte[] key = "zset".getBytes(); byte[] member1 = "one".getBytes(); byte[] member2 = "two".getBytes(); double score1 = 1.0; double score2 = 2.0; exec(commandObjects.zadd(key, score1, member1)); exec(commandObjects.zadd(key, score2, member2)); List zrangeBefore = exec(commandObjects.zrange(key, 0, -1)); assertThat(zrangeBefore, containsInAnyOrder(member1, member2)); Long removedCount = exec(commandObjects.zrem(key, member1)); assertThat(removedCount, equalTo(1L)); List zrangeAfter = exec(commandObjects.zrange(key, 0, -1)); assertThat(zrangeAfter, containsInAnyOrder(member2)); } @Test public void testZrandmember() { String key = "zset"; String member1 = "one"; String member2 = "two"; double score1 = 1.0; double score2 = 2.0; exec(commandObjects.zadd(key, score1, member1)); exec(commandObjects.zadd(key, score2, member2)); String randomMember = exec(commandObjects.zrandmember(key)); assertThat(randomMember, anyOf(equalTo(member1), equalTo(member2))); byte[] randomMemberBinary = exec(commandObjects.zrandmember(key.getBytes())); assertThat(randomMemberBinary, anyOf(equalTo(member1.getBytes()), equalTo(member2.getBytes()))); List randomMembers = exec(commandObjects.zrandmember(key, 2)); assertThat(randomMembers, containsInAnyOrder(member1, member2)); List randomMembersBinary = exec(commandObjects.zrandmember(key.getBytes(), 2)); assertThat(randomMembersBinary.get(0), anyOf(equalTo(member1.getBytes()), equalTo(member2.getBytes()))); assertThat(randomMembersBinary.get(1), anyOf(equalTo(member1.getBytes()), equalTo(member2.getBytes()))); } @Test public void testZrandmemberWithScores() { String key = "zset"; String member1 = "one"; String member2 = "two"; double score1 = 1.0; double score2 = 2.0; exec(commandObjects.zadd(key, score1, member1)); exec(commandObjects.zadd(key, score2, member2)); List randomMembersWithScores = exec(commandObjects.zrandmemberWithScores(key, 2)); assertThat(randomMembersWithScores, hasSize(2)); assertThat(randomMembersWithScores, containsInAnyOrder(new Tuple(member1, score1), new Tuple(member2, score2))); List randomMembersWithScoresBinary = exec(commandObjects.zrandmemberWithScores(key.getBytes(), 2)); assertThat(randomMembersWithScoresBinary, hasSize(2)); assertThat(randomMembersWithScoresBinary.get(0).getBinaryElement(), anyOf(equalTo(member1.getBytes()), equalTo(member2.getBytes()))); assertThat(randomMembersWithScoresBinary.get(0).getScore(), anyOf(equalTo(score1), equalTo(score2))); assertThat(randomMembersWithScoresBinary.get(1).getBinaryElement(), anyOf(equalTo(member1.getBytes()), equalTo(member2.getBytes()))); assertThat(randomMembersWithScoresBinary.get(1).getScore(), anyOf(equalTo(score1), equalTo(score2))); } @Test public void testZscore() { String key = "zset"; String member1 = "one"; double score1 = 1.0; exec(commandObjects.zadd(key, score1, member1)); Double score = exec(commandObjects.zscore(key, member1)); assertThat(score, equalTo(score1)); Double scoreBinary = exec(commandObjects.zscore(key.getBytes(), member1.getBytes())); assertThat(scoreBinary, equalTo(score1)); } @Test public void testZmscore() { String key = "zset"; String member1 = "one"; String member2 = "two"; double score1 = 1.0; double score2 = 2.0; exec(commandObjects.zadd(key, score1, member1)); exec(commandObjects.zadd(key, score2, member2)); List scores = exec(commandObjects.zmscore(key, member1, member2)); assertThat(scores, contains(score1, score2)); List scoresBinary = exec(commandObjects.zmscore(key.getBytes(), member1.getBytes(), member2.getBytes())); assertThat(scoresBinary, contains(score1, score2)); } @Test public void testZrankAndZrevrank() { String key = "zset"; String member1 = "one"; String member2 = "two"; double score1 = 1.0; double score2 = 2.0; exec(commandObjects.zadd(key, score1, member1)); exec(commandObjects.zadd(key, score2, member2)); Long rankMember1 = exec(commandObjects.zrank(key, member1)); assertThat(rankMember1, equalTo(0L)); Long rankMember2 = exec(commandObjects.zrank(key, member2)); assertThat(rankMember2, equalTo(1L)); Long rankMember1Binary = exec(commandObjects.zrank(key.getBytes(), member1.getBytes())); assertThat(rankMember1Binary, equalTo(0L)); Long rankMember2Binary = exec(commandObjects.zrank(key.getBytes(), member2.getBytes())); assertThat(rankMember2Binary, equalTo(1L)); Long revRankMember1 = exec(commandObjects.zrevrank(key, member1)); assertThat(revRankMember1, equalTo(1L)); Long revRankMember2 = exec(commandObjects.zrevrank(key, member2)); assertThat(revRankMember2, equalTo(0L)); Long revRankMember1Binary = exec(commandObjects.zrevrank(key.getBytes(), member1.getBytes())); assertThat(revRankMember1Binary, equalTo(1L)); Long revRankMember2Binary = exec(commandObjects.zrevrank(key.getBytes(), member2.getBytes())); assertThat(revRankMember2Binary, equalTo(0L)); } @Test @SinceRedisVersion(value = "7.2.0") public void testZrankWithScoreAndZrevrankWithScore() { String key = "zset"; String member1 = "one"; String member2 = "two"; double score1 = 1.0; double score2 = 2.0; exec(commandObjects.zadd(key, score1, member1)); exec(commandObjects.zadd(key, score2, member2)); KeyValue rankWithScoreMember1 = exec(commandObjects.zrankWithScore(key, member1)); assertThat(rankWithScoreMember1.getKey(), equalTo(0L)); assertThat(rankWithScoreMember1.getValue(), equalTo(score1)); KeyValue rankWithScoreMember2 = exec(commandObjects.zrankWithScore(key, member2)); assertThat(rankWithScoreMember2.getKey(), equalTo(1L)); assertThat(rankWithScoreMember2.getValue(), equalTo(score2)); KeyValue rankWithScoreMember1Binary = exec(commandObjects.zrankWithScore(key.getBytes(), member1.getBytes())); assertThat(rankWithScoreMember1Binary.getKey(), equalTo(0L)); assertThat(rankWithScoreMember1Binary.getValue(), equalTo(score1)); KeyValue rankWithScoreMember2Binary = exec(commandObjects.zrankWithScore(key.getBytes(), member2.getBytes())); assertThat(rankWithScoreMember2Binary.getKey(), equalTo(1L)); assertThat(rankWithScoreMember2Binary.getValue(), equalTo(score2)); KeyValue revRankWithScoreMember1 = exec(commandObjects.zrevrankWithScore(key, member1)); assertThat(revRankWithScoreMember1.getKey(), equalTo(1L)); assertThat(revRankWithScoreMember1.getValue(), equalTo(score1)); KeyValue revRankWithScoreMember2 = exec(commandObjects.zrevrankWithScore(key, member2)); assertThat(revRankWithScoreMember2.getKey(), equalTo(0L)); assertThat(revRankWithScoreMember2.getValue(), equalTo(score2)); KeyValue revRankWithScoreMember1Binary = exec(commandObjects.zrevrankWithScore(key.getBytes(), member1.getBytes())); assertThat(revRankWithScoreMember1Binary.getKey(), equalTo(1L)); assertThat(revRankWithScoreMember1Binary.getValue(), equalTo(score1)); KeyValue revRankWithScoreMember2Binary = exec(commandObjects.zrevrankWithScore(key.getBytes(), member2.getBytes())); assertThat(revRankWithScoreMember2Binary.getKey(), equalTo(0L)); assertThat(revRankWithScoreMember2Binary.getValue(), equalTo(score2)); } @Test public void testZpopmax() { String key = "zset"; String member1 = "one"; String member2 = "two"; double score1 = 1.0; double score2 = 2.0; exec(commandObjects.zadd(key, score1, member1)); exec(commandObjects.zadd(key, score2, member2)); Tuple poppedMax = exec(commandObjects.zpopmax(key)); assertThat(poppedMax.getElement(), equalTo(member2)); assertThat(poppedMax.getScore(), equalTo(score2)); List poppedMaxMultiple = exec(commandObjects.zpopmax(key, 2)); assertThat(poppedMaxMultiple, hasSize(1)); // Since we already popped the max, only one remains assertThat(poppedMaxMultiple.get(0).getElement(), equalTo(member1)); assertThat(poppedMaxMultiple.get(0).getScore(), equalTo(score1)); } @Test public void testZpopmaxBinary() { byte[] key = "zset".getBytes(); String member1 = "one"; String member2 = "two"; double score1 = 1.0; double score2 = 2.0; exec(commandObjects.zadd(key, score1, member1.getBytes())); exec(commandObjects.zadd(key, score2, member2.getBytes())); Tuple poppedMaxBinary = exec(commandObjects.zpopmax(key)); assertThat(poppedMaxBinary.getBinaryElement(), equalTo(member2.getBytes())); assertThat(poppedMaxBinary.getScore(), equalTo(score2)); List poppedMaxMultipleBinary = exec(commandObjects.zpopmax(key, 2)); assertThat(poppedMaxMultipleBinary, hasSize(1)); // Since we already popped the max, only one remains assertThat(poppedMaxMultipleBinary.get(0).getBinaryElement(), equalTo(member1.getBytes())); assertThat(poppedMaxMultipleBinary.get(0).getScore(), equalTo(score1)); } @Test public void testZpopmin() { String key = "zset"; String member1 = "one"; String member2 = "two"; double score1 = 1.0; double score2 = 2.0; exec(commandObjects.zadd(key, score1, member1)); exec(commandObjects.zadd(key, score2, member2)); Tuple poppedMin = exec(commandObjects.zpopmin(key)); assertThat(poppedMin.getElement(), equalTo(member1)); assertThat(poppedMin.getScore(), equalTo(score1)); List poppedMinMultiple = exec(commandObjects.zpopmin(key, 2)); assertThat(poppedMinMultiple, hasSize(1)); // Since we already popped the min, only one remains assertThat(poppedMinMultiple.get(0).getElement(), equalTo(member2)); assertThat(poppedMinMultiple.get(0).getScore(), equalTo(score2)); } @Test public void testZpopminBinary() { byte[] key = "zset".getBytes(); String member1 = "one"; String member2 = "two"; double score1 = 1.0; double score2 = 2.0; exec(commandObjects.zadd(key, score1, member1.getBytes())); exec(commandObjects.zadd(key, score2, member2.getBytes())); Tuple poppedMinBinary = exec(commandObjects.zpopmin(key)); assertThat(poppedMinBinary.getBinaryElement(), equalTo(member1.getBytes())); assertThat(poppedMinBinary.getScore(), equalTo(score1)); List poppedMinMultipleBinary = exec(commandObjects.zpopmin(key, 2)); assertThat(poppedMinMultipleBinary, hasSize(1)); // Since we already popped the min, only one remains assertThat(poppedMinMultipleBinary.get(0).getBinaryElement(), equalTo(member2.getBytes())); assertThat(poppedMinMultipleBinary.get(0).getScore(), equalTo(score2)); } @Test @ConditionalOnEnv(value = TestEnvUtil.ENV_REDIS_ENTERPRISE, enabled = false) public void testBzpopmaxAndBzpopmin() { String key1 = "zset1"; String key2 = "zset2"; String member1 = "one"; String member2 = "two"; double score1 = 1.0; double score2 = 2.0; double timeout = 2.0; // 2 seconds timeout for blocking operations exec(commandObjects.zadd(key1, score1, member1)); exec(commandObjects.zadd(key2, score2, member2)); KeyValue poppedMax = exec(commandObjects.bzpopmax(timeout, key1, key2)); assertThat(poppedMax.getKey(), anyOf(equalTo(key1), equalTo(key2))); assertThat(poppedMax.getValue().getScore(), anyOf(equalTo(score1), equalTo(score2))); KeyValue poppedMin = exec(commandObjects.bzpopmin(timeout, key1, key2)); assertThat(poppedMin.getKey(), anyOf(equalTo(key1), equalTo(key2))); assertThat(poppedMin.getValue().getScore(), anyOf(equalTo(score1), equalTo(score2))); } @Test @ConditionalOnEnv(value = TestEnvUtil.ENV_REDIS_ENTERPRISE, enabled = false) public void testBzpopmaxAndBzpopminBinary() { byte[] key1 = "zset1".getBytes(); byte[] key2 = "zset2".getBytes(); String member1 = "one"; String member2 = "two"; double score1 = 1.0; double score2 = 2.0; double timeout = 2.0; // 2 seconds timeout for blocking operations exec(commandObjects.zadd(key1, score1, member1.getBytes())); exec(commandObjects.zadd(key2, score2, member2.getBytes())); KeyValue poppedMaxBinary = exec(commandObjects.bzpopmax(timeout, key1, key2)); assertThat(poppedMaxBinary.getKey(), anyOf(equalTo(key1), equalTo(key2))); assertThat(poppedMaxBinary.getValue().getScore(), anyOf(equalTo(score1), equalTo(score2))); KeyValue poppedMinBinary = exec(commandObjects.bzpopmin(timeout, key1, key2)); assertThat(poppedMinBinary.getKey(), anyOf(equalTo(key1), equalTo(key2))); assertThat(poppedMinBinary.getValue().getScore(), anyOf(equalTo(score1), equalTo(score2))); } @Test public void testZcount() { String key = "zset"; String member1 = "one"; String member2 = "two"; String member3 = "three"; double score1 = 1.0; double score2 = 2.0; double score3 = 3.0; exec(commandObjects.zadd(key, score1, member1)); exec(commandObjects.zadd(key, score2, member2)); exec(commandObjects.zadd(key, score3, member3)); Long countInNumericRange = exec(commandObjects.zcount(key, 1.5, 2.5)); assertThat(countInNumericRange, equalTo(1L)); Long countInStringRange = exec(commandObjects.zcount(key, "(1", "3")); assertThat(countInStringRange, equalTo(2L)); Long countInNumericRangeBinary = exec(commandObjects.zcount(key.getBytes(), 1.5, 2.5)); assertThat(countInNumericRangeBinary, equalTo(1L)); Long countInBinaryRange = exec(commandObjects.zcount(key.getBytes(), "(1".getBytes(), "3".getBytes())); assertThat(countInBinaryRange, equalTo(2L)); } @Test public void testZrange() { String key = "zset"; String member1 = "one"; String member2 = "two"; double score1 = 1.0; double score2 = 2.0; exec(commandObjects.zadd(key, score1, member1)); exec(commandObjects.zadd(key, score2, member2)); List range = exec(commandObjects.zrange(key, 0, -1)); assertThat(range, contains(member1, member2)); List rangeBinary = exec(commandObjects.zrange(key.getBytes(), 0, -1)); assertThat(rangeBinary, contains(member1.getBytes(), member2.getBytes())); ZRangeParams zRangeParams = ZRangeParams.zrangeParams(0, -1); List rangeWithParams = exec(commandObjects.zrange(key, zRangeParams)); assertThat(rangeWithParams, hasItems(member1, member2)); List rangeWithParamsBinary = exec(commandObjects.zrange(key.getBytes(), zRangeParams)); assertThat(rangeWithParamsBinary.get(0), equalTo(member1.getBytes())); assertThat(rangeWithParamsBinary.get(1), equalTo(member2.getBytes())); } @Test public void testZrangeWithScores() { String key = "zset"; String member1 = "one"; String member2 = "two"; double score1 = 1.0; double score2 = 2.0; exec(commandObjects.zadd(key, score1, member1)); exec(commandObjects.zadd(key, score2, member2)); List rangeWithScores = exec(commandObjects.zrangeWithScores(key, 0, -1)); assertThat(rangeWithScores, hasSize(2)); assertThat(rangeWithScores.get(0).getElement(), equalTo(member1)); assertThat(rangeWithScores.get(0).getScore(), equalTo(score1)); assertThat(rangeWithScores.get(1).getElement(), equalTo(member2)); assertThat(rangeWithScores.get(1).getScore(), equalTo(score2)); List rangeWithScoresBinary = exec(commandObjects.zrangeWithScores(key.getBytes(), 0, -1)); assertThat(rangeWithScoresBinary, hasSize(2)); assertThat(rangeWithScoresBinary.get(0).getBinaryElement(), equalTo(member1.getBytes())); assertThat(rangeWithScoresBinary.get(0).getScore(), equalTo(score1)); assertThat(rangeWithScoresBinary.get(1).getBinaryElement(), equalTo(member2.getBytes())); assertThat(rangeWithScoresBinary.get(1).getScore(), equalTo(score2)); ZRangeParams zRangeParams = ZRangeParams.zrangeParams(0, -1); List rangeWithScoresParams = exec(commandObjects.zrangeWithScores(key, zRangeParams)); assertThat(rangeWithScoresParams, hasSize(2)); assertThat(rangeWithScoresParams.get(0).getElement(), equalTo(member1)); assertThat(rangeWithScoresParams.get(0).getScore(), equalTo(score1)); assertThat(rangeWithScoresParams.get(1).getElement(), equalTo(member2)); assertThat(rangeWithScoresParams.get(1).getScore(), equalTo(score2)); List rangeWithScoresParamsBinary = exec(commandObjects.zrangeWithScores(key.getBytes(), zRangeParams)); assertThat(rangeWithScoresParamsBinary, hasSize(2)); assertThat(rangeWithScoresParamsBinary.get(0).getBinaryElement(), equalTo(member1.getBytes())); assertThat(rangeWithScoresParamsBinary.get(0).getScore(), equalTo(score1)); assertThat(rangeWithScoresParamsBinary.get(1).getBinaryElement(), equalTo(member2.getBytes())); assertThat(rangeWithScoresParamsBinary.get(1).getScore(), equalTo(score2)); } @Test @ConditionalOnEnv(value = TestEnvUtil.ENV_REDIS_ENTERPRISE, enabled = false) public void testZrangestore() { String srcKey = "zsetSrc"; String destKey = "zsetDest"; String member1 = "one"; String member2 = "two"; double score1 = 1.0; double score2 = 2.0; exec(commandObjects.zadd(srcKey, score1, member1)); exec(commandObjects.zadd(srcKey, score2, member2)); ZRangeParams zRangeParams = ZRangeParams.zrangeByScoreParams(score1, score2); Long zrangeStore = exec(commandObjects.zrangestore(destKey, srcKey, zRangeParams)); assertThat(zrangeStore, equalTo(2L)); List zrangeWithScores = exec(commandObjects.zrangeWithScores(destKey, 0, -1)); assertThat(zrangeWithScores, hasSize(2)); assertThat(zrangeWithScores.get(0).getElement(), equalTo(member1)); assertThat(zrangeWithScores.get(1).getElement(), equalTo(member2)); } @Test @ConditionalOnEnv(value = TestEnvUtil.ENV_REDIS_ENTERPRISE, enabled = false) public void testZrangestoreBinary() { byte[] srcKey = "zsetSrcB".getBytes(); byte[] destKey = "zsetDestB".getBytes(); String member1 = "one"; String member2 = "two"; double score1 = 1.0; double score2 = 2.0; exec(commandObjects.zadd(srcKey, score1, member1.getBytes())); exec(commandObjects.zadd(srcKey, score2, member2.getBytes())); ZRangeParams zRangeParams = ZRangeParams.zrangeByScoreParams(score1, score2); Long zrangeStore = exec(commandObjects.zrangestore(destKey, srcKey, zRangeParams)); assertThat(zrangeStore, equalTo(2L)); List zrangeWithScores = exec(commandObjects.zrangeWithScores(destKey, 0, -1)); assertThat(zrangeWithScores, hasSize(2)); assertThat(zrangeWithScores.get(0).getBinaryElement(), equalTo(member1.getBytes())); assertThat(zrangeWithScores.get(1).getBinaryElement(), equalTo(member2.getBytes())); } @Test public void testZrevrange() { String key = "zset"; String member1 = "one"; String member2 = "two"; double score1 = 1.0; double score2 = 2.0; exec(commandObjects.zadd(key, score1, member1)); exec(commandObjects.zadd(key, score2, member2)); List revRange = exec(commandObjects.zrevrange(key, 0, -1)); assertThat(revRange, contains(member2, member1)); List revRangeBinary = exec(commandObjects.zrevrange(key.getBytes(), 0, -1)); assertThat(revRangeBinary, contains(member2.getBytes(), member1.getBytes())); } @Test public void testZrevrangeWithScores() { String key = "zset"; String member1 = "one"; String member2 = "two"; double score1 = 1.0; double score2 = 2.0; exec(commandObjects.zadd(key, score1, member1)); exec(commandObjects.zadd(key, score2, member2)); List revRangeWithScores = exec(commandObjects.zrevrangeWithScores(key, 0, -1)); assertThat(revRangeWithScores, hasSize(2)); assertThat(revRangeWithScores.get(0).getElement(), equalTo(member2)); assertThat(revRangeWithScores.get(0).getScore(), equalTo(score2)); assertThat(revRangeWithScores.get(1).getElement(), equalTo(member1)); assertThat(revRangeWithScores.get(1).getScore(), equalTo(score1)); List revRangeWithScoresBinary = exec(commandObjects.zrevrangeWithScores(key.getBytes(), 0, -1)); assertThat(revRangeWithScoresBinary, hasSize(2)); assertThat(revRangeWithScoresBinary.get(0).getBinaryElement(), equalTo(member2.getBytes())); assertThat(revRangeWithScoresBinary.get(0).getScore(), equalTo(score2)); assertThat(revRangeWithScoresBinary.get(1).getBinaryElement(), equalTo(member1.getBytes())); assertThat(revRangeWithScoresBinary.get(1).getScore(), equalTo(score1)); } @Test public void testZrangeByScore() { String key = "zset"; double min = 1.0; double max = 10.0; String smin = "1"; String smax = "10"; byte[] bmin = "1.0".getBytes(); byte[] bmax = "10.0".getBytes(); int offset = 0; int count = 1; exec(commandObjects.zadd(key, 1, "one")); exec(commandObjects.zadd(key, 2, "two")); exec(commandObjects.zadd(key, 3, "three")); exec(commandObjects.zadd(key, 13, "four")); List numericRange = exec(commandObjects.zrangeByScore(key, min, max)); assertThat(numericRange, contains("one", "two", "three")); List stringRange = exec(commandObjects.zrangeByScore(key, smin, smax)); assertThat(stringRange, contains("one", "two", "three")); List numericRangeOffsetCount = exec(commandObjects.zrangeByScore(key, min, max, offset, count)); assertThat(numericRangeOffsetCount, contains("one")); List stringRangeOffsetCount = exec(commandObjects.zrangeByScore(key, smin, smax, offset, count)); assertThat(stringRangeOffsetCount, contains("one")); List numericRangeBinary = exec(commandObjects.zrangeByScore(key.getBytes(), min, max)); assertThat(numericRangeBinary.get(0), equalTo("one".getBytes())); assertThat(numericRangeBinary.get(1), equalTo("two".getBytes())); assertThat(numericRangeBinary.get(2), equalTo("three".getBytes())); List stringRangeBinary = exec(commandObjects.zrangeByScore(key.getBytes(), bmin, bmax)); assertThat(stringRangeBinary, contains("one".getBytes(), "two".getBytes(), "three".getBytes())); List numericRangeOffsetCountBinary = exec(commandObjects.zrangeByScore(key.getBytes(), min, max, offset, count)); assertThat(numericRangeOffsetCountBinary.get(0), equalTo("one".getBytes())); List stringRangeOffsetCountBinary = exec(commandObjects.zrangeByScore(key.getBytes(), bmin, bmax, offset, count)); assertThat(stringRangeOffsetCountBinary, contains("one".getBytes())); } @Test public void testZrevrangeByScore() { String key = "zset"; double max = 10.0; double min = 1.0; String smax = "10"; String smin = "1"; byte[] bmax = "10.0".getBytes(); byte[] bmin = "1.0".getBytes(); int offset = 0; int count = 1; exec(commandObjects.zadd(key, 13, "four")); exec(commandObjects.zadd(key, 3, "three")); exec(commandObjects.zadd(key, 2, "two")); exec(commandObjects.zadd(key, 1, "one")); List numericRevrange = exec(commandObjects.zrevrangeByScore(key, max, min)); assertThat(numericRevrange, contains("three", "two", "one")); List stringRevrange = exec(commandObjects.zrevrangeByScore(key, smax, smin)); assertThat(stringRevrange, contains("three", "two", "one")); List numericRevrangeOffsetCount = exec(commandObjects.zrevrangeByScore(key, max, min, offset, count)); assertThat(numericRevrangeOffsetCount, contains("three")); List stringRevrangeOffsetCount = exec(commandObjects.zrevrangeByScore(key, smax, smin, offset, count)); assertThat(stringRevrangeOffsetCount, contains("three")); List numericRevrangeBinary = exec(commandObjects.zrevrangeByScore(key.getBytes(), max, min)); assertThat(numericRevrangeBinary, contains("three".getBytes(), "two".getBytes(), "one".getBytes())); List stringRevrangeBinary = exec(commandObjects.zrevrangeByScore(key.getBytes(), bmax, bmin)); assertThat(stringRevrangeBinary, contains("three".getBytes(), "two".getBytes(), "one".getBytes())); List numericRevrangeOffsetCountBinary = exec(commandObjects.zrevrangeByScore(key.getBytes(), max, min, offset, count)); assertThat(numericRevrangeOffsetCountBinary.get(0), equalTo("three".getBytes())); List stringRevrangeOffsetCountBinary = exec(commandObjects.zrevrangeByScore(key.getBytes(), bmax, bmin, offset, count)); assertThat(stringRevrangeOffsetCountBinary, contains("three".getBytes())); } @Test public void testZrangeByScoreWithScores() { String key = "zset"; double min = 1.0; double max = 10.0; String smin = "1"; String smax = "10"; byte[] bmin = "1.0".getBytes(); byte[] bmax = "10.0".getBytes(); int offset = 0; int count = 2; exec(commandObjects.zadd(key, 1, "one")); exec(commandObjects.zadd(key, 2, "two")); exec(commandObjects.zadd(key, 3, "three")); List numericRange = exec(commandObjects.zrangeByScoreWithScores(key, min, max)); assertThat(numericRange, contains( new Tuple("one", 1d), new Tuple("two", 2d), new Tuple("three", 3d))); List stringRange = exec(commandObjects.zrangeByScoreWithScores(key, smin, smax)); assertThat(stringRange, contains( new Tuple("one", 1d), new Tuple("two", 2d), new Tuple("three", 3d))); List numericRangeOffsetCount = exec(commandObjects.zrangeByScoreWithScores(key, min, max, offset, count)); assertThat(numericRangeOffsetCount, contains( new Tuple("one", 1d), new Tuple("two", 2d))); List stringRangeOffsetCount = exec(commandObjects.zrangeByScoreWithScores(key, smin, smax, offset, count)); assertThat(stringRangeOffsetCount, contains( new Tuple("one", 1d), new Tuple("two", 2d))); List numericRangeBinary = exec(commandObjects.zrangeByScoreWithScores(key.getBytes(), min, max)); assertThat(numericRangeBinary, contains( new Tuple("one".getBytes(), 1d), new Tuple("two".getBytes(), 2d), new Tuple("three".getBytes(), 3d))); List stringRangeBinary = exec(commandObjects.zrangeByScoreWithScores(key.getBytes(), bmin, bmax)); assertThat(stringRangeBinary, contains( new Tuple("one".getBytes(), 1d), new Tuple("two".getBytes(), 2d), new Tuple("three".getBytes(), 3d))); List numericRangeOffsetCountBinary = exec(commandObjects.zrangeByScoreWithScores(key.getBytes(), min, max, offset, count)); assertThat(numericRangeOffsetCountBinary, contains( new Tuple("one".getBytes(), 1d), new Tuple("two".getBytes(), 2d))); List stringRangeOffsetCountBinary = exec(commandObjects.zrangeByScoreWithScores(key.getBytes(), bmin, bmax, offset, count)); assertThat(stringRangeOffsetCountBinary, contains( new Tuple("one".getBytes(), 1d), new Tuple("two".getBytes(), 2d))); } @Test public void testZrevrangeByScoreWithScores() { String key = "zset"; double max = 10.0; double min = 1.0; String smax = "10"; String smin = "1"; byte[] bmax = "10".getBytes(); byte[] bmin = "1".getBytes(); int offset = 0; int count = 2; exec(commandObjects.zadd(key, 3, "three")); exec(commandObjects.zadd(key, 2, "two")); exec(commandObjects.zadd(key, 1, "one")); List numericRevrange = exec(commandObjects.zrevrangeByScoreWithScores(key, max, min)); assertThat(numericRevrange, contains( new Tuple("three", 3d), new Tuple("two", 2d), new Tuple("one", 1d))); List stringRevrange = exec(commandObjects.zrevrangeByScoreWithScores(key, smax, smin)); assertThat(stringRevrange, contains( new Tuple("three", 3d), new Tuple("two", 2d), new Tuple("one", 1d))); List numericRevrangeOffsetCount = exec(commandObjects.zrevrangeByScoreWithScores(key, max, min, offset, count)); assertThat(numericRevrangeOffsetCount, contains( new Tuple("three", 3d), new Tuple("two", 2d))); List stringRevrangeOffsetCount = exec(commandObjects.zrevrangeByScoreWithScores(key, smax, smin, offset, count)); assertThat(stringRevrangeOffsetCount, contains( new Tuple("three", 3d), new Tuple("two", 2d))); List numericRevrangeBinary = exec(commandObjects.zrevrangeByScoreWithScores(key.getBytes(), max, min)); assertThat(numericRevrangeBinary, contains( new Tuple("three".getBytes(), 3d), new Tuple("two".getBytes(), 2d), new Tuple("one".getBytes(), 1d))); List stringRevrangeBinary = exec(commandObjects.zrevrangeByScoreWithScores(key.getBytes(), bmax, bmin)); assertThat(stringRevrangeBinary, contains( new Tuple("three".getBytes(), 3d), new Tuple("two".getBytes(), 2d), new Tuple("one".getBytes(), 1d))); List numericRevrangeOffsetCountBinary = exec(commandObjects.zrevrangeByScoreWithScores(key.getBytes(), max, min, offset, count)); assertThat(numericRevrangeOffsetCountBinary, contains( new Tuple("three".getBytes(), 3d), new Tuple("two".getBytes(), 2d))); List stringRevrangeOffsetCountBinary = exec(commandObjects.zrevrangeByScoreWithScores(key.getBytes(), bmax, bmin, offset, count)); assertThat(stringRevrangeOffsetCountBinary, contains( new Tuple("three".getBytes(), 3d), new Tuple("two".getBytes(), 2d))); } @Test public void testZremrangeByRank() { String key = "zset"; long start = 0; long stop = 1; exec(commandObjects.zadd(key, 1, "one")); exec(commandObjects.zadd(key, 2, "two")); exec(commandObjects.zadd(key, 3, "three")); Long removedCount = exec(commandObjects.zremrangeByRank(key, start, stop)); assertThat(removedCount, equalTo(2L)); List remainingElements = exec(commandObjects.zrange(key, 0, -1)); assertThat(remainingElements, contains("three")); } @Test public void testZremrangeByRankBinary() { byte[] key = "zset".getBytes(); long start = 0; long stop = 1; exec(commandObjects.zadd(key, 1, "one".getBytes())); exec(commandObjects.zadd(key, 2, "two".getBytes())); exec(commandObjects.zadd(key, 3, "three".getBytes())); Long removedCount = exec(commandObjects.zremrangeByRank(key, start, stop)); assertThat(removedCount, equalTo(2L)); List remainingElements = exec(commandObjects.zrange(key, 0, -1)); assertThat(remainingElements, contains("three".getBytes())); } @Test public void testZremrangeByScore() { String key = "zset"; double min = 1.0; double max = 2.0; String smin = "1"; String smax = "2"; exec(commandObjects.zadd(key, 1, "one")); exec(commandObjects.zadd(key, 2, "two")); exec(commandObjects.zadd(key, 3, "three")); Long removedCountNumeric = exec(commandObjects.zremrangeByScore(key, min, max)); assertThat(removedCountNumeric, equalTo(2L)); List remainingElements = exec(commandObjects.zrange(key, 0, -1)); assertThat(remainingElements, contains("three")); exec(commandObjects.zadd(key, 1, "one")); exec(commandObjects.zadd(key, 2, "two")); Long removedCountString = exec(commandObjects.zremrangeByScore(key, smin, smax)); assertThat(removedCountString, equalTo(2L)); remainingElements = exec(commandObjects.zrange(key, 0, -1)); assertThat(remainingElements, contains("three")); } @Test public void testZremrangeByScoreBinary() { byte[] bkey = "zset".getBytes(); double min = 1.0; double max = 2.0; byte[] bmin = "1".getBytes(); byte[] bmax = "2".getBytes(); exec(commandObjects.zadd(bkey, 1, "one".getBytes())); exec(commandObjects.zadd(bkey, 2, "two".getBytes())); exec(commandObjects.zadd(bkey, 3, "three".getBytes())); Long removedCountNumericBinary = exec(commandObjects.zremrangeByScore(bkey, min, max)); assertThat(removedCountNumericBinary, equalTo(2L)); List remainingElements = exec(commandObjects.zrange(bkey, 0, -1)); assertThat(remainingElements, contains("three".getBytes())); exec(commandObjects.zadd(bkey, 1, "one".getBytes())); exec(commandObjects.zadd(bkey, 2, "two".getBytes())); Long removedCountStringBinary = exec(commandObjects.zremrangeByScore(bkey, bmin, bmax)); assertThat(removedCountStringBinary, equalTo(2L)); remainingElements = exec(commandObjects.zrange(bkey, 0, -1)); assertThat(remainingElements, contains("three".getBytes())); } @Test public void testZlexcount() { String key = "zset"; String min = "[a", max = "(g"; exec(commandObjects.zadd(key, 0, "abc")); exec(commandObjects.zadd(key, 0, "def")); exec(commandObjects.zadd(key, 0, "ghi")); Long count = exec(commandObjects.zlexcount(key, min, max)); assertThat(count, equalTo(2L)); Long countBinary = exec(commandObjects.zlexcount(key.getBytes(), min.getBytes(), max.getBytes())); assertThat(countBinary, equalTo(2L)); } @Test public void testZrangeByLex() { String key = "zset"; String min = "[abc"; String max = "(cde"; int offset = 0; int count = 2; exec(commandObjects.zadd(key, 0, "aaa")); exec(commandObjects.zadd(key, 0, "abc")); exec(commandObjects.zadd(key, 0, "bcd")); exec(commandObjects.zadd(key, 0, "cde")); List range = exec(commandObjects.zrangeByLex(key, min, max)); assertThat(range, contains("abc", "bcd")); List limitedRange = exec(commandObjects.zrangeByLex(key, min, max, offset, count)); assertThat(limitedRange, contains("abc", "bcd")); List rangeBinary = exec(commandObjects.zrangeByLex(key.getBytes(), min.getBytes(), max.getBytes())); assertThat(rangeBinary, contains("abc".getBytes(), "bcd".getBytes())); List limitedRangeBinary = exec(commandObjects.zrangeByLex(key.getBytes(), min.getBytes(), max.getBytes(), offset, count)); assertThat(limitedRangeBinary, contains("abc".getBytes(), "bcd".getBytes())); } @Test public void testZrevrangeByLex() { String key = "zset"; String max = "[cde"; String min = "(aaa"; int offset = 0; int count = 2; exec(commandObjects.zadd(key, 0, "aaa")); exec(commandObjects.zadd(key, 0, "abc")); exec(commandObjects.zadd(key, 0, "bcd")); exec(commandObjects.zadd(key, 0, "cde")); List revRange = exec(commandObjects.zrevrangeByLex(key, max, min)); assertThat(revRange, contains("cde", "bcd", "abc")); List limitedRevRange = exec(commandObjects.zrevrangeByLex(key, max, min, offset, count)); assertThat(limitedRevRange, contains("cde", "bcd")); List revRangeBinary = exec(commandObjects.zrevrangeByLex(key.getBytes(), max.getBytes(), min.getBytes())); assertThat(revRangeBinary.get(0), equalTo("cde".getBytes())); assertThat(revRangeBinary.get(1), equalTo("bcd".getBytes())); assertThat(revRangeBinary.get(2), equalTo("abc".getBytes())); List limitedRevRangeBinary = exec(commandObjects.zrevrangeByLex(key.getBytes(), max.getBytes(), min.getBytes(), offset, count)); assertThat(limitedRevRangeBinary.get(0), equalTo("cde".getBytes())); assertThat(limitedRevRangeBinary.get(1), equalTo("bcd".getBytes())); } @Test public void testZremrangeByLex() { String key = "zset"; String min = "[aaa"; String max = "(ccc"; exec(commandObjects.zadd(key, 0, "aaa")); exec(commandObjects.zadd(key, 0, "bbb")); exec(commandObjects.zadd(key, 0, "ccc")); Long removedCount = exec(commandObjects.zremrangeByLex(key, min, max)); assertThat(removedCount, equalTo(2L)); List remainingElements = exec(commandObjects.zrange(key, 0, -1)); assertThat(remainingElements, contains("ccc")); } @Test public void testZremrangeByLexBinary() { byte[] key = "zset".getBytes(); byte[] min = "[aaa".getBytes(); byte[] bmax = "(ccc".getBytes(); exec(commandObjects.zadd(key, 0, "aaa".getBytes())); exec(commandObjects.zadd(key, 0, "bbb".getBytes())); exec(commandObjects.zadd(key, 0, "ccc".getBytes())); Long removedCount = exec(commandObjects.zremrangeByLex(key, min, bmax)); assertThat(removedCount, equalTo(2L)); List remainingElements = exec(commandObjects.zrange(key, 0, -1)); assertThat(remainingElements, contains("ccc".getBytes())); } @Test public void testZscan() { String key = "zset"; String cursor = "0"; ScanParams params = new ScanParams().count(2); String member1 = "one"; double score1 = 1.0; String member2 = "two"; double score2 = 2.0; exec(commandObjects.zadd(key, score1, member1)); exec(commandObjects.zadd(key, score2, member2)); ScanResult result = exec(commandObjects.zscan(key, cursor, params)); assertThat(result.getResult(), containsInAnyOrder( new Tuple(member1, score1), new Tuple(member2, score2))); ScanResult resultBinar = exec(commandObjects.zscan(key.getBytes(), cursor.getBytes(), params)); assertThat(resultBinar.getResult(), containsInAnyOrder( new Tuple(member1, score1), new Tuple(member2, score2))); } @Test @ConditionalOnEnv(value = TestEnvUtil.ENV_REDIS_ENTERPRISE, enabled = false) public void testZdiffAndZdiffWithScores() { String key1 = "zset1"; String key2 = "zset2"; String member1 = "one"; double score1 = 1.0; String member2 = "two"; double score2 = 2.0; exec(commandObjects.zadd(key1, score1, member1)); exec(commandObjects.zadd(key1, score2, member2)); exec(commandObjects.zadd(key2, score1, member1)); List diff = exec(commandObjects.zdiff(key1, key2)); assertThat(diff, containsInAnyOrder(member2)); List diffWithScores = exec(commandObjects.zdiffWithScores(key1, key2)); assertThat(diffWithScores, containsInAnyOrder(new Tuple(member2, score2))); List diffBinary = exec(commandObjects.zdiff(key1.getBytes(), key2.getBytes())); assertThat(diffBinary, containsInAnyOrder(member2.getBytes())); List diffWithScoresBinary = exec(commandObjects.zdiffWithScores(key1.getBytes(), key2.getBytes())); assertThat(diffWithScoresBinary, containsInAnyOrder(new Tuple(member2.getBytes(), score2))); } @Test @ConditionalOnEnv(value = TestEnvUtil.ENV_REDIS_ENTERPRISE, enabled = false) public void testZdiffStore() { String dstKey = "result"; exec(commandObjects.zadd("set1", 1, "member1")); exec(commandObjects.zadd("set1", 2, "member2")); exec(commandObjects.zadd("set2", 3, "member2")); Long result = exec(commandObjects.zdiffStore(dstKey, "set1", "set2")); assertThat(result, equalTo(1L)); List resultSet = exec(commandObjects.zrange(dstKey, 0, -1)); assertThat(resultSet, containsInAnyOrder("member1")); exec(commandObjects.del(dstKey)); result = exec(commandObjects.zdiffstore(dstKey, "set1", "set2")); assertThat(result, equalTo(1L)); resultSet = exec(commandObjects.zrange(dstKey, 0, -1)); assertThat(resultSet, hasSize(1)); assertThat(resultSet, containsInAnyOrder("member1")); } @Test @ConditionalOnEnv(value = TestEnvUtil.ENV_REDIS_ENTERPRISE, enabled = false) public void testZdiffStoreBinary() { byte[] dstKey = "result".getBytes(); exec(commandObjects.zadd("set1".getBytes(), 1, "member1".getBytes())); exec(commandObjects.zadd("set1".getBytes(), 2, "member2".getBytes())); exec(commandObjects.zadd("set2".getBytes(), 3, "member2".getBytes())); Long result = exec(commandObjects.zdiffStore(dstKey, "set1".getBytes(), "set2".getBytes())); assertThat(result, equalTo(1L)); List resultSet = exec(commandObjects.zrange(dstKey, 0, -1)); assertThat(resultSet, hasSize(1)); assertThat(resultSet, containsInAnyOrder("member1".getBytes())); exec(commandObjects.del(dstKey)); result = exec(commandObjects.zdiffstore(dstKey, "set1".getBytes(), "set2".getBytes())); assertThat(result, equalTo(1L)); resultSet = exec(commandObjects.zrange(dstKey, 0, -1)); assertThat(resultSet, hasSize(1)); assertThat(resultSet, containsInAnyOrder("member1".getBytes())); } @Test @SinceRedisVersion(value = "7.2.0") @ConditionalOnEnv(value = TestEnvUtil.ENV_REDIS_ENTERPRISE, enabled = false) public void testZinterAndZintercard() { ZParams params = new ZParams().aggregate(ZParams.Aggregate.SUM).weights(1, 2); exec(commandObjects.zadd("set1", 1, "member1")); exec(commandObjects.zadd("set2", 2, "member1")); exec(commandObjects.zadd("set2", 2, "member2")); List inter = exec(commandObjects.zinter(params, "set1", "set2")); assertThat(inter, containsInAnyOrder("member1")); List interWithScores = exec(commandObjects.zinterWithScores(params, "set1", "set2")); assertThat(interWithScores, containsInAnyOrder( new Tuple("member1", 5.0))); Long card = exec(commandObjects.zintercard("set1", "set2")); assertThat(card, equalTo(1L)); Long cardLimited = exec(commandObjects.zintercard(1L, "set1", "set2")); assertThat(cardLimited, equalTo(1L)); List interBinary = exec(commandObjects.zinter(params, "set1".getBytes(), "set2".getBytes())); assertThat(interBinary, containsInAnyOrder("member1".getBytes())); List interWithScoresBinary = exec(commandObjects.zinterWithScores(params, "set1".getBytes(), "set2".getBytes())); assertThat(interWithScoresBinary, hasItem( new Tuple("member1".getBytes(), 5.0))); Long cardBinary = exec(commandObjects.zintercard("set1".getBytes(), "set2".getBytes())); assertThat(cardBinary, equalTo(1L)); Long cardLimitedBinary = exec(commandObjects.zintercard(1L, "set1".getBytes(), "set2".getBytes())); assertThat(cardLimitedBinary, equalTo(1L)); } @Test @ConditionalOnEnv(value = TestEnvUtil.ENV_REDIS_ENTERPRISE, enabled = false) public void testZinterstore() { String dstKey = "destinationIntersect"; String set1 = "sortedSet1"; String set2 = "sortedSet2"; String set3 = "sortedSet3"; double score1 = 1.0; double score2 = 2.0; double score3 = 3.0; String member1 = "member1"; String member2 = "member2"; String member3 = "member3"; exec(commandObjects.zadd(set1, score1, member1)); exec(commandObjects.zadd(set1, score2, member2)); exec(commandObjects.zadd(set2, score2, member2)); exec(commandObjects.zadd(set2, score3, member3)); exec(commandObjects.zadd(set3, score1, member1)); exec(commandObjects.zadd(set3, score3, member3)); ZParams params = new ZParams().aggregate(ZParams.Aggregate.SUM); Long interStore = exec(commandObjects.zinterstore(dstKey, set1, set2, set3)); assertThat(interStore, equalTo(0L)); Long interStoreWithParams = exec(commandObjects.zinterstore(dstKey, params, set1, set2)); assertThat(interStoreWithParams, equalTo(1L)); List dstSetContent = exec(commandObjects.zrangeWithScores(dstKey, 0, -1)); assertThat(dstSetContent, hasSize(1)); assertThat(dstSetContent.get(0).getElement(), equalTo(member2)); assertThat(dstSetContent.get(0).getScore(), equalTo(score2 * 2)); // Score aggregated as SUM } @Test @ConditionalOnEnv(value = TestEnvUtil.ENV_REDIS_ENTERPRISE, enabled = false) public void testZinterstoreBinary() { byte[] dstKey = "destinationIntersect".getBytes(); byte[] set1 = "sortedSet1".getBytes(); byte[] set2 = "sortedSet2".getBytes(); byte[] set3 = "sortedSet3".getBytes(); double score1 = 1.0; double score2 = 2.0; double score3 = 3.0; byte[] member1 = "member1".getBytes(); byte[] member2 = "member2".getBytes(); byte[] member3 = "member3".getBytes(); exec(commandObjects.zadd(set1, score1, member1)); exec(commandObjects.zadd(set1, score2, member2)); exec(commandObjects.zadd(set2, score2, member2)); exec(commandObjects.zadd(set2, score3, member3)); exec(commandObjects.zadd(set3, score1, member1)); exec(commandObjects.zadd(set3, score3, member3)); ZParams params = new ZParams().aggregate(ZParams.Aggregate.SUM); Long interStore = exec(commandObjects.zinterstore(dstKey, set1, set2, set3)); assertThat(interStore, equalTo(0L)); List dstSetContent = exec(commandObjects.zrangeWithScores(dstKey, 0, -1)); assertThat(dstSetContent, empty()); Long interStoreParams = exec(commandObjects.zinterstore(dstKey, params, set1, set2)); assertThat(interStoreParams, equalTo(1L)); List dstSetParamsContent = exec(commandObjects.zrangeWithScores(dstKey, 0, -1)); assertThat(dstSetParamsContent, hasSize(1)); assertThat(dstSetParamsContent.get(0).getBinaryElement(), equalTo(member2)); assertThat(dstSetParamsContent.get(0).getScore(), equalTo(score2 * 2)); // Score aggregated as SUM } @Test @ConditionalOnEnv(value = TestEnvUtil.ENV_REDIS_ENTERPRISE, enabled = false) public void testZunionAndZunionWithScores() { String key1 = "sortedSet1"; String key2 = "sortedSet2"; String member1 = "member1"; String member2 = "member2"; double score1 = 1.0; double score2 = 2.0; exec(commandObjects.zadd(key1, score1, member1)); exec(commandObjects.zadd(key2, score2, member1)); exec(commandObjects.zadd(key2, score2, member2)); ZParams params = new ZParams().aggregate(ZParams.Aggregate.SUM); List zunion = exec(commandObjects.zunion(params, key1, key2)); assertThat(zunion, containsInAnyOrder(member1, member2)); List zunionWithScores = exec(commandObjects.zunionWithScores(params, key1, key2)); assertThat(zunionWithScores, containsInAnyOrder( new Tuple(member1, score1 + score2), new Tuple(member2, score2))); List zunionBinary = exec(commandObjects.zunion(params, key1.getBytes(), key2.getBytes())); assertThat(zunionBinary, containsInAnyOrder(member1.getBytes(), member2.getBytes())); List zunionWithScoresBinary = exec(commandObjects.zunionWithScores(params, key1.getBytes(), key2.getBytes())); assertThat(zunionWithScoresBinary, containsInAnyOrder( new Tuple(member1, score1 + score2), new Tuple(member2, score2))); } @Test @ConditionalOnEnv(value = TestEnvUtil.ENV_REDIS_ENTERPRISE, enabled = false) public void testZunionstore() { String dstKey = "destinationSet"; String set1 = "sortedSet1"; String set2 = "sortedSet2"; double score1 = 1.0; double score2 = 2.0; double score3 = 3.0; String member1 = "member1"; String member2 = "member2"; String member3 = "member3"; exec(commandObjects.zadd(set1, score1, member1)); exec(commandObjects.zadd(set1, score2, member2)); exec(commandObjects.zadd(set1, score3, member3)); exec(commandObjects.zadd(set2, score3, member3)); ZParams params = new ZParams().aggregate(ZParams.Aggregate.MAX); Long zunionStore = exec(commandObjects.zunionstore(dstKey, set1, set2)); assertThat(zunionStore, equalTo(3L)); List dstSetContent = exec(commandObjects.zrangeWithScores(dstKey, 0, -1)); assertThat(dstSetContent, containsInAnyOrder( new Tuple(member1, score1), new Tuple(member2, score2), new Tuple(member3, score3 * 2))); Long zunionStoreParams = exec(commandObjects.zunionstore(dstKey, params, set1, set2)); assertThat(zunionStoreParams, equalTo(3L)); List dstSetContentParams = exec(commandObjects.zrangeWithScores(dstKey, 0, -1)); assertThat(dstSetContentParams, containsInAnyOrder( new Tuple(member1, score1), new Tuple(member2, score2), new Tuple(member3, score3))); } @Test @ConditionalOnEnv(value = TestEnvUtil.ENV_REDIS_ENTERPRISE, enabled = false) public void testZunionstoreBinary() { byte[] dstKey = "destinationSet".getBytes(); byte[] set1 = "sortedSet1".getBytes(); byte[] set2 = "sortedSet2".getBytes(); double score1 = 1.0; double score2 = 2.0; double score3 = 3.0; byte[] member1 = "member1".getBytes(); byte[] member2 = "member2".getBytes(); byte[] member3 = "member3".getBytes(); exec(commandObjects.zadd(set1, score1, member1)); exec(commandObjects.zadd(set1, score2, member2)); exec(commandObjects.zadd(set1, score3, member3)); exec(commandObjects.zadd(set2, score3, member3)); ZParams params = new ZParams().aggregate(ZParams.Aggregate.MAX); Long zunionStore = exec(commandObjects.zunionstore(dstKey, set1, set2)); assertThat(zunionStore, equalTo(3L)); List dstSetContent = exec(commandObjects.zrangeWithScores(dstKey, 0, -1)); assertThat(dstSetContent, containsInAnyOrder( new Tuple(member1, score1), new Tuple(member2, score2), new Tuple(member3, score3 * 2))); Long zunionStoreParams = exec(commandObjects.zunionstore(dstKey, params, set1, set2)); assertThat(zunionStoreParams, equalTo(3L)); List dstSetContentParams = exec(commandObjects.zrangeWithScores(dstKey, 0, -1)); assertThat(dstSetContentParams, containsInAnyOrder( new Tuple(member1, score1), new Tuple(member2, score2), new Tuple(member3, score3))); } @Test @SinceRedisVersion(value = "7.2.0") @ConditionalOnEnv(value = TestEnvUtil.ENV_REDIS_ENTERPRISE, enabled = false) public void testZmpopAndZmpopWithCount() { String key1 = "sortedSet1"; String key2 = "sortedSet2"; double score1 = 1.0; double score2 = 2.0; String member1 = "member1"; String member2 = "member2"; exec(commandObjects.zadd(key1, score1, member1)); exec(commandObjects.zadd(key2, score2, member2)); KeyValue> zmpop = exec(commandObjects.zmpop(SortedSetOption.MAX, key1, key2)); assertThat(zmpop, notNullValue()); assertThat(zmpop.getKey(), either(equalTo(key1)).or(equalTo(key2))); assertThat(zmpop.getValue(), hasSize(1)); KeyValue> zmpopCount = exec(commandObjects.zmpop(SortedSetOption.MIN, 2, key1, key2)); assertThat(zmpopCount, notNullValue()); assertThat(zmpopCount.getKey(), either(equalTo(key1)).or(equalTo(key2))); assertThat(zmpopCount.getValue(), hasSize(1)); } @Test @SinceRedisVersion(value = "7.2.0") @ConditionalOnEnv(value = TestEnvUtil.ENV_REDIS_ENTERPRISE, enabled = false) public void testZmpopAndZmpopWithCountBinary() { byte[] key1 = "sortedSet1".getBytes(); byte[] key2 = "sortedSet2".getBytes(); double score1 = 1.0; double score2 = 2.0; byte[] member1 = "member1".getBytes(); byte[] member2 = "member2".getBytes(); exec(commandObjects.zadd(key1, score1, member1)); exec(commandObjects.zadd(key2, score2, member2)); KeyValue> zmpopBinary = exec(commandObjects.zmpop(SortedSetOption.MAX, key1, key2)); assertThat(zmpopBinary, notNullValue()); assertThat(zmpopBinary.getKey(), either(equalTo(key1)).or(equalTo(key2))); assertThat(zmpopBinary.getValue(), hasSize(1)); KeyValue> zmpopCountBinary = exec(commandObjects.zmpop(SortedSetOption.MIN, 2, key1, key2)); assertThat(zmpopCountBinary, notNullValue()); assertThat(zmpopCountBinary.getKey(), either(equalTo(key1)).or(equalTo(key2))); assertThat(zmpopCountBinary.getValue(), hasSize(1)); } @Test @SinceRedisVersion(value = "7.0.0") @ConditionalOnEnv(value = TestEnvUtil.ENV_REDIS_ENTERPRISE, enabled = false) public void testBzmpop() { String key1 = "sortedSet1"; String key2 = "sortedSet2"; double score1 = 1.0; double score2 = 2.0; String member1 = "member1"; String member2 = "member2"; double timeout = 0.1; exec(commandObjects.zadd(key1, score1, member1)); exec(commandObjects.zadd(key1, score2, member2)); exec(commandObjects.zadd(key2, score1, member1)); exec(commandObjects.zadd(key2, score2, member2)); KeyValue> bzmpopMax = exec(commandObjects.bzmpop(timeout, SortedSetOption.MAX, key1, key2)); assertThat(bzmpopMax, notNullValue()); assertThat(bzmpopMax.getKey(), anyOf(equalTo(key1), equalTo(key2))); assertThat(bzmpopMax.getValue(), contains(new Tuple(member2, score2))); KeyValue> bzmpopMin = exec(commandObjects.bzmpop(timeout, SortedSetOption.MIN, key1, key2)); assertThat(bzmpopMin, notNullValue()); assertThat(bzmpopMin.getKey(), anyOf(equalTo(key1), equalTo(key2))); assertThat(bzmpopMin.getValue(), contains(new Tuple(member1, score1))); } @Test @SinceRedisVersion(value = "7.2.0") @ConditionalOnEnv(value = TestEnvUtil.ENV_REDIS_ENTERPRISE, enabled = false) public void testBzmpopBinary() { byte[] key1 = "sortedSet1".getBytes(); byte[] key2 = "sortedSet2".getBytes(); double score1 = 1.0; double score2 = 2.0; byte[] member1 = "member1".getBytes(); byte[] member2 = "member2".getBytes(); double timeout = 0.1; exec(commandObjects.zadd(key1, score1, member1)); exec(commandObjects.zadd(key1, score2, member2)); exec(commandObjects.zadd(key2, score1, member1)); exec(commandObjects.zadd(key2, score2, member2)); KeyValue> bzmpopMax = exec(commandObjects.bzmpop(timeout, SortedSetOption.MAX, key1, key2)); assertThat(bzmpopMax, notNullValue()); assertThat(bzmpopMax.getKey(), anyOf(equalTo(key1), equalTo(key2))); assertThat(bzmpopMax.getValue(), contains(new Tuple(member2, score2))); KeyValue> bzmpopMin = exec(commandObjects.bzmpop(timeout, SortedSetOption.MIN, key1, key2)); assertThat(bzmpopMin, notNullValue()); assertThat(bzmpopMin.getKey(), anyOf(equalTo(key1), equalTo(key2))); assertThat(bzmpopMin.getValue(), contains(new Tuple(member1, score1))); } @Test @SinceRedisVersion(value = "7.2.0") @ConditionalOnEnv(value = TestEnvUtil.ENV_REDIS_ENTERPRISE, enabled = false) public void testBzmpopCount() { String key1 = "sortedSet1"; String key2 = "sortedSet2"; double score1 = 1.0; double score2 = 2.0; double timeout = 0.1; String member1 = "member1"; String member2 = "member2"; exec(commandObjects.zadd(key1, score1, member1)); exec(commandObjects.zadd(key2, score2, member2)); KeyValue> bzmpop = exec(commandObjects.bzmpop(timeout, SortedSetOption.MAX, 1, key1, key2)); assertThat(bzmpop, notNullValue()); assertThat(bzmpop.getKey(), either(equalTo(key1)).or(equalTo(key2))); assertThat(bzmpop.getValue(), hasSize(1)); } @Test @SinceRedisVersion(value = "7.0.0") @ConditionalOnEnv(value = TestEnvUtil.ENV_REDIS_ENTERPRISE, enabled = false) public void testBzmpopCountBinary() { byte[] key1 = "sortedSet1".getBytes(); byte[] key2 = "sortedSet2".getBytes(); double score1 = 1.0; double score2 = 2.0; double timeout = 0.1; byte[] member1 = "member1".getBytes(); byte[] member2 = "member2".getBytes(); exec(commandObjects.zadd(key1, score1, member1)); exec(commandObjects.zadd(key2, score2, member2)); KeyValue> bzmpopBinary = exec(commandObjects.bzmpop(timeout, SortedSetOption.MAX, 1, key1, key2)); assertThat(bzmpopBinary, notNullValue()); assertThat(bzmpopBinary.getKey(), either(equalTo(key1)).or(equalTo(key2))); assertThat(bzmpopBinary.getValue(), hasSize(1)); } } ================================================ FILE: src/test/java/redis/clients/jedis/commands/commandobjects/CommandObjectsStandaloneTestBase.java ================================================ package redis.clients.jedis.commands.commandobjects; import org.junit.jupiter.api.Tag; import org.junit.jupiter.api.extension.RegisterExtension; import redis.clients.jedis.util.EnabledOnCommandCondition; import redis.clients.jedis.util.EnvCondition; import redis.clients.jedis.util.RedisVersionCondition; import redis.clients.jedis.Endpoints; import redis.clients.jedis.RedisProtocol; /** * Base class for tests that use the standalone client. */ @Tag("integration") public abstract class CommandObjectsStandaloneTestBase extends CommandObjectsTestBase { @RegisterExtension RedisVersionCondition redisVersionCondition = new RedisVersionCondition(() -> endpoint); @RegisterExtension EnabledOnCommandCondition enabledOnCommandCondition = new EnabledOnCommandCondition(() -> endpoint); @RegisterExtension static EnvCondition envCondition = new EnvCondition(); public CommandObjectsStandaloneTestBase(RedisProtocol protocol) { super(protocol, Endpoints.getRedisEndpoint("standalone0")); } } ================================================ FILE: src/test/java/redis/clients/jedis/commands/commandobjects/CommandObjectsStreamCommandsTest.java ================================================ package redis.clients.jedis.commands.commandobjects; import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.empty; import static org.hamcrest.Matchers.equalTo; import static org.hamcrest.Matchers.greaterThanOrEqualTo; import static org.hamcrest.Matchers.hasSize; import static org.hamcrest.Matchers.instanceOf; import static org.hamcrest.Matchers.lessThanOrEqualTo; import static org.hamcrest.Matchers.not; import static org.hamcrest.Matchers.notNullValue; import java.util.AbstractMap; import java.util.Collections; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Optional; import io.redis.test.annotations.ConditionalOnEnv; import org.junit.jupiter.api.Test; import redis.clients.jedis.RedisProtocol; import redis.clients.jedis.StreamEntryID; import redis.clients.jedis.params.XAddParams; import redis.clients.jedis.params.XAutoClaimParams; import redis.clients.jedis.params.XClaimParams; import redis.clients.jedis.params.XPendingParams; import redis.clients.jedis.params.XReadGroupParams; import redis.clients.jedis.params.XReadParams; import redis.clients.jedis.params.XTrimParams; import redis.clients.jedis.resps.StreamConsumerInfo; import redis.clients.jedis.resps.StreamConsumersInfo; import redis.clients.jedis.resps.StreamEntry; import redis.clients.jedis.resps.StreamFullInfo; import redis.clients.jedis.resps.StreamGroupInfo; import redis.clients.jedis.resps.StreamInfo; import redis.clients.jedis.resps.StreamPendingEntry; import redis.clients.jedis.resps.StreamPendingSummary; import redis.clients.jedis.util.TestEnvUtil; /** * Tests related to Stream commands. */ public class CommandObjectsStreamCommandsTest extends CommandObjectsStandaloneTestBase { public CommandObjectsStreamCommandsTest(RedisProtocol protocol) { super(protocol); } @Test public void testXaddAndXlen() { String streamKey = "testStream"; StreamEntryID entryID = StreamEntryID.NEW_ENTRY; Map entryData = new HashMap<>(); entryData.put("field1", "value1"); entryData.put("field2", "value2"); StreamEntryID addedEntryId = exec(commandObjects.xadd(streamKey, entryID, entryData)); assertThat(addedEntryId, notNullValue()); XAddParams params = new XAddParams().maxLen(1000); StreamEntryID addedEntryIdWithParams = exec(commandObjects.xadd(streamKey, params, entryData)); assertThat(addedEntryIdWithParams, notNullValue()); Long streamLength = exec(commandObjects.xlen(streamKey)); assertThat(streamLength, equalTo(2L)); } @Test public void testXaddAndXlenBinary() { byte[] streamKey = "streamKey".getBytes(); Map entryData = new HashMap<>(); entryData.put("field1".getBytes(), "value1".getBytes()); entryData.put("field2".getBytes(), "value2".getBytes()); XAddParams params = new XAddParams().maxLen(1000); byte[] addedEntryId = exec(commandObjects.xadd(streamKey, params, entryData)); assertThat(addedEntryId, notNullValue()); Long streamLengthBytes = exec(commandObjects.xlen(streamKey)); assertThat(streamLengthBytes, equalTo(1L)); } @Test public void testXrangeWithIdParameters() { String key = "testStream"; Map entryData1 = new HashMap<>(); entryData1.put("field1", "value1"); Map entryData2 = new HashMap<>(); entryData2.put("field2", "value2"); StreamEntryID startID = exec(commandObjects.xadd(key, StreamEntryID.NEW_ENTRY, entryData1)); StreamEntryID endID = exec(commandObjects.xadd(key, StreamEntryID.NEW_ENTRY, entryData2)); List xrangeAll = exec(commandObjects.xrange(key, null, (StreamEntryID) null)); assertThat(xrangeAll.size(), equalTo(2)); assertThat(xrangeAll.get(0).getFields(), equalTo(entryData1)); assertThat(xrangeAll.get(1).getFields(), equalTo(entryData2)); List xrangeAllCount = exec(commandObjects.xrange(key, null, (StreamEntryID) null, 1)); assertThat(xrangeAllCount.size(), equalTo(1)); assertThat(xrangeAllCount.get(0).getFields(), equalTo(entryData1)); List xrangeStartEnd = exec(commandObjects.xrange(key, startID, endID)); assertThat(xrangeStartEnd.size(), equalTo(2)); assertThat(xrangeStartEnd.get(0).getFields(), equalTo(entryData1)); assertThat(xrangeStartEnd.get(1).getFields(), equalTo(entryData2)); List xrangeStartEndCount = exec(commandObjects.xrange(key, startID, endID, 1)); assertThat(xrangeStartEndCount.size(), equalTo(1)); assertThat(xrangeStartEndCount.get(0).getFields(), equalTo(entryData1)); List xrangeUnknown = exec(commandObjects.xrange("nonExistingStream", null, (StreamEntryID) null)); assertThat(xrangeUnknown, empty()); } @Test public void testXrangeWithStringParameters() { String key = "testStreamWithString"; Map entryData1 = new HashMap<>(); entryData1.put("field1", "value1"); Map entryData2 = new HashMap<>(); entryData2.put("field2", "value2"); StreamEntryID startID = exec(commandObjects.xadd(key, StreamEntryID.NEW_ENTRY, entryData1)); StreamEntryID endID = exec(commandObjects.xadd(key, StreamEntryID.NEW_ENTRY, entryData2)); String start = startID.toString(); String end = endID.toString(); List xrangeStartEnd = exec(commandObjects.xrange(key, start, end)); assertThat(xrangeStartEnd.size(), equalTo(2)); assertThat(xrangeStartEnd.get(0).getFields(), equalTo(entryData1)); assertThat(xrangeStartEnd.get(1).getFields(), equalTo(entryData2)); List xrangeStartEndCount = exec(commandObjects.xrange(key, start, end, 1)); assertThat(xrangeStartEndCount.size(), equalTo(1)); assertThat(xrangeStartEndCount.get(0).getFields(), equalTo(entryData1)); List xrangeUnknown = exec(commandObjects.xrange("nonExistingStream", start, end)); assertThat(xrangeUnknown, empty()); } @Test public void testXrangeWithBinaryParameters() { String keyStr = "testStreamWithBytes"; byte[] key = keyStr.getBytes(); Map entryData1 = new HashMap<>(); entryData1.put("field1", "value1"); Map entryData2 = new HashMap<>(); entryData2.put("field2", "value2"); StreamEntryID startID = exec(commandObjects.xadd(keyStr, StreamEntryID.NEW_ENTRY, entryData1)); StreamEntryID endID = exec(commandObjects.xadd(keyStr, StreamEntryID.NEW_ENTRY, entryData2)); byte[] start = startID.toString().getBytes(); byte[] end = endID.toString().getBytes(); List xrangeAll = exec(commandObjects.xrange(key, null, null)); assertThat(xrangeAll, hasSize(2)); assertThat(xrangeAll.get(0), instanceOf(List.class)); assertThat(((List) xrangeAll.get(0)).get(0), equalTo(start)); assertThat(((List) xrangeAll.get(1)).get(0), equalTo(end)); List xrangeStartEnd = exec(commandObjects.xrange(key, start, end)); assertThat(xrangeStartEnd, hasSize(2)); assertThat(xrangeStartEnd.get(0), instanceOf(List.class)); assertThat(((List) xrangeStartEnd.get(0)).get(0), equalTo(start)); assertThat(((List) xrangeStartEnd.get(1)).get(0), equalTo(end)); List xrangeAllCount = exec(commandObjects.xrange(key, null, null, 1)); assertThat(xrangeAllCount, hasSize(1)); assertThat(xrangeAllCount.get(0), instanceOf(List.class)); assertThat(((List) xrangeAllCount.get(0)).get(0), equalTo(start)); List xrangeStartEndCount = exec(commandObjects.xrange(key, start, end, 1)); assertThat(xrangeStartEndCount, hasSize(1)); assertThat(xrangeStartEndCount.get(0), instanceOf(List.class)); assertThat(((List) xrangeStartEndCount.get(0)).get(0), equalTo(start)); List xrangeUnknown = exec(commandObjects.xrange("nonExistingStream".getBytes(), start, end)); assertThat(xrangeUnknown, empty()); } @Test public void testXrevrangeWithIdParameters() { String key = "testStreamForXrevrange"; Map entryData1 = new HashMap<>(); entryData1.put("field1", "value1"); Map entryData2 = new HashMap<>(); entryData2.put("field2", "value2"); StreamEntryID startID = exec(commandObjects.xadd(key, StreamEntryID.NEW_ENTRY, entryData1)); StreamEntryID endID = exec(commandObjects.xadd(key, StreamEntryID.NEW_ENTRY, entryData2)); List xrevrangeAll = exec(commandObjects.xrevrange(key, null, (StreamEntryID) null)); assertThat(xrevrangeAll.size(), equalTo(2)); assertThat(xrevrangeAll.get(0).getFields(), equalTo(entryData2)); // The latest entry comes first assertThat(xrevrangeAll.get(1).getFields(), equalTo(entryData1)); List xrevrangeAllCount = exec(commandObjects.xrevrange(key, null, (StreamEntryID) null, 1)); assertThat(xrevrangeAllCount.size(), equalTo(1)); assertThat(xrevrangeAllCount.get(0).getFields(), equalTo(entryData2)); // Only the latest entry is returned List xrevrangeEndStart = exec(commandObjects.xrevrange(key, endID, startID)); assertThat(xrevrangeEndStart.size(), equalTo(2)); assertThat(xrevrangeEndStart.get(0).getFields(), equalTo(entryData2)); assertThat(xrevrangeEndStart.get(1).getFields(), equalTo(entryData1)); List xrevrangeStartEndCount = exec(commandObjects.xrevrange(key, endID, startID, 1)); assertThat(xrevrangeStartEndCount.size(), equalTo(1)); assertThat(xrevrangeStartEndCount.get(0).getFields(), equalTo(entryData2)); List xrevrangeUnknown = exec(commandObjects.xrevrange("nonExistingStream", null, (StreamEntryID) null)); assertThat(xrevrangeUnknown, empty()); } @Test public void testXrevrangeWithStringParameters() { String key = "testStreamForXrevrangeString"; Map entryData1 = new HashMap<>(); entryData1.put("field1", "value1"); Map entryData2 = new HashMap<>(); entryData2.put("field2", "value2"); StreamEntryID startID = exec(commandObjects.xadd(key, StreamEntryID.NEW_ENTRY, entryData1)); StreamEntryID endID = exec(commandObjects.xadd(key, StreamEntryID.NEW_ENTRY, entryData2)); String start = startID.toString(); String end = endID.toString(); List xrevrangeAll = exec(commandObjects.xrevrange(key, null, (StreamEntryID) null)); assertThat(xrevrangeAll.size(), equalTo(2)); assertThat(xrevrangeAll.get(0).getFields(), equalTo(entryData2)); // The latest entry comes first assertThat(xrevrangeAll.get(1).getFields(), equalTo(entryData1)); List xrevrangeEndStart = exec(commandObjects.xrevrange(key, end, start)); assertThat(xrevrangeEndStart.size(), equalTo(2)); assertThat(xrevrangeEndStart.get(0).getFields(), equalTo(entryData2)); assertThat(xrevrangeEndStart.get(1).getFields(), equalTo(entryData1)); List xrevrangeAllCount = exec(commandObjects.xrevrange(key, null, (StreamEntryID) null, 1)); assertThat(xrevrangeAllCount.size(), equalTo(1)); assertThat(xrevrangeAllCount.get(0).getFields(), equalTo(entryData2)); List xrevrangeEndStartCount = exec(commandObjects.xrevrange(key, end, start, 1)); assertThat(xrevrangeEndStartCount.size(), equalTo(1)); assertThat(xrevrangeEndStartCount.get(0).getFields(), equalTo(entryData2)); List xrevrangeUnknown = exec(commandObjects.xrevrange("nonExistingStream", end, start)); assertThat(xrevrangeUnknown, empty()); } @Test public void testXrevrangeWithBinaryParameters() { String keyStr = "testStreamForXrevrangeBytes"; byte[] key = keyStr.getBytes(); Map entryData1 = new HashMap<>(); entryData1.put("field1", "value1"); Map entryData2 = new HashMap<>(); entryData2.put("field2", "value2"); StreamEntryID startID = exec(commandObjects.xadd(keyStr, StreamEntryID.NEW_ENTRY, entryData1)); StreamEntryID endID = exec(commandObjects.xadd(keyStr, StreamEntryID.NEW_ENTRY, entryData2)); byte[] start = startID.toString().getBytes(); byte[] end = endID.toString().getBytes(); List xrevrangeAll = exec(commandObjects.xrevrange(key, null, null)); assertThat(xrevrangeAll, hasSize(2)); assertThat(xrevrangeAll.get(0), instanceOf(List.class)); assertThat(((List) xrevrangeAll.get(0)).get(0), equalTo(end)); assertThat(((List) xrevrangeAll.get(1)).get(0), equalTo(start)); List xrevrangeEndStart = exec(commandObjects.xrevrange(key, end, start)); assertThat(xrevrangeEndStart, hasSize(2)); assertThat(xrevrangeEndStart.get(0), instanceOf(List.class)); assertThat(((List) xrevrangeEndStart.get(0)).get(0), equalTo(end)); assertThat(((List) xrevrangeEndStart.get(1)).get(0), equalTo(start)); List xrevrangeAllCount = exec(commandObjects.xrevrange(key, null, null, 1)); assertThat(xrevrangeAllCount, hasSize(1)); assertThat(xrevrangeAllCount.get(0), instanceOf(List.class)); assertThat(((List) xrevrangeAllCount.get(0)).get(0), equalTo(end)); List xrevrangeEndStartCount = exec(commandObjects.xrevrange(key, end, start, 1)); assertThat(xrevrangeEndStartCount, hasSize(1)); assertThat(xrevrangeEndStartCount.get(0), instanceOf(List.class)); assertThat(((List) xrevrangeEndStartCount.get(0)).get(0), equalTo(end)); List xrevrangeUnknown = exec(commandObjects.xrevrange("nonExistingStream".getBytes(), end, start)); assertThat(xrevrangeUnknown, empty()); } @Test public void testXaddWithNullId() { String key = "testStreamWithString"; // Add two entries, don't specify the IDs StreamEntryID firstId = exec(commandObjects.xadd(key, (StreamEntryID) null, Collections.singletonMap("field", "value"))); assertThat(firstId, notNullValue()); StreamEntryID secondId = exec(commandObjects.xadd(key, (StreamEntryID) null, Collections.singletonMap("field", "value"))); assertThat(secondId, notNullValue()); assertThat(secondId, not(equalTo(firstId))); assertThat(secondId.getSequence(), greaterThanOrEqualTo(firstId.getSequence())); List xrangeAll = exec(commandObjects.xrange(key, (StreamEntryID) null, null)); assertThat(xrangeAll.size(), equalTo(2)); assertThat(xrangeAll.get(0).getID(), equalTo(firstId)); assertThat(xrangeAll.get(1).getID(), equalTo(secondId)); } @Test public void testXackXpending() { String key = "testStreamForXackEffect"; String group = "testGroup"; String consumer = "testConsumer"; Map entryData = new HashMap<>(); entryData.put("field1", "value1"); exec(commandObjects.xgroupCreate(key, group, new StreamEntryID(), true)); StreamEntryID entryID = exec(commandObjects.xadd(key, StreamEntryID.NEW_ENTRY, entryData)); Map streams = Collections.singletonMap(key, StreamEntryID.XREADGROUP_UNDELIVERED_ENTRY); XReadGroupParams params = new XReadGroupParams(); List>> messages = exec(commandObjects.xreadGroup(group, consumer, params, streams)); assertThat(messages, hasSize(1)); assertThat(messages.get(0).getKey(), equalTo(key)); assertThat(messages.get(0).getValue(), hasSize(1)); assertThat(messages.get(0).getValue().get(0).getID(), equalTo(entryID)); StreamPendingSummary pendingSummary = exec(commandObjects.xpending(key, group)); assertThat(pendingSummary.getTotal(), equalTo(1L)); XPendingParams xPendingParams = new XPendingParams() .start(StreamEntryID.MINIMUM_ID).end(StreamEntryID.MAXIMUM_ID).count(1000); List pendingSummaryWithParams = exec(commandObjects.xpending(key, group, xPendingParams)); assertThat(pendingSummaryWithParams, hasSize(1)); assertThat(pendingSummaryWithParams.get(0).getConsumerName(), equalTo(consumer)); assertThat(pendingSummaryWithParams.get(0).getID(), equalTo(entryID)); Long ack = exec(commandObjects.xack(key, group, entryID)); assertThat(ack, equalTo(1L)); pendingSummary = exec(commandObjects.xpending(key, group)); assertThat(pendingSummary.getTotal(), equalTo(0L)); pendingSummaryWithParams = exec(commandObjects.xpending(key, group, xPendingParams)); assertThat(pendingSummaryWithParams, empty()); } @Test public void testXackXPendingBinary() { String keyStr = "testStreamForXackEffect"; byte[] key = keyStr.getBytes(); byte[] group = "testGroup".getBytes(); byte[] consumer = "testConsumer".getBytes(); Map entryData = new HashMap<>(); entryData.put("field1", "value1"); exec(commandObjects.xgroupCreate(key, group, new StreamEntryID().toString().getBytes(), true)); StreamEntryID entryID = exec(commandObjects.xadd(keyStr, StreamEntryID.NEW_ENTRY, entryData)); Map.Entry stream = new AbstractMap.SimpleEntry<>(key, StreamEntryID.XREADGROUP_UNDELIVERED_ENTRY.toString().getBytes()); XReadGroupParams params = new XReadGroupParams(); List messages = exec(commandObjects.xreadGroup(group, consumer, params, stream)); assertThat(messages, hasSize(1)); Object pendingSummary = exec(commandObjects.xpending(key, group)); assertThat(pendingSummary, instanceOf(List.class)); assertThat(((List) pendingSummary).get(0), equalTo(1L)); XPendingParams xPendingParams = new XPendingParams() .start(StreamEntryID.MINIMUM_ID).end(StreamEntryID.MAXIMUM_ID).count(1000); List pendingList = exec(commandObjects.xpending(key, group, xPendingParams)); assertThat(pendingList, hasSize(1)); Long ack = exec(commandObjects.xack(key, group, entryID.toString().getBytes())); assertThat(ack, equalTo(1L)); pendingSummary = exec(commandObjects.xpending(key, group)); assertThat(pendingSummary, instanceOf(List.class)); assertThat(((List) pendingSummary).get(0), equalTo(0L)); pendingList = exec(commandObjects.xpending(key, group, xPendingParams)); assertThat(pendingList, empty()); } @Test public void testXGroupSetID() { String key = "testStream"; String groupName = "testGroup"; StreamEntryID initialId = new StreamEntryID(); StreamEntryID newId = new StreamEntryID("0-1"); StreamEntryID newId2 = new StreamEntryID("0-2"); exec(commandObjects.xadd(key, StreamEntryID.NEW_ENTRY, Collections.singletonMap("field", "value"))); exec(commandObjects.xgroupCreate(key, groupName, initialId, false)); List groupIdBefore = exec(commandObjects.xinfoGroups(key)); assertThat(groupIdBefore, hasSize(1)); assertThat(groupIdBefore.get(0).getName(), equalTo(groupName)); assertThat(groupIdBefore.get(0).getLastDeliveredId(), equalTo(initialId)); String xgroupSetId = exec(commandObjects.xgroupSetID(key, groupName, newId)); assertThat(xgroupSetId, equalTo("OK")); List groupIdAfter = exec(commandObjects.xinfoGroups(key)); assertThat(groupIdAfter, hasSize(1)); assertThat(groupIdAfter.get(0).getName(), equalTo(groupName)); assertThat(groupIdAfter.get(0).getLastDeliveredId(), equalTo(newId)); String xgroupSetIdBinary = exec(commandObjects.xgroupSetID(key.getBytes(), groupName.getBytes(), newId2.toString().getBytes())); assertThat(xgroupSetIdBinary, equalTo("OK")); List groupIdAfterBinary = exec(commandObjects.xinfoGroups(key)); assertThat(groupIdAfterBinary, hasSize(1)); assertThat(groupIdAfterBinary.get(0).getName(), equalTo(groupName)); assertThat(groupIdAfterBinary.get(0).getLastDeliveredId(), equalTo(newId2)); List binaryGroupIdAfterBinary = exec(commandObjects.xinfoGroups(key.getBytes())); assertThat(binaryGroupIdAfterBinary, notNullValue()); } @Test public void testXGroupDestroy() { String key = "testStream"; String groupName = "testGroup"; StreamEntryID initialId = new StreamEntryID(); exec(commandObjects.xadd(key, StreamEntryID.NEW_ENTRY, Collections.singletonMap("field", "value"))); exec(commandObjects.xgroupCreate(key, groupName, initialId, false)); List groupIdBefore = exec(commandObjects.xinfoGroups(key)); assertThat(groupIdBefore, hasSize(1)); assertThat(groupIdBefore.get(0).getName(), equalTo(groupName)); assertThat(groupIdBefore.get(0).getLastDeliveredId(), equalTo(initialId)); Long xgroupDestroy = exec(commandObjects.xgroupDestroy(key, groupName)); assertThat(xgroupDestroy, equalTo(1L)); List groupInfoAfter = exec(commandObjects.xinfoGroups(key)); assertThat(groupInfoAfter, empty()); // Re-create the group exec(commandObjects.xgroupCreate(key, groupName, initialId, false)); List groupIdBeforeBinary = exec(commandObjects.xinfoGroups(key)); assertThat(groupIdBeforeBinary, hasSize(1)); assertThat(groupIdBeforeBinary.get(0).getName(), equalTo(groupName)); assertThat(groupIdBeforeBinary.get(0).getLastDeliveredId(), equalTo(initialId)); Long xgroupDestroyBinary = exec(commandObjects.xgroupDestroy(key.getBytes(), groupName.getBytes())); assertThat(xgroupDestroyBinary, equalTo(1L)); List groupInfoAfterBinary = exec(commandObjects.xinfoGroups(key)); assertThat(groupInfoAfterBinary, empty()); } @Test public void testXGroupConsumer() { String key = "testStream"; String groupName = "testGroup"; String consumerName = "testConsumer"; StreamEntryID initialId = new StreamEntryID(); exec(commandObjects.xadd(key, StreamEntryID.NEW_ENTRY, Collections.singletonMap("field", "value"))); exec(commandObjects.xgroupCreate(key, groupName, initialId, false)); List groupIdBefore = exec(commandObjects.xinfoGroups(key)); assertThat(groupIdBefore, hasSize(1)); assertThat(groupIdBefore.get(0).getName(), equalTo(groupName)); assertThat(groupIdBefore.get(0).getConsumers(), equalTo(0L)); Boolean createConsumer = exec(commandObjects.xgroupCreateConsumer(key, groupName, consumerName)); assertThat(createConsumer, equalTo(true)); List groupIdAfterCreateConsumer = exec(commandObjects.xinfoGroups(key)); assertThat(groupIdAfterCreateConsumer, hasSize(1)); assertThat(groupIdAfterCreateConsumer.get(0).getName(), equalTo(groupName)); assertThat(groupIdAfterCreateConsumer.get(0).getConsumers(), equalTo(1L)); Long deleteConsumer = exec(commandObjects.xgroupDelConsumer(key, groupName, consumerName)); assertThat(deleteConsumer, equalTo(0L)); List groupIdAfterDeleteConsumer = exec(commandObjects.xinfoGroups(key)); assertThat(groupIdAfterDeleteConsumer, hasSize(1)); assertThat(groupIdAfterDeleteConsumer.get(0).getName(), equalTo(groupName)); assertThat(groupIdAfterDeleteConsumer.get(0).getConsumers(), equalTo(0L)); Boolean createConsumerBinary = exec(commandObjects.xgroupCreateConsumer( key.getBytes(), groupName.getBytes(), consumerName.getBytes())); assertThat(createConsumerBinary, equalTo(true)); List groupIdAfterCreateConsumerBinary = exec(commandObjects.xinfoGroups(key)); assertThat(groupIdAfterCreateConsumerBinary, hasSize(1)); assertThat(groupIdAfterCreateConsumerBinary.get(0).getName(), equalTo(groupName)); assertThat(groupIdAfterCreateConsumerBinary.get(0).getConsumers(), equalTo(1L)); Long deleteConsumerBinary = exec(commandObjects.xgroupDelConsumer( key.getBytes(), groupName.getBytes(), consumerName.getBytes())); assertThat(deleteConsumerBinary, equalTo(0L)); List groupIdAfterDeleteConsumerBinary = exec(commandObjects.xinfoGroups(key)); assertThat(groupIdAfterDeleteConsumerBinary, hasSize(1)); assertThat(groupIdAfterDeleteConsumerBinary.get(0).getName(), equalTo(groupName)); assertThat(groupIdAfterDeleteConsumerBinary.get(0).getConsumers(), equalTo(0L)); } @Test public void testXDelWithStreamSize() { String key = "testStream"; StreamEntryID id1 = exec(commandObjects.xadd(key, StreamEntryID.NEW_ENTRY, Collections.singletonMap("field1", "value1"))); StreamEntryID id2 = exec(commandObjects.xadd(key, StreamEntryID.NEW_ENTRY, Collections.singletonMap("field2", "value2"))); Long sizeBefore = exec(commandObjects.xlen(key)); assertThat(sizeBefore, equalTo(2L)); Long xdel = exec(commandObjects.xdel(key, id1, id2)); assertThat(xdel, equalTo(2L)); Long sizeAfterStringDeletion = exec(commandObjects.xlen(key)); assertThat(sizeAfterStringDeletion, equalTo(0L)); StreamEntryID id3 = exec(commandObjects.xadd(key, StreamEntryID.NEW_ENTRY, Collections.singletonMap("field3", "value3"))); StreamEntryID id4 = exec(commandObjects.xadd(key, StreamEntryID.NEW_ENTRY, Collections.singletonMap("field4", "value4"))); Long sizeBeforeBinaryDeletion = exec(commandObjects.xlen(key)); assertThat(sizeBeforeBinaryDeletion, equalTo(2L)); Long xdelBinary = exec(commandObjects.xdel( key.getBytes(), id3.toString().getBytes(), id4.toString().getBytes())); assertThat(xdelBinary, equalTo(2L)); Long sizeAfterBinaryDeletion = exec(commandObjects.xlen(key)); assertThat(sizeAfterBinaryDeletion, equalTo(0L)); } @Test public void testXTrimCommands() { String key = "testStream"; // Populate the stream with more entries than we intend to keep for (int i = 0; i < 10; i++) { exec(commandObjects.xadd(key, StreamEntryID.NEW_ENTRY, Collections.singletonMap("field" + i, "value" + i))); } Long sizeBeforeTrim = exec(commandObjects.xlen(key)); assertThat(sizeBeforeTrim, equalTo(10L)); Long xtrim = exec(commandObjects.xtrim(key, 6, false)); assertThat(xtrim, equalTo(4L)); Long sizeAfterTrim = exec(commandObjects.xlen(key)); assertThat(sizeAfterTrim, equalTo(6L)); Long xtrimApproximate = exec(commandObjects.xtrim(key, 3, true)); assertThat(xtrimApproximate, lessThanOrEqualTo(3L)); Long sizeAfterApproximateTrim = exec(commandObjects.xlen(key)); assertThat(sizeAfterApproximateTrim, greaterThanOrEqualTo(3L)); } @Test public void testXTrimCommandsBinary() { String keyStr = "testStream"; byte[] key = keyStr.getBytes(); // Populate the stream with more entries than we intend to keep for (int i = 0; i < 10; i++) { exec(commandObjects.xadd(keyStr, StreamEntryID.NEW_ENTRY, Collections.singletonMap("field" + i, "value" + i))); } Long sizeBeforeBinaryTrim = exec(commandObjects.xlen(key)); assertThat(sizeBeforeBinaryTrim, equalTo(10L)); Long xtrimBinary = exec(commandObjects.xtrim(key, 6, false)); assertThat(xtrimBinary, equalTo(4L)); Long sizeAfterBinaryTrim = exec(commandObjects.xlen(key)); assertThat(sizeAfterBinaryTrim, equalTo(6L)); Long xtrimApproximateBinary = exec(commandObjects.xtrim(key, 3, true)); assertThat(xtrimApproximateBinary, lessThanOrEqualTo(3L)); Long sizeAfterApproximateBinaryTrim = exec(commandObjects.xlen(key)); assertThat(sizeAfterApproximateBinaryTrim, greaterThanOrEqualTo(3L)); } @Test public void testXTrimWithParams() { String key = "testStream"; // Populate the stream with more entries than we intend to keep for (int i = 0; i < 10; i++) { exec(commandObjects.xadd(key, StreamEntryID.NEW_ENTRY, Collections.singletonMap("field" + i, "value" + i))); } Long sizeBeforeTrim = exec(commandObjects.xlen(key)); assertThat(sizeBeforeTrim, equalTo(10L)); XTrimParams params = new XTrimParams().maxLen(6); Long xtrim = exec(commandObjects.xtrim(key, params)); assertThat(xtrim, equalTo(4L)); Long sizeAfterTrim = exec(commandObjects.xlen(key)); assertThat(sizeAfterTrim, equalTo(6L)); } @Test public void testXTrimWithParamsBinary() { String keyStr = "testStream"; byte[] key = keyStr.getBytes(); // Populate the stream with more entries than we intend to keep for (int i = 0; i < 10; i++) { exec(commandObjects.xadd(keyStr, StreamEntryID.NEW_ENTRY, Collections.singletonMap("field" + i, "value" + i))); } Long sizeBeforeTrim = exec(commandObjects.xlen(key)); assertThat(sizeBeforeTrim, equalTo(10L)); XTrimParams params = new XTrimParams().maxLen(6); Long xtrimBinary = exec(commandObjects.xtrim(key, params)); assertThat(xtrimBinary, equalTo(4L)); Long sizeAfterTrim = exec(commandObjects.xlen(key)); assertThat(sizeAfterTrim, equalTo(6L)); } @Test public void testXClaim() throws InterruptedException { String key = "testStream"; String group = "testGroup"; String consumer1 = "consumer1"; String consumer2 = "consumer2"; StreamEntryID initialId = new StreamEntryID(); exec(commandObjects.xgroupCreate(key, group, initialId, true)); StreamEntryID messageId = exec(commandObjects.xadd(key, StreamEntryID.NEW_ENTRY, Collections.singletonMap("field", "value2"))); // Consumer1 reads the message to make it pending Map stream = Collections.singletonMap(key, StreamEntryID.XREADGROUP_UNDELIVERED_ENTRY); List>> readEntries = exec( commandObjects.xreadGroup(group, consumer1, new XReadGroupParams().count(1), stream)); assertThat(readEntries, hasSize(1)); assertThat(readEntries.get(0).getKey(), equalTo(key)); assertThat(readEntries.get(0).getValue(), hasSize(1)); assertThat(readEntries.get(0).getValue().get(0).getID(), equalTo(messageId)); Thread.sleep(200); // Wait a bit // Claim the message for consumer2 List claimedMessages = exec( commandObjects.xclaim(key, group, consumer2, 1, new XClaimParams(), messageId)); assertThat(claimedMessages, hasSize(1)); assertThat(claimedMessages.get(0).getID(), equalTo(messageId)); } @Test public void testXClaimBinary() throws InterruptedException { String key = "testStream"; String group = "testGroup"; String consumer1 = "consumer1"; String consumer2 = "consumer2"; StreamEntryID initialId = new StreamEntryID(); exec(commandObjects.xgroupCreate(key, group, initialId, true)); StreamEntryID messageId = exec(commandObjects.xadd(key, StreamEntryID.NEW_ENTRY, Collections.singletonMap("field", "value2"))); // Consumer1 reads the message to make it pending Map stream = Collections.singletonMap(key, StreamEntryID.XREADGROUP_UNDELIVERED_ENTRY); List>> readEntries = exec( commandObjects.xreadGroup(group, consumer1, new XReadGroupParams().count(1), stream)); assertThat(readEntries, hasSize(1)); assertThat(readEntries.get(0).getKey(), equalTo(key)); assertThat(readEntries.get(0).getValue(), hasSize(1)); assertThat(readEntries.get(0).getValue().get(0).getID(), equalTo(messageId)); Thread.sleep(200); // Wait a bit byte[] bMessageId = messageId.toString().getBytes(); // Claim the message for consumer2 List claimedMessagesBytes = exec( commandObjects.xclaim(key.getBytes(), group.getBytes(), consumer2.getBytes(), 1, new XClaimParams(), bMessageId)); assertThat(claimedMessagesBytes, hasSize(1)); // Good luck with asserting the content of this! } @Test public void testXClaimJustId() throws InterruptedException { String key = "testStream"; String group = "testGroup"; String consumer1 = "consumer1"; String consumer2 = "consumer2"; StreamEntryID initialId = new StreamEntryID(); exec(commandObjects.xgroupCreate(key, group, initialId, true)); StreamEntryID messageId = exec(commandObjects.xadd(key, StreamEntryID.NEW_ENTRY, Collections.singletonMap("field", "value2"))); // Consumer1 reads the message to make it pending Map stream = Collections.singletonMap(key, StreamEntryID.XREADGROUP_UNDELIVERED_ENTRY); List>> readEntries = exec( commandObjects.xreadGroup(group, consumer1, new XReadGroupParams().count(1), stream)); assertThat(readEntries, hasSize(1)); assertThat(readEntries.get(0).getKey(), equalTo(key)); assertThat(readEntries.get(0).getValue(), hasSize(1)); assertThat(readEntries.get(0).getValue().get(0).getID(), equalTo(messageId)); Thread.sleep(200); // Wait a bit // Claim the message for consumer2 with String parameters List claimedMessagesString = exec( commandObjects.xclaimJustId(key, group, consumer2, 1, new XClaimParams(), messageId)); assertThat(claimedMessagesString, hasSize(1)); assertThat(claimedMessagesString.get(0), equalTo(messageId)); } @Test public void testXClaimJustIdBinary() throws InterruptedException { String key = "testStream"; String group = "testGroup"; String consumer1 = "consumer1"; String consumer2 = "consumer2"; StreamEntryID initialId = new StreamEntryID(); exec(commandObjects.xgroupCreate(key, group, initialId, true)); StreamEntryID messageId = exec(commandObjects.xadd(key, StreamEntryID.NEW_ENTRY, Collections.singletonMap("field", "value2"))); // Consumer1 reads the message to make it pending Map stream = Collections.singletonMap(key, StreamEntryID.XREADGROUP_UNDELIVERED_ENTRY); List>> readEntries = exec( commandObjects.xreadGroup(group, consumer1, new XReadGroupParams().count(1), stream)); assertThat(readEntries, hasSize(1)); assertThat(readEntries.get(0).getKey(), equalTo(key)); assertThat(readEntries.get(0).getValue(), hasSize(1)); assertThat(readEntries.get(0).getValue().get(0).getID(), equalTo(messageId)); Thread.sleep(200); // Wait a bit byte[] bMessageId = messageId.toString().getBytes(); // Claim the message for consumer2 with byte[] parameters List claimedMessagesBytes = exec( commandObjects.xclaimJustId(key.getBytes(), group.getBytes(), consumer2.getBytes(), 1, new XClaimParams(), bMessageId)); assertThat(claimedMessagesBytes, hasSize(1)); // Good luck with asserting the content of this! } @Test public void testXAutoClaim() throws InterruptedException { String streamKey = "testStream"; String group = "testGroup"; String consumer1 = "consumer1"; String consumer2 = "consumer2"; exec(commandObjects.xgroupCreate(streamKey, group, new StreamEntryID(), true)); Map messageBody = Collections.singletonMap("field", "value"); StreamEntryID initialEntryId = exec(commandObjects.xadd(streamKey, StreamEntryID.NEW_ENTRY, messageBody)); Map stream = Collections.singletonMap(streamKey, StreamEntryID.XREADGROUP_UNDELIVERED_ENTRY); exec(commandObjects.xreadGroup(group, consumer1, new XReadGroupParams().count(1), stream)); Thread.sleep(200); // Wait a bit StreamEntryID startId = new StreamEntryID(initialEntryId.getTime() - 1, initialEntryId.getSequence()); XAutoClaimParams params = new XAutoClaimParams().count(1); // Auto claim message for consumer2 Map.Entry> autoClaimResult = exec( commandObjects.xautoclaim(streamKey, group, consumer2, 1, startId, params)); assertThat(autoClaimResult.getValue(), hasSize(1)); assertThat(autoClaimResult.getValue().get(0).getFields(), equalTo(messageBody)); } @Test public void testXAutoClaimBinary() throws InterruptedException { byte[] streamKey = "testStream".getBytes(); byte[] group = "testGroup".getBytes(); byte[] consumer1 = "consumer1".getBytes(); byte[] consumer2 = "consumer2".getBytes(); exec(commandObjects.xgroupCreate(streamKey, group, new StreamEntryID().toString().getBytes(), true)); Map messageBody = Collections.singletonMap("field".getBytes(), "value".getBytes()); byte[] initialEntryId = exec(commandObjects.xadd(streamKey, new XAddParams(), messageBody)); Map.Entry entry = new AbstractMap.SimpleEntry<>(streamKey, StreamEntryID.XREADGROUP_UNDELIVERED_ENTRY.toString().getBytes()); exec(commandObjects.xreadGroup(group, consumer1, new XReadGroupParams().count(1), entry)); Thread.sleep(200); // Wait a bit StreamEntryID initialStreamEntryID = new StreamEntryID(new String(initialEntryId)); byte[] startId = new StreamEntryID(initialStreamEntryID.getTime() - 1, 0).toString().getBytes(); XAutoClaimParams params = new XAutoClaimParams().count(1); // Auto claim message for consumer2 in binary List autoClaimResultBinary = exec(commandObjects.xautoclaim(streamKey, group, consumer2, 1, startId, params)); assertThat(autoClaimResultBinary, not(empty())); } @Test public void testXAutoClaimJustId() throws InterruptedException { String streamKey = "testStream"; String group = "testGroup"; String consumer1 = "consumer1"; String consumer2 = "consumer2"; exec(commandObjects.xgroupCreate(streamKey, group, new StreamEntryID(), true)); Map messageBody = Collections.singletonMap("fieldSingle", "valueSingle"); StreamEntryID initialEntryId = exec(commandObjects.xadd(streamKey, StreamEntryID.NEW_ENTRY, messageBody)); Map stream = Collections.singletonMap(streamKey, StreamEntryID.XREADGROUP_UNDELIVERED_ENTRY); exec(commandObjects.xreadGroup(group, consumer1, new XReadGroupParams().count(1), stream)); Thread.sleep(200); // Wait a bit StreamEntryID startId = new StreamEntryID(initialEntryId.getTime() - 1, initialEntryId.getSequence()); XAutoClaimParams params = new XAutoClaimParams().count(1); Map.Entry> autoClaimResult = exec( commandObjects.xautoclaimJustId(streamKey, group, consumer2, 1, startId, params)); assertThat(autoClaimResult.getValue(), hasSize(1)); assertThat(autoClaimResult.getValue().get(0), equalTo(initialEntryId)); } @Test public void testXAutoClaimJustIdBinary() throws InterruptedException { byte[] streamKey = "testStream".getBytes(); byte[] group = "testGroup".getBytes(); byte[] consumer1 = "consumer1".getBytes(); byte[] consumer2 = "consumer2".getBytes(); exec(commandObjects.xgroupCreate(streamKey, group, new StreamEntryID().toString().getBytes(), true)); Map messageBody = Collections.singletonMap("fieldBinary".getBytes(), "valueBinary".getBytes()); byte[] initialEntryId = exec(commandObjects.xadd(streamKey, new XAddParams(), messageBody)); Map.Entry stream = new AbstractMap.SimpleEntry<>(streamKey, StreamEntryID.XREADGROUP_UNDELIVERED_ENTRY.toString().getBytes()); exec(commandObjects.xreadGroup(group, consumer1, new XReadGroupParams().count(1), stream)); Thread.sleep(200); // Wait a bit StreamEntryID initialStreamEntryID = new StreamEntryID(new String(initialEntryId)); byte[] startId = new StreamEntryID(initialStreamEntryID.getTime() - 1, 0).toString().getBytes(); XAutoClaimParams params = new XAutoClaimParams().count(1); List autoClaimResultBinary = exec( commandObjects.xautoclaimJustId(streamKey, group, consumer2, 1, startId, params)); assertThat(autoClaimResultBinary, not(empty())); } @Test public void testXInfoStream() { String streamKey = "testStreamInfo"; Map messageBody = Collections.singletonMap("fieldInfo", "valueInfo"); exec(commandObjects.xadd(streamKey, StreamEntryID.NEW_ENTRY, messageBody)); StreamInfo streamInfo = exec(commandObjects.xinfoStream(streamKey)); assertThat(streamInfo, notNullValue()); assertThat(streamInfo.getLength(), equalTo(1L)); assertThat(streamInfo.getFirstEntry().getFields(), equalTo(messageBody)); Object streamInfoBinary = exec(commandObjects.xinfoStream(streamKey.getBytes())); assertThat(streamInfoBinary, notNullValue()); } @Test public void testXInfoStreamFull() { String streamKey = "testStreamFullInfo"; Map messageBody = Collections.singletonMap("fieldFull", "valueFull"); exec(commandObjects.xadd(streamKey, StreamEntryID.NEW_ENTRY, messageBody)); StreamFullInfo streamFullInfo = exec(commandObjects.xinfoStreamFull(streamKey)); assertThat(streamFullInfo, notNullValue()); assertThat(streamFullInfo.getEntries(), not(empty())); assertThat(streamFullInfo.getEntries().get(0).getFields(), equalTo(messageBody)); StreamFullInfo streamFullInfoWithCount = exec(commandObjects.xinfoStreamFull(streamKey, 1)); assertThat(streamFullInfoWithCount, notNullValue()); assertThat(streamFullInfoWithCount.getEntries(), hasSize(1)); Object streamInfoBinaryFull = exec(commandObjects.xinfoStreamFull(streamKey.getBytes())); assertThat(streamInfoBinaryFull, notNullValue()); Object streamInfoBinaryFullWithCount = exec(commandObjects.xinfoStreamFull(streamKey.getBytes(), 1)); assertThat(streamInfoBinaryFullWithCount, notNullValue()); } @Test @Deprecated public void testXInfoConsumersWithActiveConsumers() { String streamKey = "testStreamWithConsumers"; String group = "testConsumerGroup"; String consumer1 = "consumer1"; String consumer2 = "consumer2"; Map messageBody1 = Collections.singletonMap("field1", "value1"); Map messageBody2 = Collections.singletonMap("field2", "value2"); exec(commandObjects.xadd(streamKey, StreamEntryID.NEW_ENTRY, messageBody1)); exec(commandObjects.xadd(streamKey, StreamEntryID.NEW_ENTRY, messageBody2)); exec(commandObjects.xgroupCreate(streamKey, group, new StreamEntryID(), true)); XReadGroupParams xReadGroupParams = new XReadGroupParams().count(1); Map stream = Collections.singletonMap(streamKey, StreamEntryID.XREADGROUP_UNDELIVERED_ENTRY); exec(commandObjects.xreadGroup(group, consumer1, xReadGroupParams, stream)); exec(commandObjects.xreadGroup(group, consumer2, xReadGroupParams, stream)); List consumersInfoList = exec(commandObjects.xinfoConsumers(streamKey, group)); assertThat(consumersInfoList, notNullValue()); assertThat(consumersInfoList, hasSize(2)); Optional consumersInfo1 = consumersInfoList.stream().filter(c -> c.getName().equals(consumer1)).findFirst(); Optional consumersInfo2 = consumersInfoList.stream().filter(c -> c.getName().equals(consumer2)).findFirst(); assertThat(consumersInfo1.isPresent(), equalTo(true)); assertThat(consumersInfo1.get().getPending(), equalTo(1L)); assertThat(consumersInfo2.isPresent(), equalTo(true)); assertThat(consumersInfo2.get().getPending(), equalTo(1L)); List consumerInfoList = exec(commandObjects.xinfoConsumers2(streamKey, group)); assertThat(consumerInfoList, notNullValue()); assertThat(consumerInfoList, hasSize(2)); Optional consumerInfo1 = consumerInfoList.stream().filter(c -> c.getName().equals(consumer1)).findFirst(); Optional consumerInfo2 = consumerInfoList.stream().filter(c -> c.getName().equals(consumer2)).findFirst(); assertThat(consumerInfo1.isPresent(), equalTo(true)); assertThat(consumerInfo1.get().getPending(), equalTo(1L)); assertThat(consumerInfo2.isPresent(), equalTo(true)); assertThat(consumerInfo2.get().getPending(), equalTo(1L)); List consumersInfoBinary = exec(commandObjects.xinfoConsumers(streamKey.getBytes(), group.getBytes())); assertThat(consumersInfoBinary, notNullValue()); } @Test @ConditionalOnEnv(value = TestEnvUtil.ENV_REDIS_ENTERPRISE, enabled = false) public void testXRead() { String streamKey1 = "testStream1"; String streamKey2 = "testStream2"; Map messageBody1 = Collections.singletonMap("field1", "value1"); Map messageBody2 = Collections.singletonMap("field2", "value2"); StreamEntryID messageId1 = exec(commandObjects.xadd(streamKey1, StreamEntryID.NEW_ENTRY, messageBody1)); StreamEntryID messageId2 = exec(commandObjects.xadd(streamKey2, StreamEntryID.NEW_ENTRY, messageBody2)); XReadParams params = XReadParams.xReadParams().count(1).block(1000); Map streams = new HashMap<>(); streams.put(streamKey1, new StreamEntryID()); streams.put(streamKey2, new StreamEntryID()); List>> xread = exec(commandObjects.xread(params, streams)); assertThat(xread, not(empty())); assertThat(xread.size(), equalTo(2)); assertThat(xread.get(0).getKey(), equalTo(streamKey1)); assertThat(xread.get(1).getKey(), equalTo(streamKey2)); assertThat(xread.get(0).getValue().get(0).getID(), equalTo(messageId1)); assertThat(xread.get(1).getValue().get(0).getID(), equalTo(messageId2)); assertThat(xread.get(0).getValue().get(0).getFields(), equalTo(messageBody1)); assertThat(xread.get(1).getValue().get(0).getFields(), equalTo(messageBody2)); byte[] streamKey1Binary = streamKey1.getBytes(); byte[] streamKey2Binary = streamKey2.getBytes(); Map.Entry stream1 = new AbstractMap.SimpleEntry<>(streamKey1Binary, new StreamEntryID().toString().getBytes()); Map.Entry stream2 = new AbstractMap.SimpleEntry<>(streamKey2Binary, new StreamEntryID().toString().getBytes()); List xreadBinary = exec(commandObjects.xread(params, stream1, stream2)); assertThat(xreadBinary, not(empty())); } @Test @ConditionalOnEnv(value = TestEnvUtil.ENV_REDIS_ENTERPRISE, enabled = false) public void testXReadAsMap() { String streamKey1 = "testStreamMap1"; String streamKey2 = "testStreamMap2"; Map messageBody1 = Collections.singletonMap("fieldMap1", "valueMap1"); Map messageBody2 = Collections.singletonMap("fieldMap2", "valueMap2"); exec(commandObjects.xadd(streamKey1, StreamEntryID.NEW_ENTRY, messageBody1)); exec(commandObjects.xadd(streamKey2, StreamEntryID.NEW_ENTRY, messageBody2)); XReadParams params = new XReadParams().count(1).block(1000); Map streams = new HashMap<>(); streams.put(streamKey1, new StreamEntryID()); streams.put(streamKey2, new StreamEntryID()); Map> xreadAsMap = exec(commandObjects.xreadAsMap(params, streams)); assertThat(xreadAsMap, notNullValue()); assertThat(xreadAsMap.keySet(), hasSize(2)); // Expecting keys for both streams assertThat(xreadAsMap.get(streamKey1).get(0).getFields(), equalTo(messageBody1)); assertThat(xreadAsMap.get(streamKey2).get(0).getFields(), equalTo(messageBody2)); } @Test public void testXReadGroupAsMap() { String streamKey = "testStreamGroupMap"; String group = "testGroupMap"; String consumer1 = "testConsumerMap1"; String consumer2 = "testConsumerMap2"; Map messageBody = Collections.singletonMap("fieldGroupMap", "valueGroupMap"); exec(commandObjects.xgroupCreate(streamKey, group, new StreamEntryID(), true)); StreamEntryID initialMessageId = exec(commandObjects.xadd(streamKey, StreamEntryID.NEW_ENTRY, messageBody)); StreamEntryID secondMessageId = exec(commandObjects.xadd(streamKey, StreamEntryID.NEW_ENTRY, messageBody)); XReadGroupParams params = new XReadGroupParams().count(1); Map streams = new HashMap<>(); streams.put(streamKey, StreamEntryID.XREADGROUP_UNDELIVERED_ENTRY); Map> xreadGroupConsumer1 = exec(commandObjects.xreadGroupAsMap(group, consumer1, params, streams)); assertThat(xreadGroupConsumer1, notNullValue()); assertThat(xreadGroupConsumer1.keySet(), hasSize(1)); assertThat(xreadGroupConsumer1.get(streamKey), not(empty())); assertThat(xreadGroupConsumer1.get(streamKey).get(0).getID(), equalTo(initialMessageId)); assertThat(xreadGroupConsumer1.get(streamKey).get(0).getFields(), equalTo(messageBody)); Map> xreadGroupConsumer2 = exec(commandObjects.xreadGroupAsMap(group, consumer2, params, streams)); assertThat(xreadGroupConsumer2, notNullValue()); assertThat(xreadGroupConsumer2.keySet(), hasSize(1)); // Expecting keys for the stream assertThat(xreadGroupConsumer2.get(streamKey), not(empty())); // Expecting at least one message assertThat(xreadGroupConsumer2.get(streamKey).get(0).getID(), equalTo(secondMessageId)); assertThat(xreadGroupConsumer2.get(streamKey).get(0).getFields(), equalTo(messageBody)); } } ================================================ FILE: src/test/java/redis/clients/jedis/commands/commandobjects/CommandObjectsStringCommandsTest.java ================================================ package redis.clients.jedis.commands.commandobjects; import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.closeTo; import static org.hamcrest.Matchers.contains; import static org.hamcrest.Matchers.equalTo; import static org.hamcrest.Matchers.greaterThan; import static org.hamcrest.Matchers.hasSize; import static org.hamcrest.Matchers.nullValue; import java.util.List; import java.util.stream.Stream; import io.redis.test.annotations.ConditionalOnEnv; import io.redis.test.annotations.EnabledOnCommand; import io.redis.test.annotations.SinceRedisVersion; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Tag; import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.Arguments; import org.junit.jupiter.params.provider.MethodSource; import redis.clients.jedis.RedisProtocol; import redis.clients.jedis.params.GetExParams; import redis.clients.jedis.params.LCSParams; import redis.clients.jedis.params.SetParams; import redis.clients.jedis.params.MSetExParams; import redis.clients.jedis.resps.LCSMatchResult; import redis.clients.jedis.util.TestEnvUtil; /** * Tests related to String commands. */ @Tag("integration") public class CommandObjectsStringCommandsTest extends CommandObjectsStandaloneTestBase { public CommandObjectsStringCommandsTest(RedisProtocol protocol) { super(protocol); } @Test public void testAppend() { String key = "testKey"; String value = "testValue"; String initialGet = exec(commandObjects.get(key)); assertThat(initialGet, nullValue()); Long append = exec(commandObjects.append(key, value)); assertThat(append, equalTo((long) value.length())); String getAfterAppend = exec(commandObjects.get(key)); assertThat(getAfterAppend, equalTo(value)); Long secondAppend = exec(commandObjects.append(key, value)); assertThat(secondAppend, equalTo((long) value.length() * 2)); String getAfterSecondAppend = exec(commandObjects.get(key)); assertThat(getAfterSecondAppend, equalTo(value + value)); } @Test public void testAppendBinary() { byte[] key = "testKeyBytes".getBytes(); byte[] value = "testValueBytes".getBytes(); byte[] initialGet = exec(commandObjects.get(key)); assertThat(initialGet, nullValue()); Long append = exec(commandObjects.append(key, value)); assertThat(append, equalTo((long) value.length)); byte[] getAfterAppend = exec(commandObjects.get(key)); assertThat(getAfterAppend, equalTo(value)); Long secondAppend = exec(commandObjects.append(key, value)); assertThat(secondAppend, equalTo((long) value.length * 2)); byte[] getAfterSecondAppend = exec(commandObjects.get(key)); byte[] expected = new byte[value.length + value.length]; System.arraycopy(value, 0, expected, 0, value.length); System.arraycopy(value, 0, expected, value.length, value.length); assertThat(getAfterSecondAppend, equalTo(expected)); } @Test public void testDecrementOperations() { String key = "testDecr"; exec(commandObjects.set(key, String.valueOf(10L))); String initialGet = exec(commandObjects.get(key)); assertThat(initialGet, equalTo("10")); Long decr = exec(commandObjects.decr(key)); assertThat(decr, equalTo(9L)); String getAfterDecr = exec(commandObjects.get(key)); assertThat(getAfterDecr, equalTo("9")); Long decrBy = exec(commandObjects.decrBy(key, 2L)); assertThat(decrBy, equalTo(7L)); String getAfterDecrBy = exec(commandObjects.get(key)); assertThat(getAfterDecrBy, equalTo("7")); } @Test public void testDecrementOperationsBinary() { byte[] key = "testDecrBytes".getBytes(); exec(commandObjects.set(key, String.valueOf(10L).getBytes())); byte[] initialGet = exec(commandObjects.get(key)); assertThat(initialGet, equalTo("10".getBytes())); Long decr = exec(commandObjects.decr(key)); assertThat(decr, equalTo(9L)); byte[] getAfterDecr = exec(commandObjects.get(key)); assertThat(getAfterDecr, equalTo("9".getBytes())); Long decrBy = exec(commandObjects.decrBy(key, 2L)); assertThat(decrBy, equalTo(7L)); byte[] getAfterDecrBy = exec(commandObjects.get(key)); assertThat(getAfterDecrBy, equalTo("7".getBytes())); } @Test public void testGetOperations() { String key = "testGet"; String value = "value"; exec(commandObjects.set(key, value)); String initialGet = exec(commandObjects.get(key)); assertThat(initialGet, equalTo(value)); String getDel = exec(commandObjects.getDel(key)); assertThat(getDel, equalTo(value)); String getAfterGetDel = exec(commandObjects.get(key)); assertThat(getAfterGetDel, nullValue()); // set again exec(commandObjects.set(key, value)); Long initialTtl = exec(commandObjects.ttl(key)); assertThat(initialTtl, equalTo(-1L)); GetExParams getExParams = GetExParams.getExParams().ex(10); String getEx = exec(commandObjects.getEx(key, getExParams)); assertThat(getEx, equalTo(value)); Long ttlAfterGetEx = exec(commandObjects.ttl(key)); assertThat(ttlAfterGetEx, greaterThan(0L)); } @Test public void testGetOperationsBinary() { byte[] key = "testGetBytes".getBytes(); byte[] value = "value".getBytes(); exec(commandObjects.set(key, value)); byte[] initialGet = exec(commandObjects.get(key)); assertThat(initialGet, equalTo(value)); byte[] getDel = exec(commandObjects.getDel(key)); assertThat(getDel, equalTo(value)); byte[] getAfterGetDel = exec(commandObjects.get(key)); assertThat(getAfterGetDel, nullValue()); // set again exec(commandObjects.set(key, value)); Long initialTtl = exec(commandObjects.ttl(key)); assertThat(initialTtl, equalTo(-1L)); GetExParams getExParams = GetExParams.getExParams().ex(10); byte[] getEx = exec(commandObjects.getEx(key, getExParams)); assertThat(getEx, equalTo(value)); Long ttlAfterGetEx = exec(commandObjects.ttl(key)); assertThat(ttlAfterGetEx, greaterThan(0L)); } @Test @Deprecated public void testGetSet() { String key = "testGetSet"; String initialValue = "initialValue"; String newValue = "newValue"; exec(commandObjects.set(key, initialValue)); String initialGet = exec(commandObjects.get(key)); assertThat(initialGet, equalTo(initialValue)); String getSet = exec(commandObjects.getSet(key, newValue)); assertThat(getSet, equalTo(initialValue)); String getAfterGetSet = exec(commandObjects.get(key)); assertThat(getAfterGetSet, equalTo(newValue)); } @Test @Deprecated public void testGetSetBinary() { byte[] key = "testGetSetBytes".getBytes(); byte[] initialValue = "initialValue".getBytes(); byte[] newValue = "newValue".getBytes(); exec(commandObjects.set(key, initialValue)); byte[] initialGet = exec(commandObjects.get(key)); assertThat(initialGet, equalTo(initialValue)); byte[] getSet = exec(commandObjects.getSet(key, newValue)); assertThat(getSet, equalTo(initialValue)); byte[] getAfterGetSet = exec(commandObjects.get(key)); assertThat(getAfterGetSet, equalTo(newValue)); } @Test public void testSetRangeAndGetRange() { String key = "testRange"; String initial = "Hello World"; String replacement = "Everyone"; long replacementOffset = 6L; exec(commandObjects.set(key, initial)); Long setRange = exec(commandObjects.setrange(key, replacementOffset, replacement)); assertThat(setRange, equalTo(14L)); // Length after replacement String getRange = exec(commandObjects.getrange(key, 0, -1)); assertThat(getRange, equalTo("Hello Everyone")); } @Test public void testSetRangeAndGetRangeBinary() { byte[] key = "testRangeBytes".getBytes(); byte[] initialValue = "Hello World".getBytes(); byte[] replacement = "Everyone".getBytes(); long replacementOffset = 6L; exec(commandObjects.set(key, initialValue)); Long setRange = exec(commandObjects.setrange(key, replacementOffset, replacement)); assertThat(setRange, equalTo(14L)); // Length after replacement byte[] getRange = exec(commandObjects.getrange(key, 0, -1)); assertThat(getRange, equalTo("Hello Everyone".getBytes())); } @Test public void testIncrementOperations() { String key = "testIncr"; exec(commandObjects.set(key, "0")); Long incr = exec(commandObjects.incr(key)); assertThat(incr, equalTo(1L)); String getAfterIncr = exec(commandObjects.get(key)); assertThat(getAfterIncr, equalTo("1")); Long incrBy = exec(commandObjects.incrBy(key, 5L)); assertThat(incrBy, equalTo(6L)); String getAfterIncrBy = exec(commandObjects.get(key)); assertThat(getAfterIncrBy, equalTo("6")); Double incrByFloat = exec(commandObjects.incrByFloat(key, 2.5)); assertThat(incrByFloat, closeTo(8.5, 0.001)); String getAfterIncrByFloat = exec(commandObjects.get(key)); assertThat(getAfterIncrByFloat, equalTo("8.5")); } @Test public void testIncrementOperationsBinary() { byte[] key = "testIncrBytes".getBytes(); exec(commandObjects.set(key, "0".getBytes())); Long incr = exec(commandObjects.incr(key)); assertThat(incr, equalTo(1L)); byte[] getAfterIncr = exec(commandObjects.get(key)); assertThat(getAfterIncr, equalTo("1".getBytes())); Long incrBy = exec(commandObjects.incrBy(key, 5L)); assertThat(incrBy, equalTo(6L)); byte[] getAfterIncrBy = exec(commandObjects.get(key)); assertThat(getAfterIncrBy, equalTo("6".getBytes())); Double incrByFloat = exec(commandObjects.incrByFloat(key, 2.5)); assertThat(incrByFloat, closeTo(8.5, 0.001)); byte[] getAfterIncrByFloat = exec(commandObjects.get(key)); assertThat(getAfterIncrByFloat, equalTo("8.5".getBytes())); } @Test @SinceRedisVersion(value = "7.0.0") @ConditionalOnEnv(value = TestEnvUtil.ENV_REDIS_ENTERPRISE, enabled = false) public void testLcs() { String keyA = "keyA"; String keyB = "keyB"; // "abcdfg" is the common substring String valueA = "abcdfgh"; String valueB = "abcdefg"; exec(commandObjects.set(keyA, valueA)); exec(commandObjects.set(keyB, valueB)); LCSMatchResult lcsLen = exec(commandObjects.lcs(keyA, keyB, new LCSParams().len())); assertThat(lcsLen.getLen(), equalTo(6L)); assertThat(lcsLen.getMatchString(), nullValue()); LCSMatchResult lcs = exec(commandObjects.lcs(keyA, keyB, new LCSParams())); assertThat(lcs.getLen(), equalTo(0L)); assertThat(lcs.getMatchString(), equalTo("abcdfg")); LCSMatchResult lcsMatches = exec( commandObjects.lcs(keyA, keyB, new LCSParams().idx().withMatchLen())); assertThat(lcsMatches.getLen(), equalTo(6L)); assertThat(lcsMatches.getMatchString(), nullValue()); assertThat(lcsMatches.getMatches(), hasSize(2)); LCSMatchResult.MatchedPosition match1 = lcsMatches.getMatches().get(0); assertThat(match1.getMatchLen(), equalTo(2L)); assertThat(match1.getA().getStart(), equalTo(4L)); assertThat(match1.getA().getEnd(), equalTo(5L)); assertThat(match1.getB().getStart(), equalTo(5L)); assertThat(match1.getB().getEnd(), equalTo(6L)); LCSMatchResult.MatchedPosition match2 = lcsMatches.getMatches().get(1); assertThat(match2.getMatchLen(), equalTo(4L)); assertThat(match2.getA().getStart(), equalTo(0L)); assertThat(match2.getA().getEnd(), equalTo(3L)); assertThat(match2.getB().getStart(), equalTo(0L)); assertThat(match2.getB().getEnd(), equalTo(3L)); } @Test @ConditionalOnEnv(value = TestEnvUtil.ENV_REDIS_ENTERPRISE, enabled = false) @SinceRedisVersion(value = "7.0.0") public void testLcsBinary() { byte[] keyA = "keyA".getBytes(); byte[] keyB = "keyB".getBytes(); // "abcdfg" is the common substring String valueA = "abcdfgh"; String valueB = "abcdefg"; exec(commandObjects.set(keyA, valueA.getBytes())); exec(commandObjects.set(keyB, valueB.getBytes())); LCSMatchResult lcsLen = exec(commandObjects.lcs(keyA, keyB, new LCSParams().len())); assertThat(lcsLen.getLen(), equalTo(6L)); assertThat(lcsLen.getMatchString(), nullValue()); LCSMatchResult lcs = exec(commandObjects.lcs(keyA, keyB, new LCSParams())); assertThat(lcs.getLen(), equalTo(0L)); assertThat(lcs.getMatchString(), equalTo("abcdfg")); LCSMatchResult lcsMatches = exec( commandObjects.lcs(keyA, keyB, new LCSParams().idx().withMatchLen())); assertThat(lcsMatches.getLen(), equalTo(6L)); assertThat(lcsMatches.getMatchString(), nullValue()); assertThat(lcsMatches.getMatches(), hasSize(2)); LCSMatchResult.MatchedPosition match1 = lcsMatches.getMatches().get(0); assertThat(match1.getMatchLen(), equalTo(2L)); assertThat(match1.getA().getStart(), equalTo(4L)); assertThat(match1.getA().getEnd(), equalTo(5L)); assertThat(match1.getB().getStart(), equalTo(5L)); assertThat(match1.getB().getEnd(), equalTo(6L)); LCSMatchResult.MatchedPosition match2 = lcsMatches.getMatches().get(1); assertThat(match2.getMatchLen(), equalTo(4L)); assertThat(match2.getA().getStart(), equalTo(0L)); assertThat(match2.getA().getEnd(), equalTo(3L)); assertThat(match2.getB().getStart(), equalTo(0L)); assertThat(match2.getB().getEnd(), equalTo(3L)); } @Test @ConditionalOnEnv(value = TestEnvUtil.ENV_REDIS_ENTERPRISE, enabled = false) public void testMgetMsetAndMsetnx() { String key1 = "key1"; String key2 = "key2"; String mset = exec(commandObjects.mset(key1, "value1", key2, "value2")); assertThat(mset, equalTo("OK")); List mget = exec(commandObjects.mget(key1, key2)); assertThat(mget, contains("value1", "value2")); Long msetNx = exec(commandObjects.msetnx(key1, "new1", key2, "new2")); assertThat(msetNx, equalTo(0L)); List mgetAfterMsetNx = exec(commandObjects.mget(key1, key2)); assertThat(mgetAfterMsetNx, contains("value1", "value2")); Long del = exec(commandObjects.del(key1, key2)); assertThat(del, equalTo(2L)); List mgetAfterDel = exec(commandObjects.mget(key1, key2)); assertThat(mgetAfterDel, contains(nullValue(), nullValue())); Long msetNxAfterDel = exec(commandObjects.msetnx(key1, "new1", key2, "new2")); assertThat(msetNxAfterDel, equalTo(1L)); List mgetAfterMsetNxAfterDel = exec(commandObjects.mget(key1, key2)); assertThat(mgetAfterMsetNxAfterDel, contains("new1", "new2")); } @Test @ConditionalOnEnv(value = TestEnvUtil.ENV_REDIS_ENTERPRISE, enabled = false) public void testMgetMsetAndMsetnxBinary() { byte[] key1 = "key1Bytes".getBytes(); byte[] key2 = "key2Bytes".getBytes(); String mset = exec(commandObjects.mset(key1, "value1".getBytes(), key2, "value2".getBytes())); assertThat(mset, equalTo("OK")); List mget = exec(commandObjects.mget(key1, key2)); assertThat(mget, contains("value1".getBytes(), "value2".getBytes())); Long msetNx = exec(commandObjects.msetnx(key1, "new1".getBytes(), key2, "new2".getBytes())); assertThat(msetNx, equalTo(0L)); List mgetAfterMsetNx = exec(commandObjects.mget(key1, key2)); assertThat(mgetAfterMsetNx, contains("value1".getBytes(), "value2".getBytes())); Long del = exec(commandObjects.del(key1, key2)); assertThat(del, equalTo(2L)); List mgetAfterDel = exec(commandObjects.mget(key1, key2)); assertThat(mgetAfterDel, contains(nullValue(), nullValue())); Long msetNxAfterDel = exec( commandObjects.msetnx(key1, "new1".getBytes(), key2, "new2".getBytes())); assertThat(msetNxAfterDel, equalTo(1L)); List mgetAfterMsetNxAfterDel = exec(commandObjects.mget(key1, key2)); assertThat(mgetAfterMsetNxAfterDel, contains("new1".getBytes(), "new2".getBytes())); } @Test public void testPsetexPttl() { String key = "tempKey"; long milliseconds = 1000L; String psetEx = exec(commandObjects.psetex(key, milliseconds, "tempValue")); assertThat(psetEx, equalTo("OK")); Long pttl = exec(commandObjects.pttl(key)); assertThat(pttl, greaterThan(0L)); } @Test public void testPsetexPttlBinary() { byte[] key = "tempKey".getBytes(); long milliseconds = 1000L; String psetEx = exec(commandObjects.psetex(key, milliseconds, "tempValue".getBytes())); assertThat(psetEx, equalTo("OK")); Long pttl = exec(commandObjects.pttl(key)); assertThat(pttl, greaterThan(0L)); } @Test public void testSetAndSetGet() { String key = "myKey"; String set = exec(commandObjects.set(key, "firstValue")); assertThat(set, equalTo("OK")); String initialGet = exec(commandObjects.get(key)); assertThat(initialGet, equalTo("firstValue")); SetParams setParams = new SetParams().ex(10); String setWithParams = exec(commandObjects.set(key, "secondValue", setParams)); assertThat(setWithParams, equalTo("OK")); String getAfterSetWithParams = exec(commandObjects.get(key)); assertThat(getAfterSetWithParams, equalTo("secondValue")); String setGet = exec(commandObjects.setGet(key, "thirdValue")); assertThat(setGet, equalTo("secondValue")); String getAfterSetGet = exec(commandObjects.get(key)); assertThat(getAfterSetGet, equalTo("thirdValue")); String setGetWithParams = exec(commandObjects.setGet(key, "finalValue", setParams)); assertThat(setGetWithParams, equalTo("thirdValue")); String finalGet = exec(commandObjects.get(key)); assertThat(finalGet, equalTo("finalValue")); } @Test public void testSetAndSetGetBinary() { byte[] key = "myKeyBytes".getBytes(); String set = exec(commandObjects.set(key, "firstValue".getBytes())); assertThat(set, equalTo("OK")); byte[] getAfterSet = exec(commandObjects.get(key)); assertThat(getAfterSet, equalTo("firstValue".getBytes())); SetParams setParams = new SetParams().ex(10); String setWithParams = exec(commandObjects.set(key, "secondValue".getBytes(), setParams)); assertThat(setWithParams, equalTo("OK")); byte[] getAfterSetWithParams = exec(commandObjects.get(key)); assertThat(getAfterSetWithParams, equalTo("secondValue".getBytes())); byte[] setGet = exec(commandObjects.setGet(key, "thirdValue".getBytes())); assertThat(setGet, equalTo("secondValue".getBytes())); byte[] getAfterSetGet = exec(commandObjects.get(key)); assertThat(getAfterSetGet, equalTo("thirdValue".getBytes())); byte[] setGetWithParams = exec(commandObjects.setGet(key, "finalValue".getBytes(), setParams)); assertThat(setGetWithParams, equalTo("thirdValue".getBytes())); byte[] getAfterSetGetWithParams = exec(commandObjects.get(key)); assertThat(getAfterSetGetWithParams, equalTo("finalValue".getBytes())); } @Test public void testSetnxAndSetexWithGets() { String key = "uniqueKey"; Long setNx = exec(commandObjects.setnx(key, "helloWorld")); assertThat(setNx, equalTo(1L)); String getAfterSetNx = exec(commandObjects.get(key)); assertThat(getAfterSetNx, equalTo("helloWorld")); String setEx = exec(commandObjects.setex(key, 10L, "newValue")); assertThat(setEx, equalTo("OK")); String getAfterSetEx = exec(commandObjects.get(key)); assertThat(getAfterSetEx, equalTo("newValue")); Long setNxAgain = exec(commandObjects.setnx(key, "anotherNewValue")); assertThat(setNxAgain, equalTo(0L)); String getAfterSetNxAgain = exec(commandObjects.get(key)); assertThat(getAfterSetNxAgain, equalTo("newValue")); } @Test public void testSetnxAndSetexWithGetsBinary() { byte[] key = "uniqueKeyBytes".getBytes(); Long setNx = exec(commandObjects.setnx(key, "helloWorld".getBytes())); assertThat(setNx, equalTo(1L)); byte[] getAfterSetNx = exec(commandObjects.get(key)); assertThat(getAfterSetNx, equalTo("helloWorld".getBytes())); String setEx = exec(commandObjects.setex(key, 10L, "newValue".getBytes())); assertThat(setEx, equalTo("OK")); byte[] getAfterSetEx = exec(commandObjects.get(key)); assertThat(getAfterSetEx, equalTo("newValue".getBytes())); Long setNxAgain = exec(commandObjects.setnx(key, "anotherNewValueBytes".getBytes())); assertThat(setNxAgain, equalTo(0L)); byte[] getAfterSetNxAgain = exec(commandObjects.get(key)); assertThat(getAfterSetNxAgain, equalTo("newValue".getBytes())); } @Test @ConditionalOnEnv(value = TestEnvUtil.ENV_REDIS_ENTERPRISE, enabled = false) public void testSubstrAndStrlen() { String key = "testKey"; String value = "HelloWorld"; int start = 1; int end = 5; // end is inclusive String fragment = "elloW"; exec(commandObjects.set(key, value)); String substr = exec(commandObjects.substr(key, start, end)); assertThat(substr, equalTo(fragment)); byte[] substrBinary = exec(commandObjects.substr(key.getBytes(), start, end)); assertThat(substrBinary, equalTo(fragment.getBytes())); Long strlen = exec(commandObjects.strlen(key)); assertThat(strlen, equalTo((long) value.length())); Long strlenBinary = exec(commandObjects.strlen(key.getBytes())); assertThat(strlenBinary, equalTo((long) value.length())); } // MSETEX NX + expiration matrix static Stream msetexNxArgsProvider() { return Stream.of(Arguments.of("EX", new MSetExParams().nx().ex(5)), Arguments.of("PX", new MSetExParams().nx().px(5000)), Arguments.of("EXAT", new MSetExParams().nx().exAt(System.currentTimeMillis() / 1000 + 5)), Arguments.of("PXAT", new MSetExParams().nx().pxAt(System.currentTimeMillis() + 5000)), Arguments.of("KEEPTTL", new MSetExParams().nx().keepTtl())); } @ParameterizedTest(name = "MSETEX NX + {0}") @MethodSource("msetexNxArgsProvider") @EnabledOnCommand(value = "MSETEX") public void testMsetexNx_parametrized(String optionLabel, MSetExParams params) { String k1 = "{t}msetex:k1"; String k2 = "{t}msetex:k2"; Boolean result = exec(commandObjects.msetex(params, k1, "v1", k2, "v2")); assertThat(result, equalTo(true)); Long ttl = exec(commandObjects.ttl(k1)); if ("KEEPTTL".equals(optionLabel)) { assertThat(ttl, equalTo(-1L)); } else { assertThat(ttl, greaterThan(0L)); } } } ================================================ FILE: src/test/java/redis/clients/jedis/commands/commandobjects/CommandObjectsTDigestCommandsTest.java ================================================ package redis.clients.jedis.commands.commandobjects; import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.closeTo; import static org.hamcrest.Matchers.equalTo; import static org.hamcrest.Matchers.greaterThanOrEqualTo; import static org.hamcrest.Matchers.hasEntry; import static org.hamcrest.Matchers.hasKey; import static org.hamcrest.Matchers.lessThanOrEqualTo; import static org.hamcrest.Matchers.notANumber; import static org.hamcrest.Matchers.notNullValue; import java.util.List; import java.util.Map; import org.junit.jupiter.api.Test; import redis.clients.jedis.RedisProtocol; import redis.clients.jedis.bloom.TDigestMergeParams; /** * Tests related to T-Digest commands. */ public class CommandObjectsTDigestCommandsTest extends CommandObjectsModulesTestBase { public CommandObjectsTDigestCommandsTest(RedisProtocol protocol) { super(protocol); } @Test public void testTDigestAddMinMax() { String key = "testTDigest"; String create = exec(commandObjects.tdigestCreate(key)); assertThat(create, equalTo("OK")); String add = exec(commandObjects.tdigestAdd(key, 1.0, 2.0, 3.0, 4.0, 5.0)); assertThat(add, equalTo("OK")); Double minValue = exec(commandObjects.tdigestMin(key)); assertThat(minValue, equalTo(1.0)); Double maxValue = exec(commandObjects.tdigestMax(key)); assertThat(maxValue, equalTo(5.0)); } @Test public void testTDigestMerge() { String destinationKey = "testTDigestMergeDest"; String sourceKey1 = "testTDigestSource1"; String sourceKey2 = "testTDigestSource2"; String create1 = exec(commandObjects.tdigestCreate(sourceKey1)); assertThat(create1, equalTo("OK")); String create2 = exec(commandObjects.tdigestCreate(sourceKey2)); assertThat(create2, equalTo("OK")); String add1 = exec(commandObjects.tdigestAdd(sourceKey1, 1.0, 2.0)); assertThat(add1, equalTo("OK")); String add2 = exec(commandObjects.tdigestAdd(sourceKey2, 3.0, 4.0)); assertThat(add2, equalTo("OK")); String merge = exec(commandObjects.tdigestMerge(destinationKey, sourceKey1, sourceKey2)); assertThat(merge, equalTo("OK")); Double minAfterMerge = exec(commandObjects.tdigestMin(destinationKey)); assertThat(minAfterMerge, equalTo(1.0)); Double maxAfterMerge = exec(commandObjects.tdigestMax(destinationKey)); assertThat(maxAfterMerge, equalTo(4.0)); } @Test public void testTDigestMergeWithParams() { String destinationKey = "testTDigestMergeDestParams"; String sourceKey1 = "testTDigestSource1Params"; String sourceKey2 = "testTDigestSource2Params"; TDigestMergeParams mergeParams = new TDigestMergeParams().compression(100); String create1 = exec(commandObjects.tdigestCreate(sourceKey1, 100)); assertThat(create1, equalTo("OK")); String create2 = exec(commandObjects.tdigestCreate(sourceKey2, 100)); assertThat(create2, equalTo("OK")); String add1 = exec(commandObjects.tdigestAdd(sourceKey1, 10.0, 20.0)); assertThat(add1, equalTo("OK")); String add2 = exec(commandObjects.tdigestAdd(sourceKey2, 30.0, 40.0)); assertThat(add2, equalTo("OK")); String merge = exec(commandObjects.tdigestMerge(mergeParams, destinationKey, sourceKey1, sourceKey2)); assertThat(merge, equalTo("OK")); Double minAfterMerge = exec(commandObjects.tdigestMin(destinationKey)); assertThat(minAfterMerge, equalTo(10.0)); Double maxAfterMerge = exec(commandObjects.tdigestMax(destinationKey)); assertThat(maxAfterMerge, equalTo(40.0)); } @Test public void testTDigestReset() { String key = "testTDigest"; String create = exec(commandObjects.tdigestCreate(key)); assertThat(create, equalTo("OK")); String add = exec(commandObjects.tdigestAdd(key, 5.0, 10.0, 15.0)); assertThat(add, equalTo("OK")); Double minBeforeReset = exec(commandObjects.tdigestMin(key)); assertThat(minBeforeReset, equalTo(5.0)); Double maxBeforeReset = exec(commandObjects.tdigestMax(key)); assertThat(maxBeforeReset, equalTo(15.0)); String reset = exec(commandObjects.tdigestReset(key)); assertThat(reset, equalTo("OK")); Double minAfterReset = exec(commandObjects.tdigestMin(key)); assertThat(minAfterReset, notANumber()); Double maxAfterReset = exec(commandObjects.tdigestMax(key)); assertThat(maxAfterReset, notANumber()); } @Test public void testTDigestCdf() { String key = "testTDigest"; String create = exec(commandObjects.tdigestCreate(key)); assertThat(create, equalTo("OK")); String add = exec(commandObjects.tdigestAdd(key, 1.0, 2.0, 3.0, 4.0, 5.0)); assertThat(add, equalTo("OK")); List cdf = exec(commandObjects.tdigestCDF(key, 1.0, 3.0, 5.0)); assertThat(cdf, notNullValue()); assertThat(cdf.size(), equalTo(3)); assertThat(cdf.get(0), lessThanOrEqualTo(0.2)); assertThat(cdf.get(1), lessThanOrEqualTo(0.6)); assertThat(cdf.get(2), lessThanOrEqualTo(1.0)); } @Test public void testTDigestQuantile() { String key = "testTDigest"; String create = exec(commandObjects.tdigestCreate(key)); assertThat(create, equalTo("OK")); String add = exec(commandObjects.tdigestAdd(key, 1.0, 2.0, 3.0, 4.0, 5.0)); assertThat(add, equalTo("OK")); List quantile = exec(commandObjects.tdigestQuantile(key, 0.25, 0.5, 0.75)); assertThat(quantile, notNullValue()); assertThat(quantile.size(), equalTo(3)); assertThat(quantile.get(0), lessThanOrEqualTo(2.0)); assertThat(quantile.get(1), lessThanOrEqualTo(3.0)); assertThat(quantile.get(2), lessThanOrEqualTo(4.0)); } @Test public void testTDigestTrimmedMean() { String key = "testTDigest"; String create = exec(commandObjects.tdigestCreate(key)); assertThat(create, equalTo("OK")); String add = exec(commandObjects.tdigestAdd(key, 1.0, 2.0, 3.0, 4.0, 5.0)); assertThat(add, equalTo("OK")); Double trimmedMean = exec(commandObjects.tdigestTrimmedMean(key, 0.1, 0.9)); assertThat(trimmedMean, notNullValue()); assertThat(trimmedMean, lessThanOrEqualTo(4.0)); assertThat(trimmedMean, greaterThanOrEqualTo(2.0)); } @Test public void testTDigestRankAndRevRank() { String key = "testTDigest"; String create = exec(commandObjects.tdigestCreate(key)); assertThat(create, equalTo("OK")); String add = exec(commandObjects.tdigestAdd(key, 1.0, 2.0, 3.0, 4.0, 5.0)); assertThat(add, equalTo("OK")); List rank = exec(commandObjects.tdigestRank(key, 1.0, 3.0, 5.0)); assertThat(rank, notNullValue()); assertThat(rank.size(), equalTo(3)); assertThat(rank.get(0), lessThanOrEqualTo(1L)); assertThat(rank.get(1), lessThanOrEqualTo(3L)); assertThat(rank.get(2), lessThanOrEqualTo(5L)); List revRank = exec(commandObjects.tdigestRevRank(key, 1.0, 3.0, 5.0)); assertThat(revRank, notNullValue()); assertThat(revRank.size(), equalTo(3)); assertThat(revRank.get(0), greaterThanOrEqualTo(4L)); assertThat(revRank.get(1), greaterThanOrEqualTo(2L)); assertThat(revRank.get(2), greaterThanOrEqualTo(0L)); } @Test public void testTDigestByRankAndByRevRank() { String key = "testTDigest"; String create = exec(commandObjects.tdigestCreate(key)); assertThat(create, equalTo("OK")); String add = exec(commandObjects.tdigestAdd(key, 0.5, 1.5, 2.5, 3.5, 4.5)); assertThat(add, equalTo("OK")); List byRank = exec(commandObjects.tdigestByRank(key, 0, 2, 4)); assertThat(byRank, notNullValue()); assertThat(byRank.size(), equalTo(3)); assertThat(byRank.get(0), closeTo(0.5, 0.1)); assertThat(byRank.get(1), closeTo(2.5, 0.1)); assertThat(byRank.get(2), closeTo(4.5, 0.1)); List byRevRank = exec(commandObjects.tdigestByRevRank(key, 0, 2, 4)); assertThat(byRevRank, notNullValue()); assertThat(byRevRank.size(), equalTo(3)); assertThat(byRevRank.get(0), closeTo(4.5, 0.1)); assertThat(byRevRank.get(1), closeTo(2.5, 0.1)); assertThat(byRevRank.get(2), closeTo(0.5, 0.1)); } @Test public void testTDigestInfo() { String key = "testTDigest"; String create = exec(commandObjects.tdigestCreate(key)); assertThat(create, equalTo("OK")); String add = exec(commandObjects.tdigestAdd(key, 1.0, 2.0, 3.0)); assertThat(add, equalTo("OK")); Map info = exec(commandObjects.tdigestInfo(key)); assertThat(info, notNullValue()); assertThat(info, hasKey("Compression")); assertThat(info, hasEntry("Observations", 3L)); } } ================================================ FILE: src/test/java/redis/clients/jedis/commands/commandobjects/CommandObjectsTestBase.java ================================================ package redis.clients.jedis.commands.commandobjects; import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.equalTo; import java.util.Collection; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Tag; import org.junit.jupiter.params.ParameterizedClass; import org.junit.jupiter.params.provider.MethodSource; import redis.clients.jedis.*; import redis.clients.jedis.commands.CommandsTestsParameters; import redis.clients.jedis.executors.CommandExecutor; import redis.clients.jedis.executors.DefaultCommandExecutor; import redis.clients.jedis.providers.ConnectionProvider; import redis.clients.jedis.providers.PooledConnectionProvider; /** * Base class for CommandObjects tests. The tests are parameterized to run with * several versions of RESP. The idea is to test commands at this low level, using * a simple executor. Higher level concepts like {@link redis.clients.jedis.UnifiedJedis}, * or {@link redis.clients.jedis.PipeliningBase} can be tested separately with mocks. *

* This class provides the basic setup, except the {@link HostAndPort} for connecting * to a running Redis server. That one is provided by abstract subclasses, depending * on if a Redis Stack server is needed, or a standalone suffices. *

* In principle all subclasses of this class should be parameterized tests, * to run with several versions of RESP. {@link CommandsTestsParameters#respVersions} */ @Tag("integration") @ParameterizedClass @MethodSource("redis.clients.jedis.commands.CommandsTestsParameters#respVersions") public abstract class CommandObjectsTestBase { /** * RESP protocol used in the tests. Injected from subclasses. */ protected final RedisProtocol protocol; /** * Host and port of the Redis server to connect to. Injected from subclasses. */ protected final EndpointConfig endpoint; /** * The {@link CommandObjects} to use for the tests. This is the subject-under-test. */ protected final CommandObjects commandObjects; /** * A {@link CommandExecutor} that can execute commands against the running Redis server. * Not exposed to subclasses, which should use a convenience method instead. */ private CommandExecutor commandExecutor; public CommandObjectsTestBase(RedisProtocol protocol, EndpointConfig endpoint) { this.protocol = protocol; this.endpoint = endpoint; commandObjects = new CommandObjects(); commandObjects.setProtocol(protocol); } @BeforeEach public void setUp() { // Configure a default command executor. DefaultJedisClientConfig clientConfig = endpoint.getClientConfigBuilder().protocol(protocol) .build(); ConnectionProvider connectionProvider = new PooledConnectionProvider(endpoint.getHostAndPort(), clientConfig); commandExecutor = new DefaultCommandExecutor(connectionProvider); // Cleanup before each test. assertThat(exec(commandObjects.flushAll()), equalTo("OK")); } /** * Convenience method for subclasses, for running any {@link CommandObject}. */ protected T exec(CommandObject commandObject) { return commandExecutor.executeCommand(commandObject); } } ================================================ FILE: src/test/java/redis/clients/jedis/commands/commandobjects/CommandObjectsTimeSeriesCommandsTest.java ================================================ package redis.clients.jedis.commands.commandobjects; import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.closeTo; import static org.hamcrest.Matchers.contains; import static org.hamcrest.Matchers.containsInAnyOrder; import static org.hamcrest.Matchers.empty; import static org.hamcrest.Matchers.equalTo; import static org.hamcrest.Matchers.hasEntry; import static org.hamcrest.Matchers.hasItems; import static org.hamcrest.Matchers.hasSize; import static org.hamcrest.Matchers.notNullValue; import static org.hamcrest.Matchers.nullValue; import java.util.AbstractMap; import java.util.List; import java.util.Map; import java.util.stream.Collectors; import io.redis.test.annotations.ConditionalOnEnv; import org.junit.jupiter.api.Test; import redis.clients.jedis.RedisProtocol; import redis.clients.jedis.timeseries.AggregationType; import redis.clients.jedis.timeseries.TSAlterParams; import redis.clients.jedis.timeseries.TSCreateParams; import redis.clients.jedis.timeseries.TSElement; import redis.clients.jedis.timeseries.TSGetParams; import redis.clients.jedis.timeseries.TSInfo; import redis.clients.jedis.timeseries.TSMGetElement; import redis.clients.jedis.timeseries.TSMGetParams; import redis.clients.jedis.timeseries.TSMRangeElements; import redis.clients.jedis.timeseries.TSMRangeParams; import redis.clients.jedis.timeseries.TSRangeParams; import redis.clients.jedis.util.TestEnvUtil; /** * Tests related to Time series commands. */ public class CommandObjectsTimeSeriesCommandsTest extends CommandObjectsModulesTestBase { public CommandObjectsTimeSeriesCommandsTest(RedisProtocol protocol) { super(protocol); } @Test public void testTsAddAndRange() throws InterruptedException { String key = "testTs"; String create = exec(commandObjects.tsCreate(key)); assertThat(create, equalTo("OK")); long currentTime = System.currentTimeMillis(); double[] values = { 42.0, 43.0, 44.0 }; for (double value : values) { Long add = exec(commandObjects.tsAdd(key, value)); assertThat(add, notNullValue()); // short delay to avoid the same timestamp Thread.sleep(10); } List range = exec(commandObjects.tsRange(key, currentTime - 1000, currentTime + 1000)); assertThat(range, hasSize(values.length)); for (int i = 0; i < values.length; i++) { assertThat(range.get(i).getValue(), equalTo(values[i])); } } @Test public void testTsAddWithTimestampDelAndRangeWithPreDeleteAssert() { String key = "testTs"; String create = exec(commandObjects.tsCreate(key)); assertThat(create, equalTo("OK")); long timestamp1 = 1000; double value1 = 42.0; long timestamp2 = 2000; double value2 = 43.0; Long add1 = exec(commandObjects.tsAdd(key, timestamp1, value1)); assertThat(add1, notNullValue()); Long add2 = exec(commandObjects.tsAdd(key, timestamp2, value2)); assertThat(add2, notNullValue()); List preDelRange = exec(commandObjects.tsRange(key, timestamp1 - 500, timestamp2 + 500)); assertThat(preDelRange, hasSize(2)); assertThat(preDelRange.get(0).getValue(), equalTo(value1)); assertThat(preDelRange.get(1).getValue(), equalTo(value2)); Long del = exec(commandObjects.tsDel(key, timestamp1 - 500, timestamp1 + 500)); assertThat(del, equalTo(1L)); List postDelRange = exec(commandObjects.tsRange(key, timestamp1 - 500, timestamp2 + 500)); assertThat(postDelRange, hasSize(1)); assertThat(postDelRange.get(0).getValue(), equalTo(value2)); } @Test public void testTsAddWithParams() { String key = "testTs"; long timestamp = System.currentTimeMillis(); double value = 42.0; TSCreateParams createParams = new TSCreateParams() .uncompressed().retention(86400000); Long add = exec(commandObjects.tsAdd(key, timestamp, value, createParams)); assertThat(add, notNullValue()); List range = exec(commandObjects.tsRange(key, timestamp - 1000, timestamp + 1000)); assertThat(range, hasSize(1)); assertThat(range.get(0).getTimestamp(), equalTo(timestamp)); assertThat(range.get(0).getValue(), equalTo(value)); } @Test @ConditionalOnEnv(value = TestEnvUtil.ENV_REDIS_ENTERPRISE, enabled = false) public void testTsMAdd() { String key1 = "testTsMAdd1"; String key2 = "testTsMAdd2"; String create1 = exec(commandObjects.tsCreate(key1)); assertThat(create1, equalTo("OK")); String create2 = exec(commandObjects.tsCreate(key2)); assertThat(create2, equalTo("OK")); long timestamp1 = 2000; long timestamp2 = 3000; Map.Entry entry1 = new AbstractMap.SimpleEntry<>(key1, new TSElement(timestamp1, 42.0)); Map.Entry entry2 = new AbstractMap.SimpleEntry<>(key2, new TSElement(timestamp2, 43.0)); List mAdd = exec(commandObjects.tsMAdd(entry1, entry2)); assertThat(mAdd, contains(timestamp1, timestamp2)); List range1 = exec(commandObjects.tsRange(key1, timestamp1 - 1000, timestamp1 + 1000)); assertThat(range1, hasSize(1)); assertThat(range1.get(0).getTimestamp(), equalTo(timestamp1)); assertThat(range1.get(0).getValue(), equalTo(42.0)); List range2 = exec(commandObjects.tsRange(key2, timestamp2 - 1000, timestamp2 + 1000)); assertThat(range2, hasSize(1)); assertThat(range2.get(0).getTimestamp(), equalTo(timestamp2)); assertThat(range2.get(0).getValue(), equalTo(43.0)); } @Test public void testTsIncrByAndDecrBy() throws InterruptedException { String key = "testTs"; String create = exec(commandObjects.tsCreate(key)); assertThat(create, equalTo("OK")); double initialValue = 10.0; double incrementValue = 5.0; double decrementValue = 3.0; Long initialAdd = exec(commandObjects.tsAdd(key, System.currentTimeMillis(), initialValue)); assertThat(initialAdd, notNullValue()); Thread.sleep(50); Long incr = exec(commandObjects.tsIncrBy(key, incrementValue)); assertThat(incr, notNullValue()); Thread.sleep(50); Long decr = exec(commandObjects.tsDecrBy(key, decrementValue)); assertThat(decr, notNullValue()); TSElement latestElement = exec(commandObjects.tsGet(key)); double expectedValue = initialValue + incrementValue - decrementValue; assertThat(latestElement.getValue(), equalTo(expectedValue)); List range = exec(commandObjects.tsRange( key, latestElement.getTimestamp() - 1000, latestElement.getTimestamp() + 1000)); assertThat(range.stream().map(TSElement::getValue).collect(Collectors.toList()), contains( closeTo(initialValue, 0.001), closeTo(initialValue + incrementValue, 0.001), closeTo(expectedValue, 0.001))); } @Test public void testTsIncrByAndDecrByWithTimestamp() { String key = "testTs"; String create = exec(commandObjects.tsCreate(key)); assertThat(create, equalTo("OK")); double initialValue = 10.0; double incrementValue = 5.0; double decrementValue = 3.0; long initialTimestamp = System.currentTimeMillis(); Long initialAdd = exec(commandObjects.tsAdd(key, initialTimestamp, initialValue)); assertThat(initialAdd, equalTo(initialTimestamp)); long incrementTimestamp = initialTimestamp + 1000; Long incr = exec(commandObjects.tsIncrBy(key, incrementValue, incrementTimestamp)); assertThat(incr, equalTo(incrementTimestamp)); long decrementTimestamp = incrementTimestamp + 1000; Long decr = exec(commandObjects.tsDecrBy(key, decrementValue, decrementTimestamp)); assertThat(decr, equalTo(decrementTimestamp)); List range = exec(commandObjects.tsRange( key, initialTimestamp - 1000, decrementTimestamp + 1000)); assertThat(range.stream().map(TSElement::getValue).collect(Collectors.toList()), contains( closeTo(initialValue, 0.001), closeTo(initialValue + incrementValue, 0.001), closeTo(initialValue + incrementValue - decrementValue, 0.001))); } @Test public void testTsRange() { String key = "tsKey"; String create = exec(commandObjects.tsCreate(key)); assertThat(create, equalTo("OK")); long fromTimestamp = 1000L; long toTimestamp = 2000L; List initialRange = exec(commandObjects.tsRange(key, fromTimestamp - 100, toTimestamp + 100)); assertThat(initialRange, hasSize(0)); exec(commandObjects.tsAdd(key, fromTimestamp, 1.0)); exec(commandObjects.tsAdd(key, toTimestamp, 2.0)); List elementsByTimestamp = exec(commandObjects.tsRange(key, fromTimestamp - 100, toTimestamp + 100)); assertThat(elementsByTimestamp.stream().map(TSElement::getValue).collect(Collectors.toList()), contains( closeTo(1.0, 0.001), closeTo(2.0, 0.001))); TSRangeParams rangeParams = new TSRangeParams(fromTimestamp - 100, toTimestamp + 100); List elementsByParams = exec(commandObjects.tsRange(key, rangeParams)); assertThat(elementsByParams.stream().map(TSElement::getValue).collect(Collectors.toList()), contains( closeTo(1.0, 0.001), closeTo(2.0, 0.001))); } @Test public void testTsRevRange() { String key = "tsRevKey"; String create = exec(commandObjects.tsCreate(key)); assertThat(create, equalTo("OK")); long fromTimestamp = 1000L; long toTimestamp = 2000L; List initialRevRange = exec(commandObjects.tsRevRange(key, fromTimestamp - 100, toTimestamp + 100)); assertThat(initialRevRange, hasSize(0)); exec(commandObjects.tsAdd(key, fromTimestamp, 1.0)); exec(commandObjects.tsAdd(key, toTimestamp, 2.0)); List elementsByTimestamp = exec(commandObjects.tsRevRange(key, fromTimestamp - 100, toTimestamp + 100)); assertThat(elementsByTimestamp.stream().map(TSElement::getValue).collect(Collectors.toList()), contains( closeTo(2.0, 0.001), closeTo(1.0, 0.001))); TSRangeParams rangeParams = new TSRangeParams(fromTimestamp - 100, toTimestamp + 100); List elementsByParams = exec(commandObjects.tsRevRange(key, rangeParams)); assertThat(elementsByParams.stream().map(TSElement::getValue).collect(Collectors.toList()), contains( closeTo(2.0, 0.001), closeTo(1.0, 0.001))); } @Test public void testTsMRangeCommands() { String key1 = "tsMRangeKey1"; String key2 = "tsMRangeKey2"; long fromTimestamp = 1000L; long toTimestamp = 3000L; String filter = "sensor_id=1234"; Map initialMRange = exec(commandObjects.tsMRange( fromTimestamp - 100, toTimestamp + 100, filter)); assertThat(initialMRange.entrySet(), hasSize(0)); TSCreateParams createParams = new TSCreateParams() .uncompressed().label("sensor_id", "1234"); exec(commandObjects.tsAdd(key1, fromTimestamp, 1.0, createParams)); exec(commandObjects.tsAdd(key1, fromTimestamp + 500, 1.5, createParams)); exec(commandObjects.tsAdd(key2, toTimestamp - 500, 2.5, createParams)); exec(commandObjects.tsAdd(key2, toTimestamp, 2.0, createParams)); Map range = exec(commandObjects.tsMRange( fromTimestamp - 100, toTimestamp + 100, filter)); assertThat(range.keySet(), hasItems(key1, key2)); assertThat(range.get(key1).getElements().stream().map(TSElement::getValue).collect(Collectors.toList()), containsInAnyOrder(closeTo(1.0, 0.001), closeTo(1.5, 0.001))); assertThat(range.get(key2).getElements().stream().map(TSElement::getValue).collect(Collectors.toList()), containsInAnyOrder(closeTo(2.5, 0.001), closeTo(2.0, 0.001))); Map revRangeResult = exec(commandObjects.tsMRevRange( fromTimestamp - 100, toTimestamp + 100, filter)); assertThat(revRangeResult.keySet(), hasItems(key1, key2)); assertThat(revRangeResult.get(key1).getElements().stream().map(TSElement::getValue).collect(Collectors.toList()), containsInAnyOrder(closeTo(1.5, 0.001), closeTo(1.0, 0.001))); assertThat(revRangeResult.get(key2).getElements().stream().map(TSElement::getValue).collect(Collectors.toList()), containsInAnyOrder(closeTo(2.0, 0.001), closeTo(2.5, 0.001))); TSMRangeParams multiRangeParamsA = new TSMRangeParams(fromTimestamp - 100, toTimestamp + 100).filter(filter); Map rangeResultWithParams = exec(commandObjects.tsMRange(multiRangeParamsA)); assertThat(rangeResultWithParams.keySet(), hasItems(key1, key2)); assertThat(rangeResultWithParams, equalTo(range)); TSMRangeParams multiRangeParams = new TSMRangeParams(fromTimestamp - 100, toTimestamp + 100).filter(filter); Map revRangeResultWithParams = exec(commandObjects.tsMRevRange(multiRangeParams)); assertThat(revRangeResultWithParams.keySet(), hasItems(key1, key2)); assertThat(revRangeResultWithParams, equalTo(revRangeResult)); } @Test public void testTsGet() { String key = "tsGetKey"; String create = exec(commandObjects.tsCreate(key)); assertThat(create, equalTo("OK")); long timestamp = 1000L; double firstValue = 2.5; double secondValue = 3.5; TSElement initialGet = exec(commandObjects.tsGet(key)); assertThat(initialGet, nullValue()); exec(commandObjects.tsAdd(key, timestamp, firstValue)); exec(commandObjects.tsAdd(key, timestamp + 100, secondValue)); TSElement getLastValue = exec(commandObjects.tsGet(key)); assertThat(getLastValue, notNullValue()); assertThat(getLastValue.getValue(), closeTo(secondValue, 0.001)); TSElement getWithParams = exec(commandObjects.tsGet(key, new TSGetParams().latest())); assertThat(getWithParams, notNullValue()); assertThat(getWithParams.getValue(), closeTo(secondValue, 0.001)); } @Test public void testTsMGet() { String key1 = "tsMGetKey1"; String key2 = "tsMGetKey2"; long timestamp1 = 1000L; long timestamp2 = 2000L; double value1 = 1.0; double value2 = 2.0; String filter = "sensor_id=1234"; TSCreateParams createParams = new TSCreateParams() .uncompressed().label("sensor_id", "1234"); exec(commandObjects.tsAdd(key1, timestamp1, value1, createParams)); exec(commandObjects.tsAdd(key2, timestamp2, value2, createParams)); TSMGetParams multiGetParams = new TSMGetParams().withLabels(); Map elements = exec(commandObjects.tsMGet(multiGetParams, filter)); assertThat(elements.keySet(), hasItems(key1, key2)); TSMGetElement element1 = elements.get(key1); assertThat(element1, notNullValue()); assertThat(element1.getElement().getTimestamp(), equalTo(timestamp1)); assertThat(element1.getElement().getValue(), closeTo(value1, 0.001)); TSMGetElement element2 = elements.get(key2); assertThat(element2, notNullValue()); assertThat(element2.getElement().getTimestamp(), equalTo(timestamp2)); assertThat(element2.getElement().getValue(), closeTo(value2, 0.001)); assertThat(element1.getLabels(), hasEntry("sensor_id", "1234")); assertThat(element2.getLabels(), hasEntry("sensor_id", "1234")); } @Test public void testTsCreateRule() { String sourceKey = "tsSourceKey"; String destKey = "tsDestKey"; AggregationType aggregationType = AggregationType.AVG; long timeBucket = 60000; // 1 minute exec(commandObjects.tsCreate(sourceKey)); exec(commandObjects.tsCreate(destKey)); String createRule = exec(commandObjects.tsCreateRule(sourceKey, destKey, aggregationType, timeBucket)); assertThat(createRule, equalTo("OK")); long timestamp1 = 1000L; // 1 second double value1 = 10.0; exec(commandObjects.tsAdd(sourceKey, timestamp1, value1)); long timestamp2 = 30000L; // 30 seconds double value2 = 20.0; exec(commandObjects.tsAdd(sourceKey, timestamp2, value2)); long timestamp3 = 100000L; // 100 seconds, should be in the second aggregation bucket double value3 = 30.0; exec(commandObjects.tsAdd(sourceKey, timestamp3, value3)); long timestamp4 = 200000L; // 200 seconds, should be in the fourth aggregation bucket double value4 = 1.0; exec(commandObjects.tsAdd(sourceKey, timestamp4, value4)); // Verify that aggregated data appears in the destination key // We only check the first three buckets, i.e. 180 seconds // The average of value1 and value2 should be in the first bucket, value3 in the second List destElements = exec(commandObjects.tsRange(destKey, 0, 180000)); assertThat(destElements.size(), equalTo(2)); double expectedAvgFirstBucket = (value1 + value2) / 2.0; assertThat(destElements.get(0).getValue(), closeTo(expectedAvgFirstBucket, 0.001)); assertThat(destElements.get(1).getValue(), closeTo(value3, 0.001)); } @Test public void testTsCreateRuleWithAlign() { String sourceKey = "tsSourceKey"; String destKey = "tsDestKey"; AggregationType aggregationType = AggregationType.AVG; long timeBucket = 60000; // 1 minute exec(commandObjects.tsCreate(sourceKey)); exec(commandObjects.tsCreate(destKey)); String createRule = exec(commandObjects.tsCreateRule(sourceKey, destKey, aggregationType, timeBucket, 2000)); assertThat(createRule, equalTo("OK")); long timestamp1 = 1000L; // 1 second double value1 = 10.0; exec(commandObjects.tsAdd(sourceKey, timestamp1, value1)); long timestamp2 = 30000L; // 30 seconds double value2 = 20.0; exec(commandObjects.tsAdd(sourceKey, timestamp2, value2)); long timestamp3 = 100000L; // 100 seconds, should be in the second aggregation bucket double value3 = 30.0; exec(commandObjects.tsAdd(sourceKey, timestamp3, value3)); long timestamp4 = 200000L; // 200 seconds, should be in the fourth aggregation bucket double value4 = 1.0; exec(commandObjects.tsAdd(sourceKey, timestamp4, value4)); // Verify that aggregated data appears in the destination key // We only check the first three buckets, i.e. 180 seconds // The average of value1 and value2 should be in the first bucket, value3 in the second List destElements = exec(commandObjects.tsRange(destKey, 2000, 182000)); assertThat(destElements.size(), equalTo(2)); assertThat(destElements.get(0).getValue(), closeTo(value2, 0.001)); assertThat(destElements.get(1).getValue(), closeTo(value3, 0.001)); } @Test public void testTsDeleteRule() { String sourceKey = "tsSourceKeyForDeletionWithData"; String destKey = "tsDestKeyForDeletionWithData"; AggregationType aggregationType = AggregationType.SUM; long bucketDuration = 60000; // 1 minute exec(commandObjects.tsCreate(sourceKey)); exec(commandObjects.tsCreate(destKey)); exec(commandObjects.tsCreateRule(sourceKey, destKey, aggregationType, bucketDuration)); long initialTimestamp = 1000; exec(commandObjects.tsAdd(sourceKey, initialTimestamp, 10.0)); // This will force aggregation of the first bucket exec(commandObjects.tsAdd(sourceKey, initialTimestamp + bucketDuration, 20.0)); List initialAggregatedData = exec(commandObjects.tsRange(destKey, 0, bucketDuration)); assertThat(initialAggregatedData.stream().map(TSElement::getValue).collect(Collectors.toList()), contains(closeTo(10.0, 0.001))); List initialAggregatedDataSecondBucket = exec(commandObjects.tsRange(destKey, bucketDuration, 2 * bucketDuration)); assertThat(initialAggregatedDataSecondBucket.stream().map(TSElement::getValue).collect(Collectors.toList()), empty()); // Delete the rule String deleteRule = exec(commandObjects.tsDeleteRule(sourceKey, destKey)); assertThat(deleteRule, equalTo("OK")); // Add more data to the source key after the rule has been deleted long postDeletionTimestamp = initialTimestamp + bucketDuration + 10; exec(commandObjects.tsAdd(sourceKey, postDeletionTimestamp, 20)); // This should force the aggregation of the second bucket, if there was a rule exec(commandObjects.tsAdd(sourceKey, postDeletionTimestamp + bucketDuration, 20)); // Make sure that the data in the destination key has not changed List postDeletionAggregatedData = exec(commandObjects.tsRange(destKey, 0, bucketDuration)); assertThat(postDeletionAggregatedData.stream().map(TSElement::getValue).collect(Collectors.toList()), contains(closeTo(10.0, 0.001))); List postDeletionAggregatedDataSecondBucket = exec(commandObjects.tsRange(destKey, bucketDuration, 2 * bucketDuration)); assertThat(postDeletionAggregatedDataSecondBucket.stream().map(TSElement::getValue).collect(Collectors.toList()), empty()); } @Test public void testTsQueryIndexWithKeyCreation() { String key1 = "temperature:sensor:1"; String key2 = "temperature:sensor:2"; String key3 = "humidity:sensor:1"; TSCreateParams paramsTempSensor1 = new TSCreateParams() .label("type", "temperature").label("sensor_id", "1"); exec(commandObjects.tsCreate(key1, paramsTempSensor1)); TSCreateParams paramsTempSensor2 = new TSCreateParams() .label("type", "temperature").label("sensor_id", "2"); exec(commandObjects.tsCreate(key2, paramsTempSensor2)); TSCreateParams paramsHumiditySensor1 = new TSCreateParams() .label("type", "humidity").label("sensor_id", "1"); exec(commandObjects.tsCreate(key3, paramsHumiditySensor1)); String[] filters = new String[]{ "type=temperature" }; List matchingKeys = exec(commandObjects.tsQueryIndex(filters)); assertThat(matchingKeys, containsInAnyOrder(key1, key2)); } @Test public void testTsAlterAndInfo() { String key = "tsKey"; TSCreateParams createParams = new TSCreateParams() .label("sensor", "temperature"); TSAlterParams alterParams = new TSAlterParams() .label("sensor", "humidity"); String create = exec(commandObjects.tsCreate(key, createParams)); assertThat(create, equalTo("OK")); TSInfo info = exec(commandObjects.tsInfo(key)); assertThat(info, notNullValue()); assertThat(info.getLabels().get("sensor"), equalTo("temperature")); assertThat(info.getChunks(), nullValue()); TSInfo debugInfo = exec(commandObjects.tsInfoDebug(key)); assertThat(debugInfo, notNullValue()); assertThat(debugInfo.getLabels().get("sensor"), equalTo("temperature")); assertThat(debugInfo.getChunks(), notNullValue()); String alter = exec(commandObjects.tsAlter(key, alterParams)); assertThat(alter, equalTo("OK")); TSInfo infoAfter = exec(commandObjects.tsInfo(key)); assertThat(infoAfter, notNullValue()); assertThat(infoAfter.getLabels().get("sensor"), equalTo("humidity")); assertThat(infoAfter.getChunks(), nullValue()); TSInfo debugInfoAfter = exec(commandObjects.tsInfoDebug(key)); assertThat(debugInfoAfter, notNullValue()); assertThat(debugInfoAfter.getLabels().get("sensor"), equalTo("humidity")); assertThat(debugInfoAfter.getChunks(), notNullValue()); } } ================================================ FILE: src/test/java/redis/clients/jedis/commands/commandobjects/CommandObjectsTopkCommandsTest.java ================================================ package redis.clients.jedis.commands.commandobjects; import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.aMapWithSize; import static org.hamcrest.Matchers.contains; import static org.hamcrest.Matchers.equalTo; import static org.hamcrest.Matchers.hasEntry; import static org.hamcrest.Matchers.hasKey; import static org.hamcrest.Matchers.hasSize; import static org.hamcrest.Matchers.notNullValue; import static org.hamcrest.Matchers.nullValue; import java.util.HashMap; import java.util.List; import java.util.Map; import org.junit.jupiter.api.Test; import redis.clients.jedis.RedisProtocol; /** * Tests related to Top-k commands. */ public class CommandObjectsTopkCommandsTest extends CommandObjectsModulesTestBase { public CommandObjectsTopkCommandsTest(RedisProtocol protocol) { super(protocol); } @Test public void testTopKAddAndQuery() { String key = "testTopK"; long topKSize = 3; String reserve = exec(commandObjects.topkReserve(key, topKSize)); assertThat(reserve, equalTo("OK")); List add = exec(commandObjects.topkAdd(key, "apple", "banana", "carrot", "apple", "banana", "date", "eggplant", "fig", "grape", "apple")); // As the values are added, some items get kicked out from top 3. They are returned in the response. assertThat(add, contains( nullValue(), nullValue(), nullValue(), nullValue(), nullValue(), equalTo("carrot"), equalTo("date"), equalTo("eggplant"), equalTo("fig"), nullValue() )); List query = exec(commandObjects.topkQuery(key, "apple", "banana", "carrot", "grape")); assertThat(query, contains(true, true, false, true)); } @Test public void testTopKIncrBy() { String key = "testTopK"; long topKSize = 3; Map itemIncrements = new HashMap<>(); itemIncrements.put("apple", 2L); itemIncrements.put("banana", 3L); itemIncrements.put("carrot", 1L); itemIncrements.put("date", 5L); String reserve = exec(commandObjects.topkReserve(key, topKSize, 2000, 7, 0.9)); assertThat(reserve, equalTo("OK")); List incrBy = exec(commandObjects.topkIncrBy(key, itemIncrements)); // Due to Map's unpredictable order, we can't assert ordering of the result assertThat(incrBy, hasSize(4)); List query = exec(commandObjects.topkQuery(key, "apple", "banana", "date", "carrot")); assertThat(query, contains(true, true, true, false)); } @Test public void testTopKListAndListWithCount() { String key = "testTopK"; long topKSize = 3; String reserve = exec(commandObjects.topkReserve(key, topKSize)); assertThat(reserve, equalTo("OK")); List add = exec(commandObjects.topkAdd(key, "apple", "banana", "carrot", "apple", "banana", "date", "eggplant", "fig", "grape", "apple")); assertThat(add, notNullValue()); List list = exec(commandObjects.topkList(key)); assertThat(list, contains("apple", "banana", "grape")); Map listWithCount = exec(commandObjects.topkListWithCount(key)); assertThat(listWithCount, aMapWithSize(3)); assertThat(listWithCount, hasEntry("apple", 3L)); assertThat(listWithCount, hasEntry("banana", 2L)); assertThat(listWithCount, hasEntry("grape", 1L)); } @Test public void testTopKInfo() { String key = "testTopK"; long topKSize = 3; long width = 1000; long depth = 7; double decay = 0.9; String reserve = exec(commandObjects.topkReserve(key, topKSize, width, depth, decay)); assertThat(reserve, equalTo("OK")); Map info = exec(commandObjects.topkInfo(key)); assertThat(info, notNullValue()); assertThat(info, hasEntry("k", 3L)); assertThat(info, hasEntry("width", width)); assertThat(info, hasEntry("depth", depth)); assertThat(info, hasKey("decay")); } } ================================================ FILE: src/test/java/redis/clients/jedis/commands/commandobjects/Person.java ================================================ package redis.clients.jedis.commands.commandobjects; import java.util.Objects; /** * Bean class used for testing Redis JSON commands. */ public class Person { private String name; private int age; public Person() { } public Person(String name, int age) { this.name = name; this.age = age; } public String getName() { return name; } public void setName(String name) { this.name = name; } public int getAge() { return age; } public void setAge(int age) { this.age = age; } @Override public boolean equals(Object o) { if (this == o) return true; if (o == null || getClass() != o.getClass()) return false; Person person = (Person) o; return age == person.age && Objects.equals(name, person.name); } @Override public int hashCode() { return Objects.hash(name, age); } } ================================================ FILE: src/test/java/redis/clients/jedis/commands/jedis/AccessControlListCommandsTest.java ================================================ package redis.clients.jedis.commands.jedis; import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.containsString; import static org.hamcrest.Matchers.endsWith; import static org.hamcrest.Matchers.startsWith; import static org.junit.jupiter.api.Assertions.assertArrayEquals; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertNotNull; import static org.junit.jupiter.api.Assertions.assertTrue; import static org.junit.jupiter.api.Assertions.fail; import static org.junit.jupiter.api.Assumptions.assumeTrue; import static redis.clients.jedis.util.ACLTestUtil.filterBinaryByClientId; import static redis.clients.jedis.util.ACLTestUtil.filterByClientId; import static redis.clients.jedis.util.RedisVersionUtil.getRedisVersion; import java.util.Arrays; import java.util.List; import io.redis.test.annotations.ConditionalOnEnv; import io.redis.test.annotations.SinceRedisVersion; import io.redis.test.utils.RedisVersion; import org.hamcrest.Matchers; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Tag; import org.junit.jupiter.params.ParameterizedClass; import org.junit.jupiter.params.provider.MethodSource; import redis.clients.jedis.CommandArguments; import redis.clients.jedis.Jedis; import redis.clients.jedis.Protocol; import redis.clients.jedis.RedisProtocol; import redis.clients.jedis.Transaction; import redis.clients.jedis.exceptions.JedisAccessControlException; import redis.clients.jedis.exceptions.JedisDataException; import redis.clients.jedis.resps.AccessControlLogEntry; import redis.clients.jedis.resps.AccessControlUser; import redis.clients.jedis.util.ACLTestUtil; import redis.clients.jedis.util.SafeEncoder; import redis.clients.jedis.util.TestEnvUtil; /** * TODO: properly define and test exceptions */ @ParameterizedClass @MethodSource("redis.clients.jedis.commands.CommandsTestsParameters#respVersions") @Tag("integration") @ConditionalOnEnv(value = TestEnvUtil.ENV_REDIS_ENTERPRISE, enabled = false) public class AccessControlListCommandsTest extends JedisCommandsTestBase { public static final String USER_NAME = "newuser"; public static final String USER_PASSWORD = "secret"; public static final String USER_ANTIREZ = "antirez"; @BeforeAll public static void prepare() throws Exception { // Use to check if the ACL test should be ran. ACL are available only in 6.0 and later assumeTrue(getRedisVersion(endpoint).isGreaterThanOrEqualTo(RedisVersion.V6_0_0), "Not running ACL test on this version of Redis"); } public AccessControlListCommandsTest(RedisProtocol protocol) { super(protocol); } @AfterEach @Override public void tearDown() throws Exception { try { jedis.aclDelUser(USER_NAME); jedis.aclDelUser(USER_ANTIREZ); } catch (Exception e) { // Ignore exception } super.tearDown(); } @Test public void aclWhoAmI() { String string = jedis.aclWhoAmI(); assertEquals("default", string); byte[] binary = jedis.aclWhoAmIBinary(); assertArrayEquals(SafeEncoder.encode("default"), binary); } @Test public void aclListDefault() { assertFalse(jedis.aclList().isEmpty()); assertFalse(jedis.aclListBinary().isEmpty()); } @Test public void addAndRemoveUser() { int existingUsers = jedis.aclList().size(); String status = jedis.aclSetUser(USER_NAME); assertEquals("OK", status); assertEquals(existingUsers + 1, jedis.aclList().size()); assertEquals(existingUsers + 1, jedis.aclListBinary().size()); // test binary long count = jedis.aclDelUser(USER_NAME); assertEquals(1, count); assertEquals(existingUsers, jedis.aclList().size()); assertEquals(existingUsers, jedis.aclListBinary().size()); // test binary } @Test public void aclUsers() { List users = jedis.aclUsers(); assertEquals(3, users.size()); assertThat(users, Matchers.hasItem("default")); assertThat(users, Matchers.hasItem("deploy")); assertThat(users, Matchers.hasItem("acljedis")); assertEquals(3, jedis.aclUsersBinary().size()); // Test binary } @Test @SinceRedisVersion(value = "7.0.0", message = "Redis 6.2.x misses [~]>") public void aclGetUser() { // get default user information AccessControlUser userInfo = jedis.aclGetUser("default"); assertFalse(userInfo.getFlags().isEmpty()); assertEquals(1, userInfo.getPassword().size()); assertEquals("+@all", userInfo.getCommands()); assertEquals("~*", userInfo.getKeys()); // create new user jedis.aclSetUser(USER_NAME); userInfo = jedis.aclGetUser(USER_NAME); assertFalse(userInfo.getFlags().isEmpty()); assertEquals("off", userInfo.getFlags().get(0)); assertTrue(userInfo.getPassword().isEmpty()); assertTrue(userInfo.getKeys().isEmpty()); // reset user jedis.aclSetUser(USER_NAME, "reset", "+@all", "~*", "-@string", "+incr", "-debug", "+debug|digest"); userInfo = jedis.aclGetUser(USER_NAME); assertThat(userInfo.getCommands(), containsString("+@all")); assertThat(userInfo.getCommands(), containsString("-@string")); assertThat(userInfo.getCommands(), containsString("+debug|digest")); } @Test public void createUserAndPasswords() { String status = jedis.aclSetUser(USER_NAME, ">" + USER_PASSWORD); assertEquals("OK", status); // create a new client to try to authenticate Jedis jedis2 = new Jedis(); // the user is just created without any permission the authentication should fail try { jedis2.auth(USER_NAME, USER_PASSWORD); fail("Should throw a WRONGPASS exception"); } catch (JedisAccessControlException e) { assertThat(e.getMessage(), startsWith("WRONGPASS ")); } // now activate the user jedis.aclSetUser(USER_NAME, "on", "+acl"); jedis2.auth(USER_NAME, USER_PASSWORD); assertEquals(USER_NAME, jedis2.aclWhoAmI()); // test invalid password jedis2.close(); try { jedis2.auth(USER_NAME, "wrong-password"); fail("Should throw a WRONGPASS exception"); } catch (JedisAccessControlException e) { assertThat(e.getMessage(), startsWith("WRONGPASS ")); } // remove password, and try to authenticate jedis.aclSetUser(USER_NAME, "<" + USER_PASSWORD); try { jedis2.auth(USER_NAME, USER_PASSWORD); fail("Should throw a WRONGPASS exception"); } catch (JedisAccessControlException e) { assertThat(e.getMessage(), startsWith("WRONGPASS ")); } jedis.aclDelUser(USER_NAME); // delete the user try { jedis2.auth(USER_NAME, "wrong-password"); fail("Should throw a WRONGPASS exception"); } catch (JedisAccessControlException e) { assertThat(e.getMessage(), startsWith("WRONGPASS ")); } jedis2.close(); } @Test public void aclSetUserWithAnyPassword() { String status = jedis.aclSetUser(USER_NAME, "nopass"); assertEquals("OK", status); status = jedis.aclSetUser(USER_NAME, "on", "+acl"); assertEquals("OK", status); // connect with this new user and try to get/set keys Jedis jedis2 = new Jedis(); String authResult = jedis2.auth(USER_NAME, "any password"); assertEquals("OK", authResult); jedis2.close(); } @Test public void aclExcudeSingleCommand() { String status = jedis.aclSetUser(USER_NAME, "nopass"); assertEquals("OK", status); status = jedis.aclSetUser(USER_NAME, "on", "+acl"); assertEquals("OK", status); status = jedis.aclSetUser(USER_NAME, "allcommands", "allkeys"); assertEquals("OK", status); status = jedis.aclSetUser(USER_NAME, "-ping"); assertEquals("OK", status); // connect with this new user and try to get/set keys Jedis jedis2 = new Jedis(); String authResult = jedis2.auth(USER_NAME, "any password"); assertEquals("OK", authResult); jedis2.incr("mycounter"); try { jedis2.ping(); fail("Should throw a NOPERM exception"); } catch (JedisAccessControlException e) { assertThat(e.getMessage(), startsWith("NOPERM ")); assertThat(e.getMessage(), containsString(" has no permissions to run the 'ping' command")); } jedis2.close(); } @Test @SinceRedisVersion("7.0.0") public void aclDryRun() { jedis.aclSetUser(USER_NAME, "nopass", "allkeys", "+set", "-get"); assertEquals("OK", jedis.aclDryRun(USER_NAME, "SET", "key", "value")); assertThat(jedis.aclDryRun(USER_NAME, "GET", "key"), endsWith(" has no permissions to run the 'get' command")); assertEquals("OK", jedis.aclDryRun(USER_NAME, new CommandArguments(Protocol.Command.SET).key("ca-key").add("value"))); assertThat(jedis.aclDryRun(USER_NAME, new CommandArguments(Protocol.Command.GET).key("ca-key")), endsWith(" has no permissions to run the 'get' command")); } @Test @SinceRedisVersion("7.0.0") public void aclDryRunBinary() { byte[] username = USER_NAME.getBytes(); jedis.aclSetUser(username, "nopass".getBytes(), "allkeys".getBytes(), "+set".getBytes(), "-get".getBytes()); assertArrayEquals("OK".getBytes(), jedis.aclDryRunBinary(username, "SET".getBytes(), "key".getBytes(), "value".getBytes())); assertThat(new String(jedis.aclDryRunBinary(username, "GET".getBytes(), "key".getBytes())), endsWith(" has no permissions to run the 'get' command")); assertArrayEquals("OK".getBytes(), jedis.aclDryRunBinary(username, new CommandArguments(Protocol.Command.SET).key("ca-key").add("value"))); assertThat(new String(jedis.aclDryRunBinary(username, new CommandArguments(Protocol.Command.GET).key("ca-key"))), endsWith(" has no permissions to run the 'get' command")); } @Test public void aclDelUser() { String statusSetUser = jedis.aclSetUser(USER_NAME); assertEquals("OK", statusSetUser); int before = jedis.aclList().size(); assertEquals(1L, jedis.aclDelUser(USER_NAME)); int after = jedis.aclList().size(); assertEquals(before - 1, after); } @Test public void basicPermissionsTest() { // create a user with login permissions jedis.aclSetUser(USER_NAME, ">" + USER_PASSWORD); // users are not able to access any command jedis.aclSetUser(USER_NAME, "on", "+acl"); // connect with this new user and try to get/set keys Jedis jedis2 = new Jedis(); jedis2.auth(USER_NAME, USER_PASSWORD); try { jedis2.set("foo", "bar"); fail("Should throw a NOPERM exception"); } catch (JedisAccessControlException e) { assertThat(e.getMessage(), startsWith("NOPERM ")); assertThat(e.getMessage(), containsString(" has no permissions to run the 'set' command")); } // change permissions of the user // by default users are not able to access any key jedis.aclSetUser(USER_NAME, "+set"); jedis2.close(); jedis2.auth(USER_NAME, USER_PASSWORD); final List nopermKeys = Arrays.asList("NOPERM No permissions to access a key", "NOPERM this user has no permissions to access one of the keys used as arguments"); try { jedis2.set("foo", "bar"); fail("Should throw a NOPERM exception"); } catch (JedisAccessControlException e) { assertThat(e.getMessage(), Matchers.isIn(nopermKeys)); } // allow user to access a subset of the key jedis.aclSetUser(USER_NAME, "allcommands", "~foo:*", "~bar:*"); // TODO : a DSL // create key foo, bar and zap jedis2.set("foo:1", "a"); jedis2.set("bar:2", "b"); try { jedis2.set("zap:3", "c"); fail("Should throw a NOPERM exception"); } catch (JedisAccessControlException e) { assertThat(e.getMessage(), Matchers.isIn(nopermKeys)); } } @Test public void aclCatTest() { List categories = jedis.aclCat(); assertFalse(categories.isEmpty()); // test binary List categoriesBinary = jedis.aclCatBinary(); assertFalse(categories.isEmpty()); assertEquals(categories.size(), categoriesBinary.size()); // test commands in a category assertFalse(jedis.aclCat("scripting").isEmpty()); try { jedis.aclCat("testcategory"); fail("Should throw a ERR exception"); } catch (Exception e) { assertEquals("ERR Unknown category 'testcategory'", e.getMessage()); } } @Test public void aclLogTest() { jedis.aclLogReset(); assertTrue(filterByClientId(jedis.aclLog(), jedis.clientId()).isEmpty()); // create new user and cconnect jedis.aclSetUser(USER_ANTIREZ, ">foo", "on", "+set", "~object:1234"); jedis.aclSetUser(USER_ANTIREZ, "+eval", "+multi", "+exec"); jedis.auth(USER_ANTIREZ, "foo"); // generate an error (antirez user does not have the permission to access foo) try { jedis.get("foo"); fail("Should have thrown an JedisAccessControlException: user does not have the permission to get(\"foo\")"); } catch (JedisAccessControlException e) { } // test the ACL Log jedis.auth(endpoint.getUsername(), endpoint.getPassword()); List aclEntries = jedis.aclLog(); // At the same time other clients can generate other log entries // Filter only the entries generated by this client List clientAclEntries = filterByClientId(aclEntries, jedis.clientId()); assertEquals(1, clientAclEntries.size(), "Number of log messages "); assertEquals(1, clientAclEntries.get(0).getCount()); assertEquals(USER_ANTIREZ, clientAclEntries.get(0).getUsername()); assertEquals("toplevel", clientAclEntries.get(0).getContext()); assertEquals("command", clientAclEntries.get(0).getReason()); assertEquals("get", clientAclEntries.get(0).getObject()); // Capture similar event jedis.aclLogReset(); assertTrue(filterByClientId(jedis.aclLog(), jedis.clientId()).isEmpty()); jedis.auth(USER_ANTIREZ, "foo"); for (int i = 0; i < 10; i++) { // generate an error (antirez user does not have the permission to access foo) try { jedis.get("foo"); fail("Should have thrown an JedisAccessControlException: user does not have the permission to get(\"foo\")"); } catch (JedisAccessControlException e) { } } // test the ACL Log jedis.auth(endpoint.getUsername(), endpoint.getPassword()); clientAclEntries = filterByClientId(jedis.aclLog(), jedis.clientId()); assertEquals(1, clientAclEntries.size(), "Number of log messages "); assertEquals(10, clientAclEntries.get(0).getCount()); assertEquals("get", clientAclEntries.get(0).getObject()); // Generate another type of error jedis.auth(USER_ANTIREZ, "foo"); try { jedis.set("somekeynotallowed", "1234"); fail("Should have thrown an JedisAccessControlException: user does not have the permission to set(\"somekeynotallowed\", \"1234\")"); } catch (JedisAccessControlException e) { } // test the ACL Log jedis.auth(endpoint.getUsername(), endpoint.getPassword()); clientAclEntries = filterByClientId(jedis.aclLog(), jedis.clientId()); assertEquals( 2, clientAclEntries.size(), "Number of log messages "); assertEquals(1, clientAclEntries.get(0).getCount()); assertEquals("somekeynotallowed", clientAclEntries.get(0).getObject()); assertEquals("key", clientAclEntries.get(0).getReason()); jedis.aclLogReset(); assertTrue(filterByClientId(jedis.aclLog(), jedis.clientId()).isEmpty()); jedis.auth(USER_ANTIREZ, "foo"); Transaction t = jedis.multi(); t.incr("foo"); try { t.exec(); fail("Should have thrown an JedisAccessControlException: user does not have the permission to incr(\"foo\")"); } catch (Exception e) { } t.close(); jedis.auth(endpoint.getUsername(), endpoint.getPassword()); clientAclEntries = filterByClientId(jedis.aclLog(), jedis.clientId()); assertEquals( 1, clientAclEntries.size(), "Number of log messages "); assertEquals(1, clientAclEntries.get(0).getCount()); assertEquals("multi", clientAclEntries.get(0).getContext()); assertEquals("incr", clientAclEntries.get(0).getObject()); // ACL LOG can accept a numerical argument to show less entries jedis.auth(USER_ANTIREZ, "foo"); for (int i = 0; i < 5; i++) { try { jedis.incr("foo"); fail("Should have thrown an JedisAccessControlException: user does not have the permission to incr(\"foo\")"); } catch (JedisAccessControlException e) { } } try { jedis.set("foo-2", "bar"); fail("Should have thrown an JedisAccessControlException: user does not have the permission to set(\"foo-2\", \"bar\")"); } catch (JedisAccessControlException e) { } jedis.auth(endpoint.getUsername(), endpoint.getPassword()); assertEquals( 3, filterByClientId(jedis.aclLog(), jedis.clientId()).size(), "Number of log messages "); assertEquals( 2, jedis.aclLog(2).size(), "Number of log messages "); // Binary tests assertEquals( 3, filterBinaryByClientId(jedis.aclLogBinary(), jedis.clientId()).size(), "Number of log messages "); assertEquals( 2, jedis.aclLogBinary(2).size(), "Number of log messages "); // RESET String status = jedis.aclLogReset(); assertEquals(status, "OK"); jedis.aclDelUser(USER_ANTIREZ); } @Test @SinceRedisVersion(value = "7.2.0", message = "Starting with Redis version 7.2.0: Added entry ID, timestamp created, and timestamp last updated.") public void aclLogWithEntryID() { jedis.aclLogReset(); try { jedis.auth("wronguser", "wrongpass"); fail("wrong user should not passed"); } catch (JedisAccessControlException e) { } List aclEntries = filterByClientId(jedis.aclLog(), jedis.clientId()); assertEquals( 1, aclEntries.size(), "Number of log messages "); assertEquals(1, aclEntries.get(0).getCount()); assertEquals("wronguser", aclEntries.get(0).getUsername()); assertEquals("toplevel", aclEntries.get(0).getContext()); assertEquals("auth", aclEntries.get(0).getReason()); assertEquals("AUTH", aclEntries.get(0).getObject()); assertTrue(aclEntries.get(0).getEntryId() >= 0); assertTrue(aclEntries.get(0).getTimestampCreated() > 0); assertEquals(aclEntries.get(0).getTimestampCreated(), aclEntries.get(0).getTimestampLastUpdated()); // RESET String status = jedis.aclLogReset(); assertEquals(status, "OK"); } @Test public void aclGenPass() { assertNotNull(jedis.aclGenPass()); // bit length case assertNotNull(jedis.aclGenPassBinary(16)); assertNotNull(jedis.aclGenPassBinary(32)); } @Test public void aclGenPassBinary() { assertNotNull(jedis.aclGenPassBinary()); // bit length case assertNotNull(jedis.aclGenPassBinary(16)); assertNotNull(jedis.aclGenPassBinary(32)); } @Test @SinceRedisVersion(value = "7.0.0", message = "Redis 6.2.x skips [&]>") public void aclBinaryCommandsTest() { jedis.aclSetUser(USER_NAME.getBytes()); assertNotNull(jedis.aclGetUser(USER_NAME)); assertEquals(1L, jedis.aclDelUser(USER_NAME.getBytes())); jedis.aclSetUser(USER_NAME.getBytes(), "reset".getBytes(), "+@all".getBytes(), "~*".getBytes(), "-@string".getBytes(), "+incr".getBytes(), "-debug".getBytes(), "+debug|digest".getBytes(), "resetchannels".getBytes(), "&testchannel:*".getBytes()); AccessControlUser userInfo = jedis.aclGetUser(USER_NAME.getBytes()); assertThat(userInfo.getCommands(), containsString("+@all")); assertThat(userInfo.getCommands(), containsString("-@string")); assertThat(userInfo.getCommands(), containsString("+debug|digest")); assertEquals("&testchannel:*", userInfo.getChannels()); jedis.aclDelUser(USER_NAME.getBytes()); jedis.aclSetUser("TEST_USER".getBytes()); jedis.aclSetUser("ANOTHER_TEST_USER".getBytes()); jedis.aclSetUser("MORE_TEST_USERS".getBytes()); assertEquals(3L, jedis.aclDelUser( "TEST_USER".getBytes(), "ANOTHER_TEST_USER".getBytes(), "MORE_TEST_USERS".getBytes())); } @Test public void aclLoadTest() { try { jedis.aclLoad(); fail("Should throw a JedisDataException: ERR This Redis instance is not configured to use an ACL file..."); } catch (JedisDataException e) { assertThat(e.getMessage(), startsWith("ERR This Redis instance is not configured to use an ACL file.")); } // TODO test with ACL file } @Test public void aclSaveTest() { try { jedis.aclSave(); fail("Should throw a JedisDataException: ERR This Redis instance is not configured to use an ACL file..."); } catch (JedisDataException e) { assertThat(e.getMessage(), startsWith("ERR This Redis instance is not configured to use an ACL file.")); } // TODO test with ACL file } } ================================================ FILE: src/test/java/redis/clients/jedis/commands/jedis/AllKindOfValuesCommandsTest.java ================================================ package redis.clients.jedis.commands.jedis; import static org.hamcrest.MatcherAssert.assertThat; import static org.junit.jupiter.api.Assertions.assertArrayEquals; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertNotNull; import static org.junit.jupiter.api.Assertions.assertNull; import static org.junit.jupiter.api.Assertions.assertThrows; import static org.junit.jupiter.api.Assertions.assertTrue; import static org.junit.jupiter.api.Assertions.fail; import static redis.clients.jedis.Protocol.Command.BLPOP; import static redis.clients.jedis.Protocol.Command.HGETALL; import static redis.clients.jedis.Protocol.Command.GET; import static redis.clients.jedis.Protocol.Command.LRANGE; import static redis.clients.jedis.Protocol.Command.PING; import static redis.clients.jedis.Protocol.Command.RPUSH; import static redis.clients.jedis.Protocol.Command.SET; import static redis.clients.jedis.Protocol.Command.XINFO; import static redis.clients.jedis.params.ScanParams.SCAN_POINTER_START; import static redis.clients.jedis.params.ScanParams.SCAN_POINTER_START_BINARY; import java.time.Duration; import java.util.*; import io.redis.test.annotations.ConditionalOnEnv; import io.redis.test.annotations.EnabledOnCommand; import io.redis.test.annotations.SinceRedisVersion; import org.hamcrest.Matchers; import org.junit.jupiter.api.Assumptions; import org.junit.jupiter.api.Disabled; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Tag; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.params.ParameterizedClass; import org.junit.jupiter.params.provider.MethodSource; import redis.clients.jedis.*; import redis.clients.jedis.args.ExpiryOption; import redis.clients.jedis.util.*; import redis.clients.jedis.params.ScanParams; import redis.clients.jedis.resps.ScanResult; import redis.clients.jedis.args.FlushMode; import redis.clients.jedis.params.RestoreParams; import redis.clients.jedis.exceptions.JedisDataException; import redis.clients.jedis.params.SetParams; @ParameterizedClass @MethodSource("redis.clients.jedis.commands.CommandsTestsParameters#respVersions") @Tag("integration") public class AllKindOfValuesCommandsTest extends JedisCommandsTestBase { private static final long TIME_SKEW = Duration.ofMillis(5).toMillis(); final byte[] bfoo = { 0x01, 0x02, 0x03, 0x04 }; final byte[] bfoo1 = { 0x01, 0x02, 0x03, 0x04, 0x0A }; final byte[] bfoo2 = { 0x01, 0x02, 0x03, 0x04, 0x0B }; final byte[] bfoo3 = { 0x01, 0x02, 0x03, 0x04, 0x0C }; final byte[] bbar = { 0x05, 0x06, 0x07, 0x08 }; final byte[] bbar1 = { 0x05, 0x06, 0x07, 0x08, 0x0A }; final byte[] bbar2 = { 0x05, 0x06, 0x07, 0x08, 0x0B }; final byte[] bbar3 = { 0x05, 0x06, 0x07, 0x08, 0x0C }; final byte[] bfoobar = { 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08 }; final byte[] bfoostar = { 0x01, 0x02, 0x03, 0x04, '*' }; final byte[] bbarstar = { 0x05, 0x06, 0x07, 0x08, '*' }; final byte[] bnx = { 0x6E, 0x78 }; final byte[] bex = { 0x65, 0x78 }; final int expireSeconds = 2; private static EndpointConfig lfuEndpoint; @BeforeAll public static void prepareLfuEndpoint() { lfuEndpoint = Endpoints.getRedisEndpoint("standalone7-with-lfu-policy"); } public AllKindOfValuesCommandsTest(RedisProtocol redisProtocol) { super(redisProtocol); } @Test public void ping() { String status = jedis.ping(); assertEquals("PONG", status); } @Test public void pingWithMessage() { String argument = "message"; assertEquals(argument, jedis.ping(argument)); assertArrayEquals(bfoobar, jedis.ping(bfoobar)); } @Test public void exists() { String status = jedis.set("foo", "bar"); assertEquals("OK", status); status = jedis.set(bfoo, bbar); assertEquals("OK", status); assertTrue(jedis.exists("foo")); assertTrue(jedis.exists(bfoo)); assertEquals(1L, jedis.del("foo")); assertEquals(1L, jedis.del(bfoo)); assertFalse(jedis.exists("foo")); assertFalse(jedis.exists(bfoo)); } @Test public void existsMany() { String status = jedis.set("foo1", "bar1"); assertEquals("OK", status); status = jedis.set("foo2", "bar2"); assertEquals("OK", status); assertEquals(2L, jedis.exists("foo1", "foo2")); assertEquals(1L, jedis.del("foo1")); assertEquals(1L, jedis.exists("foo1", "foo2")); } @Test public void del() { jedis.set("foo1", "bar1"); jedis.set("foo2", "bar2"); jedis.set("foo3", "bar3"); assertEquals(3L, jedis.del("foo1", "foo2", "foo3")); assertFalse(jedis.exists("foo1")); assertFalse(jedis.exists("foo2")); assertFalse(jedis.exists("foo3")); jedis.set("foo1", "bar1"); assertEquals(1L, jedis.del("foo1", "foo2")); assertEquals(0L, jedis.del("foo1", "foo2")); // Binary ... jedis.set(bfoo1, bbar1); jedis.set(bfoo2, bbar2); jedis.set(bfoo3, bbar3); assertEquals(3L, jedis.del(bfoo1, bfoo2, bfoo3)); assertFalse(jedis.exists(bfoo1)); assertFalse(jedis.exists(bfoo2)); assertFalse(jedis.exists(bfoo3)); jedis.set(bfoo1, bbar1); assertEquals(1, jedis.del(bfoo1, bfoo2)); assertEquals(0, jedis.del(bfoo1, bfoo2)); } @Test public void unlink() { jedis.set("foo1", "bar1"); jedis.set("foo2", "bar2"); jedis.set("foo3", "bar3"); assertEquals(3, jedis.unlink("foo1", "foo2", "foo3")); assertEquals(0, jedis.exists("foo1", "foo2", "foo3")); jedis.set("foo1", "bar1"); assertEquals(1, jedis.unlink("foo1", "foo2")); assertEquals(0, jedis.unlink("foo1", "foo2")); jedis.set("foo", "bar"); assertEquals(1, jedis.unlink("foo")); assertFalse(jedis.exists("foo")); // Binary jedis.set(bfoo1, bbar1); jedis.set(bfoo2, bbar2); jedis.set(bfoo3, bbar3); assertEquals(3, jedis.unlink(bfoo1, bfoo2, bfoo3)); assertEquals(0, jedis.exists(bfoo1, bfoo2, bfoo3)); jedis.set(bfoo1, bbar1); assertEquals(1, jedis.unlink(bfoo1, bfoo2)); assertEquals(0, jedis.unlink(bfoo1, bfoo2)); jedis.set(bfoo, bbar); assertEquals(1, jedis.unlink(bfoo)); assertFalse(jedis.exists(bfoo)); } @Test public void type() { jedis.set("foo", "bar"); assertEquals("string", jedis.type("foo")); // Binary jedis.set(bfoo, bbar); assertEquals("string", jedis.type(bfoo)); } @Test public void keys() { jedis.set("foo", "bar"); jedis.set("foobar", "bar"); Set keys = jedis.keys("foo*"); AssertUtil.assertCollectionContains(keys, "foo"); AssertUtil.assertCollectionContains(keys, "foobar"); assertEquals(Collections.emptySet(), jedis.keys("bar*")); // Binary jedis.set(bfoo, bbar); jedis.set(bfoobar, bbar); Set bkeys = jedis.keys(bfoostar); AssertUtil.assertByteArrayCollectionContains(bkeys, bfoo); AssertUtil.assertByteArrayCollectionContains(bkeys, bfoobar); assertEquals(Collections.emptySet(), jedis.keys(bbarstar)); } @Test public void randomKey() { assertNull(jedis.randomKey()); jedis.set("foo", "bar"); assertEquals("foo", jedis.randomKey()); jedis.set("bar", "foo"); String randomkey = jedis.randomKey(); assertTrue(randomkey.equals("foo") || randomkey.equals("bar")); // Binary jedis.del("foo"); jedis.del("bar"); assertNull(jedis.randomKey()); jedis.set(bfoo, bbar); assertArrayEquals(bfoo, jedis.randomBinaryKey()); jedis.set(bbar, bfoo); byte[] randomBkey = jedis.randomBinaryKey(); assertTrue(Arrays.equals(randomBkey, bfoo) || Arrays.equals(randomBkey, bbar)); } @Test @ConditionalOnEnv(value = TestEnvUtil.ENV_REDIS_ENTERPRISE, enabled = false) public void rename() { jedis.set("foo", "bar"); String status = jedis.rename("foo", "bar"); assertEquals("OK", status); assertNull(jedis.get("foo")); assertEquals("bar", jedis.get("bar")); // Binary jedis.set(bfoo, bbar); String bstatus = jedis.rename(bfoo, bbar); assertEquals("OK", bstatus); assertNull(jedis.get(bfoo)); assertArrayEquals(bbar, jedis.get(bbar)); } @Test public void renameOldAndNewAreTheSame() { assertEquals("OK", jedis.set("foo", "bar")); assertEquals("OK", jedis.rename("foo", "foo")); // Binary assertEquals("OK", jedis.set(bfoo, bbar)); assertEquals("OK", jedis.rename(bfoo, bfoo)); } @Test @ConditionalOnEnv(value = TestEnvUtil.ENV_REDIS_ENTERPRISE, enabled = false) public void renamenx() { jedis.set("foo", "bar"); assertEquals(1, jedis.renamenx("foo", "bar")); jedis.set("foo", "bar"); assertEquals(0, jedis.renamenx("foo", "bar")); // Binary jedis.set(bfoo, bbar); assertEquals(1, jedis.renamenx(bfoo, bbar)); jedis.set(bfoo, bbar); assertEquals(0, jedis.renamenx(bfoo, bbar)); } @Test public void dbSize() { assertEquals(0, jedis.dbSize()); jedis.set("foo", "bar"); assertEquals(1, jedis.dbSize()); // Binary jedis.set(bfoo, bbar); assertEquals(2, jedis.dbSize()); } @Test @SinceRedisVersion(value = "7.0.0", message = "Starting with Redis version 7.0.0: Added options: NX, XX, GT and LT.") public void expire() { assertEquals(0, jedis.expire("foo", 20L)); jedis.set("foo", "bar"); assertEquals(1, jedis.expire("foo", 20L)); assertEquals(0, jedis.expire("foo", 20L, ExpiryOption.NX)); // Binary assertEquals(0, jedis.expire(bfoo, 20L)); jedis.set(bfoo, bbar); assertEquals(1, jedis.expire(bfoo, 20L)); assertEquals(0, jedis.expire(bfoo, 20L, ExpiryOption.NX)); } @Test @SinceRedisVersion(value = "7.0.0", message = "Starting with Redis version 7.0.0: Added options: NX, XX, GT and LT.") public void expireAt() { long unixTime = (System.currentTimeMillis() / 1000L) + 20; assertEquals(0, jedis.expireAt("foo", unixTime)); jedis.set("foo", "bar"); unixTime = (System.currentTimeMillis() / 1000L) + 20; assertEquals(1, jedis.expireAt("foo", unixTime)); assertEquals(1, jedis.expireAt("foo", unixTime, ExpiryOption.XX)); // Binary assertEquals(0, jedis.expireAt(bfoo, unixTime)); jedis.set(bfoo, bbar); unixTime = (System.currentTimeMillis() / 1000L) + 20; assertEquals(1, jedis.expireAt(bfoo, unixTime)); assertEquals(1, jedis.expireAt(bfoo, unixTime, ExpiryOption.XX)); } @Test @SinceRedisVersion(value = "7.0.0") public void expireTime() { long unixTime; jedis.set("foo", "bar"); unixTime = (System.currentTimeMillis() / 1000L) + 20; jedis.expireAt("foo", unixTime); assertEquals(unixTime, jedis.expireTime("foo"), 0.0001); // Binary jedis.set(bfoo, bbar); unixTime = (System.currentTimeMillis() / 1000L) + 20; jedis.expireAt(bfoo, unixTime); assertEquals(unixTime, jedis.expireTime(bfoo), 0.0001); } @Test public void ttl() { assertEquals(-2, jedis.ttl("foo")); jedis.set("foo", "bar"); assertEquals(-1, jedis.ttl("foo")); jedis.expire("foo", 20); long ttl = jedis.ttl("foo"); assertTrue(ttl >= 0 && ttl <= 20); // Binary assertEquals(-2, jedis.ttl(bfoo)); jedis.set(bfoo, bbar); assertEquals(-1, jedis.ttl(bfoo)); jedis.expire(bfoo, 20); long bttl = jedis.ttl(bfoo); assertTrue(bttl >= 0 && bttl <= 20); } @Test public void touch() throws Exception { assertEquals(0, jedis.touch("foo1", "foo2", "foo3")); jedis.set("foo1", "bar1"); Thread.sleep(1100); // little over 1 sec assertTrue(jedis.objectIdletime("foo1") > 0); assertEquals(1, jedis.touch("foo1")); assertEquals(0L, jedis.objectIdletime("foo1").longValue()); assertEquals(1, jedis.touch("foo1", "foo2", "foo3")); jedis.set("foo2", "bar2"); jedis.set("foo3", "bar3"); assertEquals(3, jedis.touch("foo1", "foo2", "foo3")); // Binary assertEquals(0, jedis.touch(bfoo1, bfoo2, bfoo3)); jedis.set(bfoo1, bbar1); Thread.sleep(1100); // little over 1 sec assertTrue(jedis.objectIdletime(bfoo1) > 0); assertEquals(1, jedis.touch(bfoo1)); assertEquals(0L, jedis.objectIdletime(bfoo1).longValue()); assertEquals(1, jedis.touch(bfoo1, bfoo2, bfoo3)); jedis.set(bfoo2, bbar2); jedis.set(bfoo3, bbar3); assertEquals(3, jedis.touch(bfoo1, bfoo2, bfoo3)); } @Test @ConditionalOnEnv(value = TestEnvUtil.ENV_REDIS_ENTERPRISE, enabled = false) public void select() { jedis.set("foo", "bar"); String status = jedis.select(1); assertEquals("OK", status); assertNull(jedis.get("foo")); status = jedis.select(0); assertEquals("OK", status); assertEquals("bar", jedis.get("foo")); // Binary jedis.set(bfoo, bbar); String bstatus = jedis.select(1); assertEquals("OK", bstatus); assertNull(jedis.get(bfoo)); bstatus = jedis.select(0); assertEquals("OK", bstatus); assertArrayEquals(bbar, jedis.get(bfoo)); } @Test @ConditionalOnEnv(value = TestEnvUtil.ENV_REDIS_ENTERPRISE, enabled = false) public void getDB() { assertEquals(0, jedis.getDB()); jedis.select(1); assertEquals(1, jedis.getDB()); } @Test @ConditionalOnEnv(value = TestEnvUtil.ENV_REDIS_ENTERPRISE, enabled = false) public void move() { assertEquals(0, jedis.move("foo", 1)); jedis.set("foo", "bar"); assertEquals(1, jedis.move("foo", 1)); assertNull(jedis.get("foo")); jedis.select(1); assertEquals("bar", jedis.get("foo")); // Binary jedis.select(0); assertEquals(0, jedis.move(bfoo, 1)); jedis.set(bfoo, bbar); assertEquals(1, jedis.move(bfoo, 1)); assertNull(jedis.get(bfoo)); jedis.select(1); assertArrayEquals(bbar, jedis.get(bfoo)); } @Test @ConditionalOnEnv(value = TestEnvUtil.ENV_REDIS_ENTERPRISE, enabled = false) public void swapDB() { jedis.set("foo1", "bar1"); jedis.select(1); assertNull(jedis.get("foo1")); jedis.set("foo2", "bar2"); String status = jedis.swapDB(0, 1); assertEquals("OK", status); assertEquals("bar1", jedis.get("foo1")); assertNull(jedis.get("foo2")); jedis.select(0); assertNull(jedis.get("foo1")); assertEquals("bar2", jedis.get("foo2")); // Binary jedis.set(bfoo1, bbar1); jedis.select(1); assertArrayEquals(null, jedis.get(bfoo1)); jedis.set(bfoo2, bbar2); status = jedis.swapDB(0, 1); assertEquals("OK", status); assertArrayEquals(bbar1, jedis.get(bfoo1)); assertArrayEquals(null, jedis.get(bfoo2)); jedis.select(0); assertArrayEquals(null, jedis.get(bfoo1)); assertArrayEquals(bbar2, jedis.get(bfoo2)); } @Test @ConditionalOnEnv(value = TestEnvUtil.ENV_REDIS_ENTERPRISE, enabled = false) public void flushDB() { jedis.set("foo", "bar"); assertEquals(1, jedis.dbSize()); jedis.set("bar", "foo"); jedis.move("bar", 1); String status = jedis.flushDB(); assertEquals("OK", status); assertEquals(0, jedis.dbSize()); jedis.select(1); assertEquals(1, jedis.dbSize()); assertEquals("OK", jedis.flushDB(FlushMode.SYNC)); assertEquals(0, jedis.dbSize()); // Binary jedis.select(0); jedis.set(bfoo, bbar); assertEquals(1, jedis.dbSize()); jedis.set(bbar, bfoo); jedis.move(bbar, 1); String bstatus = jedis.flushDB(); assertEquals("OK", bstatus); assertEquals(0, jedis.dbSize()); jedis.select(1); assertEquals(1, jedis.dbSize()); assertEquals("OK", jedis.flushDB(FlushMode.ASYNC)); assertEquals(0, jedis.dbSize()); } @Test @ConditionalOnEnv(value = TestEnvUtil.ENV_REDIS_ENTERPRISE, enabled = false) public void flushAll() { jedis.set("foo", "bar"); assertEquals(1, jedis.dbSize()); jedis.set("bar", "foo"); jedis.move("bar", 1); String status = jedis.flushAll(); assertEquals("OK", status); assertEquals(0, jedis.dbSize()); jedis.select(1); assertEquals(0, jedis.dbSize()); jedis.set("foo", "bar"); assertEquals(1, jedis.dbSize()); assertEquals("OK", jedis.flushAll(FlushMode.SYNC)); assertEquals(0, jedis.dbSize()); // Binary jedis.select(0); jedis.set(bfoo, bbar); assertEquals(1, jedis.dbSize()); jedis.set(bbar, bfoo); jedis.move(bbar, 1); String bstatus = jedis.flushAll(); assertEquals("OK", bstatus); assertEquals(0, jedis.dbSize()); jedis.select(1); assertEquals(0, jedis.dbSize()); jedis.set(bfoo, bbar); assertEquals(1, jedis.dbSize()); assertEquals("OK", jedis.flushAll(FlushMode.ASYNC)); assertEquals(0, jedis.dbSize()); } @Test public void persist() { jedis.setex("foo", 60 * 60, "bar"); assertTrue(jedis.ttl("foo") > 0); assertEquals(1, jedis.persist("foo")); assertEquals(-1, jedis.ttl("foo")); // Binary jedis.setex(bfoo, 60 * 60, bbar); assertTrue(jedis.ttl(bfoo) > 0); assertEquals(1, jedis.persist(bfoo)); assertEquals(-1, jedis.ttl(bfoo)); } @Test public void echo() { String result = jedis.echo("hello world"); assertEquals("hello world", result); // Binary byte[] bresult = jedis.echo(SafeEncoder.encode("hello world")); assertArrayEquals(SafeEncoder.encode("hello world"), bresult); } @Test public void dumpAndRestore() { jedis.set("foo1", "bar"); byte[] sv = jedis.dump("foo1"); jedis.restore("foo2", 0, sv); assertEquals("bar", jedis.get("foo2")); jedis.set(bfoo1, bbar); sv = jedis.dump(bfoo1); jedis.restore(bfoo2, 0, sv); assertArrayEquals(bbar, jedis.get(bfoo2)); } @Test @Disabled(value = "TODO: Regression in 8.0-M02 discarding restore idle time.") public void restoreParams() { // take a separate instance Jedis jedis2 = new Jedis(endpoint.getHost(), endpoint.getPort(), 500); jedis2.auth(endpoint.getPassword()); jedis2.flushAll(); jedis2.set("foo", "bar"); jedis.set("from", "a"); byte[] serialized = jedis.dump("from"); try { jedis2.restore("foo", 0, serialized); fail("Simple restore on a existing key should fail"); } catch (JedisDataException e) { // should be here } try { jedis2.restore("foo", 0, serialized, RestoreParams.restoreParams()); fail("Simple restore on a existing key should fail"); } catch (JedisDataException e) { // should be here } assertEquals("bar", jedis2.get("foo")); jedis2.restore("foo", 1000, serialized, RestoreParams.restoreParams().replace()); assertEquals("a", jedis2.get("foo")); assertTrue(jedis2.pttl("foo") <= 1000); jedis2.restore("bar", System.currentTimeMillis() + 1000, serialized, RestoreParams.restoreParams().replace().absTtl()); assertThat(jedis2.pttl("bar"), Matchers.lessThanOrEqualTo(1000l + TIME_SKEW)); jedis2.restore("bar1", 1000, serialized, RestoreParams.restoreParams().replace().idleTime(1000)); assertEquals(1000, jedis2.objectIdletime("bar1").longValue()); jedis2.close(); Jedis lfuJedis = new Jedis(lfuEndpoint.getHostAndPort(), lfuEndpoint.getClientConfigBuilder().timeoutMillis(500).build());; lfuJedis.restore("bar1", 1000, serialized, RestoreParams.restoreParams().replace().frequency(90)); assertEquals(90, lfuJedis.objectFreq("bar1").longValue()); lfuJedis.close(); } @Test @SinceRedisVersion(value = "7.0.0") public void pexpire() { assertEquals(0, jedis.pexpire("foo", 10000)); jedis.set("foo1", "bar1"); assertEquals(1, jedis.pexpire("foo1", 10000)); jedis.set("foo2", "bar2"); assertEquals(1, jedis.pexpire("foo2", 200000000000L)); assertEquals(0, jedis.pexpire("foo2", 10000000, ExpiryOption.NX)); assertEquals(1, jedis.pexpire("foo2", 10000000, ExpiryOption.XX)); assertEquals(0, jedis.pexpire("foo2", 10000, ExpiryOption.GT)); assertEquals(1, jedis.pexpire("foo2", 10000, ExpiryOption.LT)); long pttl = jedis.pttl("foo2"); assertTrue(pttl > 100L); // Binary assertEquals(0, jedis.pexpire(bfoo, 10000)); jedis.set(bfoo, bbar); assertEquals(1, jedis.pexpire(bfoo, 10000)); assertEquals(0, jedis.pexpire(bfoo, 10000, ExpiryOption.NX)); } @Test public void pexpireAt() { long unixTime = (System.currentTimeMillis()) + 10000; assertEquals(0, jedis.pexpireAt("foo", unixTime)); jedis.set("foo", "bar"); assertEquals(1, jedis.pexpireAt("foo", unixTime)); // Binary assertEquals(0, jedis.pexpireAt(bfoo, unixTime)); jedis.set(bfoo, bbar); assertEquals(1, jedis.pexpireAt(bfoo, unixTime)); } @Test @SinceRedisVersion(value = "7.0.0") public void pexpireTime() { long unixTime = (System.currentTimeMillis()) + 10000; jedis.set("foo", "bar"); jedis.pexpireAt("foo", unixTime); assertEquals(unixTime, jedis.pexpireTime("foo"), 0.0001); // Binary jedis.set(bfoo, bbar); jedis.pexpireAt(bfoo, unixTime); assertEquals(unixTime, jedis.pexpireTime(bfoo), 0.0001); } @Test public void pttl() { assertEquals(-2, jedis.pttl("foo")); jedis.set("foo", "bar"); assertEquals(-1, jedis.pttl("foo")); jedis.pexpire("foo", 20000); long pttl = jedis.pttl("foo"); assertTrue(pttl >= 0 && pttl <= 20000); // Binary assertEquals(-2, jedis.pttl(bfoo)); jedis.set(bfoo, bbar); assertEquals(-1, jedis.pttl(bfoo)); jedis.pexpire(bfoo, 20000); pttl = jedis.pttl(bfoo); assertTrue(pttl >= 0 && pttl <= 20000); } @Test public void psetex() { long pttl; jedis.psetex("foo", 200000000000L, "bar"); pttl = jedis.pttl("foo"); assertTrue(pttl > 100000000000L); // Binary jedis.psetex(bfoo, 200000000000L, bbar); pttl = jedis.pttl(bfoo); assertTrue(pttl > 100000000000L); } @Test public void scan() { jedis.set("b", "b"); jedis.set("a", "a"); ScanResult result = jedis.scan(SCAN_POINTER_START); assertEquals(SCAN_POINTER_START, result.getCursor()); assertFalse(result.getResult().isEmpty()); // binary ScanResult bResult = jedis.scan(SCAN_POINTER_START_BINARY); assertArrayEquals(SCAN_POINTER_START_BINARY, bResult.getCursorAsBytes()); assertFalse(bResult.getResult().isEmpty()); } @Test public void scanMatch() { ScanParams params = new ScanParams(); params.match("a*"); jedis.set("b", "b"); jedis.set("a", "a"); jedis.set("aa", "aa"); ScanResult result = jedis.scan(SCAN_POINTER_START, params); assertEquals(SCAN_POINTER_START, result.getCursor()); assertFalse(result.getResult().isEmpty()); // binary params = new ScanParams(); params.match(bfoostar); jedis.set(bfoo1, bbar); jedis.set(bfoo2, bbar); jedis.set(bfoo3, bbar); ScanResult bResult = jedis.scan(SCAN_POINTER_START_BINARY, params); assertArrayEquals(SCAN_POINTER_START_BINARY, bResult.getCursorAsBytes()); assertFalse(bResult.getResult().isEmpty()); } @Test public void scanCount() { ScanParams params = new ScanParams(); params.count(2); for (int i = 0; i < 10; i++) { jedis.set("a" + i, "a" + i); } ScanResult result = jedis.scan(SCAN_POINTER_START, params); assertFalse(result.getResult().isEmpty()); // binary params = new ScanParams(); params.count(2); jedis.set(bfoo1, bbar); jedis.set(bfoo2, bbar); jedis.set(bfoo3, bbar); ScanResult bResult = jedis.scan(SCAN_POINTER_START_BINARY, params); assertFalse(bResult.getResult().isEmpty()); } @Test public void scanType() { ScanParams noParams = new ScanParams(); ScanParams pagingParams = new ScanParams().count(4); jedis.set("a", "a"); jedis.hset("b", "b", "b"); jedis.set("c", "c"); jedis.sadd("d", "d"); jedis.set("e", "e"); jedis.zadd("f", 0d, "f"); jedis.set("g", "g"); // string ScanResult scanResult; scanResult = jedis.scan(SCAN_POINTER_START, pagingParams, "string"); assertFalse(scanResult.isCompleteIteration()); int page1Count = scanResult.getResult().size(); scanResult = jedis.scan(scanResult.getCursor(), pagingParams, "string"); assertTrue(scanResult.isCompleteIteration()); int page2Count = scanResult.getResult().size(); assertEquals(4, page1Count + page2Count); scanResult = jedis.scan(SCAN_POINTER_START, noParams, "hash"); assertEquals(Collections.singletonList("b"), scanResult.getResult()); scanResult = jedis.scan(SCAN_POINTER_START, noParams, "set"); assertEquals(Collections.singletonList("d"), scanResult.getResult()); scanResult = jedis.scan(SCAN_POINTER_START, noParams, "zset"); assertEquals(Collections.singletonList("f"), scanResult.getResult()); // binary final byte[] string = "string".getBytes(); final byte[] hash = "hash".getBytes(); final byte[] set = "set".getBytes(); final byte[] zset = "zset".getBytes(); ScanResult binaryResult; jedis.set("a", "a"); jedis.hset("b", "b", "b"); jedis.set("c", "c"); jedis.sadd("d", "d"); jedis.set("e", "e"); jedis.zadd("f", 0d, "f"); jedis.set("g", "g"); binaryResult = jedis.scan(SCAN_POINTER_START_BINARY, pagingParams, string); assertFalse(binaryResult.isCompleteIteration()); page1Count = binaryResult.getResult().size(); binaryResult = jedis.scan(binaryResult.getCursorAsBytes(), pagingParams, string); assertTrue(binaryResult.isCompleteIteration()); page2Count = binaryResult.getResult().size(); assertEquals(4, page1Count + page2Count); binaryResult = jedis.scan(SCAN_POINTER_START_BINARY, noParams, hash); AssertUtil.assertByteArrayListEquals(Collections.singletonList(new byte[]{98}), binaryResult.getResult()); binaryResult = jedis.scan(SCAN_POINTER_START_BINARY, noParams, set); AssertUtil.assertByteArrayListEquals(Collections.singletonList(new byte[]{100}), binaryResult.getResult()); binaryResult = jedis.scan(SCAN_POINTER_START_BINARY, noParams, zset); AssertUtil.assertByteArrayListEquals(Collections.singletonList(new byte[]{102}), binaryResult.getResult()); } @Test public void scanIsCompleteIteration() { for (int i = 0; i < 100; i++) { jedis.set("a" + i, "a" + i); } ScanResult result = jedis.scan(SCAN_POINTER_START); // note: in theory Redis would be allowed to already return all results on the 1st scan, // but in practice this never happens for data sets greater than a few tens // see: https://redis.io/commands/scan#number-of-elements-returned-at-every-scan-call assertFalse(result.isCompleteIteration()); result = scanCompletely(result.getCursor()); assertNotNull(result); assertTrue(result.isCompleteIteration()); } private ScanResult scanCompletely(String cursor) { ScanResult scanResult; do { scanResult = jedis.scan(cursor); cursor = scanResult.getCursor(); } while (!SCAN_POINTER_START.equals(scanResult.getCursor())); return scanResult; } @Test public void setNxExAndGet() { assertEquals("OK", jedis.set("hello", "world", SetParams.setParams().nx().ex(expireSeconds))); assertEquals("world", jedis.get("hello")); assertNull(jedis.set("hello", "bar", SetParams.setParams().nx().ex(expireSeconds))); assertEquals("world", jedis.get("hello")); long ttl = jedis.ttl("hello"); assertTrue(ttl > 0 && ttl <= expireSeconds); // binary byte[] bworld = { 0x77, 0x6F, 0x72, 0x6C, 0x64 }; byte[] bhello = { 0x68, 0x65, 0x6C, 0x6C, 0x6F }; assertEquals("OK", jedis.set(bworld, bhello, SetParams.setParams().nx().ex(expireSeconds))); assertArrayEquals(bhello, jedis.get(bworld)); assertNull(jedis.set(bworld, bbar, SetParams.setParams().nx().ex(expireSeconds))); assertArrayEquals(bhello, jedis.get(bworld)); long bttl = jedis.ttl(bworld); assertTrue(bttl > 0 && bttl <= expireSeconds); } @Test public void setGetOptionTest() { assertEquals("OK", jedis.set("hello", "world")); // GET old value assertEquals("world", jedis.setGet("hello", "jedis")); assertEquals("jedis", jedis.get("hello")); // GET null value assertNull(jedis.setGet("key", "value")); } @Test public void setGet() { assertEquals("OK", jedis.set("hello", "world")); // GET old value assertEquals("world", jedis.setGet("hello", "jedis", SetParams.setParams())); assertEquals("jedis", jedis.get("hello")); // GET null value assertNull(jedis.setGet("key", "value", SetParams.setParams())); } @Test public void sendCommandTest() { Object obj = jedis.sendCommand(SET, "x", "1"); String returnValue = SafeEncoder.encode((byte[]) obj); assertEquals("OK", returnValue); obj = jedis.sendCommand(GET, "x"); returnValue = SafeEncoder.encode((byte[]) obj); assertEquals("1", returnValue); jedis.sendCommand(RPUSH, "foo", "a"); jedis.sendCommand(RPUSH, "foo", "b"); jedis.sendCommand(RPUSH, "foo", "c"); obj = jedis.sendCommand(LRANGE, "foo", "0", "2"); List list = (List) obj; List expected = new ArrayList<>(3); expected.add("a".getBytes()); expected.add("b".getBytes()); expected.add("c".getBytes()); for (int i = 0; i < 3; i++) assertArrayEquals(expected.get(i), list.get(i)); assertEquals("PONG", SafeEncoder.encode((byte[]) jedis.sendCommand(PING))); } @Test public void sendBlockingCommandTest() { assertNull(jedis.sendBlockingCommand(BLPOP, "foo", Long.toString(1L))); jedis.sendCommand(RPUSH, "foo", "bar"); assertEquals(Arrays.asList("foo", "bar"), SafeEncoder.encodeObject(jedis.sendBlockingCommand(BLPOP, "foo", Long.toString(1L)))); assertNull(jedis.sendBlockingCommand(BLPOP, "foo", Long.toString(1L))); } @Test public void encodeCompleteResponsePing() { assertEquals("PONG", SafeEncoder.encodeObject(jedis.sendCommand(PING))); } @Test public void encodeCompleteResponseHgetall() { Assumptions.assumeFalse(protocol == RedisProtocol.RESP3); HashMap entries = new HashMap<>(); entries.put("foo", "bar"); entries.put("foo2", "bar2"); jedis.hset("hash:test:encode", entries); List encodeObj = (List) SafeEncoder.encodeObject(jedis.sendCommand(HGETALL, "hash:test:encode")); assertEquals(4, encodeObj.size()); entries.forEach((k, v) -> { assertThat((Iterable) encodeObj, Matchers.hasItem(k)); assertEquals(v, findValueFromMapAsList(encodeObj, k)); }); } @Test public void encodeCompleteResponseHgetallResp3() { Assumptions.assumeTrue(protocol == RedisProtocol.RESP3); HashMap entries = new HashMap<>(); entries.put("foo", "bar"); entries.put("foo2", "bar2"); jedis.hset("hash:test:encode", entries); List encodeObj = (List) SafeEncoder.encodeObject(jedis.sendCommand(HGETALL, "hash:test:encode")); assertEquals(2, encodeObj.size()); encodeObj.forEach(kv -> { assertThat(entries, Matchers.hasEntry(kv.getKey(), kv.getValue())); }); } @Test public void encodeCompleteResponseXinfoStream() { Assumptions.assumeFalse(protocol == RedisProtocol.RESP3); HashMap entry = new HashMap<>(); entry.put("foo", "bar"); StreamEntryID entryID = jedis.xadd("mystream", StreamEntryID.NEW_ENTRY, entry); jedis.xgroupCreate("mystream", "mygroup", null, false); Object obj = jedis.sendCommand(XINFO, "STREAM", "mystream"); List encodeObj = (List) SafeEncoder.encodeObject(obj); assertThat(encodeObj.size(), Matchers.greaterThanOrEqualTo(14)); assertEquals( 0, encodeObj.size() % 2, "must have even number of elements"); // must be even assertEquals(1L, findValueFromMapAsList(encodeObj, "length")); assertEquals(entryID.toString(), findValueFromMapAsList(encodeObj, "last-generated-id")); List entryAsList = new ArrayList<>(2); entryAsList.add("foo"); entryAsList.add("bar"); assertEquals(entryAsList, ((List) findValueFromMapAsList(encodeObj, "first-entry")).get(1)); assertEquals(entryAsList, ((List) findValueFromMapAsList(encodeObj, "last-entry")).get(1)); } @Test public void encodeCompleteResponseXinfoStreamResp3() { Assumptions.assumeTrue(protocol == RedisProtocol.RESP3); HashMap entry = new HashMap<>(); entry.put("foo", "bar"); StreamEntryID entryID = jedis.xadd("mystream", StreamEntryID.NEW_ENTRY, entry); jedis.xgroupCreate("mystream", "mygroup", null, false); Object obj = jedis.sendCommand(XINFO, "STREAM", "mystream"); List encodeObj = (List) SafeEncoder.encodeObject(obj); assertThat(encodeObj.size(), Matchers.greaterThanOrEqualTo(7)); assertEquals(1L, findValueFromMapAsKeyValueList(encodeObj, "length")); assertEquals(entryID.toString(), findValueFromMapAsKeyValueList(encodeObj, "last-generated-id")); List entryAsList = new ArrayList<>(2); entryAsList.add("foo"); entryAsList.add("bar"); assertEquals(entryAsList, ((List) findValueFromMapAsKeyValueList(encodeObj, "first-entry")).get(1)); assertEquals(entryAsList, ((List) findValueFromMapAsKeyValueList(encodeObj, "last-entry")).get(1)); } private Object findValueFromMapAsList(List list, Object key) { for (int i = 0; i < list.size(); i += 2) { if (key.equals(list.get(i))) { return list.get(i + 1); } } return null; } private Object findValueFromMapAsKeyValueList(List list, Object key) { for (KeyValue kv : list) { if (key.equals(kv.getKey())) { return kv.getValue(); } } return null; } @Test @ConditionalOnEnv(value = TestEnvUtil.ENV_REDIS_ENTERPRISE, enabled = false) public void copy() { assertFalse(jedis.copy("unknown", "foo", false)); jedis.set("foo1", "bar"); assertTrue(jedis.copy("foo1", "foo2", false)); assertEquals("bar", jedis.get("foo2")); // with destinationDb assertTrue(jedis.copy("foo1", "foo3", 2, false)); jedis.select(2); assertEquals("bar", jedis.get("foo3")); jedis.select(0); // getting back to original db, for next tests // replace jedis.set("foo1", "bar1"); assertTrue(jedis.copy("foo1", "foo2", true)); assertEquals("bar1", jedis.get("foo2")); // Binary assertFalse(jedis.copy(bfoobar, bfoo, false)); jedis.set(bfoo1, bbar); assertTrue(jedis.copy(bfoo1, bfoo2, false)); assertArrayEquals(bbar, jedis.get(bfoo2)); // with destinationDb assertTrue(jedis.copy(bfoo1, bfoo3, 3, false)); jedis.select(3); assertArrayEquals(bbar, jedis.get(bfoo3)); jedis.select(0); // getting back to original db, for next tests // replace jedis.set(bfoo1, bbar1); assertTrue(jedis.copy(bfoo1, bfoo2, true)); assertArrayEquals(bbar1, jedis.get(bfoo2)); } @Test @ConditionalOnEnv(value = TestEnvUtil.ENV_REDIS_ENTERPRISE, enabled = false) public void reset() { // response test String status = jedis.reset(); assertEquals("RESET", status); // auth reset String counter = "counter"; Exception ex1 = assertThrows(JedisDataException.class, () -> { jedis.set(counter, "1"); }); assertEquals("NOAUTH Authentication required.", ex1.getMessage()); // multi reset jedis.auth(endpoint.getPassword()); jedis.set(counter, "1"); Transaction trans = jedis.multi(); trans.incr(counter); jedis.reset(); Exception ex2 = assertThrows(JedisDataException.class, trans::exec); assertEquals("EXECABORT Transaction discarded because of: NOAUTH Authentication required.", ex2.getMessage()); jedis.auth(endpoint.getPassword()); assertEquals("1", jedis.get(counter)); } @Test @EnabledOnCommand("DELEX") public void set_ex_ifeq_then_delex() { String k = "k:set-ex-ifeq"; // Initial set with EX assertEquals("OK", jedis.set(k, "v1", SetParams.setParams().ex(100))); assertTrue(jedis.ttl(k) > 0); // Conditional update with IFEQ + EX assertEquals("OK", jedis.set(k, "v2", SetParams.setParams().ex(200).condition(CompareCondition.valueEq("v1")))); assertEquals("v2", jedis.get(k)); assertTrue(jedis.ttl(k) > 100); // Delete with DELEX using value condition assertEquals(0, jedis.delex(k, CompareCondition.valueEq("wrong"))); assertEquals(1, jedis.delex(k, CompareCondition.valueEq("v2"))); assertFalse(jedis.exists(k)); } @Test @EnabledOnCommand("DELEX") public void set_exAt_ifne_then_delex() { String k = "k:set-exat-ifne"; long expiryTimestamp = (System.currentTimeMillis() / 1000) + 300; // Initial set jedis.set(k, "v1"); // Conditional update with IFNE + EXAT assertEquals("OK", jedis.set(k, "v2", SetParams.setParams().exAt(expiryTimestamp).condition(CompareCondition.valueNe("v2")))); assertEquals("v2", jedis.get(k)); assertTrue(jedis.ttl(k) > 200); // Delete with DELEX using value condition assertEquals(0, jedis.delex(k, CompareCondition.valueNe("v2"))); assertEquals(1, jedis.delex(k, CompareCondition.valueNe("wrong"))); assertFalse(jedis.exists(k)); } @Test @EnabledOnCommand("DELEX") public void setGet_px_ifdne_then_delex() { String k = "k:setget-px-ifdne"; String wrongKey = "wrong"; // Initial set jedis.set(k, "A"); jedis.set(wrongKey, "wrong"); String digestBefore = jedis.digestKey(k); String digestWrong = jedis.digestKey(wrongKey); // digest for different value // Conditional setGet with IFDNE + PX (digest not equal) assertEquals("A", jedis.setGet(k, "B", SetParams.setParams().px(100000).condition(CompareCondition.digestNe(digestWrong)))); assertEquals("B", jedis.get(k)); assertTrue(jedis.pttl(k) > 90000); // Delete with DELEX using digest condition String digestAfter = jedis.digestKey(k); assertEquals(0, jedis.delex(k, CompareCondition.digestNe(digestAfter))); assertEquals(1, jedis.delex(k, CompareCondition.digestNe(digestBefore))); assertFalse(jedis.exists(k)); } @Test @EnabledOnCommand("DELEX") public void setGet_pxAt_ifdeq_then_delex() { String k = "k:setget-pxat-ifdeq"; long expiryTimestampMs = System.currentTimeMillis() + 300000; // Initial set jedis.set(k, "X"); String digestX = jedis.digestKey(k); // Conditional setGet with IFDEQ + PXAT (digest equal) assertEquals("X", jedis.setGet(k, "Y", SetParams.setParams().pxAt(expiryTimestampMs) .condition(CompareCondition.digestEq(digestX)))); assertEquals("Y", jedis.get(k)); assertTrue(jedis.pttl(k) > 200000); // Delete with DELEX using digest condition String digestY = jedis.digestKey(k); assertEquals(0, jedis.delex(k, CompareCondition.digestEq(digestX))); assertEquals(1, jedis.delex(k, CompareCondition.digestEq(digestY))); assertFalse(jedis.exists(k)); } } ================================================ FILE: src/test/java/redis/clients/jedis/commands/jedis/BinaryValuesCommandsTest.java ================================================ package redis.clients.jedis.commands.jedis; import static org.junit.jupiter.api.Assertions.assertArrayEquals; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertNull; import static org.junit.jupiter.api.Assertions.assertTrue; import static redis.clients.jedis.Protocol.Command.BLPOP; import static redis.clients.jedis.Protocol.Command.GET; import static redis.clients.jedis.Protocol.Command.LRANGE; import static redis.clients.jedis.Protocol.Command.RPUSH; import static redis.clients.jedis.Protocol.Command.SET; import static redis.clients.jedis.params.SetParams.setParams; import static redis.clients.jedis.util.AssertUtil.assertByteArrayListEquals; import java.util.ArrayList; import java.util.List; import java.util.stream.Stream; import io.redis.test.annotations.EnabledOnCommand; import io.redis.test.annotations.ConditionalOnEnv; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.junit.jupiter.params.ParameterizedClass; import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.Arguments; import org.junit.jupiter.params.provider.MethodSource; import redis.clients.jedis.Protocol; import redis.clients.jedis.RedisProtocol; import redis.clients.jedis.exceptions.JedisDataException; import redis.clients.jedis.params.GetExParams; import redis.clients.jedis.params.MSetExParams; import redis.clients.jedis.util.SafeEncoder; import redis.clients.jedis.util.TestEnvUtil; @ParameterizedClass @MethodSource("redis.clients.jedis.commands.CommandsTestsParameters#respVersions") public class BinaryValuesCommandsTest extends JedisCommandsTestBase { byte[] bfoo = { 0x01, 0x02, 0x03, 0x04 }; byte[] bbar = { 0x05, 0x06, 0x07, 0x08 }; byte[] bxx = { 0x78, 0x78 }; byte[] bnx = { 0x6E, 0x78 }; byte[] bex = { 0x65, 0x78 }; byte[] bpx = { 0x70, 0x78 }; int expireSeconds = 2; long expireMillis = expireSeconds * 1000; byte[] binaryValue; public BinaryValuesCommandsTest(RedisProtocol protocol) { super(protocol); } @BeforeEach public void startUp() { StringBuilder sb = new StringBuilder(); for (int n = 0; n < 1000; n++) { sb.append("A"); } binaryValue = sb.toString().getBytes(); } @Test public void setAndGet() { assertEquals("OK", jedis.set(bfoo, binaryValue)); assertArrayEquals(binaryValue, jedis.get(bfoo)); assertNull(jedis.get(bbar)); } @Test public void setNxExAndGet() { assertEquals("OK", jedis.set(bfoo, binaryValue, setParams().nx().ex(expireSeconds))); assertArrayEquals(binaryValue, jedis.get(bfoo)); assertNull(jedis.get(bbar)); } @Test public void setIfNotExistAndGet() { assertEquals("OK", jedis.set(bfoo, binaryValue)); // nx should fail if value exists assertNull(jedis.set(bfoo, binaryValue, setParams().nx().ex(expireSeconds))); assertArrayEquals(binaryValue, jedis.get(bfoo)); assertNull(jedis.get(bbar)); } @Test public void setIfExistAndGet() { assertEquals("OK", jedis.set(bfoo, binaryValue)); // nx should fail if value exists assertEquals("OK", jedis.set(bfoo, binaryValue, setParams().xx().ex(expireSeconds))); assertArrayEquals(binaryValue, jedis.get(bfoo)); assertNull(jedis.get(bbar)); } @Test public void setFailIfNotExistAndGet() { // xx should fail if value does NOT exists assertNull(jedis.set(bfoo, binaryValue, setParams().xx().ex(expireSeconds))); } @Test public void setAndExpireMillis() { assertEquals("OK", jedis.set(bfoo, binaryValue, setParams().nx().px(expireMillis))); long ttl = jedis.ttl(bfoo); assertTrue(ttl > 0 && ttl <= expireSeconds); } @Test public void setAndExpire() { assertEquals("OK", jedis.set(bfoo, binaryValue, setParams().nx().ex(expireSeconds))); long ttl = jedis.ttl(bfoo); assertTrue(ttl > 0 && ttl <= expireSeconds); } @Test public void setAndKeepttl() { assertEquals("OK", jedis.set(bfoo, binaryValue, setParams().nx().ex(expireSeconds))); assertEquals("OK", jedis.set(bfoo, binaryValue, setParams().keepttl())); assertEquals("OK", jedis.set(bfoo, binaryValue, setParams().keepTtl())); long ttl = jedis.ttl(bfoo); assertTrue(0 < ttl && ttl <= expireSeconds); jedis.set(bfoo, binaryValue); ttl = jedis.ttl(bfoo); assertTrue(ttl < 0); } @Test public void setAndPxat() { assertEquals("OK", jedis.set(bfoo, binaryValue, setParams().nx().pxAt(System.currentTimeMillis() + expireMillis))); long ttl = jedis.ttl(bfoo); assertTrue(ttl > 0 && ttl <= expireSeconds); } @Test public void setAndExat() { assertEquals("OK", jedis.set(bfoo, binaryValue, setParams().nx().exAt(System.currentTimeMillis() / 1000 + expireSeconds))); long ttl = jedis.ttl(bfoo); assertTrue(ttl > 0 && ttl <= expireSeconds); } @Test public void getSet() { assertNull(jedis.getSet(bfoo, binaryValue)); assertArrayEquals(binaryValue, jedis.get(bfoo)); } @Test public void getDel() { assertEquals("OK", jedis.set(bfoo, bbar)); assertArrayEquals(bbar, jedis.getDel(bfoo)); assertNull(jedis.get(bfoo)); } @Test public void getEx() { assertNull(jedis.getEx(bfoo, GetExParams.getExParams().ex(1))); jedis.set(bfoo, bbar); assertArrayEquals(bbar, jedis.getEx(bfoo, GetExParams.getExParams().ex(10))); long ttl = jedis.ttl(bfoo); assertTrue(ttl > 0 && ttl <= 10); assertArrayEquals(bbar, jedis.getEx(bfoo, GetExParams.getExParams().px(20000l))); ttl = jedis.ttl(bfoo); assertTrue(ttl > 10 && ttl <= 20); assertArrayEquals(bbar, jedis.getEx(bfoo, GetExParams.getExParams().exAt(System.currentTimeMillis() / 1000 + 30))); ttl = jedis.ttl(bfoo); assertTrue(ttl > 20 && ttl <= 30); assertArrayEquals(bbar, jedis.getEx(bfoo, GetExParams.getExParams().pxAt(System.currentTimeMillis() + 40000l))); ttl = jedis.ttl(bfoo); assertTrue(ttl > 30 && ttl <= 40); assertArrayEquals(bbar, jedis.getEx(bfoo, GetExParams.getExParams().persist())); assertEquals(-1L, jedis.ttl(bfoo)); } @Test public void mget() { List values = jedis.mget(bfoo, bbar); List expected = new ArrayList<>(); expected.add(null); expected.add(null); assertByteArrayListEquals(expected, values); jedis.set(bfoo, binaryValue); expected = new ArrayList<>(); expected.add(binaryValue); expected.add(null); assertByteArrayListEquals(expected, jedis.mget(bfoo, bbar)); jedis.set(bbar, bfoo); expected = new ArrayList<>(); expected.add(binaryValue); expected.add(bfoo); assertByteArrayListEquals(expected, jedis.mget(bfoo, bbar)); } @Test public void setnx() { assertEquals(1, jedis.setnx(bfoo, binaryValue)); assertArrayEquals(binaryValue, jedis.get(bfoo)); assertEquals(0, jedis.setnx(bfoo, bbar)); assertArrayEquals(binaryValue, jedis.get(bfoo)); } @Test public void setex() { assertEquals("OK", jedis.setex(bfoo, 20, binaryValue)); long ttl = jedis.ttl(bfoo); assertTrue(ttl > 0 && ttl <= 20); } @Test public void mset() { assertEquals("OK", jedis.mset(bfoo, binaryValue, bbar, bfoo)); assertArrayEquals(binaryValue, jedis.get(bfoo)); assertArrayEquals(bfoo, jedis.get(bbar)); } @Test @ConditionalOnEnv(value = TestEnvUtil.ENV_REDIS_ENTERPRISE, enabled = false) public void msetnx() { assertEquals(1, jedis.msetnx(bfoo, binaryValue, bbar, bfoo)); assertArrayEquals(binaryValue, jedis.get(bfoo)); assertArrayEquals(bfoo, jedis.get(bbar)); assertEquals(0, jedis.msetnx(bfoo, bbar, "bar2".getBytes(), "foo2".getBytes())); assertArrayEquals(binaryValue, jedis.get(bfoo)); assertArrayEquals(bfoo, jedis.get(bbar)); } @Test public void incr() { assertEquals(1, jedis.incr(bfoo)); assertEquals(2, jedis.incr(bfoo)); } @Test public void incrWrongValue() { Assertions.assertThrows(JedisDataException.class, () -> { jedis.set(bfoo, binaryValue); jedis.incr(bfoo); }); } @Test public void incrBy() { assertEquals(2, jedis.incrBy(bfoo, 2)); assertEquals(4, jedis.incrBy(bfoo, 2)); } @Test public void incrByWrongValue() { Assertions.assertThrows(JedisDataException.class, () -> { jedis.set(bfoo, binaryValue); jedis.incrBy(bfoo, 2); }); } @Test public void incrByFloat() { assertEquals(10.5, jedis.incrByFloat(bfoo, 10.5), 0.0); assertEquals(10.6, jedis.incrByFloat(bfoo, 0.1), 0.0); } @Test public void decr() { assertEquals(-1, jedis.decr(bfoo)); assertEquals(-2, jedis.decr(bfoo)); } @Test public void decrWrongValue() { Assertions.assertThrows(JedisDataException.class, () -> { jedis.set(bfoo, binaryValue); jedis.decr(bfoo); }); } @Test public void decrBy() { assertEquals(-2, jedis.decrBy(bfoo, 2)); assertEquals(-4, jedis.decrBy(bfoo, 2)); } @Test public void decrByWrongValue() { Assertions.assertThrows(JedisDataException.class, () -> { jedis.set(bfoo, binaryValue); jedis.decrBy(bfoo, 2); }); } @Test public void append() { byte[] first512 = new byte[512]; System.arraycopy(binaryValue, 0, first512, 0, 512); assertEquals(512, jedis.append(bfoo, first512)); assertArrayEquals(first512, jedis.get(bfoo)); byte[] rest = new byte[binaryValue.length - 512]; System.arraycopy(binaryValue, 512, rest, 0, binaryValue.length - 512); assertEquals(binaryValue.length, jedis.append(bfoo, rest)); assertArrayEquals(binaryValue, jedis.get(bfoo)); } @Test @ConditionalOnEnv(value = TestEnvUtil.ENV_REDIS_ENTERPRISE, enabled = false) public void substr() { jedis.set(bfoo, binaryValue); byte[] first512 = new byte[512]; System.arraycopy(binaryValue, 0, first512, 0, 512); byte[] rfirst512 = jedis.substr(bfoo, 0, 511); assertArrayEquals(first512, rfirst512); byte[] last512 = new byte[512]; System.arraycopy(binaryValue, binaryValue.length - 512, last512, 0, 512); assertArrayEquals(last512, jedis.substr(bfoo, -512, -1)); assertArrayEquals(binaryValue, jedis.substr(bfoo, 0, -1)); assertArrayEquals(last512, jedis.substr(bfoo, binaryValue.length - 512, 100000)); } @Test public void strlen() { jedis.set(bfoo, binaryValue); assertEquals(binaryValue.length, jedis.strlen(bfoo)); } @Test public void setGet() { assertEquals("OK", jedis.set(bfoo, bbar)); // GET old value assertArrayEquals(bbar, jedis.setGet(bfoo, binaryValue)); assertArrayEquals(binaryValue, jedis.get(bfoo)); // GET null value assertNull(jedis.setGet(bbar, bfoo)); } @Test public void sendCommandTest() { Object obj = jedis.sendCommand(SET, "x".getBytes(), "1".getBytes()); String returnValue = SafeEncoder.encode((byte[]) obj); assertEquals("OK", returnValue); obj = jedis.sendCommand(GET, "x".getBytes()); returnValue = SafeEncoder.encode((byte[]) obj); assertEquals("1", returnValue); jedis.sendCommand(RPUSH, "foo".getBytes(), "a".getBytes()); jedis.sendCommand(RPUSH, "foo".getBytes(), "b".getBytes()); jedis.sendCommand(RPUSH, "foo".getBytes(), "c".getBytes()); obj = jedis.sendCommand(LRANGE, "foo".getBytes(), "0".getBytes(), "2".getBytes()); List list = (List) obj; List expected = new ArrayList<>(3); expected.add("a".getBytes()); expected.add("b".getBytes()); expected.add("c".getBytes()); for (int i = 0; i < 3; i++) assertArrayEquals(expected.get(i), list.get(i)); } @Test public void sendBlockingCommandTest() { assertNull(jedis.sendBlockingCommand(BLPOP, bfoo, Protocol.toByteArray(1L))); jedis.sendCommand(RPUSH, bfoo, bbar); List blpop = (List) jedis.sendBlockingCommand(BLPOP, bfoo, Protocol.toByteArray(1L)); assertEquals(2, blpop.size()); assertArrayEquals(bfoo, blpop.get(0)); assertArrayEquals(bbar, blpop.get(1)); assertNull(jedis.sendBlockingCommand(BLPOP, bfoo, Protocol.toByteArray(1L))); } // MSETEX NX + expiration matrix (binary) static Stream msetexNxArgsProvider() { return java.util.stream.Stream.of(Arguments.of("EX", new MSetExParams().nx().ex(5)), Arguments.of("PX", new MSetExParams().nx().px(5000)), Arguments.of("EXAT", new MSetExParams().nx().exAt(System.currentTimeMillis() / 1000 + 5)), Arguments.of("PXAT", new MSetExParams().nx().pxAt(System.currentTimeMillis() + 5000)), Arguments.of("KEEPTTL", new MSetExParams().nx().keepTtl())); } @ParameterizedTest(name = "MSETEX NX + {0} (binary)") @MethodSource("msetexNxArgsProvider") @EnabledOnCommand("MSETEX") public void msetexNx_binary_parametrized(String optionLabel, MSetExParams params) { byte[] k1 = "{t}msetex:jb:k1".getBytes(); byte[] k2 = "{t}msetex:jb:k2".getBytes(); boolean result = jedis.msetex(params, k1, "v1".getBytes(), k2, "v2".getBytes()); assertTrue(result); long ttl = jedis.ttl(k1); if ("KEEPTTL".equals(optionLabel)) { assertEquals(-1L, ttl); } else { assertTrue(ttl > 0L); } } } ================================================ FILE: src/test/java/redis/clients/jedis/commands/jedis/BitCommandsTest.java ================================================ package redis.clients.jedis.commands.jedis; import java.util.List; import io.redis.test.annotations.SinceRedisVersion; import io.redis.test.annotations.ConditionalOnEnv; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Tag; import org.junit.jupiter.params.ParameterizedClass; import org.junit.jupiter.params.provider.MethodSource; import redis.clients.jedis.Protocol; import redis.clients.jedis.RedisProtocol; import redis.clients.jedis.args.BitCountOption; import redis.clients.jedis.args.BitOP; import redis.clients.jedis.exceptions.JedisDataException; import redis.clients.jedis.params.BitPosParams; import redis.clients.jedis.util.SafeEncoder; import redis.clients.jedis.util.TestEnvUtil; import static org.junit.jupiter.api.Assertions.assertArrayEquals; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertThrows; import static org.junit.jupiter.api.Assertions.assertTrue; import static org.junit.jupiter.api.Assertions.fail; @ParameterizedClass @MethodSource("redis.clients.jedis.commands.CommandsTestsParameters#respVersions") @Tag("integration") public class BitCommandsTest extends JedisCommandsTestBase { public BitCommandsTest(RedisProtocol protocol) { super(protocol); } @Test public void setAndgetbit() { assertFalse(jedis.setbit("foo", 0, true)); assertTrue(jedis.getbit("foo", 0)); // Binary assertFalse(jedis.setbit("bfoo".getBytes(), 0, true)); assertTrue(jedis.getbit("bfoo".getBytes(), 0)); } @Test public void bitpos() { String foo = "foo"; jedis.set(foo, String.valueOf(0)); // string "0" with bits: 0011 0000 jedis.setbit(foo, 3, true); jedis.setbit(foo, 7, true); jedis.setbit(foo, 13, true); jedis.setbit(foo, 39, true); /* * bit: 00110001 / 00000100 / 00000000 / 00000000 / 00000001 * byte: 0 1 2 3 4 */ long offset = jedis.bitpos(foo, true); assertEquals(2, offset); offset = jedis.bitpos(foo, false); assertEquals(0, offset); offset = jedis.bitpos(foo, true, new BitPosParams(1)); assertEquals(13, offset); offset = jedis.bitpos(foo, false, new BitPosParams(1)); assertEquals(8, offset); offset = jedis.bitpos(foo, true, new BitPosParams(2, 3)); assertEquals(-1, offset); offset = jedis.bitpos(foo, false, new BitPosParams(2, 3)); assertEquals(16, offset); offset = jedis.bitpos(foo, true, new BitPosParams(3, 4)); assertEquals(39, offset); } @Test public void bitposBinary() { // binary byte[] bfoo = { 0x01, 0x02, 0x03, 0x04 }; jedis.set(bfoo, Protocol.toByteArray(0)); // bits: 0011 0000 jedis.setbit(bfoo, 3, true); jedis.setbit(bfoo, 7, true); jedis.setbit(bfoo, 13, true); jedis.setbit(bfoo, 39, true); /* * bit: 00110001 / 00000100 / 00000000 / 00000000 / 00000001 * byte: 0 1 2 3 4 */ long offset = jedis.bitpos(bfoo, true); assertEquals(2, offset); offset = jedis.bitpos(bfoo, false); assertEquals(0, offset); offset = jedis.bitpos(bfoo, true, new BitPosParams(1)); assertEquals(13, offset); offset = jedis.bitpos(bfoo, false, new BitPosParams(1)); assertEquals(8, offset); offset = jedis.bitpos(bfoo, true, new BitPosParams(2, 3)); assertEquals(-1, offset); offset = jedis.bitpos(bfoo, false, new BitPosParams(2, 3)); assertEquals(16, offset); offset = jedis.bitpos(bfoo, true, new BitPosParams(3, 4)); assertEquals(39, offset); } @Test public void bitposWithNoMatchingBitExist() { String foo = "foo"; jedis.set(foo, String.valueOf(0)); for (int idx = 0; idx < 8; idx++) { jedis.setbit(foo, idx, true); } /* * bit: 11111111 * byte: 0 */ long offset = jedis.bitpos(foo, false); // offset should be last index + 1 assertEquals(8, offset); } @Test public void bitposWithNoMatchingBitExistWithinRange() { String foo = "foo"; jedis.set(foo, String.valueOf(0)); for (int idx = 0; idx < 8 * 5; idx++) { jedis.setbit(foo, idx, true); } /* * bit: 11111111 / 11111111 / 11111111 / 11111111 / 11111111 * byte: 0 1 2 3 4 */ long offset = jedis.bitpos(foo, false, new BitPosParams(2, 3)); // offset should be -1 assertEquals(-1, offset); } @Test @SinceRedisVersion(value = "7.0.0", message = "7.0.0 Added the BYTE|BIT option.") public void bitposModifier() { jedis.set("mykey", "\\x00\\xff\\xf0"); assertEquals(0, jedis.bitpos("mykey", false)); assertEquals(1, jedis.bitpos("mykey", true)); assertEquals(1, jedis.bitpos("mykey", true, BitPosParams.bitPosParams())); assertEquals(18, jedis.bitpos("mykey", true, BitPosParams.bitPosParams().start(2))); assertEquals(18, jedis.bitpos("mykey", true, BitPosParams.bitPosParams().start(2).end(-1))); assertEquals(18, jedis.bitpos("mykey", true, BitPosParams.bitPosParams().start(2).end(-1) .modifier(BitCountOption.BYTE))); assertEquals(9, jedis.bitpos("mykey", true, BitPosParams.bitPosParams().start(7).end(15) .modifier(BitCountOption.BIT))); } @Test @SinceRedisVersion("7.0.0") public void setAndgetrange() { jedis.set("key1", "Hello World"); assertEquals(11, jedis.setrange("key1", 6, "Jedis")); assertEquals("Hello Jedis", jedis.get("key1")); assertEquals("Hello", jedis.getrange("key1", 0, 4)); assertEquals("Jedis", jedis.getrange("key1", 6, 11)); } @Test public void bitCount() { jedis.setbit("foo", 16, true); jedis.setbit("foo", 24, true); jedis.setbit("foo", 40, true); jedis.setbit("foo", 56, true); assertEquals(4, (long) jedis.bitcount("foo")); assertEquals(4, (long) jedis.bitcount("foo".getBytes())); assertEquals(3, (long) jedis.bitcount("foo", 2L, 5L)); assertEquals(3, (long) jedis.bitcount("foo".getBytes(), 2L, 5L)); } @Test @SinceRedisVersion("7.0.0") public void bitCountModifier() { jedis.setbit("foo", 16, true); jedis.setbit("foo", 24, true); jedis.setbit("foo", 40, true); jedis.setbit("foo", 56, true); assertEquals(3, (long) jedis.bitcount("foo", 2L, 5L, BitCountOption.BYTE)); assertEquals(3, (long) jedis.bitcount("foo".getBytes(), 2L, 5L, BitCountOption.BYTE)); assertEquals(0, (long) jedis.bitcount("foo", 2L, 5L, BitCountOption.BIT)); assertEquals(0, (long) jedis.bitcount("foo".getBytes(), 2L, 5L, BitCountOption.BIT)); } @Test @ConditionalOnEnv(value = TestEnvUtil.ENV_REDIS_ENTERPRISE, enabled = false) public void bitOp() { jedis.set("key1", "\u0060"); jedis.set("key2", "\u0044"); jedis.bitop(BitOP.AND, "resultAnd", "key1", "key2"); String resultAnd = jedis.get("resultAnd"); assertEquals("\u0040", resultAnd); jedis.bitop(BitOP.OR, "resultOr", "key1", "key2"); String resultOr = jedis.get("resultOr"); assertEquals("\u0064", resultOr); jedis.bitop(BitOP.XOR, "resultXor", "key1", "key2"); String resultXor = jedis.get("resultXor"); assertEquals("\u0024", resultXor); } @Test @ConditionalOnEnv(value = TestEnvUtil.ENV_REDIS_ENTERPRISE, enabled = false) public void bitOpNot() { jedis.setbit("key", 0, true); jedis.setbit("key", 4, true); jedis.bitop(BitOP.NOT, "resultNot", "key"); String resultNot = jedis.get("resultNot"); assertEquals("\u0077", resultNot); } @Test @ConditionalOnEnv(value = TestEnvUtil.ENV_REDIS_ENTERPRISE, enabled = false) public void bitOpBinary() { byte[] dest = {0x0}; byte[] key1 = {0x1}; byte[] key2 = {0x2}; jedis.set(key1, new byte[]{0x6}); jedis.set(key2, new byte[]{0x3}); jedis.bitop(BitOP.AND, dest, key1, key2); assertArrayEquals(new byte[]{0x2}, jedis.get(dest)); jedis.bitop(BitOP.OR, dest, key1, key2); assertArrayEquals(new byte[]{0x7}, jedis.get(dest)); jedis.bitop(BitOP.XOR, dest, key1, key2); assertArrayEquals(new byte[]{0x5}, jedis.get(dest)); jedis.setbit(key1, 0, true); jedis.bitop(BitOP.NOT, dest, key1); assertArrayEquals(new byte[]{0x79}, jedis.get(dest)); } @Test public void bitOpNotMultiSourceShouldFail() { Assertions.assertThrows(JedisDataException.class, () -> { jedis.bitop(BitOP.NOT, "dest", "src1", "src2"); }); } @Test public void testBitfield() { List responses = jedis.bitfield("mykey", "INCRBY", "i5", "100", "1", "GET", "u4", "0"); assertEquals(1L, responses.get(0).longValue()); assertEquals(0L, responses.get(1).longValue()); } @Test public void testBitfieldReadonly() { List responses = jedis.bitfield("mykey", "INCRBY", "i5", "100", "1", "GET", "u4", "0"); assertEquals(1L, responses.get(0).longValue()); assertEquals(0L, responses.get(1).longValue()); List responses2 = jedis.bitfieldReadonly("mykey", "GET", "i5", "100"); assertEquals(1L, responses2.get(0).longValue()); try { jedis.bitfieldReadonly("mykey", "INCRBY", "i5", "100", "1", "GET", "u4", "0"); fail("Readonly command shouldn't allow INCRBY"); } catch (JedisDataException e) { } } @Test public void testBinaryBitfield() { List responses = jedis.bitfield(SafeEncoder.encode("mykey"), SafeEncoder.encode("INCRBY"), SafeEncoder.encode("i5"), SafeEncoder.encode("100"), SafeEncoder.encode("1"), SafeEncoder.encode("GET"), SafeEncoder.encode("u4"), SafeEncoder.encode("0")); assertEquals(1L, responses.get(0).longValue()); assertEquals(0L, responses.get(1).longValue()); } @Test public void testBinaryBitfieldReadonly() { List responses = jedis.bitfield("mykey", "INCRBY", "i5", "100", "1", "GET", "u4", "0"); assertEquals(1L, responses.get(0).longValue()); assertEquals(0L, responses.get(1).longValue()); List responses2 = jedis.bitfieldReadonly(SafeEncoder.encode("mykey"), SafeEncoder.encode("GET"), SafeEncoder.encode("i5"), SafeEncoder.encode("100")); assertEquals(1L, responses2.get(0).longValue()); } @Test @SinceRedisVersion("8.1.240") @ConditionalOnEnv(value = TestEnvUtil.ENV_REDIS_ENTERPRISE, enabled = false) public void bitOpDiff() { // Use single-byte values for simplicity byte[] key1 = new byte[] { (byte) 0b00000111 }; // bits 0,1,2 set byte[] key2 = new byte[] { (byte) 0b00000010 }; // bit 1 set byte[] key3 = new byte[] { (byte) 0b00000100 }; // bit 2 set String destKey = "resultDiff"; // Set keys using byte arrays jedis.set("key1".getBytes(), key1); jedis.set("key2".getBytes(), key2); jedis.set("key3".getBytes(), key3); // DIFF(key1, key2, key3) = key1 AND NOT(key2 OR key3) // key1 = 00000111 (bits 0,1,2 set) // key2 = 00000010 (bit 1 set) // key3 = 00000100 (bit 2 set) // key2 OR key3 = 00000110 (bits 1,2 set) // NOT(key2 OR key3) = 11111001 (all bits except 1,2 set) // key1 AND NOT(key2 OR key3) = 00000001 (only bit 0 set) jedis.bitop(BitOP.DIFF, destKey, "key1", "key2", "key3"); // Get result as bytes byte[] resultBytes = jedis.get(destKey).getBytes(); // Expected result: 00000001 (only bit 0 set) byte[] expectedBytes = new byte[] { (byte) 0b00000001 }; // Verify the result assertArrayEquals(expectedBytes, resultBytes); } @Test @SinceRedisVersion("8.1.240") @ConditionalOnEnv(value = TestEnvUtil.ENV_REDIS_ENTERPRISE, enabled = false) public void bitOpDiff1() { // Use single-byte values for simplicity byte[] key1 = new byte[] { (byte) 0x07 }; // 00000111 - bits 0,1,2 set byte[] key2 = new byte[] { (byte) 0x02 }; // 00000010 - bit 1 set byte[] key3 = new byte[] { (byte) 0x04 }; // 00000100 - bit 2 set String destKey = "resultDiff1"; // Set keys using byte arrays jedis.set("key1".getBytes(), key1); jedis.set("key2".getBytes(), key2); jedis.set("key3".getBytes(), key3); // DIFF1(key1, key2, key3) = NOT(key1) AND (key2 OR key3) // key1 = 00000111 (bits 0,1,2 set) // key2 = 00000010 (bit 1 set) // key3 = 00000100 (bit 2 set) // key2 OR key3 = 00000110 (bits 1,2 set) // NOT(key1) = 11111000 (all bits except 0,1,2 set) // NOT(key1) AND (key2 OR key3) = 00000000 (no bits set) jedis.bitop(BitOP.DIFF1, destKey, "key1", "key2", "key3"); // Get result as bytes byte[] resultBytes = jedis.get(destKey).getBytes(); // Expected result: 00000000 (no bits set) byte[] expectedBytes = new byte[] { (byte) 0x00 }; // Verify the result assertArrayEquals(expectedBytes, resultBytes); } @Test @SinceRedisVersion("8.1.240") @ConditionalOnEnv(value = TestEnvUtil.ENV_REDIS_ENTERPRISE, enabled = false) public void bitOpAndor() { // Use single-byte values for simplicity byte[] key1 = new byte[] { (byte) 0x07 }; // 00000111 - bits 0,1,2 set byte[] key2 = new byte[] { (byte) 0x02 }; // 00000010 - bit 1 set byte[] key3 = new byte[] { (byte) 0x04 }; // 00000100 - bit 2 set String destKey = "resultAndor"; // Set keys using byte arrays jedis.set("key1".getBytes(), key1); jedis.set("key2".getBytes(), key2); jedis.set("key3".getBytes(), key3); // ANDOR(key1, key2, key3) = key1 AND (key2 OR key3) // key1 = 00000111 (bits 0,1,2 set) // key2 = 00000010 (bit 1 set) // key3 = 00000100 (bit 2 set) // key2 OR key3 = 00000110 (bits 1,2 set) // key1 AND (key2 OR key3) = 00000110 (bits 1,2 set) jedis.bitop(BitOP.ANDOR, destKey, "key1", "key2", "key3"); // Get result as bytes byte[] resultBytes = jedis.get(destKey).getBytes(); // Expected result: 00000110 (bits 1,2 set) byte[] expectedBytes = new byte[] { (byte) 0x06 }; // Verify the result assertArrayEquals(expectedBytes, resultBytes); } @Test @SinceRedisVersion("8.1.240") @ConditionalOnEnv(value = TestEnvUtil.ENV_REDIS_ENTERPRISE, enabled = false) public void bitOpOne() { // Use single-byte values for simplicity byte[] key1 = new byte[] { (byte) 0x01 }; // 00000001 - bit 0 set byte[] key2 = new byte[] { (byte) 0x02 }; // 00000010 - bit 1 set byte[] key3 = new byte[] { (byte) 0x04 }; // 00000100 - bit 2 set String destKey = "resultOne"; // Set keys using byte arrays jedis.set("key1".getBytes(), key1); jedis.set("key2".getBytes(), key2); jedis.set("key3".getBytes(), key3); // ONE(key1, key2, key3) = bits set in exactly one of the inputs // key1 = 00000001 (bit 0 set) // key2 = 00000010 (bit 1 set) // key3 = 00000100 (bit 2 set) // Result = 00000111 (bits 0,1,2 set - each in exactly one input) jedis.bitop(BitOP.ONE, destKey, "key1", "key2", "key3"); // Get result as bytes byte[] resultBytes = jedis.get(destKey).getBytes(); // Expected result: 00000111 (bits 0,1,2 set) byte[] expectedBytes = new byte[] { (byte) 0x07 }; // Verify the result assertArrayEquals(expectedBytes, resultBytes); } @Test @SinceRedisVersion("8.1.240") @ConditionalOnEnv(value = TestEnvUtil.ENV_REDIS_ENTERPRISE, enabled = false) public void bitOpDiffBinary() { byte[] key1 = { (byte) 0x07 }; // 00000111 - bits 0,1,2 set byte[] key2 = { (byte) 0x02 }; // 00000010 - bit 1 set byte[] key3 = { (byte) 0x04 }; // 00000100 - bit 2 set byte[] dest = "resultDiffBinary".getBytes(); jedis.set("key1".getBytes(), key1); jedis.set("key2".getBytes(), key2); jedis.set("key3".getBytes(), key3); // DIFF(key1, key2, key3) = key1 AND NOT(key2 OR key3) // key1 = 00000111 (bits 0,1,2 set) // key2 = 00000010 (bit 1 set) // key3 = 00000100 (bit 2 set) // key2 OR key3 = 00000110 (bits 1,2 set) // NOT(key2 OR key3) = 11111001 (all bits except 1,2 set) // key1 AND NOT(key2 OR key3) = 00000001 (only bit 0 set) jedis.bitop(BitOP.DIFF, dest, "key1".getBytes(), "key2".getBytes(), "key3".getBytes()); // Expected result: 00000001 (only bit 0 set) byte[] expectedBytes = new byte[] { (byte) 0x01 }; // Verify the result assertArrayEquals(expectedBytes, jedis.get(dest)); } @Test @SinceRedisVersion("8.1.240") @ConditionalOnEnv(value = TestEnvUtil.ENV_REDIS_ENTERPRISE, enabled = false) public void bitOpDiff1Binary() { byte[] key1 = { (byte) 0x07 }; // 00000111 - bits 0,1,2 set byte[] key2 = { (byte) 0x02 }; // 00000010 - bit 1 set byte[] key3 = { (byte) 0x04 }; // 00000100 - bit 2 set byte[] dest = "resultDiff1Binary".getBytes(); jedis.set("key1".getBytes(), key1); jedis.set("key2".getBytes(), key2); jedis.set("key3".getBytes(), key3); // DIFF1(key1, key2, key3) = NOT(key1) AND (key2 OR key3) // key1 = 00000111 (bits 0,1,2 set) // key2 = 00000010 (bit 1 set) // key3 = 00000100 (bit 2 set) // key2 OR key3 = 00000110 (bits 1,2 set) // NOT(key1) = 11111000 (all bits except 0,1,2 set) // NOT(key1) AND (key2 OR key3) = 00000000 (no bits set) jedis.bitop(BitOP.DIFF1, dest, "key1".getBytes(), "key2".getBytes(), "key3".getBytes()); // Expected result: 00000000 (no bits set) byte[] expectedBytes = new byte[] { (byte) 0x00 }; // Verify the result assertArrayEquals(expectedBytes, jedis.get(dest)); } @Test @SinceRedisVersion("8.1.240") @ConditionalOnEnv(value = TestEnvUtil.ENV_REDIS_ENTERPRISE, enabled = false) public void bitOpAndorBinary() { byte[] key1 = { (byte) 0x07 }; // 00000111 - bits 0,1,2 set byte[] key2 = { (byte) 0x02 }; // 00000010 - bit 1 set byte[] key3 = { (byte) 0x04 }; // 00000100 - bit 2 set byte[] dest = "resultAndorBinary".getBytes(); jedis.set("key1".getBytes(), key1); jedis.set("key2".getBytes(), key2); jedis.set("key3".getBytes(), key3); // ANDOR(key1, key2, key3) = key1 AND (key2 OR key3) // key1 = 00000111 (bits 0,1,2 set) // key2 = 00000010 (bit 1 set) // key3 = 00000100 (bit 2 set) // key2 OR key3 = 00000110 (bits 1,2 set) // key1 AND (key2 OR key3) = 00000110 (bits 1,2 set) jedis.bitop(BitOP.ANDOR, dest, "key1".getBytes(), "key2".getBytes(), "key3".getBytes()); // Expected result: 00000110 (bits 1,2 set) byte[] expectedBytes = new byte[] { (byte) 0x06 }; // Verify the result assertArrayEquals(expectedBytes, jedis.get(dest)); } @Test @SinceRedisVersion("8.1.240") @ConditionalOnEnv(value = TestEnvUtil.ENV_REDIS_ENTERPRISE, enabled = false) public void bitOpOneBinary() { byte[] key1 = { (byte) 0x01 }; // 00000001 - bit 0 set byte[] key2 = { (byte) 0x02 }; // 00000010 - bit 1 set byte[] key3 = { (byte) 0x04 }; // 00000100 - bit 2 set byte[] dest = "resultOneBinary".getBytes(); jedis.set("key1".getBytes(), key1); jedis.set("key2".getBytes(), key2); jedis.set("key3".getBytes(), key3); // ONE(key1, key2, key3) = bits set in exactly one of the inputs // key1 = 00000001 (bit 0 set) // key2 = 00000010 (bit 1 set) // key3 = 00000100 (bit 2 set) // Result = 00000111 (bits 0,1,2 set - each in exactly one input) jedis.bitop(BitOP.ONE, dest, "key1".getBytes(), "key2".getBytes(), "key3".getBytes()); // Expected result: 00000111 (bits 0,1,2 set) byte[] expectedBytes = new byte[] { (byte) 0x07 }; // Verify the result assertArrayEquals(expectedBytes, jedis.get(dest)); } @Test @SinceRedisVersion("8.1.240") public void bitOpDiffSingleSourceShouldFail() { assertThrows(JedisDataException.class, () -> jedis.bitop(BitOP.DIFF, "dest", "src1")); } @Test @SinceRedisVersion("8.1.240") public void bitOpDiff1SingleSourceShouldFail() { assertThrows(JedisDataException.class, () -> jedis.bitop(BitOP.DIFF1, "dest", "src1")); } @Test @SinceRedisVersion("8.1.240") public void bitOpAndorSingleSourceShouldFail() { assertThrows(JedisDataException.class, () -> jedis.bitop(BitOP.ANDOR, "dest", "src1")); } @Test @SinceRedisVersion("8.1.240") public void bitOpDiffBinarySingleSourceShouldFail() { byte[] dest = "dest".getBytes(); byte[] src1 = "src1".getBytes(); assertThrows(JedisDataException.class, () -> jedis.bitop(BitOP.DIFF, dest, src1)); } @Test @SinceRedisVersion("8.1.240") public void bitOpDiff1BinarySingleSourceShouldFail() { byte[] dest = "dest".getBytes(); byte[] src1 = "src1".getBytes(); assertThrows(JedisDataException.class, () -> jedis.bitop(BitOP.DIFF1, dest, src1)); } @Test @SinceRedisVersion("8.1.240") public void bitOpAndorBinarySingleSourceShouldFail() { byte[] dest = "dest".getBytes(); byte[] src1 = "src1".getBytes(); assertThrows(JedisDataException.class, () -> jedis.bitop(BitOP.ANDOR, dest, src1)); } } ================================================ FILE: src/test/java/redis/clients/jedis/commands/jedis/ClientCommandsTest.java ================================================ package redis.clients.jedis.commands.jedis; import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.greaterThanOrEqualTo; import static org.junit.jupiter.api.Assertions.assertArrayEquals; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertNotNull; import static org.junit.jupiter.api.Assertions.assertTrue; import static org.junit.jupiter.api.Assertions.fail; import static redis.clients.jedis.params.ClientKillParams.SkipMe; import java.util.concurrent.ExecutionException; import java.util.concurrent.Executors; import java.util.concurrent.Future; import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeoutException; import java.util.regex.Matcher; import java.util.regex.Pattern; import io.redis.test.annotations.SinceRedisVersion; import io.redis.test.annotations.ConditionalOnEnv; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Tag; import org.junit.jupiter.params.ParameterizedClass; import org.junit.jupiter.params.provider.MethodSource; import redis.clients.jedis.Jedis; import redis.clients.jedis.RedisProtocol; import redis.clients.jedis.args.ClientAttributeOption; import redis.clients.jedis.args.ClientType; import redis.clients.jedis.args.UnblockType; import redis.clients.jedis.exceptions.JedisConnectionException; import redis.clients.jedis.params.ClientKillParams; import redis.clients.jedis.resps.TrackingInfo; import redis.clients.jedis.util.TestEnvUtil; @ParameterizedClass @MethodSource("redis.clients.jedis.commands.CommandsTestsParameters#respVersions") @Tag("integration") public class ClientCommandsTest extends JedisCommandsTestBase { private final String clientName = "fancy_jedis_name"; private final Pattern pattern = Pattern.compile("\\bname=" + clientName + "\\b"); private Jedis client; public ClientCommandsTest(RedisProtocol protocol) { super(protocol); } @BeforeEach @Override public void setUp() throws Exception { super.setUp(); client = new Jedis(endpoint.getHost(), endpoint.getPort(), 500); client.auth(endpoint.getPassword()); client.clientSetname(clientName); } @AfterEach @Override public void tearDown() throws Exception { client.close(); super.tearDown(); } @Test public void nameString() { String name = "string"; client.clientSetname(name); assertEquals(name, client.clientGetname()); } @Test public void nameBinary() { byte[] name = "binary".getBytes(); client.clientSetname(name); assertArrayEquals(name, client.clientGetnameBinary()); } @Test @SinceRedisVersion("7.2.0") public void clientSetInfoCommand() { String libName = "Jedis::A-Redis-Java-library"; String libVersion = "999.999.999"; assertEquals("OK", client.clientSetInfo(ClientAttributeOption.LIB_NAME, libName)); assertEquals("OK", client.clientSetInfo(ClientAttributeOption.LIB_VER, libVersion)); String info = client.clientInfo(); assertTrue(info.contains("lib-name=" + libName)); assertTrue(info.contains("lib-ver=" + libVersion)); } @Test @SinceRedisVersion("7.2.0") public void clientSetInfoCommandExpandLibName() { String baseLibName = "Jedis::A-Redis-Java-library"; String upstreamDriver = "spring-data-redis_v3.2.0"; String expandedLibName = baseLibName + "(" + upstreamDriver + ")"; String libVersion = "999.999.999"; assertEquals("OK", client.clientSetInfo(ClientAttributeOption.LIB_NAME, expandedLibName)); assertEquals("OK", client.clientSetInfo(ClientAttributeOption.LIB_VER, libVersion)); String info = client.clientInfo(); assertTrue(info.contains("lib-name=" + expandedLibName)); assertTrue(info.contains("lib-ver=" + libVersion)); } @Test public void clientId() { long clientId = client.clientId(); String info = findInClientList(); Matcher matcher = Pattern.compile("\\bid=(\\d+)\\b").matcher(info); matcher.find(); assertEquals(clientId, Long.parseLong(matcher.group(1))); } @Test public void clientIdmultipleConnection() { try (Jedis client2 = new Jedis(endpoint.getHost(), endpoint.getPort(), 500)) { client2.auth(endpoint.getPassword()); client2.clientSetname("fancy_jedis_another_name"); // client-id is monotonically increasing assertTrue(client.clientId() < client2.clientId()); } } @Test public void clientIdReconnect() { long clientIdInitial = client.clientId(); client.disconnect(); client.connect(); client.auth(endpoint.getPassword()); long clientIdAfterReconnect = client.clientId(); assertTrue(clientIdInitial < clientIdAfterReconnect); } @Test public void clientUnblock() throws InterruptedException, TimeoutException { long clientId = client.clientId(); assertEquals(0, jedis.clientUnblock(clientId, UnblockType.ERROR)); Future future = Executors.newSingleThreadExecutor() .submit(() -> client.brpop(100000, "foo")); try { // to make true command already executed TimeUnit.MILLISECONDS.sleep(500); assertEquals(1, jedis.clientUnblock(clientId, UnblockType.ERROR)); future.get(1, TimeUnit.SECONDS); } catch (ExecutionException e) { assertEquals( "redis.clients.jedis.exceptions.JedisDataException: UNBLOCKED client unblocked via CLIENT UNBLOCK", e.getMessage()); } } @Test public void killIdString() { String info = findInClientList(); Matcher matcher = Pattern.compile("\\bid=(\\d+)\\b").matcher(info); matcher.find(); String id = matcher.group(1); assertEquals(1, jedis.clientKill(new ClientKillParams().id(id))); assertDisconnected(client); } @Test public void killIdBinary() { String info = findInClientList(); Matcher matcher = Pattern.compile("\\bid=(\\d+)\\b").matcher(info); matcher.find(); byte[] id = matcher.group(1).getBytes(); assertEquals(1, jedis.clientKill(new ClientKillParams().id(id))); assertDisconnected(client); } @Test public void killTypeNormal() { long clients = jedis.clientKill(new ClientKillParams().type(ClientType.NORMAL)); assertTrue(clients > 0); assertDisconnected(client); } @Test public void killSkipmeNo() { jedis.clientKill(new ClientKillParams().type(ClientType.NORMAL).skipMe(SkipMe.NO)); assertDisconnected(client); assertDisconnected(jedis); } @Test public void killSkipmeYesNo() { jedis.clientKill(new ClientKillParams().type(ClientType.NORMAL).skipMe(SkipMe.YES)); assertDisconnected(client); ClientKillParams skipmeNo = new ClientKillParams().type(ClientType.NORMAL).skipMe(SkipMe.NO); assertThat(jedis.clientKill(skipmeNo), greaterThanOrEqualTo(1L)); assertDisconnected(jedis); } @Test public void killAddrString() { String info = findInClientList(); Matcher matcher = Pattern.compile("\\baddr=(\\S+)\\b").matcher(info); matcher.find(); String addr = matcher.group(1); assertEquals(1, jedis.clientKill(new ClientKillParams().addr(addr))); assertDisconnected(client); } @Test public void killAddrBinary() { String info = findInClientList(); Matcher matcher = Pattern.compile("\\baddr=(\\S+)\\b").matcher(info); matcher.find(); String addr = matcher.group(1); assertEquals(1, jedis.clientKill(new ClientKillParams().addr(addr))); assertDisconnected(client); } @Test public void killLAddr() { String info = findInClientList(); Matcher matcher = Pattern.compile("\\bladdr=(\\S+)\\b").matcher(info); matcher.find(); String laddr = matcher.group(1); long clients = jedis.clientKill(new ClientKillParams().laddr(laddr)); assertTrue(clients >= 1); assertDisconnected(client); } @Test public void killAddrIpPort() { String info = findInClientList(); Matcher matcher = Pattern.compile("\\baddr=(\\S+)\\b").matcher(info); matcher.find(); String addr = matcher.group(1); int lastColon = addr.lastIndexOf(":"); String[] hp = new String[] { addr.substring(0, lastColon), addr.substring(lastColon + 1) }; assertEquals(1, jedis.clientKill(new ClientKillParams().addr(hp[0], Integer.parseInt(hp[1])))); assertDisconnected(client); } @Test @ConditionalOnEnv(value = TestEnvUtil.ENV_REDIS_ENTERPRISE, enabled = false) public void killUser() { client.aclSetUser("test_kill", "on", "+acl", ">password1"); try (Jedis client2 = new Jedis(endpoint.getHost(), endpoint.getPort(), 500)) { client2.auth("test_kill", "password1"); assertEquals(1, jedis.clientKill(new ClientKillParams().user("test_kill"))); assertDisconnected(client2); } finally { jedis.aclDelUser("test_kill"); } } @Test @SinceRedisVersion(value = "7.4.0", message = "MAXAGE (since Redis 7.4)") public void killMaxAge() throws InterruptedException { long maxAge = 2; // sleep twice the maxAge, to be sure Thread.sleep(maxAge * 2 * 1000); try (Jedis client2 = new Jedis(endpoint.getHost(), endpoint.getPort(), 500)) { client2.auth(endpoint.getPassword()); long killedClients = jedis.clientKill(new ClientKillParams().maxAge(maxAge)); // The reality is that some tests leak clients, so we can't assert // on the exact number of killed clients. assertTrue(killedClients > 0); assertDisconnected(client); assertConnected(client2); } } @Test public void clientInfo() { String info = client.clientInfo(); assertNotNull(info); assertEquals(1, info.split("\n").length); assertTrue(info.contains(clientName)); } @Test @ConditionalOnEnv(value = TestEnvUtil.ENV_REDIS_ENTERPRISE, enabled = false) public void clientListWithClientId() { long id = client.clientId(); String listInfo = jedis.clientList(id); assertNotNull(listInfo); assertTrue(listInfo.contains(clientName)); } @Test public void listWithType() { assertTrue(client.clientList(ClientType.NORMAL).split("\\n").length > 1); assertEquals(0, client.clientList(ClientType.MASTER).length()); assertEquals(1, client.clientList(ClientType.SLAVE).split("\\n").length); if (!TestEnvUtil.getTestEnvProvider().equals(TestEnvUtil.ENV_REDIS_ENTERPRISE)) { assertEquals(1, client.clientList(ClientType.REPLICA).split("\\n").length); } assertEquals(1, client.clientList(ClientType.PUBSUB).split("\\n").length); } @Test public void trackingInfo() { TrackingInfo trackingInfo = client.clientTrackingInfo(); assertEquals(1, trackingInfo.getFlags().size()); assertEquals(-1, trackingInfo.getRedirect()); assertEquals(0, trackingInfo.getPrefixes().size()); } @Test public void trackingInfoResp3() { Jedis clientResp3 = new Jedis(endpoint.getHostAndPort(), endpoint.getClientConfigBuilder().protocol(RedisProtocol.RESP3).build()); TrackingInfo trackingInfo = clientResp3.clientTrackingInfo(); assertEquals(1, trackingInfo.getFlags().size()); assertEquals(-1, trackingInfo.getRedirect()); assertEquals(0, trackingInfo.getPrefixes().size()); } private void assertDisconnected(Jedis j) { try { j.ping(); fail("Jedis connection should be disconnected"); } catch (JedisConnectionException jce) { // should be here } } private void assertConnected(Jedis j) { assertEquals("PONG", j.ping()); } private String findInClientList() { for (String clientInfo : jedis.clientList().split("\n")) { if (pattern.matcher(clientInfo).find()) { return clientInfo; } } return null; } } ================================================ FILE: src/test/java/redis/clients/jedis/commands/jedis/ClusterBinaryValuesCommandsTest.java ================================================ package redis.clients.jedis.commands.jedis; import static org.junit.jupiter.api.Assertions.assertArrayEquals; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertTrue; import static org.junit.jupiter.api.Assertions.fail; import static redis.clients.jedis.Protocol.Command.*; import static redis.clients.jedis.util.AssertUtil.assertByteArrayListEquals; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Set; import org.junit.jupiter.api.Test; import redis.clients.jedis.GeoCoordinate; import redis.clients.jedis.args.GeoUnit; import redis.clients.jedis.params.GeoRadiusParam; import redis.clients.jedis.params.GeoRadiusStoreParam; import redis.clients.jedis.util.SafeEncoder; public class ClusterBinaryValuesCommandsTest extends ClusterJedisCommandsTestBase { @Test public void nullKeys() { String foo = "foo"; try { cluster.exists((String) null); fail(); } catch (NullPointerException e) { // expected } try { cluster.exists(foo, null); fail(); } catch (NullPointerException e) { // expected } try { cluster.exists(null, foo); fail(); } catch (NullPointerException e) { // expected } } @Test public void testBinaryGetAndSet() { byte[] byteKey = "foo".getBytes(); byte[] byteValue = "2".getBytes(); cluster.set(byteKey, byteValue); assertArrayEquals(byteValue, cluster.get(byteKey)); } @Test public void testIncr() { byte[] byteKey = "foo".getBytes(); byte[] byteValue = "2".getBytes(); cluster.set(byteKey, byteValue); cluster.incr(byteKey); assertArrayEquals("3".getBytes(), cluster.get(byteKey)); } @Test public void testSadd() { byte[] byteKey = "languages".getBytes(); byte[] firstLanguage = "java".getBytes(); byte[] secondLanguage = "python".getBytes(); byte[][] listLanguages = { firstLanguage, secondLanguage }; cluster.sadd(byteKey, listLanguages); Set setLanguages = cluster.smembers(byteKey); List languages = new ArrayList<>(); for (byte[] language : setLanguages) { languages.add(new String(language)); } assertTrue(languages.contains("java")); assertTrue(languages.contains("python")); } @Test public void testHmset() { byte[] key = "jedis".getBytes(); byte[] field = "language".getBytes(); byte[] value = "java".getBytes(); HashMap map = new HashMap(); map.put(field, value); cluster.hmset(key, map); List listResults = cluster.hmget(key, field); for (byte[] result : listResults) { assertArrayEquals(value, result); } } @Test public void testRpush() { byte[] value1 = "value1".getBytes(); byte[] value2 = "value2".getBytes(); byte[] key = "key1".getBytes(); cluster.del(key); cluster.rpush(key, value1); cluster.rpush(key, value2); assertEquals(2, (long) cluster.llen(key)); } @Test public void georadiusStoreBinary() { // prepare datas Map bcoordinateMap = new HashMap(); bcoordinateMap.put("Palermo".getBytes(), new GeoCoordinate(13.361389, 38.115556)); bcoordinateMap.put("Catania".getBytes(), new GeoCoordinate(15.087269, 37.502669)); cluster.geoadd("{Sicily}".getBytes(), bcoordinateMap); long size = cluster.georadiusStore("{Sicily}".getBytes(), 15, 37, 200, GeoUnit.KM, GeoRadiusParam.geoRadiusParam(), GeoRadiusStoreParam.geoRadiusStoreParam().store("{Sicily}Store")); assertEquals(2, size); List bexpected = new ArrayList(); bexpected.add("Palermo".getBytes()); bexpected.add("Catania".getBytes()); assertByteArrayListEquals(bexpected, cluster.zrange("{Sicily}Store".getBytes(), 0, -1)); } @Test public void testKeys() { assertEquals(0, cluster.keys("{f}o*".getBytes()).size()); cluster.set("{f}oo1".getBytes(), "bar".getBytes()); cluster.set("{f}oo2".getBytes(), "bar".getBytes()); cluster.set("{f}oo3".getBytes(), "bar".getBytes()); assertEquals(3, cluster.keys("{f}o*".getBytes()).size()); } @Test public void testBinaryGeneralCommand() { byte[] key = "x".getBytes(); byte[] value = "1".getBytes(); cluster.sendCommand("z".getBytes(), SET, key, value); cluster.sendCommand("y".getBytes(), INCR, key); Object returnObj = cluster.sendCommand("w".getBytes(), GET, key); assertEquals("2", SafeEncoder.encode((byte[]) returnObj)); } @Test public void testGeneralCommand() { cluster.sendCommand("z", SET, "x", "1"); cluster.sendCommand("y", INCR, "x"); Object returnObj = cluster.sendCommand("w", GET, "x"); assertEquals("2", SafeEncoder.encode((byte[]) returnObj)); } @Test public void testKeysBroadcastAndAggregation() { // Use keys without hash tags - they will be distributed across different hash slots // These keys intentionally don't use hash tags like {key} to ensure distribution String key1 = "testkey_alpha"; String key2 = "testkey_beta"; String key3 = "testkey_gamma"; // Initially, no keys should match the pattern assertEquals(0, cluster.keys("testkey_*").size()); // Set multiple keys that will be distributed across different hash slots cluster.set(key1, "value1"); cluster.set(key2, "value2"); cluster.set(key3, "value3"); // Call keys with a pattern that should match all three keys // This triggers broadcasting to all shards and aggregation of results Set matchedKeys = cluster.keys("testkey_*"); // Verify all keys are returned, demonstrating proper cross-shard aggregation assertEquals(3, matchedKeys.size()); assertTrue(matchedKeys.contains(key1)); assertTrue(matchedKeys.contains(key2)); assertTrue(matchedKeys.contains(key3)); // Test with a more specific pattern that matches only some keys Set alphaKeys = cluster.keys("testkey_a*"); assertEquals(1, alphaKeys.size()); assertTrue(alphaKeys.contains(key1)); // Test binary version with keys distributed across different slots byte[] bkey1 = "binkey_one".getBytes(); byte[] bkey2 = "binkey_two".getBytes(); byte[] bkey3 = "binkey_three".getBytes(); cluster.set(bkey1, "bvalue1".getBytes()); cluster.set(bkey2, "bvalue2".getBytes()); cluster.set(bkey3, "bvalue3".getBytes()); // Verify binary KEYS also broadcasts and aggregates correctly Set binaryMatchedKeys = cluster.keys("binkey_*".getBytes()); assertEquals(3, binaryMatchedKeys.size()); } } ================================================ FILE: src/test/java/redis/clients/jedis/commands/jedis/ClusterCommandsTest.java ================================================ package redis.clients.jedis.commands.jedis; import java.util.List; import java.util.Map; import io.redis.test.annotations.SinceRedisVersion; import org.hamcrest.MatcherAssert; import org.hamcrest.Matchers; import org.junit.jupiter.api.AfterAll; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Tag; import org.junit.jupiter.api.extension.RegisterExtension; import redis.clients.jedis.EndpointConfig; import redis.clients.jedis.HostAndPort; import redis.clients.jedis.Jedis; import redis.clients.jedis.args.ClusterResetType; import redis.clients.jedis.Endpoints; import redis.clients.jedis.exceptions.JedisDataException; import redis.clients.jedis.resps.ClusterShardInfo; import redis.clients.jedis.resps.ClusterShardNodeInfo; import redis.clients.jedis.util.JedisClusterCRC16; import redis.clients.jedis.util.JedisClusterTestUtil; import redis.clients.jedis.util.RedisVersionCondition; import static org.junit.jupiter.api.Assertions.assertInstanceOf; import static org.junit.jupiter.api.Assertions.assertNotEquals; import static org.junit.jupiter.api.Assertions.assertNull; import static org.junit.jupiter.api.Assertions.assertTrue; import static org.junit.jupiter.api.Assertions.assertNotNull; import static org.junit.jupiter.api.Assertions.assertEquals; @Tag("integration") public class ClusterCommandsTest { private static EndpointConfig endpoint; private static Jedis node1; private static Jedis node2; private static HostAndPort nodeInfo1; private static HostAndPort nodeInfo2; @RegisterExtension public RedisVersionCondition versionCondition = new RedisVersionCondition( () -> Endpoints.getRedisEndpoint("cluster-unbound")); @BeforeAll public static void prepareEndpoints() { endpoint = Endpoints.getRedisEndpoint("cluster-unbound"); nodeInfo1 = endpoint.getHostsAndPorts().get(0); nodeInfo2 = endpoint.getHostsAndPorts().get(1); } @BeforeEach public void setUp() throws Exception { node1 = new Jedis(nodeInfo1); node1.auth(endpoint.getPassword()); node1.flushAll(); node2 = new Jedis(nodeInfo2); node2.auth(endpoint.getPassword()); node2.flushAll(); } @AfterEach public void tearDown() { node1.disconnect(); node2.disconnect(); } @AfterAll public static void resetRedisAfter() { if (endpoint != null) { removeSlots(); } } public static void removeSlots() { if (endpoint == null) return; try (Jedis node = new Jedis(nodeInfo1)) { node.auth(endpoint.getPassword()); node.clusterReset(ClusterResetType.SOFT); } try (Jedis node = new Jedis(nodeInfo2)) { node.auth(endpoint.getPassword()); node.clusterReset(ClusterResetType.SOFT); } } @Test public void testClusterSoftReset() { node1.clusterMeet(nodeInfo2.getHost(), nodeInfo2.getPort()); assertTrue(node1.clusterNodes().split("\n").length > 1); node1.clusterReset(ClusterResetType.SOFT); assertEquals(1, node1.clusterNodes().split("\n").length); } @Test public void testClusterHardReset() { String nodeId = JedisClusterTestUtil.getNodeId(node1.clusterNodes()); node1.clusterReset(ClusterResetType.HARD); String newNodeId = JedisClusterTestUtil.getNodeId(node1.clusterNodes()); assertNotEquals(nodeId, newNodeId); } @Test public void clusterSetSlotImporting() { node2.clusterAddSlots(6000); String[] nodes = node1.clusterNodes().split("\n"); String nodeId = nodes[0].split(" ")[0]; String status = node1.clusterSetSlotImporting(6000, nodeId); assertEquals("OK", status); node2.clusterDelSlots(6000); } @Test public void clusterNodes() { String nodes = node1.clusterNodes(); assertTrue(nodes.split("\n").length > 0); } // // @Test // public void clusterMeet() { // String status = node1.clusterMeet("127.0.0.1", nodeInfo2.getPort()); // assertEquals("OK", status); // } @Test public void clusterAddSlotsAndDelSlots() { assertEquals("OK", node1.clusterAddSlots(1, 2, 3, 4, 5)); assertEquals("OK", node1.clusterDelSlots(1, 2, 3, 4, 5)); } @Test public void clusterInfo() { String info = node1.clusterInfo(); assertNotNull(info); } @Test @SinceRedisVersion("7.0.0") public void addAndDelSlotsRange() { // test add assertEquals("OK", node1.clusterAddSlotsRange(100, 105)); String clusterNodes = node1.clusterNodes(); assertTrue(clusterNodes.contains("connected 100-105")); assertEquals("OK", node1.clusterAddSlotsRange(110, 120)); clusterNodes = node1.clusterNodes(); assertTrue(clusterNodes.contains("connected 100-105 110-120")); // test del assertEquals("OK", node1.clusterDelSlotsRange(100, 105)); clusterNodes = node1.clusterNodes(); assertTrue(clusterNodes.contains("connected 110-120")); assertEquals("OK", node1.clusterDelSlotsRange(110, 120)); } @Test public void clusterGetKeysInSlot() { node1.clusterAddSlots(500); List keys = node1.clusterGetKeysInSlot(500, 1); assertEquals(0, keys.size()); node1.clusterDelSlots(500); } @Test public void clusterGetKeysInSlotBinary() { node1.clusterAddSlots(501); List keys = node1.clusterGetKeysInSlotBinary(501, 1); assertEquals(0, keys.size()); node1.clusterDelSlots(501); } @Test public void clusterSetSlotNode() { String[] nodes = node1.clusterNodes().split("\n"); String nodeId = nodes[0].split(" ")[0]; String status = node1.clusterSetSlotNode(10000, nodeId); assertEquals("OK", status); } @Test public void clusterSetSlotMigrating() { node1.clusterAddSlots(5000); String[] nodes = node1.clusterNodes().split("\n"); String nodeId = nodes[0].split(" ")[0]; String status = node1.clusterSetSlotMigrating(5000, nodeId); assertEquals("OK", status); node1.clusterDelSlots(5000); } @Test public void clusterSlots() { // please see cluster slot output format from below commit // @see: https://github.com/antirez/redis/commit/e14829de3025ffb0d3294e5e5a1553afd9f10b60 assertEquals("OK", node1.clusterAddSlots(3000, 3001, 3002)); List slots = node1.clusterSlots(); assertNotNull(slots); assertTrue(slots.size() > 0); for (Object slotInfoObj : slots) { assertNotNull(slotInfoObj); List slotInfo = (List) slotInfoObj; assertTrue(slotInfo.size() >= 2); assertInstanceOf(Long.class, slotInfo.get(0)); assertInstanceOf(Long.class, slotInfo.get(1)); if (slotInfo.size() > 2) { // assigned slots assertInstanceOf(List.class, slotInfo.get(2)); } } node1.clusterDelSlots(3000, 3001, 3002); } @Test @SinceRedisVersion("7.0.0") public void clusterShards() { assertEquals("OK", node1.clusterAddSlots(3100, 3101, 3102, 3105)); List shards = node1.clusterShards(); assertNotNull(shards); assertTrue(shards.size() > 0); for (ClusterShardInfo shardInfo : shards) { assertNotNull(shardInfo); assertTrue(shardInfo.getSlots().size() > 1); for (List slotRange : shardInfo.getSlots()) { assertEquals(2, slotRange.size()); } for (ClusterShardNodeInfo nodeInfo : shardInfo.getNodes()) { assertNotNull(nodeInfo.getId()); assertNotNull(nodeInfo.getEndpoint()); assertNotNull(nodeInfo.getIp()); assertNull(nodeInfo.getHostname()); assertNotNull(nodeInfo.getPort()); assertNotNull(nodeInfo.getRole()); assertNotNull(nodeInfo.getReplicationOffset()); assertNotNull(nodeInfo.getHealth()); } } node1.clusterDelSlots(3100, 3101, 3102, 3105); } @Test @SinceRedisVersion("7.0.0") public void clusterLinks() throws InterruptedException { List> links = node1.clusterLinks(); assertNotNull(links); assertEquals(0, links.size()); } @Test public void testClusterKeySlot() { // It assumes JedisClusterCRC16 is correctly implemented assertEquals(JedisClusterCRC16.getSlot("{user1000}.following"), node1.clusterKeySlot("{user1000}.following")); assertEquals(JedisClusterCRC16.getSlot("foo{bar}{zap}"), node1.clusterKeySlot("foo{bar}{zap}")); assertEquals(JedisClusterCRC16.getSlot("foo{}{bar}"), node1.clusterKeySlot("foo{}{bar}")); assertEquals(JedisClusterCRC16.getSlot("foo{{bar}}zap"), node1.clusterKeySlot("foo{{bar}}zap")); } @Test public void clusterCountFailureReports() { assertEquals(0, node1.clusterCountFailureReports(node1.clusterMyId())); } @Test public void clusterMyId() { MatcherAssert.assertThat(node1.clusterMyId(), Matchers.not(Matchers.isEmptyOrNullString())); } @Test @SinceRedisVersion("7.2.0") public void clusterMyShardId() { MatcherAssert.assertThat(node1.clusterMyShardId(), Matchers.not(Matchers.isEmptyOrNullString())); } @Test public void testClusterEpoch() { try { assertEquals("OK", node1.clusterSetConfigEpoch(1)); } catch (JedisDataException jde) { assertEquals("ERR The user can assign a config epoch only when the node does not know any other node.", jde.getMessage()); } } @Test public void ClusterBumpEpoch() { MatcherAssert.assertThat(node1.clusterBumpEpoch(), Matchers.matchesPattern("^BUMPED|STILL [0-9]+$")); } } ================================================ FILE: src/test/java/redis/clients/jedis/commands/jedis/ClusterHotkeysCommandsTest.java ================================================ package redis.clients.jedis.commands.jedis; import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.greaterThan; import static org.hamcrest.Matchers.greaterThanOrEqualTo; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertNotNull; import static org.junit.jupiter.api.Assertions.assertThrows; import static org.junit.jupiter.api.Assertions.assertTrue; import java.util.List; import java.util.Map; import io.redis.test.annotations.ConditionalOnEnv; import io.redis.test.annotations.EnabledOnCommand; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Tag; import redis.clients.jedis.HostAndPort; import redis.clients.jedis.RedisClient; import redis.clients.jedis.args.HotkeysMetric; import redis.clients.jedis.params.HotkeysParams; import redis.clients.jedis.resps.HotkeysInfo; import redis.clients.jedis.util.JedisClusterCRC16; import redis.clients.jedis.util.TestEnvUtil; /** * Tests that HOTKEYS commands are not supported in cluster mode. *

* The HOTKEYS command is a node-local operation that tracks hot keys on a single Redis instance. In * a Redis Cluster, keys are distributed across multiple nodes, and there is no built-in mechanism * to aggregate hotkeys data across all nodes. Therefore, HOTKEYS commands are intentionally * disabled in cluster mode to avoid confusion and incorrect results. *

* Users who need hotkeys functionality in a cluster environment should connect directly to * individual nodes and run HOTKEYS commands on each node separately. */ @Tag("integration") @EnabledOnCommand("HOTKEYS") @ConditionalOnEnv(value = TestEnvUtil.ENV_OSS_DOCKER, enabled = true) public class ClusterHotkeysCommandsTest extends ClusterJedisCommandsTestBase { @Test public void hotkeysStartNotSupportedInCluster() { assertThrows(UnsupportedOperationException.class, () -> cluster.hotkeysStart(HotkeysParams.hotkeysParams().metrics(HotkeysMetric.CPU))); } @Test public void hotkeysStopNotSupportedInCluster() { assertThrows(UnsupportedOperationException.class, () -> cluster.hotkeysStop()); } @Test public void hotkeysResetNotSupportedInCluster() { assertThrows(UnsupportedOperationException.class, () -> cluster.hotkeysReset()); } @Test public void hotkeysGetNotSupportedInCluster() { assertThrows(UnsupportedOperationException.class, () -> cluster.hotkeysGet()); } // Test slots - consecutive slots (server groups them as a range) and a non-consecutive slot private static final int SLOT_0 = 0; private static final int SLOT_1 = 1; private static final int SLOT_2 = 2; private static final int SLOT_100 = 100; // Non-consecutive slot to test multiple ranges // Keys with hash tags that hash to slots 0, 1, 2 // The hash tag content was found by iterating JedisClusterCRC16.getSlot() private static final String KEY_SLOT_0 = "key{3560}"; // {3560} hashes to slot 0 private static final String KEY_SLOT_1 = "key{22179}"; // {22179} hashes to slot 1 private static final String KEY_SLOT_2 = "key{48756}"; // {48756} hashes to slot 2 /** * Tests HOTKEYS with SLOTS parameter by connecting directly to a single cluster node using a * standalone RedisClient. Verifies all response fields are correctly parsed. */ @Test public void hotkeysWithSlotsOnSingleClusterNode() { // Verify our pre-computed keys hash to the expected slots assertEquals(SLOT_0, JedisClusterCRC16.getSlot(KEY_SLOT_0)); assertEquals(SLOT_1, JedisClusterCRC16.getSlot(KEY_SLOT_1)); assertEquals(SLOT_2, JedisClusterCRC16.getSlot(KEY_SLOT_2)); HostAndPort nodeHostAndPort = endpoint.getHostsAndPorts().get(0); try (RedisClient client = RedisClient.builder().hostAndPort(nodeHostAndPort) .clientConfig(endpoint.getClientConfigBuilder().build()).build()) { // Clean up any previous state client.hotkeysStop(); client.hotkeysReset(); // Start hotkeys tracking with consecutive slots (0, 1, 2) and a non-consecutive slot (100) // Server should group consecutive slots into a range and keep non-consecutive as single slot String result = client .hotkeysStart(HotkeysParams.hotkeysParams().metrics(HotkeysMetric.CPU, HotkeysMetric.NET) .sample(2).slots(SLOT_0, SLOT_1, SLOT_2, SLOT_100)); assertEquals("OK", result); // Generate traffic on keys that hash to slots 0, 1, 2 String[] keys = { KEY_SLOT_0, KEY_SLOT_1, KEY_SLOT_2 }; for (int i = 0; i < 50; i++) { for (String key : keys) { client.set(key, "value" + i); client.get(key); } } HotkeysInfo info = client.hotkeysGet(); assertNotNull(info); // Verify tracking state assertTrue(info.isTrackingActive()); assertEquals(2, info.getSampleRatio()); // Verify selected slots - should have 2 entries: // 1. Range [0, 2] for consecutive slots 0, 1, 2 // 2. Single slot [100] for non-consecutive slot List selectedSlots = info.getSelectedSlots(); assertNotNull(selectedSlots); assertEquals(2, selectedSlots.size()); // First entry: range [0, 2] assertEquals(2, selectedSlots.get(0).length); assertEquals(SLOT_0, selectedSlots.get(0)[0]); assertEquals(SLOT_2, selectedSlots.get(0)[1]); // Second entry: single slot [100] assertEquals(1, selectedSlots.get(1).length); assertEquals(SLOT_100, selectedSlots.get(1)[0]); // Verify slot-specific CPU metrics (only present when SLOTS is used) assertNotNull(info.getSampledCommandSelectedSlotsUs()); assertThat(info.getSampledCommandSelectedSlotsUs(), greaterThan(0L)); assertNotNull(info.getAllCommandsSelectedSlotsUs()); assertThat(info.getAllCommandsSelectedSlotsUs(), greaterThan(0L)); assertThat(info.getAllCommandsAllSlotsUs(), greaterThan(0L)); // Verify slot-specific network bytes metrics assertNotNull(info.getNetBytesSampledCommandsSelectedSlots()); assertThat(info.getNetBytesSampledCommandsSelectedSlots(), greaterThan(0L)); assertNotNull(info.getNetBytesAllCommandsSelectedSlots()); assertThat(info.getNetBytesAllCommandsSelectedSlots(), greaterThan(0L)); assertThat(info.getNetBytesAllCommandsAllSlots(), greaterThan(0L)); // Verify timing fields assertThat(info.getCollectionStartTimeUnixMs(), greaterThan(0L)); assertThat(info.getCollectionDurationMs(), greaterThanOrEqualTo(0L)); assertThat(info.getTotalCpuTimeUserMs(), greaterThanOrEqualTo(0L)); assertThat(info.getTotalCpuTimeSysMs(), greaterThanOrEqualTo(0L)); assertThat(info.getTotalNetBytes(), greaterThan(0L)); // Verify key metrics maps contain our 3 keys with values > 0 Map byCpuTimeUs = info.getByCpuTimeUs(); assertNotNull(byCpuTimeUs); assertEquals(3, byCpuTimeUs.size()); assertTrue(byCpuTimeUs.containsKey(KEY_SLOT_0)); assertTrue(byCpuTimeUs.containsKey(KEY_SLOT_1)); assertTrue(byCpuTimeUs.containsKey(KEY_SLOT_2)); assertThat(byCpuTimeUs.get(KEY_SLOT_0), greaterThan(0L)); assertThat(byCpuTimeUs.get(KEY_SLOT_1), greaterThan(0L)); assertThat(byCpuTimeUs.get(KEY_SLOT_2), greaterThan(0L)); Map byNetBytes = info.getByNetBytes(); assertNotNull(byNetBytes); assertEquals(3, byNetBytes.size()); assertTrue(byNetBytes.containsKey(KEY_SLOT_0)); assertTrue(byNetBytes.containsKey(KEY_SLOT_1)); assertTrue(byNetBytes.containsKey(KEY_SLOT_2)); assertThat(byNetBytes.get(KEY_SLOT_0), greaterThan(0L)); assertThat(byNetBytes.get(KEY_SLOT_1), greaterThan(0L)); assertThat(byNetBytes.get(KEY_SLOT_2), greaterThan(0L)); client.hotkeysStop(); client.hotkeysReset(); } } } ================================================ FILE: src/test/java/redis/clients/jedis/commands/jedis/ClusterJedisCommandsTestBase.java ================================================ package redis.clients.jedis.commands.jedis; import java.util.HashSet; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Tag; import org.junit.jupiter.api.extension.RegisterExtension; import org.junit.jupiter.api.BeforeAll; import redis.clients.jedis.EndpointConfig; import redis.clients.jedis.Endpoints; import redis.clients.jedis.RedisClusterClient; import redis.clients.jedis.util.EnabledOnCommandCondition; import redis.clients.jedis.util.EnvCondition; import redis.clients.jedis.util.RedisVersionCondition; @Tag("integration") public abstract class ClusterJedisCommandsTestBase { protected static EndpointConfig endpoint; protected RedisClusterClient cluster; @RegisterExtension public RedisVersionCondition versionCondition = new RedisVersionCondition( () -> Endpoints.getRedisEndpoint("cluster-stable")); @RegisterExtension public EnabledOnCommandCondition enabledOnCommandCondition = new EnabledOnCommandCondition( () -> Endpoints.getRedisEndpoint("cluster-stable")); @RegisterExtension public static EnvCondition envCondition = new EnvCondition(); @BeforeAll public static void prepareEndpoint() { endpoint = Endpoints.getRedisEndpoint("cluster-stable"); } @BeforeEach public void setUp() { cluster = RedisClusterClient.builder() .nodes(new HashSet<>(endpoint.getHostsAndPorts())) .clientConfig(endpoint.getClientConfigBuilder().build()) .build(); cluster.flushAll(); } @AfterEach public void tearDown() { if (cluster != null) { cluster.flushAll(); cluster.close(); } } } ================================================ FILE: src/test/java/redis/clients/jedis/commands/jedis/ClusterScriptingCommandsTest.java ================================================ package redis.clients.jedis.commands.jedis; import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; import java.util.List; import java.util.Map; import io.redis.test.annotations.SinceRedisVersion; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Tag; import redis.clients.jedis.HostAndPort; import redis.clients.jedis.args.FlushMode; import redis.clients.jedis.exceptions.JedisBroadcastException; import redis.clients.jedis.exceptions.JedisClusterOperationException; import redis.clients.jedis.exceptions.JedisDataException; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertThrows; import static org.junit.jupiter.api.Assertions.assertSame; import static org.junit.jupiter.api.Assertions.assertTrue; @Tag("integration") public class ClusterScriptingCommandsTest extends ClusterJedisCommandsTestBase { @Test public void testJedisClusterException() { String script = "return {KEYS[1],KEYS[2],ARGV[1],ARGV[2],ARGV[3]}"; List keys = new ArrayList<>(); keys.add("key1"); keys.add("key2"); List args = new ArrayList<>(); args.add("first"); args.add("second"); args.add("third"); assertThrows(JedisClusterOperationException.class, () -> { cluster.eval(script, keys, args); }); } @Test public void testEval2() { String script = "return redis.call('set',KEYS[1],'bar')"; int numKeys = 1; String[] args = { "foo" }; cluster.eval(script, numKeys, args); assertEquals("bar", cluster.get("foo")); } @Test public void testScriptLoadAndScriptExists() { String sha1 = cluster.scriptLoad("return redis.call('get','foo')", "key1"); assertTrue(cluster.scriptExists(sha1, "key1")); } @Test public void testEvalsha() { String sha1 = cluster.scriptLoad("return 10", "key1"); Object o = cluster.evalsha(sha1, 1, "key1"); assertEquals("10", o.toString()); } @Test public void testJedisClusterException2() { byte[] script = "return {KEYS[1],KEYS[2],ARGV[1],ARGV[2],ARGV[3]}".getBytes(); List keys = new ArrayList(); keys.add("key1".getBytes()); keys.add("key2".getBytes()); List args = new ArrayList(); args.add("first".getBytes()); args.add("second".getBytes()); args.add("third".getBytes()); assertThrows( JedisClusterOperationException.class, ()-> cluster.eval(script, keys, args)); } @Test public void testBinaryEval() { byte[] script = "return redis.call('set',KEYS[1],'bar')".getBytes(); byte[] args = "foo".getBytes(); cluster.eval(script, 1, args); assertEquals("bar", cluster.get("foo")); } @Test public void testBinaryScriptFlush() { byte[] byteKey = "key1".getBytes(); cluster.scriptLoad("return redis.call('get','foo')".getBytes(), byteKey); assertEquals("OK", cluster.scriptFlush(byteKey)); assertEquals("OK", cluster.scriptFlush(byteKey, FlushMode.SYNC)); } @Test public void testBinaryScriptKill() { byte[] byteKey = "key1".getBytes(); assertThrows(JedisDataException.class, ()-> cluster.scriptKill(byteKey)); } @Test public void testBinaryScriptExists() { byte[] byteKey = "key1".getBytes(); byte[] sha1 = cluster.scriptLoad("return redis.call('get','foo')".getBytes(), byteKey); byte[][] arraySha1 = { sha1 }; assertEquals(Collections.singletonList(Boolean.TRUE), cluster.scriptExists(byteKey, arraySha1)); } @Test public void broadcast() { String script_1 = "return 'jedis'"; String sha1_1 = cluster.scriptLoad(script_1); String script_2 = "return 79"; String sha1_2 = cluster.scriptLoad(script_2); assertEquals(Arrays.asList(true, true), cluster.scriptExists(Arrays.asList(sha1_1, sha1_2))); cluster.scriptFlush(); assertEquals(Arrays.asList(false, false), cluster.scriptExists(Arrays.asList(sha1_1, sha1_2))); } @Test @SinceRedisVersion("7.0.0") public void broadcastWithError() { JedisBroadcastException error = assertThrows(JedisBroadcastException.class, () -> cluster.functionDelete("xyz")); Map replies = error.getReplies(); assertEquals(3, replies.size()); replies.values().forEach(r -> { assertSame(JedisDataException.class, r.getClass()); assertEquals("ERR Library not found", ((JedisDataException) r).getMessage()); }); } } ================================================ FILE: src/test/java/redis/clients/jedis/commands/jedis/ClusterShardedPublishSubscribeCommandsTest.java ================================================ package redis.clients.jedis.commands.jedis; import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.hasItems; import static org.junit.jupiter.api.Assertions.assertArrayEquals; import static org.junit.jupiter.api.Assertions.assertEquals; import java.util.HashMap; import java.util.Map; import io.redis.test.annotations.SinceRedisVersion; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Tag; import redis.clients.jedis.BinaryJedisShardedPubSub; import redis.clients.jedis.Connection; import redis.clients.jedis.Jedis; import redis.clients.jedis.JedisShardedPubSub; import redis.clients.jedis.util.JedisClusterCRC16; import redis.clients.jedis.util.SafeEncoder; @SinceRedisVersion(value = "7.0.0", message = "Sharded Pub/Sub") @Tag("integration") public class ClusterShardedPublishSubscribeCommandsTest extends ClusterJedisCommandsTestBase { private void publishOne(final String channel, final String message) { Thread t = new Thread(() -> cluster.spublish(channel, message)); t.start(); } @Test public void subscribe() throws InterruptedException { cluster.ssubscribe(new JedisShardedPubSub() { @Override public void onSMessage(String channel, String message) { assertEquals("foo", channel); assertEquals("exit", message); sunsubscribe(); } @Override public void onSSubscribe(String channel, int subscribedChannels) { assertEquals("foo", channel); assertEquals(1, subscribedChannels); // now that I'm subscribed... publish publishOne("foo", "exit"); } @Override public void onSUnsubscribe(String channel, int subscribedChannels) { assertEquals("foo", channel); assertEquals(0, subscribedChannels); } }, "foo"); } @Test public void subscribeMany() { cluster.ssubscribe(new JedisShardedPubSub() { @Override public void onSMessage(String channel, String message) { sunsubscribe(channel); } @Override public void onSSubscribe(String channel, int subscribedChannels) { publishOne(channel, "exit"); } }, "{foo}", "{foo}bar"); } @Test public void pubSubChannels() { cluster.ssubscribe(new JedisShardedPubSub() { private int count = 0; @Override public void onSSubscribe(String channel, int subscribedChannels) { count++; // All channels are subscribed if (count == 3) { try (Connection conn = cluster.getConnectionFromSlot(JedisClusterCRC16.getSlot("testchan")); Jedis jedis = new Jedis(conn)) { assertThat(jedis.pubsubShardChannels(), hasItems("{testchan}1", "{testchan}2", "{testchan}3")); } sunsubscribe(); } } }, "{testchan}1", "{testchan}2", "{testchan}3"); } @Test public void pubSubChannelsWithPattern() { cluster.ssubscribe(new JedisShardedPubSub() { private int count = 0; @Override public void onSSubscribe(String channel, int subscribedChannels) { count++; // All channels are subscribed if (count == 3) { try (Connection conn = cluster.getConnectionFromSlot(JedisClusterCRC16.getSlot("testchan")); Jedis otherJedis = new Jedis(conn)) { assertThat(otherJedis.pubsubShardChannels("*testchan*"), hasItems("{testchan}1", "{testchan}2", "{testchan}3")); } sunsubscribe(); } } }, "{testchan}1", "{testchan}2", "{testchan}3"); } @Test public void pubSubNumSub() { final Map expectedNumSub = new HashMap<>(); expectedNumSub.put("{testchannel}1", 1L); expectedNumSub.put("{testchannel}2", 1L); cluster.ssubscribe(new JedisShardedPubSub() { private int count = 0; @Override public void onSSubscribe(String channel, int subscribedChannels) { count++; if (count == 2) { try (Connection conn = cluster.getConnectionFromSlot(JedisClusterCRC16.getSlot("testchannel")); Jedis otherJedis = new Jedis(conn)) { Map numSub = otherJedis.pubsubShardNumSub("{testchannel}1", "{testchannel}2"); assertEquals(expectedNumSub, numSub); } sunsubscribe(); } } }, "{testchannel}1", "{testchannel}2"); } @Test public void binarySubscribe() { cluster.ssubscribe(new BinaryJedisShardedPubSub() { @Override public void onSMessage(byte[] channel, byte[] message) { assertArrayEquals(SafeEncoder.encode("foo"), channel); assertArrayEquals(SafeEncoder.encode("exit"), message); sunsubscribe(); } @Override public void onSSubscribe(byte[] channel, int subscribedChannels) { assertArrayEquals(SafeEncoder.encode("foo"), channel); assertEquals(1, subscribedChannels); publishOne(SafeEncoder.encode(channel), "exit"); } @Override public void onSUnsubscribe(byte[] channel, int subscribedChannels) { assertArrayEquals(SafeEncoder.encode("foo"), channel); assertEquals(0, subscribedChannels); } }, SafeEncoder.encode("foo")); } @Test public void binarySubscribeMany() { cluster.ssubscribe(new BinaryJedisShardedPubSub() { @Override public void onSMessage(byte[] channel, byte[] message) { sunsubscribe(channel); } @Override public void onSSubscribe(byte[] channel, int subscribedChannels) { publishOne(SafeEncoder.encode(channel), "exit"); } }, SafeEncoder.encode("{foo}"), SafeEncoder.encode("{foo}bar")); } } ================================================ FILE: src/test/java/redis/clients/jedis/commands/jedis/ClusterValuesCommandsTest.java ================================================ package redis.clients.jedis.commands.jedis; import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.notNullValue; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertNull; import static org.junit.jupiter.api.Assertions.assertTrue; import static org.junit.jupiter.api.Assertions.fail; import java.util.ArrayList; import java.util.HashMap; import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Set; import io.redis.test.annotations.ConditionalOnEnv; import io.redis.test.annotations.SinceRedisVersion; import org.junit.jupiter.api.Test; import redis.clients.jedis.BuilderFactory; import redis.clients.jedis.CommandArguments; import redis.clients.jedis.CommandObject; import redis.clients.jedis.GeoCoordinate; import redis.clients.jedis.JedisPubSub; import redis.clients.jedis.Protocol; import redis.clients.jedis.ScanIteration; import redis.clients.jedis.args.GeoUnit; import redis.clients.jedis.params.GeoRadiusParam; import redis.clients.jedis.params.GeoRadiusStoreParam; import redis.clients.jedis.resps.ScanResult; import redis.clients.jedis.util.KeyValue; import redis.clients.jedis.util.TestEnvUtil; public class ClusterValuesCommandsTest extends ClusterJedisCommandsTestBase { @Test public void nullKeys() { byte[] bfoo = new byte[]{0x0b, 0x0f, 0x00, 0x00}; try { cluster.exists((byte[]) null); fail(); } catch (NullPointerException e) { // expected } try { cluster.exists(bfoo, null); fail(); } catch (NullPointerException e) { // expected } try { cluster.exists(null, bfoo); fail(); } catch (NullPointerException e) { // expected } } @Test public void testHincrByFloat() { Double value = cluster.hincrByFloat("foo", "bar", 1.5d); assertEquals((Double) 1.5d, value); value = cluster.hincrByFloat("foo", "bar", -1.5d); assertEquals((Double) 0d, value); value = cluster.hincrByFloat("foo", "bar", -10.7d); assertEquals(Double.valueOf(-10.7d), value); } @Test public void georadiusStore() { // prepare datas Map coordinateMap = new HashMap(); coordinateMap.put("Palermo", new GeoCoordinate(13.361389, 38.115556)); coordinateMap.put("Catania", new GeoCoordinate(15.087269, 37.502669)); cluster.geoadd("{Sicily}", coordinateMap); long size = cluster.georadiusStore("{Sicily}", 15, 37, 200, GeoUnit.KM, GeoRadiusParam.geoRadiusParam(), GeoRadiusStoreParam.geoRadiusStoreParam().store("{Sicily}Store")); assertEquals(2, size); List expected = new ArrayList(); expected.add("Palermo"); expected.add("Catania"); assertEquals(expected, cluster.zrange("{Sicily}Store", 0, -1)); } private void publishOne(final String channel, final String message) { Thread t = new Thread(new Runnable() { public void run() { try { cluster.publish(channel, message); } catch (Exception ex) { } } }); t.start(); } @Test public void subscribe() throws InterruptedException { cluster.subscribe(new JedisPubSub() { public void onMessage(String channel, String message) { assertEquals("foo", channel); assertEquals("exit", message); unsubscribe(); } public void onSubscribe(String channel, int subscribedChannels) { assertEquals("foo", channel); assertEquals(1, subscribedChannels); // now that I'm subscribed... publish publishOne("foo", "exit"); } public void onUnsubscribe(String channel, int subscribedChannels) { assertEquals("foo", channel); assertEquals(0, subscribedChannels); } }, "foo"); } @Test public void rawPingBroadcast() { String reply = cluster.broadcastCommand( new CommandObject<>(new CommandArguments(Protocol.Command.PING), BuilderFactory.STRING)); assertEquals("PONG", reply); } @Test public void pingBroadcast() { assertEquals("PONG", cluster.ping()); } @Test public void info() { String info = cluster.info(); assertThat(info, notNullValue()); info = cluster.info("server"); assertThat(info, notNullValue()); } @Test public void flushAllBroadcast() { assertNull(cluster.get("foo")); assertEquals("OK", cluster.set("foo", "bar")); assertEquals("bar", cluster.get("foo")); cluster.flushAll(); assertNull(cluster.get("foo")); } @Test public void scanIteration() { Set allIn = new HashSet<>(26 * 26); char[] arr = new char[2]; for (int i = 0; i < 26; i++) { arr[0] = (char) ('a' + i); for (int j = 0; j < 26; j++) { arr[1] = (char) ('a' + j); String str = new String(arr); cluster.incr(str); allIn.add(str); } } Set allScan = new HashSet<>(); ScanIteration scan = cluster.scanIteration(10, "*"); while (!scan.isIterationCompleted()) { ScanResult batch = scan.nextBatch(); allScan.addAll(batch.getResult()); } assertEquals(allIn, allScan); Set allTypeScan = new HashSet<>(); ScanIteration typeScan = cluster.scanIteration(10, "*", "string"); while (!typeScan.isIterationCompleted()) { ScanResult batch = typeScan.nextBatch(); allTypeScan.addAll(batch.getResult()); } assertEquals(allIn, allTypeScan); } @Test public void scanIterationCollect() { Set allIn = new HashSet<>(26 * 26); char[] arr = new char[2]; for (int i = 0; i < 26; i++) { arr[0] = (char) ('a' + i); for (int j = 0; j < 26; j++) { arr[1] = (char) ('a' + j); String str = new String(arr); cluster.incr(str); allIn.add(str); } } assertEquals(allIn, cluster.scanIteration(100, "*").collect(new HashSet<>(26 * 26))); } @Test public void dbSizeAggregation() { // Set some keys across the cluster (different hash slots) cluster.set("key1", "value1"); cluster.set("key2", "value2"); cluster.set("key3", "value3"); // dbSize should return sum of keys across all shards long dbSize = cluster.dbSize(); assertTrue(dbSize >= 3); } @Test public void msetCrossShard() { // MSET with keys on different shards (MULTI_SHARD policy) // Using keys without hash tags to distribute across shards assertEquals("OK", cluster.mset("mset_key_a", "value_a", "mset_key_b", "value_b", "mset_key_c", "value_c")); // Verify all keys were set assertEquals("value_a", cluster.get("mset_key_a")); assertEquals("value_b", cluster.get("mset_key_b")); assertEquals("value_c", cluster.get("mset_key_c")); } @Test public void scriptExistsAggregation() { String script = "return 1"; String sampleKey = "testKey"; // Load a script to get its SHA1 String sha1 = cluster.scriptLoad(script, sampleKey); // Verify it exists (single SHA1 check - returns Boolean, aggregated via AGG_LOGICAL_AND) assertTrue(cluster.scriptExists(sha1, sampleKey)); // Test with multiple SHA1s - one exists, one doesn't String unknownSha1 = "0000000000000000000000000000000000000000"; List results = cluster.scriptExists(sampleKey, sha1, unknownSha1); assertEquals(2, results.size()); assertTrue(results.get(0)); // Known script exists assertFalse(results.get(1)); // Unknown script doesn't exist } @Test @SinceRedisVersion(value = "8.2.0", message = "CLUSTER SLOT-STATS requires Redis 8.2 or later") @ConditionalOnEnv(value = TestEnvUtil.ENV_REDIS_ENTERPRISE, enabled = false) public void clusterSlotStatsAggregation() { // Set some keys across the cluster to ensure slots have data cluster.set("key1", "value1"); cluster.set("key2", "value2"); cluster.set("key3", "value3"); // Use broadcastCommand to send to all shards and aggregate with DEFAULT policy // CLUSTER SLOT-STATS SLOTSRANGE returns a list (array) of slot statistics from each shard // Each element is [slot_number, {key-count: N, cpu-usec: N, ...}] // The DEFAULT response policy should concatenate lists from all nodes List result = cluster.broadcastCommand( new CommandObject<>( new CommandArguments(Protocol.Command.CLUSTER).add("SLOT-STATS").add("SLOTSRANGE").add(0).add(16383), BuilderFactory.RAW_OBJECT_LIST)); // Verify we got aggregated results from multiple shards assertThat(result, notNullValue()); // The result should be a concatenated list containing slot statistics from all shards // In a 3-shard cluster, each shard returns stats only for slots it owns // so the merged list should contain entries from all 16384 slots assertFalse(result.isEmpty(), "Should have aggregated slot statistics from cluster nodes"); // Verify the aggregated list contains entries from all shards assertEquals(16384, result.size(), "Aggregated list should contain slot statistics from multiple shards"); } @Test @SinceRedisVersion(value = "7.2.0", message = "WAITAOF requires Redis 7.2 or later") @ConditionalOnEnv(value = TestEnvUtil.ENV_REDIS_ENTERPRISE, enabled = false) public void waitAOFAggregation() { // Set some keys across the cluster to ensure there's data to sync cluster.set("key1", "value1"); cluster.set("key2", "value2"); cluster.set("key3", "value3"); // Use broadcastCommand to send WAITAOF to all shards and aggregate with AGG_MIN policy // WAITAOF returns a KeyValue where: // - key = number of local AOF syncs // - value = number of replica AOF syncs // The AGG_MIN response policy should return the minimum values across all shards KeyValue result = cluster.broadcastCommand( new CommandObject<>( new CommandArguments(Protocol.Command.WAITAOF).add(0).add(0).add(100), BuilderFactory.LONG_LONG_PAIR)); // Verify we got aggregated results assertThat(result, notNullValue()); // With numLocal=0 and numReplicas=0, the command should return immediately // The minimum across all shards should be >= 0 for both values assertTrue(result.getKey() >= 0, "Local AOF sync count should be >= 0"); assertTrue(result.getValue() >= 0, "Replica AOF sync count should be >= 0"); } } ================================================ FILE: src/test/java/redis/clients/jedis/commands/jedis/ControlCommandsTest.java ================================================ package redis.clients.jedis.commands.jedis; import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.greaterThan; import static org.hamcrest.Matchers.greaterThanOrEqualTo; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertInstanceOf; import static org.junit.jupiter.api.Assertions.assertNotNull; import static org.junit.jupiter.api.Assertions.assertArrayEquals; import static org.junit.jupiter.api.Assertions.assertNull; import static org.junit.jupiter.api.Assertions.assertThrows; import static org.junit.jupiter.api.Assertions.assertTrue; import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Set; import java.util.concurrent.Callable; import java.util.concurrent.ExecutionException; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.Future; import java.util.concurrent.TimeUnit; import io.redis.test.annotations.ConditionalOnEnv; import io.redis.test.annotations.SinceRedisVersion; import org.hamcrest.MatcherAssert; import org.hamcrest.Matchers; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Tag; import org.junit.jupiter.params.ParameterizedClass; import org.junit.jupiter.params.provider.MethodSource; import redis.clients.jedis.*; import redis.clients.jedis.args.ClientPauseMode; import redis.clients.jedis.args.LatencyEvent; import redis.clients.jedis.exceptions.JedisDataException; import redis.clients.jedis.params.CommandListFilterByParams; import redis.clients.jedis.params.LolwutParams; import redis.clients.jedis.resps.CommandDocument; import redis.clients.jedis.resps.CommandInfo; import redis.clients.jedis.resps.LatencyHistoryInfo; import redis.clients.jedis.resps.LatencyLatestInfo; import redis.clients.jedis.util.AssertUtil; import redis.clients.jedis.util.KeyValue; import redis.clients.jedis.util.SafeEncoder; import redis.clients.jedis.util.TestEnvUtil; @ParameterizedClass @MethodSource("redis.clients.jedis.commands.CommandsTestsParameters#respVersions") @Tag("integration") public class ControlCommandsTest extends JedisCommandsTestBase { public ControlCommandsTest(RedisProtocol redisProtocol) { super(redisProtocol); } @Test @ConditionalOnEnv(value = TestEnvUtil.ENV_REDIS_ENTERPRISE, enabled = false) public void save() { try { String status = jedis.save(); assertEquals("OK", status); } catch (JedisDataException e) { assertTrue("ERR Background save already in progress".equalsIgnoreCase(e.getMessage())); } } @Test @ConditionalOnEnv(value = TestEnvUtil.ENV_REDIS_ENTERPRISE, enabled = false) public void bgsave() { try { String status = jedis.bgsave(); assertEquals("Background saving started", status); } catch (JedisDataException e) { assertTrue("ERR Background save already in progress".equalsIgnoreCase(e.getMessage())); } } @Test @ConditionalOnEnv(value = TestEnvUtil.ENV_REDIS_ENTERPRISE, enabled = false) public void bgsaveSchedule() { Set responses = new HashSet<>(); responses.add("OK"); responses.add("Background saving scheduled"); responses.add("Background saving started"); String status = jedis.bgsaveSchedule(); assertTrue(responses.contains(status)); } @Test @ConditionalOnEnv(value = TestEnvUtil.ENV_REDIS_ENTERPRISE, enabled = false) public void bgrewriteaof() { String scheduled = "Background append only file rewriting scheduled"; String started = "Background append only file rewriting started"; String status = jedis.bgrewriteaof(); boolean ok = status.equals(scheduled) || status.equals(started); assertTrue(ok); } @Test @ConditionalOnEnv(value = TestEnvUtil.ENV_REDIS_ENTERPRISE, enabled = false) public void lastsave() throws InterruptedException { long saved = jedis.lastsave(); assertTrue(saved > 0); } @Test public void info() { String info = jedis.info(); assertNotNull(info); info = jedis.info("server"); assertNotNull(info); } @Test @ConditionalOnEnv(value = TestEnvUtil.ENV_REDIS_ENTERPRISE, enabled = false) public void readonly() { try { jedis.readonly(); } catch (JedisDataException e) { assertTrue("ERR This instance has cluster support disabled".equalsIgnoreCase(e.getMessage())); } } @Test @ConditionalOnEnv(value = TestEnvUtil.ENV_REDIS_ENTERPRISE, enabled = false) public void readwrite() { try { jedis.readwrite(); } catch (JedisDataException e) { assertTrue("ERR This instance has cluster support disabled".equalsIgnoreCase(e.getMessage())); } } @Test public void roleMaster() { EndpointConfig endpoint = Endpoints.getRedisEndpoint("standalone0"); try (Jedis master = new Jedis(endpoint.getHostAndPort(), endpoint.getClientConfigBuilder().build())) { List role = master.role(); assertEquals("master", role.get(0)); assertInstanceOf(Long.class, role.get(1)); assertInstanceOf(List.class, role.get(2)); // binary List brole = master.roleBinary(); assertArrayEquals("master".getBytes(), (byte[]) brole.get(0)); assertInstanceOf(Long.class, brole.get(1)); assertInstanceOf(List.class, brole.get(2)); } } @Test @ConditionalOnEnv(value = TestEnvUtil.ENV_REDIS_ENTERPRISE, enabled = false) public void roleSlave() { EndpointConfig primaryEndpoint = Endpoints.getRedisEndpoint("standalone0"); EndpointConfig secondaryEndpoint = Endpoints.getRedisEndpoint( "standalone4-replica-of-standalone1"); try (Jedis slave = new Jedis(secondaryEndpoint.getHostAndPort(), secondaryEndpoint.getClientConfigBuilder().build())) { List role = slave.role(); assertEquals("slave", role.get(0)); assertEquals((long) primaryEndpoint.getPort(), role.get(2)); assertEquals("connected", role.get(3)); assertInstanceOf(Long.class, role.get(4)); // binary List brole = slave.roleBinary(); assertArrayEquals("slave".getBytes(), (byte[]) brole.get(0)); assertEquals((long) primaryEndpoint.getPort(), brole.get(2)); assertArrayEquals("connected".getBytes(), (byte[]) brole.get(3)); assertInstanceOf(Long.class, brole.get(4)); } } @Test @ConditionalOnEnv(value = TestEnvUtil.ENV_REDIS_ENTERPRISE, enabled = false) public void roleSentinel() { try (Jedis sentinel = new Jedis(Endpoints.getRedisEndpoint("sentinel-standalone2-1").getHostAndPort())) { List role = sentinel.role(); assertEquals("sentinel", role.get(0)); assertInstanceOf(List.class, role.get(1)); AssertUtil.assertCollectionContains((List) role.get(1), "mymaster"); // binary List brole = sentinel.roleBinary(); assertArrayEquals("sentinel".getBytes(), (byte[]) brole.get(0)); assertInstanceOf(List.class, brole.get(1)); AssertUtil.assertByteArrayCollectionContains((List) brole.get(1), "mymaster".getBytes()); } } @Test public void monitor() { new Thread(new Runnable() { @Override public void run() { try { // sleep 100ms to make sure that monitor thread runs first Thread.sleep(100); } catch (InterruptedException e) { } try (Jedis j = new Jedis(endpoint.getHostAndPort())) { j.auth(endpoint.getPassword()); for (int i = 0; i < 5; i++) { j.incr("foobared"); } j.disconnect(); } } }).start(); jedis.monitor(new JedisMonitor() { private int count = 0; @Override public void onCommand(String command) { if (command.contains("INCR")) { count++; } if (count == 5) { client.disconnect(); } } }); } @Test public void configGet() { Map info = jedis.configGet("s*"); // slowlog-max-len assertNotNull(info); assertFalse(info.isEmpty()); // assertTrue(info.size() % 2 == 0); Map infoBinary = jedis.configGet("s*".getBytes()); assertNotNull(infoBinary); assertFalse(infoBinary.isEmpty()); // assertTrue(infoBinary.size() % 2 == 0); } @Test public void configSet() { Map info = jedis.configGet("slowlog-max-len"); String val = info.get("slowlog-max-len"); assertNotNull(val); assertEquals("OK", jedis.configSet("slowlog-max-len", "200")); assertEquals("OK", jedis.configSet("slowlog-max-len", val)); } @Test public void configSetBinary() { byte[] slowloglen = SafeEncoder.encode("slowlog-max-len"); Map info = jedis.configGet(slowloglen); byte[] memory = info.get(slowloglen); assertNotNull(memory); assertEquals("OK", jedis.configSet(slowloglen, Protocol.toByteArray(200))); assertEquals("OK", jedis.configSet(slowloglen, memory)); } @Test @SinceRedisVersion(value = "7.0.0", message = "Starting with Redis version 7.0.0: Added the ability to pass multiple pattern parameters in one call") public void configGetSetMulti() { String[] params = new String[]{"hash-max-listpack-entries", "set-max-intset-entries", "zset-max-listpack-entries"}; Map info = jedis.configGet(params); assertEquals(3, info.size()); assertEquals("OK", jedis.configSet(info)); byte[][] bparams = new byte[][]{SafeEncoder.encode("hash-max-listpack-entries"), SafeEncoder.encode("set-max-intset-entries"), SafeEncoder.encode("zset-max-listpack-entries")}; Map binfo = jedis.configGet(bparams); assertEquals(3, binfo.size()); assertEquals("OK", jedis.configSetBinary(binfo)); } @Test @ConditionalOnEnv(value = TestEnvUtil.ENV_OSS_DOCKER, enabled = true) public void waitReplicas() { assertEquals(1, jedis.waitReplicas(1, 100)); } @Test @SinceRedisVersion("7.2.0") public void waitAof() { assertEquals(KeyValue.of(0L, 0L), jedis.waitAOF(0L, 0L, 100L)); } @Test @ConditionalOnEnv(value = TestEnvUtil.ENV_REDIS_ENTERPRISE, enabled = false) public void clientPause() throws InterruptedException, ExecutionException { ExecutorService executorService = Executors.newFixedThreadPool(2); try (Jedis jedisToPause1 = createJedis(); Jedis jedisToPause2 = createJedis();) { jedis.clientPause(1000L); Future latency1 = executorService.submit(new Callable() { @Override public Long call() throws Exception { long startMillis = System.currentTimeMillis(); assertEquals("PONG", jedisToPause1.ping()); return System.currentTimeMillis() - startMillis; } }); Future latency2 = executorService.submit(new Callable() { @Override public Long call() throws Exception { long startMillis = System.currentTimeMillis(); assertEquals("PONG", jedisToPause2.ping()); return System.currentTimeMillis() - startMillis; } }); assertThat(latency1.get(), greaterThan(100L)); assertThat(latency2.get(), greaterThan(100L)); } finally { executorService.shutdown(); if (!executorService.awaitTermination(2, TimeUnit.SECONDS)) { executorService.shutdownNow(); } } } @Test @ConditionalOnEnv(value = TestEnvUtil.ENV_REDIS_ENTERPRISE, enabled = false) public void clientPauseAll() throws InterruptedException, ExecutionException { ExecutorService executorService = Executors.newFixedThreadPool(1); try (Jedis jedisPause = createJedis()) { jedis.clientPause(1000L, ClientPauseMode.ALL); Future latency = executorService.submit(new Callable() { @Override public Long call() throws Exception { long startMillis = System.currentTimeMillis(); jedisPause.get("key"); return System.currentTimeMillis() - startMillis; } }); assertThat(latency.get(), greaterThan(100L)); } finally { executorService.shutdown(); if (!executorService.awaitTermination(5, TimeUnit.SECONDS)) { executorService.shutdownNow(); } } } @Test @ConditionalOnEnv(value = TestEnvUtil.ENV_REDIS_ENTERPRISE, enabled = false) public void clientPauseWrite() throws InterruptedException, ExecutionException { ExecutorService executorService = Executors.newFixedThreadPool(2); try (Jedis jedisRead = createJedis(); Jedis jedisWrite = createJedis();) { jedis.clientPause(1000L, ClientPauseMode.WRITE); Future latencyRead = executorService.submit(new Callable() { @Override public Long call() throws Exception { long startMillis = System.currentTimeMillis(); jedisRead.get("key"); return System.currentTimeMillis() - startMillis; } }); Future latencyWrite = executorService.submit(new Callable() { @Override public Long call() throws Exception { long startMillis = System.currentTimeMillis(); jedisWrite.set("key", "value"); return System.currentTimeMillis() - startMillis; } }); assertThat(latencyRead.get(), Matchers.lessThan(100L)); assertThat(latencyWrite.get(), Matchers.greaterThan(100L)); } finally { executorService.shutdown(); if (!executorService.awaitTermination(5, TimeUnit.SECONDS)) { executorService.shutdownNow(); } } } @Test @ConditionalOnEnv(value = TestEnvUtil.ENV_REDIS_ENTERPRISE, enabled = false) public void clientUnpause() { assertEquals("OK", jedis.clientUnpause()); } @Test @SinceRedisVersion("7.0.0") @ConditionalOnEnv(value = TestEnvUtil.ENV_REDIS_ENTERPRISE, enabled = false) public void clientNoEvict() { assertEquals("OK", jedis.clientNoEvictOn()); assertEquals("OK", jedis.clientNoEvictOff()); } @Test @SinceRedisVersion("7.2.0") public void clientNoTouch() { assertEquals("OK", jedis.clientNoTouchOn()); assertEquals("OK", jedis.clientNoTouchOff()); } @Test @ConditionalOnEnv(value = TestEnvUtil.ENV_REDIS_ENTERPRISE, enabled = false) public void memoryDoctorString() { String memoryInfo = jedis.memoryDoctor(); assertNotNull(memoryInfo); } @Test @ConditionalOnEnv(value = TestEnvUtil.ENV_REDIS_ENTERPRISE, enabled = false) public void memoryDoctorBinary() { byte[] memoryInfo = jedis.memoryDoctorBinary(); assertNotNull(memoryInfo); } @Test public void memoryUsageString() { // Note: It has been recommended not to base MEMORY USAGE test on exact value, as the response // may subject to be 'tuned' especially targeting a major Redis release. jedis.set("foo", "bar"); assertThat(jedis.memoryUsage("foo"), greaterThan(20l)); jedis.lpush("foobar", "fo", "ba", "sha"); assertThat(jedis.memoryUsage("foobar", 2), greaterThan(36l)); assertNull(jedis.memoryUsage("roo", 2)); } @Test public void memoryUsageBinary() { // Note: It has been recommended not to base MEMORY USAGE test on exact value, as the response // may subject to be 'tuned' especially targeting a major Redis release. byte[] bfoo = {0x01, 0x02, 0x03, 0x04}; byte[] bbar = {0x05, 0x06, 0x07, 0x08}; byte[] bfoobar = {0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08}; jedis.set(bfoo, bbar); assertThat(jedis.memoryUsage(bfoo), greaterThan(20l)); jedis.lpush(bfoobar, new byte[]{0x01, 0x02}, new byte[]{0x05, 0x06}, new byte[]{0x00}); assertThat(jedis.memoryUsage(bfoobar, 2), greaterThanOrEqualTo(40l)); assertNull(jedis.memoryUsage("roo", 2)); } @Test @ConditionalOnEnv(value = TestEnvUtil.ENV_REDIS_ENTERPRISE, enabled = false) public void memoryPurge() { String memoryPurge = jedis.memoryPurge(); assertNotNull(memoryPurge); } @Test @ConditionalOnEnv(value = TestEnvUtil.ENV_REDIS_ENTERPRISE, enabled = false) public void memoryStats() { Map stats = jedis.memoryStats(); assertNotNull(stats); } @Test @ConditionalOnEnv(value = TestEnvUtil.ENV_REDIS_ENTERPRISE, enabled = false) public void latencyDoctor() { String report = jedis.latencyDoctor(); assertNotNull(report); } @Test @ConditionalOnEnv(value = TestEnvUtil.ENV_REDIS_ENTERPRISE, enabled = false) public void latencyLatest() { Map report = jedis.latencyLatest(); assertNotNull(report); } @Test @ConditionalOnEnv(value = TestEnvUtil.ENV_REDIS_ENTERPRISE, enabled = false) public void latencyHistoryFork() { List report = jedis.latencyHistory(LatencyEvent.FORK); assertNotNull(report); } @Test @ConditionalOnEnv(value = TestEnvUtil.ENV_REDIS_ENTERPRISE, enabled = false) public void latencyReset() { assertTrue(jedis.latencyReset() >= 0); } @Test public void commandCount() { assertTrue(jedis.commandCount() > 100); } @Test @SinceRedisVersion("7.0.0") public void commandDocs() { Map docs = jedis.commandDocs("SORT", "SET"); CommandDocument sortDoc = docs.get("sort"); assertEquals("generic", sortDoc.getGroup()); MatcherAssert.assertThat(sortDoc.getSummary(), Matchers.isOneOf( "Sort the elements in a list, set or sorted set", "Sorts the elements in a list, a set, or a sorted set, optionally storing the result.")); assertNull(sortDoc.getHistory()); CommandDocument setDoc = docs.get("set"); assertEquals("1.0.0", setDoc.getSince()); assertEquals("O(1)", setDoc.getComplexity()); assertEquals("2.6.12: Added the `EX`, `PX`, `NX` and `XX` options.", setDoc.getHistory().get(0)); } @Test @SinceRedisVersion("7.0.0") public void commandGetKeys() { List keys = jedis.commandGetKeys("SORT", "mylist", "ALPHA", "STORE", "outlist"); assertEquals(2, keys.size()); List>> keySandFlags = jedis.commandGetKeysAndFlags("SET", "k1", "v1"); assertEquals("k1", keySandFlags.get(0).getKey()); assertEquals(2, keySandFlags.get(0).getValue().size()); } @Test @SinceRedisVersion("7.0.0") public void commandNoArgs() { Map infos = jedis.command(); assertThat(infos.size(), greaterThan(0)); CommandInfo getInfo = infos.get("get"); assertEquals(2, getInfo.getArity()); assertEquals(2, getInfo.getFlags().size()); assertEquals(1, getInfo.getFirstKey()); assertEquals(1, getInfo.getLastKey()); assertEquals(1, getInfo.getStep()); assertNull(infos.get("foo")); // non-existing command CommandInfo setInfo = infos.get("set"); assertEquals(3, setInfo.getAclCategories().size()); assertEquals(0, setInfo.getTips().size()); assertEquals(0, setInfo.getSubcommands().size()); } @Test @SinceRedisVersion("7.0.0") public void commandInfo() { Map infos = jedis.commandInfo("GET", "foo", "SET"); CommandInfo getInfo = infos.get("get"); assertEquals(2, getInfo.getArity()); assertEquals(2, getInfo.getFlags().size()); assertEquals(1, getInfo.getFirstKey()); assertEquals(1, getInfo.getLastKey()); assertEquals(1, getInfo.getStep()); assertNull(infos.get("foo")); // non-existing command CommandInfo setInfo = infos.get("set"); assertEquals(3, setInfo.getAclCategories().size()); assertEquals(0, setInfo.getTips().size()); assertEquals(0, setInfo.getSubcommands().size()); } @Test // GitHub Issue #4020 @SinceRedisVersion("7.0.0") public void commandInfoAcl() { Map infos = jedis.commandInfo("ACL"); assertThat(infos, Matchers.aMapWithSize(1)); CommandInfo aclInfo = infos.get("acl"); assertEquals(-2, aclInfo.getArity()); assertEquals(0, aclInfo.getFlags().size()); assertEquals(0, aclInfo.getFirstKey()); assertEquals(0, aclInfo.getLastKey()); assertEquals(0, aclInfo.getStep()); assertEquals(1, aclInfo.getAclCategories().size()); assertEquals(0, aclInfo.getTips().size()); assertThat(aclInfo.getSubcommands().size(), Matchers.greaterThanOrEqualTo(12)); aclInfo.getSubcommands().forEach((name, subcommand) -> { assertThat(name, Matchers.startsWith("acl|")); assertNotNull(subcommand); assertEquals(name, subcommand.getName()); }); } @Test @SinceRedisVersion("7.0.0") public void commandList() { List commands = jedis.commandList(); assertTrue(commands.size() > 100); commands = jedis.commandListFilterBy(CommandListFilterByParams.commandListFilterByParams().filterByModule("JSON")); assertEquals(0, commands.size()); // json module was not loaded commands = jedis.commandListFilterBy(CommandListFilterByParams.commandListFilterByParams().filterByAclCat("admin")); assertTrue(commands.size() > 10); commands = jedis.commandListFilterBy(CommandListFilterByParams.commandListFilterByParams().filterByPattern("a*")); assertTrue(commands.size() > 10); assertThrows(IllegalArgumentException.class, () -> jedis.commandListFilterBy(CommandListFilterByParams.commandListFilterByParams())); } @Test public void lolwut() { assertNotNull(jedis.lolwut()); assertNotNull(jedis.lolwut(new LolwutParams().version(5))); assertNotNull(jedis.lolwut(new LolwutParams().version(5).optionalArguments())); } } ================================================ FILE: src/test/java/redis/clients/jedis/commands/jedis/FailoverCommandsTest.java ================================================ package redis.clients.jedis.commands.jedis; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Tag; import redis.clients.jedis.EndpointConfig; import redis.clients.jedis.HostAndPort; import redis.clients.jedis.Jedis; import redis.clients.jedis.exceptions.JedisDataException; import redis.clients.jedis.params.FailoverParams; import redis.clients.jedis.Endpoints; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertThrows; import static org.junit.jupiter.api.Assertions.fail; @Tag("integration") public class FailoverCommandsTest { private static final int INVALID_PORT = 6000; private static EndpointConfig node1; private static EndpointConfig node2; private HostAndPort masterAddress; private HostAndPort replicaAddress; @BeforeAll public static void setUp() { node1 = Endpoints.getRedisEndpoint("standalone9-failover"); node2 = Endpoints.getRedisEndpoint("standalone10-replica-of-standalone9"); } @BeforeEach public void prepare() { String role1, role2; try (Jedis jedis1 = new Jedis(node1.getHostAndPort(), node1.getClientConfigBuilder().build())) { role1 = (String) jedis1.role().get(0); } try (Jedis jedis2 = new Jedis(node2.getHostAndPort(), node2.getClientConfigBuilder().build())) { role2 = (String) jedis2.role().get(0); } if ("master".equals(role1) && "slave".equals(role2)) { masterAddress = node1.getHostAndPort(); replicaAddress = node2.getHostAndPort(); } else if ("master".equals(role2) && "slave".equals(role1)) { masterAddress = node2.getHostAndPort(); replicaAddress = node1.getHostAndPort(); } else { fail(); } } @Test public void failoverMaster() throws InterruptedException { try (Jedis master = new Jedis(masterAddress)) { assertEquals("OK", master.failover()); Thread.sleep(250); // assertEquals("slave", master.role().get(0)); // Above test has a tendency to get stuck. So, doing following 'not so ideal' test. if ("slave".equals(master.role().get(0))) { // ok } else { // failover stuck } } } @Test public void failoverReplica() { try (Jedis replica = new Jedis(replicaAddress)) { replica.failover(); fail("FAILOVER is not valid when server is a replica."); } catch(JedisDataException ex) { assertEquals("ERR FAILOVER is not valid when server is a replica.", ex.getMessage()); } } @Test public void failoverForceWithoutToFailFast() { try (Jedis master = new Jedis(masterAddress)) { assertThrows(IllegalArgumentException.class, () -> master.failover(FailoverParams.failoverParams() .timeout(100).force())); } } @Test public void failoverForceWithoutTimeoutFailFast() { try (Jedis master = new Jedis(masterAddress)) { assertThrows(IllegalArgumentException.class, ()-> master.failover(FailoverParams.failoverParams() .to(new HostAndPort("127.0.0.1", INVALID_PORT)).force())); } } @Test public void failoverToWrongPort() { try (Jedis master = new Jedis(masterAddress)) { master.failover(FailoverParams.failoverParams().to("127.0.0.1", INVALID_PORT)); fail("FAILOVER target HOST and PORT is not a replica."); } catch(JedisDataException ex) { assertEquals("ERR FAILOVER target HOST and PORT is not a replica.", ex.getMessage()); } } @Test public void failoverToWrongPortWithTimeout() { try (Jedis master = new Jedis(masterAddress)) { master.failover(FailoverParams.failoverParams().to("127.0.0.1", INVALID_PORT).timeout(1000)); fail("FAILOVER target HOST and PORT is not a replica."); } catch(JedisDataException ex) { assertEquals("ERR FAILOVER target HOST and PORT is not a replica.", ex.getMessage()); } } @Test public void abortMaster() { try (Jedis master = new Jedis(masterAddress)) { master.failoverAbort(); } catch(JedisDataException ex) { assertEquals("ERR No failover in progress.", ex.getMessage()); } } @Test public void abortReplica() { try (Jedis replica = new Jedis(replicaAddress)) { replica.failoverAbort(); } catch(JedisDataException ex) { assertEquals("ERR No failover in progress.", ex.getMessage()); } } } ================================================ FILE: src/test/java/redis/clients/jedis/commands/jedis/GeoCommandsTest.java ================================================ package redis.clients.jedis.commands.jedis; import static org.hamcrest.MatcherAssert.assertThat; import static org.junit.jupiter.api.Assertions.assertArrayEquals; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertNull; import static org.junit.jupiter.api.Assertions.assertTrue; import static org.junit.jupiter.api.Assertions.fail; import static redis.clients.jedis.util.AssertUtil.assertByteArrayListEquals; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; import io.redis.test.annotations.ConditionalOnEnv; import org.junit.jupiter.api.Test; import org.junit.jupiter.params.ParameterizedClass; import org.junit.jupiter.params.provider.MethodSource; import redis.clients.jedis.GeoCoordinate; import redis.clients.jedis.RedisProtocol; import redis.clients.jedis.args.GeoUnit; import redis.clients.jedis.params.GeoSearchParam; import redis.clients.jedis.resps.GeoRadiusResponse; import redis.clients.jedis.params.GeoAddParams; import redis.clients.jedis.params.GeoRadiusParam; import redis.clients.jedis.params.GeoRadiusStoreParam; import redis.clients.jedis.util.GeoCoordinateMatcher; import redis.clients.jedis.util.SafeEncoder; import redis.clients.jedis.util.TestEnvUtil; @ParameterizedClass @MethodSource("redis.clients.jedis.commands.CommandsTestsParameters#respVersions") public class GeoCommandsTest extends JedisCommandsTestBase { final byte[] bfoo = { 0x01, 0x02, 0x03, 0x04 }; final byte[] bA = { 0x0A }; final byte[] bB = { 0x0B }; final byte[] bC = { 0x0C }; final byte[] bD = { 0x0D }; final byte[] bNotexist = { 0x0F }; private static final double EPSILON = 1e-5; public GeoCommandsTest(RedisProtocol protocol) { super(protocol); } @Test public void geoadd() { assertEquals(1, jedis.geoadd("foo", 1, 2, "a")); assertEquals(0, jedis.geoadd("foo", 2, 3, "a")); Map coordinateMap = new HashMap<>(); coordinateMap.put("a", new GeoCoordinate(3, 4)); coordinateMap.put("b", new GeoCoordinate(2, 3)); coordinateMap.put("c", new GeoCoordinate(3.314, 2.3241)); assertEquals(2, jedis.geoadd("foo", coordinateMap)); // binary assertEquals(1, jedis.geoadd(bfoo, 1, 2, bA)); assertEquals(0, jedis.geoadd(bfoo, 2, 3, bA)); Map bcoordinateMap = new HashMap<>(); bcoordinateMap.put(bA, new GeoCoordinate(3, 4)); bcoordinateMap.put(bB, new GeoCoordinate(2, 3)); bcoordinateMap.put(bC, new GeoCoordinate(3.314, 2.3241)); assertEquals(2, jedis.geoadd(bfoo, bcoordinateMap)); } @Test public void geoaddWithParams() { assertEquals(1, jedis.geoadd("foo", 1, 2, "a")); Map coordinateMap = new HashMap<>(); coordinateMap.put("a", new GeoCoordinate(3, 4)); assertEquals(0, jedis.geoadd("foo", GeoAddParams.geoAddParams().nx(), coordinateMap)); assertEquals(1, jedis.geoadd("foo", GeoAddParams.geoAddParams().xx().ch(), coordinateMap)); coordinateMap.clear(); coordinateMap.put("b", new GeoCoordinate(6, 7)); // never add elements. assertEquals(0, jedis.geoadd("foo", GeoAddParams.geoAddParams().xx(), coordinateMap)); assertEquals(1, jedis.geoadd("foo", GeoAddParams.geoAddParams().nx(), coordinateMap)); // binary assertEquals(1, jedis.geoadd(bfoo, 1, 2, bA)); Map bcoordinateMap = new HashMap<>(); bcoordinateMap.put(bA, new GeoCoordinate(3, 4)); assertEquals(0, jedis.geoadd(bfoo, GeoAddParams.geoAddParams().nx(), bcoordinateMap)); assertEquals(1, jedis.geoadd(bfoo, GeoAddParams.geoAddParams().xx().ch(), bcoordinateMap)); bcoordinateMap.clear(); bcoordinateMap.put(bB, new GeoCoordinate(6, 7)); // never add elements. assertEquals(0, jedis.geoadd(bfoo, GeoAddParams.geoAddParams().xx(), bcoordinateMap)); assertEquals(1, jedis.geoadd(bfoo, GeoAddParams.geoAddParams().nx(), bcoordinateMap)); } @Test public void geodist() { prepareGeoData(); Double dist = jedis.geodist("foo", "a", "b"); assertEquals(157149, dist.intValue()); dist = jedis.geodist("foo", "a", "b", GeoUnit.KM); assertEquals(157, dist.intValue()); dist = jedis.geodist("foo", "a", "b", GeoUnit.MI); assertEquals(97, dist.intValue()); dist = jedis.geodist("foo", "a", "b", GeoUnit.FT); assertEquals(515583, dist.intValue()); // binary dist = jedis.geodist(bfoo, bA, bB); assertEquals(157149, dist.intValue()); dist = jedis.geodist(bfoo, bA, bB, GeoUnit.KM); assertEquals(157, dist.intValue()); dist = jedis.geodist(bfoo, bA, bB, GeoUnit.MI); assertEquals(97, dist.intValue()); dist = jedis.geodist(bfoo, bA, bB, GeoUnit.FT); assertEquals(515583, dist.intValue()); } @Test public void geohash() { prepareGeoData(); List hashes = jedis.geohash("foo", "a", "b", "notexist"); assertEquals(3, hashes.size()); assertEquals("s0dnu20t9j0", hashes.get(0)); assertEquals("s093jd0k720", hashes.get(1)); assertNull(hashes.get(2)); // binary List bhashes = jedis.geohash(bfoo, bA, bB, bNotexist); assertEquals(3, bhashes.size()); assertArrayEquals(SafeEncoder.encode("s0dnu20t9j0"), bhashes.get(0)); assertArrayEquals(SafeEncoder.encode("s093jd0k720"), bhashes.get(1)); assertNull(bhashes.get(2)); } @Test public void geopos() { prepareGeoData(); List coordinates = jedis.geopos("foo", "a", "b", "notexist"); assertEquals(3, coordinates.size()); assertEquals(3.0, coordinates.get(0).getLongitude(), EPSILON); assertEquals(4.0, coordinates.get(0).getLatitude(), EPSILON); assertEquals(2.0, coordinates.get(1).getLongitude(), EPSILON); assertEquals(3.0, coordinates.get(1).getLatitude(), EPSILON); assertNull(coordinates.get(2)); List bcoordinates = jedis.geopos(bfoo, bA, bB, bNotexist); assertEquals(3, bcoordinates.size()); assertEquals(3.0, bcoordinates.get(0).getLongitude(), EPSILON); assertEquals(4.0, bcoordinates.get(0).getLatitude(), EPSILON); assertEquals(2.0, bcoordinates.get(1).getLongitude(), EPSILON); assertEquals(3.0, bcoordinates.get(1).getLatitude(), EPSILON); assertNull(bcoordinates.get(2)); } @Test public void georadius() { // prepare datas Map coordinateMap = new HashMap<>(); coordinateMap.put("Palermo", new GeoCoordinate(13.361389, 38.115556)); coordinateMap.put("Catania", new GeoCoordinate(15.087269, 37.502669)); jedis.geoadd("Sicily", coordinateMap); List members = jedis.georadius("Sicily", 15, 37, 200, GeoUnit.KM); assertEquals(2, members.size()); // sort members = jedis.georadius("Sicily", 15, 37, 200, GeoUnit.KM, GeoRadiusParam.geoRadiusParam() .sortDescending()); assertEquals(2, members.size()); assertEquals("Catania", members.get(1).getMemberByString()); assertEquals("Palermo", members.get(0).getMemberByString()); // sort, count 1 members = jedis.georadius("Sicily", 15, 37, 200, GeoUnit.KM, GeoRadiusParam.geoRadiusParam() .sortAscending().count(1)); assertEquals(1, members.size()); // sort, count 1, withdist, withcoord members = jedis.georadius("Sicily", 15, 37, 200, GeoUnit.KM, GeoRadiusParam.geoRadiusParam() .sortAscending().count(1).withCoord().withDist().withHash()); assertEquals(1, members.size()); GeoRadiusResponse response = members.get(0); assertEquals(56.4413, response.getDistance(), EPSILON); assertEquals(15.087269, response.getCoordinate().getLongitude(), EPSILON); assertEquals(37.502669, response.getCoordinate().getLatitude(), EPSILON); assertEquals(3479447370796909L, response.getRawScore()); // sort, count 1, with hash members = jedis.georadius("Sicily", 15, 37, 200, GeoUnit.KM, GeoRadiusParam.geoRadiusParam() .sortAscending().count(1).withHash()); assertEquals(1, members.size()); response = members.get(0); assertEquals(3479447370796909L, response.getRawScore()); // sort, count 1, any members = jedis.georadius("Sicily", 15, 37, 200, GeoUnit.KM, GeoRadiusParam.geoRadiusParam() .sortDescending().count(1, true)); assertEquals(1, members.size()); response = members.get(0); assertTrue(coordinateMap.containsKey(response.getMemberByString())); } @Test @ConditionalOnEnv(value = TestEnvUtil.ENV_REDIS_ENTERPRISE, enabled = false) public void georadiusStore() { // prepare datas Map coordinateMap = new HashMap<>(); coordinateMap.put("Palermo", new GeoCoordinate(13.361389, 38.115556)); coordinateMap.put("Catania", new GeoCoordinate(15.087269, 37.502669)); jedis.geoadd("Sicily", coordinateMap); long size = jedis.georadiusStore("Sicily", 15, 37, 200, GeoUnit.KM, GeoRadiusParam.geoRadiusParam(), GeoRadiusStoreParam.geoRadiusStoreParam().store("SicilyStore")); assertEquals(2, size); List expected = new ArrayList<>(); expected.add("Palermo"); expected.add("Catania"); assertEquals(expected, jedis.zrange("SicilyStore", 0, -1)); } @Test public void georadiusReadonly() { // prepare datas Map coordinateMap = new HashMap<>(); coordinateMap.put("Palermo", new GeoCoordinate(13.361389, 38.115556)); coordinateMap.put("Catania", new GeoCoordinate(15.087269, 37.502669)); jedis.geoadd("Sicily", coordinateMap); List members = jedis.georadiusReadonly("Sicily", 15, 37, 200, GeoUnit.KM); assertEquals(2, members.size()); // sort members = jedis.georadiusReadonly("Sicily", 15, 37, 200, GeoUnit.KM, GeoRadiusParam.geoRadiusParam().sortAscending()); assertEquals(2, members.size()); assertEquals("Catania", members.get(0).getMemberByString()); assertEquals("Palermo", members.get(1).getMemberByString()); // sort, count 1 members = jedis.georadiusReadonly("Sicily", 15, 37, 200, GeoUnit.KM, GeoRadiusParam.geoRadiusParam().sortAscending().count(1)); assertEquals(1, members.size()); // sort, count 1, withdist, withcoord members = jedis.georadiusReadonly("Sicily", 15, 37, 200, GeoUnit.KM, GeoRadiusParam.geoRadiusParam().sortAscending().count(1).withCoord().withDist()); assertEquals(1, members.size()); GeoRadiusResponse response = members.get(0); assertEquals(56.4413, response.getDistance(), EPSILON); assertEquals(15.087269, response.getCoordinate().getLongitude(), EPSILON); assertEquals(37.502669, response.getCoordinate().getLatitude(), EPSILON); } @Test public void georadiusBinary() { // prepare datas Map bcoordinateMap = new HashMap<>(); bcoordinateMap.put(bA, new GeoCoordinate(13.361389, 38.115556)); bcoordinateMap.put(bB, new GeoCoordinate(15.087269, 37.502669)); jedis.geoadd(bfoo, bcoordinateMap); List members = jedis.georadius(bfoo, 15, 37, 200, GeoUnit.KM); assertEquals(2, members.size()); // sort members = jedis.georadius(bfoo, 15, 37, 200, GeoUnit.KM, GeoRadiusParam.geoRadiusParam() .sortAscending()); assertEquals(2, members.size()); assertArrayEquals(bB, members.get(0).getMember()); assertArrayEquals(bA, members.get(1).getMember()); // sort, count 1 members = jedis.georadius(bfoo, 15, 37, 200, GeoUnit.KM, GeoRadiusParam.geoRadiusParam() .sortAscending().count(1)); assertEquals(1, members.size()); // sort, count 1, withdist, withcoord members = jedis.georadius(bfoo, 15, 37, 200, GeoUnit.KM, GeoRadiusParam.geoRadiusParam() .sortAscending().count(1).withCoord().withDist()); assertEquals(1, members.size()); GeoRadiusResponse response = members.get(0); assertEquals(56.4413, response.getDistance(), EPSILON); assertEquals(15.087269, response.getCoordinate().getLongitude(), EPSILON); assertEquals(37.502669, response.getCoordinate().getLatitude(), EPSILON); } @Test @ConditionalOnEnv(value = TestEnvUtil.ENV_REDIS_ENTERPRISE, enabled = false) public void georadiusStoreBinary() { // prepare datas Map bcoordinateMap = new HashMap<>(); bcoordinateMap.put(bA, new GeoCoordinate(13.361389, 38.115556)); bcoordinateMap.put(bB, new GeoCoordinate(15.087269, 37.502669)); jedis.geoadd(bfoo, bcoordinateMap); long size = jedis.georadiusStore(bfoo, 15, 37, 200, GeoUnit.KM, GeoRadiusParam.geoRadiusParam(), GeoRadiusStoreParam.geoRadiusStoreParam().store("SicilyStore")); assertEquals(2, size); List bexpected = new ArrayList<>(); bexpected.add(bA); bexpected.add(bB); assertByteArrayListEquals(bexpected, jedis.zrange("SicilyStore".getBytes(), 0, -1)); } @Test public void georadiusReadonlyBinary() { // prepare datas Map bcoordinateMap = new HashMap<>(); bcoordinateMap.put(bA, new GeoCoordinate(13.361389, 38.115556)); bcoordinateMap.put(bB, new GeoCoordinate(15.087269, 37.502669)); jedis.geoadd(bfoo, bcoordinateMap); List members = jedis.georadiusReadonly(bfoo, 15, 37, 200, GeoUnit.KM); assertEquals(2, members.size()); // sort members = jedis.georadiusReadonly(bfoo, 15, 37, 200, GeoUnit.KM, GeoRadiusParam.geoRadiusParam().sortAscending()); assertEquals(2, members.size()); assertArrayEquals(bB, members.get(0).getMember()); assertArrayEquals(bA, members.get(1).getMember()); // sort, count 1 members = jedis.georadiusReadonly(bfoo, 15, 37, 200, GeoUnit.KM, GeoRadiusParam.geoRadiusParam().sortAscending().count(1)); assertEquals(1, members.size()); // sort, count 1, withdist, withcoord members = jedis.georadiusReadonly(bfoo, 15, 37, 200, GeoUnit.KM, GeoRadiusParam.geoRadiusParam().sortAscending().count(1).withCoord().withDist()); assertEquals(1, members.size()); GeoRadiusResponse response = members.get(0); assertEquals(56.4413, response.getDistance(), EPSILON); assertEquals(15.087269, response.getCoordinate().getLongitude(), EPSILON); assertEquals(37.502669, response.getCoordinate().getLatitude(), EPSILON); } @Test public void georadiusByMember() { jedis.geoadd("Sicily", 13.583333, 37.316667, "Agrigento"); jedis.geoadd("Sicily", 13.361389, 38.115556, "Palermo"); jedis.geoadd("Sicily", 15.087269, 37.502669, "Catania"); List members = jedis.georadiusByMember("Sicily", "Agrigento", 100, GeoUnit.KM); assertEquals(2, members.size()); members = jedis.georadiusByMember("Sicily", "Agrigento", 100, GeoUnit.KM, GeoRadiusParam .geoRadiusParam().sortAscending()); assertEquals(2, members.size()); assertEquals("Agrigento", members.get(0).getMemberByString()); assertEquals("Palermo", members.get(1).getMemberByString()); members = jedis.georadiusByMember("Sicily", "Agrigento", 100, GeoUnit.KM, GeoRadiusParam .geoRadiusParam().sortAscending().count(1).withCoord().withDist()); assertEquals(1, members.size()); GeoRadiusResponse member = members.get(0); assertEquals("Agrigento", member.getMemberByString()); assertEquals(0, member.getDistance(), EPSILON); assertEquals(13.583333, member.getCoordinate().getLongitude(), EPSILON); assertEquals(37.316667, member.getCoordinate().getLatitude(), EPSILON); } @Test @ConditionalOnEnv(value = TestEnvUtil.ENV_REDIS_ENTERPRISE, enabled = false) public void georadiusByMemberStore() { jedis.geoadd("Sicily", 13.583333, 37.316667, "Agrigento"); jedis.geoadd("Sicily", 13.361389, 38.115556, "Palermo"); jedis.geoadd("Sicily", 15.087269, 37.502669, "Catania"); long size = jedis.georadiusByMemberStore("Sicily", "Agrigento", 100, GeoUnit.KM, GeoRadiusParam.geoRadiusParam(), GeoRadiusStoreParam.geoRadiusStoreParam().store("SicilyStore")); assertEquals(2, size); List expected = new ArrayList<>(); expected.add("Agrigento"); expected.add("Palermo"); assertEquals(expected, jedis.zrange("SicilyStore", 0, -1)); } @Test public void georadiusByMemberReadonly() { jedis.geoadd("Sicily", 13.583333, 37.316667, "Agrigento"); jedis.geoadd("Sicily", 13.361389, 38.115556, "Palermo"); jedis.geoadd("Sicily", 15.087269, 37.502669, "Catania"); List members = jedis.georadiusByMemberReadonly("Sicily", "Agrigento", 100, GeoUnit.KM); assertEquals(2, members.size()); members = jedis.georadiusByMemberReadonly("Sicily", "Agrigento", 100, GeoUnit.KM, GeoRadiusParam.geoRadiusParam().sortAscending()); assertEquals(2, members.size()); assertEquals("Agrigento", members.get(0).getMemberByString()); assertEquals("Palermo", members.get(1).getMemberByString()); members = jedis.georadiusByMemberReadonly("Sicily", "Agrigento", 100, GeoUnit.KM, GeoRadiusParam.geoRadiusParam().sortAscending().count(1).withCoord().withDist()); assertEquals(1, members.size()); GeoRadiusResponse member = members.get(0); assertEquals("Agrigento", member.getMemberByString()); assertEquals(0, member.getDistance(), EPSILON); assertEquals(13.583333, member.getCoordinate().getLongitude(), EPSILON); assertEquals(37.316667, member.getCoordinate().getLatitude(), EPSILON); } @Test public void georadiusByMemberBinary() { jedis.geoadd(bfoo, 13.583333, 37.316667, bA); jedis.geoadd(bfoo, 13.361389, 38.115556, bB); jedis.geoadd(bfoo, 15.087269, 37.502669, bC); List members = jedis.georadiusByMember(bfoo, bA, 100, GeoUnit.KM); assertEquals(2, members.size()); members = jedis.georadiusByMember(bfoo, bA, 100, GeoUnit.KM, GeoRadiusParam.geoRadiusParam().sortAscending()); assertEquals(2, members.size()); assertArrayEquals(bA, members.get(0).getMember()); assertArrayEquals(bB, members.get(1).getMember()); members = jedis.georadiusByMember(bfoo, bA, 100, GeoUnit.KM, GeoRadiusParam.geoRadiusParam().sortAscending().count(1).withCoord().withDist()); assertEquals(1, members.size()); GeoRadiusResponse member = members.get(0); assertArrayEquals(bA, member.getMember()); assertEquals(0, member.getDistance(), EPSILON); assertEquals(13.583333, member.getCoordinate().getLongitude(), EPSILON); assertEquals(37.316667, member.getCoordinate().getLatitude(), EPSILON); } @Test @ConditionalOnEnv(value = TestEnvUtil.ENV_REDIS_ENTERPRISE, enabled = false) public void georadiusByMemberStoreBinary() { jedis.geoadd(bfoo, 13.583333, 37.316667, bA); jedis.geoadd(bfoo, 13.361389, 38.115556, bB); jedis.geoadd(bfoo, 15.087269, 37.502669, bC); assertEquals(2, jedis.georadiusByMemberStore(bfoo, bA, 100, GeoUnit.KM, GeoRadiusParam.geoRadiusParam(), GeoRadiusStoreParam.geoRadiusStoreParam().store("SicilyStore"))); List bexpected = new ArrayList<>(); bexpected.add(bA); bexpected.add(bB); assertByteArrayListEquals(bexpected, jedis.zrange("SicilyStore".getBytes(), 0, -1)); } @Test public void georadiusByMemberReadonlyBinary() { jedis.geoadd(bfoo, 13.583333, 37.316667, bA); jedis.geoadd(bfoo, 13.361389, 38.115556, bB); jedis.geoadd(bfoo, 15.087269, 37.502669, bC); List members = jedis.georadiusByMemberReadonly(bfoo, bA, 100, GeoUnit.KM); assertEquals(2, members.size()); members = jedis.georadiusByMemberReadonly(bfoo, bA, 100, GeoUnit.KM, GeoRadiusParam.geoRadiusParam().sortAscending()); assertEquals(2, members.size()); assertArrayEquals(bA, members.get(0).getMember()); assertArrayEquals(bB, members.get(1).getMember()); members = jedis.georadiusByMemberReadonly(bfoo, bA, 100, GeoUnit.KM, GeoRadiusParam.geoRadiusParam().sortAscending().count(1).withCoord().withDist()); assertEquals(1, members.size()); GeoRadiusResponse member = members.get(0); assertArrayEquals(bA, member.getMember()); assertEquals(0, member.getDistance(), EPSILON); assertEquals(13.583333, member.getCoordinate().getLongitude(), EPSILON); assertEquals(37.316667, member.getCoordinate().getLatitude(), EPSILON); } @Test public void geosearch() { jedis.geoadd("barcelona", 2.1909389952632d, 41.433791470673d, "place1"); jedis.geoadd("barcelona", 2.1873744593677d, 41.406342043777d, "place2"); jedis.geoadd("barcelona", 2.583333d, 41.316667d, "place3"); // FROMLONLAT and BYRADIUS List members = jedis.geosearch("barcelona", new GeoCoordinate(2.191d,41.433d), 1000, GeoUnit.M); assertEquals(1, members.size()); assertEquals("place1", members.get(0).getMemberByString()); // using Params members = jedis.geosearch("barcelona", new GeoSearchParam().byRadius(3000, GeoUnit.M) .fromLonLat(2.191d,41.433d).desc()); assertEquals(2, members.size()); assertEquals("place2", members.get(0).getMemberByString()); // FROMMEMBER and BYRADIUS members = jedis.geosearch("barcelona","place3", 100, GeoUnit.KM); assertEquals(3, members.size()); // using Params members = jedis.geosearch("barcelona", new GeoSearchParam().fromMember("place1") .byRadius(100, GeoUnit.KM).withDist().withCoord().withHash().count(2)); assertEquals(2, members.size()); assertEquals("place1", members.get(0).getMemberByString()); GeoRadiusResponse res2 = members.get(1); assertEquals("place2", res2.getMemberByString()); assertEquals(3.0674157, res2.getDistance(), 5); assertEquals(new GeoCoordinate(2.187376320362091, 41.40634178640635), res2.getCoordinate()); // FROMMEMBER and BYBOX members = jedis.geosearch("barcelona","place3", 100, 100, GeoUnit.KM); assertEquals(3, members.size()); // using Params members = jedis.geosearch("barcelona", new GeoSearchParam().fromMember("place3") .byBox(100, 100, GeoUnit.KM).asc().count(1, true)); assertEquals(1, members.size()); // FROMLONLAT and BYBOX members = jedis.geosearch("barcelona", new GeoCoordinate(2.191, 41.433), 1, 1, GeoUnit.KM); assertEquals(1, members.size()); // using Params members = jedis.geosearch("barcelona", new GeoSearchParam().byBox(1,1, GeoUnit.KM) .fromLonLat(2.191, 41.433).withDist().withCoord()); assertEquals(1, members.size()); assertEquals("place1", members.get(0).getMemberByString()); assertEquals(0.0881, members.get(0).getDistance(), 10); assertThat(members.get(0).getCoordinate(), GeoCoordinateMatcher.atCoordinates(2.19093829393386841, 41.43379028184083523)); } @Test public void geosearchNegative() { // combine byradius and bybox try { jedis.geosearch("barcelona", new GeoSearchParam() .byRadius(3000, GeoUnit.M) .byBox(300, 300, GeoUnit.M)); fail(); } catch (IllegalArgumentException ignored) { } // without byradius and without bybox try { jedis.geosearch("barcelona", new GeoSearchParam().fromMember("foobar")); fail(); } catch (IllegalArgumentException ignored) { } // combine frommember and fromlonlat try { jedis.geosearch("barcelona", new GeoSearchParam() .fromMember("foobar") .fromLonLat(10,10)); fail(); } catch (IllegalArgumentException ignored) { } // without frommember and without fromlonlat try { jedis.geosearch("barcelona", new GeoSearchParam().byRadius(10, GeoUnit.MI)); fail(); } catch (IllegalArgumentException ignored) { } } @Test @ConditionalOnEnv(value = TestEnvUtil.ENV_REDIS_ENTERPRISE, enabled = false) public void geosearchstore() { jedis.geoadd("barcelona", 2.1909389952632d, 41.433791470673d, "place1"); jedis.geoadd("barcelona", 2.1873744593677d, 41.406342043777d, "place2"); jedis.geoadd("barcelona", 2.583333d, 41.316667d, "place3"); // FROMLONLAT and BYRADIUS long members = jedis.geosearchStore("tel-aviv", "barcelona", new GeoCoordinate(2.191d,41.433d), 1000, GeoUnit.M); assertEquals(1, members); List expected = new ArrayList<>(); expected.add("place1"); assertEquals(expected, jedis.zrange("tel-aviv", 0, -1)); members = jedis.geosearchStore("tel-aviv","barcelona", new GeoSearchParam() .byRadius(3000, GeoUnit.M) .fromLonLat(new GeoCoordinate(2.191d,41.433d))); assertEquals(2, members); assertEquals(2, members); // FROMMEMBER and BYRADIUS members = jedis.geosearchStore("tel-aviv", "barcelona","place3", 100, GeoUnit.KM); assertEquals(3, members); // FROMMEMBER and BYBOX members = jedis.geosearchStore("tel-aviv","barcelona","place3", 100, 100, GeoUnit.KM); assertEquals(3, members); // FROMLONLAT and BYBOX members = jedis.geosearchStore("tel-aviv","barcelona", new GeoCoordinate(2.191, 41.433), 1, 1, GeoUnit.KM); assertEquals(1, members); } @Test @ConditionalOnEnv(value = TestEnvUtil.ENV_REDIS_ENTERPRISE, enabled = false) public void geosearchstoreWithdist() { jedis.geoadd("barcelona", 2.1909389952632d, 41.433791470673d, "place1"); jedis.geoadd("barcelona", 2.1873744593677d, 41.406342043777d, "place2"); long members = jedis.geosearchStoreStoreDist("tel-aviv","barcelona", new GeoSearchParam().byRadius(3000, GeoUnit.M) .fromLonLat(2.191d,41.433d)); assertEquals(2, members); assertEquals(88.05060698409301, jedis.zscore("tel-aviv", "place1"), 5); } private void prepareGeoData() { Map coordinateMap = new HashMap<>(); coordinateMap.put("a", new GeoCoordinate(3, 4)); coordinateMap.put("b", new GeoCoordinate(2, 3)); coordinateMap.put("c", new GeoCoordinate(3.314, 2.3241)); assertEquals(3, jedis.geoadd("foo", coordinateMap)); Map bcoordinateMap = new HashMap<>(); bcoordinateMap.put(bA, new GeoCoordinate(3, 4)); bcoordinateMap.put(bB, new GeoCoordinate(2, 3)); bcoordinateMap.put(bC, new GeoCoordinate(3.314, 2.3241)); assertEquals(3, jedis.geoadd(bfoo, bcoordinateMap)); } } ================================================ FILE: src/test/java/redis/clients/jedis/commands/jedis/HashesCommandsTest.java ================================================ package redis.clients.jedis.commands.jedis; import static java.util.Arrays.asList; import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.both; import static org.hamcrest.Matchers.contains; import static org.hamcrest.Matchers.containsInAnyOrder; import static org.hamcrest.Matchers.equalTo; import static org.hamcrest.Matchers.greaterThan; import static org.hamcrest.Matchers.greaterThanOrEqualTo; import static org.hamcrest.Matchers.lessThanOrEqualTo; import static org.junit.jupiter.api.Assertions.assertArrayEquals; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertNull; import static org.junit.jupiter.api.Assertions.assertTrue; import static redis.clients.jedis.params.ScanParams.SCAN_POINTER_START; import static redis.clients.jedis.params.ScanParams.SCAN_POINTER_START_BINARY; import static redis.clients.jedis.util.AssertUtil.assertByteArrayListEquals; import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; import java.util.HashMap; import java.util.LinkedHashMap; import java.util.LinkedHashSet; import java.util.List; import java.util.Map; import java.util.Set; import java.util.stream.Collectors; import io.redis.test.annotations.SinceRedisVersion; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Tag; import org.junit.jupiter.api.extension.RegisterExtension; import org.junit.jupiter.params.ParameterizedClass; import org.junit.jupiter.params.provider.MethodSource; import redis.clients.jedis.Pipeline; import redis.clients.jedis.RedisProtocol; import redis.clients.jedis.Response; import redis.clients.jedis.args.ExpiryOption; import redis.clients.jedis.params.HGetExParams; import redis.clients.jedis.params.HSetExParams; import redis.clients.jedis.params.ScanParams; import redis.clients.jedis.resps.ScanResult; import redis.clients.jedis.util.AssertUtil; import redis.clients.jedis.util.EnabledOnCommandCondition; import redis.clients.jedis.util.JedisByteHashMap; import redis.clients.jedis.util.RedisVersionCondition; @ParameterizedClass @MethodSource("redis.clients.jedis.commands.CommandsTestsParameters#respVersions") @Tag("integration") public class HashesCommandsTest extends JedisCommandsTestBase { @RegisterExtension public RedisVersionCondition versionCondition = new RedisVersionCondition(() -> endpoint); @RegisterExtension public EnabledOnCommandCondition enabledOnCommandCondition = new EnabledOnCommandCondition(() -> endpoint); final byte[] bfoo = { 0x01, 0x02, 0x03, 0x04 }; final byte[] bbar = { 0x05, 0x06, 0x07, 0x08 }; final byte[] bcar = { 0x09, 0x0A, 0x0B, 0x0C }; final byte[] bbare = { 0x05, 0x06, 0x07, 0x08, 0x09 }; final byte[] bcare = { 0x09, 0x0A, 0x0B, 0x0C, 0x0D }; final byte[] bbar1 = { 0x05, 0x06, 0x07, 0x08, 0x0A }; final byte[] bbar2 = { 0x05, 0x06, 0x07, 0x08, 0x0B }; final byte[] bbar3 = { 0x05, 0x06, 0x07, 0x08, 0x0C }; final byte[] bbarstar = { 0x05, 0x06, 0x07, 0x08, '*' }; public HashesCommandsTest(RedisProtocol protocol) { super(protocol); } @Test public void hset() { assertEquals(1, jedis.hset("foo", "bar", "car")); assertEquals(0, jedis.hset("foo", "bar", "foo")); // Binary assertEquals(1, jedis.hset(bfoo, bbar, bcar)); assertEquals(0, jedis.hset(bfoo, bbar, bfoo)); } @Test public void hget() { jedis.hset("foo", "bar", "car"); assertNull(jedis.hget("bar", "foo")); assertNull(jedis.hget("foo", "car")); assertEquals("car", jedis.hget("foo", "bar")); // Binary jedis.hset(bfoo, bbar, bcar); assertNull(jedis.hget(bbar, bfoo)); assertNull(jedis.hget(bfoo, bcar)); assertArrayEquals(bcar, jedis.hget(bfoo, bbar)); } @Test @SinceRedisVersion("7.9.0") public void hgetex() { long seconds = 20; jedis.hset("foo", "bar", "car"); assertEquals(asList("car"), jedis.hgetex("foo", HGetExParams.hGetExParams().ex(seconds), "bar")); assertThat(jedis.httl("foo", "bar").get(0), greaterThanOrEqualTo(seconds - 1)); jedis.hset("foo", "bar", "car"); assertEquals(asList("car"), jedis.hgetex("foo", HGetExParams.hGetExParams().persist(), "bar")); assertEquals(jedis.httl("foo", "bar").get(0), Long.valueOf(-1)); jedis.hset("foo", "bar", "car"); jedis.hset("foo", "bare", "care"); assertEquals(asList("car", "care"), jedis.hgetex("foo", HGetExParams.hGetExParams().ex(seconds), "bar", "bare")); assertThat(jedis.httl("foo", "bar").get(0), greaterThanOrEqualTo(seconds - 1)); assertThat(jedis.httl("foo", "bare").get(0), greaterThanOrEqualTo(seconds - 1)); // Binary jedis.hset(bfoo, bbar, bcar); assertByteArrayListEquals(asList(bcar), jedis.hgetex(bfoo, HGetExParams.hGetExParams().ex(seconds), bbar)); assertThat(jedis.httl(bfoo, bbar).get(0), greaterThanOrEqualTo(seconds - 1)); jedis.hset(bfoo, bbar, bcar); assertByteArrayListEquals(asList(bcar), jedis.hgetex(bfoo, HGetExParams.hGetExParams().persist(), bbar)); assertEquals(Long.valueOf(-1), jedis.httl(bfoo, bbar).get(0)); jedis.hset(bfoo, bbar, bcar); jedis.hset(bfoo, bbare, bcare); assertByteArrayListEquals(asList(bcar, bcare), jedis.hgetex(bfoo, HGetExParams.hGetExParams().ex(seconds), bbar, bbare)); assertThat(jedis.httl(bfoo, bbar).get(0), greaterThanOrEqualTo(seconds - 1)); assertThat(jedis.httl(bfoo, bbare).get(0), greaterThanOrEqualTo(seconds - 1)); } @Test @SinceRedisVersion("7.9.0") public void hgetdel() { jedis.hset("foo", "bar", "car"); assertEquals(asList("car"), jedis.hgetdel("foo", "bar")); assertNull(jedis.hget("foo", "bar")); jedis.hset("foo", "bar", "car"); jedis.hset("foo", "bare", "care"); assertEquals(asList("car", "care"), jedis.hgetdel("foo", "bar", "bare")); assertNull(jedis.hget("foo", "bar")); assertNull(jedis.hget("foo", "bare")); // Binary jedis.hset(bfoo, bbar, bcar); assertByteArrayListEquals(asList(bcar), jedis.hgetdel(bfoo, bbar)); assertNull(jedis.hget(bfoo, bbar)); jedis.hset(bfoo, bbar, bcar); jedis.hset(bfoo, bbare, bcare); assertByteArrayListEquals(asList(bcar, bcare), jedis.hgetdel(bfoo, bbar, bbare)); assertNull(jedis.hget(bfoo, bbar)); assertNull(jedis.hget(bfoo, bbare)); } @Test @SinceRedisVersion("7.9.0") public void hsetex() { long seconds = 20; jedis.del("foo"); assertEquals(1, jedis.hsetex("foo", HSetExParams.hSetExParams().ex(seconds).fnx(), "bar", "car")); assertThat(jedis.httl("foo", "bar").get(0), greaterThanOrEqualTo(seconds - 1)); assertEquals(1, jedis.hsetex("foo", HSetExParams.hSetExParams().keepTtl(), "bar", "car")); assertThat(jedis.httl("foo", "bar").get(0), greaterThanOrEqualTo(seconds - 1)); assertEquals(1, jedis.hsetex("foo", HSetExParams.hSetExParams().fxx(), "bar", "car")); assertEquals(Long.valueOf(-1), jedis.httl("foo", "bar").get(0)); HashMap hash = new HashMap(); hash.put("bar", "car"); hash.put("bare", "care"); jedis.del("foo"); assertEquals(1, jedis.hsetex("foo", HSetExParams.hSetExParams().ex(seconds).fnx(), hash)); assertThat(jedis.httl("foo", "bar").get(0), greaterThanOrEqualTo(seconds - 1)); assertThat(jedis.httl("foo", "bare").get(0), greaterThanOrEqualTo(seconds - 1)); assertEquals(1, jedis.hsetex("foo", HSetExParams.hSetExParams().keepTtl(), hash)); assertThat(jedis.httl("foo", "bar").get(0), greaterThanOrEqualTo(seconds - 1)); assertThat(jedis.httl("foo", "bare").get(0), greaterThanOrEqualTo(seconds - 1)); assertEquals(1, jedis.hsetex("foo", HSetExParams.hSetExParams().fxx(), hash)); assertEquals(Long.valueOf(-1), jedis.httl("foo", "bar").get(0)); assertEquals(Long.valueOf(-1), jedis.httl("foo", "bare").get(0)); // Binary jedis.del(bfoo); assertEquals(1, jedis.hsetex(bfoo, HSetExParams.hSetExParams().ex(seconds).fnx(), bbar, bcar)); assertThat(jedis.httl(bfoo, bbar).get(0), greaterThanOrEqualTo(seconds - 1)); assertEquals(1, jedis.hsetex(bfoo, HSetExParams.hSetExParams().keepTtl(), bbar, bcar)); assertThat(jedis.httl(bfoo, bbar).get(0), greaterThanOrEqualTo(seconds - 1)); assertEquals(1, jedis.hsetex(bfoo, HSetExParams.hSetExParams().fxx(), bbar, bcar)); assertEquals(Long.valueOf(-1), jedis.httl(bfoo, bbar).get(0)); HashMap bhash = new HashMap(); bhash.put(bbar, bcar); bhash.put(bbare, bcare); jedis.del(bfoo); assertEquals(1, jedis.hsetex(bfoo, HSetExParams.hSetExParams().ex(seconds).fnx(), bhash)); assertThat(jedis.httl(bfoo, bbar).get(0), greaterThanOrEqualTo(seconds - 1)); assertThat(jedis.httl(bfoo, bbare).get(0), greaterThanOrEqualTo(seconds - 1)); assertEquals(1, jedis.hsetex(bfoo, HSetExParams.hSetExParams().keepTtl(), bhash)); assertThat(jedis.httl(bfoo, bbar).get(0), greaterThanOrEqualTo(seconds - 1)); assertThat(jedis.httl(bfoo, bbare).get(0), greaterThanOrEqualTo(seconds - 1)); assertEquals(1, jedis.hsetex(bfoo, HSetExParams.hSetExParams().fxx(), bhash)); assertEquals(Long.valueOf(-1), jedis.httl(bfoo, bbar).get(0)); assertEquals(Long.valueOf(-1), jedis.httl(bfoo, bbare).get(0)); } @Test public void hsetnx() { assertEquals(1, jedis.hsetnx("foo", "bar", "car")); assertEquals("car", jedis.hget("foo", "bar")); assertEquals(0, jedis.hsetnx("foo", "bar", "foo")); assertEquals("car", jedis.hget("foo", "bar")); assertEquals(1, jedis.hsetnx("foo", "car", "bar")); assertEquals("bar", jedis.hget("foo", "car")); // Binary assertEquals(1, jedis.hsetnx(bfoo, bbar, bcar)); assertArrayEquals(bcar, jedis.hget(bfoo, bbar)); assertEquals(0, jedis.hsetnx(bfoo, bbar, bfoo)); assertArrayEquals(bcar, jedis.hget(bfoo, bbar)); assertEquals(1, jedis.hsetnx(bfoo, bcar, bbar)); assertArrayEquals(bbar, jedis.hget(bfoo, bcar)); } @Test public void hmset() { Map hash = new HashMap(); hash.put("bar", "car"); hash.put("car", "bar"); assertEquals("OK", jedis.hmset("foo", hash)); assertEquals("car", jedis.hget("foo", "bar")); assertEquals("bar", jedis.hget("foo", "car")); // Binary Map bhash = new HashMap(); bhash.put(bbar, bcar); bhash.put(bcar, bbar); assertEquals("OK", jedis.hmset(bfoo, bhash)); assertArrayEquals(bcar, jedis.hget(bfoo, bbar)); assertArrayEquals(bbar, jedis.hget(bfoo, bcar)); } @Test public void hsetVariadic() { Map hash = new HashMap(); hash.put("bar", "car"); hash.put("car", "bar"); assertEquals(2, jedis.hset("foo", hash)); assertEquals("car", jedis.hget("foo", "bar")); assertEquals("bar", jedis.hget("foo", "car")); // Binary Map bhash = new HashMap(); bhash.put(bbar, bcar); bhash.put(bcar, bbar); assertEquals(2, jedis.hset(bfoo, bhash)); assertArrayEquals(bcar, jedis.hget(bfoo, bbar)); assertArrayEquals(bbar, jedis.hget(bfoo, bcar)); } @Test public void hmget() { Map hash = new HashMap(); hash.put("bar", "car"); hash.put("car", "bar"); jedis.hmset("foo", hash); List values = jedis.hmget("foo", "bar", "car", "foo"); List expected = new ArrayList(); expected.add("car"); expected.add("bar"); expected.add(null); assertEquals(expected, values); // Binary Map bhash = new HashMap(); bhash.put(bbar, bcar); bhash.put(bcar, bbar); jedis.hmset(bfoo, bhash); List bvalues = jedis.hmget(bfoo, bbar, bcar, bfoo); List bexpected = new ArrayList(); bexpected.add(bcar); bexpected.add(bbar); bexpected.add(null); AssertUtil.assertByteArrayListEquals(bexpected, bvalues); } @Test public void hincrBy() { assertEquals(1, jedis.hincrBy("foo", "bar", 1)); assertEquals(0, jedis.hincrBy("foo", "bar", -1)); assertEquals(-10, jedis.hincrBy("foo", "bar", -10)); // Binary assertEquals(1, jedis.hincrBy(bfoo, bbar, 1)); assertEquals(0, jedis.hincrBy(bfoo, bbar, -1)); assertEquals(-10, jedis.hincrBy(bfoo, bbar, -10)); } @Test public void hincrByFloat() { assertEquals(1.5d, jedis.hincrByFloat("foo", "bar", 1.5d), 0); assertEquals(0d, jedis.hincrByFloat("foo", "bar", -1.5d), 0); assertEquals(-10.7d, jedis.hincrByFloat("foo", "bar", -10.7d), 0); // Binary assertEquals(1.5d, jedis.hincrByFloat(bfoo, bbar, 1.5d), 0d); assertEquals(0d, jedis.hincrByFloat(bfoo, bbar, -1.5d), 0d); assertEquals(-10.7d, jedis.hincrByFloat(bfoo, bbar, -10.7d), 0d); } @Test public void hexists() { Map hash = new HashMap(); hash.put("bar", "car"); hash.put("car", "bar"); jedis.hmset("foo", hash); assertFalse(jedis.hexists("bar", "foo")); assertFalse(jedis.hexists("foo", "foo")); assertTrue(jedis.hexists("foo", "bar")); // Binary Map bhash = new HashMap(); bhash.put(bbar, bcar); bhash.put(bcar, bbar); jedis.hmset(bfoo, bhash); assertFalse(jedis.hexists(bbar, bfoo)); assertFalse(jedis.hexists(bfoo, bfoo)); assertTrue(jedis.hexists(bfoo, bbar)); } @Test public void hdel() { Map hash = new HashMap<>(); hash.put("bar", "car"); hash.put("car", "bar"); jedis.hmset("foo", hash); assertEquals(0, jedis.hdel("bar", "foo")); assertEquals(0, jedis.hdel("foo", "foo")); assertEquals(1, jedis.hdel("foo", "bar")); assertNull(jedis.hget("foo", "bar")); // Binary Map bhash = new HashMap<>(); bhash.put(bbar, bcar); bhash.put(bcar, bbar); jedis.hmset(bfoo, bhash); assertEquals(0, jedis.hdel(bbar, bfoo)); assertEquals(0, jedis.hdel(bfoo, bfoo)); assertEquals(1, jedis.hdel(bfoo, bbar)); assertNull(jedis.hget(bfoo, bbar)); // Deleting multiple fields returns the right value. jedis.hmset("foo", hash); assertEquals(2, jedis.hdel("foo", "bar", "car", "dne")); jedis.hmset(bfoo, bhash); assertEquals(2, jedis.hdel(bfoo, bbar, bcar, bbar1)); } @Test public void hlen() { Map hash = new HashMap(); hash.put("bar", "car"); hash.put("car", "bar"); jedis.hmset("foo", hash); assertEquals(0, jedis.hlen("bar")); assertEquals(2, jedis.hlen("foo")); // Binary Map bhash = new HashMap(); bhash.put(bbar, bcar); bhash.put(bcar, bbar); jedis.hmset(bfoo, bhash); assertEquals(0, jedis.hlen(bbar)); assertEquals(2, jedis.hlen(bfoo)); } @Test public void hkeys() { Map hash = new LinkedHashMap(); hash.put("bar", "car"); hash.put("car", "bar"); jedis.hmset("foo", hash); Set keys = jedis.hkeys("foo"); Set expected = new LinkedHashSet(); expected.add("bar"); expected.add("car"); assertEquals(expected, keys); // Binary Map bhash = new LinkedHashMap(); bhash.put(bbar, bcar); bhash.put(bcar, bbar); jedis.hmset(bfoo, bhash); Set bkeys = jedis.hkeys(bfoo); Set bexpected = new LinkedHashSet(); bexpected.add(bbar); bexpected.add(bcar); AssertUtil.assertByteArraySetEquals(bexpected, bkeys); } @Test public void hvals() { Map hash = new LinkedHashMap(); hash.put("bar", "car"); hash.put("car", "bar"); jedis.hmset("foo", hash); List vals = jedis.hvals("foo"); assertEquals(2, vals.size()); AssertUtil.assertCollectionContains(vals, "bar"); AssertUtil.assertCollectionContains(vals, "car"); // Binary Map bhash = new LinkedHashMap(); bhash.put(bbar, bcar); bhash.put(bcar, bbar); jedis.hmset(bfoo, bhash); List bvals = jedis.hvals(bfoo); assertEquals(2, bvals.size()); AssertUtil.assertByteArrayCollectionContains(bvals, bbar); AssertUtil.assertByteArrayCollectionContains(bvals, bcar); } @Test public void hgetAll() { Map h = new HashMap(); h.put("bar", "car"); h.put("car", "bar"); jedis.hmset("foo", h); Map hash = jedis.hgetAll("foo"); assertEquals(2, hash.size()); assertEquals("car", hash.get("bar")); assertEquals("bar", hash.get("car")); // Binary Map bh = new HashMap(); bh.put(bbar, bcar); bh.put(bcar, bbar); jedis.hmset(bfoo, bh); Map bhash = jedis.hgetAll(bfoo); assertEquals(2, bhash.size()); assertArrayEquals(bcar, bhash.get(bbar)); assertArrayEquals(bbar, bhash.get(bcar)); } @Test public void hgetAllPipeline() { Map bh = new HashMap(); bh.put(bbar, bcar); bh.put(bcar, bbar); jedis.hmset(bfoo, bh); Pipeline pipeline = jedis.pipelined(); Response> bhashResponse = pipeline.hgetAll(bfoo); pipeline.sync(); Map bhash = bhashResponse.get(); assertEquals(2, bhash.size()); assertArrayEquals(bcar, bhash.get(bbar)); assertArrayEquals(bbar, bhash.get(bcar)); } @Test public void hscan() { jedis.hset("foo", "b", "y"); jedis.hset("foo", "a", "x"); ScanResult> result = jedis.hscan("foo", SCAN_POINTER_START); assertEquals(SCAN_POINTER_START, result.getCursor()); assertEquals(2, result.getResult().size()); assertThat( result.getResult().stream().map(Map.Entry::getKey).collect(Collectors.toList()), containsInAnyOrder("a", "b")); assertThat( result.getResult().stream().map(Map.Entry::getValue).collect(Collectors.toList()), containsInAnyOrder("x", "y")); // binary jedis.hset(bfoo, bbar, bcar); ScanResult> bResult = jedis.hscan(bfoo, SCAN_POINTER_START_BINARY); assertArrayEquals(SCAN_POINTER_START_BINARY, bResult.getCursorAsBytes()); assertEquals(1, bResult.getResult().size()); assertThat( bResult.getResult().stream().map(Map.Entry::getKey).collect(Collectors.toList()), containsInAnyOrder(bbar)); assertThat( bResult.getResult().stream().map(Map.Entry::getValue).collect(Collectors.toList()), containsInAnyOrder(bcar)); } @Test public void hscanMatch() { ScanParams params = new ScanParams(); params.match("a*"); jedis.hset("foo", "b", "y"); jedis.hset("foo", "a", "x"); jedis.hset("foo", "aa", "xx"); ScanResult> result = jedis.hscan("foo", SCAN_POINTER_START, params); assertEquals(SCAN_POINTER_START, result.getCursor()); assertEquals(2, result.getResult().size()); assertThat( result.getResult().stream().map(Map.Entry::getKey).collect(Collectors.toList()), containsInAnyOrder("a", "aa")); assertThat( result.getResult().stream().map(Map.Entry::getValue).collect(Collectors.toList()), containsInAnyOrder("x", "xx")); // binary params = new ScanParams(); params.match(bbarstar); jedis.hset(bfoo, bbar, bcar); jedis.hset(bfoo, bbar1, bcar); jedis.hset(bfoo, bbar2, bcar); jedis.hset(bfoo, bbar3, bcar); ScanResult> bResult = jedis.hscan(bfoo, SCAN_POINTER_START_BINARY, params); assertArrayEquals(SCAN_POINTER_START_BINARY, bResult.getCursorAsBytes()); assertEquals(4, bResult.getResult().size()); assertThat( bResult.getResult().stream().map(Map.Entry::getKey).collect(Collectors.toList()), containsInAnyOrder(bbar, bbar1, bbar2, bbar3)); assertThat( bResult.getResult().stream().map(Map.Entry::getValue).collect(Collectors.toList()), containsInAnyOrder(bcar, bcar, bcar, bcar)); } @Test public void hscanCount() { ScanParams params = new ScanParams(); params.count(2); for (int i = 0; i < 10; i++) { jedis.hset("foo", "a" + i, "x" + i); } ScanResult> result = jedis.hscan("foo", SCAN_POINTER_START, params); assertFalse(result.getResult().isEmpty()); assertThat( result.getResult().stream().map(Map.Entry::getKey).map(s -> s.substring(0, 1)).collect(Collectors.toSet()), containsInAnyOrder("a")); assertThat( result.getResult().stream().map(Map.Entry::getValue).map(s -> s.substring(0, 1)).collect(Collectors.toSet()), containsInAnyOrder("x")); // binary params = new ScanParams(); params.count(2); jedis.hset(bfoo, bbar, bcar); jedis.hset(bfoo, bbar1, bcar); jedis.hset(bfoo, bbar2, bcar); jedis.hset(bfoo, bbar3, bcar); ScanResult> bResult = jedis.hscan(bfoo, SCAN_POINTER_START_BINARY, params); assertFalse(bResult.getResult().isEmpty()); assertThat( bResult.getResult().stream().map(Map.Entry::getKey) .map(a -> Arrays.copyOfRange(a, 0, 4)).map(Arrays::toString).collect(Collectors.toSet()), containsInAnyOrder(Arrays.toString(bbar))); assertThat( bResult.getResult().stream().map(Map.Entry::getValue) .map(a -> Arrays.copyOfRange(a, 0, 4)).map(Arrays::toString).collect(Collectors.toSet()), containsInAnyOrder(Arrays.toString(bcar))); } @Test @SinceRedisVersion("7.4.0") public void hscanNoValues() { jedis.hset("foo", "b", "y"); jedis.hset("foo", "a", "x"); ScanResult result = jedis.hscanNoValues("foo", SCAN_POINTER_START); assertEquals(SCAN_POINTER_START, result.getCursor()); assertEquals(2, result.getResult().size()); assertThat(result.getResult(), containsInAnyOrder("a", "b")); // binary jedis.hset(bfoo, bbar, bcar); ScanResult bResult = jedis.hscanNoValues(bfoo, SCAN_POINTER_START_BINARY); assertArrayEquals(SCAN_POINTER_START_BINARY, bResult.getCursorAsBytes()); assertEquals(1, bResult.getResult().size()); assertThat(bResult.getResult(), containsInAnyOrder(bbar)); } @Test @SinceRedisVersion(value = "7.4.0", message = "NOVALUES flag (since Redis 7.4)") public void hscanNoValuesMatch() { ScanParams params = new ScanParams(); params.match("a*"); jedis.hset("foo", "b", "y"); jedis.hset("foo", "a", "x"); jedis.hset("foo", "aa", "xx"); ScanResult result = jedis.hscanNoValues("foo", SCAN_POINTER_START, params); assertEquals(SCAN_POINTER_START, result.getCursor()); assertEquals(2, result.getResult().size()); assertThat(result.getResult(), containsInAnyOrder("a", "aa")); // binary params = new ScanParams(); params.match(bbarstar); jedis.hset(bfoo, bbar, bcar); jedis.hset(bfoo, bbar1, bcar); jedis.hset(bfoo, bbar2, bcar); jedis.hset(bfoo, bbar3, bcar); ScanResult bResult = jedis.hscanNoValues(bfoo, SCAN_POINTER_START_BINARY, params); assertArrayEquals(SCAN_POINTER_START_BINARY, bResult.getCursorAsBytes()); assertEquals(4, bResult.getResult().size()); assertThat(bResult.getResult(), containsInAnyOrder(bbar, bbar1, bbar2, bbar3)); } @Test @SinceRedisVersion(value = "7.4.0", message = "NOVALUES flag (since Redis 7.4)") public void hscanNoValuesCount() { ScanParams params = new ScanParams(); params.count(2); for (int i = 0; i < 10; i++) { jedis.hset("foo", "a" + i, "a" + i); } ScanResult result = jedis.hscanNoValues("foo", SCAN_POINTER_START, params); assertFalse(result.getResult().isEmpty()); assertThat( result.getResult().stream().map(s -> s.substring(0, 1)).collect(Collectors.toSet()), containsInAnyOrder("a")); // binary params = new ScanParams(); params.count(2); jedis.hset(bfoo, bbar, bcar); jedis.hset(bfoo, bbar1, bcar); jedis.hset(bfoo, bbar2, bcar); jedis.hset(bfoo, bbar3, bcar); ScanResult bResult = jedis.hscanNoValues(bfoo, SCAN_POINTER_START_BINARY, params); assertFalse(bResult.getResult().isEmpty()); assertThat( bResult.getResult().stream() .map(a -> Arrays.copyOfRange(a, 0, 4)).map(Arrays::toString).collect(Collectors.toSet()), containsInAnyOrder(Arrays.toString(bbar))); } @Test public void testHstrLen_EmptyHash() { long response = jedis.hstrlen("myhash", "k1"); assertEquals(0L, response); } @Test public void testHstrLen() { Map values = new HashMap<>(); values.put("key", "value"); jedis.hmset("myhash", values); long response = jedis.hstrlen("myhash", "key"); assertEquals(5L, response); } @Test public void testBinaryHstrLen() { Map values = new HashMap<>(); values.put(bbar, bcar); jedis.hmset(bfoo, values); long response = jedis.hstrlen(bfoo, bbar); assertEquals(4L, response); } @Test public void hrandfield() { assertNull(jedis.hrandfield("foo")); assertEquals(Collections.emptyList(), jedis.hrandfield("foo", 1)); assertEquals(Collections.emptyList(), jedis.hrandfieldWithValues("foo", 1)); assertEquals(Collections.emptyList(), jedis.hrandfieldWithValues("foo", -1)); Map hash = new LinkedHashMap<>(); hash.put("bar", "bar"); hash.put("car", "car"); hash.put("bar1", "bar1"); jedis.hset("foo", hash); assertTrue(hash.containsKey(jedis.hrandfield("foo"))); assertEquals(2, jedis.hrandfield("foo", 2).size()); List> actual = jedis.hrandfieldWithValues("foo", 2); assertEquals(2, actual.size()); actual.forEach(e -> assertEquals(hash.get(e.getKey()), e.getValue())); actual = jedis.hrandfieldWithValues("foo", 5); assertEquals(3, actual.size()); actual.forEach(e -> assertEquals(hash.get(e.getKey()), e.getValue())); actual = jedis.hrandfieldWithValues("foo", -5); assertEquals(5, actual.size()); actual.forEach(e -> assertEquals(hash.get(e.getKey()), e.getValue())); // binary assertNull(jedis.hrandfield(bfoo)); assertEquals(Collections.emptyList(), jedis.hrandfield(bfoo, 1)); assertEquals(Collections.emptyList(), jedis.hrandfieldWithValues(bfoo, 1)); assertEquals(Collections.emptyList(), jedis.hrandfieldWithValues(bfoo, -1)); Map bhash = new JedisByteHashMap(); bhash.put(bbar, bbar); bhash.put(bcar, bcar); bhash.put(bbar1, bbar1); jedis.hset(bfoo, bhash); assertTrue(bhash.containsKey(jedis.hrandfield(bfoo))); assertEquals(2, jedis.hrandfield(bfoo, 2).size()); List> bactual = jedis.hrandfieldWithValues(bfoo, 2); assertEquals(2, bactual.size()); bactual.forEach(e -> assertArrayEquals(bhash.get(e.getKey()), e.getValue())); bactual = jedis.hrandfieldWithValues(bfoo, 5); assertEquals(3, bactual.size()); bactual.forEach(e -> assertArrayEquals(bhash.get(e.getKey()), e.getValue())); bactual = jedis.hrandfieldWithValues(bfoo, -5); assertEquals(5, bactual.size()); bactual.forEach(e -> assertArrayEquals(bhash.get(e.getKey()), e.getValue())); } @Test @SinceRedisVersion("7.4.0") public void hexpireAndHttl() { long seconds1 = 20; long seconds2 = 10; jedis.hset("foo", "bar", "car"); jedis.hset("foo", "bare", "care"); assertEquals(asList(1L, -2L), jedis.hexpire("foo", seconds1, "bar", "bared")); jedis.hset("foo", "bared", "cared"); assertEquals(asList(0L, 1L), jedis.hexpire("foo", seconds2, ExpiryOption.NX, "bar", "bared")); assertThat(jedis.httl("foo", "bar", "bare", "bared"), contains(greaterThanOrEqualTo(seconds1 - 1), equalTo(-1L), both(lessThanOrEqualTo(seconds2)).and(greaterThanOrEqualTo(seconds2 - 1)))); } @Test @SinceRedisVersion("7.4.0") public void hexpireAndHttlBinary() { long seconds1 = 20; long seconds2 = 10; jedis.hset(bfoo, bbar1, bcar); jedis.hset(bfoo, bbar2, bcar); assertEquals(asList(1L, -2L), jedis.hexpire(bfoo, seconds1, bbar1, bbar3)); jedis.hset(bfoo, bbar3, bcar); assertEquals(asList(0L, 1L), jedis.hexpire(bfoo, seconds2, ExpiryOption.NX, bbar1, bbar3)); assertThat(jedis.httl(bfoo, bbar1, bbar2, bbar3), contains(greaterThanOrEqualTo(seconds1 - 1), equalTo(-1L), both(lessThanOrEqualTo(seconds2)).and(greaterThanOrEqualTo(seconds2 - 1)))); } @Test @SinceRedisVersion("7.4.0") public void hpexpireAndHpttl() { long millis1 = 20_000; long millis2 = 10_000; jedis.hset("foo", "bar", "car"); assertEquals(asList(1L, -2L), jedis.hpexpire("foo", millis1, "bar", "bared")); jedis.hset("foo", "bared", "cared"); assertEquals(asList(1L, 0L), jedis.hpexpire("foo", millis2, ExpiryOption.XX, "bar", "bared")); assertThat(jedis.hpttl("foo", "bar", "bare", "bared"), contains(both(lessThanOrEqualTo(millis2)).and(greaterThan(millis2 - 1000)), equalTo(-2L), equalTo(-1L))); } @Test @SinceRedisVersion("7.4.0") public void hpexpireAndHpttlBinary() { long millis1 = 20_000; long millis2 = 10_000; jedis.hset(bfoo, bbar1, bcar); assertEquals(asList(1L, -2L), jedis.hpexpire(bfoo, millis1, bbar1, bbar3)); jedis.hset(bfoo, bbar3, bcar); assertEquals(asList(1L, 0L), jedis.hpexpire(bfoo, millis2, ExpiryOption.XX, bbar1, bbar3)); assertThat(jedis.hpttl(bfoo, bbar1, bbar2, bbar3), contains(both(lessThanOrEqualTo(millis2)).and(greaterThan(millis2 - 1000)), equalTo(-2L), equalTo(-1L))); } @Test @SinceRedisVersion("7.4.0") public void hexpireAtAndExpireTime() { long currSeconds = System.currentTimeMillis() / 1000; long seconds1 = currSeconds + 20; long seconds2 = currSeconds + 10; jedis.hset("foo", "bar", "car"); jedis.hset("foo", "bare", "care"); assertEquals(asList(1L, -2L), jedis.hexpireAt("foo", seconds1, "bar", "bared")); jedis.hset("foo", "bared", "cared"); assertEquals(asList(1L, 1L), jedis.hexpireAt("foo", seconds2, ExpiryOption.LT, "bar", "bared")); assertThat(jedis.hexpireTime("foo", "bar", "bare", "bared"), contains(both(lessThanOrEqualTo(seconds2)).and(greaterThanOrEqualTo(seconds2 - 1)), equalTo(-1L), both(lessThanOrEqualTo(seconds2)).and(greaterThanOrEqualTo(seconds2 - 1)))); } @Test @SinceRedisVersion("7.4.0") public void hexpireAtAndExpireTimeBinary() { long currSeconds = System.currentTimeMillis() / 1000; long seconds1 = currSeconds + 20; long seconds2 = currSeconds + 10; jedis.hset(bfoo, bbar1, bcar); jedis.hset(bfoo, bbar2, bcar); assertEquals(asList(1L, -2L), jedis.hexpireAt(bfoo, seconds1, bbar1, bbar3)); jedis.hset(bfoo, bbar3, bcar); assertEquals(asList(1L, 1L), jedis.hexpireAt(bfoo, seconds2, ExpiryOption.LT, bbar1, bbar3)); assertThat(jedis.hexpireTime(bfoo, bbar1, bbar2, bbar3), contains(both(lessThanOrEqualTo(seconds2)).and(greaterThanOrEqualTo(seconds2 - 1)), equalTo(-1L), both(lessThanOrEqualTo(seconds2)).and(greaterThanOrEqualTo(seconds2 - 1)))); } @Test @SinceRedisVersion("7.4.0") public void hpexpireAtAndPexpireTime() { long currMillis = System.currentTimeMillis(); long unixMillis = currMillis + 20_000; jedis.hset("foo", "bar", "car"); assertEquals(asList(1L, -2L), jedis.hpexpireAt("foo", unixMillis - 100, "bar", "bared")); jedis.hset("foo", "bared", "cared"); assertEquals(asList(1L, 0L), jedis.hpexpireAt("foo", unixMillis, ExpiryOption.GT, "bar", "bared")); assertThat(jedis.hpexpireTime("foo", "bar", "bare", "bared"), contains(equalTo(unixMillis), equalTo(-2L), equalTo(-1L))); } @Test @SinceRedisVersion("7.4.0") public void hpexpireAtAndPexpireTimeBinary() { long currMillis = System.currentTimeMillis(); long unixMillis = currMillis + 20_000; jedis.hset(bfoo, bbar1, bcar); assertEquals(asList(1L, -2L), jedis.hpexpireAt(bfoo, unixMillis - 100, bbar1, bbar3)); jedis.hset(bfoo, bbar3, bcar); assertEquals(asList(1L, 0L), jedis.hpexpireAt(bfoo, unixMillis, ExpiryOption.GT, bbar1, bbar3)); assertThat(jedis.hpexpireTime(bfoo, bbar1, bbar2, bbar3), contains(equalTo(unixMillis), equalTo(-2L), equalTo(-1L))); } @Test @SinceRedisVersion("7.4.0") public void hpersist() { long seconds = 20; jedis.hset("foo", "bar", "car"); jedis.hset("foo", "bare", "care"); assertEquals(asList(1L, -2L), jedis.hexpire("foo", seconds, "bar", "bared")); assertEquals(asList(1L, -1L, -2L), jedis.hpersist("foo", "bar", "bare", "bared")); assertThat(jedis.httl("foo", "bar", "bare", "bared"), contains(equalTo(-1L), equalTo(-1L), equalTo(-2L))); } @Test @SinceRedisVersion("7.4.0") public void hpersistBinary() { long seconds = 20; jedis.hset(bfoo, bbar1, bcar); jedis.hset(bfoo, bbar2, bcar); assertEquals(asList(1L, -2L), jedis.hexpire(bfoo, seconds, bbar1, bbar3)); assertEquals(asList(1L, -1L, -2L), jedis.hpersist(bfoo, bbar1, bbar2, bbar3)); assertThat(jedis.httl(bfoo, bbar1, bbar2, bbar3), contains(equalTo(-1L), equalTo(-1L), equalTo(-2L))); } } ================================================ FILE: src/test/java/redis/clients/jedis/commands/jedis/HotkeysCommandsTest.java ================================================ package redis.clients.jedis.commands.jedis; import static org.awaitility.Awaitility.await; import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.greaterThan; import static org.hamcrest.Matchers.greaterThanOrEqualTo; import static org.hamcrest.Matchers.lessThan; import static org.hamcrest.Matchers.lessThanOrEqualTo; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertNotNull; import static org.junit.jupiter.api.Assertions.assertNull; import static org.junit.jupiter.api.Assertions.assertTrue; import static redis.clients.jedis.util.RedisVersionUtil.getRedisVersion; import java.time.Duration; import io.redis.test.annotations.ConditionalOnEnv; import io.redis.test.annotations.EnabledOnCommand; import io.redis.test.utils.RedisVersion; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Tag; import org.junit.jupiter.params.ParameterizedClass; import org.junit.jupiter.params.provider.MethodSource; import redis.clients.jedis.RedisProtocol; import redis.clients.jedis.args.HotkeysMetric; import redis.clients.jedis.params.HotkeysParams; import redis.clients.jedis.resps.HotkeysInfo; import redis.clients.jedis.util.RedisVersionUtil; import redis.clients.jedis.util.TestEnvUtil; @ParameterizedClass @MethodSource("redis.clients.jedis.commands.CommandsTestsParameters#respVersions") @Tag("integration") @EnabledOnCommand("HOTKEYS") @ConditionalOnEnv(value = TestEnvUtil.ENV_OSS_DOCKER, enabled = true) public class HotkeysCommandsTest extends JedisCommandsTestBase { public HotkeysCommandsTest(RedisProtocol protocol) { super(protocol); } @Override @BeforeEach public void setUp() throws Exception { super.setUp(); clearState(); } @AfterEach public void cleanUp() { clearState(); } private void clearState() { if (jedis != null) { jedis.flushAll(); jedis.hotkeysStop(); jedis.hotkeysReset(); } } @Test public void hotkeysGetBeforeStart() { HotkeysInfo reply = jedis.hotkeysGet(); assertNull(reply); } @Test public void hotkeysLifecycle() { String startResult = jedis .hotkeysStart(HotkeysParams.hotkeysParams().metrics(HotkeysMetric.CPU)); assertEquals("OK", startResult); jedis.set("key1", "value1"); jedis.get("key1"); HotkeysInfo reply = jedis.hotkeysGet(); assertNotNull(reply); assertTrue(reply.isTrackingActive()); String stopResult = jedis.hotkeysStop(); assertEquals("OK", stopResult); reply = jedis.hotkeysGet(); assertNotNull(reply); assertFalse(reply.isTrackingActive()); assertTrue(reply.getByCpuTimeUs().containsKey("key1")); jedis.hotkeysStart(HotkeysParams.hotkeysParams().metrics(HotkeysMetric.CPU)); jedis.set("key2", "val2"); reply = jedis.hotkeysGet(); assertTrue(reply.isTrackingActive()); assertTrue(reply.getByCpuTimeUs().containsKey("key2")); assertFalse(reply.getByCpuTimeUs().containsKey("key1")); jedis.hotkeysStop(); String resetResult = jedis.hotkeysReset(); assertEquals("OK", resetResult); reply = jedis.hotkeysGet(); assertNull(reply); } @Test public void hotkeysBothMetrics() { jedis.hotkeysStart( HotkeysParams.hotkeysParams().metrics(HotkeysMetric.CPU, HotkeysMetric.NET).sample(1)); String cpuHot = "stats:counter"; for (int i = 0; i < 20; i++) { jedis.incr(cpuHot); } String netHot = "blob:data"; String largeValue = new String(new char[6000]).replace('\0', 'x'); jedis.set(netHot, largeValue); jedis.get(netHot); HotkeysInfo reply = jedis.hotkeysGet(); assertNotNull(reply); assertTrue(reply.getByCpuTimeUs().containsKey(cpuHot)); assertThat(reply.getByCpuTimeUs().get(cpuHot), greaterThan(0L)); assertTrue(reply.getByNetBytes().containsKey(netHot)); assertThat(reply.getByNetBytes().get(netHot), greaterThan(6000L)); } @Test public void hotkeysStartOptions() { jedis.hotkeysStart(HotkeysParams.hotkeysParams().metrics(HotkeysMetric.CPU).sample(5)); for (int i = 0; i < 20; i++) { jedis.set("samplekey" + i, "value" + i); } HotkeysInfo reply = jedis.hotkeysGet(); assertNotNull(reply); assertEquals(5, reply.getSampleRatio()); assertThat(reply.getByCpuTimeUs().size(), lessThan(20)); jedis.hotkeysStop(); jedis.hotkeysReset(); jedis.hotkeysStart(HotkeysParams.hotkeysParams().metrics(HotkeysMetric.CPU).count(10)); for (int i = 1; i <= 25; i++) { jedis.set("countkey" + i, "value" + i); } reply = jedis.hotkeysGet(); assertNotNull(reply); assertThat(reply.getByCpuTimeUs().size(), lessThanOrEqualTo(10)); } @Test public void hotkeysResponseFields() { jedis.hotkeysStart(HotkeysParams.hotkeysParams().metrics(HotkeysMetric.CPU, HotkeysMetric.NET)); jedis.set("testkey", "testvalue"); jedis.get("testkey"); HotkeysInfo reply = jedis.hotkeysGet(); assertNotNull(reply); assertTrue(reply.isTrackingActive()); assertEquals(1, reply.getSampleRatio()); assertNotNull(reply.getSelectedSlots()); // In standalone mode, server returns slot ranges (e.g., [[0, 16383]] for all slots) assertThat(reply.getCollectionStartTimeUnixMs(), greaterThan(0L)); assertThat(reply.getCollectionDurationMs(), greaterThanOrEqualTo(0L)); assertNotNull(reply.getByCpuTimeUs()); assertNotNull(reply.getByNetBytes()); } @Test public void hotkeysDurationOption() { jedis.hotkeysStart(HotkeysParams.hotkeysParams().metrics(HotkeysMetric.CPU).duration(1)); jedis.set("durationkey", "testvalue"); await().atMost(Duration.ofSeconds(2)).until(() -> { HotkeysInfo info = jedis.hotkeysGet(); return info != null && !info.isTrackingActive(); }); HotkeysInfo reply = jedis.hotkeysGet(); assertNotNull(reply); assertFalse(reply.isTrackingActive()); assertThat(reply.getCollectionDurationMs(), greaterThanOrEqualTo(1000L)); assertTrue(reply.getByCpuTimeUs().containsKey("durationkey")); } @Test public void infoHotkeysSection() { boolean isRedis8_6_1OrHigher = getRedisVersion(jedis) .isGreaterThanOrEqualTo(RedisVersion.of("8.6.1")); String info = jedis.info(); // Hotkeys section is displayed in info even when empty starting from Redis 8.6.1+ if (isRedis8_6_1OrHigher) { assertTrue(info.contains("# Hotkeys")); } else { assertFalse(info.contains("# Hotkeys")); } jedis.hotkeysStart(HotkeysParams.hotkeysParams().metrics(HotkeysMetric.CPU)); info = jedis.info(); assertTrue(info.contains("# Hotkeys")); assertTrue(info.contains("hotkeys-tracking-active:1")); jedis.hotkeysStop(); info = jedis.info(); assertTrue(info.contains("# Hotkeys")); assertTrue(info.contains("hotkeys-tracking-active:0")); jedis.hotkeysReset(); info = jedis.info(); // Hotkeys section is displayed in info even when empty starting from Redis 8.6.1+ if (isRedis8_6_1OrHigher) { assertTrue(info.contains("# Hotkeys")); } else { assertFalse(info.contains("# Hotkeys")); } } } ================================================ FILE: src/test/java/redis/clients/jedis/commands/jedis/HyperLogLogCommandsTest.java ================================================ package redis.clients.jedis.commands.jedis; import io.redis.test.annotations.ConditionalOnEnv; import org.junit.jupiter.api.Test; import org.junit.jupiter.params.ParameterizedClass; import org.junit.jupiter.params.provider.MethodSource; import redis.clients.jedis.RedisProtocol; import redis.clients.jedis.util.SafeEncoder; import redis.clients.jedis.util.TestEnvUtil; import static org.junit.jupiter.api.Assertions.assertEquals; @ParameterizedClass @MethodSource("redis.clients.jedis.commands.CommandsTestsParameters#respVersions") public class HyperLogLogCommandsTest extends JedisCommandsTestBase { public HyperLogLogCommandsTest(RedisProtocol protocol) { super(protocol); } @Test public void pfadd() { long status = jedis.pfadd("foo", "a"); assertEquals(1, status); status = jedis.pfadd("foo", "a"); assertEquals(0, status); } @Test public void pfaddBinary() { byte[] bFoo = SafeEncoder.encode("foo"); byte[] bBar = SafeEncoder.encode("bar"); byte[] bBar2 = SafeEncoder.encode("bar2"); long status = jedis.pfadd(bFoo, bBar, bBar2); assertEquals(1, status); status = jedis.pfadd(bFoo, bBar, bBar2); assertEquals(0, status); } @Test public void pfcount() { long status = jedis.pfadd("hll", "foo", "bar", "zap"); assertEquals(1, status); status = jedis.pfadd("hll", "zap", "zap", "zap"); assertEquals(0, status); status = jedis.pfadd("hll", "foo", "bar"); assertEquals(0, status); status = jedis.pfcount("hll"); assertEquals(3, status); } @Test @ConditionalOnEnv(value = TestEnvUtil.ENV_REDIS_ENTERPRISE, enabled = false) public void pfcounts() { long status = jedis.pfadd("hll_1", "foo", "bar", "zap"); assertEquals(1, status); status = jedis.pfadd("hll_2", "foo", "bar", "zap"); assertEquals(1, status); status = jedis.pfadd("hll_3", "foo", "bar", "baz"); assertEquals(1, status); status = jedis.pfcount("hll_1"); assertEquals(3, status); status = jedis.pfcount("hll_2"); assertEquals(3, status); status = jedis.pfcount("hll_3"); assertEquals(3, status); status = jedis.pfcount("hll_1", "hll_2"); assertEquals(3, status); status = jedis.pfcount("hll_1", "hll_2", "hll_3"); assertEquals(4, status); } @Test public void pfcountBinary() { byte[] bHll = SafeEncoder.encode("hll"); byte[] bFoo = SafeEncoder.encode("foo"); byte[] bBar = SafeEncoder.encode("bar"); byte[] bZap = SafeEncoder.encode("zap"); long status = jedis.pfadd(bHll, bFoo, bBar, bZap); assertEquals(1, status); status = jedis.pfadd(bHll, bZap, bZap, bZap); assertEquals(0, status); status = jedis.pfadd(bHll, bFoo, bBar); assertEquals(0, status); status = jedis.pfcount(bHll); assertEquals(3, status); } @Test @ConditionalOnEnv(value = TestEnvUtil.ENV_REDIS_ENTERPRISE, enabled = false) public void pfmerge() { long status = jedis.pfadd("hll1", "foo", "bar", "zap", "a"); assertEquals(1, status); status = jedis.pfadd("hll2", "a", "b", "c", "foo"); assertEquals(1, status); String mergeStatus = jedis.pfmerge("hll3", "hll1", "hll2"); assertEquals("OK", mergeStatus); status = jedis.pfcount("hll3"); assertEquals(6, status); } @Test @ConditionalOnEnv(value = TestEnvUtil.ENV_REDIS_ENTERPRISE, enabled = false) public void pfmergeBinary() { byte[] bHll1 = SafeEncoder.encode("hll1"); byte[] bHll2 = SafeEncoder.encode("hll2"); byte[] bHll3 = SafeEncoder.encode("hll3"); byte[] bFoo = SafeEncoder.encode("foo"); byte[] bBar = SafeEncoder.encode("bar"); byte[] bZap = SafeEncoder.encode("zap"); byte[] bA = SafeEncoder.encode("a"); byte[] bB = SafeEncoder.encode("b"); byte[] bC = SafeEncoder.encode("c"); long status = jedis.pfadd(bHll1, bFoo, bBar, bZap, bA); assertEquals(1, status); status = jedis.pfadd(bHll2, bA, bB, bC, bFoo); assertEquals(1, status); String mergeStatus = jedis.pfmerge(bHll3, bHll1, bHll2); assertEquals("OK", mergeStatus); status = jedis.pfcount(bHll3); assertEquals(6, status); } } ================================================ FILE: src/test/java/redis/clients/jedis/commands/jedis/JedisCommandsTestBase.java ================================================ package redis.clients.jedis.commands.jedis; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Tag; import org.junit.jupiter.api.extension.RegisterExtension; import org.junit.jupiter.api.BeforeAll; import redis.clients.jedis.*; import redis.clients.jedis.util.EnabledOnCommandCondition; import redis.clients.jedis.util.EnvCondition; import redis.clients.jedis.util.RedisVersionCondition; @Tag("integration") public abstract class JedisCommandsTestBase { protected static EndpointConfig endpoint; @RegisterExtension public RedisVersionCondition versionCondition = new RedisVersionCondition( () -> Endpoints.getRedisEndpoint("standalone0")); @RegisterExtension public EnabledOnCommandCondition enabledOnCommandCondition = new EnabledOnCommandCondition( () -> Endpoints.getRedisEndpoint("standalone0")); @RegisterExtension public static EnvCondition envCondition = new EnvCondition(); @BeforeAll public static void prepareEndpoint() { endpoint = Endpoints.getRedisEndpoint("standalone0"); } protected final RedisProtocol protocol; protected Jedis jedis; /** * The RESP protocol is to be injected by the subclasses, usually via JUnit * parameterized tests, because most of the subclassed tests are meant to be * executed against multiple RESP versions. For the special cases where a single * RESP version is relevant, we still force the subclass to be explicit and * call this constructor. * * @param protocol The RESP protocol to use during the tests. */ public JedisCommandsTestBase(RedisProtocol protocol) { this.protocol = protocol; } @BeforeEach public void setUp() throws Exception { jedis = new Jedis(endpoint.getHostAndPort(), endpoint.getClientConfigBuilder() .protocol(protocol).timeoutMillis(500).build()); jedis.flushAll(); } @AfterEach public void tearDown() throws Exception { jedis.close(); } protected Jedis createJedis() { return new Jedis(endpoint.getHostAndPort(), endpoint.getClientConfigBuilder() .protocol(protocol).build()); } } ================================================ FILE: src/test/java/redis/clients/jedis/commands/jedis/ListCommandsTest.java ================================================ package redis.clients.jedis.commands.jedis; import static org.junit.jupiter.api.Assertions.assertArrayEquals; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertNotNull; import static org.junit.jupiter.api.Assertions.assertNull; import static org.junit.jupiter.api.Assertions.fail; import static redis.clients.jedis.util.AssertUtil.assertByteArrayListEquals; import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; import java.util.List; import io.redis.test.annotations.SinceRedisVersion; import io.redis.test.annotations.ConditionalOnEnv; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Tag; import org.junit.jupiter.api.Timeout; import org.junit.jupiter.params.ParameterizedClass; import org.junit.jupiter.params.provider.MethodSource; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import redis.clients.jedis.Jedis; import redis.clients.jedis.RedisProtocol; import redis.clients.jedis.args.ListPosition; import redis.clients.jedis.args.ListDirection; import redis.clients.jedis.exceptions.JedisDataException; import redis.clients.jedis.params.LPosParams; import redis.clients.jedis.util.KeyValue; import redis.clients.jedis.util.TestEnvUtil; @ParameterizedClass @MethodSource("redis.clients.jedis.commands.CommandsTestsParameters#respVersions") @Tag("integration") public class ListCommandsTest extends JedisCommandsTestBase { private final Logger logger = LoggerFactory.getLogger(getClass()); final byte[] bfoo = { 0x01, 0x02, 0x03, 0x04 }; final byte[] bfoo1 = { 0x01, 0x02, 0x03, 0x04, 0x05 }; final byte[] bbar = { 0x05, 0x06, 0x07, 0x08 }; final byte[] bcar = { 0x09, 0x0A, 0x0B, 0x0C }; final byte[] bA = { 0x0A }; final byte[] bB = { 0x0B }; final byte[] bC = { 0x0C }; final byte[] b1 = { 0x01 }; final byte[] b2 = { 0x02 }; final byte[] b3 = { 0x03 }; final byte[] bhello = { 0x04, 0x02 }; final byte[] bx = { 0x02, 0x04 }; final byte[] bdst = { 0x11, 0x12, 0x13, 0x14 }; public ListCommandsTest(RedisProtocol protocol) { super(protocol); } @Test public void rpush() { assertEquals(1, jedis.rpush("foo", "bar")); assertEquals(2, jedis.rpush("foo", "foo")); assertEquals(4, jedis.rpush("foo", "bar", "foo")); // Binary assertEquals(1, jedis.rpush(bfoo, bbar)); assertEquals(2, jedis.rpush(bfoo, bfoo)); assertEquals(4, jedis.rpush(bfoo, bbar, bfoo)); } @Test public void lpush() { assertEquals(1, jedis.lpush("foo", "bar")); assertEquals(2, jedis.lpush("foo", "foo")); assertEquals(4, jedis.lpush("foo", "bar", "foo")); // Binary assertEquals(1, jedis.lpush(bfoo, bbar)); assertEquals(2, jedis.lpush(bfoo, bfoo)); assertEquals(4, jedis.lpush(bfoo, bbar, bfoo)); } @Test public void llen() { assertEquals(0, jedis.llen("foo")); jedis.lpush("foo", "bar"); jedis.lpush("foo", "car"); assertEquals(2, jedis.llen("foo")); // Binary assertEquals(0, jedis.llen(bfoo)); jedis.lpush(bfoo, bbar); jedis.lpush(bfoo, bcar); assertEquals(2, jedis.llen(bfoo)); } @Test public void llenNotOnList() { try { jedis.set("foo", "bar"); jedis.llen("foo"); fail("JedisDataException expected"); } catch (final JedisDataException e) { } // Binary try { jedis.set(bfoo, bbar); jedis.llen(bfoo); fail("JedisDataException expected"); } catch (final JedisDataException e) { } } @Test public void lrange() { jedis.rpush("foo", "a"); jedis.rpush("foo", "b"); jedis.rpush("foo", "c"); List expected = new ArrayList(); expected.add("a"); expected.add("b"); expected.add("c"); List range = jedis.lrange("foo", 0, 2); assertEquals(expected, range); range = jedis.lrange("foo", 0, 20); assertEquals(expected, range); expected = new ArrayList(); expected.add("b"); expected.add("c"); range = jedis.lrange("foo", 1, 2); assertEquals(expected, range); range = jedis.lrange("foo", 2, 1); assertEquals(Collections. emptyList(), range); // Binary jedis.rpush(bfoo, bA); jedis.rpush(bfoo, bB); jedis.rpush(bfoo, bC); List bexpected = new ArrayList(); bexpected.add(bA); bexpected.add(bB); bexpected.add(bC); List brange = jedis.lrange(bfoo, 0, 2); assertByteArrayListEquals(bexpected, brange); brange = jedis.lrange(bfoo, 0, 20); assertByteArrayListEquals(bexpected, brange); bexpected = new ArrayList(); bexpected.add(bB); bexpected.add(bC); brange = jedis.lrange(bfoo, 1, 2); assertByteArrayListEquals(bexpected, brange); brange = jedis.lrange(bfoo, 2, 1); assertByteArrayListEquals(Collections. emptyList(), brange); } @Test public void ltrim() { jedis.lpush("foo", "1"); jedis.lpush("foo", "2"); jedis.lpush("foo", "3"); String status = jedis.ltrim("foo", 0, 1); List expected = new ArrayList(); expected.add("3"); expected.add("2"); assertEquals("OK", status); assertEquals(2, jedis.llen("foo")); assertEquals(expected, jedis.lrange("foo", 0, 100)); // Binary jedis.lpush(bfoo, b1); jedis.lpush(bfoo, b2); jedis.lpush(bfoo, b3); String bstatus = jedis.ltrim(bfoo, 0, 1); List bexpected = new ArrayList(); bexpected.add(b3); bexpected.add(b2); assertEquals("OK", bstatus); assertEquals(2, jedis.llen(bfoo)); assertByteArrayListEquals(bexpected, jedis.lrange(bfoo, 0, 100)); } @Test public void lset() { jedis.lpush("foo", "1"); jedis.lpush("foo", "2"); jedis.lpush("foo", "3"); List expected = new ArrayList(); expected.add("3"); expected.add("bar"); expected.add("1"); String status = jedis.lset("foo", 1, "bar"); assertEquals("OK", status); assertEquals(expected, jedis.lrange("foo", 0, 100)); // Binary jedis.lpush(bfoo, b1); jedis.lpush(bfoo, b2); jedis.lpush(bfoo, b3); List bexpected = new ArrayList(); bexpected.add(b3); bexpected.add(bbar); bexpected.add(b1); String bstatus = jedis.lset(bfoo, 1, bbar); assertEquals("OK", bstatus); assertByteArrayListEquals(bexpected, jedis.lrange(bfoo, 0, 100)); } @Test public void lindex() { jedis.lpush("foo", "1"); jedis.lpush("foo", "2"); jedis.lpush("foo", "3"); assertEquals("3", jedis.lindex("foo", 0)); assertNull(jedis.lindex("foo", 100)); // Binary jedis.lpush(bfoo, b1); jedis.lpush(bfoo, b2); jedis.lpush(bfoo, b3); assertArrayEquals(b3, jedis.lindex(bfoo, 0)); assertNull(jedis.lindex(bfoo, 100)); } @Test public void lrem() { jedis.lpush("foo", "hello"); jedis.lpush("foo", "hello"); jedis.lpush("foo", "x"); jedis.lpush("foo", "hello"); jedis.lpush("foo", "c"); jedis.lpush("foo", "b"); jedis.lpush("foo", "a"); List expected = new ArrayList(); expected.add("a"); expected.add("b"); expected.add("c"); expected.add("hello"); expected.add("x"); assertEquals(2, jedis.lrem("foo", -2, "hello")); assertEquals(expected, jedis.lrange("foo", 0, 1000)); assertEquals(0, jedis.lrem("bar", 100, "foo")); // Binary jedis.lpush(bfoo, bhello); jedis.lpush(bfoo, bhello); jedis.lpush(bfoo, bx); jedis.lpush(bfoo, bhello); jedis.lpush(bfoo, bC); jedis.lpush(bfoo, bB); jedis.lpush(bfoo, bA); List bexpected = new ArrayList(); bexpected.add(bA); bexpected.add(bB); bexpected.add(bC); bexpected.add(bhello); bexpected.add(bx); assertEquals(2, jedis.lrem(bfoo, -2, bhello)); assertByteArrayListEquals(bexpected, jedis.lrange(bfoo, 0, 1000)); assertEquals(0, jedis.lrem(bbar, 100, bfoo)); } @Test public void lpop() { assertNull(jedis.lpop("foo")); assertNull(jedis.lpop("foo", 0)); jedis.rpush("foo", "a"); jedis.rpush("foo", "b"); jedis.rpush("foo", "c"); assertEquals("a", jedis.lpop("foo")); assertEquals(Arrays.asList("b", "c"), jedis.lpop("foo", 10)); assertNull(jedis.lpop("foo")); assertNull(jedis.lpop("foo", 1)); // Binary assertNull(jedis.lpop(bfoo)); assertNull(jedis.lpop(bfoo, 0)); jedis.rpush(bfoo, bA); jedis.rpush(bfoo, bB); jedis.rpush(bfoo, bC); assertArrayEquals(bA, jedis.lpop(bfoo)); assertByteArrayListEquals(Arrays.asList(bB, bC), jedis.lpop(bfoo, 10)); assertNull(jedis.lpop(bfoo)); assertNull(jedis.lpop(bfoo, 1)); } @Test public void rpop() { assertNull(jedis.rpop("foo")); assertNull(jedis.rpop("foo", 0)); jedis.rpush("foo", "a"); jedis.rpush("foo", "b"); jedis.rpush("foo", "c"); assertEquals("c", jedis.rpop("foo")); assertEquals(Arrays.asList("b", "a"), jedis.rpop("foo", 10)); assertNull(jedis.rpop("foo")); assertNull(jedis.rpop("foo", 1)); // Binary assertNull(jedis.rpop(bfoo)); assertNull(jedis.rpop(bfoo, 0)); jedis.rpush(bfoo, bA); jedis.rpush(bfoo, bB); jedis.rpush(bfoo, bC); assertArrayEquals(bC, jedis.rpop(bfoo)); assertByteArrayListEquals(Arrays.asList(bB, bA), jedis.rpop(bfoo, 10)); assertNull(jedis.rpop(bfoo)); assertNull(jedis.rpop(bfoo, 1)); } @Test @ConditionalOnEnv(value = TestEnvUtil.ENV_REDIS_ENTERPRISE, enabled = false) public void rpoplpush() { jedis.rpush("foo", "a"); jedis.rpush("foo", "b"); jedis.rpush("foo", "c"); jedis.rpush("dst", "foo"); jedis.rpush("dst", "bar"); String element = jedis.rpoplpush("foo", "dst"); assertEquals("c", element); List srcExpected = new ArrayList(); srcExpected.add("a"); srcExpected.add("b"); List dstExpected = new ArrayList(); dstExpected.add("c"); dstExpected.add("foo"); dstExpected.add("bar"); assertEquals(srcExpected, jedis.lrange("foo", 0, 1000)); assertEquals(dstExpected, jedis.lrange("dst", 0, 1000)); // Binary jedis.rpush(bfoo, bA); jedis.rpush(bfoo, bB); jedis.rpush(bfoo, bC); jedis.rpush(bdst, bfoo); jedis.rpush(bdst, bbar); byte[] belement = jedis.rpoplpush(bfoo, bdst); assertArrayEquals(bC, belement); List bsrcExpected = new ArrayList(); bsrcExpected.add(bA); bsrcExpected.add(bB); List bdstExpected = new ArrayList(); bdstExpected.add(bC); bdstExpected.add(bfoo); bdstExpected.add(bbar); assertByteArrayListEquals(bsrcExpected, jedis.lrange(bfoo, 0, 1000)); assertByteArrayListEquals(bdstExpected, jedis.lrange(bdst, 0, 1000)); } @Test @ConditionalOnEnv(value = TestEnvUtil.ENV_REDIS_ENTERPRISE, enabled = false) public void blpop() throws InterruptedException { List result = jedis.blpop(1, "foo"); assertNull(result); jedis.lpush("foo", "bar"); result = jedis.blpop(1, "foo"); assertNotNull(result); assertEquals(2, result.size()); assertEquals("foo", result.get(0)); assertEquals("bar", result.get(1)); // Multi keys result = jedis.blpop(1, "foo", "foo1"); assertNull(result); jedis.lpush("foo", "bar"); jedis.lpush("foo1", "bar1"); result = jedis.blpop(1, "foo1", "foo"); assertNotNull(result); assertEquals(2, result.size()); assertEquals("foo1", result.get(0)); assertEquals("bar1", result.get(1)); // Binary jedis.lpush(bfoo, bbar); List bresult = jedis.blpop(1, bfoo); assertNotNull(bresult); assertEquals(2, bresult.size()); assertArrayEquals(bfoo, bresult.get(0)); assertArrayEquals(bbar, bresult.get(1)); // Binary Multi keys bresult = jedis.blpop(1, bfoo, bfoo1); assertNull(bresult); jedis.lpush(bfoo, bbar); jedis.lpush(bfoo1, bcar); bresult = jedis.blpop(1, bfoo, bfoo1); assertNotNull(bresult); assertEquals(2, bresult.size()); assertArrayEquals(bfoo, bresult.get(0)); assertArrayEquals(bbar, bresult.get(1)); } @Test @ConditionalOnEnv(value = TestEnvUtil.ENV_REDIS_ENTERPRISE, enabled = false) public void blpopDouble() throws InterruptedException { KeyValue result = jedis.blpop(0.1, "foo"); assertNull(result); jedis.lpush("foo", "bar"); result = jedis.blpop(3.2, "foo"); assertNotNull(result); assertEquals("foo", result.getKey()); assertEquals("bar", result.getValue()); // Multi keys result = jedis.blpop(0.18, "foo", "foo1"); assertNull(result); jedis.lpush("foo", "bar"); jedis.lpush("foo1", "bar1"); result = jedis.blpop(1d, "foo1", "foo"); assertNotNull(result); assertEquals("foo1", result.getKey()); assertEquals("bar1", result.getValue()); // Binary jedis.lpush(bfoo, bbar); KeyValue bresult = jedis.blpop(3.12, bfoo); assertNotNull(bresult); assertArrayEquals(bfoo, bresult.getKey()); assertArrayEquals(bbar, bresult.getValue()); // Binary Multi keys bresult = jedis.blpop(0.11, bfoo, bfoo1); assertNull(bresult); jedis.lpush(bfoo, bbar); jedis.lpush(bfoo1, bcar); bresult = jedis.blpop(1d, bfoo, bfoo1); assertNotNull(bresult); assertArrayEquals(bfoo, bresult.getKey()); assertArrayEquals(bbar, bresult.getValue()); } @Test @Timeout(5) public void blpopDoubleWithSleep() { KeyValue result = jedis.blpop(0.04, "foo"); assertNull(result); new Thread(() -> { try { Thread.sleep(30); } catch(InterruptedException e) { logger.error("", e); } try (Jedis j = createJedis()) { j.lpush("foo", "bar"); } }).start(); result = jedis.blpop(1.2, "foo"); assertNotNull(result); assertEquals("foo", result.getKey()); assertEquals("bar", result.getValue()); } @Test @ConditionalOnEnv(value = TestEnvUtil.ENV_REDIS_ENTERPRISE, enabled = false) public void brpop() throws InterruptedException { List result = jedis.brpop(1, "foo"); assertNull(result); jedis.lpush("foo", "bar"); result = jedis.brpop(1, "foo"); assertNotNull(result); assertEquals(2, result.size()); assertEquals("foo", result.get(0)); assertEquals("bar", result.get(1)); // Multi keys result = jedis.brpop(1, "foo", "foo1"); assertNull(result); jedis.lpush("foo", "bar"); jedis.lpush("foo1", "bar1"); result = jedis.brpop(1, "foo1", "foo"); assertNotNull(result); assertEquals(2, result.size()); assertEquals("foo1", result.get(0)); assertEquals("bar1", result.get(1)); // Binary jedis.lpush(bfoo, bbar); List bresult = jedis.brpop(1, bfoo); assertNotNull(bresult); assertEquals(2, bresult.size()); assertArrayEquals(bfoo, bresult.get(0)); assertArrayEquals(bbar, bresult.get(1)); // Binary Multi keys bresult = jedis.brpop(1, bfoo, bfoo1); assertNull(bresult); jedis.lpush(bfoo, bbar); jedis.lpush(bfoo1, bcar); bresult = jedis.brpop(1, bfoo, bfoo1); assertNotNull(bresult); assertEquals(2, bresult.size()); assertArrayEquals(bfoo, bresult.get(0)); assertArrayEquals(bbar, bresult.get(1)); } @Test @ConditionalOnEnv(value = TestEnvUtil.ENV_REDIS_ENTERPRISE, enabled = false) public void brpopDouble() throws InterruptedException { KeyValue result = jedis.brpop(0.1, "foo"); assertNull(result); jedis.lpush("foo", "bar"); result = jedis.brpop(3.2, "foo"); assertNotNull(result); assertEquals("foo", result.getKey()); assertEquals("bar", result.getValue()); // Multi keys result = jedis.brpop(0.18, "foo", "foo1"); assertNull(result); jedis.lpush("foo", "bar"); jedis.lpush("foo1", "bar1"); result = jedis.brpop(1d, "foo1", "foo"); assertNotNull(result); assertEquals("foo1", result.getKey()); assertEquals("bar1", result.getValue()); // Binary jedis.lpush(bfoo, bbar); KeyValue bresult = jedis.brpop(3.12, bfoo); assertNotNull(bresult); assertArrayEquals(bfoo, bresult.getKey()); assertArrayEquals(bbar, bresult.getValue()); // Binary Multi keys bresult = jedis.brpop(0.11, bfoo, bfoo1); assertNull(bresult); jedis.lpush(bfoo, bbar); jedis.lpush(bfoo1, bcar); bresult = jedis.brpop(1d, bfoo, bfoo1); assertNotNull(bresult); assertArrayEquals(bfoo, bresult.getKey()); assertArrayEquals(bbar, bresult.getValue()); } @Test @Timeout(5) public void brpopDoubleWithSleep() { KeyValue result = jedis.brpop(0.04, "foo"); assertNull(result); new Thread(() -> { try { Thread.sleep(30); } catch(InterruptedException e) { logger.error("", e); } try (Jedis j = createJedis()) { j.lpush("foo", "bar"); } }).start(); result = jedis.brpop(1.2, "foo"); assertNotNull(result); assertEquals("foo", result.getKey()); assertEquals("bar", result.getValue()); } @Test public void lpushx() { assertEquals(0, jedis.lpushx("foo", "bar")); jedis.lpush("foo", "a"); assertEquals(2, jedis.lpushx("foo", "b")); // Binary assertEquals(0, jedis.lpushx(bfoo, bbar)); jedis.lpush(bfoo, bA); assertEquals(2, jedis.lpushx(bfoo, bB)); } @Test public void rpushx() { assertEquals(0, jedis.rpushx("foo", "bar")); jedis.lpush("foo", "a"); assertEquals(2, jedis.rpushx("foo", "b")); // Binary assertEquals(0, jedis.rpushx(bfoo, bbar)); jedis.lpush(bfoo, bA); assertEquals(2, jedis.rpushx(bfoo, bB)); } @Test public void linsert() { assertEquals(0, jedis.linsert("foo", ListPosition.BEFORE, "bar", "car")); jedis.lpush("foo", "a"); assertEquals(2, jedis.linsert("foo", ListPosition.AFTER, "a", "b")); List expected = new ArrayList(); expected.add("a"); expected.add("b"); assertEquals(expected, jedis.lrange("foo", 0, 100)); assertEquals(-1, jedis.linsert("foo", ListPosition.BEFORE, "bar", "car")); // Binary assertEquals(0, jedis.linsert(bfoo, ListPosition.BEFORE, bbar, bcar)); jedis.lpush(bfoo, bA); assertEquals(2, jedis.linsert(bfoo, ListPosition.AFTER, bA, bB)); List bexpected = new ArrayList(); bexpected.add(bA); bexpected.add(bB); assertByteArrayListEquals(bexpected, jedis.lrange(bfoo, 0, 100)); assertEquals(-1, jedis.linsert(bfoo, ListPosition.BEFORE, bbar, bcar)); } @Test @ConditionalOnEnv(value = TestEnvUtil.ENV_REDIS_ENTERPRISE, enabled = false) public void brpoplpush() { new Thread(new Runnable() { @Override public void run() { try { Thread.sleep(100); } catch (InterruptedException e) { logger.error("", e); } try (Jedis j = createJedis()) { j.lpush("foo", "a"); } } }).start(); String element = jedis.brpoplpush("foo", "bar", 0); assertEquals("a", element); assertEquals(1, jedis.llen("bar")); assertEquals("a", jedis.lrange("bar", 0, -1).get(0)); // Binary new Thread(new Runnable() { @Override public void run() { try { Thread.sleep(100); } catch (InterruptedException e) { logger.error("", e); } try (Jedis j = createJedis()) { j.lpush(bfoo, bA); } } }).start(); byte[] belement = jedis.brpoplpush(bfoo, bbar, 0); assertArrayEquals(bA, belement); assertEquals(1, jedis.llen("bar")); assertArrayEquals(bA, jedis.lrange(bbar, 0, -1).get(0)); } @Test public void lpos() { jedis.rpush("foo", "a"); jedis.rpush("foo", "b"); jedis.rpush("foo", "c"); Long pos = jedis.lpos("foo", "b"); assertEquals(1, pos.intValue()); pos = jedis.lpos("foo", "d"); assertNull(pos); jedis.rpush("foo", "a"); jedis.rpush("foo", "b"); jedis.rpush("foo", "b"); pos = jedis.lpos("foo", "b", LPosParams.lPosParams()); assertEquals(1, pos.intValue()); pos = jedis.lpos("foo", "b", LPosParams.lPosParams().rank(3)); assertEquals(5, pos.intValue()); pos = jedis.lpos("foo", "b", LPosParams.lPosParams().rank(-2)); assertEquals(4, pos.intValue()); pos = jedis.lpos("foo", "b", LPosParams.lPosParams().rank(-5)); assertNull(pos); pos = jedis.lpos("foo", "b", LPosParams.lPosParams().rank(1).maxlen(2)); assertEquals(1, pos.intValue()); pos = jedis.lpos("foo", "b", LPosParams.lPosParams().rank(2).maxlen(2)); assertNull(pos); pos = jedis.lpos("foo", "b", LPosParams.lPosParams().rank(-2).maxlen(2)); assertEquals(4, pos.intValue()); List expected = new ArrayList(); expected.add(1L); expected.add(4L); expected.add(5L); List posList = jedis.lpos("foo", "b", LPosParams.lPosParams(), 2); assertEquals(expected.subList(0, 2), posList); posList = jedis.lpos("foo", "b", LPosParams.lPosParams(), 0); assertEquals(expected, posList); posList = jedis.lpos("foo", "b", LPosParams.lPosParams().rank(2), 0); assertEquals(expected.subList(1, 3), posList); posList = jedis.lpos("foo", "b", LPosParams.lPosParams().rank(2).maxlen(5), 0); assertEquals(expected.subList(1, 2), posList); Collections.reverse(expected); posList = jedis.lpos("foo", "b", LPosParams.lPosParams().rank(-2), 0); assertEquals(expected.subList(1, 3), posList); posList = jedis.lpos("foo", "b", LPosParams.lPosParams().rank(-1).maxlen(5), 2); assertEquals(expected.subList(0, 2), posList); // Binary jedis.rpush(bfoo, bA); jedis.rpush(bfoo, bB); jedis.rpush(bfoo, bC); pos = jedis.lpos(bfoo, bB); assertEquals(1, pos.intValue()); pos = jedis.lpos(bfoo, b3); assertNull(pos); jedis.rpush(bfoo, bA); jedis.rpush(bfoo, bB); jedis.rpush(bfoo, bA); pos = jedis.lpos(bfoo, bB, LPosParams.lPosParams().rank(2)); assertEquals(4, pos.intValue()); pos = jedis.lpos(bfoo, bB, LPosParams.lPosParams().rank(-2).maxlen(5)); assertEquals(1, pos.intValue()); expected.clear(); expected.add(0L); expected.add(3L); expected.add(5L); posList = jedis.lpos(bfoo, bA, LPosParams.lPosParams().maxlen(6), 0); assertEquals(expected, posList); posList = jedis.lpos(bfoo, bA, LPosParams.lPosParams().maxlen(6).rank(2), 1); assertEquals(expected.subList(1, 2), posList); } @Test @ConditionalOnEnv(value = TestEnvUtil.ENV_REDIS_ENTERPRISE, enabled = false) public void lmove() { jedis.rpush("foo", "bar1", "bar2", "bar3"); assertEquals("bar3", jedis.lmove("foo", "bar", ListDirection.RIGHT, ListDirection.LEFT)); assertEquals(Collections.singletonList("bar3"), jedis.lrange("bar", 0, -1)); assertEquals(Arrays.asList("bar1", "bar2"), jedis.lrange("foo", 0, -1)); // Binary jedis.rpush(bfoo, b1, b2, b3); assertArrayEquals(b3, jedis.lmove(bfoo, bbar, ListDirection.RIGHT, ListDirection.LEFT)); assertByteArrayListEquals(Collections.singletonList(b3), jedis.lrange(bbar, 0, -1)); assertByteArrayListEquals(Arrays.asList(b1, b2), jedis.lrange(bfoo, 0, -1)); } @Test @ConditionalOnEnv(value = TestEnvUtil.ENV_REDIS_ENTERPRISE, enabled = false) public void blmove() { new Thread(() -> { try { Thread.sleep(100); } catch (InterruptedException e) { logger.error("", e); } try (Jedis j = createJedis()) { j.rpush("foo", "bar1", "bar2", "bar3"); } }).start(); assertEquals("bar3", jedis.blmove("foo", "bar", ListDirection.RIGHT, ListDirection.LEFT, 0)); assertEquals(Collections.singletonList("bar3"), jedis.lrange("bar", 0, -1)); assertEquals(Arrays.asList("bar1", "bar2"), jedis.lrange("foo", 0, -1)); // Binary new Thread(() -> { try { Thread.sleep(100); } catch (InterruptedException e) { logger.error("", e); } try (Jedis j = createJedis()) { j.rpush(bfoo, b1, b2, b3); } }).start(); assertArrayEquals(b3, jedis.blmove(bfoo, bbar, ListDirection.RIGHT, ListDirection.LEFT, 0)); assertByteArrayListEquals(Collections.singletonList(b3), jedis.lrange(bbar, 0, -1)); assertByteArrayListEquals(Arrays.asList(b1, b2), jedis.lrange(bfoo, 0, -1)); } @Test @SinceRedisVersion("7.0.0") @ConditionalOnEnv(value = TestEnvUtil.ENV_REDIS_ENTERPRISE, enabled = false) public void lmpop() { String mylist1 = "mylist1"; String mylist2 = "mylist2"; // add elements to list jedis.lpush(mylist1, "one", "two", "three", "four", "five"); jedis.lpush(mylist2, "one", "two", "three", "four", "five"); KeyValue> elements = jedis.lmpop(ListDirection.LEFT, mylist1, mylist2); assertEquals(mylist1, elements.getKey()); assertEquals(1, elements.getValue().size()); elements = jedis.lmpop(ListDirection.LEFT, 5, mylist1, mylist2); assertEquals(mylist1, elements.getKey()); assertEquals(4, elements.getValue().size()); elements = jedis.lmpop(ListDirection.RIGHT, 100, mylist1, mylist2); assertEquals(mylist2, elements.getKey()); assertEquals(5, elements.getValue().size()); elements = jedis.lmpop(ListDirection.RIGHT, mylist1, mylist2); assertNull(elements); } @Test @SinceRedisVersion("7.0.0") @ConditionalOnEnv(value = TestEnvUtil.ENV_REDIS_ENTERPRISE, enabled = false) public void blmpopSimple() { String mylist1 = "mylist1"; String mylist2 = "mylist2"; // add elements to list jedis.lpush(mylist1, "one", "two", "three", "four", "five"); jedis.lpush(mylist2, "one", "two", "three", "four", "five"); KeyValue> elements = jedis.blmpop(1L, ListDirection.LEFT, mylist1, mylist2); assertEquals(mylist1, elements.getKey()); assertEquals(1, elements.getValue().size()); elements = jedis.blmpop(1L, ListDirection.LEFT, 5, mylist1, mylist2); assertEquals(mylist1, elements.getKey()); assertEquals(4, elements.getValue().size()); elements = jedis.blmpop(1L, ListDirection.RIGHT, 100, mylist1, mylist2); assertEquals(mylist2, elements.getKey()); assertEquals(5, elements.getValue().size()); elements = jedis.blmpop(1L, ListDirection.RIGHT, mylist1, mylist2); assertNull(elements); } } ================================================ FILE: src/test/java/redis/clients/jedis/commands/jedis/MigrateTest.java ================================================ package redis.clients.jedis.commands.jedis; import io.redis.test.annotations.ConditionalOnEnv; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.params.ParameterizedClass; import org.junit.jupiter.params.provider.MethodSource; import redis.clients.jedis.*; import redis.clients.jedis.exceptions.JedisDataException; import redis.clients.jedis.params.MigrateParams; import redis.clients.jedis.util.TestEnvUtil; import static org.junit.jupiter.api.Assertions.assertArrayEquals; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertNull; import static org.junit.jupiter.api.Assertions.assertTrue; import static org.junit.jupiter.api.Assertions.fail; @ParameterizedClass @MethodSource("redis.clients.jedis.commands.CommandsTestsParameters#respVersions") @ConditionalOnEnv(value = TestEnvUtil.ENV_REDIS_ENTERPRISE, enabled = false) public class MigrateTest extends JedisCommandsTestBase { private static final byte[] bfoo = { 0x01, 0x02, 0x03 }; private static final byte[] bbar = { 0x04, 0x05, 0x06 }; private static final byte[] bfoo1 = { 0x07, 0x08, 0x01 }; private static final byte[] bbar1 = { 0x09, 0x00, 0x01 }; private static final byte[] bfoo2 = { 0x07, 0x08, 0x02 }; private static final byte[] bbar2 = { 0x09, 0x00, 0x02 }; private static final byte[] bfoo3 = { 0x07, 0x08, 0x03 }; private static final byte[] bbar3 = { 0x09, 0x00, 0x03 }; private Jedis dest; private Jedis destAuth; private static EndpointConfig destEndpoint; private static EndpointConfig destEndpointWithAuth; private static String host; private static int port; private static int portAuth; private static final int db = 2; private static final int dbAuth = 3; private static final int timeout = Protocol.DEFAULT_TIMEOUT; @BeforeAll public static void prepareEndpoints() { destEndpoint = Endpoints.getRedisEndpoint("standalone7-with-lfu-policy"); destEndpointWithAuth = Endpoints.getRedisEndpoint("standalone1"); host = destEndpoint.getHost(); port = destEndpoint.getPort(); portAuth = destEndpointWithAuth.getPort(); } public MigrateTest(RedisProtocol protocol) { super(protocol); } @BeforeEach @Override public void setUp() throws Exception { super.setUp(); dest = new Jedis(host, port, 500); dest.flushAll(); dest.select(db); destAuth = new Jedis(destEndpointWithAuth.getHostAndPort(), destEndpointWithAuth.getClientConfigBuilder().build()); destAuth.flushAll(); destAuth.select(dbAuth); } @AfterEach @Override public void tearDown() throws Exception { dest.close(); destAuth.close(); super.tearDown(); } @Test public void nokey() { assertEquals("NOKEY", jedis.migrate(host, port, "foo", db, timeout)); assertEquals("NOKEY", jedis.migrate(host, port, bfoo, db, timeout)); assertEquals("NOKEY", jedis.migrate(host, port, db, timeout, new MigrateParams(), "foo1", "foo2", "foo3")); assertEquals("NOKEY", jedis.migrate(host, port, db, timeout, new MigrateParams(), bfoo1, bfoo2, bfoo3)); } @Test public void migrate() { jedis.set("foo", "bar"); assertEquals("OK", jedis.migrate(host, port, "foo", db, timeout)); assertEquals("bar", dest.get("foo")); assertNull(jedis.get("foo")); jedis.set(bfoo, bbar); assertEquals("OK", jedis.migrate(host, port, bfoo, db, timeout)); assertArrayEquals(bbar, dest.get(bfoo)); assertNull(jedis.get(bfoo)); } @Test public void migrateEmptyParams() { jedis.set("foo", "bar"); assertEquals("OK", jedis.migrate(host, port, db, timeout, new MigrateParams(), "foo")); assertEquals("bar", dest.get("foo")); assertNull(jedis.get("foo")); jedis.set(bfoo, bbar); assertEquals("OK", jedis.migrate(host, port, db, timeout, new MigrateParams(), bfoo)); assertArrayEquals(bbar, dest.get(bfoo)); assertNull(jedis.get(bfoo)); } @Test public void migrateCopy() { jedis.set("foo", "bar"); assertEquals("OK", jedis.migrate(host, port, db, timeout, new MigrateParams().copy(), "foo")); assertEquals("bar", dest.get("foo")); assertEquals("bar", jedis.get("foo")); jedis.set(bfoo, bbar); assertEquals("OK", jedis.migrate(host, port, db, timeout, new MigrateParams().copy(), bfoo)); assertArrayEquals(bbar, dest.get(bfoo)); assertArrayEquals(bbar, jedis.get(bfoo)); } @Test public void migrateReplace() { jedis.set("foo", "bar1"); dest.set("foo", "bar2"); assertEquals("OK", jedis.migrate(host, port, db, timeout, new MigrateParams().replace(), "foo")); assertEquals("bar1", dest.get("foo")); assertNull(jedis.get("foo")); jedis.set(bfoo, bbar1); dest.set(bfoo, bbar2); assertEquals("OK", jedis.migrate(host, port, db, timeout, new MigrateParams().replace(), bfoo)); assertArrayEquals(bbar1, dest.get(bfoo)); assertNull(jedis.get(bfoo)); } @Test public void migrateCopyReplace() { jedis.set("foo", "bar1"); dest.set("foo", "bar2"); assertEquals("OK", jedis.migrate(host, port, db, timeout, new MigrateParams().copy().replace(), "foo")); assertEquals("bar1", dest.get("foo")); assertEquals("bar1", jedis.get("foo")); jedis.set(bfoo, bbar1); dest.set(bfoo, bbar2); assertEquals("OK", jedis.migrate(host, port, db, timeout, new MigrateParams().copy().replace(), bfoo)); assertArrayEquals(bbar1, dest.get(bfoo)); assertArrayEquals(bbar1, jedis.get(bfoo)); } @Test public void migrateAuth() { jedis.set("foo", "bar"); assertEquals("OK", jedis.migrate(host, portAuth, dbAuth, timeout, new MigrateParams().auth(destEndpointWithAuth.getPassword()), "foo")); assertEquals("bar", destAuth.get("foo")); assertNull(jedis.get("foo")); jedis.set(bfoo, bbar); assertEquals("OK", jedis.migrate(host, portAuth, dbAuth, timeout, new MigrateParams().auth(destEndpointWithAuth.getPassword()), bfoo)); assertArrayEquals(bbar, destAuth.get(bfoo)); assertNull(jedis.get(bfoo)); } @Test public void migrateAuth2() { destAuth.set("foo", "bar"); assertEquals("OK", destAuth.migrate(host, endpoint.getPort(), 0, timeout, new MigrateParams().auth2(endpoint.getUsername(), endpoint.getPassword()), "foo")); assertEquals("bar", jedis.get("foo")); assertNull(destAuth.get("foo")); // binary dest.set(bfoo1, bbar1); assertEquals("OK", dest.migrate(host, endpoint.getPort(), 0, timeout, new MigrateParams().auth2(endpoint.getUsername(), endpoint.getPassword()), bfoo1)); assertArrayEquals(bbar1, jedis.get(bfoo1)); assertNull(dest.get(bfoo1)); } @Test public void migrateCopyReplaceAuth() { jedis.set("foo", "bar1"); destAuth.set("foo", "bar2"); assertEquals("OK", jedis.migrate(host, portAuth, dbAuth, timeout, new MigrateParams().copy().replace().auth(destEndpointWithAuth.getPassword()), "foo")); assertEquals("bar1", destAuth.get("foo")); assertEquals("bar1", jedis.get("foo")); jedis.set(bfoo, bbar1); destAuth.set(bfoo, bbar2); assertEquals( "OK", jedis.migrate(host, portAuth, dbAuth, timeout, new MigrateParams().copy().replace().auth(destEndpointWithAuth.getPassword()), bfoo)); assertArrayEquals(bbar1, destAuth.get(bfoo)); assertArrayEquals(bbar1, jedis.get(bfoo)); } @Test public void migrateMulti() { jedis.mset("foo1", "bar1", "foo2", "bar2", "foo3", "bar3"); assertEquals("OK", jedis.migrate(host, port, db, timeout, new MigrateParams(), "foo1", "foo2", "foo3")); assertEquals("bar1", dest.get("foo1")); assertEquals("bar2", dest.get("foo2")); assertEquals("bar3", dest.get("foo3")); jedis.mset(bfoo1, bbar1, bfoo2, bbar2, bfoo3, bbar3); assertEquals("OK", jedis.migrate(host, port, db, timeout, new MigrateParams(), bfoo1, bfoo2, bfoo3)); assertArrayEquals(bbar1, dest.get(bfoo1)); assertArrayEquals(bbar2, dest.get(bfoo2)); assertArrayEquals(bbar3, dest.get(bfoo3)); } @Test public void migrateConflict() { jedis.mset("foo1", "bar1", "foo2", "bar2", "foo3", "bar3"); dest.set("foo2", "bar"); try { jedis.migrate(host, port, db, timeout, new MigrateParams(), "foo1", "foo2", "foo3"); fail("Should get BUSYKEY error"); } catch (JedisDataException jde) { assertTrue(jde.getMessage().contains("BUSYKEY")); } assertEquals("bar1", dest.get("foo1")); assertEquals("bar", dest.get("foo2")); assertEquals("bar3", dest.get("foo3")); jedis.mset(bfoo1, bbar1, bfoo2, bbar2, bfoo3, bbar3); dest.set(bfoo2, bbar); try { jedis.migrate(host, port, db, timeout, new MigrateParams(), bfoo1, bfoo2, bfoo3); fail("Should get BUSYKEY error"); } catch (JedisDataException jde) { assertTrue(jde.getMessage().contains("BUSYKEY")); } assertArrayEquals(bbar1, dest.get(bfoo1)); assertArrayEquals(bbar, dest.get(bfoo2)); assertArrayEquals(bbar3, dest.get(bfoo3)); } } ================================================ FILE: src/test/java/redis/clients/jedis/commands/jedis/ModuleTest.java ================================================ package redis.clients.jedis.commands.jedis; import java.util.List; import io.redis.test.annotations.ConditionalOnEnv; import org.junit.jupiter.api.Test; import org.junit.jupiter.params.ParameterizedClass; import org.junit.jupiter.params.provider.MethodSource; import redis.clients.jedis.util.TestEnvUtil; import redis.clients.jedis.Module; import redis.clients.jedis.RedisProtocol; import redis.clients.jedis.commands.ProtocolCommand; import redis.clients.jedis.util.SafeEncoder; import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.equalTo; import static org.hamcrest.Matchers.hasItem; import static org.hamcrest.Matchers.hasProperty; import static org.hamcrest.Matchers.not; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertTrue; @ParameterizedClass @MethodSource("redis.clients.jedis.commands.CommandsTestsParameters#respVersions") @ConditionalOnEnv(value = TestEnvUtil.ENV_OSS_SOURCE, enabled = true) public class ModuleTest extends JedisCommandsTestBase { enum ModuleCommand implements ProtocolCommand { SIMPLE("testmodule.simple"); private final byte[] raw; ModuleCommand(String alt) { raw = SafeEncoder.encode(alt); } @Override public byte[] getRaw() { return raw; } } public ModuleTest(RedisProtocol protocol) { super(protocol); } @Test public void testModules() { try { assertEquals("OK", jedis.moduleLoad(TestEnvUtil.testModuleSoPath())); List modules = jedis.moduleList(); assertThat(modules, hasItem(hasProperty("name", equalTo("testmodule")))); Object output = jedis.sendCommand(ModuleCommand.SIMPLE); assertTrue((Long) output > 0); } finally { assertEquals("OK", jedis.moduleUnload("testmodule")); List modules = jedis.moduleList(); assertThat(modules, not(hasItem(hasProperty("name", equalTo("testmodule"))))); } } } ================================================ FILE: src/test/java/redis/clients/jedis/commands/jedis/ObjectCommandsTest.java ================================================ package redis.clients.jedis.commands.jedis; import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.*; import static org.junit.jupiter.api.Assertions.assertThrows; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertNotNull; import static org.junit.jupiter.api.Assertions.assertNull; import java.util.List; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.junit.jupiter.params.ParameterizedClass; import org.junit.jupiter.params.provider.MethodSource; import redis.clients.jedis.*; import redis.clients.jedis.exceptions.JedisDataException; import redis.clients.jedis.util.SafeEncoder; @ParameterizedClass @MethodSource("redis.clients.jedis.commands.CommandsTestsParameters#respVersions") public class ObjectCommandsTest extends JedisCommandsTestBase { private final String key = "mylist"; private final byte[] binaryKey = SafeEncoder.encode(key); private static EndpointConfig lfuEndpoint; private Jedis lfuJedis; @BeforeAll public static void prepareEndpoints() { lfuEndpoint = Endpoints.getRedisEndpoint("standalone7-with-lfu-policy"); } public ObjectCommandsTest(RedisProtocol protocol) { super(protocol); } @BeforeEach @Override public void setUp() throws Exception { super.setUp(); lfuJedis = new Jedis(lfuEndpoint.getHostAndPort(), lfuEndpoint.getClientConfigBuilder().build()); lfuJedis.connect(); lfuJedis.flushAll(); } @AfterEach @Override public void tearDown() throws Exception { lfuJedis.disconnect(); super.tearDown(); } @Test public void objectRefcount() { jedis.lpush(key, "hello world"); Long refcount = jedis.objectRefcount(key); assertEquals(Long.valueOf(1), refcount); // Binary refcount = jedis.objectRefcount(binaryKey); assertEquals(Long.valueOf(1), refcount); } @Test public void objectEncodingString() { jedis.set(key, "hello world"); assertThat(jedis.objectEncoding(key), containsString("str")); // Binary assertThat(SafeEncoder.encode(jedis.objectEncoding(binaryKey)), containsString("str")); } @Test public void objectEncodingList() { jedis.lpush(key, "hello world"); assertThat(jedis.objectEncoding(key), containsString("list")); // Binary assertThat(SafeEncoder.encode(jedis.objectEncoding(binaryKey)), containsString("list")); } @Test public void objectIdletime() throws InterruptedException { jedis.lpush(key, "hello world"); Long time = jedis.objectIdletime(key); assertThat(time, lessThanOrEqualTo(10L)); // Binary time = jedis.objectIdletime(binaryKey); assertThat(time, lessThanOrEqualTo(10L)); } @Test public void objectHelp() { // String List helpTexts = jedis.objectHelp(); assertNotNull(helpTexts); // Binary List helpBinaryTexts = jedis.objectHelpBinary(); assertNotNull(helpBinaryTexts); } @Test public void objectFreq() { lfuJedis.set(key, "test1"); lfuJedis.get(key); // String assertThat(lfuJedis.objectFreq(key), greaterThanOrEqualTo(1L)); // Binary assertThat(lfuJedis.objectFreq(binaryKey), greaterThanOrEqualTo(1L)); assertNull(lfuJedis.objectFreq("no_such_key")); jedis.set(key, "test2"); assertThrows(JedisDataException.class, () -> jedis.objectFreq(key), "Freq is only allowed with LFU policy"); } } ================================================ FILE: src/test/java/redis/clients/jedis/commands/jedis/PublishSubscribeCommandsTest.java ================================================ package redis.clients.jedis.commands.jedis; import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.hasItems; import static org.junit.jupiter.api.Assertions.assertArrayEquals; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertThrows; import static org.junit.jupiter.api.Assertions.assertTrue; import static org.junit.jupiter.api.Assertions.fail; import static org.junit.jupiter.api.Assumptions.assumeTrue; import static redis.clients.jedis.Protocol.Command.CLIENT; import java.io.IOException; import java.net.UnknownHostException; import java.util.ArrayList; import java.util.Collections; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.concurrent.CountDownLatch; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicBoolean; import java.util.function.Consumer; import io.redis.test.annotations.ConditionalOnEnv; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Timeout; import org.junit.jupiter.params.ParameterizedClass; import org.junit.jupiter.params.provider.MethodSource; import redis.clients.jedis.BinaryJedisPubSub; import redis.clients.jedis.Jedis; import redis.clients.jedis.JedisPubSub; import redis.clients.jedis.RedisProtocol; import redis.clients.jedis.exceptions.JedisException; import redis.clients.jedis.util.SafeEncoder; import redis.clients.jedis.util.TestEnvUtil; @ParameterizedClass @Timeout(value = 5, unit = TimeUnit.MINUTES) @MethodSource("redis.clients.jedis.commands.CommandsTestsParameters#respVersions") public class PublishSubscribeCommandsTest extends JedisCommandsTestBase { public PublishSubscribeCommandsTest(RedisProtocol protocol) { super(protocol); } private void publishOne(final String channel, final String message) { Thread t = new Thread(new Runnable() { public void run() { try ( Jedis j = createJedis()) { j.publish(channel, message); j.disconnect(); } catch (Exception ex) { // ignore } } }); t.start(); } @Test public void subscribe() throws InterruptedException { jedis.subscribe(new JedisPubSub() { public void onMessage(String channel, String message) { assertEquals("foo", channel); assertEquals("exit", message); unsubscribe(); } public void onSubscribe(String channel, int subscribedChannels) { assertEquals("foo", channel); assertEquals(1, subscribedChannels); // now that I'm subscribed... publish publishOne("foo", "exit"); } public void onUnsubscribe(String channel, int subscribedChannels) { assertEquals("foo", channel); assertEquals(0, subscribedChannels); } }, "foo"); } @Test public void pubSubChannels() { jedis.subscribe(new JedisPubSub() { private int count = 0; @Override public void onSubscribe(String channel, int subscribedChannels) { count++; // All channels are subscribed if (count == 3) { List activeChannels; try (Jedis otherJedis = createJedis()) { activeChannels = otherJedis.pubsubChannels(); } // Since we are utilizing sentinel for the tests, there is an additional // '__sentinel__:hello' channel that has subscribers and will be returned // from PUBSUB CHANNELS. assertThat(activeChannels, hasItems("testchan1", "testchan2", "testchan3")); unsubscribe(); } } }, "testchan1", "testchan2", "testchan3"); } @Test public void pubSubChannelsWithPattern() { jedis.subscribe(new JedisPubSub() { private int count = 0; @Override public void onSubscribe(String channel, int subscribedChannels) { count++; // All channels are subscribed if (count == 3) { List activeChannels; try (Jedis otherJedis = createJedis()) { activeChannels = otherJedis.pubsubChannels("test*"); } assertThat(activeChannels, hasItems("testchan1", "testchan2", "testchan3")); unsubscribe(); } } }, "testchan1", "testchan2", "testchan3"); } @Test public void pubSubChannelWithPingPong() throws InterruptedException { final CountDownLatch latchUnsubscribed = new CountDownLatch(1); final CountDownLatch latchReceivedPong = new CountDownLatch(1); jedis.subscribe(new JedisPubSub() { @Override public void onSubscribe(String channel, int subscribedChannels) { publishOne("testchan1", "hello"); } @Override public void onMessage(String channel, String message) { this.ping(); } @Override public void onPong(String pattern) { latchReceivedPong.countDown(); unsubscribe(); } @Override public void onUnsubscribe(String channel, int subscribedChannels) { latchUnsubscribed.countDown(); } }, "testchan1"); assertEquals(0L, latchReceivedPong.getCount()); assertEquals(0L, latchUnsubscribed.getCount()); } @Test public void pubSubChannelWithPingPongWithArgument() throws InterruptedException { final CountDownLatch latchUnsubscribed = new CountDownLatch(1); final CountDownLatch latchReceivedPong = new CountDownLatch(1); final List pongPatterns = new ArrayList<>(); jedis.subscribe(new JedisPubSub() { @Override public void onSubscribe(String channel, int subscribedChannels) { publishOne("testchan1", "hello"); } @Override public void onMessage(String channel, String message) { this.ping("hi!"); } @Override public void onPong(String pattern) { pongPatterns.add(pattern); latchReceivedPong.countDown(); unsubscribe(); } @Override public void onUnsubscribe(String channel, int subscribedChannels) { latchUnsubscribed.countDown(); } }, "testchan1"); assertEquals(0L, latchReceivedPong.getCount()); assertEquals(0L, latchUnsubscribed.getCount()); assertEquals(Collections.singletonList("hi!"), pongPatterns); } @Test public void pubSubNumPat() { jedis.psubscribe(new JedisPubSub() { private int count = 0; @Override public void onPSubscribe(String pattern, int subscribedChannels) { count++; if (count == 3) { Long numPatterns; try (Jedis otherJedis = createJedis()) { numPatterns = otherJedis.pubsubNumPat(); } assertEquals(Long.valueOf(2L), numPatterns); punsubscribe(); } } }, "test*", "test*", "chan*"); } @Test public void pubSubNumSub() { final Map expectedNumSub = new HashMap<>(); expectedNumSub.put("testchannel2", 1L); expectedNumSub.put("testchannel1", 1L); jedis.subscribe(new JedisPubSub() { private int count = 0; @Override public void onSubscribe(String channel, int subscribedChannels) { count++; if (count == 2) { Map numSub; try (Jedis otherJedis = createJedis()) { numSub = otherJedis.pubsubNumSub("testchannel1", "testchannel2"); } assertEquals(expectedNumSub, numSub); unsubscribe(); } } }, "testchannel1", "testchannel2"); } @Test public void subscribeMany() throws UnknownHostException, IOException, InterruptedException { jedis.subscribe(new JedisPubSub() { public void onMessage(String channel, String message) { unsubscribe(channel); } public void onSubscribe(String channel, int subscribedChannels) { publishOne(channel, "exit"); } }, "foo", "bar"); } @Test public void psubscribe() throws UnknownHostException, IOException, InterruptedException { jedis.psubscribe(new JedisPubSub() { public void onPSubscribe(String pattern, int subscribedChannels) { assertEquals("foo.*", pattern); assertEquals(1, subscribedChannels); publishOne("foo.bar", "exit"); } public void onPUnsubscribe(String pattern, int subscribedChannels) { assertEquals("foo.*", pattern); assertEquals(0, subscribedChannels); } public void onPMessage(String pattern, String channel, String message) { assertEquals("foo.*", pattern); assertEquals("foo.bar", channel); assertEquals("exit", message); punsubscribe(); } }, "foo.*"); } @Test public void psubscribeMany() throws UnknownHostException, IOException, InterruptedException { jedis.psubscribe(new JedisPubSub() { public void onPSubscribe(String pattern, int subscribedChannels) { publishOne(pattern.replace("*", "123"), "exit"); } public void onPMessage(String pattern, String channel, String message) { punsubscribe(pattern); } }, "foo.*", "bar.*"); } @Test public void subscribeLazily() throws UnknownHostException, IOException, InterruptedException { final JedisPubSub pubsub = new JedisPubSub() { public void onMessage(String channel, String message) { unsubscribe(channel); } public void onSubscribe(String channel, int subscribedChannels) { publishOne(channel, "exit"); if (!channel.equals("bar")) { this.subscribe("bar"); this.psubscribe("bar.*"); } } public void onPSubscribe(String pattern, int subscribedChannels) { publishOne(pattern.replace("*", "123"), "exit"); } public void onPMessage(String pattern, String channel, String message) { punsubscribe(pattern); } }; jedis.subscribe(pubsub, "foo"); } @Test public void binarySubscribe() throws UnknownHostException, IOException, InterruptedException { jedis.subscribe(new BinaryJedisPubSub() { public void onMessage(byte[] channel, byte[] message) { assertArrayEquals(SafeEncoder.encode("foo"), channel); assertArrayEquals(SafeEncoder.encode("exit"), message); unsubscribe(); } public void onSubscribe(byte[] channel, int subscribedChannels) { assertArrayEquals(SafeEncoder.encode("foo"), channel); assertEquals(1, subscribedChannels); publishOne(SafeEncoder.encode(channel), "exit"); } public void onUnsubscribe(byte[] channel, int subscribedChannels) { assertArrayEquals(SafeEncoder.encode("foo"), channel); assertEquals(0, subscribedChannels); } }, SafeEncoder.encode("foo")); } @Test public void binarySubscribeMany() throws UnknownHostException, IOException, InterruptedException { jedis.subscribe(new BinaryJedisPubSub() { public void onMessage(byte[] channel, byte[] message) { unsubscribe(channel); } public void onSubscribe(byte[] channel, int subscribedChannels) { publishOne(SafeEncoder.encode(channel), "exit"); } }, SafeEncoder.encode("foo"), SafeEncoder.encode("bar")); } @Test public void binaryPsubscribe() throws UnknownHostException, IOException, InterruptedException { jedis.psubscribe(new BinaryJedisPubSub() { public void onPSubscribe(byte[] pattern, int subscribedChannels) { assertArrayEquals(SafeEncoder.encode("foo.*"), pattern); assertEquals(1, subscribedChannels); publishOne(SafeEncoder.encode(pattern).replace("*", "bar"), "exit"); } public void onPUnsubscribe(byte[] pattern, int subscribedChannels) { assertArrayEquals(SafeEncoder.encode("foo.*"), pattern); assertEquals(0, subscribedChannels); } public void onPMessage(byte[] pattern, byte[] channel, byte[] message) { assertArrayEquals(SafeEncoder.encode("foo.*"), pattern); assertArrayEquals(SafeEncoder.encode("foo.bar"), channel); assertArrayEquals(SafeEncoder.encode("exit"), message); punsubscribe(); } }, SafeEncoder.encode("foo.*")); } @Test public void binaryPsubscribeMany() throws UnknownHostException, IOException, InterruptedException { jedis.psubscribe(new BinaryJedisPubSub() { public void onPSubscribe(byte[] pattern, int subscribedChannels) { publishOne(SafeEncoder.encode(pattern).replace("*", "123"), "exit"); } public void onPMessage(byte[] pattern, byte[] channel, byte[] message) { punsubscribe(pattern); } }, SafeEncoder.encode("foo.*"), SafeEncoder.encode("bar.*")); } @Test public void binaryPubSubChannelWithPingPong() throws InterruptedException { final CountDownLatch latchUnsubscribed = new CountDownLatch(1); final CountDownLatch latchReceivedPong = new CountDownLatch(1); jedis.subscribe(new BinaryJedisPubSub() { @Override public void onSubscribe(byte[] channel, int subscribedChannels) { publishOne("testchan1", "hello"); } @Override public void onMessage(byte[] channel, byte[] message) { this.ping(); } @Override public void onPong(byte[] pattern) { latchReceivedPong.countDown(); unsubscribe(); } @Override public void onUnsubscribe(byte[] channel, int subscribedChannels) { latchUnsubscribed.countDown(); } }, SafeEncoder.encode("testchan1")); assertEquals(0L, latchReceivedPong.getCount()); assertEquals(0L, latchUnsubscribed.getCount()); } @Test public void binaryPubSubChannelWithPingPongWithArgument() throws InterruptedException { final CountDownLatch latchUnsubscribed = new CountDownLatch(1); final CountDownLatch latchReceivedPong = new CountDownLatch(1); final List pongPatterns = new ArrayList<>(); final byte[] pingMessage = SafeEncoder.encode("hi!"); jedis.subscribe(new BinaryJedisPubSub() { @Override public void onSubscribe(byte[] channel, int subscribedChannels) { publishOne("testchan1", "hello"); } @Override public void onMessage(byte[] channel, byte[] message) { this.ping(pingMessage); } @Override public void onPong(byte[] pattern) { pongPatterns.add(pattern); latchReceivedPong.countDown(); unsubscribe(); } @Override public void onUnsubscribe(byte[] channel, int subscribedChannels) { latchUnsubscribed.countDown(); } }, SafeEncoder.encode("testchan1")); assertEquals(0L, latchReceivedPong.getCount()); assertEquals(0L, latchUnsubscribed.getCount()); assertArrayEquals(pingMessage, pongPatterns.get(0)); } @Test public void binarySubscribeLazily() throws UnknownHostException, IOException, InterruptedException { final BinaryJedisPubSub pubsub = new BinaryJedisPubSub() { public void onMessage(byte[] channel, byte[] message) { unsubscribe(channel); } public void onSubscribe(byte[] channel, int subscribedChannels) { publishOne(SafeEncoder.encode(channel), "exit"); if (!SafeEncoder.encode(channel).equals("bar")) { this.subscribe(SafeEncoder.encode("bar")); this.psubscribe(SafeEncoder.encode("bar.*")); } } public void onPSubscribe(byte[] pattern, int subscribedChannels) { publishOne(SafeEncoder.encode(pattern).replace("*", "123"), "exit"); } public void onPMessage(byte[] pattern, byte[] channel, byte[] message) { punsubscribe(pattern); } }; jedis.subscribe(pubsub, SafeEncoder.encode("foo")); } @Test public void unsubscribeWhenNotSusbscribed() throws InterruptedException { JedisPubSub pubsub = new JedisPubSub() { }; assertThrows(JedisException.class, pubsub::unsubscribe); } @Test // NOTE(imalinovskyi): Pushing 100Mb over high latency network can take a lot of time, // so skipping for RE for now @ConditionalOnEnv(value = TestEnvUtil.ENV_REDIS_ENTERPRISE, enabled = false) public void handleClientOutputBufferLimitForSubscribeTooSlow() throws InterruptedException { assertThrows(JedisException.class, () -> { final Jedis j = createJedis(); final AtomicBoolean exit = new AtomicBoolean(false); final Thread t = new Thread(new Runnable() { public void run() { try { // we already set jedis1 config to // client-output-buffer-limit pubsub 256k 128k 5 // it means if subscriber delayed to receive over 256k or // 128k continuously 5 sec, // redis disconnects subscriber // we publish over 100M data for making situation for exceed // client-output-buffer-limit String veryLargeString = makeLargeString(10485760); // 10M * 10 = 100M for (int i = 0; i < 10 && !exit.get(); i++) { j.publish("foo", veryLargeString); } j.disconnect(); } catch (Exception ex) { } } }); t.start(); try { jedis.subscribe(new JedisPubSub() { public void onMessage(String channel, String message) { try { // wait 0.5 secs to slow down subscribe and // client-output-buffer exceed Thread.sleep(100); } catch (Exception e) { try { t.join(); } catch (InterruptedException e1) { } fail(e.getMessage()); } } }, "foo"); } finally { // exit the publisher thread. if exception is thrown, thread might // still keep publishing things. exit.set(true); if (t.isAlive()) { t.join(); } } }); } private String makeLargeString(int size) { StringBuffer sb = new StringBuffer(); for (int i = 0; i < size; i++) sb.append((char) ('a' + i % 26)); return sb.toString(); } @Test @Timeout(5) @ConditionalOnEnv(value = TestEnvUtil.ENV_REDIS_ENTERPRISE, enabled = false) public void subscribeCacheInvalidateChannel() { assumeTrue(protocol != RedisProtocol.RESP3); final String cacheInvalidate = "__redis__:invalidate"; final AtomicBoolean onMessage = new AtomicBoolean(false); final JedisPubSub pubsub = new JedisPubSub() { @Override public void onMessage(String channel, String message) { onMessage.set(true); assertEquals(cacheInvalidate, channel); if (message != null) { assertEquals("foo", message); consumeJedis(j -> j.flushAll()); } else { unsubscribe(channel); } } @Override public void onSubscribe(String channel, int subscribedChannels) { assertEquals(cacheInvalidate, channel); consumeJedis(j -> j.set("foo", "bar")); } }; try (Jedis subscriber = createJedis()) { long clientId = subscriber.clientId(); subscriber.sendCommand(CLIENT, "TRACKING", "ON", "REDIRECT", Long.toString(clientId), "BCAST"); subscriber.subscribe(pubsub, cacheInvalidate); assertTrue(onMessage.get(), "Subscriber didn't get any message."); } } @Test @Timeout(5) @ConditionalOnEnv(value = TestEnvUtil.ENV_REDIS_ENTERPRISE, enabled = false) public void subscribeCacheInvalidateChannelBinary() { assumeTrue(protocol != RedisProtocol.RESP3); final byte[] cacheInvalidate = "__redis__:invalidate".getBytes(); final AtomicBoolean onMessage = new AtomicBoolean(false); final BinaryJedisPubSub pubsub = new BinaryJedisPubSub() { @Override public void onMessage(byte[] channel, byte[] message) { onMessage.set(true); assertArrayEquals(cacheInvalidate, channel); if (message != null) { assertArrayEquals("foo".getBytes(), message); consumeJedis(j -> j.flushAll()); } else { unsubscribe(channel); } } @Override public void onSubscribe(byte[] channel, int subscribedChannels) { assertArrayEquals(cacheInvalidate, channel); consumeJedis(j -> j.set("foo".getBytes(), "bar".getBytes())); } }; try (Jedis subscriber = createJedis()) { long clientId = subscriber.clientId(); subscriber.sendCommand(CLIENT, "TRACKING", "ON", "REDIRECT", Long.toString(clientId), "BCAST"); subscriber.subscribe(pubsub, cacheInvalidate); assertTrue(onMessage.get(), "Subscriber didn't get any message."); } } private void consumeJedis(Consumer consumer) { Thread t = new Thread(() -> consumer.accept(jedis)); t.start(); } } ================================================ FILE: src/test/java/redis/clients/jedis/commands/jedis/ScriptingCommandsTest.java ================================================ package redis.clients.jedis.commands.jedis; import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; import java.util.List; import java.util.Map; import io.redis.test.annotations.SinceRedisVersion; import io.redis.test.annotations.ConditionalOnEnv; import io.redis.test.utils.RedisVersion; import org.hamcrest.MatcherAssert; import org.hamcrest.Matchers; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Tag; import org.junit.jupiter.params.ParameterizedClass; import org.junit.jupiter.params.provider.MethodSource; import redis.clients.jedis.Jedis; import redis.clients.jedis.RedisProtocol; import redis.clients.jedis.args.FlushMode; import redis.clients.jedis.args.FunctionRestorePolicy; import redis.clients.jedis.exceptions.JedisConnectionException; import redis.clients.jedis.exceptions.JedisDataException; import redis.clients.jedis.exceptions.JedisNoScriptException; import redis.clients.jedis.resps.FunctionStats; import redis.clients.jedis.resps.LibraryInfo; import redis.clients.jedis.util.*; import static org.junit.jupiter.api.Assertions.assertArrayEquals; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertNotNull; import static org.junit.jupiter.api.Assertions.assertNull; import static org.junit.jupiter.api.Assertions.assertThrows; import static org.junit.jupiter.api.Assertions.assertTrue; import static org.junit.jupiter.api.Assertions.fail; @ParameterizedClass @MethodSource("redis.clients.jedis.commands.CommandsTestsParameters#respVersions") @Tag("integration") public class ScriptingCommandsTest extends JedisCommandsTestBase { public ScriptingCommandsTest(RedisProtocol redisProtocol) { super(redisProtocol); } @BeforeEach @Override public void setUp() throws Exception { super.setUp(); if (RedisVersionUtil.getRedisVersion(jedis) .isGreaterThanOrEqualTo(RedisVersion.V7_0_0)) { jedis.functionFlush(); } } final byte[] bfoo = { 0x01, 0x02, 0x03, 0x04 }; final byte[] bfoo1 = { 0x01, 0x02, 0x03, 0x04, 0x0A }; final byte[] bfoo2 = { 0x01, 0x02, 0x03, 0x04, 0x0B }; final byte[] bfoo3 = { 0x01, 0x02, 0x03, 0x04, 0x0C }; final byte[] bbar = { 0x05, 0x06, 0x07, 0x08 }; final byte[] bbar1 = { 0x05, 0x06, 0x07, 0x08, 0x0A }; final byte[] bbar2 = { 0x05, 0x06, 0x07, 0x08, 0x0B }; final byte[] bbar3 = { 0x05, 0x06, 0x07, 0x08, 0x0C }; final byte[] bfoobar = { 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08 }; @SuppressWarnings("unchecked") @Test @ConditionalOnEnv(value = TestEnvUtil.ENV_REDIS_ENTERPRISE, enabled = false) public void evalMultiBulk() { String script = "return {KEYS[1],KEYS[2],ARGV[1],ARGV[2],ARGV[3]}"; List keys = new ArrayList(); keys.add("key1"); keys.add("key2"); List args = new ArrayList(); args.add("first"); args.add("second"); args.add("third"); List response = (List) jedis.eval(script, keys, args); assertEquals(5, response.size()); assertEquals("key1", response.get(0)); assertEquals("key2", response.get(1)); assertEquals("first", response.get(2)); assertEquals("second", response.get(3)); assertEquals("third", response.get(4)); } @SuppressWarnings("unchecked") @Test @ConditionalOnEnv(value = TestEnvUtil.ENV_REDIS_ENTERPRISE, enabled = false) public void evalMultiBulkWithBinaryJedis() { String script = "return {KEYS[1],KEYS[2],ARGV[1],ARGV[2],ARGV[3]}"; List keys = new ArrayList(); keys.add("key1".getBytes()); keys.add("key2".getBytes()); List args = new ArrayList(); args.add("first".getBytes()); args.add("second".getBytes()); args.add("third".getBytes()); List responses = (List) jedis.eval(script.getBytes(), keys, args); assertEquals(5, responses.size()); assertEquals("key1", new String(responses.get(0))); assertEquals("key2", new String(responses.get(1))); assertEquals("first", new String(responses.get(2))); assertEquals("second", new String(responses.get(3))); assertEquals("third", new String(responses.get(4))); } @Test public void evalBulk() { String script = "return KEYS[1]"; List keys = new ArrayList(); keys.add("key1"); List args = new ArrayList(); args.add("first"); String response = (String) jedis.eval(script, keys, args); assertEquals("key1", response); } @Test public void evalInt() { String script = "return 2"; List keys = new ArrayList(); keys.add("key1"); Long response = (Long) jedis.eval(script, keys, new ArrayList()); assertEquals(Long.valueOf(2), response); } @Test public void evalNestedLists() { String script = "return { {KEYS[1]} , {2} }"; List results = (List) jedis.eval(script, 1, "key1"); MatcherAssert.assertThat((List) results.get(0), Matchers.hasItem("key1")); MatcherAssert.assertThat((List) results.get(1), Matchers.hasItem(2L)); } @Test public void evalNoArgs() { String script = "return KEYS[1]"; List keys = new ArrayList(); keys.add("key1"); String response = (String) jedis.eval(script, keys, new ArrayList()); assertEquals("key1", response); } @Test @SinceRedisVersion(value = "7.0.0") public void evalReadonly() { String script = "return KEYS[1]"; List keys = new ArrayList(); keys.add("key1"); List args = new ArrayList(); args.add("first"); String response = (String) jedis.evalReadonly(script, keys, args); assertEquals("key1", response); } @Test public void evalsha() { jedis.set("foo", "bar"); jedis.eval("return redis.call('get','foo')"); String result = (String) jedis.evalsha("6b1bf486c81ceb7edf3c093f4c48582e38c0e791"); assertEquals("bar", result); } @Test @SinceRedisVersion(value = "7.0.0") public void evalshaReadonly() { jedis.set("foo", "bar"); jedis.eval("return redis.call('get','foo')"); String result = (String) jedis.evalshaReadonly("6b1bf486c81ceb7edf3c093f4c48582e38c0e791", Collections.emptyList(), Collections.emptyList()); assertEquals("bar", result); } @Test public void evalshaBinary() { jedis.set(SafeEncoder.encode("foo"), SafeEncoder.encode("bar")); jedis.eval(SafeEncoder.encode("return redis.call('get','foo')")); byte[] result = (byte[]) jedis.evalsha(SafeEncoder .encode("6b1bf486c81ceb7edf3c093f4c48582e38c0e791")); assertArrayEquals(SafeEncoder.encode("bar"), result); } @Test @SinceRedisVersion(value = "7.0.0") public void evalshaReadonlyBinary() { jedis.set(SafeEncoder.encode("foo"), SafeEncoder.encode("bar")); jedis.eval(SafeEncoder.encode("return redis.call('get','foo')")); byte[] result = (byte[]) jedis.evalshaReadonly(SafeEncoder.encode("6b1bf486c81ceb7edf3c093f4c48582e38c0e791"), Collections.emptyList(), Collections.emptyList()); assertArrayEquals(SafeEncoder.encode("bar"), result); } @Test public void evalshaShaNotFound() { assertThrows(JedisNoScriptException.class, () -> { jedis.evalsha("ffffffffffffffffffffffffffffffffffffffff"); }); } @Test public void scriptFlush() { jedis.set("foo", "bar"); jedis.eval("return redis.call('get','foo')"); jedis.scriptFlush(); assertFalse(jedis.scriptExists("6b1bf486c81ceb7edf3c093f4c48582e38c0e791")); } @Test public void scriptFlushMode() { jedis.set("foo", "bar"); jedis.eval("return redis.call('get','foo')"); String sha1 = "6b1bf486c81ceb7edf3c093f4c48582e38c0e791"; assertTrue(jedis.scriptExists(sha1)); jedis.scriptFlush(FlushMode.SYNC); assertFalse(jedis.scriptExists(sha1)); } @Test public void scriptExists() { jedis.scriptLoad("return redis.call('get','foo')"); List exists = jedis.scriptExists("ffffffffffffffffffffffffffffffffffffffff", "6b1bf486c81ceb7edf3c093f4c48582e38c0e791"); assertFalse(exists.get(0)); assertTrue(exists.get(1)); } @Test public void scriptExistsBinary() { jedis.scriptLoad(SafeEncoder.encode("return redis.call('get','foo')")); List exists = jedis.scriptExists( SafeEncoder.encode("ffffffffffffffffffffffffffffffffffffffff"), SafeEncoder.encode("6b1bf486c81ceb7edf3c093f4c48582e38c0e791")); assertFalse(exists.get(0)); assertTrue(exists.get(1)); } @Test public void scriptLoad() { jedis.scriptLoad("return redis.call('get','foo')"); assertTrue(jedis.scriptExists("6b1bf486c81ceb7edf3c093f4c48582e38c0e791")); } @Test public void scriptLoadBinary() { jedis.scriptLoad(SafeEncoder.encode("return redis.call('get','foo')")); assertTrue(jedis.scriptExists(SafeEncoder.encode("6b1bf486c81ceb7edf3c093f4c48582e38c0e791"))); } @Test public void scriptKill() { try { jedis.scriptKill(); } catch (JedisDataException e) { assertTrue(e.getMessage().contains("No scripts in execution right now.")); } } @Test @ConditionalOnEnv(value = TestEnvUtil.ENV_REDIS_ENTERPRISE, enabled = false) public void scriptEvalReturnNullValues() { jedis.del("key1"); jedis.del("key2"); String script = "return {redis.call('hget',KEYS[1],ARGV[1]),redis.call('hget',KEYS[2],ARGV[2])}"; List results = (List) jedis.eval(script, 2, "key1", "key2", "1", "2"); assertEquals(2, results.size()); assertNull(results.get(0)); assertNull(results.get(1)); } @Test @ConditionalOnEnv(value = TestEnvUtil.ENV_REDIS_ENTERPRISE, enabled = false) public void scriptEvalShaReturnNullValues() { jedis.del("key1"); jedis.del("key2"); String script = "return {redis.call('hget',KEYS[1],ARGV[1]),redis.call('hget',KEYS[2],ARGV[2])}"; String sha = jedis.scriptLoad(script); List results = (List) jedis.evalsha(sha, 2, "key1", "key2", "1", "2"); assertEquals(2, results.size()); assertNull(results.get(0)); assertNull(results.get(1)); } @Test @ConditionalOnEnv(value = TestEnvUtil.ENV_REDIS_ENTERPRISE, enabled = false) public void scriptEvalShaReturnValues() { jedis.hset("foo", "foo1", "bar1"); jedis.hset("foobar", "foo2", "bar2"); String script = "return {redis.call('hget',KEYS[1],ARGV[1]),redis.call('hget',KEYS[2],ARGV[2])}"; String sha = jedis.scriptLoad(script); List results = (List) jedis.evalsha(sha, Arrays.asList("foo", "foobar"), Arrays.asList("foo1", "foo2")); assertEquals(2, results.size()); assertEquals("bar1", results.get(0)); assertEquals("bar2", results.get(1)); } @Test @ConditionalOnEnv(value = TestEnvUtil.ENV_REDIS_ENTERPRISE, enabled = false) public void scriptEvalShaReturnValuesBinary() { jedis.hset(bfoo, bfoo1, bbar1); jedis.hset(bfoobar, bfoo2, bbar2); byte[] script = "return {redis.call('hget',KEYS[1],ARGV[1]),redis.call('hget',KEYS[2],ARGV[2])}".getBytes(); byte[] sha = jedis.scriptLoad(script); List results = (List) jedis.evalsha(sha, Arrays.asList(bfoo, bfoobar), Arrays.asList(bfoo1, bfoo2)); assertEquals(2, results.size()); assertArrayEquals(bbar1, results.get(0)); assertArrayEquals(bbar2, results.get(1)); } @Test public void scriptExistsWithBrokenConnection() { Jedis deadClient = createJedis(); deadClient.clientSetname("DEAD"); ClientKillerUtil.killClient(deadClient, "DEAD"); // sure, script doesn't exist, but it's just for checking connection try { deadClient.scriptExists("abcdefg"); } catch (JedisConnectionException e) { // ignore it } assertEquals(true, deadClient.isBroken()); deadClient.close(); } @Test public void emptyLuaTableReply() { Object reply = jedis.eval("return {}"); assertEquals(Collections.emptyList(), reply); } @Test @SinceRedisVersion(value = "7.0.0") public void functionLoadAndDelete() { String engine = "Lua"; String library = "mylib"; String function = "redis.register_function('myfunc', function(keys, args) return args[1] end)"; String functionCode = String.format("#!%s name=%s \n %s", engine, library, function); assertEquals(library, jedis.functionLoad(functionCode)); assertEquals(library, jedis.functionLoadReplace(functionCode)); assertEquals("OK", jedis.functionDelete(library)); // Binary assertEquals(library, jedis.functionLoad(functionCode.getBytes())); assertEquals(library, jedis.functionLoadReplace(functionCode.getBytes())); assertEquals("OK", jedis.functionDelete(library.getBytes())); } @Test @SinceRedisVersion(value = "7.0.0") public void functionFlush() { String engine = "Lua"; String library = "mylib"; String function = "redis.register_function('myfunc', function(keys, args) return args[1] end)"; String functionCode = String.format("#!%s name=%s \n %s", engine, library, function); assertEquals(library, jedis.functionLoad(functionCode)); jedis.functionFlush(); assertEquals(library, jedis.functionLoad(functionCode)); jedis.functionFlush(FlushMode.ASYNC); assertEquals(library, jedis.functionLoad(functionCode)); jedis.functionFlush(FlushMode.SYNC); } @Test @SinceRedisVersion(value = "7.0.0") public void functionList() { String engine = "LUA"; String library = "mylib"; String function = "redis.register_function('myfunc', function(keys, args) return args[1] end)"; String functionCode = String.format("#!%s name=%s \n %s", engine, library, function); jedis.functionLoad(functionCode); LibraryInfo response = jedis.functionList().get(0); assertEquals(library, response.getLibraryName()); assertEquals(engine, response.getEngine()); assertEquals(1, response.getFunctions().size()); // check function info Map func = response.getFunctions().get(0); assertEquals("myfunc", func.get("name")); assertNull(func.get("description")); assertTrue(((List) func.get("flags")).isEmpty()); // check WITHCODE response = jedis.functionListWithCode().get(0); assertEquals("myfunc", func.get("name")); assertEquals(functionCode, response.getLibraryCode()); // check with LIBRARYNAME response = jedis.functionList(library).get(0); assertEquals(library, response.getLibraryName()); // check with code and with LIBRARYNAME response = jedis.functionListWithCode(library).get(0); assertEquals(library, response.getLibraryName()); assertEquals(functionCode, response.getLibraryCode()); // Binary if (protocol != RedisProtocol.RESP3) { List bresponse = (List) jedis.functionListBinary().get(0); assertArrayEquals(library.getBytes(), (byte[]) bresponse.get(1)); bresponse = (List) jedis.functionListWithCodeBinary().get(0); assertArrayEquals(library.getBytes(), (byte[]) bresponse.get(1)); assertNotNull(bresponse.get(7)); bresponse = (List) jedis.functionList(library.getBytes()).get(0); assertArrayEquals(library.getBytes(), (byte[]) bresponse.get(1)); bresponse = (List) jedis.functionListWithCode(library.getBytes()).get(0); assertArrayEquals(library.getBytes(), (byte[]) bresponse.get(1)); assertNotNull(bresponse.get(7)); } else { List bresponse = (List) jedis.functionListBinary().get(0); assertArrayEquals(library.getBytes(), (byte[]) bresponse.get(0).getValue()); bresponse = (List) jedis.functionListWithCodeBinary().get(0); assertArrayEquals(library.getBytes(), (byte[]) bresponse.get(0).getValue()); assertNotNull(bresponse.get(3)); bresponse = (List) jedis.functionList(library.getBytes()).get(0); assertArrayEquals(library.getBytes(), (byte[]) bresponse.get(0).getValue()); bresponse = (List) jedis.functionListWithCode(library.getBytes()).get(0); assertArrayEquals(library.getBytes(), (byte[]) bresponse.get(0).getValue()); assertNotNull(bresponse.get(3)); } } @Test @SinceRedisVersion(value = "7.0.0") public void functionDumpRestore() { String engine = "Lua"; String library = "mylib"; String function = "redis.register_function('myfunc', function(keys, args) return args[1] end)"; jedis.functionLoad(String.format("#!%s name=%s \n %s", engine, library, function)); byte[] payload = jedis.functionDump(); jedis.functionFlush(); assertEquals("OK", jedis.functionRestore(payload)); jedis.functionFlush(); assertEquals("OK", jedis.functionRestore(payload, FunctionRestorePolicy.FLUSH)); jedis.functionFlush(); assertEquals("OK", jedis.functionRestore(payload, FunctionRestorePolicy.APPEND)); jedis.functionFlush(); assertEquals("OK", jedis.functionRestore(payload, FunctionRestorePolicy.REPLACE)); jedis.functionFlush(); } @Test @SinceRedisVersion(value = "7.0.0") public void functionStatsWithoutRunning() { String engine = "Lua"; String library = "mylib"; String function = "redis.register_function('myfunc', function(keys, args) return args[1] end)"; jedis.functionLoad(String.format("#!%s name=%s \n %s", engine, library, function)); FunctionStats stats = jedis.functionStats(); assertNull(stats.getRunningScript()); assertEquals(1, stats.getEngines().size()); } // // @Test // public void functionStatsWithRunning() throws InterruptedException { // jedis.functionFlush(); // function = "redis.register_function('myfunc', function(keys, args)\n local a = 1 while true do a = a + 1 end \nend)"; // // jedis.functionLoad(String.format("#!%s name=%s \n %s", engine, library, function)); // jedis.fcall("myfunc", new ArrayList<>(), new ArrayList<>()); // stats = jedis.functionStats(); // assertNotNull(stats.getScript()); // assertEquals("myfunc", stats.getScript().getName()); // } // // @Test // public void functionKill() { // String engine = "Lua"; // String library = "mylib"; // String function = "redis.register_function('myfunc', function(keys, args)\n local a = 1 while true do a = a + 1 end \nend)"; // // jedis.functionLoad(String.format("#!%s name=%s \n %s", engine, library, function)); // jedis.fcall("myfunc", Collections.emptyList(), Collections.emptyList()); // assertEquals("OK", jedis.functionKill()); // } @Test @SinceRedisVersion(value = "7.0.0") public void functionKillWithoutRunningFunction() { String engine = "Lua"; String library = "mylib"; String function = "redis.register_function('myfunc', function(keys, args)\n local a = 1 while true do a = a + 1 end \nend)"; jedis.functionLoad(String.format("#!%s name=%s \n %s", engine, library, function)); try { jedis.functionKill(); fail("Should get NOTBUSY error."); } catch (JedisDataException jde) { assertEquals("NOTBUSY No scripts in execution right now.", jde.getMessage()); } } @Test @SinceRedisVersion(value = "7.0.0") public void fcall() { String engine = "Lua"; String library = "mylib"; String function = "redis.register_function('myfunc', function(keys, args) return args end)"; jedis.functionLoad(String.format("#!%s name=%s \n %s", engine, library, function)); List args = Arrays.asList("hello"); assertEquals(args, jedis.fcall("myfunc", Collections.emptyList(), args)); } @Test @SinceRedisVersion(value = "7.0.0") public void fcallBinary() { String engine = "Lua"; String library = "mylib"; String function = "redis.register_function('myfunc', function(keys, args) return args[1] end)"; jedis.functionLoad(String.format("#!%s name=%s \n %s", engine, library, function)); List bargs = Arrays.asList("hello".getBytes()); assertArrayEquals("hello".getBytes(), (byte[]) jedis.fcall("myfunc".getBytes(), Collections.emptyList(), bargs)); } @Test @SinceRedisVersion(value = "7.0.0") public void fcallReadonly() { String engine = "Lua"; String library = "mylib"; String function = "redis.register_function{function_name='noop', callback=function() return 1 end, flags={ 'no-writes' }}"; jedis.functionLoad(String.format("#!%s name=%s \n %s", engine, library, function)); assertEquals(Long.valueOf(1), jedis.fcallReadonly("noop", Collections.emptyList(), Collections.emptyList())); // Binary assertEquals(Long.valueOf(1), jedis.fcallReadonly("noop".getBytes(), Collections.emptyList(), Collections.emptyList())); } } ================================================ FILE: src/test/java/redis/clients/jedis/commands/jedis/SentinelCommandsTest.java ================================================ package redis.clients.jedis.commands.jedis; import static org.hamcrest.MatcherAssert.assertThat; import static org.junit.jupiter.api.Assertions.assertEquals; import java.util.Arrays; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Set; import java.util.stream.Collectors; import io.redis.test.annotations.ConditionalOnEnv; import org.hamcrest.Matchers; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Tag; import org.junit.jupiter.api.extension.RegisterExtension; import redis.clients.jedis.HostAndPort; import redis.clients.jedis.Jedis; import redis.clients.jedis.Endpoints; import redis.clients.jedis.util.EnvCondition; import redis.clients.jedis.util.TestEnvUtil; @Tag("integration") @ConditionalOnEnv(value = TestEnvUtil.ENV_REDIS_ENTERPRISE, enabled = false) public class SentinelCommandsTest { @RegisterExtension public static EnvCondition envCondition = new EnvCondition(); protected static final String MASTER_NAME = "mymaster"; protected static List nodes; protected static Set nodesPorts; protected static List sentinels2; @BeforeAll public static void prepare() throws Exception { nodes = Arrays.asList(Endpoints.getRedisEndpoint("standalone2-primary").getHostAndPort(), Endpoints.getRedisEndpoint("standalone3-replica-of-standalone2").getHostAndPort()); nodesPorts = nodes.stream().map(HostAndPort::getPort).map(String::valueOf) .collect(Collectors.toSet()); sentinels2 = Arrays.asList( Endpoints.getRedisEndpoint("sentinel-standalone2-1").getHostAndPort(), Endpoints.getRedisEndpoint("sentinel-standalone2-3").getHostAndPort()); } @Test public void myIdAndSentinels() { Map idToPort = new HashMap<>(); sentinels2.forEach((hap) -> { try (Jedis sentinel = new Jedis(hap)) { String id = sentinel.sentinelMyId(); assertThat(id, Matchers.not(Matchers.emptyOrNullString())); idToPort.put(id, hap.getPort()); } }); assertEquals(2, idToPort.size()); try (Jedis sentinel = new Jedis(sentinels2.stream().findAny().get())) { List> detailsList = sentinel.sentinelSentinels(MASTER_NAME); assertThat(detailsList, Matchers.not(Matchers.empty())); detailsList.forEach((details) -> assertEquals(idToPort.get(details.get("runid")), Integer.valueOf(details.get("port")))); } } @Test public void masterAndMasters() { String runId, port; try (Jedis sentinel = new Jedis(sentinels2.get(0))) { Map details = sentinel.sentinelMaster(MASTER_NAME); assertEquals(MASTER_NAME, details.get("name")); runId = details.get("runid"); port = details.get("port"); assertThat(port, Matchers.in(nodesPorts)); } try (Jedis sentinel2 = new Jedis(sentinels2.get(1))) { Map details = sentinel2.sentinelMasters().get(0); assertEquals(MASTER_NAME, details.get("name")); assertEquals(runId, details.get("runid")); assertEquals(port, details.get("port")); } } @Test public void replicas() { try (Jedis sentinel = new Jedis(sentinels2.stream().findAny().get())) { List> detailsList = sentinel.sentinelReplicas(MASTER_NAME); assertThat(detailsList, Matchers.not(Matchers.empty())); detailsList.forEach((details) -> assertThat(details.get("port"), Matchers.in(nodesPorts))); } } } ================================================ FILE: src/test/java/redis/clients/jedis/commands/jedis/SetCommandsTest.java ================================================ package redis.clients.jedis.commands.jedis; import static org.junit.jupiter.api.Assertions.assertArrayEquals; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertNull; import static org.junit.jupiter.api.Assertions.assertTrue; import static redis.clients.jedis.params.ScanParams.SCAN_POINTER_START; import static redis.clients.jedis.params.ScanParams.SCAN_POINTER_START_BINARY; import static redis.clients.jedis.util.AssertUtil.assertByteArrayCollectionContainsAll; import static redis.clients.jedis.util.AssertUtil.assertByteArraySetEquals; import static redis.clients.jedis.util.AssertUtil.assertCollectionContainsAll; import static redis.clients.jedis.util.ByteArrayUtil.byteArrayCollectionRemoveAll; import java.util.Arrays; import java.util.Comparator; import java.util.HashSet; import java.util.List; import java.util.Set; import io.redis.test.annotations.SinceRedisVersion; import io.redis.test.annotations.ConditionalOnEnv; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Tag; import org.junit.jupiter.params.ParameterizedClass; import org.junit.jupiter.params.provider.MethodSource; import redis.clients.jedis.RedisProtocol; import redis.clients.jedis.params.ScanParams; import redis.clients.jedis.resps.ScanResult; import redis.clients.jedis.util.TestEnvUtil; @ParameterizedClass @MethodSource("redis.clients.jedis.commands.CommandsTestsParameters#respVersions") @Tag("integration") public class SetCommandsTest extends JedisCommandsTestBase { final byte[] bfoo = { 0x01, 0x02, 0x03, 0x04 }; final byte[] bbar = { 0x05, 0x06, 0x07, 0x08 }; final byte[] bcar = { 0x09, 0x0A, 0x0B, 0x0C }; final byte[] ba = { 0x0A }; final byte[] bb = { 0x0B }; final byte[] bc = { 0x0C }; final byte[] bd = { 0x0D }; final byte[] bx = { 0x42 }; final byte[] bbar1 = { 0x05, 0x06, 0x07, 0x08, 0x0A }; final byte[] bbar2 = { 0x05, 0x06, 0x07, 0x08, 0x0B }; final byte[] bbar3 = { 0x05, 0x06, 0x07, 0x08, 0x0C }; final byte[] bbarstar = { 0x05, 0x06, 0x07, 0x08, '*' }; public SetCommandsTest(RedisProtocol protocol) { super(protocol); } @Test public void sadd() { long status = jedis.sadd("foo", "a"); assertEquals(1, status); status = jedis.sadd("foo", "a"); assertEquals(0, status); long bstatus = jedis.sadd(bfoo, ba); assertEquals(1, bstatus); bstatus = jedis.sadd(bfoo, ba); assertEquals(0, bstatus); } @Test public void smembers() { jedis.sadd("foo", "a"); jedis.sadd("foo", "b"); Set expected = new HashSet(); expected.add("a"); expected.add("b"); Set members = jedis.smembers("foo"); assertEquals(expected, members); // Binary jedis.sadd(bfoo, ba); jedis.sadd(bfoo, bb); Set bexpected = new HashSet(); bexpected.add(bb); bexpected.add(ba); Set bmembers = jedis.smembers(bfoo); assertByteArraySetEquals(bexpected, bmembers); } @Test public void srem() { jedis.sadd("foo", "a"); jedis.sadd("foo", "b"); long status = jedis.srem("foo", "a"); Set expected = new HashSet(); expected.add("b"); assertEquals(1, status); assertEquals(expected, jedis.smembers("foo")); status = jedis.srem("foo", "bar"); assertEquals(0, status); // Binary jedis.sadd(bfoo, ba); jedis.sadd(bfoo, bb); long bstatus = jedis.srem(bfoo, ba); Set bexpected = new HashSet(); bexpected.add(bb); assertEquals(1, bstatus); assertByteArraySetEquals(bexpected, jedis.smembers(bfoo)); bstatus = jedis.srem(bfoo, bbar); assertEquals(0, bstatus); } @Test public void spop() { jedis.sadd("foo", "a"); jedis.sadd("foo", "b"); String member = jedis.spop("foo"); assertTrue("a".equals(member) || "b".equals(member)); assertEquals(1, jedis.smembers("foo").size()); member = jedis.spop("bar"); assertNull(member); // Binary jedis.sadd(bfoo, ba); jedis.sadd(bfoo, bb); byte[] bmember = jedis.spop(bfoo); assertTrue(Arrays.equals(ba, bmember) || Arrays.equals(bb, bmember)); assertEquals(1, jedis.smembers(bfoo).size()); bmember = jedis.spop(bbar); assertNull(bmember); } @Test public void spopWithCount() { jedis.sadd("foo", "a"); jedis.sadd("foo", "b"); jedis.sadd("foo", "c"); Set superSet = new HashSet(); superSet.add("c"); superSet.add("b"); superSet.add("a"); Set members = jedis.spop("foo", 2); assertEquals(2, members.size()); assertCollectionContainsAll(superSet, members); superSet.removeAll(members); members = jedis.spop("foo", 2); assertEquals(1, members.size()); assertEquals(superSet, members); assertTrue(jedis.spop("foo", 2).isEmpty()); // Binary jedis.sadd(bfoo, ba); jedis.sadd(bfoo, bb); jedis.sadd(bfoo, bc); Set bsuperSet = new HashSet(); bsuperSet.add(bc); bsuperSet.add(bb); bsuperSet.add(ba); Set bmembers = jedis.spop(bfoo, 2); assertEquals(2, bmembers.size()); assertByteArrayCollectionContainsAll(bsuperSet, bmembers); byteArrayCollectionRemoveAll(bsuperSet, bmembers); bmembers = jedis.spop(bfoo, 2); assertEquals(1, bmembers.size()); assertByteArraySetEquals(bsuperSet, bmembers); assertTrue(jedis.spop(bfoo, 2).isEmpty()); } @Test @ConditionalOnEnv(value = TestEnvUtil.ENV_REDIS_ENTERPRISE, enabled = false) public void smove() { jedis.sadd("foo", "a"); jedis.sadd("foo", "b"); jedis.sadd("bar", "c"); long status = jedis.smove("foo", "bar", "a"); Set expectedSrc = new HashSet(); expectedSrc.add("b"); Set expectedDst = new HashSet(); expectedDst.add("c"); expectedDst.add("a"); assertEquals(status, 1); assertEquals(expectedSrc, jedis.smembers("foo")); assertEquals(expectedDst, jedis.smembers("bar")); status = jedis.smove("foo", "bar", "a"); assertEquals(status, 0); // Binary jedis.sadd(bfoo, ba); jedis.sadd(bfoo, bb); jedis.sadd(bbar, bc); long bstatus = jedis.smove(bfoo, bbar, ba); Set bexpectedSrc = new HashSet(); bexpectedSrc.add(bb); Set bexpectedDst = new HashSet(); bexpectedDst.add(bc); bexpectedDst.add(ba); assertEquals(bstatus, 1); assertByteArraySetEquals(bexpectedSrc, jedis.smembers(bfoo)); assertByteArraySetEquals(bexpectedDst, jedis.smembers(bbar)); bstatus = jedis.smove(bfoo, bbar, ba); assertEquals(bstatus, 0); } @Test public void scard() { jedis.sadd("foo", "a"); jedis.sadd("foo", "b"); long card = jedis.scard("foo"); assertEquals(2, card); card = jedis.scard("bar"); assertEquals(0, card); // Binary jedis.sadd(bfoo, ba); jedis.sadd(bfoo, bb); long bcard = jedis.scard(bfoo); assertEquals(2, bcard); bcard = jedis.scard(bbar); assertEquals(0, bcard); } @Test public void sismember() { jedis.sadd("foo", "a"); jedis.sadd("foo", "b"); assertTrue(jedis.sismember("foo", "a")); assertFalse(jedis.sismember("foo", "c")); // Binary jedis.sadd(bfoo, ba); jedis.sadd(bfoo, bb); assertTrue(jedis.sismember(bfoo, ba)); assertFalse(jedis.sismember(bfoo, bc)); } @Test public void smismember() { jedis.sadd("foo", "a", "b"); assertEquals(Arrays.asList(true, false), jedis.smismember("foo", "a", "c")); // Binary jedis.sadd(bfoo, ba, bb); assertEquals(Arrays.asList(true, false), jedis.smismember(bfoo, ba, bc)); } @Test @ConditionalOnEnv(value = TestEnvUtil.ENV_REDIS_ENTERPRISE, enabled = false) public void sinter() { jedis.sadd("foo", "a"); jedis.sadd("foo", "b"); jedis.sadd("bar", "b"); jedis.sadd("bar", "c"); Set expected = new HashSet(); expected.add("b"); Set intersection = jedis.sinter("foo", "bar"); assertEquals(expected, intersection); // Binary jedis.sadd(bfoo, ba); jedis.sadd(bfoo, bb); jedis.sadd(bbar, bb); jedis.sadd(bbar, bc); Set bexpected = new HashSet(); bexpected.add(bb); Set bintersection = jedis.sinter(bfoo, bbar); assertByteArraySetEquals(bexpected, bintersection); } @Test @ConditionalOnEnv(value = TestEnvUtil.ENV_REDIS_ENTERPRISE, enabled = false) public void sinterstore() { jedis.sadd("foo", "a"); jedis.sadd("foo", "b"); jedis.sadd("bar", "b"); jedis.sadd("bar", "c"); Set expected = new HashSet(); expected.add("b"); long status = jedis.sinterstore("car", "foo", "bar"); assertEquals(1, status); assertEquals(expected, jedis.smembers("car")); // Binary jedis.sadd(bfoo, ba); jedis.sadd(bfoo, bb); jedis.sadd(bbar, bb); jedis.sadd(bbar, bc); Set bexpected = new HashSet(); bexpected.add(bb); long bstatus = jedis.sinterstore(bcar, bfoo, bbar); assertEquals(1, bstatus); assertByteArraySetEquals(bexpected, jedis.smembers(bcar)); } @Test @SinceRedisVersion("7.0.0") @ConditionalOnEnv(value = TestEnvUtil.ENV_REDIS_ENTERPRISE, enabled = false) public void sintercard() { jedis.sadd("foo", "a"); jedis.sadd("foo", "b"); jedis.sadd("bar", "a"); jedis.sadd("bar", "b"); jedis.sadd("bar", "c"); long card = jedis.sintercard("foo", "bar"); assertEquals(2, card); long limitedCard = jedis.sintercard(1, "foo", "bar"); assertEquals(1, limitedCard); // Binary jedis.sadd(bfoo, ba); jedis.sadd(bfoo, bb); jedis.sadd(bbar, ba); jedis.sadd(bbar, bb); jedis.sadd(bbar, bc); long bcard = jedis.sintercard(bfoo, bbar); assertEquals(2, bcard); long blimitedCard = jedis.sintercard(1, bfoo, bbar); assertEquals(1, blimitedCard); } @Test @ConditionalOnEnv(value = TestEnvUtil.ENV_REDIS_ENTERPRISE, enabled = false) public void sunion() { jedis.sadd("foo", "a"); jedis.sadd("foo", "b"); jedis.sadd("bar", "b"); jedis.sadd("bar", "c"); Set expected = new HashSet(); expected.add("a"); expected.add("b"); expected.add("c"); Set union = jedis.sunion("foo", "bar"); assertEquals(expected, union); // Binary jedis.sadd(bfoo, ba); jedis.sadd(bfoo, bb); jedis.sadd(bbar, bb); jedis.sadd(bbar, bc); Set bexpected = new HashSet(); bexpected.add(bb); bexpected.add(bc); bexpected.add(ba); Set bunion = jedis.sunion(bfoo, bbar); assertByteArraySetEquals(bexpected, bunion); } @Test @ConditionalOnEnv(value = TestEnvUtil.ENV_REDIS_ENTERPRISE, enabled = false) public void sunionstore() { jedis.sadd("foo", "a"); jedis.sadd("foo", "b"); jedis.sadd("bar", "b"); jedis.sadd("bar", "c"); Set expected = new HashSet(); expected.add("a"); expected.add("b"); expected.add("c"); long status = jedis.sunionstore("car", "foo", "bar"); assertEquals(3, status); assertEquals(expected, jedis.smembers("car")); // Binary jedis.sadd(bfoo, ba); jedis.sadd(bfoo, bb); jedis.sadd(bbar, bb); jedis.sadd(bbar, bc); Set bexpected = new HashSet(); bexpected.add(bb); bexpected.add(bc); bexpected.add(ba); long bstatus = jedis.sunionstore(bcar, bfoo, bbar); assertEquals(3, bstatus); assertByteArraySetEquals(bexpected, jedis.smembers(bcar)); } @Test @ConditionalOnEnv(value = TestEnvUtil.ENV_REDIS_ENTERPRISE, enabled = false) public void sdiff() { jedis.sadd("foo", "x"); jedis.sadd("foo", "a"); jedis.sadd("foo", "b"); jedis.sadd("foo", "c"); jedis.sadd("bar", "c"); jedis.sadd("car", "a"); jedis.sadd("car", "d"); Set expected = new HashSet(); expected.add("x"); expected.add("b"); Set diff = jedis.sdiff("foo", "bar", "car"); assertEquals(expected, diff); // Binary jedis.sadd(bfoo, bx); jedis.sadd(bfoo, ba); jedis.sadd(bfoo, bb); jedis.sadd(bfoo, bc); jedis.sadd(bbar, bc); jedis.sadd(bcar, ba); jedis.sadd(bcar, bd); Set bexpected = new HashSet(); bexpected.add(bb); bexpected.add(bx); Set bdiff = jedis.sdiff(bfoo, bbar, bcar); assertByteArraySetEquals(bexpected, bdiff); } @Test @ConditionalOnEnv(value = TestEnvUtil.ENV_REDIS_ENTERPRISE, enabled = false) public void sdiffstore() { jedis.sadd("foo", "x"); jedis.sadd("foo", "a"); jedis.sadd("foo", "b"); jedis.sadd("foo", "c"); jedis.sadd("bar", "c"); jedis.sadd("car", "a"); jedis.sadd("car", "d"); Set expected = new HashSet(); expected.add("x"); expected.add("b"); long status = jedis.sdiffstore("tar", "foo", "bar", "car"); assertEquals(2, status); assertEquals(expected, jedis.smembers("tar")); // Binary jedis.sadd(bfoo, bx); jedis.sadd(bfoo, ba); jedis.sadd(bfoo, bb); jedis.sadd(bfoo, bc); jedis.sadd(bbar, bc); jedis.sadd(bcar, ba); jedis.sadd(bcar, bd); Set bexpected = new HashSet(); bexpected.add(bx); bexpected.add(bb); long bstatus = jedis.sdiffstore("tar".getBytes(), bfoo, bbar, bcar); assertEquals(2, bstatus); assertByteArraySetEquals(bexpected, jedis.smembers("tar".getBytes())); } @Test public void srandmember() { jedis.sadd("foo", "a"); jedis.sadd("foo", "b"); String member = jedis.srandmember("foo"); assertTrue("a".equals(member) || "b".equals(member)); assertEquals(2, jedis.smembers("foo").size()); List members = jedis.srandmember("foo", 2); members.sort(Comparator.naturalOrder()); assertEquals( Arrays.asList("a", "b"), members); member = jedis.srandmember("bar"); assertNull(member); members = jedis.srandmember("bar", 2); assertEquals(0, members.size()); // Binary jedis.sadd(bfoo, ba); jedis.sadd(bfoo, bb); byte[] bmember = jedis.srandmember(bfoo); assertTrue(Arrays.equals(ba, bmember) || Arrays.equals(bb, bmember)); assertEquals(2, jedis.smembers(bfoo).size()); List bmembers = jedis.srandmember(bfoo, 2); assertEquals(2, bmembers.size()); bmember = jedis.srandmember(bbar); assertNull(bmember); members = jedis.srandmember("bbar", 2); assertEquals(0, members.size()); } @Test public void sscan() { jedis.sadd("foo", "a", "b"); ScanResult result = jedis.sscan("foo", SCAN_POINTER_START); assertEquals(SCAN_POINTER_START, result.getCursor()); assertFalse(result.getResult().isEmpty()); // binary jedis.sadd(bfoo, ba, bb); ScanResult bResult = jedis.sscan(bfoo, SCAN_POINTER_START_BINARY); assertArrayEquals(SCAN_POINTER_START_BINARY, bResult.getCursorAsBytes()); assertFalse(bResult.getResult().isEmpty()); } @Test public void sscanMatch() { ScanParams params = new ScanParams(); params.match("a*"); jedis.sadd("foo", "b", "a", "aa"); ScanResult result = jedis.sscan("foo", SCAN_POINTER_START, params); assertEquals(SCAN_POINTER_START, result.getCursor()); assertFalse(result.getResult().isEmpty()); // binary params = new ScanParams(); params.match(bbarstar); jedis.sadd(bfoo, bbar1, bbar2, bbar3); ScanResult bResult = jedis.sscan(bfoo, SCAN_POINTER_START_BINARY, params); assertArrayEquals(SCAN_POINTER_START_BINARY, bResult.getCursorAsBytes()); assertFalse(bResult.getResult().isEmpty()); } @Test public void sscanCount() { ScanParams params = new ScanParams(); params.count(2); jedis.sadd("foo", "a1", "a2", "a3", "a4", "a5"); ScanResult result = jedis.sscan("foo", SCAN_POINTER_START, params); assertFalse(result.getResult().isEmpty()); // binary params = new ScanParams(); params.count(2); jedis.sadd(bfoo, bbar1, bbar2, bbar3); ScanResult bResult = jedis.sscan(bfoo, SCAN_POINTER_START_BINARY, params); assertFalse(bResult.getResult().isEmpty()); } } ================================================ FILE: src/test/java/redis/clients/jedis/commands/jedis/SlowlogCommandsTest.java ================================================ package redis.clients.jedis.commands.jedis; import static org.hamcrest.MatcherAssert.assertThat; import static org.junit.jupiter.api.Assertions.assertArrayEquals; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertNotNull; import java.net.InetAddress; import java.net.NetworkInterface; import java.net.SocketException; import java.util.*; import io.redis.test.annotations.ConditionalOnEnv; import org.hamcrest.Matchers; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.junit.jupiter.params.ParameterizedClass; import org.junit.jupiter.params.provider.MethodSource; import redis.clients.jedis.Protocol; import redis.clients.jedis.RedisProtocol; import redis.clients.jedis.resps.Slowlog; import redis.clients.jedis.util.SafeEncoder; import redis.clients.jedis.util.TestEnvUtil; @ParameterizedClass @MethodSource("redis.clients.jedis.commands.CommandsTestsParameters#respVersions") public class SlowlogCommandsTest extends JedisCommandsTestBase { private static final String SLOWLOG_TIME_PARAM = "slowlog-log-slower-than"; private static final String ZERO_STRING = "0"; private String slowlogTimeValue; public SlowlogCommandsTest(RedisProtocol protocol) { super(protocol); } @BeforeEach @Override public void setUp() throws Exception { super.setUp(); slowlogTimeValue = jedis.configGet(SLOWLOG_TIME_PARAM).get(SLOWLOG_TIME_PARAM); } @AfterEach @Override public void tearDown() throws Exception { jedis.configSet(SLOWLOG_TIME_PARAM, slowlogTimeValue); super.tearDown(); } @Test @ConditionalOnEnv(value = TestEnvUtil.ENV_REDIS_ENTERPRISE, enabled = false) public void slowlog() { jedis.configSet(SLOWLOG_TIME_PARAM, ZERO_STRING); jedis.slowlogReset(); jedis.set("foo", "bar"); jedis.set("foo2", "bar2"); List reducedLog = jedis.slowlogGet(1); assertEquals(1, reducedLog.size()); Slowlog log = reducedLog.get(0); assertThat(log.getId(), Matchers.greaterThan(0L)); assertThat(log.getTimeStamp(), Matchers.greaterThan(0L)); assertThat(log.getExecutionTime(), Matchers.greaterThanOrEqualTo(0L)); assertNotNull(log.getArgs()); List breducedLog = jedis.slowlogGetBinary(1); assertEquals(1, breducedLog.size()); List log1 = jedis.slowlogGet(); List blog1 = jedis.slowlogGetBinary(); assertNotNull(log1); assertNotNull(blog1); // assertEquals(7, jedis.slowlogLen()); assertThat(jedis.slowlogLen(), Matchers.allOf(Matchers.greaterThanOrEqualTo(6L), Matchers.lessThanOrEqualTo(13L))); assertThat(jedis.slowlogGet().toString(), Matchers.containsString("SLOWLOG")); } @Test @ConditionalOnEnv(value = TestEnvUtil.ENV_REDIS_ENTERPRISE, enabled = false) public void slowlogObjectDetails() { final String clientName = "slowlog-object-client-" + UUID.randomUUID(); jedis.clientSetname(clientName); jedis.slowlogReset(); jedis.configSet(SLOWLOG_TIME_PARAM, ZERO_STRING); List logs = jedis.slowlogGet(); // Get only 'CONFIG SET', or including 'SLOWLOG RESET' //assertEquals(1, logs.size()); assertThat(logs.size(), Matchers.allOf(Matchers.greaterThanOrEqualTo(1), Matchers.lessThanOrEqualTo(2))); Slowlog log = logs.get(logs.size() - 1); assertEquals(clientName, log.getClientName()); assertThat(log.getId(), Matchers.greaterThan(0L)); assertThat(log.getTimeStamp(), Matchers.greaterThan(0L)); assertThat(log.getExecutionTime(), Matchers.greaterThanOrEqualTo(0L)); assertEquals(4, log.getArgs().size()); assertEquals(SafeEncoder.encode(Protocol.Command.CONFIG.getRaw()), log.getArgs().get(0)); assertEquals(SafeEncoder.encode(Protocol.Keyword.SET.getRaw()), log.getArgs().get(1)); assertEquals(SLOWLOG_TIME_PARAM, log.getArgs().get(2)); assertEquals(ZERO_STRING, log.getArgs().get(3)); assertThat(log.getClientIpPort().getHost(), Matchers.in(getAllLocalIps())); assertThat(log.getClientIpPort().getPort(), Matchers.greaterThan(0)); } @Test @ConditionalOnEnv(value = TestEnvUtil.ENV_REDIS_ENTERPRISE, enabled = false) public void slowlogBinaryObjectDetails() { final byte[] clientName = SafeEncoder.encode("slowlog-binary-client"); jedis.clientSetname(clientName); jedis.slowlogReset(); jedis.configSet(SafeEncoder.encode(SLOWLOG_TIME_PARAM), SafeEncoder.encode(ZERO_STRING)); List logs = jedis.slowlogGetBinary(); // Get only 'CONFIG SET', or including 'SLOWLOG RESET' //assertEquals(1, logs.size()); assertThat(logs.size(), Matchers.allOf(Matchers.greaterThanOrEqualTo(1), Matchers.lessThanOrEqualTo(2))); List log = (List) logs.get(0); assertThat((Long) log.get(0), Matchers.greaterThan(0L)); assertThat((Long) log.get(1), Matchers.greaterThan(0L)); assertThat((Long) log.get(2), Matchers.greaterThanOrEqualTo(0L)); List args = (List) log.get(3); assertEquals(4, args.size()); assertArrayEquals(Protocol.Command.CONFIG.getRaw(), (byte[]) args.get(0)); assertArrayEquals(Protocol.Keyword.SET.getRaw(), (byte[]) args.get(1)); assertArrayEquals(SafeEncoder.encode(SLOWLOG_TIME_PARAM), (byte[]) args.get(2)); assertArrayEquals(Protocol.toByteArray(0), (byte[]) args.get(3)); // assertTrue(SafeEncoder.encode((byte[]) log.get(4)).startsWith("127.0.0.1:")); assertThat(((byte[]) log.get(4)).length, Matchers.greaterThanOrEqualTo(10)); // 'IP:PORT' assertArrayEquals(clientName, (byte[]) log.get(5)); } private static Set getAllLocalIps() { Set allLocalIps = new HashSet<>(); try { for (NetworkInterface netIf : Collections.list(NetworkInterface.getNetworkInterfaces())) { for (InetAddress addr : Collections.list(netIf.getInetAddresses())) { allLocalIps.add(addr.getHostAddress()); } } } catch (SocketException e) { throw new RuntimeException(e); } //ipv6 loopback allLocalIps.add("[::1]"); return allLocalIps; } } ================================================ FILE: src/test/java/redis/clients/jedis/commands/jedis/SortedSetCommandsTest.java ================================================ package redis.clients.jedis.commands.jedis; import static java.util.Collections.singletonList; import static org.junit.jupiter.api.Assertions.assertArrayEquals; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertNotNull; import static org.junit.jupiter.api.Assertions.assertNull; import static redis.clients.jedis.params.ScanParams.SCAN_POINTER_START; import static redis.clients.jedis.params.ScanParams.SCAN_POINTER_START_BINARY; import static redis.clients.jedis.util.AssertUtil.assertByteArrayListEquals; import java.util.*; import io.redis.test.annotations.SinceRedisVersion; import io.redis.test.annotations.ConditionalOnEnv; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Tag; import org.junit.jupiter.params.ParameterizedClass; import org.junit.jupiter.params.provider.MethodSource; import redis.clients.jedis.RedisProtocol; import redis.clients.jedis.args.SortedSetOption; import redis.clients.jedis.params.*; import redis.clients.jedis.resps.ScanResult; import redis.clients.jedis.resps.Tuple; import redis.clients.jedis.util.AssertUtil; import redis.clients.jedis.util.KeyValue; import redis.clients.jedis.util.SafeEncoder; import redis.clients.jedis.util.TestEnvUtil; @ParameterizedClass @MethodSource("redis.clients.jedis.commands.CommandsTestsParameters#respVersions") @Tag("integration") public class SortedSetCommandsTest extends JedisCommandsTestBase { final byte[] bfoo = { 0x01, 0x02, 0x03, 0x04 }; final byte[] bbar = { 0x05, 0x06, 0x07, 0x08 }; final byte[] bcar = { 0x09, 0x0A, 0x0B, 0x0C }; final byte[] ba = { 0x0A }; final byte[] bb = { 0x0B }; final byte[] bc = { 0x0C }; final byte[] bInclusiveB = { 0x5B, 0x0B }; final byte[] bExclusiveC = { 0x28, 0x0C }; final byte[] bLexMinusInf = { 0x2D }; final byte[] bLexPlusInf = { 0x2B }; final byte[] bbar1 = { 0x05, 0x06, 0x07, 0x08, 0x0A }; final byte[] bbar2 = { 0x05, 0x06, 0x07, 0x08, 0x0B }; final byte[] bbar3 = { 0x05, 0x06, 0x07, 0x08, 0x0C }; final byte[] bbarstar = { 0x05, 0x06, 0x07, 0x08, '*' }; public SortedSetCommandsTest(RedisProtocol protocol) { super(protocol); } @Test public void zadd() { assertEquals(1, jedis.zadd("foo", 1d, "a")); assertEquals(1, jedis.zadd("foo", 10d, "b")); assertEquals(1, jedis.zadd("foo", 0.1d, "c")); assertEquals(0, jedis.zadd("foo", 2d, "a")); // Binary assertEquals(1, jedis.zadd(bfoo, 1d, ba)); assertEquals(1, jedis.zadd(bfoo, 10d, bb)); assertEquals(1, jedis.zadd(bfoo, 0.1d, bc)); assertEquals(0, jedis.zadd(bfoo, 2d, ba)); } @Test public void zaddWithParams() { jedis.del("foo"); // xx: never add new member assertEquals(0L, jedis.zadd("foo", 1d, "a", ZAddParams.zAddParams().xx())); jedis.zadd("foo", 1d, "a"); // nx: never update current member assertEquals(0L, jedis.zadd("foo", 2d, "a", ZAddParams.zAddParams().nx())); assertEquals(Double.valueOf(1d), jedis.zscore("foo", "a")); Map scoreMembers = new HashMap(); scoreMembers.put("a", 2d); scoreMembers.put("b", 1d); // ch: return count of members not only added, but also updated assertEquals(2L, jedis.zadd("foo", scoreMembers, ZAddParams.zAddParams().ch())); // lt: only update existing elements if the new score is less than the current score. jedis.zadd("foo", 3d, "a", ZAddParams.zAddParams().lt()); assertEquals(Double.valueOf(2d), jedis.zscore("foo", "a")); jedis.zadd("foo", 1d, "a", ZAddParams.zAddParams().lt()); assertEquals(Double.valueOf(1d), jedis.zscore("foo", "a")); // gt: only update existing elements if the new score is greater than the current score. jedis.zadd("foo", 0d, "b", ZAddParams.zAddParams().gt()); assertEquals(Double.valueOf(1d), jedis.zscore("foo", "b")); jedis.zadd("foo", 2d, "b", ZAddParams.zAddParams().gt()); assertEquals(Double.valueOf(2d), jedis.zscore("foo", "b")); // incr: don't update already existing elements. assertNull(jedis.zaddIncr("foo", 1d, "b", ZAddParams.zAddParams().nx())); assertEquals(Double.valueOf(2d), jedis.zscore("foo", "b")); // incr: update elements that already exist. assertEquals(Double.valueOf(3d), jedis.zaddIncr("foo", 1d,"b", ZAddParams.zAddParams().xx())); assertEquals(Double.valueOf(3d), jedis.zscore("foo", "b")); // binary jedis.del(bfoo); // xx: never add new member assertEquals(0L, jedis.zadd(bfoo, 1d, ba, ZAddParams.zAddParams().xx())); jedis.zadd(bfoo, 1d, ba); // nx: never update current member assertEquals(0L, jedis.zadd(bfoo, 2d, ba, ZAddParams.zAddParams().nx())); assertEquals(Double.valueOf(1d), jedis.zscore(bfoo, ba)); Map binaryScoreMembers = new HashMap(); binaryScoreMembers.put(ba, 2d); binaryScoreMembers.put(bb, 1d); // ch: return count of members not only added, but also updated assertEquals(2L, jedis.zadd(bfoo, binaryScoreMembers, ZAddParams.zAddParams().ch())); // lt: only update existing elements if the new score is less than the current score. jedis.zadd(bfoo, 3d, ba, ZAddParams.zAddParams().lt()); assertEquals(Double.valueOf(2d), jedis.zscore(bfoo, ba)); jedis.zadd(bfoo, 1d, ba, ZAddParams.zAddParams().lt()); assertEquals(Double.valueOf(1d), jedis.zscore(bfoo, ba)); // gt: only update existing elements if the new score is greater than the current score. jedis.zadd(bfoo, 0d, bb, ZAddParams.zAddParams().gt()); assertEquals(Double.valueOf(1d), jedis.zscore(bfoo, bb)); jedis.zadd(bfoo, 2d, bb, ZAddParams.zAddParams().gt()); assertEquals(Double.valueOf(2d), jedis.zscore(bfoo, bb)); // incr: don't update already existing elements. assertNull(jedis.zaddIncr(bfoo, 1d, bb, ZAddParams.zAddParams().nx())); assertEquals(Double.valueOf(2d), jedis.zscore(bfoo, bb)); // incr: update elements that already exist. assertEquals(Double.valueOf(3d), jedis.zaddIncr(bfoo, 1d, bb, ZAddParams.zAddParams().xx())); assertEquals(Double.valueOf(3d), jedis.zscore(bfoo, bb)); } @Test public void zrange() { jedis.zadd("foo", 1d, "a"); jedis.zadd("foo", 10d, "b"); jedis.zadd("foo", 0.1d, "c"); jedis.zadd("foo", 2d, "a"); List expected = new ArrayList(); expected.add("c"); expected.add("a"); List range = jedis.zrange("foo", 0, 1); assertEquals(expected, range); expected.add("b"); range = jedis.zrange("foo", 0, 100); assertEquals(expected, range); // Binary jedis.zadd(bfoo, 1d, ba); jedis.zadd(bfoo, 10d, bb); jedis.zadd(bfoo, 0.1d, bc); jedis.zadd(bfoo, 2d, ba); List bexpected = new ArrayList(); bexpected.add(bc); bexpected.add(ba); List brange = jedis.zrange(bfoo, 0, 1); assertByteArrayListEquals(bexpected, brange); bexpected.add(bb); brange = jedis.zrange(bfoo, 0, 100); assertByteArrayListEquals(bexpected, brange); } @Test public void zrangeByLex() { jedis.zadd("foo", 1, "aa"); jedis.zadd("foo", 1, "c"); jedis.zadd("foo", 1, "bb"); jedis.zadd("foo", 1, "d"); List expected = new ArrayList(); expected.add("bb"); expected.add("c"); // exclusive aa ~ inclusive c assertEquals(expected, jedis.zrangeByLex("foo", "(aa", "[c")); expected.clear(); expected.add("bb"); expected.add("c"); // with LIMIT assertEquals(expected, jedis.zrangeByLex("foo", "-", "+", 1, 2)); } @Test public void zrangeByLexBinary() { // binary jedis.zadd(bfoo, 1, ba); jedis.zadd(bfoo, 1, bc); jedis.zadd(bfoo, 1, bb); List bExpected = new ArrayList(); bExpected.add(bb); assertByteArrayListEquals(bExpected, jedis.zrangeByLex(bfoo, bInclusiveB, bExclusiveC)); bExpected.clear(); bExpected.add(ba); bExpected.add(bb); // with LIMIT assertByteArrayListEquals(bExpected, jedis.zrangeByLex(bfoo, bLexMinusInf, bLexPlusInf, 0, 2)); } @Test public void zrevrangeByLex() { jedis.zadd("foo", 1, "aa"); jedis.zadd("foo", 1, "c"); jedis.zadd("foo", 1, "bb"); jedis.zadd("foo", 1, "d"); List expected = new ArrayList(); expected.add("c"); expected.add("bb"); // exclusive aa ~ inclusive c assertEquals(expected, jedis.zrevrangeByLex("foo", "[c", "(aa")); expected.clear(); expected.add("c"); expected.add("bb"); // with LIMIT assertEquals(expected, jedis.zrevrangeByLex("foo", "+", "-", 1, 2)); } @Test public void zrevrangeByLexBinary() { // binary jedis.zadd(bfoo, 1, ba); jedis.zadd(bfoo, 1, bc); jedis.zadd(bfoo, 1, bb); List bExpected = new ArrayList(); bExpected.add(bb); assertByteArrayListEquals(bExpected, jedis.zrevrangeByLex(bfoo, bExclusiveC, bInclusiveB)); bExpected.clear(); bExpected.add(bc); bExpected.add(bb); // with LIMIT assertByteArrayListEquals(bExpected, jedis.zrevrangeByLex(bfoo, bLexPlusInf, bLexMinusInf, 0, 2)); } @Test public void zrevrange() { jedis.zadd("foo", 1d, "a"); jedis.zadd("foo", 10d, "b"); jedis.zadd("foo", 0.1d, "c"); jedis.zadd("foo", 2d, "a"); List expected = new ArrayList(); expected.add("b"); expected.add("a"); List range = jedis.zrevrange("foo", 0, 1); assertEquals(expected, range); expected.add("c"); range = jedis.zrevrange("foo", 0, 100); assertEquals(expected, range); // Binary jedis.zadd(bfoo, 1d, ba); jedis.zadd(bfoo, 10d, bb); jedis.zadd(bfoo, 0.1d, bc); jedis.zadd(bfoo, 2d, ba); List bexpected = new ArrayList(); bexpected.add(bb); bexpected.add(ba); List brange = jedis.zrevrange(bfoo, 0, 1); assertByteArrayListEquals(bexpected, brange); bexpected.add(bc); brange = jedis.zrevrange(bfoo, 0, 100); assertByteArrayListEquals(bexpected, brange); } @Test public void zrem() { jedis.zadd("foo", 1d, "a"); jedis.zadd("foo", 2d, "b"); assertEquals(1, jedis.zrem("foo", "a")); List expected = new ArrayList(); expected.add("b"); assertEquals(expected, jedis.zrange("foo", 0, 100)); assertEquals(0, jedis.zrem("foo", "bar")); // Binary jedis.zadd(bfoo, 1d, ba); jedis.zadd(bfoo, 2d, bb); assertEquals(1, jedis.zrem(bfoo, ba)); List bexpected = new ArrayList(); bexpected.add(bb); assertByteArrayListEquals(bexpected, jedis.zrange(bfoo, 0, 100)); assertEquals(0, jedis.zrem(bfoo, bbar)); } @Test public void zincrby() { jedis.zadd("foo", 1d, "a"); jedis.zadd("foo", 2d, "b"); assertEquals(3d, jedis.zincrby("foo", 2d, "a"), 0); List expected = new ArrayList(); expected.add("b"); expected.add("a"); assertEquals(expected, jedis.zrange("foo", 0, 100)); // Binary jedis.zadd(bfoo, 1d, ba); jedis.zadd(bfoo, 2d, bb); assertEquals(3d, jedis.zincrby(bfoo, 2d, ba), 0); List bexpected = new ArrayList(); bexpected.add(bb); bexpected.add(ba); assertByteArrayListEquals(bexpected, jedis.zrange(bfoo, 0, 100)); } @Test public void zincrbyWithParams() { jedis.del("foo"); // xx: never add new member assertNull(jedis.zincrby("foo", 2d, "a", ZIncrByParams.zIncrByParams().xx())); jedis.zadd("foo", 2d, "a"); // nx: never update current member assertNull(jedis.zincrby("foo", 1d, "a", ZIncrByParams.zIncrByParams().nx())); assertEquals(Double.valueOf(2d), jedis.zscore("foo", "a")); // Binary jedis.del(bfoo); // xx: never add new member assertNull(jedis.zincrby(bfoo, 2d, ba, ZIncrByParams.zIncrByParams().xx())); jedis.zadd(bfoo, 2d, ba); // nx: never update current member assertNull(jedis.zincrby(bfoo, 1d, ba, ZIncrByParams.zIncrByParams().nx())); assertEquals(Double.valueOf(2d), jedis.zscore(bfoo, ba)); } @Test public void zrank() { jedis.zadd("foo", 1d, "a"); jedis.zadd("foo", 2d, "b"); long rank = jedis.zrank("foo", "a"); assertEquals(0, rank); rank = jedis.zrank("foo", "b"); assertEquals(1, rank); assertNull(jedis.zrank("car", "b")); // Binary jedis.zadd(bfoo, 1d, ba); jedis.zadd(bfoo, 2d, bb); long brank = jedis.zrank(bfoo, ba); assertEquals(0, brank); brank = jedis.zrank(bfoo, bb); assertEquals(1, brank); assertNull(jedis.zrank(bcar, bb)); } @Test @SinceRedisVersion("7.2.0") public void zrankWithScore() { jedis.zadd("foo", 1d, "a"); jedis.zadd("foo", 2d, "b"); KeyValue keyValue = jedis.zrankWithScore("foo", "a"); assertEquals(Long.valueOf(0), keyValue.getKey()); assertEquals(Double.valueOf(1d), keyValue.getValue()); keyValue = jedis.zrankWithScore("foo", "b"); assertEquals(Long.valueOf(1), keyValue.getKey()); assertEquals(Double.valueOf(2d), keyValue.getValue()); assertNull(jedis.zrankWithScore("car", "b")); // Binary jedis.zadd(bfoo, 1d, ba); jedis.zadd(bfoo, 2d, bb); keyValue = jedis.zrankWithScore(bfoo, ba); assertEquals(Long.valueOf(0), keyValue.getKey()); assertEquals(Double.valueOf(1d), keyValue.getValue()); keyValue = jedis.zrankWithScore(bfoo, bb); assertEquals(Long.valueOf(1), keyValue.getKey()); assertEquals(Double.valueOf(2d), keyValue.getValue()); assertNull(jedis.zrankWithScore(bcar, bb)); } @Test public void zrevrank() { jedis.zadd("foo", 1d, "a"); jedis.zadd("foo", 2d, "b"); long rank = jedis.zrevrank("foo", "a"); assertEquals(1, rank); rank = jedis.zrevrank("foo", "b"); assertEquals(0, rank); // Binary jedis.zadd(bfoo, 1d, ba); jedis.zadd(bfoo, 2d, bb); long brank = jedis.zrevrank(bfoo, ba); assertEquals(1, brank); brank = jedis.zrevrank(bfoo, bb); assertEquals(0, brank); } @Test public void zrangeWithScores() { jedis.zadd("foo", 1d, "a"); jedis.zadd("foo", 10d, "b"); jedis.zadd("foo", 0.1d, "c"); jedis.zadd("foo", 2d, "a"); List expected = new ArrayList(); expected.add(new Tuple("c", 0.1d)); expected.add(new Tuple("a", 2d)); List range = jedis.zrangeWithScores("foo", 0, 1); assertEquals(expected, range); expected.add(new Tuple("b", 10d)); range = jedis.zrangeWithScores("foo", 0, 100); assertEquals(expected, range); // Binary jedis.zadd(bfoo, 1d, ba); jedis.zadd(bfoo, 10d, bb); jedis.zadd(bfoo, 0.1d, bc); jedis.zadd(bfoo, 2d, ba); List bexpected = new ArrayList(); bexpected.add(new Tuple(bc, 0.1d)); bexpected.add(new Tuple(ba, 2d)); List brange = jedis.zrangeWithScores(bfoo, 0, 1); assertEquals(bexpected, brange); bexpected.add(new Tuple(bb, 10d)); brange = jedis.zrangeWithScores(bfoo, 0, 100); assertEquals(bexpected, brange); } @Test public void zrevrangeWithScores() { jedis.zadd("foo", 1d, "a"); jedis.zadd("foo", 10d, "b"); jedis.zadd("foo", 0.1d, "c"); jedis.zadd("foo", 2d, "a"); List expected = new ArrayList(); expected.add(new Tuple("b", 10d)); expected.add(new Tuple("a", 2d)); List range = jedis.zrevrangeWithScores("foo", 0, 1); assertEquals(expected, range); expected.add(new Tuple("c", 0.1d)); range = jedis.zrevrangeWithScores("foo", 0, 100); assertEquals(expected, range); // Binary jedis.zadd(bfoo, 1d, ba); jedis.zadd(bfoo, 10d, bb); jedis.zadd(bfoo, 0.1d, bc); jedis.zadd(bfoo, 2d, ba); List bexpected = new ArrayList(); bexpected.add(new Tuple(bb, 10d)); bexpected.add(new Tuple(ba, 2d)); List brange = jedis.zrevrangeWithScores(bfoo, 0, 1); assertEquals(bexpected, brange); bexpected.add(new Tuple(bc, 0.1d)); brange = jedis.zrevrangeWithScores(bfoo, 0, 100); assertEquals(bexpected, brange); } @Test public void zcard() { jedis.zadd("foo", 1d, "a"); jedis.zadd("foo", 10d, "b"); jedis.zadd("foo", 0.1d, "c"); jedis.zadd("foo", 2d, "a"); assertEquals(3, jedis.zcard("foo")); // Binary jedis.zadd(bfoo, 1d, ba); jedis.zadd(bfoo, 10d, bb); jedis.zadd(bfoo, 0.1d, bc); jedis.zadd(bfoo, 2d, ba); assertEquals(3, jedis.zcard(bfoo)); } @Test public void zscore() { jedis.zadd("foo", 1d, "a"); jedis.zadd("foo", 10d, "b"); jedis.zadd("foo", 0.1d, "c"); jedis.zadd("foo", 2d, "a"); assertEquals((Double) 10d, jedis.zscore("foo", "b")); assertEquals((Double) 0.1d, jedis.zscore("foo", "c")); assertNull(jedis.zscore("foo", "s")); // Binary jedis.zadd(bfoo, 1d, ba); jedis.zadd(bfoo, 10d, bb); jedis.zadd(bfoo, 0.1d, bc); jedis.zadd(bfoo, 2d, ba); assertEquals((Double) 10d, jedis.zscore(bfoo, bb)); assertEquals((Double) 0.1d, jedis.zscore(bfoo, bc)); assertNull(jedis.zscore(bfoo, SafeEncoder.encode("s"))); } @Test public void zmscore() { jedis.zadd("foo", 1d, "a"); jedis.zadd("foo", 10d, "b"); jedis.zadd("foo", 0.1d, "c"); jedis.zadd("foo", 2d, "a"); assertEquals(Arrays.asList(10d, 0.1d, null), jedis.zmscore("foo", "b", "c", "s")); // Binary jedis.zadd(bfoo, 1d, ba); jedis.zadd(bfoo, 10d, bb); jedis.zadd(bfoo, 0.1d, bc); jedis.zadd(bfoo, 2d, ba); assertEquals(Arrays.asList(10d, 0.1d, null), jedis.zmscore(bfoo, bb, bc, SafeEncoder.encode("s"))); } @Test public void zpopmax() { jedis.zadd("foo", 1d, "a"); jedis.zadd("foo", 10d, "b"); jedis.zadd("foo", 0.1d, "c"); jedis.zadd("foo", 2d, "d"); Tuple actual = jedis.zpopmax("foo"); Tuple expected = new Tuple("b", 10d); assertEquals(expected, actual); actual = jedis.zpopmax("foo"); expected = new Tuple("d", 2d); assertEquals(expected, actual); actual = jedis.zpopmax("foo"); expected = new Tuple("a", 1d); assertEquals(expected, actual); actual = jedis.zpopmax("foo"); expected = new Tuple("c", 0.1d); assertEquals(expected, actual); // Empty actual = jedis.zpopmax("foo"); assertNull(actual); // Binary jedis.zadd(bfoo, 1d, ba); jedis.zadd(bfoo, 10d, bb); jedis.zadd(bfoo, 0.1d, bc); jedis.zadd(bfoo, 2d, ba); // First actual = jedis.zpopmax(bfoo); expected = new Tuple(bb, 10d); assertEquals(expected, actual); // Second actual = jedis.zpopmax(bfoo); expected = new Tuple(ba, 2d); assertEquals(expected, actual); // Third actual = jedis.zpopmax(bfoo); expected = new Tuple(bc, 0.1d); assertEquals(expected, actual); // Empty actual = jedis.zpopmax(bfoo); assertNull(actual); } @Test public void zpopmaxWithCount() { jedis.zadd("foo", 1d, "a"); jedis.zadd("foo", 10d, "b"); jedis.zadd("foo", 0.1d, "c"); jedis.zadd("foo", 2d, "d"); jedis.zadd("foo", 0.03, "e"); List actual = jedis.zpopmax("foo", 2); assertEquals(2, actual.size()); List expected = new ArrayList(); expected.add(new Tuple("b", 10d)); expected.add(new Tuple("d", 2d)); assertEquals(expected, actual); actual = jedis.zpopmax("foo", 3); assertEquals(3, actual.size()); expected.clear(); expected.add(new Tuple("a", 1d)); expected.add(new Tuple("c", 0.1d)); expected.add(new Tuple("e", 0.03d)); assertEquals(expected, actual); // Empty actual = jedis.zpopmax("foo", 1); expected.clear(); assertEquals(expected, actual); // Binary jedis.zadd(bfoo, 1d, ba); jedis.zadd(bfoo, 10d, bb); jedis.zadd(bfoo, 0.1d, bc); jedis.zadd(bfoo, 2d, ba); // First actual = jedis.zpopmax(bfoo, 1); expected.clear(); expected.add(new Tuple(bb, 10d)); assertEquals(expected, actual); // Second actual = jedis.zpopmax(bfoo, 1); expected.clear(); expected.add(new Tuple(ba, 2d)); assertEquals(expected, actual); // Last 2 (just 1, because 1 was overwritten) actual = jedis.zpopmax(bfoo, 1); expected.clear(); expected.add(new Tuple(bc, 0.1d)); assertEquals(expected, actual); // Empty actual = jedis.zpopmax(bfoo, 1); expected.clear(); assertEquals(expected, actual); } @Test public void zpopmin() { jedis.zadd("foo", 1d, "a", ZAddParams.zAddParams().nx()); jedis.zadd("foo", 10d, "b", ZAddParams.zAddParams().nx()); jedis.zadd("foo", 0.1d, "c", ZAddParams.zAddParams().nx()); jedis.zadd("foo", 2d, "a", ZAddParams.zAddParams().nx()); List range = jedis.zpopmin("foo", 2); List expected = new ArrayList(); expected.add(new Tuple("c", 0.1d)); expected.add(new Tuple("a", 1d)); assertEquals(expected, range); assertEquals(new Tuple("b", 10d), jedis.zpopmin("foo")); // Binary jedis.zadd(bfoo, 1d, ba); jedis.zadd(bfoo, 10d, bb); jedis.zadd(bfoo, 0.1d, bc); jedis.zadd(bfoo, 2d, ba); List brange = jedis.zpopmin(bfoo, 2); List bexpected = new ArrayList(); bexpected.add(new Tuple(bc, 0.1d)); bexpected.add(new Tuple(ba, 2d)); assertEquals(bexpected, brange); assertEquals(new Tuple(bb, 10d), jedis.zpopmin(bfoo)); } @Test public void zcount() { jedis.zadd("foo", 1d, "a"); jedis.zadd("foo", 10d, "b"); jedis.zadd("foo", 0.1d, "c"); jedis.zadd("foo", 2d, "a"); assertEquals(2, jedis.zcount("foo", 0.01d, 2.1d)); assertEquals(3, jedis.zcount("foo", "(0.01", "+inf")); // Binary jedis.zadd(bfoo, 1d, ba); jedis.zadd(bfoo, 10d, bb); jedis.zadd(bfoo, 0.1d, bc); jedis.zadd(bfoo, 2d, ba); assertEquals(2, jedis.zcount(bfoo, 0.01d, 2.1d)); assertEquals(3, jedis.zcount(bfoo, SafeEncoder.encode("(0.01"), SafeEncoder.encode("+inf"))); } @Test public void zlexcount() { jedis.zadd("foo", 1, "a"); jedis.zadd("foo", 1, "b"); jedis.zadd("foo", 1, "c"); jedis.zadd("foo", 1, "aa"); assertEquals(2, jedis.zlexcount("foo", "[aa", "(c")); assertEquals(4, jedis.zlexcount("foo", "-", "+")); assertEquals(3, jedis.zlexcount("foo", "-", "(c")); assertEquals(3, jedis.zlexcount("foo", "[aa", "+")); } @Test public void zlexcountBinary() { // Binary jedis.zadd(bfoo, 1, ba); jedis.zadd(bfoo, 1, bc); jedis.zadd(bfoo, 1, bb); assertEquals(1, jedis.zlexcount(bfoo, bInclusiveB, bExclusiveC)); assertEquals(3, jedis.zlexcount(bfoo, bLexMinusInf, bLexPlusInf)); } @Test public void zrangebyscore() { jedis.zadd("foo", 1d, "a"); jedis.zadd("foo", 10d, "b"); jedis.zadd("foo", 0.1d, "c"); jedis.zadd("foo", 2d, "a"); List range = jedis.zrangeByScore("foo", 0d, 2d); List expected = new ArrayList(); expected.add("c"); expected.add("a"); assertEquals(expected, range); range = jedis.zrangeByScore("foo", 0d, 2d, 0, 1); expected = new ArrayList(); expected.add("c"); assertEquals(expected, range); range = jedis.zrangeByScore("foo", 0d, 2d, 1, 1); List range2 = jedis.zrangeByScore("foo", "-inf", "(2"); assertEquals(expected, range2); expected = new ArrayList(); expected.add("a"); assertEquals(expected, range); // Binary jedis.zadd(bfoo, 1d, ba); jedis.zadd(bfoo, 10d, bb); jedis.zadd(bfoo, 0.1d, bc); jedis.zadd(bfoo, 2d, ba); List brange = jedis.zrangeByScore(bfoo, 0d, 2d); List bexpected = new ArrayList(); bexpected.add(bc); bexpected.add(ba); assertByteArrayListEquals(bexpected, brange); brange = jedis.zrangeByScore(bfoo, 0d, 2d, 0, 1); bexpected = new ArrayList(); bexpected.add(bc); assertByteArrayListEquals(bexpected, brange); brange = jedis.zrangeByScore(bfoo, 0d, 2d, 1, 1); List brange2 = jedis.zrangeByScore(bfoo, SafeEncoder.encode("-inf"), SafeEncoder.encode("(2")); assertByteArrayListEquals(bexpected, brange2); bexpected = new ArrayList(); bexpected.add(ba); assertByteArrayListEquals(bexpected, brange); } @Test public void zrevrangebyscore() { jedis.zadd("foo", 1.0d, "a"); jedis.zadd("foo", 2.0d, "b"); jedis.zadd("foo", 3.0d, "c"); jedis.zadd("foo", 4.0d, "d"); jedis.zadd("foo", 5.0d, "e"); List range = jedis.zrevrangeByScore("foo", 3d, Double.NEGATIVE_INFINITY, 0, 1); List expected = new ArrayList(); expected.add("c"); assertEquals(expected, range); range = jedis.zrevrangeByScore("foo", 3.5d, Double.NEGATIVE_INFINITY, 0, 2); expected = new ArrayList(); expected.add("c"); expected.add("b"); assertEquals(expected, range); range = jedis.zrevrangeByScore("foo", 3.5d, Double.NEGATIVE_INFINITY, 1, 1); expected = new ArrayList(); expected.add("b"); assertEquals(expected, range); range = jedis.zrevrangeByScore("foo", 4d, 2d); expected = new ArrayList(); expected.add("d"); expected.add("c"); expected.add("b"); assertEquals(expected, range); range = jedis.zrevrangeByScore("foo", "4", "2", 0, 2); expected = new ArrayList(); expected.add("d"); expected.add("c"); assertEquals(expected, range); range = jedis.zrevrangeByScore("foo", "+inf", "(4"); expected = new ArrayList(); expected.add("e"); assertEquals(expected, range); // Binary jedis.zadd(bfoo, 1d, ba); jedis.zadd(bfoo, 10d, bb); jedis.zadd(bfoo, 0.1d, bc); jedis.zadd(bfoo, 2d, ba); List brange = jedis.zrevrangeByScore(bfoo, 2d, 0d); List bexpected = new ArrayList(); bexpected.add(ba); bexpected.add(bc); assertByteArrayListEquals(bexpected, brange); brange = jedis.zrevrangeByScore(bfoo, 2d, 0d, 0, 1); bexpected = new ArrayList(); bexpected.add(ba); assertByteArrayListEquals(bexpected, brange); List brange2 = jedis.zrevrangeByScore(bfoo, SafeEncoder.encode("+inf"), SafeEncoder.encode("(2")); bexpected = new ArrayList(); bexpected.add(bb); assertByteArrayListEquals(bexpected, brange2); brange = jedis.zrevrangeByScore(bfoo, 2d, 0d, 1, 1); bexpected = new ArrayList(); bexpected.add(bc); assertByteArrayListEquals(bexpected, brange); } @Test public void zrangebyscoreWithScores() { jedis.zadd("foo", 1d, "a"); jedis.zadd("foo", 10d, "b"); jedis.zadd("foo", 0.1d, "c"); jedis.zadd("foo", 2d, "a"); List range = jedis.zrangeByScoreWithScores("foo", 0d, 2d); List expected = new ArrayList(); expected.add(new Tuple("c", 0.1d)); expected.add(new Tuple("a", 2d)); assertEquals(expected, range); range = jedis.zrangeByScoreWithScores("foo", 0d, 2d, 0, 1); expected = new ArrayList(); expected.add(new Tuple("c", 0.1d)); assertEquals(expected, range); range = jedis.zrangeByScoreWithScores("foo", 0d, 2d, 1, 1); expected = new ArrayList(); expected.add(new Tuple("a", 2d)); assertEquals(expected, range); // Binary jedis.zadd(bfoo, 1d, ba); jedis.zadd(bfoo, 10d, bb); jedis.zadd(bfoo, 0.1d, bc); jedis.zadd(bfoo, 2d, ba); List brange = jedis.zrangeByScoreWithScores(bfoo, 0d, 2d); List bexpected = new ArrayList(); bexpected.add(new Tuple(bc, 0.1d)); bexpected.add(new Tuple(ba, 2d)); assertEquals(bexpected, brange); brange = jedis.zrangeByScoreWithScores(bfoo, 0d, 2d, 0, 1); bexpected = new ArrayList(); bexpected.add(new Tuple(bc, 0.1d)); assertEquals(bexpected, brange); brange = jedis.zrangeByScoreWithScores(bfoo, 0d, 2d, 1, 1); bexpected = new ArrayList(); bexpected.add(new Tuple(ba, 2d)); assertEquals(bexpected, brange); } @Test public void zrangeParams() { jedis.zadd("foo", 1, "aa"); jedis.zadd("foo", 1, "c"); jedis.zadd("foo", 1, "bb"); jedis.zadd("foo", 1, "d"); List expected = new ArrayList(); expected.add("c"); expected.add("bb"); assertEquals(expected, jedis.zrange("foo", ZRangeParams.zrangeByLexParams("[c", "(aa").rev())); assertNotNull(jedis.zrangeWithScores("foo", ZRangeParams.zrangeByScoreParams(0, 1))); // Binary jedis.zadd(bfoo, 1, ba); jedis.zadd(bfoo, 1, bc); jedis.zadd(bfoo, 1, bb); List bExpected = new ArrayList(); bExpected.add(bb); assertByteArrayListEquals(bExpected, jedis.zrange(bfoo, ZRangeParams.zrangeByLexParams(bExclusiveC, bInclusiveB).rev())); assertNotNull(jedis.zrangeWithScores(bfoo, ZRangeParams.zrangeByScoreParams(0, 1).limit(0, 3))); } @Test public void zrangeParamsLongMinMax() { long min = 0; long max = 1; jedis.zadd("foo", 1, "a"); jedis.zadd("foo", 2, "b"); List expected = new ArrayList(); expected.add("b"); expected.add("a"); assertEquals(expected, jedis.zrange("foo", ZRangeParams.zrangeParams(min, max).rev())); } @Test @ConditionalOnEnv(value = TestEnvUtil.ENV_REDIS_ENTERPRISE, enabled = false) public void zrangestore() { jedis.zadd("foo", 1, "aa"); jedis.zadd("foo", 2, "c"); jedis.zadd("foo", 3, "bb"); long stored = jedis.zrangestore("bar", "foo", ZRangeParams.zrangeByScoreParams(1, 2)); assertEquals(2, stored); List range = jedis.zrange("bar", 0, -1); List expected = new ArrayList<>(); expected.add("aa"); expected.add("c"); assertEquals(expected, range); // Binary jedis.zadd(bfoo, 1d, ba); jedis.zadd(bfoo, 10d, bb); jedis.zadd(bfoo, 0.1d, bc); jedis.zadd(bfoo, 2d, ba); long bstored = jedis.zrangestore(bbar, bfoo, ZRangeParams.zrangeParams(0, 1).rev()); assertEquals(2, bstored); List brange = jedis.zrevrange(bbar, 0, 1); List bexpected = new ArrayList<>(); bexpected.add(bb); bexpected.add(ba); assertByteArrayListEquals(bexpected, brange); } @Test public void zrevrangebyscoreWithScores() { jedis.zadd("foo", 1.0d, "a"); jedis.zadd("foo", 2.0d, "b"); jedis.zadd("foo", 3.0d, "c"); jedis.zadd("foo", 4.0d, "d"); jedis.zadd("foo", 5.0d, "e"); List range = jedis.zrevrangeByScoreWithScores("foo", 3d, Double.NEGATIVE_INFINITY, 0, 1); List expected = new ArrayList(); expected.add(new Tuple("c", 3.0d)); assertEquals(expected, range); range = jedis.zrevrangeByScoreWithScores("foo", 3.5d, Double.NEGATIVE_INFINITY, 0, 2); expected = new ArrayList(); expected.add(new Tuple("c", 3.0d)); expected.add(new Tuple("b", 2.0d)); assertEquals(expected, range); range = jedis.zrevrangeByScoreWithScores("foo", 3.5d, Double.NEGATIVE_INFINITY, 1, 1); expected = new ArrayList(); expected.add(new Tuple("b", 2.0d)); assertEquals(expected, range); range = jedis.zrevrangeByScoreWithScores("foo", 4d, 2d); expected = new ArrayList(); expected.add(new Tuple("d", 4.0d)); expected.add(new Tuple("c", 3.0d)); expected.add(new Tuple("b", 2.0d)); assertEquals(expected, range); // Binary jedis.zadd(bfoo, 1d, ba); jedis.zadd(bfoo, 10d, bb); jedis.zadd(bfoo, 0.1d, bc); jedis.zadd(bfoo, 2d, ba); List brange = jedis.zrevrangeByScoreWithScores(bfoo, 2d, 0d); List bexpected = new ArrayList(); bexpected.add(new Tuple(ba, 2d)); bexpected.add(new Tuple(bc, 0.1d)); assertEquals(bexpected, brange); brange = jedis.zrevrangeByScoreWithScores(bfoo, 2d, 0d, 0, 1); bexpected = new ArrayList(); bexpected.add(new Tuple(ba, 2d)); assertEquals(bexpected, brange); brange = jedis.zrevrangeByScoreWithScores(bfoo, 2d, 0d, 1, 1); bexpected = new ArrayList(); bexpected.add(new Tuple(bc, 0.1d)); assertEquals(bexpected, brange); } @Test public void zremrangeByRank() { jedis.zadd("foo", 1d, "a"); jedis.zadd("foo", 10d, "b"); jedis.zadd("foo", 0.1d, "c"); jedis.zadd("foo", 2d, "a"); assertEquals(1, jedis.zremrangeByRank("foo", 0, 0)); List expected = new ArrayList(); expected.add("a"); expected.add("b"); assertEquals(expected, jedis.zrange("foo", 0, 100)); // Binary jedis.zadd(bfoo, 1d, ba); jedis.zadd(bfoo, 10d, bb); jedis.zadd(bfoo, 0.1d, bc); jedis.zadd(bfoo, 2d, ba); assertEquals(1, jedis.zremrangeByRank(bfoo, 0, 0)); List bexpected = new ArrayList(); bexpected.add(ba); bexpected.add(bb); assertByteArrayListEquals(bexpected, jedis.zrange(bfoo, 0, 100)); } @Test public void zremrangeByScore() { jedis.zadd("foo", 1d, "a"); jedis.zadd("foo", 10d, "b"); jedis.zadd("foo", 0.1d, "c"); jedis.zadd("foo", 2d, "a"); assertEquals(2, jedis.zremrangeByScore("foo", 0, 2)); List expected = new ArrayList(); expected.add("b"); assertEquals(expected, jedis.zrange("foo", 0, 100)); // Binary jedis.zadd(bfoo, 1d, ba); jedis.zadd(bfoo, 10d, bb); jedis.zadd(bfoo, 0.1d, bc); jedis.zadd(bfoo, 2d, ba); assertEquals(2, jedis.zremrangeByScore(bfoo, 0, 2)); List bexpected = new ArrayList(); bexpected.add(bb); assertByteArrayListEquals(bexpected, jedis.zrange(bfoo, 0, 100)); } @Test public void zremrangeByScoreExclusive() { jedis.zadd("foo", 1d, "a"); jedis.zadd("foo", 0d, "c"); jedis.zadd("foo", 2d, "b"); assertEquals(1, jedis.zremrangeByScore("foo", "(0", "(2")); // Binary jedis.zadd(bfoo, 1d, ba); jedis.zadd(bfoo, 0d, bc); jedis.zadd(bfoo, 2d, bb); assertEquals(1, jedis.zremrangeByScore(bfoo, "(0".getBytes(), "(2".getBytes())); } @Test public void zremrangeByLex() { jedis.zadd("foo", 1, "a"); jedis.zadd("foo", 1, "b"); jedis.zadd("foo", 1, "c"); jedis.zadd("foo", 1, "aa"); assertEquals(2, jedis.zremrangeByLex("foo", "[aa", "(c")); List expected = new ArrayList(); expected.add("a"); expected.add("c"); assertEquals(expected, jedis.zrangeByLex("foo", "-", "+")); } @Test public void zremrangeByLexBinary() { jedis.zadd(bfoo, 1, ba); jedis.zadd(bfoo, 1, bc); jedis.zadd(bfoo, 1, bb); assertEquals(1, jedis.zremrangeByLex(bfoo, bInclusiveB, bExclusiveC)); List bexpected = new ArrayList(); bexpected.add(ba); bexpected.add(bc); assertByteArrayListEquals(bexpected, jedis.zrangeByLex(bfoo, bLexMinusInf, bLexPlusInf)); } @Test @ConditionalOnEnv(value = TestEnvUtil.ENV_REDIS_ENTERPRISE, enabled = false) public void zunion() { jedis.zadd("foo", 1, "a"); jedis.zadd("foo", 2, "b"); jedis.zadd("bar", 2, "a"); jedis.zadd("bar", 2, "b"); ZParams params = new ZParams(); params.weights(2, 2.5); params.aggregate(ZParams.Aggregate.SUM); List expected = new ArrayList<>(); expected.add("a"); expected.add("b"); assertEquals(expected, jedis.zunion(params, "foo", "bar")); List expectedTuple = new ArrayList<>(); expectedTuple.add(new Tuple("a", new Double(7))); expectedTuple.add(new Tuple("b", new Double(9))); assertEquals(expectedTuple, jedis.zunionWithScores(params, "foo", "bar")); // Binary jedis.zadd(bfoo, 1, ba); jedis.zadd(bfoo, 2, bb); jedis.zadd(bbar, 2, ba); jedis.zadd(bbar, 2, bb); List bexpected = new ArrayList<>(); bexpected.add(ba); bexpected.add(bb); AssertUtil.assertByteArrayListEquals(bexpected, jedis.zunion(params, bfoo, bbar)); List bexpectedTuple = new ArrayList<>(); bexpectedTuple.add(new Tuple(ba, new Double(7))); bexpectedTuple.add(new Tuple(bb, new Double(9))); assertEquals(bexpectedTuple, jedis.zunionWithScores(params, bfoo, bbar)); } @Test @ConditionalOnEnv(value = TestEnvUtil.ENV_REDIS_ENTERPRISE, enabled = false) public void zunionstore() { jedis.zadd("foo", 1, "a"); jedis.zadd("foo", 2, "b"); jedis.zadd("bar", 2, "a"); jedis.zadd("bar", 2, "b"); assertEquals(2, jedis.zunionstore("dst", "foo", "bar")); List expected = new ArrayList(); expected.add(new Tuple("a", new Double(3))); expected.add(new Tuple("b", new Double(4))); assertEquals(expected, jedis.zrangeWithScores("dst", 0, 100)); // Binary jedis.zadd(bfoo, 1, ba); jedis.zadd(bfoo, 2, bb); jedis.zadd(bbar, 2, ba); jedis.zadd(bbar, 2, bb); assertEquals(2, jedis.zunionstore(SafeEncoder.encode("dst"), bfoo, bbar)); List bexpected = new ArrayList(); bexpected.add(new Tuple(ba, new Double(3))); bexpected.add(new Tuple(bb, new Double(4))); assertEquals(bexpected, jedis.zrangeWithScores(SafeEncoder.encode("dst"), 0, 100)); } @Test @ConditionalOnEnv(value = TestEnvUtil.ENV_REDIS_ENTERPRISE, enabled = false) public void zunionstoreParams() { jedis.zadd("foo", 1, "a"); jedis.zadd("foo", 2, "b"); jedis.zadd("bar", 2, "a"); jedis.zadd("bar", 2, "b"); ZParams params = new ZParams(); params.weights(2, 2.5); params.aggregate(ZParams.Aggregate.SUM); assertEquals(2, jedis.zunionstore("dst", params, "foo", "bar")); List expected = new ArrayList(); expected.add(new Tuple("a", new Double(7))); expected.add(new Tuple("b", new Double(9))); assertEquals(expected, jedis.zrangeWithScores("dst", 0, 100)); // Binary jedis.zadd(bfoo, 1, ba); jedis.zadd(bfoo, 2, bb); jedis.zadd(bbar, 2, ba); jedis.zadd(bbar, 2, bb); ZParams bparams = new ZParams(); bparams.weights(2, 2.5); bparams.aggregate(ZParams.Aggregate.SUM); assertEquals(2, jedis.zunionstore(SafeEncoder.encode("dst"), bparams, bfoo, bbar)); List bexpected = new ArrayList(); bexpected.add(new Tuple(ba, new Double(7))); bexpected.add(new Tuple(bb, new Double(9))); assertEquals(bexpected, jedis.zrangeWithScores(SafeEncoder.encode("dst"), 0, 100)); } @Test @ConditionalOnEnv(value = TestEnvUtil.ENV_REDIS_ENTERPRISE, enabled = false) public void zinter() { jedis.zadd("foo", 1, "a"); jedis.zadd("foo", 2, "b"); jedis.zadd("bar", 2, "a"); ZParams params = new ZParams(); params.weights(2, 2.5); params.aggregate(ZParams.Aggregate.SUM); assertEquals(singletonList("a"), jedis.zinter(params, "foo", "bar")); assertEquals(singletonList(new Tuple("a", new Double(7))), jedis.zinterWithScores(params, "foo", "bar")); // Binary jedis.zadd(bfoo, 1, ba); jedis.zadd(bfoo, 2, bb); jedis.zadd(bbar, 2, ba); ZParams bparams = new ZParams(); bparams.weights(2, 2.5); bparams.aggregate(ZParams.Aggregate.SUM); AssertUtil.assertByteArrayListEquals(singletonList(ba), jedis.zinter(params, bfoo, bbar)); assertEquals(singletonList(new Tuple(ba, new Double(7))), jedis.zinterWithScores(bparams, bfoo, bbar)); } @Test @ConditionalOnEnv(value = TestEnvUtil.ENV_REDIS_ENTERPRISE, enabled = false) public void zinterstore() { jedis.zadd("foo", 1, "a"); jedis.zadd("foo", 2, "b"); jedis.zadd("bar", 2, "a"); assertEquals(1, jedis.zinterstore("dst", "foo", "bar")); List expected = new ArrayList(); expected.add(new Tuple("a", new Double(3))); assertEquals(expected, jedis.zrangeWithScores("dst", 0, 100)); // Binary jedis.zadd(bfoo, 1, ba); jedis.zadd(bfoo, 2, bb); jedis.zadd(bbar, 2, ba); assertEquals(1, jedis.zinterstore(SafeEncoder.encode("dst"), bfoo, bbar)); List bexpected = new ArrayList(); bexpected.add(new Tuple(ba, new Double(3))); assertEquals(bexpected, jedis.zrangeWithScores(SafeEncoder.encode("dst"), 0, 100)); } @Test @ConditionalOnEnv(value = TestEnvUtil.ENV_REDIS_ENTERPRISE, enabled = false) public void zintertoreParams() { jedis.zadd("foo", 1, "a"); jedis.zadd("foo", 2, "b"); jedis.zadd("bar", 2, "a"); ZParams params = new ZParams(); params.weights(2, 2.5); params.aggregate(ZParams.Aggregate.SUM); assertEquals(1, jedis.zinterstore("dst", params, "foo", "bar")); List expected = new ArrayList(); expected.add(new Tuple("a", new Double(7))); assertEquals(expected, jedis.zrangeWithScores("dst", 0, 100)); // Binary jedis.zadd(bfoo, 1, ba); jedis.zadd(bfoo, 2, bb); jedis.zadd(bbar, 2, ba); ZParams bparams = new ZParams(); bparams.weights(2, 2.5); bparams.aggregate(ZParams.Aggregate.SUM); assertEquals(1, jedis.zinterstore(SafeEncoder.encode("dst"), bparams, bfoo, bbar)); List bexpected = new ArrayList(); bexpected.add(new Tuple(ba, new Double(7))); assertEquals(bexpected, jedis.zrangeWithScores(SafeEncoder.encode("dst"), 0, 100)); } @Test @SinceRedisVersion("7.0.0") @ConditionalOnEnv(value = TestEnvUtil.ENV_REDIS_ENTERPRISE, enabled = false) public void zintercard() { jedis.zadd("foo", 1, "a"); jedis.zadd("foo", 2, "b"); jedis.zadd("bar", 2, "a"); jedis.zadd("bar", 1, "b"); assertEquals(2, jedis.zintercard("foo", "bar")); assertEquals(1, jedis.zintercard(1, "foo", "bar")); // Binary jedis.zadd(bfoo, 1, ba); jedis.zadd(bfoo, 2, bb); jedis.zadd(bbar, 2, ba); jedis.zadd(bbar, 2, bb); assertEquals(2, jedis.zintercard(bfoo, bbar)); assertEquals(1, jedis.zintercard(1, bfoo, bbar)); } @Test public void zscan() { jedis.zadd("foo", 1, "a"); jedis.zadd("foo", 2, "b"); ScanResult result = jedis.zscan("foo", SCAN_POINTER_START); assertEquals(SCAN_POINTER_START, result.getCursor()); assertFalse(result.getResult().isEmpty()); // binary jedis.zadd(bfoo, 1, ba); jedis.zadd(bfoo, 1, bb); ScanResult bResult = jedis.zscan(bfoo, SCAN_POINTER_START_BINARY); assertArrayEquals(SCAN_POINTER_START_BINARY, bResult.getCursorAsBytes()); assertFalse(bResult.getResult().isEmpty()); } @Test public void zscanMatch() { ScanParams params = new ScanParams(); params.match("a*"); jedis.zadd("foo", 2, "b"); jedis.zadd("foo", 1, "a"); jedis.zadd("foo", 11, "aa"); ScanResult result = jedis.zscan("foo", SCAN_POINTER_START, params); assertEquals(SCAN_POINTER_START, result.getCursor()); assertFalse(result.getResult().isEmpty()); // binary params = new ScanParams(); params.match(bbarstar); jedis.zadd(bfoo, 2, bbar1); jedis.zadd(bfoo, 1, bbar2); jedis.zadd(bfoo, 11, bbar3); ScanResult bResult = jedis.zscan(bfoo, SCAN_POINTER_START_BINARY, params); assertArrayEquals(SCAN_POINTER_START_BINARY, bResult.getCursorAsBytes()); assertFalse(bResult.getResult().isEmpty()); } @Test public void zscanCount() { ScanParams params = new ScanParams(); params.count(2); jedis.zadd("foo", 1, "a1"); jedis.zadd("foo", 2, "a2"); jedis.zadd("foo", 3, "a3"); jedis.zadd("foo", 4, "a4"); jedis.zadd("foo", 5, "a5"); ScanResult result = jedis.zscan("foo", SCAN_POINTER_START, params); assertFalse(result.getResult().isEmpty()); // binary params = new ScanParams(); params.count(2); jedis.zadd(bfoo, 2, bbar1); jedis.zadd(bfoo, 1, bbar2); jedis.zadd(bfoo, 11, bbar3); ScanResult bResult = jedis.zscan(bfoo, SCAN_POINTER_START_BINARY, params); assertFalse(bResult.getResult().isEmpty()); } @Test public void infinity() { jedis.zadd("key", Double.POSITIVE_INFINITY, "pos"); assertEquals(Double.POSITIVE_INFINITY, jedis.zscore("key", "pos"), 0d); jedis.zadd("key", Double.NEGATIVE_INFINITY, "neg"); assertEquals(Double.NEGATIVE_INFINITY, jedis.zscore("key", "neg"), 0d); jedis.zadd("key", 0d, "zero"); List set = jedis.zrangeWithScores("key", 0, -1); Iterator itr = set.iterator(); assertEquals(Double.NEGATIVE_INFINITY, itr.next().getScore(), 0d); assertEquals(0d, itr.next().getScore(), 0d); assertEquals(Double.POSITIVE_INFINITY, itr.next().getScore(), 0d); } @Test @ConditionalOnEnv(value = TestEnvUtil.ENV_REDIS_ENTERPRISE, enabled = false) public void bzpopmax() { assertNull(jedis.bzpopmax(1, "foo", "bar")); jedis.zadd("foo", 1d, "a", ZAddParams.zAddParams().nx()); jedis.zadd("foo", 10d, "b", ZAddParams.zAddParams().nx()); jedis.zadd("bar", 0.1d, "c", ZAddParams.zAddParams().nx()); assertEquals(new KeyValue<>("foo", new Tuple("b", 10d)), jedis.bzpopmax(0, "foo", "bar")); // Binary assertNull(jedis.bzpopmax(1, bfoo, bbar)); jedis.zadd(bfoo, 1d, ba); jedis.zadd(bfoo, 10d, bb); jedis.zadd(bbar, 0.1d, bc); KeyValue actual = jedis.bzpopmax(0, bfoo, bbar); assertArrayEquals(bfoo, actual.getKey()); assertEquals(new Tuple(bb, 10d), actual.getValue()); } @Test @ConditionalOnEnv(value = TestEnvUtil.ENV_REDIS_ENTERPRISE, enabled = false) public void bzpopmin() { assertNull(jedis.bzpopmin(1, "bar", "foo")); jedis.zadd("foo", 1d, "a", ZAddParams.zAddParams().nx()); jedis.zadd("foo", 10d, "b", ZAddParams.zAddParams().nx()); jedis.zadd("bar", 0.1d, "c", ZAddParams.zAddParams().nx()); assertEquals(new KeyValue<>("bar", new Tuple("c", 0.1)), jedis.bzpopmin(0, "bar", "foo")); // Binary assertNull(jedis.bzpopmin(1, bbar, bfoo)); jedis.zadd(bfoo, 1d, ba); jedis.zadd(bfoo, 10d, bb); jedis.zadd(bbar, 0.1d, bc); KeyValue actual = jedis.bzpopmin(0, bbar, bfoo); assertArrayEquals(bbar, (byte[]) actual.getKey()); assertEquals(new Tuple(bc, 0.1), actual.getValue()); } @Test @ConditionalOnEnv(value = TestEnvUtil.ENV_REDIS_ENTERPRISE, enabled = false) public void zdiff() { jedis.zadd("foo", 1.0, "a"); jedis.zadd("foo", 2.0, "b"); jedis.zadd("bar", 1.0, "a"); assertEquals(0, jedis.zdiff("bar1", "bar2").size()); assertEquals(singletonList("b"), jedis.zdiff("foo", "bar")); assertEquals(singletonList(new Tuple("b", 2.0d)), jedis.zdiffWithScores("foo", "bar")); // binary jedis.zadd(bfoo, 1.0, ba); jedis.zadd(bfoo, 2.0, bb); jedis.zadd(bbar, 1.0, ba); assertEquals(0, jedis.zdiff(bbar1, bbar2).size()); List bactual = jedis.zdiff(bfoo, bbar); assertEquals(1, bactual.size()); assertArrayEquals(bb, bactual.iterator().next()); assertEquals(singletonList(new Tuple(bb, 2.0d)), jedis.zdiffWithScores(bfoo, bbar)); } @Test @ConditionalOnEnv(value = TestEnvUtil.ENV_REDIS_ENTERPRISE, enabled = false) public void zdiffstore() { jedis.zadd("foo", 1.0, "a"); jedis.zadd("foo", 2.0, "b"); jedis.zadd("bar", 1.0, "a"); assertEquals(0, jedis.zdiffstore("bar3", "bar1", "bar2")); assertEquals(1, jedis.zdiffstore("bar3", "foo", "bar")); assertEquals(singletonList("b"), jedis.zrange("bar3", 0, -1)); // binary jedis.zadd(bfoo, 1.0, ba); jedis.zadd(bfoo, 2.0, bb); jedis.zadd(bbar, 1.0, ba); assertEquals(0, jedis.zdiffstore(bbar3, bbar1, bbar2)); assertEquals(1, jedis.zdiffstore(bbar3, bfoo, bbar)); List bactual = jedis.zrange(bbar3, 0, -1); assertArrayEquals(bb, bactual.iterator().next()); } @Test public void zrandmember() { assertNull(jedis.zrandmember("foo")); assertEquals(Collections.emptyList(), jedis.zrandmember("foo", 1)); assertEquals(Collections.emptyList(), jedis.zrandmemberWithScores("foo", 1)); Map hash = new HashMap<>(); hash.put("bar1", 1d); hash.put("bar2", 10d); hash.put("bar3", 0.1d); jedis.zadd("foo", hash); AssertUtil.assertCollectionContains(hash.keySet(), jedis.zrandmember("foo")); assertEquals(2, jedis.zrandmember("foo", 2).size()); List actual = jedis.zrandmemberWithScores("foo", 2); assertEquals(2, actual.size()); actual.forEach(t -> assertEquals(hash.get(t.getElement()), t.getScore(), 0d)); // Binary assertNull(jedis.zrandmember(bfoo)); assertEquals(Collections.emptyList(), jedis.zrandmember(bfoo, 1)); assertEquals(Collections.emptyList(), jedis.zrandmemberWithScores(bfoo, 1)); Map bhash = new HashMap<>(); bhash.put(bbar1, 1d); bhash.put(bbar2, 10d); bhash.put(bbar3, 0.1d); jedis.zadd(bfoo, bhash); AssertUtil.assertByteArrayCollectionContains(bhash.keySet(), jedis.zrandmember(bfoo)); assertEquals(2, jedis.zrandmember(bfoo, 2).size()); List bactual = jedis.zrandmemberWithScores(bfoo, 2); assertEquals(2, bactual.size()); bactual.forEach(t -> assertEquals(getScoreFromByteMap(bhash, t.getBinaryElement()), t.getScore(), 0d)); } private Double getScoreFromByteMap(Map bhash, byte[] key) { for (Map.Entry en : bhash.entrySet()) { if (Arrays.equals(en.getKey(), key)) { return en.getValue(); } } return null; } @Test @SinceRedisVersion("7.0.0") public void zmpop() { jedis.zadd("foo", 1d, "a", ZAddParams.zAddParams().nx()); jedis.zadd("foo", 10d, "b", ZAddParams.zAddParams().nx()); jedis.zadd("foo", 0.1d, "c", ZAddParams.zAddParams().nx()); jedis.zadd("foo", 2d, "a", ZAddParams.zAddParams().nx()); KeyValue> single = jedis.zmpop(SortedSetOption.MAX, "foo"); KeyValue> range = jedis.zmpop(SortedSetOption.MIN, 2, "foo"); assertEquals(new Tuple("b", 10d), single.getValue().get(0)); assertEquals(2, range.getValue().size()); assertNull(jedis.zmpop(SortedSetOption.MAX, "foo")); } @Test @SinceRedisVersion("7.0.0") public void bzmpopSimple() { jedis.zadd("foo", 1d, "a", ZAddParams.zAddParams().nx()); jedis.zadd("foo", 10d, "b", ZAddParams.zAddParams().nx()); jedis.zadd("foo", 0.1d, "c", ZAddParams.zAddParams().nx()); jedis.zadd("foo", 2d, "a", ZAddParams.zAddParams().nx()); KeyValue> single = jedis.bzmpop(1L, SortedSetOption.MAX, "foo"); KeyValue> range = jedis.bzmpop(1L, SortedSetOption.MIN, 2, "foo"); assertEquals(new Tuple("b", 10d), single.getValue().get(0)); assertEquals(2, range.getValue().size()); assertNull(jedis.bzmpop(1L, SortedSetOption.MAX, "foo")); } } ================================================ FILE: src/test/java/redis/clients/jedis/commands/jedis/SortingCommandsTest.java ================================================ package redis.clients.jedis.commands.jedis; import static org.junit.jupiter.api.Assertions.assertEquals; import static redis.clients.jedis.util.AssertUtil.assertByteArrayListEquals; import java.util.ArrayList; import java.util.List; import io.redis.test.annotations.EnabledOnCommand; import io.redis.test.annotations.ConditionalOnEnv; import org.junit.jupiter.api.Test; import org.junit.jupiter.params.ParameterizedClass; import org.junit.jupiter.params.provider.MethodSource; import redis.clients.jedis.RedisProtocol; import redis.clients.jedis.params.SortingParams; import redis.clients.jedis.util.TestEnvUtil; @ParameterizedClass @MethodSource("redis.clients.jedis.commands.CommandsTestsParameters#respVersions") public class SortingCommandsTest extends JedisCommandsTestBase { final byte[] bfoo = { 0x01, 0x02, 0x03, 0x04 }; final byte[] bfoodest = { 0x01, 0x02, 0x03, 0x04, 0x05 }; final byte[] bbar1 = { 0x05, 0x06, 0x07, 0x08, '1' }; final byte[] bbar2 = { 0x05, 0x06, 0x07, 0x08, '2' }; final byte[] bbar3 = { 0x05, 0x06, 0x07, 0x08, '3' }; final byte[] bbar10 = { 0x05, 0x06, 0x07, 0x08, '1', '0' }; final byte[] bbarstar = { 0x05, 0x06, 0x07, 0x08, '*' }; final byte[] bcar1 = { 0x0A, 0x0B, 0x0C, 0x0D, '1' }; final byte[] bcar2 = { 0x0A, 0x0B, 0x0C, 0x0D, '2' }; final byte[] bcar10 = { 0x0A, 0x0B, 0x0C, 0x0D, '1', '0' }; final byte[] bcarstar = { 0x0A, 0x0B, 0x0C, 0x0D, '*' }; final byte[] b1 = { '1' }; final byte[] b2 = { '2' }; final byte[] b3 = { '3' }; final byte[] b10 = { '1', '0' }; public SortingCommandsTest(RedisProtocol protocol) { super(protocol); } @Test public void sort() { jedis.lpush("foo", "3"); jedis.lpush("foo", "2"); jedis.lpush("foo", "1"); List result = jedis.sort("foo"); List expected = new ArrayList(); expected.add("1"); expected.add("2"); expected.add("3"); assertEquals(expected, result); // Binary jedis.lpush(bfoo, b3); jedis.lpush(bfoo, b2); jedis.lpush(bfoo, b1); List bresult = jedis.sort(bfoo); List bexpected = new ArrayList(); bexpected.add(b1); bexpected.add(b2); bexpected.add(b3); assertByteArrayListEquals(bexpected, bresult); } @Test @ConditionalOnEnv(value = TestEnvUtil.ENV_REDIS_ENTERPRISE, enabled = false) public void sortBy() { jedis.lpush("foo", "2"); jedis.lpush("foo", "3"); jedis.lpush("foo", "1"); jedis.set("bar1", "3"); jedis.set("bar2", "2"); jedis.set("bar3", "1"); SortingParams sp = new SortingParams(); sp.by("bar*"); List result = jedis.sort("foo", sp); List expected = new ArrayList(); expected.add("3"); expected.add("2"); expected.add("1"); assertEquals(expected, result); // Sort to dest key long resultCount = jedis.sort("foo", sp, "foodest"); assertEquals(3L, resultCount); result = jedis.lpop("foodest", 5); assertEquals(expected, result); // Binary jedis.lpush(bfoo, b2); jedis.lpush(bfoo, b3); jedis.lpush(bfoo, b1); jedis.set(bbar1, b3); jedis.set(bbar2, b2); jedis.set(bbar3, b1); SortingParams bsp = new SortingParams(); bsp.by(bbarstar); List bresult = jedis.sort(bfoo, bsp); List bexpected = new ArrayList(); bexpected.add(b3); bexpected.add(b2); bexpected.add(b1); assertByteArrayListEquals(bexpected, bresult); // Sort to dest key resultCount = jedis.sort(bfoo, sp, bfoodest); assertEquals(3L, resultCount); bresult = jedis.lpop(bfoodest, 5); assertByteArrayListEquals(bexpected, bresult); } @Test public void sortDesc() { jedis.lpush("foo", "3"); jedis.lpush("foo", "2"); jedis.lpush("foo", "1"); SortingParams sp = new SortingParams(); sp.desc(); List result = jedis.sort("foo", sp); List expected = new ArrayList(); expected.add("3"); expected.add("2"); expected.add("1"); assertEquals(expected, result); // Binary jedis.lpush(bfoo, b3); jedis.lpush(bfoo, b2); jedis.lpush(bfoo, b1); SortingParams bsp = new SortingParams(); bsp.desc(); List bresult = jedis.sort(bfoo, bsp); List bexpected = new ArrayList(); bexpected.add(b3); bexpected.add(b2); bexpected.add(b1); assertByteArrayListEquals(bexpected, bresult); } @Test public void sortLimit() { for (int n = 10; n > 0; n--) { jedis.lpush("foo", String.valueOf(n)); } SortingParams sp = new SortingParams(); sp.limit(0, 3); List result = jedis.sort("foo", sp); List expected = new ArrayList(); expected.add("1"); expected.add("2"); expected.add("3"); assertEquals(expected, result); // Binary jedis.rpush(bfoo, new byte[] { (byte) '4' }); jedis.rpush(bfoo, new byte[] { (byte) '3' }); jedis.rpush(bfoo, new byte[] { (byte) '2' }); jedis.rpush(bfoo, new byte[] { (byte) '1' }); SortingParams bsp = new SortingParams(); bsp.limit(0, 3); List bresult = jedis.sort(bfoo, bsp); List bexpected = new ArrayList(); bexpected.add(b1); bexpected.add(b2); bexpected.add(b3); assertByteArrayListEquals(bexpected, bresult); } @Test public void sortAlpha() { jedis.lpush("foo", "1"); jedis.lpush("foo", "2"); jedis.lpush("foo", "10"); SortingParams sp = new SortingParams(); sp.alpha(); List result = jedis.sort("foo", sp); List expected = new ArrayList(); expected.add("1"); expected.add("10"); expected.add("2"); assertEquals(expected, result); // Binary jedis.lpush(bfoo, b1); jedis.lpush(bfoo, b2); jedis.lpush(bfoo, b10); SortingParams bsp = new SortingParams(); bsp.alpha(); List bresult = jedis.sort(bfoo, bsp); List bexpected = new ArrayList(); bexpected.add(b1); bexpected.add(b10); bexpected.add(b2); assertByteArrayListEquals(bexpected, bresult); } @Test public void sortGet() { jedis.lpush("foo", "1"); jedis.lpush("foo", "2"); jedis.lpush("foo", "10"); jedis.set("bar1", "bar1"); jedis.set("bar2", "bar2"); jedis.set("bar10", "bar10"); jedis.set("car1", "car1"); jedis.set("car2", "car2"); jedis.set("car10", "car10"); SortingParams sp = new SortingParams(); sp.get("car*", "bar*"); List result = jedis.sort("foo", sp); List expected = new ArrayList(); expected.add("car1"); expected.add("bar1"); expected.add("car2"); expected.add("bar2"); expected.add("car10"); expected.add("bar10"); assertEquals(expected, result); // Binary jedis.lpush(bfoo, b1); jedis.lpush(bfoo, b2); jedis.lpush(bfoo, b10); jedis.set(bbar1, bbar1); jedis.set(bbar2, bbar2); jedis.set(bbar10, bbar10); jedis.set(bcar1, bcar1); jedis.set(bcar2, bcar2); jedis.set(bcar10, bcar10); SortingParams bsp = new SortingParams(); bsp.get(bcarstar, bbarstar); List bresult = jedis.sort(bfoo, bsp); List bexpected = new ArrayList(); bexpected.add(bcar1); bexpected.add(bbar1); bexpected.add(bcar2); bexpected.add(bbar2); bexpected.add(bcar10); bexpected.add(bbar10); assertByteArrayListEquals(bexpected, bresult); } @Test @ConditionalOnEnv(value = TestEnvUtil.ENV_REDIS_ENTERPRISE, enabled = false) public void sortStore() { jedis.lpush("foo", "1"); jedis.lpush("foo", "2"); jedis.lpush("foo", "10"); long result = jedis.sort("foo", "result"); List expected = new ArrayList(); expected.add("1"); expected.add("2"); expected.add("10"); assertEquals(3, result); assertEquals(expected, jedis.lrange("result", 0, 1000)); // Binary jedis.lpush(bfoo, b1); jedis.lpush(bfoo, b2); jedis.lpush(bfoo, b10); byte[] bkresult = new byte[] { 0X09, 0x0A, 0x0B, 0x0C }; long bresult = jedis.sort(bfoo, bkresult); List bexpected = new ArrayList(); bexpected.add(b1); bexpected.add(b2); bexpected.add(b10); assertEquals(3, bresult); assertByteArrayListEquals(bexpected, jedis.lrange(bkresult, 0, 1000)); } @Test @EnabledOnCommand("SORT_RO") public void sort_ro() { jedis.rpush("foo", "1", "3", "2"); List result = jedis.sortReadonly("foo", new SortingParams().desc()); List expected = new ArrayList<>(); expected.add("3"); expected.add("2"); expected.add("1"); assertEquals(expected, result); // Binary jedis.rpush(bfoo, b3, b1, b2); List bresult = jedis.sortReadonly(bfoo, new SortingParams().alpha()); List bexpected = new ArrayList<>(); bexpected.add(b1); bexpected.add(b2); bexpected.add(b3); assertByteArrayListEquals(bexpected, bresult); } } ================================================ FILE: src/test/java/redis/clients/jedis/commands/jedis/StreamsBinaryCommandsTest.java ================================================ package redis.clients.jedis.commands.jedis; import io.redis.test.annotations.EnabledOnCommand; import io.redis.test.annotations.SinceRedisVersion; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Tag; import org.junit.jupiter.params.ParameterizedClass; import org.junit.jupiter.params.provider.MethodSource; import redis.clients.jedis.RedisProtocol; import redis.clients.jedis.StreamEntryID; import redis.clients.jedis.args.StreamDeletionPolicy; import redis.clients.jedis.exceptions.JedisDataException; import redis.clients.jedis.params.XAddParams; import redis.clients.jedis.params.XCfgSetParams; import redis.clients.jedis.params.XReadGroupParams; import redis.clients.jedis.params.XReadParams; import redis.clients.jedis.params.XTrimParams; import redis.clients.jedis.resps.StreamEntryBinary; import redis.clients.jedis.resps.StreamEntryDeletionResult; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; import static io.redis.test.utils.RedisVersion.V8_4_0_STRING; import static java.util.Collections.singletonMap; import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.hasSize; import static org.junit.jupiter.api.Assertions.assertArrayEquals; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertNotNull; import static org.junit.jupiter.api.Assertions.assertNull; import static org.junit.jupiter.api.Assertions.assertTrue; import static redis.clients.jedis.StreamEntryID.XREADGROUP_UNDELIVERED_ENTRY; import static redis.clients.jedis.util.StreamEntryBinaryListMatcher.equalsStreamEntries; /** * Test replicated from {@link redis.clients.jedis.commands.unified.StreamsBinaryCommandsTestBase} * but adapted to use Jedis commands. Note: Consider merging with the unified test class if * possible. e.g., by using a common base class */ @ParameterizedClass @MethodSource("redis.clients.jedis.commands.CommandsTestsParameters#respVersions") @Tag("integration") public class StreamsBinaryCommandsTest extends JedisCommandsTestBase { protected static final byte[] STREAM_KEY_1 = "{binary-stream}-1".getBytes(); protected static final byte[] STREAM_KEY_2 = "{binary-stream}-2".getBytes(); protected static final byte[] GROUP_NAME = "group-1".getBytes(); protected static final byte[] CONSUMER_NAME = "consumer-1".getBytes(); protected static final byte[] FIELD_KEY_1 = "binary-field-1".getBytes(); // Test with invalid UTF-8 characters protected static final byte[] BINARY_VALUE_1 = new byte[] { 0x00, 0x01, 0x02, 0x03, (byte) 0xFF }; protected static final byte[] FIELD_KEY_2 = "binary-field-1".getBytes(); protected static final byte[] BINARY_VALUE_2 = "binary-value-2".getBytes(); protected static final Map HASH_1 = singletonMap(FIELD_KEY_1, BINARY_VALUE_1); protected static final Map HASH_2 = singletonMap(FIELD_KEY_2, BINARY_VALUE_2); protected static final List stream1Entries = new ArrayList<>(); protected static final List stream2Entries = new ArrayList<>(); static { stream1Entries.add(new StreamEntryBinary(new StreamEntryID("0-1"), HASH_1)); stream1Entries.add(new StreamEntryBinary(new StreamEntryID("0-3"), HASH_2)); stream2Entries.add(new StreamEntryBinary(new StreamEntryID("0-2"), HASH_1)); } public StreamsBinaryCommandsTest(RedisProtocol protocol) { super(protocol); } /** * Creates a map of stream keys to StreamEntryID objects. * @param streamOffsets Array of stream key and offset pairs * @return Map of stream keys to StreamEntryID objects */ public static Map offsets(Object... streamOffsets) { if (streamOffsets.length % 2 != 0) { throw new IllegalArgumentException("Stream offsets must be provided as key-value pairs"); } Map result = new HashMap<>(); for (int i = 0; i < streamOffsets.length; i += 2) { byte[] key = (byte[]) streamOffsets[i]; Object value = streamOffsets[i + 1]; StreamEntryID id; if (value instanceof String) { id = new StreamEntryID((String) value); } else if (value instanceof StreamEntryID) { id = (StreamEntryID) value; } else { throw new IllegalArgumentException("Offset must be a String or StreamEntryID"); } result.put(key, id); } return result; } @BeforeEach public void setUpTestStream() { setUpTestStream(StreamEntryID.XGROUP_LAST_ENTRY.toString().getBytes()); } private void setUpTestStream(byte[] startId) { jedis.del(STREAM_KEY_1); jedis.del(STREAM_KEY_2); try { jedis.xgroupCreate(STREAM_KEY_1, GROUP_NAME, startId, true); } catch (JedisDataException e) { if (!e.getMessage().contains("BUSYGROUP")) { throw e; } } try { jedis.xgroupCreate(STREAM_KEY_2, GROUP_NAME, startId, true); } catch (JedisDataException e) { if (!e.getMessage().contains("BUSYGROUP")) { throw e; } } } @Test public void xreadBinaryNoEntries() { List>> actualEntries = jedis.xreadBinary( XReadParams.xReadParams(), offsets(STREAM_KEY_1, "0-0")); assertNull(actualEntries); } @Test public void xreadBinary() { stream1Entries.forEach( entry -> jedis.xadd(STREAM_KEY_1, new XAddParams().id(entry.getID()), entry.getFields())); List>> actualEntries = jedis.xreadBinary( XReadParams.xReadParams(), offsets(STREAM_KEY_1, "0-0")); assertThat(actualEntries, hasSize(1)); assertArrayEquals(STREAM_KEY_1, actualEntries.get(0).getKey()); assertThat(actualEntries.get(0).getValue(), equalsStreamEntries(stream1Entries)); } @Test public void xreadBinaryCount() { stream1Entries.forEach( entry -> jedis.xadd(STREAM_KEY_1, new XAddParams().id(entry.getID()), entry.getFields())); List>> actualEntries = jedis.xreadBinary( XReadParams.xReadParams().count(1), offsets(STREAM_KEY_1, "0-0")); assertThat(actualEntries, hasSize(1)); assertArrayEquals(STREAM_KEY_1, actualEntries.get(0).getKey()); assertThat(actualEntries.get(0).getValue(), equalsStreamEntries(stream1Entries.subList(0, 1))); } @Test public void xreadBinaryAsMapNoEntries() { Map> actualEntries = jedis.xreadBinaryAsMap( XReadParams.xReadParams(), offsets(STREAM_KEY_1, "0-0")); assertNull(actualEntries); } @Test public void xreadBinaryAsMap() { stream1Entries.forEach( entry -> jedis.xadd(STREAM_KEY_1, new XAddParams().id(entry.getID()), entry.getFields())); Map> actualEntries = jedis.xreadBinaryAsMap( XReadParams.xReadParams(), offsets(STREAM_KEY_1, "0-0")); assertThat(actualEntries.entrySet(), hasSize(1)); assertThat(actualEntries.get(STREAM_KEY_1), equalsStreamEntries(stream1Entries)); } @Test public void xreadBinaryAsMapCount() { stream1Entries.forEach( entry -> jedis.xadd(STREAM_KEY_1, new XAddParams().id(entry.getID()), entry.getFields())); Map> actualEntries = jedis.xreadBinaryAsMap( XReadParams.xReadParams().count(1), offsets(STREAM_KEY_1, "0-0")); assertThat(actualEntries.entrySet(), hasSize(1)); assertThat(actualEntries.get(STREAM_KEY_1), equalsStreamEntries(stream1Entries.subList(0, 1))); } @Test public void xreadBinaryAsMapWithMultipleStreams() { // Add entries to the streams stream1Entries.forEach( entry -> jedis.xadd(STREAM_KEY_1, new XAddParams().id(entry.getID()), entry.getFields())); stream2Entries.forEach( entry -> jedis.xadd(STREAM_KEY_2, new XAddParams().id(entry.getID()), entry.getFields())); Map> actualEntries = jedis.xreadBinaryAsMap( XReadParams.xReadParams(), offsets(STREAM_KEY_1, "0-0", STREAM_KEY_2, "0-0")); assertThat(actualEntries.entrySet(), hasSize(2)); assertThat(actualEntries.get(STREAM_KEY_1), equalsStreamEntries(stream1Entries)); assertThat(actualEntries.get(STREAM_KEY_2), equalsStreamEntries(stream2Entries)); } @Test public void xreadGroupBinary() { // Add entries to the streams stream1Entries.forEach( entry -> jedis.xadd(STREAM_KEY_1, new XAddParams().id(entry.getID()), entry.getFields())); List>> actualEntries = jedis.xreadGroupBinary( GROUP_NAME, CONSUMER_NAME, XReadGroupParams.xReadGroupParams(), offsets(STREAM_KEY_1, XREADGROUP_UNDELIVERED_ENTRY)); // verify the result contains entries from one stream // and is under the expected stream key assertThat(actualEntries, hasSize(1)); assertArrayEquals(STREAM_KEY_1, actualEntries.get(0).getKey()); assertThat(actualEntries.get(0).getValue(), equalsStreamEntries(stream1Entries)); } @Test public void xreadGroupBinaryAsMap() { stream1Entries.forEach( entry -> jedis.xadd(STREAM_KEY_1, new XAddParams().id(entry.getID()), entry.getFields())); Map> actualEntries = jedis.xreadGroupBinaryAsMap(GROUP_NAME, CONSUMER_NAME, XReadGroupParams.xReadGroupParams(), offsets(STREAM_KEY_1, XREADGROUP_UNDELIVERED_ENTRY)); assertThat(actualEntries.entrySet(), hasSize(1)); assertThat(actualEntries.get(STREAM_KEY_1), equalsStreamEntries(stream1Entries)); } @Test public void xreadGroupBinaryAsMapMultipleStreams() { // Add entries to the streams stream1Entries.forEach( entry -> jedis.xadd(STREAM_KEY_1, new XAddParams().id(entry.getID()), entry.getFields())); stream2Entries.forEach( entry -> jedis.xadd(STREAM_KEY_2, new XAddParams().id(entry.getID()), entry.getFields())); Map> actualEntries = jedis.xreadGroupBinaryAsMap(GROUP_NAME, CONSUMER_NAME, XReadGroupParams.xReadGroupParams(), offsets(STREAM_KEY_1, XREADGROUP_UNDELIVERED_ENTRY, STREAM_KEY_2, XREADGROUP_UNDELIVERED_ENTRY)); assertThat(actualEntries.entrySet(), hasSize(2)); assertThat(actualEntries.get(STREAM_KEY_1), equalsStreamEntries(stream1Entries)); assertThat(actualEntries.get(STREAM_KEY_2), equalsStreamEntries(stream2Entries)); } // ========== XACKDEL Command Tests ========== @Test @SinceRedisVersion("8.1.240") public void testXackdel() { // Add a message to the stream byte[] messageId = jedis.xadd(STREAM_KEY_1, new XAddParams().id("1-0"), HASH_1); assertNotNull(messageId); assertEquals(1L, jedis.xlen(STREAM_KEY_1)); // Read the message with consumer group to add it to PEL Map streams = offsets(STREAM_KEY_1, XREADGROUP_UNDELIVERED_ENTRY); List>> messages = jedis.xreadGroupBinary( GROUP_NAME, CONSUMER_NAME, XReadGroupParams.xReadGroupParams().count(1), streams); assertEquals(1, messages.size()); assertEquals(1, messages.get(0).getValue().size()); byte[] readMessageId = messages.get(0).getValue().get(0).getID().toString().getBytes(); // Test XACKDEL - should acknowledge and delete the message List results = jedis.xackdel(STREAM_KEY_1, GROUP_NAME, readMessageId); assertThat(results, hasSize(1)); assertEquals(StreamEntryDeletionResult.DELETED, results.get(0)); // Verify message is deleted from stream assertEquals(0L, jedis.xlen(STREAM_KEY_1)); } @Test @SinceRedisVersion("8.1.240") public void testXackdelWithTrimMode() { // Add multiple messages jedis.xadd(STREAM_KEY_1, new XAddParams().id("1-0"), HASH_1); jedis.xadd(STREAM_KEY_1, new XAddParams().id("2-0"), HASH_2); assertEquals(2L, jedis.xlen(STREAM_KEY_1)); // Read the messages with consumer group Map streams = offsets(STREAM_KEY_1, XREADGROUP_UNDELIVERED_ENTRY); List>> messages = jedis.xreadGroupBinary( GROUP_NAME, CONSUMER_NAME, XReadGroupParams.xReadGroupParams().count(2), streams); assertEquals(1, messages.size()); assertEquals(2, messages.get(0).getValue().size()); // Test XACKDEL with KEEP_REFERENCES mode byte[] readId1 = messages.get(0).getValue().get(0).getID().toString().getBytes(); List results = jedis.xackdel(STREAM_KEY_1, GROUP_NAME, StreamDeletionPolicy.KEEP_REFERENCES, readId1); assertThat(results, hasSize(1)); assertEquals(StreamEntryDeletionResult.DELETED, results.get(0)); // Verify one message is deleted assertEquals(1L, jedis.xlen(STREAM_KEY_1)); } @Test @SinceRedisVersion("8.1.240") public void testXackdelUnreadMessages() { // Add test entries but don't read them byte[] id1 = jedis.xadd(STREAM_KEY_1, new XAddParams().id("1-0"), HASH_1); // Test XACKDEL on unread messages - should return NOT_FOUND for PEL List results = jedis.xackdel(STREAM_KEY_1, GROUP_NAME, id1); assertThat(results, hasSize(1)); // Should return NOT_FOUND because message was never read by the consumer group assertEquals(StreamEntryDeletionResult.NOT_FOUND, results.get(0)); // Stream should still contain the message assertEquals(1L, jedis.xlen(STREAM_KEY_1)); } @Test @SinceRedisVersion("8.1.240") public void testXackdelMultipleMessages() { // Add multiple messages jedis.xadd(STREAM_KEY_1, new XAddParams().id("1-0"), HASH_1); jedis.xadd(STREAM_KEY_1, new XAddParams().id("2-0"), HASH_2); jedis.xadd(STREAM_KEY_1, new XAddParams().id("3-0"), HASH_1); assertEquals(3L, jedis.xlen(STREAM_KEY_1)); // Read the messages with consumer group Map streams = offsets(STREAM_KEY_1, XREADGROUP_UNDELIVERED_ENTRY); List>> messages = jedis.xreadGroupBinary( GROUP_NAME, CONSUMER_NAME, XReadGroupParams.xReadGroupParams().count(3), streams); assertEquals(1, messages.size()); assertEquals(3, messages.get(0).getValue().size()); // Test XACKDEL with multiple IDs byte[] readId1 = messages.get(0).getValue().get(0).getID().toString().getBytes(); byte[] readId2 = messages.get(0).getValue().get(1).getID().toString().getBytes(); List results = jedis.xackdel(STREAM_KEY_1, GROUP_NAME, readId1, readId2); assertThat(results, hasSize(2)); assertEquals(StreamEntryDeletionResult.DELETED, results.get(0)); assertEquals(StreamEntryDeletionResult.DELETED, results.get(1)); // Verify two messages are deleted assertEquals(1L, jedis.xlen(STREAM_KEY_1)); } // ========== XDELEX Command Tests ========== @Test @SinceRedisVersion("8.1.240") public void testXdelex() { // Add test entries byte[] id1 = jedis.xadd(STREAM_KEY_1, new XAddParams().id("1-0"), HASH_1); byte[] id2 = jedis.xadd(STREAM_KEY_1, new XAddParams().id("2-0"), HASH_2); assertEquals(2L, jedis.xlen(STREAM_KEY_1)); // Test basic XDELEX without parameters (should behave like XDEL with KEEP_REFERENCES) List results = jedis.xdelex(STREAM_KEY_1, id1); assertThat(results, hasSize(1)); assertEquals(StreamEntryDeletionResult.DELETED, results.get(0)); // Verify entry is deleted from stream assertEquals(1L, jedis.xlen(STREAM_KEY_1)); } @Test @SinceRedisVersion("8.1.240") public void testXdelexWithTrimMode() { // Add test entries byte[] id1 = jedis.xadd(STREAM_KEY_1, new XAddParams().id("1-0"), HASH_1); jedis.xadd(STREAM_KEY_1, new XAddParams().id("2-0"), HASH_2); // Test XDELEX with DELETE_REFERENCES mode List results = jedis.xdelex(STREAM_KEY_1, StreamDeletionPolicy.DELETE_REFERENCES, id1); assertThat(results, hasSize(1)); assertEquals(StreamEntryDeletionResult.DELETED, results.get(0)); // Verify entry is deleted from stream assertEquals(1L, jedis.xlen(STREAM_KEY_1)); } @Test @SinceRedisVersion("8.1.240") public void testXdelexMultipleEntries() { // Add test entries byte[] id1 = jedis.xadd(STREAM_KEY_1, new XAddParams().id("1-0"), HASH_1); jedis.xadd(STREAM_KEY_1, new XAddParams().id("2-0"), HASH_2); byte[] id3 = jedis.xadd(STREAM_KEY_1, new XAddParams().id("3-0"), HASH_1); assertEquals(3L, jedis.xlen(STREAM_KEY_1)); // Test XDELEX with multiple IDs List results = jedis.xdelex(STREAM_KEY_1, id1, id3); assertThat(results, hasSize(2)); assertEquals(StreamEntryDeletionResult.DELETED, results.get(0)); assertEquals(StreamEntryDeletionResult.DELETED, results.get(1)); // Verify two entries are deleted assertEquals(1L, jedis.xlen(STREAM_KEY_1)); } @Test @SinceRedisVersion("8.1.240") public void testXdelexNonExistentEntries() { // Add one entry byte[] id1 = jedis.xadd(STREAM_KEY_1, new XAddParams().id("1-0"), HASH_1); assertEquals(1L, jedis.xlen(STREAM_KEY_1)); // Test XDELEX with mix of existing and non-existent IDs byte[] nonExistentId = "999-0".getBytes(); List results = jedis.xdelex(STREAM_KEY_1, id1, nonExistentId); assertThat(results, hasSize(2)); assertEquals(StreamEntryDeletionResult.DELETED, results.get(0)); // Existing entry assertEquals(StreamEntryDeletionResult.NOT_FOUND, results.get(1)); // Non-existent entry // Verify existing entry is deleted assertEquals(0L, jedis.xlen(STREAM_KEY_1)); } @Test @SinceRedisVersion("8.1.240") public void testXdelexWithConsumerGroups() { // Add test entries jedis.xadd(STREAM_KEY_1, new XAddParams().id("1-0"), HASH_1); jedis.xadd(STREAM_KEY_1, new XAddParams().id("2-0"), HASH_2); assertEquals(2L, jedis.xlen(STREAM_KEY_1)); // Read messages with consumer group to add them to PEL Map streams = offsets(STREAM_KEY_1, XREADGROUP_UNDELIVERED_ENTRY); List>> messages = jedis.xreadGroupBinary( GROUP_NAME, CONSUMER_NAME, XReadGroupParams.xReadGroupParams().count(2), streams); assertEquals(1, messages.size()); assertEquals(2, messages.get(0).getValue().size()); // Acknowledge only the first message byte[] readId1 = messages.get(0).getValue().get(0).getID().toString().getBytes(); byte[] readId2 = messages.get(0).getValue().get(1).getID().toString().getBytes(); jedis.xack(STREAM_KEY_1, GROUP_NAME, readId1); // Test XDELEX with ACKNOWLEDGED mode - should only delete acknowledged entries List results = jedis.xdelex(STREAM_KEY_1, StreamDeletionPolicy.ACKNOWLEDGED, readId1, readId2); assertThat(results, hasSize(2)); assertEquals(StreamEntryDeletionResult.DELETED, results.get(0)); // id1 was acknowledged assertEquals(StreamEntryDeletionResult.NOT_DELETED_UNACKNOWLEDGED_OR_STILL_REFERENCED, results.get(1)); // id2 not acknowledged // Verify only acknowledged entry was deleted assertEquals(1L, jedis.xlen(STREAM_KEY_1)); } @Test @SinceRedisVersion("8.1.240") public void testXdelexEmptyStream() { // Test XDELEX on empty stream byte[] nonExistentId = "1-0".getBytes(); List results = jedis.xdelex(STREAM_KEY_1, nonExistentId); assertThat(results, hasSize(1)); assertEquals(StreamEntryDeletionResult.NOT_FOUND, results.get(0)); } // ========== XTRIM Command Tests with trimmingMode ========== @Test @SinceRedisVersion("8.1.240") public void testXtrimWithKeepReferences() { // Add test entries for (int i = 1; i <= 5; i++) { jedis.xadd(STREAM_KEY_1, new XAddParams().id(i + "-0"), HASH_1); } assertEquals(5L, jedis.xlen(STREAM_KEY_1)); // Read messages with consumer group to create PEL entries Map streams = offsets(STREAM_KEY_1, XREADGROUP_UNDELIVERED_ENTRY); jedis.xreadGroupBinary(GROUP_NAME, CONSUMER_NAME, XReadGroupParams.xReadGroupParams().count(3), streams); // Test XTRIM with KEEP_REFERENCES mode - should preserve PEL references long trimmed = jedis.xtrim(STREAM_KEY_1, XTrimParams.xTrimParams().maxLen(3).trimmingMode( StreamDeletionPolicy.KEEP_REFERENCES)); assertEquals(2L, trimmed); // Should trim 2 entries assertEquals(3L, jedis.xlen(STREAM_KEY_1)); } @Test @SinceRedisVersion("8.1.240") public void testXtrimWithAcknowledged() { // Add test entries for (int i = 1; i <= 5; i++) { jedis.xadd(STREAM_KEY_1, new XAddParams().id(i + "-0"), HASH_1); } assertEquals(5L, jedis.xlen(STREAM_KEY_1)); // Read messages with consumer group Map streams = offsets(STREAM_KEY_1, XREADGROUP_UNDELIVERED_ENTRY); List>> messages = jedis.xreadGroupBinary( GROUP_NAME, CONSUMER_NAME, XReadGroupParams.xReadGroupParams().count(3), streams); assertEquals(1, messages.size()); assertEquals(3, messages.get(0).getValue().size()); // Acknowledge only the first 2 messages byte[] readId1 = messages.get(0).getValue().get(0).getID().toString().getBytes(); byte[] readId2 = messages.get(0).getValue().get(1).getID().toString().getBytes(); jedis.xack(STREAM_KEY_1, GROUP_NAME, readId1, readId2); // Test XTRIM with ACKNOWLEDGED mode - should only trim acknowledged entries long trimmed = jedis.xtrim(STREAM_KEY_1, XTrimParams.xTrimParams().maxLen(3).trimmingMode( StreamDeletionPolicy.ACKNOWLEDGED)); // The exact behavior depends on implementation, but it should respect acknowledgment status assertTrue(trimmed >= 0); assertTrue(jedis.xlen(STREAM_KEY_1) <= 5); // Should not exceed original length } // ========== XREADGROUP CLAIM Tests ========== @Test @SinceRedisVersion(V8_4_0_STRING) public void xreadgroupClaimReturnsMetadataOrdered() throws InterruptedException { setUpTestStream("0-0".getBytes()); final byte[] CONSUMER_1 = "consumer-1".getBytes(); final byte[] CONSUMER_2 = "consumer-2".getBytes(); final long IDLE_TIME_MS = 5; // Produce two entries Map hash = singletonMap(FIELD_KEY_1, BINARY_VALUE_1); jedis.xadd(STREAM_KEY_1, XAddParams.xAddParams().id(StreamEntryID.NEW_ENTRY), hash); jedis.xadd(STREAM_KEY_1, XAddParams.xAddParams().id(StreamEntryID.NEW_ENTRY), hash); Map streams = offsets(STREAM_KEY_1, XREADGROUP_UNDELIVERED_ENTRY); jedis.xreadGroupBinary(GROUP_NAME, CONSUMER_1, XReadGroupParams.xReadGroupParams().count(10), streams); // Ensure idle time so entries are claimable Thread.sleep(IDLE_TIME_MS); // Produce fresh entries that are NOT claimed (not pending) jedis.xadd(STREAM_KEY_1, XAddParams.xAddParams().id(StreamEntryID.NEW_ENTRY), hash); jedis.xadd(STREAM_KEY_1, XAddParams.xAddParams().id(StreamEntryID.NEW_ENTRY), hash); // Read with consumer-2 using CLAIM List>> consumer2Result = jedis.xreadGroupBinary( GROUP_NAME, CONSUMER_2, XReadGroupParams.xReadGroupParams().claim(IDLE_TIME_MS).count(10), streams); assertNotNull(consumer2Result); assertEquals(1, consumer2Result.size()); List entries = consumer2Result.get(0).getValue(); assertEquals(4, entries.size()); long claimedCount = entries.stream().filter(StreamEntryBinary::isClaimed).count(); long freshCount = entries.size() - claimedCount; assertEquals(2, claimedCount); assertEquals(2, freshCount); // Assert order: pending entries are first StreamEntryBinary first = entries.get(0); StreamEntryBinary second = entries.get(1); StreamEntryBinary third = entries.get(2); StreamEntryBinary fourth = entries.get(3); // Claimed entries assertTrue(first.isClaimed()); assertTrue(second.isClaimed()); assertTrue(first.getMillisElapsedFromDelivery() >= IDLE_TIME_MS); assertTrue(second.getMillisElapsedFromDelivery() >= IDLE_TIME_MS); // Fresh entries assertFalse(third.isClaimed()); assertFalse(fourth.isClaimed()); assertEquals(Long.valueOf(0), third.getDeliveredCount()); assertEquals(Long.valueOf(0), fourth.getDeliveredCount()); assertEquals(Long.valueOf(0), third.getMillisElapsedFromDelivery()); assertEquals(Long.valueOf(0), fourth.getMillisElapsedFromDelivery()); } // ========== Idempotent Producer Tests ========== @Test @EnabledOnCommand("XCFGSET") public void testXaddIdmpAuto() { // Add entry with IDMPAUTO Map message = new HashMap<>(); message.put("order".getBytes(), "12345".getBytes()); message.put("amount".getBytes(), "100.00".getBytes()); byte[] id1 = jedis.xadd(STREAM_KEY_1, XAddParams.xAddParams().idmpAuto("producer-1".getBytes()), message); assertNotNull(id1); assertEquals(1L, jedis.xlen(STREAM_KEY_1)); // Add same message again with same producer - should return same ID (duplicate detected) byte[] id2 = jedis.xadd(STREAM_KEY_1, XAddParams.xAddParams().idmpAuto("producer-1".getBytes()), message); assertArrayEquals(id1, id2); // Duplicate returns same ID assertEquals(1L, jedis.xlen(STREAM_KEY_1)); // Stream length unchanged // Add same message with different producer - should succeed byte[] id3 = jedis.xadd(STREAM_KEY_1, XAddParams.xAddParams().idmpAuto("producer-2".getBytes()), message); assertNotNull(id3); assertEquals(2L, jedis.xlen(STREAM_KEY_1)); // Add different message with same producer - should succeed Map message2 = new HashMap<>(); message2.put("order".getBytes(), "67890".getBytes()); message2.put("amount".getBytes(), "200.00".getBytes()); byte[] id4 = jedis.xadd(STREAM_KEY_1, XAddParams.xAddParams().idmpAuto("producer-1".getBytes()), message2); assertNotNull(id4); assertEquals(3L, jedis.xlen(STREAM_KEY_1)); } @Test @EnabledOnCommand("XCFGSET") public void testXaddIdmp() { Map hash1 = singletonMap(FIELD_KEY_1, BINARY_VALUE_1); Map hash2 = singletonMap(FIELD_KEY_2, BINARY_VALUE_1); // Add entry with explicit idempotent ID byte[] id1 = jedis.xadd(STREAM_KEY_1, XAddParams.xAddParams().idmp("producer-1".getBytes(), "iid-001".getBytes()), hash1); assertNotNull(id1); assertEquals(1L, jedis.xlen(STREAM_KEY_1)); // Add with same producer and idempotent ID - should return same ID (duplicate detected) byte[] id2 = jedis.xadd(STREAM_KEY_1, XAddParams.xAddParams().idmp("producer-1".getBytes(), "iid-001".getBytes()), hash2); assertArrayEquals(id1, id2); // Duplicate returns same ID assertEquals(1L, jedis.xlen(STREAM_KEY_1)); // Add with same producer but different idempotent ID - should succeed byte[] id3 = jedis.xadd(STREAM_KEY_1, XAddParams.xAddParams().idmp("producer-1".getBytes(), "iid-002".getBytes()), hash1); assertNotNull(id3); assertEquals(2L, jedis.xlen(STREAM_KEY_1)); // Add with different producer but same idempotent ID - should succeed byte[] id4 = jedis.xadd(STREAM_KEY_1, XAddParams.xAddParams().idmp("producer-2".getBytes(), "iid-001".getBytes()), hash1); assertNotNull(id4); assertEquals(3L, jedis.xlen(STREAM_KEY_1)); } @Test @EnabledOnCommand("XCFGSET") public void testXcfgset() { // Add an entry to create the stream jedis.xadd(STREAM_KEY_1, XAddParams.xAddParams(), singletonMap(FIELD_KEY_1, BINARY_VALUE_1)); // Configure idempotent producer settings byte[] result = jedis.xcfgset(STREAM_KEY_1, XCfgSetParams.xCfgSetParams().idmpDuration(1000).idmpMaxsize(500)); assertArrayEquals("OK".getBytes(), result); } } ================================================ FILE: src/test/java/redis/clients/jedis/commands/jedis/StreamsCommandsTest.java ================================================ package redis.clients.jedis.commands.jedis; import static io.redis.test.utils.RedisVersion.V8_4_0_STRING; import static java.util.Collections.singleton; import static java.util.Collections.singletonMap; import static org.hamcrest.MatcherAssert.assertThat; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertNotEquals; import static org.junit.jupiter.api.Assertions.assertNotNull; import static org.junit.jupiter.api.Assertions.assertNull; import static org.junit.jupiter.api.Assertions.assertTrue; import static org.junit.jupiter.api.Assertions.fail; import java.time.Duration; import java.util.HashMap; import java.util.LinkedHashMap; import java.util.List; import java.util.Map; import java.util.Map.Entry; import java.util.concurrent.atomic.AtomicReference; import io.redis.test.annotations.EnabledOnCommand; import io.redis.test.annotations.SinceRedisVersion; import io.redis.test.annotations.ConditionalOnEnv; import io.redis.test.utils.RedisVersion; import org.hamcrest.Matchers; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Tag; import org.junit.jupiter.params.ParameterizedClass; import org.junit.jupiter.params.provider.MethodSource; import redis.clients.jedis.BuilderFactory; import redis.clients.jedis.Jedis; import redis.clients.jedis.Pipeline; import redis.clients.jedis.RedisProtocol; import redis.clients.jedis.Response; import redis.clients.jedis.StreamEntryID; import redis.clients.jedis.Transaction; import redis.clients.jedis.exceptions.JedisDataException; import redis.clients.jedis.exceptions.JedisException; import redis.clients.jedis.params.*; import redis.clients.jedis.resps.*; import redis.clients.jedis.args.StreamDeletionPolicy; import redis.clients.jedis.util.RedisVersionUtil; import redis.clients.jedis.util.SafeEncoder; import redis.clients.jedis.util.TestEnvUtil; @ParameterizedClass @MethodSource("redis.clients.jedis.commands.CommandsTestsParameters#respVersions") @Tag("integration") public class StreamsCommandsTest extends JedisCommandsTestBase { public StreamsCommandsTest(RedisProtocol protocol) { super(protocol); } @Test public void xadd() { try { Map map1 = new HashMap<>(); jedis.xadd("stream1", (StreamEntryID) null, map1); fail(); } catch (JedisDataException expected) { assertTrue(expected.getMessage().contains("wrong number of arguments")); } Map map1 = new HashMap<>(); map1.put("f1", "v1"); StreamEntryID id1 = jedis.xadd("xadd-stream1", (StreamEntryID) null, map1); assertNotNull(id1); Map map2 = new HashMap<>(); map2.put("f1", "v1"); map2.put("f2", "v2"); StreamEntryID id2 = jedis.xadd("xadd-stream1", (StreamEntryID) null, map2); assertTrue(id2.compareTo(id1) > 0); Map map3 = new HashMap<>(); map3.put("f2", "v2"); map3.put("f3", "v3"); StreamEntryID id3 = jedis.xadd("xadd-stream2", (StreamEntryID) null, map3); Map map4 = new HashMap<>(); map4.put("f2", "v2"); map4.put("f3", "v3"); StreamEntryID idIn = new StreamEntryID(id3.getTime() + 1, 1L); StreamEntryID id4 = jedis.xadd("xadd-stream2", idIn, map4); assertEquals(idIn, id4); assertTrue(id4.compareTo(id3) > 0); Map map5 = new HashMap<>(); map5.put("f4", "v4"); map5.put("f5", "v5"); StreamEntryID id5 = jedis.xadd("xadd-stream2", (StreamEntryID) null, map5); assertTrue(id5.compareTo(id4) > 0); Map map6 = new HashMap<>(); map6.put("f4", "v4"); map6.put("f5", "v5"); StreamEntryID id6 = jedis.xadd("xadd-stream2", map6, XAddParams.xAddParams().maxLen(3)); assertTrue(id6.compareTo(id5) > 0); assertEquals(3L, jedis.xlen("xadd-stream2")); } @Test public void xaddWithParams() { try { jedis.xadd("stream1", new HashMap<>(), XAddParams.xAddParams()); fail(); } catch (JedisDataException expected) { assertTrue(expected.getMessage().contains("wrong number of arguments")); } try { jedis.xadd("stream1", XAddParams.xAddParams(), new HashMap<>()); fail(); } catch (JedisDataException expected) { assertTrue(expected.getMessage().contains("wrong number of arguments")); } StreamEntryID id1 = jedis.xadd("xadd-stream1", (StreamEntryID) null, singletonMap("f1", "v1")); assertNotNull(id1); Map map2 = new HashMap<>(); map2.put("f1", "v1"); map2.put("f2", "v2"); StreamEntryID id2 = jedis.xadd("xadd-stream1", map2, XAddParams.xAddParams()); assertTrue(id2.compareTo(id1) > 0); Map map3 = new HashMap<>(); map3.put("f2", "v2"); map3.put("f3", "v3"); StreamEntryID id3 = jedis.xadd("xadd-stream2", XAddParams.xAddParams(), map3); Map map4 = new HashMap<>(); map4.put("f2", "v2"); map4.put("f3", "v3"); StreamEntryID idIn = new StreamEntryID(id3.getTime() + 1, 1L); StreamEntryID id4 = jedis.xadd("xadd-stream2", map4, XAddParams.xAddParams().id(idIn)); assertEquals(idIn, id4); assertTrue(id4.compareTo(id3) > 0); Map map5 = new HashMap<>(); map5.put("f4", "v4"); map5.put("f5", "v5"); StreamEntryID id5 = jedis.xadd("xadd-stream2", XAddParams.xAddParams(), map5); assertTrue(id5.compareTo(id4) > 0); Map map6 = new HashMap<>(); map6.put("f4", "v4"); map6.put("f5", "v5"); StreamEntryID id6 = jedis.xadd("xadd-stream2", map6, XAddParams.xAddParams().maxLen(3).exactTrimming()); assertTrue(id6.compareTo(id5) > 0); assertEquals(3L, jedis.xlen("xadd-stream2")); // nomkstream StreamEntryID id7 = jedis.xadd("xadd-stream3", XAddParams.xAddParams().noMkStream().maxLen(3).exactTrimming(), map6); assertNull(id7); assertFalse(jedis.exists("xadd-stream3")); // minid jedis.xadd("xadd-stream3", map6, XAddParams.xAddParams().minId("2").id(new StreamEntryID(2))); assertEquals(1L, jedis.xlen("xadd-stream3")); jedis.xadd("xadd-stream3", XAddParams.xAddParams().minId("4").id(new StreamEntryID(3)), map6); assertEquals(0L, jedis.xlen("xadd-stream3")); } @Test @SinceRedisVersion(value = "7.0.0") public void xaddParamsId() { StreamEntryID id; String key = "kk"; Map map = singletonMap("ff", "vv"); id = jedis.xadd(key, XAddParams.xAddParams().id(new StreamEntryID(0, 1)), map); assertNotNull(id); assertEquals("0-1", id.toString()); assertEquals(0, id.getTime()); assertEquals(1, id.getSequence()); id = jedis.xadd(key, XAddParams.xAddParams().id(2, 3), map); assertNotNull(id); assertEquals(2, id.getTime()); assertEquals(3, id.getSequence()); id = jedis.xadd(key, XAddParams.xAddParams().id(4), map); assertNotNull(id); assertEquals(4, id.getTime()); assertEquals(0, id.getSequence()); id = jedis.xadd(key, XAddParams.xAddParams().id("5-6"), map); assertNotNull(id); assertEquals(5, id.getTime()); assertEquals(6, id.getSequence()); id = jedis.xadd(key, XAddParams.xAddParams().id("7-8".getBytes()), map); assertNotNull(id); assertEquals(7, id.getTime()); assertEquals(8, id.getSequence()); id = jedis.xadd(key, XAddParams.xAddParams(), map); assertNotNull(id); } @Test @SinceRedisVersion("8.1.240") public void xaddWithTrimmingModeKeepReferences() { String streamKey = "xadd-trim-keep-ref-stream"; String groupName = "test-group"; String consumerName = "test-consumer"; Map map = singletonMap("field", "value"); // Add initial entries to the stream for (int i = 1; i <= 5; i++) { jedis.xadd(streamKey, XAddParams.xAddParams().id(new StreamEntryID(i + "-0")), map); } assertEquals(5L, jedis.xlen(streamKey)); // Create consumer group and read messages to create PEL entries jedis.xgroupCreate(streamKey, groupName, new StreamEntryID("0-0"), false); Map streamQuery = singletonMap(streamKey, StreamEntryID.XREADGROUP_UNDELIVERED_ENTRY); jedis.xreadGroup(groupName, consumerName, XReadGroupParams.xReadGroupParams().count(3), streamQuery); // Verify PEL has entries List pendingBefore = jedis.xpending(streamKey, groupName, XPendingParams.xPendingParams().count(10)); assertEquals(3, pendingBefore.size()); // Add new entry with maxLen=3 and KEEP_REFERENCES mode StreamEntryID newId = jedis.xadd(streamKey, XAddParams.xAddParams() .id(new StreamEntryID("6-0")) .maxLen(3) .trimmingMode(StreamDeletionPolicy.KEEP_REFERENCES), map); assertNotNull(newId); // Stream should be trimmed to 3 entries assertEquals(3L, jedis.xlen(streamKey)); // PEL references should be preserved even though entries were trimmed List pendingAfter = jedis.xpending(streamKey, groupName, XPendingParams.xPendingParams().count(10)); assertEquals(3, pendingAfter.size()); // PEL entries should still exist } @Test @SinceRedisVersion("8.1.240") public void xaddWithTrimmingModeDeleteReferences() { String streamKey = "xadd-trim-del-ref-stream"; String groupName = "test-group"; String consumerName = "test-consumer"; Map map = singletonMap("field", "value"); // Add initial entries to the stream for (int i = 1; i <= 5; i++) { jedis.xadd(streamKey, XAddParams.xAddParams().id(new StreamEntryID(i + "-0")), map); } assertEquals(5L, jedis.xlen(streamKey)); // Create consumer group and read messages to create PEL entries jedis.xgroupCreate(streamKey, groupName, new StreamEntryID("0-0"), false); Map streamQuery = singletonMap(streamKey, StreamEntryID.XREADGROUP_UNDELIVERED_ENTRY); jedis.xreadGroup(groupName, consumerName, XReadGroupParams.xReadGroupParams().count(3), streamQuery); // Verify PEL has entries List pendingBefore = jedis.xpending(streamKey, groupName, XPendingParams.xPendingParams().count(10)); assertEquals(3, pendingBefore.size()); // Add new entry with maxLen=3 and DELETE_REFERENCES mode StreamEntryID newId = jedis.xadd(streamKey, XAddParams.xAddParams() .id(new StreamEntryID("6-0")) .maxLen(3) .trimmingMode(StreamDeletionPolicy.DELETE_REFERENCES), map); assertNotNull(newId); // Stream should be trimmed to 3 entries assertEquals(3L, jedis.xlen(streamKey)); // PEL references should be removed for trimmed entries List pendingAfter = jedis.xpending(streamKey, groupName, XPendingParams.xPendingParams().count(10)); // Only entries that still exist in the stream should remain in PEL assertTrue(pendingAfter.size() <= 3); } @Test @SinceRedisVersion("8.1.240") public void xaddWithTrimmingModeAcknowledged() { String streamKey = "xadd-trim-acked-stream"; String groupName = "test-group"; String consumerName = "test-consumer"; Map map = singletonMap("field", "value"); // Add initial entries to the stream for (int i = 1; i <= 5; i++) { jedis.xadd(streamKey, XAddParams.xAddParams().id(new StreamEntryID(i + "-0")), map); } assertEquals(5L, jedis.xlen(streamKey)); // Create consumer group and read messages jedis.xgroupCreate(streamKey, groupName, new StreamEntryID("0-0"), false); Map streamQuery = singletonMap(streamKey, StreamEntryID.XREADGROUP_UNDELIVERED_ENTRY); List>> messages = jedis.xreadGroup(groupName, consumerName, XReadGroupParams.xReadGroupParams().count(3), streamQuery); // Acknowledge the first 2 messages StreamEntryID id1 = messages.get(0).getValue().get(0).getID(); StreamEntryID id2 = messages.get(0).getValue().get(1).getID(); jedis.xack(streamKey, groupName, id1, id2); // Verify PEL state List pendingBefore = jedis.xpending(streamKey, groupName, XPendingParams.xPendingParams().count(10)); assertEquals(1, pendingBefore.size()); // Only 1 unacknowledged message // Add new entry with maxLen=3 and ACKNOWLEDGED mode StreamEntryID newId = jedis.xadd(streamKey, XAddParams.xAddParams() .id(new StreamEntryID("6-0")) .maxLen(3) .trimmingMode(StreamDeletionPolicy.ACKNOWLEDGED), map); assertNotNull(newId); // Stream length should respect acknowledgment status long streamLen = jedis.xlen(streamKey); assertTrue(streamLen >= 3); // Should not trim unacknowledged entries aggressively // PEL should still contain unacknowledged entries List pendingAfter = jedis.xpending(streamKey, groupName, XPendingParams.xPendingParams().count(10)); assertFalse(pendingAfter.isEmpty()); // Unacknowledged entries should remain } @Test @SinceRedisVersion("8.1.240") public void xaddWithMinIdTrimmingModeKeepReferences() { String streamKey = "xadd-minid-keep-ref-stream"; String groupName = "test-group"; String consumerName = "test-consumer"; Map map = singletonMap("field", "value"); // Add initial entries with specific IDs for (int i = 1; i <= 5; i++) { jedis.xadd(streamKey, XAddParams.xAddParams().id(new StreamEntryID("0-" + i)), map); } assertEquals(5L, jedis.xlen(streamKey)); // Create consumer group and read messages to create PEL entries jedis.xgroupCreate(streamKey, groupName, new StreamEntryID("0-0"), false); Map streamQuery = singletonMap(streamKey, StreamEntryID.XREADGROUP_UNDELIVERED_ENTRY); jedis.xreadGroup(groupName, consumerName, XReadGroupParams.xReadGroupParams().count(3), streamQuery); // Verify PEL has entries List pendingBefore = jedis.xpending(streamKey, groupName, XPendingParams.xPendingParams().count(10)); assertEquals(3, pendingBefore.size()); // Add new entry with minId="0-3" and KEEP_REFERENCES mode (should trim entries < 0-3) StreamEntryID newId = jedis.xadd(streamKey, XAddParams.xAddParams() .id(new StreamEntryID("0-6")) .minId("0-3") .trimmingMode(StreamDeletionPolicy.KEEP_REFERENCES), map); assertNotNull(newId); // Stream should have entries >= 0-3 plus the new entry long streamLen = jedis.xlen(streamKey); assertTrue(streamLen >= 3); // Should keep entries 0-3, 0-4, 0-5, 0-6 // PEL references should be preserved even for trimmed entries List pendingAfter = jedis.xpending(streamKey, groupName, XPendingParams.xPendingParams().count(10)); assertEquals(3, pendingAfter.size()); // PEL entries should still exist } @Test @SinceRedisVersion("8.1.240") public void xaddWithMinIdTrimmingModeDeleteReferences() { String streamKey = "xadd-minid-del-ref-stream"; String groupName = "test-group"; String consumerName = "test-consumer"; Map map = singletonMap("field", "value"); // Add initial entries with specific IDs for (int i = 1; i <= 5; i++) { jedis.xadd(streamKey, XAddParams.xAddParams().id(new StreamEntryID("0-" + i)), map); } assertEquals(5L, jedis.xlen(streamKey)); // Create consumer group and read messages to create PEL entries jedis.xgroupCreate(streamKey, groupName, new StreamEntryID("0-0"), false); Map streamQuery = singletonMap(streamKey, StreamEntryID.XREADGROUP_UNDELIVERED_ENTRY); jedis.xreadGroup(groupName, consumerName, XReadGroupParams.xReadGroupParams().count(3), streamQuery); // Verify PEL has entries List pendingBefore = jedis.xpending(streamKey, groupName, XPendingParams.xPendingParams().count(10)); assertEquals(3, pendingBefore.size()); // Add new entry with minId="0-3" and DELETE_REFERENCES mode StreamEntryID newId = jedis.xadd(streamKey, XAddParams.xAddParams() .id(new StreamEntryID("0-6")) .minId("0-3") .trimmingMode(StreamDeletionPolicy.DELETE_REFERENCES), map); assertNotNull(newId); // Stream should have entries >= 0-3 plus the new entry long streamLen = jedis.xlen(streamKey); assertTrue(streamLen >= 3); // PEL references should be removed for trimmed entries (0-1, 0-2) List pendingAfter = jedis.xpending(streamKey, groupName, XPendingParams.xPendingParams().count(10)); // Only entries that still exist in the stream should remain in PEL assertTrue(pendingAfter.size() <= pendingBefore.size()); } @Test @SinceRedisVersion("8.1.240") public void xaddWithApproximateTrimmingAndTrimmingMode() { String streamKey = "xadd-approx-trim-stream"; String groupName = "test-group"; String consumerName = "test-consumer"; Map map = singletonMap("field", "value"); // Add initial entries for (int i = 1; i <= 10; i++) { jedis.xadd(streamKey, XAddParams.xAddParams().id(new StreamEntryID(i + "-0")), map); } assertEquals(10L, jedis.xlen(streamKey)); // Create consumer group and read messages jedis.xgroupCreate(streamKey, groupName, new StreamEntryID("0-0"), false); Map streamQuery = singletonMap(streamKey, StreamEntryID.XREADGROUP_UNDELIVERED_ENTRY); jedis.xreadGroup(groupName, consumerName, XReadGroupParams.xReadGroupParams().count(5), streamQuery); // Add new entry with approximate trimming and KEEP_REFERENCES mode StreamEntryID newId = jedis.xadd(streamKey, XAddParams.xAddParams() .id(new StreamEntryID("11-0")) .maxLen(5) .approximateTrimming() .trimmingMode(StreamDeletionPolicy.KEEP_REFERENCES), map); assertNotNull(newId); // With approximate trimming, the exact length may vary but should be around the target long streamLen = jedis.xlen(streamKey); assertTrue(streamLen >= 5); // Should be approximately 5, but may be more due to approximation // PEL should preserve references List pendingAfter = jedis.xpending(streamKey, groupName, XPendingParams.xPendingParams().count(10)); assertEquals(5, pendingAfter.size()); // All read messages should remain in PEL } @Test @SinceRedisVersion("8.1.240") public void xaddWithExactTrimmingAndTrimmingMode() { String streamKey = "xadd-exact-trim-mode-stream"; String groupName = "test-group"; String consumerName = "test-consumer"; Map map = singletonMap("field", "value"); // Add initial entries for (int i = 1; i <= 5; i++) { jedis.xadd(streamKey, XAddParams.xAddParams().id(new StreamEntryID(i + "-0")), map); } assertEquals(5L, jedis.xlen(streamKey)); // Create consumer group and read messages jedis.xgroupCreate(streamKey, groupName, new StreamEntryID("0-0"), false); Map streamQuery = singletonMap(streamKey, StreamEntryID.XREADGROUP_UNDELIVERED_ENTRY); jedis.xreadGroup(groupName, consumerName, XReadGroupParams.xReadGroupParams().count(3), streamQuery); // Add new entry with exact trimming and DELETE_REFERENCES mode StreamEntryID newId = jedis.xadd(streamKey, XAddParams.xAddParams() .id(new StreamEntryID("6-0")) .maxLen(3) .exactTrimming() .trimmingMode(StreamDeletionPolicy.DELETE_REFERENCES), map); assertNotNull(newId); // With exact trimming, stream should be exactly 3 entries assertEquals(3L, jedis.xlen(streamKey)); // PEL references should be cleaned up for trimmed entries List pendingAfter = jedis.xpending(streamKey, groupName, XPendingParams.xPendingParams().count(10)); // Only entries that still exist in the stream should remain in PEL assertTrue(pendingAfter.size() <= 3); } @Test @SinceRedisVersion("8.1.240") public void xaddWithLimitAndTrimmingMode() { String streamKey = "xadd-limit-trim-mode-stream"; String groupName = "test-group"; String consumerName = "test-consumer"; Map map = singletonMap("field", "value"); // Add initial entries for (int i = 1; i <= 10; i++) { jedis.xadd(streamKey, XAddParams.xAddParams().id(new StreamEntryID(i + "-0")), map); } assertEquals(10L, jedis.xlen(streamKey)); // Create consumer group and read messages jedis.xgroupCreate(streamKey, groupName, new StreamEntryID("0-0"), false); Map streamQuery = singletonMap(streamKey, StreamEntryID.XREADGROUP_UNDELIVERED_ENTRY); jedis.xreadGroup(groupName, consumerName, XReadGroupParams.xReadGroupParams().count(5), streamQuery); // Add new entry with limit and KEEP_REFERENCES mode (limit requires approximate trimming) StreamEntryID newId = jedis.xadd(streamKey, XAddParams.xAddParams() .id(new StreamEntryID("11-0")) .maxLen(5) .approximateTrimming() // Required for limit to work .limit(2) // Limit the number of entries to examine for trimming .trimmingMode(StreamDeletionPolicy.KEEP_REFERENCES), map); assertNotNull(newId); // With limit, trimming may be less aggressive long streamLen = jedis.xlen(streamKey); assertTrue(streamLen >= 5); // Should be at least 5, but may be more due to limit // PEL should preserve references List pendingAfter = jedis.xpending(streamKey, groupName, XPendingParams.xPendingParams().count(10)); assertEquals(5, pendingAfter.size()); // All read messages should remain in PEL } @Test public void xdel() { Map map1 = new HashMap<>(); map1.put("f1", "v1"); StreamEntryID id1 = jedis.xadd("xdel-stream", (StreamEntryID) null, map1); assertNotNull(id1); StreamEntryID id2 = jedis.xadd("xdel-stream", (StreamEntryID) null, map1); assertNotNull(id2); assertEquals(2L, jedis.xlen("xdel-stream")); assertEquals(1L, jedis.xdel("xdel-stream", id1)); assertEquals(1L, jedis.xlen("xdel-stream")); } @Test public void xlen() { assertEquals(0L, jedis.xlen("xlen-stream")); Map map = new HashMap<>(); map.put("f1", "v1"); jedis.xadd("xlen-stream", (StreamEntryID) null, map); assertEquals(1L, jedis.xlen("xlen-stream")); jedis.xadd("xlen-stream", (StreamEntryID) null, map); assertEquals(2L, jedis.xlen("xlen-stream")); } @Test public void xrange() { List range = jedis.xrange("xrange-stream", (StreamEntryID) null, (StreamEntryID) null, Integer.MAX_VALUE); assertEquals(0, range.size()); Map map = new HashMap<>(); map.put("f1", "v1"); StreamEntryID id1 = jedis.xadd("xrange-stream", (StreamEntryID) null, map); StreamEntryID id2 = jedis.xadd("xrange-stream", (StreamEntryID) null, map); List range2 = jedis.xrange("xrange-stream", (StreamEntryID) null, (StreamEntryID) null, 3); assertEquals(2, range2.size()); assertEquals(range2.get(0).toString(), id1 + " " + map); List range3 = jedis.xrange("xrange-stream", id1, null, 2); assertEquals(2, range3.size()); List range4 = jedis.xrange("xrange-stream", id1, id2, 2); assertEquals(2, range4.size()); List range5 = jedis.xrange("xrange-stream", id1, id2, 1); assertEquals(1, range5.size()); List range6 = jedis.xrange("xrange-stream", id2, null, 4); assertEquals(1, range6.size()); StreamEntryID id3 = jedis.xadd("xrange-stream", (StreamEntryID) null, map); List range7 = jedis.xrange("xrange-stream", id3, id3, 4); assertEquals(1, range7.size()); List range8 = jedis.xrange("xrange-stream", (StreamEntryID) null, (StreamEntryID) null); assertEquals(3, range8.size()); range8 = jedis.xrange("xrange-stream", StreamEntryID.MINIMUM_ID, StreamEntryID.MAXIMUM_ID); assertEquals(3, range8.size()); } @Test public void xrangeExclusive() { Map map = new HashMap<>(); map.put("f1", "v1"); String id1 = jedis.xadd("xrange-stream", (StreamEntryID) null, map).toString(); jedis.xadd("xrange-stream", (StreamEntryID) null, map); List range2 = jedis.xrange("xrange-stream", id1, "+", 2); assertEquals(2, range2.size()); List range3 = jedis.xrange("xrange-stream", "(" + id1, "+", 2); assertEquals(1, range3.size()); } @Test @ConditionalOnEnv(value = TestEnvUtil.ENV_REDIS_ENTERPRISE, enabled = false) public void xreadWithParams() { final String key1 = "xread-stream1"; final String key2 = "xread-stream2"; Map streamQeury1 = singletonMap(key1, new StreamEntryID()); // Before creating Stream assertNull(jedis.xread(XReadParams.xReadParams().block(1), streamQeury1)); assertNull(jedis.xread(XReadParams.xReadParams(), streamQeury1)); Map map = new HashMap<>(); map.put("f1", "v1"); StreamEntryID id1 = jedis.xadd(key1, (StreamEntryID) null, map); StreamEntryID id2 = jedis.xadd(key2, (StreamEntryID) null, map); // Read only a single Stream List>> streams1 = jedis.xread(XReadParams.xReadParams().count(1).block(1), streamQeury1); assertEquals(1, streams1.size()); assertEquals(key1, streams1.get(0).getKey()); assertEquals(1, streams1.get(0).getValue().size()); assertEquals(id1, streams1.get(0).getValue().get(0).getID()); assertEquals(map, streams1.get(0).getValue().get(0).getFields()); assertNull(jedis.xread(XReadParams.xReadParams().block(1), singletonMap(key1, id1))); assertNull(jedis.xread(XReadParams.xReadParams(), singletonMap(key1, id1))); // Read from two Streams Map streamQuery2 = new LinkedHashMap<>(); streamQuery2.put(key1, new StreamEntryID()); streamQuery2.put(key2, new StreamEntryID()); List>> streams2 = jedis.xread(XReadParams.xReadParams().count(2).block(1), streamQuery2); assertEquals(2, streams2.size()); } @Test @ConditionalOnEnv(value = TestEnvUtil.ENV_REDIS_ENTERPRISE, enabled = false) public void xreadAsMap() { final String stream1 = "xread-stream1"; final String stream2 = "xread-stream2"; Map streamQeury1 = singletonMap(stream1, new StreamEntryID()); // Before creating Stream assertNull(jedis.xreadAsMap(XReadParams.xReadParams().block(1), streamQeury1)); assertNull(jedis.xreadAsMap(XReadParams.xReadParams(), streamQeury1)); Map map = new HashMap<>(); map.put("f1", "v1"); StreamEntryID id1 = new StreamEntryID(1); StreamEntryID id2 = new StreamEntryID(2); StreamEntryID id3 = new StreamEntryID(3); assertEquals(id1, jedis.xadd(stream1, id1, map)); assertEquals(id2, jedis.xadd(stream2, id2, map)); assertEquals(id3, jedis.xadd(stream1, id3, map)); // Read only a single Stream Map> streams1 = jedis.xreadAsMap(XReadParams.xReadParams().count(2), streamQeury1); assertEquals(singleton(stream1), streams1.keySet()); List list1 = streams1.get(stream1); assertEquals(2, list1.size()); assertEquals(id1, list1.get(0).getID()); assertEquals(map, list1.get(0).getFields()); assertEquals(id3, list1.get(1).getID()); assertEquals(map, list1.get(1).getFields()); // Read from two Streams Map streamQuery2 = new LinkedHashMap<>(); streamQuery2.put(stream1, new StreamEntryID()); streamQuery2.put(stream2, new StreamEntryID()); Map> streams2 = jedis.xreadAsMap(XReadParams.xReadParams().count(1), streamQuery2); assertEquals(2, streams2.size()); assertEquals(id1, streams2.get(stream1).get(0).getID()); assertEquals(id2, streams2.get(stream2).get(0).getID()); } @Test @SinceRedisVersion(value = "7.4.0", message = "From Redis 7.4, you can use the + sign as a special ID to request last entry") public void xreadAsMapLastEntry() { final String stream1 = "xread-stream1"; final String stream2 = "xread-stream2"; Map streamQeury1 = singletonMap(stream1, new StreamEntryID()); // Before creating Stream assertNull(jedis.xreadAsMap(XReadParams.xReadParams().block(1), streamQeury1)); assertNull(jedis.xreadAsMap(XReadParams.xReadParams(), streamQeury1)); Map map = new HashMap<>(); map.put("f1", "v1"); StreamEntryID id1 = new StreamEntryID(1); StreamEntryID id2 = new StreamEntryID(2); StreamEntryID id3 = new StreamEntryID(3); assertEquals(id1, jedis.xadd(stream1, id1, map)); assertEquals(id2, jedis.xadd(stream2, id2, map)); assertEquals(id3, jedis.xadd(stream1, id3, map)); // Read from last entry Map streamQueryLE = singletonMap(stream1, StreamEntryID.XREAD_LAST_ENTRY); Map> streamsLE = jedis.xreadAsMap(XReadParams.xReadParams().count(1), streamQueryLE); assertEquals(singleton(stream1), streamsLE.keySet()); assertEquals(1, streamsLE.get(stream1).size()); assertEquals(id3, streamsLE.get(stream1).get(0).getID()); assertEquals(map, streamsLE.get(stream1).get(0).getFields()); } @Test public void xreadBlockZero() throws InterruptedException { final AtomicReference readId = new AtomicReference<>(); Thread t = new Thread(new Runnable() { @Override public void run() { try (Jedis blockJedis = createJedis()) { long startTime = System.currentTimeMillis(); List>> read = blockJedis.xread(XReadParams.xReadParams().block(0), singletonMap("block0-stream", new StreamEntryID())); long endTime = System.currentTimeMillis(); assertTrue(endTime - startTime > 500); assertNotNull(read); readId.set(read.get(0).getValue().get(0).getID()); } } }, "xread-block-0-thread"); t.start(); Thread.sleep(1000); StreamEntryID addedId = jedis.xadd("block0-stream", (StreamEntryID) null, singletonMap("foo", "bar")); t.join(); assertEquals(addedId, readId.get()); } @Test public void xtrim() { Map map1 = new HashMap(); map1.put("f1", "v1"); for (int i = 1; i <= 5; i++) { jedis.xadd("xtrim-stream", (StreamEntryID) null, map1); } assertEquals(5L, jedis.xlen("xtrim-stream")); jedis.xtrim("xtrim-stream", 3, false); assertEquals(3L, jedis.xlen("xtrim-stream")); } @Test public void xtrimWithParams() { Map map1 = new HashMap<>(); map1.put("f1", "v1"); for (int i = 1; i <= 5; i++) { jedis.xadd("xtrim-stream", new StreamEntryID("0-" + i), map1); } assertEquals(5L, jedis.xlen("xtrim-stream")); jedis.xtrim("xtrim-stream", XTrimParams.xTrimParams().maxLen(3).exactTrimming()); assertEquals(3L, jedis.xlen("xtrim-stream")); // minId jedis.xtrim("xtrim-stream", XTrimParams.xTrimParams().minId("0-4").exactTrimming()); assertEquals(2L, jedis.xlen("xtrim-stream")); } @Test public void xrevrange() { List range = jedis.xrevrange("xrevrange-stream", (StreamEntryID) null, (StreamEntryID) null, Integer.MAX_VALUE); assertEquals(0, range.size()); Map map = new HashMap<>(); map.put("f1", "v1"); StreamEntryID id1 = jedis.xadd("xrevrange-stream", (StreamEntryID) null, map); StreamEntryID id2 = jedis.xadd("xrevrange-stream", (StreamEntryID) null, map); List range2 = jedis.xrange("xrevrange-stream", (StreamEntryID) null, (StreamEntryID) null, 3); assertEquals(2, range2.size()); List range3 = jedis.xrevrange("xrevrange-stream", null, id1, 2); assertEquals(2, range3.size()); List range4 = jedis.xrevrange("xrevrange-stream", id2, id1, 2); assertEquals(2, range4.size()); List range5 = jedis.xrevrange("xrevrange-stream", id2, id1, 1); assertEquals(1, range5.size()); List range6 = jedis.xrevrange("xrevrange-stream", null, id2, 4); assertEquals(1, range6.size()); StreamEntryID id3 = jedis.xadd("xrevrange-stream", (StreamEntryID) null, map); List range7 = jedis.xrevrange("xrevrange-stream", id3, id3, 4); assertEquals(1, range7.size()); List range8 = jedis.xrevrange("xrevrange-stream", (StreamEntryID) null, (StreamEntryID) null); assertEquals(3, range8.size()); range8 = jedis.xrevrange("xrevrange-stream", StreamEntryID.MAXIMUM_ID, StreamEntryID.MINIMUM_ID); assertEquals(3, range8.size()); } @Test public void xrevrangeExclusive() { Map map = new HashMap<>(); map.put("f1", "v1"); String id1 = jedis.xadd("xrange-stream", (StreamEntryID) null, map).toString(); jedis.xadd("xrange-stream", (StreamEntryID) null, map); List range2 = jedis.xrevrange("xrange-stream", "+", id1, 2); assertEquals(2, range2.size()); List range3 = jedis.xrevrange("xrange-stream", "+", "(" + id1, 2); assertEquals(1, range3.size()); } @Test public void xgroup() { Map map = new HashMap<>(); map.put("f1", "v1"); StreamEntryID id1 = jedis.xadd("xgroup-stream", (StreamEntryID) null, map); assertEquals("OK", jedis.xgroupCreate("xgroup-stream", "consumer-group-name", null, false)); assertEquals("OK", jedis.xgroupSetID("xgroup-stream", "consumer-group-name", id1)); assertEquals("OK", jedis.xgroupCreate("xgroup-stream", "consumer-group-name1", StreamEntryID.XGROUP_LAST_ENTRY, false)); jedis.xgroupDestroy("xgroup-stream", "consumer-group-name"); assertEquals(0L, jedis.xgroupDelConsumer("xgroup-stream", "consumer-group-name1","myconsumer1")); assertTrue(jedis.xgroupCreateConsumer("xgroup-stream", "consumer-group-name1","myconsumer2")); assertEquals(0L, jedis.xgroupDelConsumer("xgroup-stream", "consumer-group-name1","myconsumer2")); } @Test @ConditionalOnEnv(value = TestEnvUtil.ENV_REDIS_ENTERPRISE, enabled = false) public void xreadGroupWithParams() { // Simple xreadGroup with NOACK Map map = new HashMap<>(); map.put("f1", "v1"); jedis.xadd("xreadGroup-stream1", (StreamEntryID) null, map); jedis.xgroupCreate("xreadGroup-stream1", "xreadGroup-group", null, false); Map streamQeury1 = singletonMap("xreadGroup-stream1", StreamEntryID.XREADGROUP_UNDELIVERED_ENTRY); List>> range = jedis.xreadGroup("xreadGroup-group", "xreadGroup-consumer", XReadGroupParams.xReadGroupParams().count(1).noAck(), streamQeury1); assertEquals(1, range.size()); assertEquals(1, range.get(0).getValue().size()); jedis.xadd("xreadGroup-stream1", (StreamEntryID) null, map); jedis.xadd("xreadGroup-stream2", (StreamEntryID) null, map); jedis.xgroupCreate("xreadGroup-stream2", "xreadGroup-group", null, false); // Read only a single Stream List>> streams1 = jedis.xreadGroup("xreadGroup-group", "xreadGroup-consumer", XReadGroupParams.xReadGroupParams().count(1).block(1).noAck(), streamQeury1); assertEquals(1, streams1.size()); assertEquals(1, streams1.get(0).getValue().size()); // Read from two Streams Map streamQuery2 = new LinkedHashMap<>(); streamQuery2.put("xreadGroup-stream1", new StreamEntryID()); streamQuery2.put("xreadGroup-stream2", new StreamEntryID()); List>> streams2 = jedis.xreadGroup("xreadGroup-group", "xreadGroup-consumer", XReadGroupParams.xReadGroupParams().count(1).block(1).noAck(), streamQuery2); assertEquals(2, streams2.size()); // Read only fresh messages StreamEntryID id4 = jedis.xadd("xreadGroup-stream1", (StreamEntryID) null, map); Map streamQeuryFresh = singletonMap("xreadGroup-stream1", StreamEntryID.XREADGROUP_UNDELIVERED_ENTRY); List>> streamsFresh = jedis.xreadGroup("xreadGroup-group", "xreadGroup-consumer", XReadGroupParams.xReadGroupParams().count(4).block(100).noAck(), streamQeuryFresh); assertEquals(1, streamsFresh.size()); assertEquals(id4, streamsFresh.get(0).getValue().get(0).getID()); } @Test public void xreadGroupAsMap() { final String stream1 = "xreadGroup-stream1"; Map map = singletonMap("f1", "v1"); StreamEntryID id1 = jedis.xadd(stream1, StreamEntryID.NEW_ENTRY, map); jedis.xgroupCreate(stream1, "xreadGroup-group", null, false); Map streamQeury1 = singletonMap(stream1, StreamEntryID.XREADGROUP_UNDELIVERED_ENTRY); Map> range = jedis.xreadGroupAsMap("xreadGroup-group", "xreadGroup-consumer", XReadGroupParams.xReadGroupParams().noAck(), streamQeury1); assertEquals(singleton(stream1), range.keySet()); List list = range.get(stream1); assertEquals(1, list.size()); assertEquals(id1, list.get(0).getID()); assertEquals(map, list.get(0).getFields()); assertNull(list.get(0).getMillisElapsedFromDelivery()); assertNull(list.get(0).getDeliveredCount()); } @Test public void xreadGroupWithParamsWhenPendingMessageIsDiscarded() { // Add two message to stream Map map1 = new HashMap<>(); map1.put("f1", "v1"); Map map2 = new HashMap<>(); map2.put("f2", "v2"); XAddParams xAddParams = XAddParams.xAddParams().id(StreamEntryID.NEW_ENTRY).maxLen(2); StreamEntryID firstMessageEntryId = jedis.xadd("xreadGroup-discard-stream1", xAddParams, map1); jedis.xadd("xreadGroup-discard-stream1", xAddParams, map2); jedis.xgroupCreate("xreadGroup-discard-stream1", "xreadGroup-group", null, false); Map streamQuery1 = singletonMap("xreadGroup-discard-stream1", StreamEntryID.XREADGROUP_UNDELIVERED_ENTRY); List>> range = jedis.xreadGroup("xreadGroup-group", "xreadGroup-consumer", XReadGroupParams.xReadGroupParams().count(1), streamQuery1); assertEquals(1, range.size()); assertEquals(1, range.get(0).getValue().size()); assertEquals(firstMessageEntryId, range.get(0).getValue().get(0).getID()); assertEquals(map1, range.get(0).getValue().get(0).getFields()); assertNull(range.get(0).getValue().get(0).getMillisElapsedFromDelivery()); assertNull(range.get(0).getValue().get(0).getDeliveredCount()); // Add third message, the fields of pending message1 will be discarded by redis-server Map map3 = new HashMap<>(); map3.put("f3", "v3"); jedis.xadd("xreadGroup-discard-stream1", xAddParams, map3); Map streamQueryPending = singletonMap("xreadGroup-discard-stream1", new StreamEntryID()); List>> pendingMessages = jedis.xreadGroup("xreadGroup-group", "xreadGroup-consumer", XReadGroupParams.xReadGroupParams().count(1).noAck(), streamQueryPending); assertEquals(1, pendingMessages.size()); assertEquals(1, pendingMessages.get(0).getValue().size()); assertEquals(firstMessageEntryId, pendingMessages.get(0).getValue().get(0).getID()); assertNull(pendingMessages.get(0).getValue().get(0).getFields()); assertNull(pendingMessages.get(0).getValue().get(0).getDeliveredCount()); assertNull(pendingMessages.get(0).getValue().get(0).getMillisElapsedFromDelivery()); } @Test public void xack() { Map map = new HashMap<>(); map.put("f1", "v1"); jedis.xadd("xack-stream", (StreamEntryID) null, map); jedis.xgroupCreate("xack-stream", "xack-group", null, false); Map streamQeury1 = singletonMap("xack-stream", StreamEntryID.XREADGROUP_UNDELIVERED_ENTRY); // Empty Stream List>> range = jedis.xreadGroup("xack-group", "xack-consumer", XReadGroupParams.xReadGroupParams().count(1).block(1), streamQeury1); assertEquals(1, range.size()); assertEquals(1L, jedis.xack("xack-stream", "xack-group", range.get(0).getValue().get(0).getID())); } @Test public void xpendingWithParams() { final String stream = "xpendeing-stream"; assertEquals("OK", jedis.xgroupCreate(stream, "xpendeing-group", null, true)); // Get the summary from empty stream StreamPendingSummary emptySummary = jedis.xpending(stream, "xpendeing-group"); assertEquals(0, emptySummary.getTotal()); assertNull(emptySummary.getMinId()); assertNull(emptySummary.getMaxId()); assertNull(emptySummary.getConsumerMessageCount()); Map map = new HashMap<>(); map.put("f1", "v1"); StreamEntryID id1 = jedis.xadd(stream, (StreamEntryID) null, map); Map streamQeury1 = singletonMap(stream, StreamEntryID.XREADGROUP_UNDELIVERED_ENTRY); // Read the event from Stream put it on pending List>> range = jedis.xreadGroup("xpendeing-group", "xpendeing-consumer", XReadGroupParams.xReadGroupParams().count(1).block(1), streamQeury1); assertEquals(1, range.size()); assertEquals(1, range.get(0).getValue().size()); assertEquals(map, range.get(0).getValue().get(0).getFields()); // Get the summary about the pending messages StreamPendingSummary pendingSummary = jedis.xpending(stream, "xpendeing-group"); assertEquals(1, pendingSummary.getTotal()); assertEquals(id1, pendingSummary.getMinId()); assertEquals(1l, pendingSummary.getConsumerMessageCount().get("xpendeing-consumer").longValue()); // Get the pending event List pendingRange = jedis.xpending(stream, "xpendeing-group", new XPendingParams().count(3).consumer("xpendeing-consumer")); assertEquals(1, pendingRange.size()); assertEquals(id1, pendingRange.get(0).getID()); assertEquals(1, pendingRange.get(0).getDeliveredTimes()); assertEquals("xpendeing-consumer", pendingRange.get(0).getConsumerName()); assertTrue(pendingRange.get(0).toString().contains("xpendeing-consumer")); // Without consumer pendingRange = jedis.xpending(stream, "xpendeing-group", new XPendingParams().count(3)); assertEquals(1, pendingRange.size()); assertEquals(id1, pendingRange.get(0).getID()); assertEquals(1, pendingRange.get(0).getDeliveredTimes()); assertEquals("xpendeing-consumer", pendingRange.get(0).getConsumerName()); // with idle pendingRange = jedis.xpending(stream, "xpendeing-group", new XPendingParams().idle(Duration.ofMinutes(1).toMillis()).count(3)); assertEquals(0, pendingRange.size()); } @Test public void xpendingRange() { final String stream = "xpendeing-stream"; Map map = new HashMap<>(); map.put("foo", "bar"); StreamEntryID m1 = jedis.xadd(stream, (StreamEntryID) null, map); StreamEntryID m2 = jedis.xadd(stream, (StreamEntryID) null, map); jedis.xgroupCreate(stream, "xpendeing-group", null, false); // read 1 message from the group with each consumer Map streamQeury = singletonMap(stream, StreamEntryID.XREADGROUP_UNDELIVERED_ENTRY); jedis.xreadGroup("xpendeing-group", "consumer1", XReadGroupParams.xReadGroupParams().count(1), streamQeury); jedis.xreadGroup("xpendeing-group", "consumer2", XReadGroupParams.xReadGroupParams().count(1), streamQeury); List response = jedis.xpending(stream, "xpendeing-group", XPendingParams.xPendingParams("(0", "+", 5)); assertEquals(2, response.size()); assertEquals(m1, response.get(0).getID()); assertEquals("consumer1", response.get(0).getConsumerName()); assertEquals(m2, response.get(1).getID()); assertEquals("consumer2", response.get(1).getConsumerName()); response = jedis.xpending(stream, "xpendeing-group", XPendingParams.xPendingParams(StreamEntryID.MINIMUM_ID, StreamEntryID.MAXIMUM_ID, 5)); assertEquals(2, response.size()); assertEquals(m1, response.get(0).getID()); assertEquals("consumer1", response.get(0).getConsumerName()); assertEquals(m2, response.get(1).getID()); assertEquals("consumer2", response.get(1).getConsumerName()); } @Test public void xclaimWithParams() { final String stream = "xpendeing-stream"; Map map = new HashMap<>(); map.put("f1", "v1"); jedis.xadd(stream, (StreamEntryID) null, map); assertEquals("OK", jedis.xgroupCreate(stream, "xpendeing-group", null, false)); // Read the event from Stream put it on pending jedis.xreadGroup("xpendeing-group", "xpendeing-consumer", XReadGroupParams.xReadGroupParams().count(1).block(1), singletonMap(stream, StreamEntryID.XREADGROUP_UNDELIVERED_ENTRY)); // Get the pending event List pendingRange = jedis.xpending(stream, "xpendeing-group", XPendingParams.xPendingParams().count(3).consumer("xpendeing-consumer")); // Sleep for 100ms so we can claim events pending for more than 50ms try { Thread.sleep(100); } catch (InterruptedException e) { e.printStackTrace(); } List streamEntrys = jedis.xclaim(stream, "xpendeing-group", "xpendeing-consumer2", 50, XClaimParams.xClaimParams().idle(0).retryCount(0), pendingRange.get(0).getID()); assertEquals(1, streamEntrys.size()); assertEquals(pendingRange.get(0).getID(), streamEntrys.get(0).getID()); assertEquals("v1", streamEntrys.get(0).getFields().get("f1")); } @Test public void xclaimJustId() { final String stream = "xpendeing-stream"; Map map = new HashMap<>(); map.put("f1", "v1"); jedis.xadd(stream, (StreamEntryID) null, map); assertEquals("OK", jedis.xgroupCreate(stream, "xpendeing-group", null, false)); // Read the event from Stream put it on pending jedis.xreadGroup("xpendeing-group", "xpendeing-consumer", XReadGroupParams.xReadGroupParams().count(1).block(1), singletonMap(stream, StreamEntryID.XREADGROUP_UNDELIVERED_ENTRY)); // Get the pending event List pendingRange = jedis.xpending(stream, "xpendeing-group", XPendingParams.xPendingParams().count(3).consumer("xpendeing-consumer")); // Sleep for 100ms so we can claim events pending for more than 50ms try { Thread.sleep(100); } catch (InterruptedException e) { e.printStackTrace(); } List streamEntryIDS = jedis.xclaimJustId(stream, "xpendeing-group", "xpendeing-consumer2", 50, XClaimParams.xClaimParams().idle(0).retryCount(0), pendingRange.get(0).getID()); assertEquals(1, streamEntryIDS.size()); assertEquals(pendingRange.get(0).getID(), streamEntryIDS.get(0)); } @Test public void xautoclaim() { Map map = new HashMap<>(); map.put("f1", "v1"); jedis.xadd("xpending-stream", (StreamEntryID) null, map); assertEquals("OK", jedis.xgroupCreate("xpending-stream", "xpending-group", null, false)); // Read the event from Stream put it on pending jedis.xreadGroup("xpending-group", "xpending-consumer", XReadGroupParams.xReadGroupParams().count(1).block(1), singletonMap("xpending-stream", StreamEntryID.XREADGROUP_UNDELIVERED_ENTRY)); // Get the pending event List pendingRange = jedis.xpending("xpending-stream", "xpending-group", XPendingParams.xPendingParams().count(3).consumer("xpending-consumer")); // Sleep for 100ms so we can auto claim events pending for more than 50ms try { Thread.sleep(100); } catch (InterruptedException e) { e.printStackTrace(); } // Auto claim pending events to different consumer Map.Entry> streamEntrys = jedis.xautoclaim("xpending-stream", "xpending-group", "xpending-consumer2", 50, new StreamEntryID(), new XAutoClaimParams().count(1)); assertEquals(1, streamEntrys.getValue().size()); assertEquals(pendingRange.get(0).getID(), streamEntrys.getValue().get(0).getID()); assertEquals("v1", streamEntrys.getValue().get(0).getFields().get("f1")); } @Test public void xautoclaimBinary() { Map map = new HashMap<>(); map.put("f1", "v1"); jedis.xadd("xpending-stream", XAddParams.xAddParams(), map); assertEquals("OK", jedis.xgroupCreate("xpending-stream", "xpending-group", null, false)); // Read the event from Stream put it on pending jedis.xreadGroup("xpending-group", "xpending-consumer", XReadGroupParams.xReadGroupParams().count(1).block(1), singletonMap("xpending-stream", StreamEntryID.XREADGROUP_UNDELIVERED_ENTRY)); // Get the pending event List pendingRange = jedis.xpending("xpending-stream", "xpending-group", XPendingParams.xPendingParams().count(3).consumer("xpending-consumer")); // Sleep for 100ms so we can auto claim events pending for more than 50ms try { Thread.sleep(100); } catch (InterruptedException e) { e.printStackTrace(); } // Auto claim pending events to different consumer List streamEntrys = jedis.xautoclaim(SafeEncoder.encode("xpending-stream"), SafeEncoder.encode("xpending-group"), SafeEncoder.encode("xpending-consumer2"), 50, SafeEncoder.encode(new StreamEntryID().toString()), new XAutoClaimParams().count(1)); Map.Entry> res = BuilderFactory.STREAM_AUTO_CLAIM_RESPONSE.build(streamEntrys); assertEquals(1, res.getValue().size()); assertEquals(pendingRange.get(0).getID(), res.getValue().get(0).getID()); assertEquals("v1", res.getValue().get(0).getFields().get("f1")); } @Test public void xautoclaimJustId() { Map map = new HashMap<>(); map.put("f1", "v1"); jedis.xadd("xpending-stream", (StreamEntryID) null, map); assertEquals("OK", jedis.xgroupCreate("xpending-stream", "xpending-group", null, false)); // Read the event from Stream put it on pending jedis.xreadGroup("xpending-group", "xpending-consumer", XReadGroupParams.xReadGroupParams().count(1).block(1), singletonMap("xpending-stream", StreamEntryID.XREADGROUP_UNDELIVERED_ENTRY)); // Get the pending event List pendingRange = jedis.xpending("xpending-stream", "xpending-group", XPendingParams.xPendingParams().count(3).consumer("xpending-consumer")); // Sleep for 100ms so we can auto claim events pending for more than 50ms try { Thread.sleep(100); } catch (InterruptedException e) { e.printStackTrace(); } // Auto claim pending events to different consumer Map.Entry> streamEntrys = jedis.xautoclaimJustId("xpending-stream", "xpending-group", "xpending-consumer2", 50, new StreamEntryID(), new XAutoClaimParams().count(1)); assertEquals(1, streamEntrys.getValue().size()); assertEquals(pendingRange.get(0).getID().getTime(), streamEntrys.getValue().get(0).getTime()); assertEquals(pendingRange.get(0).getID().getSequence(), streamEntrys.getValue().get(0).getSequence()); } @Test public void xautoclaimJustIdBinary() { Map map = new HashMap<>(); map.put("f1", "v1"); jedis.xadd("xpending-stream", XAddParams.xAddParams(), map); assertEquals("OK", jedis.xgroupCreate("xpending-stream", "xpending-group", null, false)); // Read the event from Stream put it on pending jedis.xreadGroup("xpending-group", "xpending-consumer", XReadGroupParams.xReadGroupParams().count(1).block(1), singletonMap("xpending-stream", StreamEntryID.XREADGROUP_UNDELIVERED_ENTRY)); // Get the pending event List pendingRange = jedis.xpending("xpending-stream", "xpending-group", XPendingParams.xPendingParams().count(3).consumer("xpending-consumer")); // Sleep for 100ms so we can auto claim events pending for more than 50ms try { Thread.sleep(100); } catch (InterruptedException e) { e.printStackTrace(); } // Auto claim pending events to different consumer List streamEntrys = jedis.xautoclaimJustId(SafeEncoder.encode("xpending-stream"), SafeEncoder.encode("xpending-group"), SafeEncoder.encode("xpending-consumer2"), 50, SafeEncoder.encode(new StreamEntryID().toString()), new XAutoClaimParams().count(1)); Map.Entry> res = BuilderFactory.STREAM_AUTO_CLAIM_JUSTID_RESPONSE.build(streamEntrys); assertEquals(1, res.getValue().size()); assertEquals(pendingRange.get(0).getID().getTime(), res.getValue().get(0).getTime()); assertEquals(pendingRange.get(0).getID().getSequence(), res.getValue().get(0).getSequence()); } @Test public void xinfo() throws InterruptedException { final String STREAM_NAME = "xadd-stream1"; final String F1 = "f1"; final String V1 = "v1"; final String V2 = "v2"; final String G1 = "G1"; final String G2 = "G2"; final String MY_CONSUMER = "myConsumer"; final String MY_CONSUMER2 = "myConsumer2"; final RedisVersion redisVersion = RedisVersionUtil.getRedisVersion(jedis); Map map1 = new HashMap<>(); map1.put(F1, V1); StreamEntryID id1 = jedis.xadd(STREAM_NAME, (StreamEntryID) null, map1); map1.put(F1, V2); StreamEntryID id2 = jedis.xadd(STREAM_NAME, (StreamEntryID) null, map1); assertNotNull(id1); StreamInfo streamInfo = jedis.xinfoStream(STREAM_NAME); assertNotNull(id2); jedis.xgroupCreate(STREAM_NAME, G1, StreamEntryID.XGROUP_LAST_ENTRY, false); Map streamQeury11 = singletonMap( STREAM_NAME, new StreamEntryID("0-0")); jedis.xreadGroup(G1, MY_CONSUMER, XReadGroupParams.xReadGroupParams().count(1), streamQeury11); Thread.sleep(1); List groupInfo = jedis.xinfoGroups(STREAM_NAME); List consumersInfo = jedis.xinfoConsumers(STREAM_NAME, G1); List consumerInfo = jedis.xinfoConsumers2(STREAM_NAME, G1); // Stream info test assertEquals(2L, streamInfo.getStreamInfo().get(StreamInfo.LENGTH)); assertEquals(1L, streamInfo.getStreamInfo().get(StreamInfo.RADIX_TREE_KEYS)); assertEquals(2L, streamInfo.getStreamInfo().get(StreamInfo.RADIX_TREE_NODES)); assertEquals(0L, streamInfo.getStreamInfo().get(StreamInfo.GROUPS)); assertEquals(V1, ((StreamEntry) streamInfo.getStreamInfo().get(StreamInfo.FIRST_ENTRY)).getFields().get(F1)); assertEquals(V2, ((StreamEntry) streamInfo.getStreamInfo().get(StreamInfo.LAST_ENTRY)).getFields().get(F1)); assertEquals(id2, streamInfo.getStreamInfo().get(StreamInfo.LAST_GENERATED_ID)); // Using getters assertEquals(2, streamInfo.getLength()); assertEquals(1, streamInfo.getRadixTreeKeys()); assertEquals(2, streamInfo.getRadixTreeNodes()); assertEquals(0, streamInfo.getGroups()); assertEquals(V1, streamInfo.getFirstEntry().getFields().get(F1)); assertEquals(V2, streamInfo.getLastEntry().getFields().get(F1)); assertEquals(id2, streamInfo.getLastGeneratedId()); // Group info test assertEquals(1, groupInfo.size()); assertEquals(G1, groupInfo.get(0).getGroupInfo().get(StreamGroupInfo.NAME)); assertEquals(1L, groupInfo.get(0).getGroupInfo().get(StreamGroupInfo.CONSUMERS)); assertEquals(0L, groupInfo.get(0).getGroupInfo().get(StreamGroupInfo.PENDING)); assertEquals(id2, groupInfo.get(0).getGroupInfo().get(StreamGroupInfo.LAST_DELIVERED)); // Using getters assertEquals(1, groupInfo.size()); assertEquals(G1, groupInfo.get(0).getName()); assertEquals(1, groupInfo.get(0).getConsumers()); assertEquals(0, groupInfo.get(0).getPending()); assertEquals(id2, groupInfo.get(0).getLastDeliveredId()); // Consumers info test assertEquals(MY_CONSUMER, consumersInfo.get(0).getConsumerInfo().get(StreamConsumersInfo.NAME)); assertEquals(0L, consumersInfo.get(0).getConsumerInfo().get(StreamConsumersInfo.PENDING)); assertTrue((Long) consumersInfo.get(0).getConsumerInfo().get(StreamConsumersInfo.IDLE) > 0); // Using getters assertEquals(MY_CONSUMER, consumersInfo.get(0).getName()); assertEquals(0L, consumersInfo.get(0).getPending()); assertThat(consumersInfo.get(0).getIdle(), Matchers.greaterThanOrEqualTo(0L)); if ( redisVersion.isGreaterThanOrEqualTo(RedisVersion.V7_2_0)) { assertThat(consumersInfo.get(0).getInactive(), Matchers.any(Long.class)); } // Consumer info test assertEquals(MY_CONSUMER, consumerInfo.get(0).getConsumerInfo().get(StreamConsumerInfo.NAME)); assertEquals(0L, consumerInfo.get(0).getConsumerInfo().get(StreamConsumerInfo.PENDING)); assertTrue((Long) consumerInfo.get(0).getConsumerInfo().get(StreamConsumerInfo.IDLE) > 0); // Using getters assertEquals(MY_CONSUMER, consumerInfo.get(0).getName()); assertEquals(0L, consumerInfo.get(0).getPending()); assertThat(consumerInfo.get(0).getIdle(), Matchers.greaterThanOrEqualTo(0L)); if (redisVersion.isGreaterThanOrEqualTo(RedisVersion.V7_2_0)) { assertThat(consumerInfo.get(0).getInactive(), Matchers.any(Long.class)); } // test with more groups and consumers jedis.xgroupCreate(STREAM_NAME, G2, StreamEntryID.XGROUP_LAST_ENTRY, false); jedis.xreadGroup(G1, MY_CONSUMER2, XReadGroupParams.xReadGroupParams().count(1), streamQeury11); jedis.xreadGroup(G2, MY_CONSUMER, XReadGroupParams.xReadGroupParams().count(1), streamQeury11); jedis.xreadGroup(G2, MY_CONSUMER2, XReadGroupParams.xReadGroupParams().count(1), streamQeury11); List manyGroupsInfo = jedis.xinfoGroups(STREAM_NAME); List manyConsumersInfo = jedis.xinfoConsumers(STREAM_NAME, G2); List manyConsumerInfo = jedis.xinfoConsumers2(STREAM_NAME, G2); assertEquals(2, manyGroupsInfo.size()); assertEquals(2, manyConsumersInfo.size()); assertEquals(2, manyConsumerInfo.size()); StreamFullInfo streamInfoFull = jedis.xinfoStreamFull(STREAM_NAME); assertEquals(2, streamInfoFull.getEntries().size()); assertEquals(2, streamInfoFull.getGroups().size()); assertEquals(2, streamInfoFull.getLength()); assertEquals(1, streamInfoFull.getRadixTreeKeys()); assertEquals(2, streamInfoFull.getRadixTreeNodes()); assertEquals(0, streamInfo.getGroups()); assertEquals(G1, streamInfoFull.getGroups().get(0).getName()); assertEquals(G2, streamInfoFull.getGroups().get(1).getName()); assertEquals(V1, streamInfoFull.getEntries().get(0).getFields().get(F1)); assertEquals(V2, streamInfoFull.getEntries().get(1).getFields().get(F1)); assertEquals(id2, streamInfoFull.getLastGeneratedId()); streamInfoFull = jedis.xinfoStreamFull(STREAM_NAME, 10); assertEquals(G1, streamInfoFull.getGroups().get(0).getName()); assertEquals(G2, streamInfoFull.getGroups().get(1).getName()); assertEquals(V1, streamInfoFull.getEntries().get(0).getFields().get(F1)); assertEquals(V2, streamInfoFull.getEntries().get(1).getFields().get(F1)); assertEquals(id2, streamInfoFull.getLastGeneratedId()); // Not existing key - redis cli return error so we expect exception try { jedis.xinfoStream("random"); fail("Command should fail"); } catch (JedisException e) { assertEquals("ERR no such key", e.getMessage()); } } @Test public void xinfoStreamFullWithPending() { Map map = singletonMap("f1", "v1"); StreamEntryID id1 = jedis.xadd("streamfull2", (StreamEntryID) null, map); StreamEntryID id2 = jedis.xadd("streamfull2", (StreamEntryID) null, map); jedis.xgroupCreate("streamfull2", "xreadGroup-group", null, false); Map streamQeury1 = singletonMap("streamfull2", StreamEntryID.XREADGROUP_UNDELIVERED_ENTRY); List>> range = jedis.xreadGroup("xreadGroup-group", "xreadGroup-consumer", XReadGroupParams.xReadGroupParams().count(1), streamQeury1); assertEquals(1, range.size()); assertEquals(1, range.get(0).getValue().size()); StreamFullInfo full = jedis.xinfoStreamFull("streamfull2"); assertEquals(1, full.getGroups().size()); StreamGroupFullInfo group = full.getGroups().get(0); assertEquals("xreadGroup-group", group.getName()); assertEquals(1, group.getPending().size()); List groupPendingEntry = group.getPending().get(0); assertEquals(id1, groupPendingEntry.get(0)); assertEquals("xreadGroup-consumer", groupPendingEntry.get(1)); assertEquals(1, group.getConsumers().size()); StreamConsumerFullInfo consumer = group.getConsumers().get(0); assertEquals("xreadGroup-consumer", consumer.getName()); assertThat(consumer.getSeenTime(), Matchers.greaterThanOrEqualTo(0L)); if (RedisVersionUtil.getRedisVersion(jedis).isGreaterThanOrEqualTo(RedisVersion.V7_2_0)) { assertThat(consumer.getActiveTime(), Matchers.greaterThanOrEqualTo(0L)); } assertEquals(1, consumer.getPending().size()); List consumerPendingEntry = consumer.getPending().get(0); assertEquals(id1, consumerPendingEntry.get(0)); } @Test public void pipeline() { Map map = new HashMap<>(); map.put("a", "b"); Pipeline p = jedis.pipelined(); Response id1 = p.xadd("stream1", StreamEntryID.NEW_ENTRY, map); Response id2 = p.xadd("stream1", StreamEntryID.NEW_ENTRY, map); Response> results = p.xrange("stream1", (StreamEntryID) null, (StreamEntryID) null, 2); p.sync(); List entries = results.get(); assertEquals(2, entries.size()); assertEquals(id1.get(), entries.get(0).getID()); assertEquals(map, entries.get(0).getFields()); assertEquals(id2.get(), entries.get(1).getID()); assertEquals(map, entries.get(1).getFields()); p = jedis.pipelined(); Response> results2 = p.xrevrange("stream1", null, id1.get(), 2); p.sync(); assertEquals(2, results2.get().size()); } @Test public void transaction() { Map map = new HashMap<>(); map.put("a", "b"); Transaction t = jedis.multi(); Response id1 = t.xadd("stream1", StreamEntryID.NEW_ENTRY, map); Response id2 = t.xadd("stream1", StreamEntryID.NEW_ENTRY, map); Response> results = t.xrange("stream1", (StreamEntryID) null, (StreamEntryID) null, 2); t.exec(); List entries = results.get(); assertEquals(2, entries.size()); assertEquals(id1.get(), entries.get(0).getID()); assertEquals(map, entries.get(0).getFields()); assertEquals(id2.get(), entries.get(1).getID()); assertEquals(map, entries.get(1).getFields()); } // ========== XREADGROUP CLAIM Tests ========== private static final String STREAM_KEY = "test-stream-claim"; private static final String GROUP_NAME = "test-group"; private static final String CONSUMER_1 = "consumer-1"; private static final String CONSUMER_2 = "consumer-2"; private static final long IDLE_TIME_MS = 5; private static final Map HASH = singletonMap("field", "value"); private Map beforeEachClaimTest() throws InterruptedException { jedis.del(STREAM_KEY); // Produce two entries Map hash = singletonMap("field", "value"); jedis.xadd(STREAM_KEY, StreamEntryID.NEW_ENTRY, hash); jedis.xadd(STREAM_KEY, StreamEntryID.NEW_ENTRY, hash); // Create group and consume with consumer-1 try { jedis.xgroupCreate(STREAM_KEY, GROUP_NAME, new StreamEntryID("0-0"), false); } catch (JedisDataException e) { if (!e.getMessage().contains("BUSYGROUP")) { throw e; } } Map streams = singletonMap(STREAM_KEY, StreamEntryID.XREADGROUP_UNDELIVERED_ENTRY); jedis.xreadGroup(GROUP_NAME, CONSUMER_1, XReadGroupParams.xReadGroupParams().count(10), streams); // Ensure idle time Thread.sleep(IDLE_TIME_MS); return streams; } @Test @SinceRedisVersion(V8_4_0_STRING) public void xreadgroupClaimReturnsMetadataOrdered() throws InterruptedException { Map streams = beforeEachClaimTest(); // Produce fresh entries jedis.xadd(STREAM_KEY, StreamEntryID.NEW_ENTRY, HASH); jedis.xadd(STREAM_KEY, StreamEntryID.NEW_ENTRY, HASH); // Read with consumer-2 using CLAIM List>> consumer2Result = jedis.xreadGroup(GROUP_NAME, CONSUMER_2, XReadGroupParams.xReadGroupParams().claim(IDLE_TIME_MS - 1).count(10), streams); assertNotNull(consumer2Result); assertEquals(1, consumer2Result.size()); List entries = consumer2Result.get(0).getValue(); assertEquals(4, entries.size()); long claimedCount = entries.stream().filter(StreamEntry::isClaimed).count(); long freshCount = entries.size() - claimedCount; assertEquals(2, claimedCount); assertEquals(2, freshCount); // Assert order: pending entries are first StreamEntry first = entries.get(0); StreamEntry second = entries.get(1); StreamEntry third = entries.get(2); StreamEntry fourth = entries.get(3); // Claimed entries assertTrue(first.isClaimed()); assertTrue(second.isClaimed()); assertTrue(first.getMillisElapsedFromDelivery() >= IDLE_TIME_MS); assertTrue(second.getMillisElapsedFromDelivery() >= IDLE_TIME_MS); // Fresh entries assertFalse(third.isClaimed()); assertFalse(fourth.isClaimed()); assertEquals(Long.valueOf(0), third.getDeliveredCount()); assertEquals(Long.valueOf(0), fourth.getDeliveredCount()); assertEquals(Long.valueOf(0), third.getMillisElapsedFromDelivery()); assertEquals(Long.valueOf(0), fourth.getMillisElapsedFromDelivery()); } @Test @SinceRedisVersion(V8_4_0_STRING) public void xreadgroupClaimMovesPendingFromC1ToC2AndRemainsPendingUntilAck() throws InterruptedException { Map streams = beforeEachClaimTest(); // Verify pending belongs to consumer-1 StreamPendingSummary before = jedis.xpending(STREAM_KEY, GROUP_NAME); assertEquals(2L, before.getTotal()); assertEquals(2L, before.getConsumerMessageCount().getOrDefault(CONSUMER_1, 0L).longValue()); // Claim with consumer-2 List>> res = jedis.xreadGroup(GROUP_NAME, CONSUMER_2, XReadGroupParams.xReadGroupParams().claim(IDLE_TIME_MS).count(10), streams); assertNotNull(res); assertEquals(1, res.size()); List entries = res.get(0).getValue(); long claimed = entries.stream().filter(StreamEntry::isClaimed).count(); assertEquals(2, claimed); // After claim: entries are pending for consumer-2 StreamPendingSummary afterClaim = jedis.xpending(STREAM_KEY, GROUP_NAME); assertEquals(2L, afterClaim.getTotal()); assertEquals(0L, afterClaim.getConsumerMessageCount().getOrDefault(CONSUMER_1, 0L).longValue()); assertEquals(2L, afterClaim.getConsumerMessageCount().getOrDefault(CONSUMER_2, 0L).longValue()); // XACK the claimed entries long acked = jedis.xack(STREAM_KEY, GROUP_NAME, entries.get(0).getID(), entries.get(1).getID()); assertEquals(2, acked); StreamPendingSummary afterAck = jedis.xpending(STREAM_KEY, GROUP_NAME); assertEquals(0L, afterAck.getTotal()); } @Test @SinceRedisVersion(V8_4_0_STRING) public void xreadgroupClaimWithNoackDoesNotCreatePendingAndRemovesClaimedFromPel() throws InterruptedException { Map streams = beforeEachClaimTest(); // Verify pending belongs to consumer-1 StreamPendingSummary before = jedis.xpending(STREAM_KEY, GROUP_NAME); assertEquals(2L, before.getTotal()); assertEquals(2L, before.getConsumerMessageCount().getOrDefault(CONSUMER_1, 0L).longValue()); assertEquals(0L, before.getConsumerMessageCount().getOrDefault(CONSUMER_2, 0L).longValue()); // Produce fresh entries jedis.xadd(STREAM_KEY, StreamEntryID.NEW_ENTRY, HASH); jedis.xadd(STREAM_KEY, StreamEntryID.NEW_ENTRY, HASH); // Claim with NOACK using consumer-2 List>> res = jedis.xreadGroup(GROUP_NAME, CONSUMER_2, XReadGroupParams.xReadGroupParams().claim(IDLE_TIME_MS).noAck().count(10), streams); assertNotNull(res); assertEquals(1, res.size()); List entries = res.get(0).getValue(); long claimedCount = entries.stream().filter(StreamEntry::isClaimed).count(); long freshCount = entries.size() - claimedCount; assertEquals(2, claimedCount); assertEquals(2, freshCount); // After NOACK read, previously pending entries remain pending StreamPendingSummary afterNoack = jedis.xpending(STREAM_KEY, GROUP_NAME); assertEquals(2L, afterNoack.getTotal()); // Claimed entries are now owned by consumer-2 assertEquals(0L, afterNoack.getConsumerMessageCount().getOrDefault(CONSUMER_1, 0L).longValue()); assertEquals(2L, afterNoack.getConsumerMessageCount().getOrDefault(CONSUMER_2, 0L).longValue()); } @Test public void xreadGroupPreservesFieldOrder() { String streamKey = "field-order-stream"; String groupName = "field-order-group"; String consumerName = "field-order-consumer"; // Use LinkedHashMap to ensure insertion order: a, z, m Map fields = new LinkedHashMap<>(); fields.put("a", "1"); fields.put("z", "4"); fields.put("m", "2"); jedis.xadd(streamKey, StreamEntryID.NEW_ENTRY, fields); jedis.xgroupCreate(streamKey, groupName, new StreamEntryID("0-0"), false); Map streamQuery = singletonMap(streamKey, StreamEntryID.XREADGROUP_UNDELIVERED_ENTRY); List>> result = jedis.xreadGroup(groupName, consumerName, XReadGroupParams.xReadGroupParams().count(1), streamQuery); assertEquals(1, result.size()); assertEquals(1, result.get(0).getValue().size()); StreamEntry entry = result.get(0).getValue().get(0); Map returnedFields = entry.getFields(); // Verify field order is preserved - this will fail with HashMap, pass with LinkedHashMap String[] expectedOrder = {"a", "z", "m"}; String[] actualOrder = returnedFields.keySet().toArray(new String[0]); assertEquals(expectedOrder.length, actualOrder.length, "Field count should match"); for (int i = 0; i < expectedOrder.length; i++) { assertEquals(expectedOrder[i], actualOrder[i], String.format("Field order mismatch at position %d: expected '%s' but got '%s'. " + "Full order: expected [a, z, m], actual %s", i, expectedOrder[i], actualOrder[i], java.util.Arrays.toString(actualOrder))); } } @Test public void xreadAsMapPreservesStreamOrder() { // Test that xreadAsMap preserves the order of streams when reading from multiple streams String streamKey1 = "{stream-order}-test-1"; String streamKey2 = "{stream-order}-test-2"; String streamKey3 = "{stream-order}-test-3"; // Add entries to streams in specific order Map fields = new LinkedHashMap<>(); fields.put("field", "value1"); jedis.xadd(streamKey1, StreamEntryID.NEW_ENTRY, fields); fields.put("field", "value2"); jedis.xadd(streamKey2, StreamEntryID.NEW_ENTRY, fields); fields.put("field", "value3"); jedis.xadd(streamKey3, StreamEntryID.NEW_ENTRY, fields); // Read from multiple streams in specific order Map streams = new LinkedHashMap<>(); streams.put(streamKey1, new StreamEntryID("0-0")); streams.put(streamKey2, new StreamEntryID("0-0")); streams.put(streamKey3, new StreamEntryID("0-0")); Map> result = jedis.xreadAsMap( XReadParams.xReadParams().count(10), streams); assertNotNull(result); assertEquals(3, result.size()); // Verify that the order of streams in the result matches the order in the request String[] expectedOrder = {streamKey1, streamKey2, streamKey3}; String[] actualOrder = result.keySet().toArray(new String[0]); assertEquals(expectedOrder.length, actualOrder.length, "Stream count should match"); for (int i = 0; i < expectedOrder.length; i++) { assertEquals(expectedOrder[i], actualOrder[i], String.format("Stream order mismatch at position %d: expected '%s' but got '%s'", i, expectedOrder[i], actualOrder[i])); } } // ========== Idempotent Producer Tests ========== @Test @EnabledOnCommand("XCFGSET") public void testXaddIdmpAuto() { // Add entry with IDMPAUTO Map message = new HashMap<>(); message.put("order", "12345"); message.put("amount", "100.00"); StreamEntryID id1 = jedis.xadd(STREAM_KEY, XAddParams.xAddParams().idmpAuto("producer-1"), message); assertNotNull(id1); assertEquals(1L, jedis.xlen(STREAM_KEY)); // Add same message again with same producer - should return same ID (duplicate detected) StreamEntryID id2 = jedis.xadd(STREAM_KEY, XAddParams.xAddParams().idmpAuto("producer-1"), message); assertEquals(id1, id2); // Duplicate returns same entry ID as before assertEquals(1L, jedis.xlen(STREAM_KEY)); // Stream length unchanged // Add same message with different producer - should succeed StreamEntryID id3 = jedis.xadd(STREAM_KEY, XAddParams.xAddParams().idmpAuto("producer-2"), message); assertNotNull(id3); assertEquals(2L, jedis.xlen(STREAM_KEY)); // Add different message with same producer - should succeed Map message2 = new HashMap<>(); message2.put("order", "67890"); message2.put("amount", "200.00"); StreamEntryID id4 = jedis.xadd(STREAM_KEY, XAddParams.xAddParams().idmpAuto("producer-1"), message2); assertNotNull(id4); assertEquals(3L, jedis.xlen(STREAM_KEY)); } @Test @EnabledOnCommand("XCFGSET") public void testXaddIdmp() { Map hash1 = singletonMap("field1", "value1"); Map hash2 = singletonMap("field2", "value2"); // Add entry with explicit idempotent ID StreamEntryID id1 = jedis.xadd(STREAM_KEY, XAddParams.xAddParams().idmp("producer-1", "iid-001"), hash1); assertNotNull(id1); assertEquals(1L, jedis.xlen(STREAM_KEY)); // Add with same producer and idempotent ID - should return same ID (duplicate detected) StreamEntryID id2 = jedis.xadd(STREAM_KEY, XAddParams.xAddParams().idmp("producer-1", "iid-001"), hash2); assertEquals(id1, id2); // Duplicate returns same entry ID as before assertEquals(1L, jedis.xlen(STREAM_KEY)); // Stream length unchanged // Add with same producer but different idempotent ID - should succeed StreamEntryID id3 = jedis.xadd(STREAM_KEY, XAddParams.xAddParams().idmp("producer-1", "iid-002"), hash1); assertNotNull(id3); assertEquals(2L, jedis.xlen(STREAM_KEY)); // Add with different producer but same idempotent ID - should succeed StreamEntryID id4 = jedis.xadd(STREAM_KEY, XAddParams.xAddParams().idmp("producer-2", "iid-001"), hash1); assertNotNull(id4); assertEquals(3L, jedis.xlen(STREAM_KEY)); } @Test @EnabledOnCommand("XCFGSET") public void testXcfgset() { // Add an entry to create the stream jedis.xadd(STREAM_KEY, StreamEntryID.NEW_ENTRY, singletonMap("field", "value")); // Configure idempotent producer settings String result = jedis.xcfgset(STREAM_KEY, XCfgSetParams.xCfgSetParams().idmpDuration(1000).idmpMaxsize(500)); assertEquals("OK", result); // Verify settings via XINFO STREAM StreamInfo info = jedis.xinfoStream(STREAM_KEY); assertEquals(Long.valueOf(1000), info.getIdmpDuration()); assertEquals(Long.valueOf(500), info.getIdmpMaxsize()); } @Test @EnabledOnCommand("XCFGSET") public void testXinfoStreamIdempotentFields() { Map hash1 = singletonMap("field1", "value1"); Map hash2 = singletonMap("field2", "value2"); // Add an entry to create the stream jedis.xadd(STREAM_KEY, StreamEntryID.NEW_ENTRY, hash1); // Configure idempotent settings jedis.xcfgset(STREAM_KEY, XCfgSetParams.xCfgSetParams().idmpDuration(100).idmpMaxsize(100)); // Add some entries with idempotent IDs jedis.xadd(STREAM_KEY, XAddParams.xAddParams().idmp("producer-1", "iid-001"), hash1); jedis.xadd(STREAM_KEY, XAddParams.xAddParams().idmp("producer-1", "iid-002"), hash2); jedis.xadd(STREAM_KEY, XAddParams.xAddParams().idmp("producer-2", "iid-001"), hash1); // Try to add a duplicate jedis.xadd(STREAM_KEY, XAddParams.xAddParams().idmp("producer-1", "iid-001"), hash2); // Check XINFO STREAM response StreamInfo info = jedis.xinfoStream(STREAM_KEY); // Verify idempotent configuration fields assertEquals(Long.valueOf(100), info.getIdmpDuration()); assertEquals(Long.valueOf(100), info.getIdmpMaxsize()); // Verify idempotent statistics fields assertEquals(Long.valueOf(2), info.getPidsTracked()); // 2 producers assertEquals(Long.valueOf(3), info.getIidsTracked()); // 3 unique IDs assertEquals(Long.valueOf(3), info.getIidsAdded()); // 3 entries added assertEquals(Long.valueOf(1), info.getIidsDuplicates()); // 1 duplicate rejected } @Test @EnabledOnCommand("XCFGSET") public void testXaddIdmpWithTrimming() { // Add first entry with IDMPAUTO and trimming StreamEntryID id1 = jedis.xadd(STREAM_KEY, XAddParams.xAddParams().idmpAuto("producer1").maxLen(2), singletonMap("field", "value1")); assertNotNull(id1); assertEquals(1, jedis.xlen(STREAM_KEY)); // Add duplicate - should return same ID StreamEntryID id2 = jedis.xadd(STREAM_KEY, XAddParams.xAddParams().idmpAuto("producer1").maxLen(2), singletonMap("field", "value1")); assertEquals(id1, id2); assertEquals(1, jedis.xlen(STREAM_KEY)); // Still 1 entry // Add different message - should add new entry and trim StreamEntryID id3 = jedis.xadd(STREAM_KEY, XAddParams.xAddParams().idmpAuto("producer1").maxLen(2), singletonMap("field", "value2")); assertNotNull(id3); assertNotEquals(id1, id3); // Different IDs assertEquals(2, jedis.xlen(STREAM_KEY)); // Now 2 entries } @Test @EnabledOnCommand("XCFGSET") public void testXcfgsetDefaults() { jedis.xadd(STREAM_KEY, StreamEntryID.NEW_ENTRY, singletonMap("init", "value")); // Verify default values StreamInfo info = jedis.xinfoStream(STREAM_KEY); assertEquals(Long.valueOf(100), info.getIdmpDuration()); assertEquals(Long.valueOf(100), info.getIdmpMaxsize()); assertEquals("OK", jedis.xcfgset(STREAM_KEY, XCfgSetParams.xCfgSetParams().idmpDuration(200).idmpMaxsize(200))); StreamInfo infoAfter = jedis.xinfoStream(STREAM_KEY); assertEquals(Long.valueOf(200), infoAfter.getIdmpDuration()); assertEquals(Long.valueOf(200), infoAfter.getIdmpMaxsize()); } } ================================================ FILE: src/test/java/redis/clients/jedis/commands/jedis/StringValuesCommandsTest.java ================================================ package redis.clients.jedis.commands.jedis; import java.util.ArrayList; import java.util.List; import java.util.stream.Stream; import io.redis.test.annotations.EnabledOnCommand; import io.redis.test.annotations.SinceRedisVersion; import io.redis.test.annotations.ConditionalOnEnv; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Tag; import org.junit.jupiter.params.ParameterizedClass; import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.Arguments; import org.junit.jupiter.params.provider.MethodSource; import redis.clients.jedis.RedisProtocol; import redis.clients.jedis.params.LCSParams; import redis.clients.jedis.params.MSetExParams; import redis.clients.jedis.resps.LCSMatchResult; import redis.clients.jedis.exceptions.JedisDataException; import redis.clients.jedis.params.GetExParams; import redis.clients.jedis.util.TestEnvUtil; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertNull; import static org.junit.jupiter.api.Assertions.assertThrows; import static org.junit.jupiter.api.Assertions.assertTrue; @ParameterizedClass @MethodSource("redis.clients.jedis.commands.CommandsTestsParameters#respVersions") @Tag("integration") public class StringValuesCommandsTest extends JedisCommandsTestBase { public StringValuesCommandsTest(RedisProtocol protocol) { super(protocol); } @Test public void setAndGet() { String status = jedis.set("foo", "bar"); assertEquals("OK", status); String value = jedis.get("foo"); assertEquals("bar", value); assertNull(jedis.get("bar")); } @Test public void getSet() { String value = jedis.getSet("foo", "bar"); assertNull(value); value = jedis.get("foo"); assertEquals("bar", value); } @Test public void getDel() { String status = jedis.set("foo", "bar"); assertEquals("OK", status); String value = jedis.getDel("foo"); assertEquals("bar", value); assertNull(jedis.get("foo")); } @Test public void getEx() { assertNull(jedis.getEx("foo", GetExParams.getExParams().ex(1))); jedis.set("foo", "bar"); assertEquals("bar", jedis.getEx("foo", GetExParams.getExParams().ex(10))); long ttl = jedis.ttl("foo"); assertTrue(ttl > 0 && ttl <= 10); assertEquals("bar", jedis.getEx("foo", GetExParams.getExParams().px(20000l))); ttl = jedis.ttl("foo"); assertTrue(ttl > 10 && ttl <= 20); assertEquals("bar", jedis.getEx("foo", GetExParams.getExParams().exAt(System.currentTimeMillis() / 1000 + 30))); ttl = jedis.ttl("foo"); assertTrue(ttl > 20 && ttl <= 30); assertEquals("bar", jedis.getEx("foo", GetExParams.getExParams().pxAt(System.currentTimeMillis() + 40000l))); ttl = jedis.ttl("foo"); assertTrue(ttl > 30 && ttl <= 40); assertEquals("bar", jedis.getEx("foo", GetExParams.getExParams().persist())); assertEquals(-1, jedis.ttl("foo")); } @Test public void mget() { List values = jedis.mget("foo", "bar"); List expected = new ArrayList(); expected.add(null); expected.add(null); assertEquals(expected, values); jedis.set("foo", "bar"); expected = new ArrayList(); expected.add("bar"); expected.add(null); values = jedis.mget("foo", "bar"); assertEquals(expected, values); jedis.set("bar", "foo"); expected = new ArrayList(); expected.add("bar"); expected.add("foo"); values = jedis.mget("foo", "bar"); assertEquals(expected, values); } @Test public void setnx() { assertEquals(1, jedis.setnx("foo", "bar")); assertEquals("bar", jedis.get("foo")); assertEquals(0, jedis.setnx("foo", "bar2")); assertEquals("bar", jedis.get("foo")); } @Test public void setex() { String status = jedis.setex("foo", 20, "bar"); assertEquals("OK", status); long ttl = jedis.ttl("foo"); assertTrue(ttl > 0 && ttl <= 20); } @Test public void mset() { String status = jedis.mset("foo", "bar", "bar", "foo"); assertEquals("OK", status); assertEquals("bar", jedis.get("foo")); assertEquals("foo", jedis.get("bar")); } @Test @ConditionalOnEnv(value = TestEnvUtil.ENV_REDIS_ENTERPRISE, enabled = false) public void msetnx() { assertEquals(1, jedis.msetnx("foo", "bar", "bar", "foo")); assertEquals("bar", jedis.get("foo")); assertEquals("foo", jedis.get("bar")); assertEquals(0, jedis.msetnx("foo", "bar1", "bar2", "foo2")); assertEquals("bar", jedis.get("foo")); assertEquals("foo", jedis.get("bar")); } @Test public void incr() { assertEquals(1, jedis.incr("foo")); assertEquals(2, jedis.incr("foo")); } @Test public void incrWrongValue() { jedis.set("foo", "bar"); assertThrows(JedisDataException.class, () -> jedis.incr("foo")); } @Test public void incrBy() { assertEquals(2, jedis.incrBy("foo", 2)); assertEquals(5, jedis.incrBy("foo", 3)); } @Test public void incrByWrongValue() { jedis.set("foo", "bar"); assertThrows(JedisDataException.class, () -> jedis.incrBy("foo", 2)); } @Test public void incrByFloat() { assertEquals(10.5, jedis.incrByFloat("foo", 10.5), 0.0); assertEquals(10.6, jedis.incrByFloat("foo", 0.1), 0.0); } @Test public void incrByFloatWrongValue() { jedis.set("foo", "bar"); assertThrows(JedisDataException.class, () -> jedis.incrByFloat("foo", 2d)); } @Test public void decrWrongValue() { jedis.set("foo", "bar"); assertThrows(JedisDataException.class, () -> jedis.decr("foo")); } @Test public void decr() { assertEquals(-1, jedis.decr("foo")); assertEquals(-2, jedis.decr("foo")); } @Test public void decrBy() { assertEquals(-2, jedis.decrBy("foo", 2)); assertEquals(-4, jedis.decrBy("foo", 2)); } @Test public void decrByWrongValue() { jedis.set("foo", "bar"); assertThrows(JedisDataException.class, () -> jedis.decrBy("foo", 2)); } @Test public void append() { assertEquals(3, jedis.append("foo", "bar")); assertEquals("bar", jedis.get("foo")); assertEquals(6, jedis.append("foo", "bar")); assertEquals("barbar", jedis.get("foo")); } @Test @ConditionalOnEnv(value = TestEnvUtil.ENV_REDIS_ENTERPRISE, enabled = false) public void substr() { jedis.set("s", "This is a string"); assertEquals("This", jedis.substr("s", 0, 3)); assertEquals("ing", jedis.substr("s", -3, -1)); assertEquals("This is a string", jedis.substr("s", 0, -1)); assertEquals(" string", jedis.substr("s", 9, 100000)); } @Test public void strlen() { String str = "This is a string"; jedis.set("s", str); assertEquals(str.length(), jedis.strlen("s")); } @Test public void incrLargeNumbers() { assertEquals(1, jedis.incr("foo")); assertEquals(1L + Integer.MAX_VALUE, jedis.incrBy("foo", Integer.MAX_VALUE)); } @Test public void incrReallyLargeNumbers() { jedis.set("foo", Long.toString(Long.MAX_VALUE)); assertThrows(JedisDataException.class, () -> jedis.incr("foo")); } @Test public void psetex() { String status = jedis.psetex("foo", 20000, "bar"); assertEquals("OK", status); long ttl = jedis.ttl("foo"); assertTrue(ttl > 0 && ttl <= 20000); } @Test @SinceRedisVersion("7.0.0") @ConditionalOnEnv(value = TestEnvUtil.ENV_REDIS_ENTERPRISE, enabled = false) public void lcs() { jedis.mset("key1", "ohmytext", "key2", "mynewtext"); LCSMatchResult stringMatchResult = jedis.lcs("key1", "key2", LCSParams.LCSParams()); assertEquals("mytext", stringMatchResult.getMatchString()); stringMatchResult = jedis.lcs("key1", "key2", LCSParams.LCSParams().idx().withMatchLen()); assertEquals(stringMatchResult.getLen(), 6); assertEquals(2, stringMatchResult.getMatches().size()); stringMatchResult = jedis.lcs("key1", "key2", LCSParams.LCSParams().idx().minMatchLen(10)); assertEquals(0, stringMatchResult.getMatches().size()); } // MSETEX NX + expiration matrix static Stream msetexNxArgsProvider() { return Stream.of(Arguments.of("EX", new MSetExParams().nx().ex(5)), Arguments.of("PX", new MSetExParams().nx().px(5000)), Arguments.of("EXAT", new MSetExParams().nx().exAt(System.currentTimeMillis() / 1000 + 5)), Arguments.of("PXAT", new MSetExParams().nx().pxAt(System.currentTimeMillis() + 5000)), Arguments.of("KEEPTTL", new MSetExParams().nx().keepTtl())); } @ParameterizedTest(name = "MSETEX NX + {0}") @MethodSource("msetexNxArgsProvider") @EnabledOnCommand("MSETEX") public void msetexNx_parametrized(String optionLabel, MSetExParams params) { String k1 = "{t}msetex:js:k1"; String k2 = "{t}msetex:js:k2"; boolean result = jedis.msetex(params, k1, "v1", k2, "v2"); assertTrue(result); long ttl = jedis.ttl(k1); if ("KEEPTTL".equals(optionLabel)) { assertEquals(-1L, ttl); } else { assertTrue(ttl > 0L); } } @Test @EnabledOnCommand("MSETEX") public void msetexXxEx() { String k1 = "{t}msetex:js:xx:k1"; String k2 = "{t}msetex:js:xx:k2"; // First set the keys so they exist (XX requires existing keys) jedis.set(k1, "initial1"); jedis.set(k2, "initial2"); // Now use MSETEX with XX and EX MSetExParams params = new MSetExParams().xx().ex(5); boolean result = jedis.msetex(params, k1, "v1", k2, "v2"); assertTrue(result); // Verify values were updated assertEquals("v1", jedis.get(k1)); assertEquals("v2", jedis.get(k2)); // Verify TTL is set long ttl = jedis.ttl(k1); assertTrue(ttl > 0L); } } ================================================ FILE: src/test/java/redis/clients/jedis/commands/jedis/TransactionCommandsTest.java ================================================ package redis.clients.jedis.commands.jedis; import static org.junit.jupiter.api.Assertions.assertArrayEquals; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertNotNull; import static org.junit.jupiter.api.Assertions.assertNull; import static org.junit.jupiter.api.Assertions.assertSame; import static org.junit.jupiter.api.Assertions.assertThrows; import static org.junit.jupiter.api.Assertions.assertTrue; import static org.junit.jupiter.api.Assertions.fail; import static org.mockito.ArgumentMatchers.any; import static redis.clients.jedis.Protocol.Command.INCR; import static redis.clients.jedis.Protocol.Command.GET; import static redis.clients.jedis.Protocol.Command.SET; import java.io.IOException; import java.net.UnknownHostException; import java.util.ArrayList; import java.util.List; import java.util.Set; import io.redis.test.annotations.ConditionalOnEnv; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.junit.jupiter.params.ParameterizedClass; import org.junit.jupiter.params.provider.MethodSource; import org.mockito.MockedStatic; import org.mockito.Mockito; import redis.clients.jedis.Jedis; import redis.clients.jedis.Protocol; import redis.clients.jedis.RedisProtocol; import redis.clients.jedis.Response; import redis.clients.jedis.Transaction; import redis.clients.jedis.exceptions.JedisConnectionException; import redis.clients.jedis.exceptions.JedisDataException; import redis.clients.jedis.util.SafeEncoder; import redis.clients.jedis.util.TestEnvUtil; @ParameterizedClass @MethodSource("redis.clients.jedis.commands.CommandsTestsParameters#respVersions") public class TransactionCommandsTest extends JedisCommandsTestBase { final byte[] bfoo = { 0x01, 0x02, 0x03, 0x04 }; final byte[] bbar = { 0x05, 0x06, 0x07, 0x08 }; final byte[] ba = { 0x0A }; final byte[] bb = { 0x0B }; final byte[] bmykey = { 0x42, 0x02, 0x03, 0x04 }; Jedis nj; public TransactionCommandsTest(RedisProtocol protocol) { super(protocol); } @BeforeEach @Override public void setUp() throws Exception { super.setUp(); nj = new Jedis(endpoint.getHostAndPort(), endpoint.getClientConfigBuilder().timeoutMillis(500).build()); } @AfterEach @Override public void tearDown() throws Exception { nj.close(); super.tearDown(); } @Test public void multi() { Transaction trans = jedis.multi(); trans.sadd("foo", "a"); trans.sadd("foo", "b"); trans.scard("foo"); List response = trans.exec(); List expected = new ArrayList(); expected.add(1L); expected.add(1L); expected.add(2L); assertEquals(expected, response); // Binary trans = jedis.multi(); trans.sadd(bfoo, ba); trans.sadd(bfoo, bb); trans.scard(bfoo); response = trans.exec(); expected = new ArrayList(); expected.add(1L); expected.add(1L); expected.add(2L); assertEquals(expected, response); } @Test @ConditionalOnEnv(value = TestEnvUtil.ENV_REDIS_ENTERPRISE, enabled = false) public void watch() throws UnknownHostException, IOException { jedis.watch("mykey", "somekey"); Transaction t = jedis.multi(); nj.set("mykey", "bar"); t.set("mykey", "foo"); List resp = t.exec(); assertNull(resp); assertEquals("bar", jedis.get("mykey")); // Binary jedis.watch(bmykey, "foobar".getBytes()); t = jedis.multi(); nj.set(bmykey, bbar); t.set(bmykey, bfoo); resp = t.exec(); assertNull(resp); assertArrayEquals(bbar, jedis.get(bmykey)); } @Test public void unwatch() { jedis.watch("mykey"); jedis.get("mykey"); String val = "foo"; assertEquals("OK", jedis.unwatch()); Transaction t = jedis.multi(); nj.set("mykey", "bar"); t.set("mykey", val); List resp = t.exec(); assertEquals(1, resp.size()); assertEquals("OK", resp.get(0)); // Binary jedis.watch(bmykey); jedis.get(bmykey); byte[] bval = bfoo; assertEquals("OK", jedis.unwatch()); t = jedis.multi(); nj.set(bmykey, bbar); t.set(bmykey, bval); resp = t.exec(); assertEquals(1, resp.size()); assertEquals("OK", resp.get(0)); } @Test public void validateWhenInMulti() { jedis.multi(); assertThrows(IllegalStateException.class, () -> jedis.ping()); } @Test public void discard() { Transaction t = jedis.multi(); String status = t.discard(); assertEquals("OK", status); } @Test public void discardFail() { Transaction trans = jedis.multi(); trans.set("a", "a"); trans.set("b", "b"); try (MockedStatic protocol = Mockito.mockStatic(Protocol.class)) { protocol.when(() -> Protocol.read(any())).thenThrow(JedisConnectionException.class); trans.discard(); fail("Should get mocked JedisConnectionException."); } catch (JedisConnectionException jce) { // should be here } finally { // close() should pass trans.close(); } assertTrue(jedis.isBroken()); } @Test public void execFail() { Transaction trans = jedis.multi(); trans.set("a", "a"); trans.set("b", "b"); try (MockedStatic protocol = Mockito.mockStatic(Protocol.class)) { protocol.when(() -> Protocol.read(any())).thenThrow(JedisConnectionException.class); trans.exec(); fail("Should get mocked JedisConnectionException."); } catch (JedisConnectionException jce) { // should be here } finally { // close() should pass trans.close(); } assertTrue(jedis.isBroken()); } @Test @ConditionalOnEnv(value = TestEnvUtil.ENV_REDIS_ENTERPRISE, enabled = false) public void transactionResponse() { jedis.set("string", "foo"); jedis.lpush("list", "foo"); jedis.hset("hash", "foo", "bar"); jedis.zadd("zset", 1, "foo"); jedis.sadd("set", "foo"); Transaction t = jedis.multi(); Response string = t.get("string"); Response list = t.lpop("list"); Response hash = t.hget("hash", "foo"); Response> zset = t.zrange("zset", 0, -1); Response set = t.spop("set"); t.exec(); assertEquals("foo", string.get()); assertEquals("foo", list.get()); assertEquals("bar", hash.get()); assertEquals("foo", zset.get().iterator().next()); assertEquals("foo", set.get()); } @Test @ConditionalOnEnv(value = TestEnvUtil.ENV_REDIS_ENTERPRISE, enabled = false) public void transactionResponseBinary() { jedis.set("string", "foo"); jedis.lpush("list", "foo"); jedis.hset("hash", "foo", "bar"); jedis.zadd("zset", 1, "foo"); jedis.sadd("set", "foo"); Transaction t = jedis.multi(); Response string = t.get("string".getBytes()); Response list = t.lpop("list".getBytes()); Response hash = t.hget("hash".getBytes(), "foo".getBytes()); Response> zset = t.zrange("zset".getBytes(), 0, -1); Response set = t.spop("set".getBytes()); t.exec(); assertArrayEquals("foo".getBytes(), string.get()); assertArrayEquals("foo".getBytes(), list.get()); assertArrayEquals("bar".getBytes(), hash.get()); assertArrayEquals("foo".getBytes(), zset.get().iterator().next()); assertArrayEquals("foo".getBytes(), set.get()); } @Test public void transactionResponseWithinPipeline() { jedis.set("string", "foo"); Transaction t = jedis.multi(); Response string = t.get("string"); assertThrows(IllegalStateException.class, string::get); t.exec(); } @Test public void transactionResponseWithError() { Transaction t = jedis.multi(); t.set("foo", "bar"); Response> error = t.smembers("foo"); Response r = t.get("foo"); List l = t.exec(); assertSame(JedisDataException.class, l.get(1).getClass()); try { error.get(); fail("We expect exception here!"); } catch (JedisDataException e) { // that is fine we should be here } assertEquals("bar", r.get()); } // // @Test // public void execGetResponse() { // Transaction t = jedis.multi(); // // t.set("foo", "bar"); // t.smembers("foo"); // t.get("foo"); // // List> lr = t.execGetResponse(); // try { // lr.get(1).get(); // fail("We expect exception here!"); // } catch (JedisDataException e) { // // that is fine we should be here // } // assertEquals("bar", lr.get(2).get()); // } // // @Test // public void select() { // jedis.select(1); // jedis.set("foo", "bar"); // jedis.watch("foo"); // Transaction t = jedis.multi(); // t.select(0); // t.set("bar", "foo"); // // Jedis jedis2 = createJedis(); // jedis2.select(1); // jedis2.set("foo", "bar2"); // // List results = t.exec(); // assertNull(results); // } @Test public void testResetStateWhenInMulti() { Transaction t = jedis.multi(); t.set("foooo", "barrr"); jedis.resetState(); assertNull(jedis.get("foooo")); } // // @Test // public void testResetStateWhenInMultiWithinPipeline() { // Pipeline p = jedis.pipelined(); // p.multi(); // p.set("foooo", "barrr"); // // jedis.resetState(); // assertNull(jedis.get("foooo")); // } @Test @ConditionalOnEnv(value = TestEnvUtil.ENV_REDIS_ENTERPRISE, enabled = false) public void testResetStateWhenInWatch() { jedis.watch("mykey", "somekey"); // state reset : unwatch jedis.resetState(); Transaction t = jedis.multi(); nj.set("mykey", "bar"); t.set("mykey", "foo"); List resp = t.exec(); assertNotNull(resp); assertEquals(1, resp.size()); assertEquals("foo", jedis.get("mykey")); } @Test public void testResetStateWithFullyExecutedTransaction() { Jedis jedis2 = createJedis(); Transaction t = jedis2.multi(); t.set("mykey", "foo"); t.get("mykey"); List resp = t.exec(); assertNotNull(resp); assertEquals(2, resp.size()); jedis2.resetState(); jedis2.close(); } @Test public void testCloseable() { // we need to test with fresh instance of Jedis Jedis jedis2 = new Jedis(endpoint.getHostAndPort(), endpoint.getClientConfigBuilder().timeoutMillis(500).build());; Transaction transaction = jedis2.multi(); transaction.set("a", "1"); transaction.set("b", "2"); transaction.close(); try { transaction.exec(); fail("close should discard transaction"); } catch (IllegalStateException e) { assertTrue(e.getMessage().contains("EXEC without MULTI")); // pass } } @Test @ConditionalOnEnv(value = TestEnvUtil.ENV_REDIS_ENTERPRISE, enabled = false) public void testTransactionWithGeneralCommand() { Transaction t = jedis.multi(); t.set("string", "foo"); t.lpush("list", "foo"); t.hset("hash", "foo", "bar"); t.zadd("zset", 1, "foo"); t.sendCommand(SET, "x", "1"); t.sadd("set", "foo"); t.sendCommand(INCR, "x"); Response string = t.get("string"); Response list = t.lpop("list"); Response hash = t.hget("hash", "foo"); Response> zset = t.zrange("zset", 0, -1); Response set = t.spop("set"); Response x = t.sendCommand(GET, "x"); t.exec(); assertEquals("foo", string.get()); assertEquals("foo", list.get()); assertEquals("bar", hash.get()); assertEquals("foo", zset.get().iterator().next()); assertEquals("foo", set.get()); assertEquals("2", SafeEncoder.encode((byte[]) x.get())); } @Test @ConditionalOnEnv(value = TestEnvUtil.ENV_REDIS_ENTERPRISE, enabled = false) public void transactionResponseWithErrorWithGeneralCommand() { Transaction t = jedis.multi(); t.set("foo", "bar"); t.sendCommand(SET, "x", "1"); Response> error = t.smembers("foo"); Response r = t.get("foo"); Response x = t.sendCommand(GET, "x"); t.sendCommand(INCR, "x"); List l = t.exec(); assertSame(JedisDataException.class, l.get(2).getClass()); try { error.get(); fail("We expect exception here!"); } catch (JedisDataException e) { // that is fine we should be here } assertEquals("bar", r.get()); assertEquals("1", SafeEncoder.encode((byte[]) x.get())); } } ================================================ FILE: src/test/java/redis/clients/jedis/commands/jedis/VariadicCommandsTest.java ================================================ package redis.clients.jedis.commands.jedis; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertNull; import static redis.clients.jedis.util.AssertUtil.assertByteArrayListEquals; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; import org.junit.jupiter.api.Test; import org.junit.jupiter.params.ParameterizedClass; import org.junit.jupiter.params.provider.MethodSource; import redis.clients.jedis.RedisProtocol; @ParameterizedClass @MethodSource("redis.clients.jedis.commands.CommandsTestsParameters#respVersions") public class VariadicCommandsTest extends JedisCommandsTestBase { final byte[] bfoo = { 0x01, 0x02, 0x03, 0x04 }; final byte[] bbar = { 0x05, 0x06, 0x07, 0x08 }; final byte[] bcar = { 0x09, 0x0A, 0x0B, 0x0C }; final byte[] bfoo1 = { 0x01, 0x02, 0x03, 0x04, 0x0A }; final byte[] bfoo2 = { 0x01, 0x02, 0x03, 0x04, 0x0B }; public VariadicCommandsTest(RedisProtocol protocol) { super(protocol); } @Test public void hdel() { Map hash = new HashMap(); hash.put("bar", "car"); hash.put("car", "bar"); hash.put("foo2", "bar"); jedis.hmset("foo", hash); assertEquals(0, jedis.hdel("bar", "foo", "foo1")); assertEquals(0, jedis.hdel("foo", "foo", "foo1")); assertEquals(2, jedis.hdel("foo", "bar", "foo2")); assertNull(jedis.hget("foo", "bar")); // Binary Map bhash = new HashMap(); bhash.put(bbar, bcar); bhash.put(bcar, bbar); bhash.put(bfoo2, bbar); jedis.hmset(bfoo, bhash); assertEquals(0, jedis.hdel(bbar, bfoo, bfoo1)); assertEquals(0, jedis.hdel(bfoo, bfoo, bfoo1)); assertEquals(2, jedis.hdel(bfoo, bbar, bfoo2)); assertNull(jedis.hget(bfoo, bbar)); } @Test public void rpush() { long size = jedis.rpush("foo", "bar", "foo"); assertEquals(2, size); List expected = new ArrayList(); expected.add("bar"); expected.add("foo"); List values = jedis.lrange("foo", 0, -1); assertEquals(expected, values); // Binary size = jedis.rpush(bfoo, bbar, bfoo); assertEquals(2, size); List bexpected = new ArrayList(); bexpected.add(bbar); bexpected.add(bfoo); List bvalues = jedis.lrange(bfoo, 0, -1); assertByteArrayListEquals(bexpected, bvalues); } @Test public void lpush() { long size = jedis.lpush("foo", "bar", "foo"); assertEquals(2, size); List expected = new ArrayList(); expected.add("foo"); expected.add("bar"); List values = jedis.lrange("foo", 0, -1); assertEquals(expected, values); // Binary size = jedis.lpush(bfoo, bbar, bfoo); assertEquals(2, size); List bexpected = new ArrayList(); bexpected.add(bfoo); bexpected.add(bbar); List bvalues = jedis.lrange(bfoo, 0, -1); assertByteArrayListEquals(bexpected, bvalues); } @Test public void sadd() { long status = jedis.sadd("foo", "bar", "foo1"); assertEquals(2, status); status = jedis.sadd("foo", "bar", "car"); assertEquals(1, status); status = jedis.sadd("foo", "bar", "foo1"); assertEquals(0, status); status = jedis.sadd(bfoo, bbar, bfoo1); assertEquals(2, status); status = jedis.sadd(bfoo, bbar, bcar); assertEquals(1, status); status = jedis.sadd(bfoo, bbar, bfoo1); assertEquals(0, status); } @Test public void zadd() { Map scoreMembers = new HashMap(); scoreMembers.put("bar", 1d); scoreMembers.put("foo", 10d); long status = jedis.zadd("foo", scoreMembers); assertEquals(2, status); scoreMembers.clear(); scoreMembers.put("car", 0.1d); scoreMembers.put("bar", 2d); status = jedis.zadd("foo", scoreMembers); assertEquals(1, status); Map bscoreMembers = new HashMap(); bscoreMembers.put(bbar, 1d); bscoreMembers.put(bfoo, 10d); status = jedis.zadd(bfoo, bscoreMembers); assertEquals(2, status); bscoreMembers.clear(); bscoreMembers.put(bcar, 0.1d); bscoreMembers.put(bbar, 2d); status = jedis.zadd(bfoo, bscoreMembers); assertEquals(1, status); } @Test public void zrem() { jedis.zadd("foo", 1d, "bar"); jedis.zadd("foo", 2d, "car"); jedis.zadd("foo", 3d, "foo1"); long status = jedis.zrem("foo", "bar", "car"); List expected = new ArrayList(); expected.add("foo1"); assertEquals(2, status); assertEquals(expected, jedis.zrange("foo", 0, 100)); status = jedis.zrem("foo", "bar", "car"); assertEquals(0, status); status = jedis.zrem("foo", "bar", "foo1"); assertEquals(1, status); // Binary jedis.zadd(bfoo, 1d, bbar); jedis.zadd(bfoo, 2d, bcar); jedis.zadd(bfoo, 3d, bfoo1); status = jedis.zrem(bfoo, bbar, bcar); List bexpected = new ArrayList(); bexpected.add(bfoo1); assertEquals(2, status); assertByteArrayListEquals(bexpected, jedis.zrange(bfoo, 0, 100)); status = jedis.zrem(bfoo, bbar, bcar); assertEquals(0, status); status = jedis.zrem(bfoo, bbar, bfoo1); assertEquals(1, status); } } ================================================ FILE: src/test/java/redis/clients/jedis/commands/jedis/VectorSetCommandsTest.java ================================================ package redis.clients.jedis.commands.jedis; import io.redis.test.annotations.SinceRedisVersion; import org.junit.jupiter.api.Tag; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.TestInfo; import org.junit.jupiter.params.ParameterizedClass; import org.junit.jupiter.params.provider.MethodSource; import redis.clients.jedis.RedisProtocol; import redis.clients.jedis.util.VectorTestUtils; import redis.clients.jedis.exceptions.JedisDataException; import redis.clients.jedis.params.VAddParams; import redis.clients.jedis.params.VSimParams; import redis.clients.jedis.resps.RawVector; import redis.clients.jedis.resps.VSimScoreAttribs; import redis.clients.jedis.resps.VectorInfo; import redis.clients.jedis.util.SafeEncoder; import java.util.Arrays; import java.util.List; import java.util.Map; import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.*; import static org.junit.jupiter.api.Assertions.*; import static redis.clients.jedis.util.VectorTestUtils.floatArrayToFP32Bytes; /** * Integration tests for Vector Set commands using Jedis client. *

* Tests are parameterized to run against multiple RESP protocol versions. Repeating tests from * {@link redis.clients.jedis.commands.unified.VectorSetCommandsTestBase} against Jedis client.s *

*/ @ParameterizedClass @MethodSource("redis.clients.jedis.commands.CommandsTestsParameters#respVersions") @Tag("integration") @Tag("vector-set") public class VectorSetCommandsTest extends JedisCommandsTestBase { public VectorSetCommandsTest(RedisProtocol protocol) { super(protocol); } /** * Test the basic VADD method with float array. */ @Test @SinceRedisVersion("8.0.0") public void testVaddWithFloatArray(TestInfo testInfo) { String testKey = testInfo.getDisplayName() + ":test:vector:set"; String elementId = "point:F"; float[] vector = { 1.0f, 2.0f }; // Add a new element boolean result = jedis.vadd(testKey, vector, elementId); assertTrue(result); // Verify cardinality and dimension assertEquals(1L, jedis.vcard(testKey)); assertEquals(2L, jedis.vdim(testKey)); // Verify the vector was stored correctly List storedVector = jedis.vemb(testKey, elementId); assertEquals(2, storedVector.size()); assertEquals(1.0, storedVector.get(0), 0.01); assertEquals(2.0, storedVector.get(1), 0.01); // Test duplicate addition - should return false result = jedis.vadd(testKey, vector, elementId); assertFalse(result); // Cardinality should remain the same assertEquals(1L, jedis.vcard(testKey)); } /** * Test VADD method with float array and parameters. Overload 2: vadd(String key, float[] vector, * String element, VAddParams params) */ @Test @SinceRedisVersion("8.0.0") public void testVaddWithFloatArrayAndParams(TestInfo testInfo) { String testKey = testInfo.getDisplayName() + ":test:vector:set"; String elementId = "point:G"; float[] vector = { 1.0f, 2.0f }; // Create parameters VAddParams params = new VAddParams(); // Add a new element with parameters boolean result = jedis.vadd(testKey, vector, elementId, params); assertTrue(result); // Verify cardinality and dimension assertEquals(1L, jedis.vcard(testKey)); assertEquals(2L, jedis.vdim(testKey)); // Verify the vector was stored correctly List storedVector = jedis.vemb(testKey, elementId); assertEquals(2, storedVector.size()); assertEquals(1.0, storedVector.get(0), 0.01); assertEquals(2.0, storedVector.get(1), 0.01); } /** * Test VADD method with FP32 byte blob. Overload 3: vaddFP32(String key, byte[] vectorBlob, * String element) */ @Test @SinceRedisVersion("8.0.0") public void testVaddWithFP32ByteBlob(TestInfo testInfo) { String testKey = testInfo.getDisplayName() + ":test:vector:set"; String elementId = "point:H"; float[] vector = { 1.0f, 2.0f }; // Convert float array to FP32 byte blob byte[] vectorBlob = floatArrayToFP32Bytes(vector); // Add a new element with FP32 byte blob boolean result = jedis.vaddFP32(testKey, vectorBlob, elementId); assertTrue(result); // Verify cardinality and dimension assertEquals(1L, jedis.vcard(testKey)); assertEquals(2L, jedis.vdim(testKey)); // Verify the vector was stored correctly List storedVector = jedis.vemb(testKey, elementId); assertEquals(2, storedVector.size()); assertEquals(1.0, storedVector.get(0), 0.01); assertEquals(2.0, storedVector.get(1), 0.01); } /** * Test VADD method with FP32 byte blob and parameters. Overload 4: vaddFP32(String key, byte[] * vectorBlob, String element, VAddParams params) */ @Test @SinceRedisVersion("8.0.0") public void testVaddWithFP32ByteBlobAndParams(TestInfo testInfo) { String testKey = testInfo.getDisplayName() + ":test:vector:set"; String elementId = "point:I"; float[] vector = { 1.0f, 2.0f }; // Convert float array to FP32 byte blob byte[] vectorBlob = floatArrayToFP32Bytes(vector); // Create parameters VAddParams params = new VAddParams(); // Add a new element with FP32 byte blob and parameters boolean result = jedis.vaddFP32(testKey, vectorBlob, elementId, params); assertTrue(result); // Verify cardinality and dimension assertEquals(1L, jedis.vcard(testKey)); assertEquals(2L, jedis.vdim(testKey)); // Verify the vector was stored correctly List storedVector = jedis.vemb(testKey, elementId); assertEquals(2, storedVector.size()); assertEquals(1.0, storedVector.get(0), 0.01); assertEquals(2.0, storedVector.get(1), 0.01); } /** * Test VADD with quantization parameters. Demonstrates how quantization parameters can be used * with VADD. */ @Test @SinceRedisVersion("8.0.0") public void testVaddWithQuantization(TestInfo testInfo) { String baseKey = testInfo.getDisplayName() + ":test:vector:set"; float[] vector = { 1.0f, 2.0f }; // Test with basic VADD first to establish a baseline String defaultKey = baseKey + ":default"; jedis.del(defaultKey); boolean result = jedis.vadd(defaultKey, vector, "point:DEFAULT"); assertTrue(result); List defaultVector = jedis.vemb(defaultKey, "point:DEFAULT"); assertEquals(2, defaultVector.size()); assertEquals(1.0, defaultVector.get(0), 0.01); assertEquals(2.0, defaultVector.get(1), 0.01); assertEquals(1L, jedis.vcard(defaultKey)); // Test with Q8 quantization parameters String q8Key = baseKey + ":q8"; VAddParams quantParams = new VAddParams().q8(); jedis.del(q8Key); result = jedis.vadd(q8Key, vector, "point:Q8", quantParams); assertTrue(result); List quantVector = jedis.vemb(q8Key, "point:Q8"); assertEquals(2, quantVector.size()); assertEquals(1.0, quantVector.get(0), 0.01); assertEquals(2.0, quantVector.get(1), 0.01); assertEquals(1L, jedis.vcard(q8Key)); // Test with NOQUANT quantization parameters String noQuantKey = baseKey + ":noQuant"; VAddParams noQuantParams = new VAddParams().q8(); jedis.del(noQuantKey); result = jedis.vadd(noQuantKey, vector, "point:NOQUANT", noQuantParams); assertTrue(result); List noQuantVector = jedis.vemb(noQuantKey, "point:NOQUANT"); assertEquals(2, noQuantVector.size()); assertEquals(1.0, noQuantVector.get(0), 0.01); assertEquals(2.0, noQuantVector.get(1), 0.01); assertEquals(1L, jedis.vcard(noQuantKey)); } /** * Test VADD with dimension reduction using float array. Verifies that high-dimensional vectors * are reduced to target dimensions. */ @Test @SinceRedisVersion("8.0.0") public void testVaddWithReduceDimension(TestInfo testInfo) { String testKey = testInfo.getDisplayName() + ":test:vector:set"; String elementId = "point:REDUCED"; // Use a 4-dimensional vector that will be reduced to 2 dimensions float[] highDimVector = { 1.0f, 2.0f, 3.0f, 4.0f }; int targetDim = 2; // Create parameters for dimension reduction VAddParams params = new VAddParams(); // Add element with dimension reduction boolean result = jedis.vadd(testKey, highDimVector, elementId, targetDim, params); assertTrue(result); // Verify cardinality assertEquals(1L, jedis.vcard(testKey)); // Verify the vector was reduced to target dimensions assertEquals(targetDim, jedis.vdim(testKey)); // Retrieve and verify the reduced vector List reducedVector = jedis.vemb(testKey, elementId); assertEquals(targetDim, reducedVector.size()); // The values will be different due to random projection, but should exist assertNotNull(reducedVector.get(0)); assertNotNull(reducedVector.get(1)); } /** * Test vaddFP32 with dimension reduction using byte blob. Verifies that FP32 format vectors are * properly reduced. */ @Test @SinceRedisVersion("8.0.0") public void testVaddFP32WithReduceDimension(TestInfo testInfo) { String testKey = testInfo.getDisplayName() + ":test:vector:set"; String elementId = "point:FP32_REDUCED"; // Use a 4-dimensional vector that will be reduced to 2 dimensions float[] highDimVector = { 1.0f, 2.0f, 3.0f, 4.0f }; int targetDim = 2; // Convert to FP32 byte blob byte[] vectorBlob = floatArrayToFP32Bytes(highDimVector); // Create parameters for dimension reduction VAddParams params = new VAddParams(); // Add element with dimension reduction using FP32 format boolean result = jedis.vaddFP32(testKey, vectorBlob, elementId, targetDim, params); assertTrue(result); // Verify cardinality assertEquals(1L, jedis.vcard(testKey)); // Verify the vector was reduced to target dimensions assertEquals(targetDim, jedis.vdim(testKey)); // Retrieve and verify the reduced vector List reducedVector = jedis.vemb(testKey, elementId); assertEquals(targetDim, reducedVector.size()); // The values will be different due to random projection, but should exist assertNotNull(reducedVector.get(0)); assertNotNull(reducedVector.get(1)); } /** * Test VADD with dimension reduction and additional parameters. Verifies that REDUCE works * alongside other VAddParams. */ @Test @SinceRedisVersion("8.0.0") public void testVaddWithReduceDimensionAndParams(TestInfo testInfo) { String testKey = testInfo.getDisplayName() + ":test:vector:set"; String elementId = "point:REDUCED_WITH_PARAMS"; // Use a 6-dimensional vector that will be reduced to 3 dimensions float[] highDimVector = { 1.0f, 2.0f, 3.0f, 4.0f, 5.0f, 6.0f }; int targetDim = 3; // Create parameters with quantization and dimension reduction VAddParams params = new VAddParams().q8().ef(100); // Add element with dimension reduction and additional parameters boolean result = jedis.vadd(testKey, highDimVector, elementId, targetDim, params); assertTrue(result); // Verify cardinality assertEquals(1L, jedis.vcard(testKey)); // Verify the vector was reduced to target dimensions assertEquals(targetDim, jedis.vdim(testKey)); // Retrieve and verify the reduced vector List reducedVector = jedis.vemb(testKey, elementId); assertEquals(targetDim, reducedVector.size()); // All dimensions should have values (may be quantized) for (Double value : reducedVector) { assertNotNull(value); } } /** * Test VADD with SETATTR parameter. Verifies that attributes can be set when adding elements to * vector sets. */ @Test @SinceRedisVersion("8.0.0") public void testVaddWithSetAttr(TestInfo testInfo) { String testKey = testInfo.getDisplayName() + ":test:vector:set"; String elementId = "point:WITH_ATTR"; float[] vector = { 1.0f, 2.0f }; // Create simple text attributes for the element String attributes = "category=test,priority=high,score=95.5"; // Create parameters with attributes VAddParams params = new VAddParams().setAttr(attributes); // Add element with attributes boolean result = jedis.vadd(testKey, vector, elementId, params); assertTrue(result); // Verify cardinality and dimension assertEquals(1L, jedis.vcard(testKey)); assertEquals(2L, jedis.vdim(testKey)); // Verify the vector was stored correctly List storedVector = jedis.vemb(testKey, elementId); assertEquals(2, storedVector.size()); assertEquals(1.0, storedVector.get(0), 0.01); assertEquals(2.0, storedVector.get(1), 0.01); // Verify the attributes were stored correctly using VGETATTR String retrievedAttrs = jedis.vgetattr(testKey, elementId); assertNotNull(retrievedAttrs); assertEquals(attributes, retrievedAttrs); } /** * Test VADD with SETATTR and other parameters combined. Verifies that SETATTR works alongside * quantization and other options. */ @Test @SinceRedisVersion("8.0.0") public void testVaddWithSetAttrAndQuantization(TestInfo testInfo) { String testKey = testInfo.getDisplayName() + ":test:vector:set"; String elementId = "point:ATTR_QUANT"; float[] vector = { 1.0f, 2.0f }; // Create simple text attributes String attributes = "type=quantized,method=Q8,timestamp=2024-01-01"; // Create parameters with both attributes and quantization VAddParams params = new VAddParams().setAttr(attributes).q8().ef(100); // Add element with attributes and quantization boolean result = jedis.vadd(testKey, vector, elementId, params); assertTrue(result); // Verify cardinality and dimension assertEquals(1L, jedis.vcard(testKey)); assertEquals(2L, jedis.vdim(testKey)); // Verify the vector was stored (may be quantized) List storedVector = jedis.vemb(testKey, elementId); assertEquals(2, storedVector.size()); assertEquals(1.0, storedVector.get(0), 0.1); // Larger tolerance for quantization assertEquals(2.0, storedVector.get(1), 0.1); // Verify the attributes were stored correctly String retrievedAttrs = jedis.vgetattr(testKey, elementId); assertNotNull(retrievedAttrs); assertEquals(attributes, retrievedAttrs); } /** * Test VADD with SETATTR using FP32 format. Verifies that attributes work with binary vector * format. */ @Test @SinceRedisVersion("8.0.0") public void testVaddFP32WithSetAttr(TestInfo testInfo) { String testKey = testInfo.getDisplayName() + ":test:vector:set"; String elementId = "point:FP32_ATTR"; float[] vector = { 1.0f, 2.0f }; // Convert to FP32 byte blob byte[] vectorBlob = floatArrayToFP32Bytes(vector); // Create simple text attributes String attributes = "format=FP32,source=binary,validated=true"; // Create parameters with attributes VAddParams params = new VAddParams().setAttr(attributes); // Add element with FP32 format and attributes boolean result = jedis.vaddFP32(testKey, vectorBlob, elementId, params); assertTrue(result); // Verify cardinality and dimension assertEquals(1L, jedis.vcard(testKey)); assertEquals(2L, jedis.vdim(testKey)); // Verify the vector was stored correctly List storedVector = jedis.vemb(testKey, elementId); assertEquals(2, storedVector.size()); assertEquals(1.0, storedVector.get(0), 0.01); assertEquals(2.0, storedVector.get(1), 0.01); // Verify the attributes were stored correctly String retrievedAttrs = jedis.vgetattr(testKey, elementId); assertNotNull(retrievedAttrs); assertEquals(attributes, retrievedAttrs); } /** * Test VGETATTR command functionality. Verifies that attributes can be retrieved from vector set * elements. */ @Test @SinceRedisVersion("8.0.0") public void testVgetattr(TestInfo testInfo) { String testKey = testInfo.getDisplayName() + ":test:vector:set"; String elementId = "point:GETATTR_TEST"; float[] vector = { 1.0f, 2.0f }; // First add an element without attributes boolean result = jedis.vadd(testKey, vector, elementId); assertTrue(result); // VGETATTR should return null for element without attributes String attrs = jedis.vgetattr(testKey, elementId); assertNull(attrs); // Now add an element with attributes String elementWithAttrs = "point:WITH_ATTRS"; String attributes = "name=test_point,value=42,active=true"; VAddParams params = new VAddParams().setAttr(attributes); result = jedis.vadd(testKey, vector, elementWithAttrs, params); assertTrue(result); // VGETATTR should return the attributes String retrievedAttrs = jedis.vgetattr(testKey, elementWithAttrs); assertNotNull(retrievedAttrs); assertEquals(attributes, retrievedAttrs); // Test VGETATTR with non-existent element String nonExistentAttrs = jedis.vgetattr(testKey, "non_existent_element"); assertNull(nonExistentAttrs); } /** * Test VGETATTR with binary key and element. Verifies that VGETATTR works with byte array keys * and elements. */ @Test @SinceRedisVersion("8.0.0") public void testVgetattrBinary(TestInfo testInfo) { byte[] testKey = (testInfo.getDisplayName() + ":test:vector:set:binary").getBytes(); byte[] elementId = "binary_element_with_attrs".getBytes(); float[] vector = { 1.0f, 2.0f }; // VGETATTR should return null for element without attributes assertNull(jedis.vgetattr(testKey, elementId)); // Now add an element with attributes using binary key and element String attributes = "name=binary_test_point,value=42,active=true"; VAddParams params = new VAddParams().setAttr(attributes); boolean result = jedis.vadd(testKey, vector, elementId, params); assertTrue(result); // VGETATTR should return the attributes as byte array byte[] retrievedAttrs = jedis.vgetattr(testKey, elementId); assertNotNull(retrievedAttrs); // Convert byte array back to string and verify content String retrievedAttrsString = SafeEncoder.encode(retrievedAttrs); assertEquals(attributes, retrievedAttrsString); } /** * Test VSETATTR command functionality. Verifies that attributes can be set on vector set * elements. */ @Test @SinceRedisVersion("8.0.0") public void testVsetattr(TestInfo testInfo) { String testKey = testInfo.getDisplayName() + ":test:vector:set"; String elementId = "point:SETATTR_TEST"; float[] vector = { 1.0f, 2.0f }; // First add an element without attributes boolean result = jedis.vadd(testKey, vector, elementId); assertTrue(result); // Set attributes using VSETATTR String attributes = "name=test_point,value=42,active=true"; boolean setResult = jedis.vsetattr(testKey, elementId, attributes); assertTrue(setResult); // Verify attributes were set using VGETATTR String retrievedAttrs = jedis.vgetattr(testKey, elementId); assertNotNull(retrievedAttrs); assertEquals(attributes, retrievedAttrs); // Update attributes with new values String updatedAttributes = "name=updated_point,value=100,active=false,new_field=added"; setResult = jedis.vsetattr(testKey, elementId, updatedAttributes); assertTrue(setResult); // Verify updated attributes retrievedAttrs = jedis.vgetattr(testKey, elementId); assertNotNull(retrievedAttrs); assertEquals(updatedAttributes, retrievedAttrs); } /** * Test VSETATTR with binary key and element. Verifies that VSETATTR works with byte array keys * and elements. */ @Test @SinceRedisVersion("8.0.0") public void testVsetattrBinary(TestInfo testInfo) { byte[] testKey = (testInfo.getDisplayName() + ":test:vector:set:binary").getBytes(); byte[] elementId = "binary_setattr_element".getBytes(); float[] vector = { 1.0f, 2.0f }; // First add an element without attributes boolean result = jedis.vadd(testKey, vector, elementId); assertTrue(result); // Set attributes using binary VSETATTR String attributes = "name=binary_test_point,value=42,active=true"; byte[] attributesBytes = attributes.getBytes(); boolean setResult = jedis.vsetattr(testKey, elementId, attributesBytes); assertTrue(setResult); // Verify attributes were set using binary VGETATTR byte[] retrievedAttrs = jedis.vgetattr(testKey, elementId); assertNotNull(retrievedAttrs); // Convert back to string and verify String retrievedAttrsString = SafeEncoder.encode(retrievedAttrs); assertEquals(attributes, retrievedAttrsString); // Update attributes with new values using binary VSETATTR String updatedAttributes = "name=updated_binary_point,value=100,active=false,new_field=added"; byte[] updatedAttributesBytes = updatedAttributes.getBytes(); setResult = jedis.vsetattr(testKey, elementId, updatedAttributesBytes); assertTrue(setResult); // Verify updated attributes using binary VGETATTR retrievedAttrs = jedis.vgetattr(testKey, elementId); assertNotNull(retrievedAttrs); String updatedRetrievedString = SafeEncoder.encode(retrievedAttrs); assertEquals(updatedAttributes, updatedRetrievedString); } /** * Test VLINKS command functionality. Verifies that vector set links can be retrieved correctly. */ @Test @SinceRedisVersion("8.0.0") public void testVlinks(TestInfo testInfo) { String testKey = testInfo.getDisplayName() + ":test:vector:set"; // Add some vectors to create a vector set with links float[] vector1 = { 1.0f, 0.0f }; float[] vector2 = { 0.0f, 1.0f }; float[] vector3 = { 1.0f, 1.0f }; jedis.vadd(testKey, vector1, "element1"); jedis.vadd(testKey, vector2, "element2"); jedis.vadd(testKey, vector3, "element3"); // Get links for element1 List> links = jedis.vlinks(testKey, "element1"); assertNotNull(links); assertFalse(links.isEmpty()); for (List linkList : links) { for (String rawLink : linkList) { assertTrue(rawLink.equals("element2") || rawLink.equals("element3")); } } } /** * Test VLINKS command functionality. Verifies that vector set links can be retrieved correctly. */ @Test @SinceRedisVersion("8.0.0") public void testVlinksWithScores(TestInfo testInfo) { String testKey = testInfo.getDisplayName() + ":test:vector:set"; // Add some vectors to create a vector set with links float[] vector1 = { 1.0f, 0.0f }; float[] vector2 = { 0.0f, 1.0f }; float[] vector3 = { 1.0f, 1.0f }; jedis.vadd(testKey, vector1, "element1"); jedis.vadd(testKey, vector2, "element2"); jedis.vadd(testKey, vector3, "element3"); // Get links for element1 List> links = jedis.vlinksWithScores(testKey, "element1"); assertNotNull(links); assertFalse(links.isEmpty()); for (Map scores : links) { for (String element : scores.keySet()) { assertTrue(element.equals("element2") || element.equals("element3")); assertTrue(scores.get(element) > 0.0); } } } /** * Test VLINKS with binary key and element. Verifies that VLINKS works with byte array keys and * elements. */ @Test @SinceRedisVersion("8.0.0") public void testVlinksBinary(TestInfo testInfo) { byte[] testKey = (testInfo.getDisplayName() + ":test:vector:set:binary").getBytes(); byte[] elementId = "binary_element".getBytes(); // Add vectors using binary key and elements float[] vector1 = { 1.0f, 0.0f }; float[] vector2 = { 0.0f, 1.0f }; jedis.vadd(testKey, vector1, elementId); jedis.vadd(testKey, vector2, "element2".getBytes()); // Get links using binary VLINKS List> binaryLinks = jedis.vlinks(testKey, elementId); assertNotNull(binaryLinks); assertThat(binaryLinks.size(), is(greaterThan(0))); // If there are links, verify they are valid strings for (List linkList : binaryLinks) { for (byte[] rawLink : linkList) { String link = SafeEncoder.encode(rawLink); assertThat(link, is(notNullValue())); assertThat(link, not(emptyString())); } } } /** * Test VLINKS command functionality. Verifies that vector set links can be retrieved correctly. */ @Test @SinceRedisVersion("8.0.0") public void testVlinksBinaryWithScores(TestInfo testInfo) { byte[] testKey = (testInfo.getDisplayName() + ":test:vector:set:binary").getBytes(); // Add some vectors to create a vector set with links float[] vector1 = { 1.0f, 0.0f }; float[] vector2 = { 0.0f, 1.0f }; float[] vector3 = { 1.0f, 1.0f }; jedis.vadd(testKey, vector1, "element1".getBytes()); jedis.vadd(testKey, vector2, "element2".getBytes()); jedis.vadd(testKey, vector3, "element3".getBytes()); // Get links for element1 List> links = jedis.vlinksWithScores(testKey, "element1".getBytes()); assertNotNull(links); assertFalse(links.isEmpty()); for (Map scores : links) { for (byte[] element : scores.keySet()) { assertTrue(Arrays.equals(element, "element2".getBytes()) || Arrays.equals(element, "element3".getBytes())); assertTrue(scores.get(element) > 0.0); } } } /** * Test VLINKS with non-existent element. Verifies that VLINKS handles non-existent elements * correctly. */ @Test @SinceRedisVersion("8.0.0") public void testVlinksNonExistent(TestInfo testInfo) { String testKey = testInfo.getDisplayName() + ":test:vector:set:nonexistent"; // Add a vector first float[] vector = { 1.0f, 2.0f }; jedis.vadd(testKey, vector, "existing_element"); // Try to get links for non-existent element List> links = jedis.vlinks(testKey, "non_existent_element"); // Should return empty list or null for non-existent elements // Exact behavior depends on Redis implementation assertTrue(links == null || links.isEmpty()); } /** * Test VRANDMEMBER command functionality. Verifies that random vector set members can be * retrieved correctly. */ @Test @SinceRedisVersion("8.0.0") public void testVrandmember(TestInfo testInfo) { String testKey = testInfo.getDisplayName() + ":test:vector:set"; // Add some vectors to the set float[] vector1 = { 1.0f, 0.0f }; float[] vector2 = { 0.0f, 1.0f }; float[] vector3 = { 1.0f, 1.0f }; jedis.vadd(testKey, vector1, "element1"); jedis.vadd(testKey, vector2, "element2"); jedis.vadd(testKey, vector3, "element3"); // Get a single random member String randomMember = jedis.vrandmember(testKey); assertNotNull(randomMember); // Should be one of the added elements assertTrue(randomMember.equals("element1") || randomMember.equals("element2") || randomMember.equals("element3")); } /** * Test VRANDMEMBER with count parameter. Verifies that multiple random members can be retrieved. */ @Test @SinceRedisVersion("8.0.0") public void testVrandmemberWithCount(TestInfo testInfo) { String testKey = testInfo.getDisplayName() + ":test:vector:set:count"; // Add multiple vectors for (int i = 1; i <= 5; i++) { float[] vector = { (float) i, (float) i }; jedis.vadd(testKey, vector, "element" + i); } // Get 3 random members List randomMembers = jedis.vrandmember(testKey, 3); assertNotNull(randomMembers); assertEquals(3, randomMembers.size()); // All returned members should be valid element IDs String[] validElements = { "element1", "element2", "element3", "element4", "element5" }; for (String member : randomMembers) { assertTrue(Arrays.asList(validElements).contains(member)); } // Test with count larger than set size List allMembers = jedis.vrandmember(testKey, 10); assertNotNull(allMembers); assertTrue(allMembers.size() <= 5); // Should not exceed actual set size } /** * Test VRANDMEMBER with binary key. Verifies that VRANDMEMBER works with byte array keys. */ @Test @SinceRedisVersion("8.0.0") public void testVrandmemberBinary(TestInfo testInfo) { byte[] testKey = (testInfo.getDisplayName() + ":test:vector:set:binary").getBytes(); // Add vectors using binary key float[] vector1 = { 1.0f, 0.0f }; float[] vector2 = { 0.0f, 1.0f }; jedis.vadd(testKey, vector1, "binary_element1".getBytes()); jedis.vadd(testKey, vector2, "binary_element2".getBytes()); // Get random member using binary key byte[] randomMember = jedis.vrandmember(testKey); assertNotNull(randomMember); // Convert to string for comparison String randomMemberStr = SafeEncoder.encode(randomMember); assertTrue( randomMemberStr.equals("binary_element1") || randomMemberStr.equals("binary_element2")); // Test with count using binary key List randomMembers = jedis.vrandmember(testKey, 2); assertNotNull(randomMembers); assertTrue(randomMembers.size() <= 2); // Verify all returned members are valid for (byte[] member : randomMembers) { assertNotNull(member); String memberStr = SafeEncoder.encode(member); assertTrue(memberStr.equals("binary_element1") || memberStr.equals("binary_element2")); } } /** * Test VRANDMEMBER with empty vector set. Verifies that VRANDMEMBER handles empty sets correctly. */ @Test @SinceRedisVersion("8.0.0") public void testVrandmemberEmptySet(TestInfo testInfo) { String testKey = testInfo.getDisplayName() + ":test:empty:vector:set"; // Try to get random member from empty/non-existent set String randomMember = jedis.vrandmember(testKey); // Should return null for empty set assertNull(randomMember); // Test with count on empty set List randomMembers = jedis.vrandmember(testKey, 5); // Should return empty list for empty set assertTrue(randomMembers.isEmpty()); } /** * Test VRANDMEMBER with single element. Verifies that VRANDMEMBER works correctly with only one * element. */ @Test @SinceRedisVersion("8.0.0") public void testVrandmemberSingleElement(TestInfo testInfo) { String testKey = testInfo.getDisplayName() + ":test:vector:set:single"; // Add only one vector float[] vector = { 1.0f, 2.0f }; jedis.vadd(testKey, vector, "single_element"); // Get random member (should always be the single element) String randomMember = jedis.vrandmember(testKey); assertNotNull(randomMember); assertEquals("single_element", randomMember); // Test with count List randomMembers = jedis.vrandmember(testKey, 3); assertNotNull(randomMembers); assertEquals(1, randomMembers.size()); // Should only return the single element assertEquals("single_element", randomMembers.get(0)); } /** * Test VRANDMEMBER with negative count. Verifies that VRANDMEMBER handles negative count (allows * duplicates). */ @Test @SinceRedisVersion("8.0.0") public void testVrandmemberNegativeCount(TestInfo testInfo) { String testKey = testInfo.getDisplayName() + ":test:vector:set:negative"; // Add some vectors float[] vector1 = { 1.0f, 0.0f }; float[] vector2 = { 0.0f, 1.0f }; jedis.vadd(testKey, vector1, "element1"); jedis.vadd(testKey, vector2, "element2"); // Get random members with negative count (allows duplicates) List randomMembers = jedis.vrandmember(testKey, -5); assertNotNull(randomMembers); assertEquals(5, randomMembers.size()); // Should return exactly 5 elements (with possible // duplicates) // All returned members should be valid element IDs for (String member : randomMembers) { assertTrue(member.equals("element1") || member.equals("element2")); } } /** * Test VREM command functionality. Verifies that vector set elements can be removed correctly. */ @Test @SinceRedisVersion("8.0.0") public void testVrem(TestInfo testInfo) { String testKey = testInfo.getDisplayName() + ":test:vector:set"; // Add some vectors to the set float[] vector1 = { 1.0f, 0.0f }; float[] vector2 = { 0.0f, 1.0f }; float[] vector3 = { 1.0f, 1.0f }; jedis.vadd(testKey, vector1, "element1"); jedis.vadd(testKey, vector2, "element2"); jedis.vadd(testKey, vector3, "element3"); // Verify initial cardinality assertEquals(3L, jedis.vcard(testKey)); // Remove one element boolean removed = jedis.vrem(testKey, "element2"); assertTrue(removed); assertEquals(2L, jedis.vcard(testKey)); // Try to remove the same element again (should return false) removed = jedis.vrem(testKey, "element2"); assertFalse(removed); assertEquals(2L, jedis.vcard(testKey)); // Remove remaining elements removed = jedis.vrem(testKey, "element1"); assertTrue(removed); assertEquals(1L, jedis.vcard(testKey)); removed = jedis.vrem(testKey, "element3"); assertTrue(removed); assertEquals(0L, jedis.vcard(testKey)); } /** * Test VREM with binary key and elements. Verifies that VREM works with byte array keys and * elements. */ @Test @SinceRedisVersion("8.0.0") public void testVremBinary(TestInfo testInfo) { byte[] testKey = (testInfo.getDisplayName() + ":test:vector:set:binary").getBytes(); // Add vectors using binary key and elements float[] vector1 = { 1.0f, 0.0f }; float[] vector2 = { 0.0f, 1.0f }; float[] vector3 = { 1.0f, 1.0f }; jedis.vadd(testKey, vector1, "binary_element1".getBytes()); jedis.vadd(testKey, vector2, "binary_element2".getBytes()); jedis.vadd(testKey, vector3, "binary_element3".getBytes()); // Verify initial cardinality assertEquals(3L, jedis.vcard(testKey)); // Remove element using binary VREM boolean removed = jedis.vrem(testKey, "binary_element2".getBytes()); assertTrue(removed); assertEquals(2L, jedis.vcard(testKey)); // Remove remaining elements using binary VREM boolean removed1 = jedis.vrem(testKey, "binary_element1".getBytes()); boolean removed3 = jedis.vrem(testKey, "binary_element3".getBytes()); assertTrue(removed1); assertTrue(removed3); assertEquals(0L, jedis.vcard(testKey)); } /** * Test VREM with non-existent elements. Verifies that VREM handles non-existent elements * correctly. */ @Test @SinceRedisVersion("8.0.0") public void testVremNonExistent(TestInfo testInfo) { String testKey = testInfo.getDisplayName() + ":test:vector:set:nonexistent"; // Add one vector float[] vector = { 1.0f, 2.0f }; jedis.vadd(testKey, vector, "existing_element"); // Try to remove non-existent element boolean removed = jedis.vrem(testKey, "non_existent_element"); assertFalse(removed); assertEquals(1L, jedis.vcard(testKey)); // Cardinality should remain unchanged // Try to remove from non-existent vector set String nonExistentKey = testInfo.getDisplayName() + ":non:existent:key"; removed = jedis.vrem(nonExistentKey, "any_element"); assertFalse(removed); } /** * Test VSETATTR with empty attributes (attribute deletion). Verifies that setting empty * attributes removes them. */ @Test @SinceRedisVersion("8.0.0") public void testVsetattrDelete(TestInfo testInfo) { String testKey = testInfo.getDisplayName() + ":test:vector:set"; String elementId = "point:DELETE_ATTR"; float[] vector = { 1.0f, 2.0f }; // Add element with attributes String attributes = "category=test,priority=high"; VAddParams params = new VAddParams().setAttr(attributes); boolean result = jedis.vadd(testKey, vector, elementId, params); assertTrue(result); // Verify attributes exist String retrievedAttrs = jedis.vgetattr(testKey, elementId); assertNotNull(retrievedAttrs); assertEquals(attributes, retrievedAttrs); // Delete attributes by setting empty string boolean setResult = jedis.vsetattr(testKey, elementId, ""); assertTrue(setResult); // Verify attributes are deleted (should return null or empty) retrievedAttrs = jedis.vgetattr(testKey, elementId); assertNull(retrievedAttrs); } /** * Test VINFO command functionality. Verifies that vector set information can be retrieved. */ @Test @SinceRedisVersion("8.0.0") public void testVinfo(TestInfo testInfo) { String testKey = testInfo.getDisplayName() + ":test:vector:set"; float[] vector1 = { 1.0f, 2.0f }; float[] vector2 = { 3.0f, 4.0f }; // Add some elements to the vector set VAddParams params = new VAddParams().setAttr("{\"type\": \"fruit\", \"color\": \"red\"}"); boolean result1 = jedis.vadd(testKey, vector1, "element1", params); assertTrue(result1); boolean result2 = jedis.vadd(testKey, vector2, "element2"); assertTrue(result2); // Get vector set information VectorInfo info = jedis.vinfo(testKey); assertNotNull(info); // Verify basic information is present assertNotNull(info.getVectorInfo()); assertFalse(info.getVectorInfo().isEmpty()); assertEquals(2, info.getDimensionality()); assertEquals("int8", info.getType()); assertEquals(2L, info.getSize()); assertEquals(16L, info.getMaxNodes()); assertThat(info.getMaxNodeUid(), greaterThan(0L)); assertThat(info.getVSetUid(), greaterThan(0L)); assertEquals(0L, info.getProjectionInputDim()); assertEquals(1L, info.getAttributesCount()); assertNotNull(info.getMaxLevel()); } /** * Test VINFO with empty vector set. Verifies behavior when vector set doesn't exist. */ @Test @SinceRedisVersion("8.0.0") public void testVinfoNotExistingSet(TestInfo testInfo) { String testKey = testInfo.getDisplayName() + ":test:empty:vector:set"; VectorInfo info = jedis.vinfo(testKey); assertNull(info); } /** * Test VCARD command functionality. Verifies that vector set cardinality can be retrieved * correctly. */ @Test @SinceRedisVersion("8.0.0") public void testVcard(TestInfo testInfo) { String testKey = testInfo.getDisplayName() + ":test:vector:set"; float[] vector1 = { 1.0f, 2.0f }; float[] vector2 = { 3.0f, 4.0f }; // Initially, cardinality should be 0 for non-existent vector set assertEquals(0L, jedis.vcard(testKey)); // Add first element boolean result1 = jedis.vadd(testKey, vector1, "element1"); assertTrue(result1); assertEquals(1L, jedis.vcard(testKey)); assertEquals(1L, jedis.vcard(testKey.getBytes())); // Add second element boolean result2 = jedis.vadd(testKey, vector2, "element2"); assertTrue(result2); assertEquals(2L, jedis.vcard(testKey)); assertEquals(2L, jedis.vcard(testKey.getBytes())); // Try to add duplicate element (should not increase cardinality) boolean result3 = jedis.vadd(testKey, vector1, "element1"); assertFalse(result3); // Should return false for duplicate assertEquals(2L, jedis.vcard(testKey)); // Cardinality should remain 3 assertEquals(2L, jedis.vcard(testKey.getBytes())); // Remove an element boolean removed = jedis.vrem(testKey, "element2"); assertTrue(removed); assertEquals(1L, jedis.vcard(testKey)); assertEquals(1L, jedis.vcard(testKey.getBytes())); // Remove last element removed = jedis.vrem(testKey, "element1"); assertTrue(removed); assertEquals(0L, jedis.vcard(testKey)); assertEquals(0L, jedis.vcard(testKey.getBytes())); } /** * Test VCARD with non-existent vector set. */ @Test @SinceRedisVersion("8.0.0") public void testVcardNotExistingSet(TestInfo testInfo) { String testKey = testInfo.getDisplayName() + ":test:empty:vector:set"; // VCARD should return 0 for non-existent vector set assertEquals(0L, jedis.vcard(testKey)); } /** * Test VDIM command functionality. Verifies that vector set dimension can be retrieved correctly. */ @Test @SinceRedisVersion("8.0.0") public void testVdim(TestInfo testInfo) { String testKey = testInfo.getDisplayName() + ":test:vector:set"; // Add 2D vector float[] vector2D = { 1.0f, 2.0f }; boolean result = jedis.vadd(testKey, vector2D, "element1"); assertTrue(result); assertEquals(2L, jedis.vdim(testKey)); assertEquals(2L, jedis.vdim(testKey.getBytes())); // Test different dimensions String testKey3D = testInfo.getDisplayName() + ":test:vector:set:3d"; float[] vector3D = { 1.0f, 2.0f, 3.0f }; jedis.vadd(testKey3D, vector3D, "element3d"); assertEquals(3L, jedis.vdim(testKey3D)); assertEquals(3L, jedis.vdim(testKey3D.getBytes())); } @Test @SinceRedisVersion("8.0.0") public void testVdimNotExistingSet(TestInfo testInfo) { String testKey = testInfo.getDisplayName() + ":test:empty:vector:set"; JedisDataException thrown = assertThrows(JedisDataException.class, () -> jedis.vdim(testKey)); assertThat(thrown.getMessage(), is("ERR key does not exist")); thrown = assertThrows(JedisDataException.class, () -> jedis.vdim(testKey.getBytes())); assertThat(thrown.getMessage(), is("ERR key does not exist")); } // Test VDIM with empty set @Test @SinceRedisVersion("8.0.0") public void testVdimWithEmptySet(TestInfo testInfo) { String testKey = testInfo.getDisplayName() + ":test:empty:vector:set"; // Add 2D vector float[] vector2D = { 1.0f, 2.0f }; assertTrue(jedis.vadd(testKey, vector2D, "element1")); assertTrue(jedis.vrem(testKey, "element1")); assertEquals(0L, (jedis.vcard(testKey))); JedisDataException thrown = assertThrows(JedisDataException.class, () -> jedis.vdim(testKey)); assertThat(thrown.getMessage(), is("ERR key does not exist")); thrown = assertThrows(JedisDataException.class, () -> jedis.vdim(testKey.getBytes())); assertThat(thrown.getMessage(), is("ERR key does not exist")); } /** * Test VDIM with dimension reduction. Verifies that VDIM returns the reduced dimension when * REDUCE is used. */ @Test @SinceRedisVersion("8.0.0") public void testVdimWithDimensionReduction(TestInfo testInfo) { String testKey = testInfo.getDisplayName() + ":test:vector:set:reduced"; // Add 4D vector with dimension reduction to 2D float[] vector4D = { 1.0f, 2.0f, 3.0f, 4.0f }; VAddParams params = new VAddParams(); boolean result = jedis.vadd(testKey, vector4D, "element_reduced", 2, params); assertTrue(result); // VDIM should return the reduced dimension (2), not the original (4) assertEquals(2L, jedis.vdim(testKey)); assertEquals(2L, jedis.vdim(testKey.getBytes())); } /** * Test VEMB command functionality. Verifies that vector embeddings can be retrieved correctly. */ @Test @SinceRedisVersion("8.0.0") public void testVemb(TestInfo testInfo) { String testKey = testInfo.getDisplayName() + ":test:vector:set"; // Add vector to the set float[] originalVector = { 1.0f, 2.0f }; VAddParams params = new VAddParams().noQuant(); boolean result = jedis.vadd(testKey, originalVector, "element1", params); assertTrue(result); // Retrieve the vector using VEMB List retrievedVector = jedis.vemb(testKey, "element1"); assertNotNull(retrievedVector); assertEquals(2, retrievedVector.size()); // Verify vector values (with small tolerance for floating point precision) assertEquals(1.0f, retrievedVector.get(0), 0.001); assertEquals(2.0f, retrievedVector.get(1), 0.001); } /** * Test VEMB with binary key and element. Verifies that VEMB works with byte array keys and * elements. */ @Test @SinceRedisVersion("8.0.0") public void testVembBinary(TestInfo testInfo) { byte[] testKey = (testInfo.getDisplayName() + ":test:vector:set:binary").getBytes(); byte[] elementId = "binary_element".getBytes(); // Add vector to the set using binary key and element float[] originalVector = { 0.0f, 1.0f }; boolean result = jedis.vadd(testKey, originalVector, elementId); assertTrue(result); // Retrieve the vector using binary VEMB List retrievedVector = jedis.vemb(testKey, elementId); assertNotNull(retrievedVector); assertEquals(2, retrievedVector.size()); // Verify vector values assertEquals(0.0, retrievedVector.get(0), 0.001); assertEquals(1.0, retrievedVector.get(1), 0.001); } /** * Test VEMB with RAW option. Verifies that VEMB can return raw vector data when RAW flag is used * with FP32 format. */ @Test @SinceRedisVersion("8.0.0") public void testVembRaw(TestInfo testInfo) { String testKey = testInfo.getDisplayName() + ":test:vector:set:raw"; // Add vector to the set using FP32 format float[] originalVector = { 1.0f, 2.0f, 3.0f, 4.0f, 5.0f }; byte[] vectorBlob = floatArrayToFP32Bytes(originalVector); VAddParams params = new VAddParams().noQuant(); boolean result = jedis.vaddFP32(testKey, vectorBlob, "raw_element", params); assertTrue(result); // Retrieve the vector using VEMB with RAW option RawVector rawVector = jedis.vembRaw(testKey, "raw_element"); assertNotNull(rawVector); // Verify the raw data length matches the original vector length byte[] rawData = rawVector.getRawData(); int expectedLength = originalVector.length * 4; // 4 bytes per float assertEquals(expectedLength, rawData.length); // Verify the quantization type is FP32 assertEquals("f32", rawVector.getQuantizationType()); // Verify the norm is present (L2 norm of the vector) assertNotNull(rawVector.getNorm()); assertTrue(rawVector.getNorm() > 0); // Verify the raw data contains the correct float values by converting back // IEEE 754 32-bit floats are stored in little-endian format List reconstructedVector = VectorTestUtils.fp32BytesToFloatArray(rawData); // Verify the reconstructed vector matches the original assertEquals(originalVector.length, reconstructedVector.size()); for (int i = 0; i < originalVector.length; i++) { assertEquals(originalVector[i] / rawVector.getNorm(), reconstructedVector.get(i), 0.001f); } } /** * Test VEMB with RAW option. Verifies that VEMB can return raw vector data when RAW flag is used * with FP32 format. */ @Test @SinceRedisVersion("8.0.0") public void testVembRawBinary(TestInfo testInfo) { String testKey = testInfo.getDisplayName() + ":test:vector:set:raw"; // Add vector to the set using FP32 format float[] originalVector = { 1.0f, 2.0f, 3.0f, 4.0f, 5.0f }; byte[] vectorBlob = floatArrayToFP32Bytes(originalVector); VAddParams params = new VAddParams().noQuant(); boolean result = jedis.vaddFP32(testKey, vectorBlob, "raw_element", params); assertTrue(result); // Retrieve the vector using VEMB with RAW option RawVector rawVector = jedis.vembRaw(testKey.getBytes(), "raw_element".getBytes()); assertNotNull(rawVector); // Verify the raw data length matches the original vector length byte[] rawData = rawVector.getRawData(); int expectedLength = originalVector.length * 4; // 4 bytes per float assertEquals(expectedLength, rawData.length); // Verify the quantization type is FP32 assertEquals("f32", rawVector.getQuantizationType()); // Verify the norm is present (L2 norm of the vector) assertNotNull(rawVector.getNorm()); assertTrue(rawVector.getNorm() > 0); // Verify the raw data contains the correct float values by converting back // IEEE 754 32-bit floats are stored in little-endian format List reconstructedVector = VectorTestUtils.fp32BytesToFloatArray(rawData); // Verify the reconstructed vector matches the original assertEquals(originalVector.length, reconstructedVector.size()); for (int i = 0; i < originalVector.length; i++) { assertEquals(originalVector[i] / rawVector.getNorm(), reconstructedVector.get(i), 0.001f); } } /** * Test VEMB with non-existent element. Verifies that VEMB handles non-existent elements * correctly. */ @Test @SinceRedisVersion("8.0.0") public void testVembNonExistent(TestInfo testInfo) { String testKey = testInfo.getDisplayName() + ":test:vector:set:nonexistent"; // Add a vector first float[] vector = { 1.0f, 2.0f }; jedis.vadd(testKey, vector, "existing_element"); // Try to retrieve non-existent element assertNull(jedis.vemb(testKey, "non_existent_element")); } /** * Test VSIM command functionality. Verifies vector similarity search with vectors and elements. */ @Test @SinceRedisVersion("8.0.0") public void testVsim(TestInfo testInfo) { String testKey = testInfo.getDisplayName() + ":test:vector:set"; setupVSimTestSet(testKey); // Test vsim with vector List similar = jedis.vsim(testKey, new float[] { 0.15f, 0.25f, 0.35f }); assertNotNull(similar); assertThat(similar, is(not(empty()))); assertThat(similar.size(), is(4)); assertThat(similar, hasItems("element1", "element2", "element3", "element4")); // Test vsim with element similar = jedis.vsimByElement(testKey, "element1"); assertNotNull(similar); assertThat(similar, is(not(empty()))); assertThat(similar.size(), is(4)); assertThat(similar, hasItems("element1", "element2", "element3", "element4")); // Test vsim with vector and parameters VSimParams params = new VSimParams().count(2); similar = jedis.vsim(testKey, new float[] { 0.15f, 0.25f, 0.35f }, params); assertNotNull(similar); assertThat(similar.size(), is(2)); // Test vsim with element and parameters similar = jedis.vsimByElement(testKey, "element1", params); assertNotNull(similar); assertThat(similar.size(), is(2)); } private void setupVSimTestSet(String testKey) { // Add test vectors float[] vector1 = { 0.1f, 0.2f, 0.3f }; float[] vector2 = { 0.2f, 0.3f, 0.4f }; float[] vector3 = { 0.3f, 0.4f, 0.5f }; float[] vector4 = { -0.1f, -0.2f, -0.3f }; jedis.vadd(testKey, vector1, "element1"); jedis.vadd(testKey, vector2, "element2"); jedis.vadd(testKey, vector3, "element3"); jedis.vadd(testKey, vector4, "element4"); } /** * Test VSIM command with scores functionality. Verifies vector similarity search returns scores * correctly. */ @Test @SinceRedisVersion("8.0.0") public void testVsimWithScores(TestInfo testInfo) { String testKey = testInfo.getDisplayName() + ":test:vector:set:scores"; setupVSimTestSet(testKey); // Test vsim with vector and scores VSimParams params = new VSimParams(); Map similarWithScores = jedis.vsimWithScores(testKey, new float[] { 0.15f, 0.25f, 0.35f }, params); assertThat(similarWithScores.keySet(), hasItems("element1", "element2", "element3")); assertThat(similarWithScores.values(), everyItem(greaterThanOrEqualTo(0.0))); // Test vsim with element and scores similarWithScores = jedis.vsimByElementWithScores(testKey, "element1", params); assertThat(similarWithScores.keySet(), hasItems("element1", "element2", "element3")); assertThat(similarWithScores.get("element1"), closeTo(1, 0.01)); assertThat(similarWithScores.get("element4"), closeTo(0, 0.01)); assertEquals(1.0, similarWithScores.get("element1"), 0.001); // Test with count parameter params = new VSimParams().count(2); similarWithScores = jedis.vsimWithScores(testKey, new float[] { 0.15f, 0.25f, 0.35f }, params); assertThat(similarWithScores.keySet(), hasItems("element1", "element2")); assertThat(similarWithScores.values(), everyItem(greaterThan(0.0))); // Test with epsilon parameter (distance-based filtering) params = new VSimParams().epsilon(0.2); // Only elements with similarity >= 0.8 similarWithScores = jedis.vsimWithScores(testKey, new float[] { -0.1f, -0.2f, -0.3f }, params); assertNotNull(similarWithScores); assertThat(similarWithScores.keySet(), hasItems("element4")); // Verify all returned scores meet the epsilon threshold for (Double score : similarWithScores.values()) { assertTrue(score >= (1.0 - 0.2)); // score >= 0.8 } } /** * Test VSIM with scores and attributes. */ @Test @SinceRedisVersion("8.2.0") public void testVsimWithScoresAndAttribs(TestInfo testInfo) { String testKey = testInfo.getDisplayName() + ":test:vector:set:scores:attribs"; // Add test vectors with attributes VAddParams addParams1 = new VAddParams().setAttr("category=test,priority=high"); jedis.vadd(testKey, new float[] { 0.1f, 0.2f, 0.3f }, "element1", addParams1); VAddParams addParams2 = new VAddParams().setAttr("category=prod,priority=low"); jedis.vadd(testKey, new float[] { 0.15f, 0.25f, 0.35f }, "element2", addParams2); // Add element without attributes jedis.vadd(testKey, new float[] { 0.9f, 0.8f, 0.7f }, "element3"); VSimParams params = new VSimParams(); Map similarWithScoresAndAttribs = jedis .vsimWithScoresAndAttribs(testKey, new float[] { 0.15f, 0.25f, 0.35f }, params); assertNotNull(similarWithScoresAndAttribs); assertThat(similarWithScoresAndAttribs, is(not(anEmptyMap()))); // Verify scores and attributes are present for (Map.Entry entry : similarWithScoresAndAttribs.entrySet()) { String element = entry.getKey(); VSimScoreAttribs data = entry.getValue(); assertNotNull(data); assertNotNull(data.getScore()); assertThat(data.getScore(), is(both(greaterThanOrEqualTo(0.0)).and(lessThanOrEqualTo(1.0)))); // Check attributes based on element if ("element1".equals(element)) { assertEquals("category=test,priority=high", data.getAttributes()); } else if ("element2".equals(element)) { assertEquals("category=prod,priority=low", data.getAttributes()); } else if ("element3".equals(element)) { assertNull(data.getAttributes()); // No attributes set } } } /** * Test VSIM by element with scores and attributes. */ @Test @SinceRedisVersion("8.2.0") public void testVsimByElementWithScoresAndAttribs(TestInfo testInfo) { String testKey = testInfo.getDisplayName() + ":test:vector:set:element:scores:attribs"; // Add test vectors with attributes VAddParams addParams1 = new VAddParams().setAttr("type=reference,quality=high"); jedis.vadd(testKey, new float[] { 0.1f, 0.2f, 0.3f }, "reference", addParams1); VAddParams addParams2 = new VAddParams().setAttr("type=similar,quality=medium"); jedis.vadd(testKey, new float[] { 0.12f, 0.22f, 0.32f }, "similar1", addParams2); VAddParams addParams3 = new VAddParams().setAttr("type=different,quality=low"); jedis.vadd(testKey, new float[] { 0.9f, 0.8f, 0.7f }, "different", addParams3); VSimParams params = new VSimParams(); Map similarWithScoresAndAttribs = jedis .vsimByElementWithScoresAndAttribs(testKey, "reference", params); assertNotNull(similarWithScoresAndAttribs); assertThat(similarWithScoresAndAttribs, is(not(anEmptyMap()))); // Reference element should have perfect similarity with itself assertTrue(similarWithScoresAndAttribs.containsKey("reference")); VSimScoreAttribs referenceData = similarWithScoresAndAttribs.get("reference"); assertThat(referenceData.getScore(), is(closeTo(1.0, 0.001))); assertEquals("type=reference,quality=high", referenceData.getAttributes()); } /** * Test VSIM command with binary keys and elements. Verifies vector similarity search works with * byte arrays. */ @Test @SinceRedisVersion("8.0.0") public void testVsimBinary(TestInfo testInfo) { byte[] testKey = (testInfo.getDisplayName() + ":test:vector:set:binary").getBytes(); setupVSimTestSetBinary(testKey); // Test vsim with vector (binary) List similar = jedis.vsim(testKey, new float[] { 0.15f, 0.25f, 0.35f }); assertNotNull(similar); assertThat(similar, is(not(empty()))); assertThat(similar.size(), is(3)); assertThat(getBinaryElementNames(similar), hasItems("element1", "element2", "element3")); // Test vsim with element (binary) similar = jedis.vsimByElement(testKey, "element1".getBytes()); assertNotNull(similar); assertThat(similar, is(not(empty()))); assertThat(similar.size(), is(3)); assertThat(getBinaryElementNames(similar), hasItems("element1", "element2", "element3")); // Test vsim with vector and parameters (binary) VSimParams params = new VSimParams().count(2); similar = jedis.vsim(testKey, new float[] { 0.15f, 0.25f, 0.35f }, params); assertNotNull(similar); assertThat(similar.size(), is(2)); // Test vsim with element and parameters (binary) similar = jedis.vsimByElement(testKey, "element1".getBytes(), params); assertNotNull(similar); assertThat(similar.size(), is(2)); } /** * Test VSIM command with binary keys and scores. Verifies vector similarity search returns scores * with binary data. */ @Test @SinceRedisVersion("8.0.0") public void testVsimBinaryWithScores(TestInfo testInfo) { byte[] testKey = (testInfo.getDisplayName() + ":test:vector:set:binary:scores").getBytes(); setupVSimTestSetBinary(testKey); // Test vsim with vector and scores (binary) VSimParams params = new VSimParams(); Map similarWithScores = jedis.vsimWithScores(testKey, new float[] { 0.15f, 0.25f, 0.35f }, params); assertNotNull(similarWithScores); assertThat(similarWithScores, is(not(anEmptyMap()))); // Verify scores are present and valid for (Map.Entry entry : similarWithScores.entrySet()) { assertNotNull(entry.getKey()); assertNotNull(entry.getValue()); assertThat(entry.getValue(), is(greaterThan(0.0))); } // Test vsim with element and scores (binary) similarWithScores = jedis.vsimByElementWithScores(testKey, "element1".getBytes(), params); assertNotNull(similarWithScores); assertThat(similarWithScores, is(not(anEmptyMap()))); // Element1 should have perfect similarity with itself Double element1Score = getBinaryScoreForElement(similarWithScores, "element1"); assertNotNull(element1Score); assertThat(element1Score, is(closeTo(1.0, 0.001))); // Test with count parameter (binary) params = new VSimParams().count(2); similarWithScores = jedis.vsimWithScores(testKey, new float[] { 0.15f, 0.25f, 0.35f }, params); assertNotNull(similarWithScores); assertThat(similarWithScores.size(), is(2)); } /** * Helper method to set up test vector set for binary VSIM tests. */ private void setupVSimTestSetBinary(byte[] testKey) { // Add test vectors - same as non-binary version float[] vector1 = { 0.1f, 0.2f, 0.3f }; float[] vector2 = { 0.15f, 0.25f, 0.35f }; float[] vector3 = { 0.9f, 0.8f, 0.7f }; jedis.vadd(testKey, vector1, "element1".getBytes()); jedis.vadd(testKey, vector2, "element2".getBytes()); jedis.vadd(testKey, vector3, "element3".getBytes()); } /** * Helper method to convert binary element list to string names for assertions. */ private List getBinaryElementNames(List binaryElements) { return binaryElements.stream().map(String::new).collect(java.util.stream.Collectors.toList()); } /** * Helper method to get score for a specific element from binary score map. */ private Double getBinaryScoreForElement(Map scoreMap, String elementName) { byte[] elementBytes = elementName.getBytes(); for (Map.Entry entry : scoreMap.entrySet()) { if (java.util.Arrays.equals(entry.getKey(), elementBytes)) { return entry.getValue(); } } return null; } } ================================================ FILE: src/test/java/redis/clients/jedis/commands/unified/AllKindOfValuesCommandsTestBase.java ================================================ package redis.clients.jedis.commands.unified; import static org.hamcrest.MatcherAssert.assertThat; import static org.junit.jupiter.api.Assertions.assertArrayEquals; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertNotNull; import static org.junit.jupiter.api.Assertions.assertNull; import static org.junit.jupiter.api.Assertions.assertTrue; import static org.junit.jupiter.api.Assertions.fail; import static org.junit.jupiter.api.Assumptions.assumeFalse; import static org.junit.jupiter.api.Assumptions.assumeTrue; import static redis.clients.jedis.Protocol.Command.BLPOP; import static redis.clients.jedis.Protocol.Command.GET; import static redis.clients.jedis.Protocol.Command.HGETALL; import static redis.clients.jedis.Protocol.Command.LRANGE; import static redis.clients.jedis.Protocol.Command.PING; import static redis.clients.jedis.Protocol.Command.RPUSH; import static redis.clients.jedis.Protocol.Command.SET; import static redis.clients.jedis.Protocol.Command.XINFO; import static redis.clients.jedis.util.SafeEncoder.encode; import static redis.clients.jedis.util.SafeEncoder.encodeObject; import static redis.clients.jedis.params.ScanParams.SCAN_POINTER_START; import static redis.clients.jedis.params.ScanParams.SCAN_POINTER_START_BINARY; import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; import java.util.HashMap; import java.util.HashSet; import java.util.List; import java.util.Set; import io.redis.test.annotations.ConditionalOnEnv; import io.redis.test.annotations.EnabledOnCommand; import io.redis.test.annotations.SinceRedisVersion; import org.hamcrest.Matchers; import org.junit.jupiter.api.Disabled; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Tag; import redis.clients.jedis.CommandArguments; import redis.clients.jedis.*; import redis.clients.jedis.args.ExpiryOption; import redis.clients.jedis.params.ScanParams; import redis.clients.jedis.resps.ScanResult; import redis.clients.jedis.params.RestoreParams; import redis.clients.jedis.exceptions.JedisDataException; import redis.clients.jedis.params.SetParams; import redis.clients.jedis.util.*; @Tag("integration") public abstract class AllKindOfValuesCommandsTestBase extends UnifiedJedisCommandsTestBase { protected final byte[] bfoo = { 0x01, 0x02, 0x03, 0x04 }; protected final byte[] bfoo1 = { 0x01, 0x02, 0x03, 0x04, 0x0A }; protected final byte[] bfoo2 = { 0x01, 0x02, 0x03, 0x04, 0x0B }; protected final byte[] bfoo3 = { 0x01, 0x02, 0x03, 0x04, 0x0C }; protected final byte[] bbar = { 0x05, 0x06, 0x07, 0x08 }; protected final byte[] bbar1 = { 0x05, 0x06, 0x07, 0x08, 0x0A }; protected final byte[] bbar2 = { 0x05, 0x06, 0x07, 0x08, 0x0B }; protected final byte[] bbar3 = { 0x05, 0x06, 0x07, 0x08, 0x0C }; protected final byte[] bfoobar = { 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08 }; protected final byte[] bfoostar = { 0x01, 0x02, 0x03, 0x04, '*' }; protected final byte[] bbarstar = { 0x05, 0x06, 0x07, 0x08, '*' }; protected final byte[] bnx = { 0x6E, 0x78 }; protected final byte[] bex = { 0x65, 0x78 }; final int expireSeconds = 2; public AllKindOfValuesCommandsTestBase(RedisProtocol protocol) { super(protocol); } @Test public void exists() { String status = jedis.set("foo", "bar"); assertEquals("OK", status); status = jedis.set(bfoo, bbar); assertEquals("OK", status); assertTrue(jedis.exists("foo")); assertTrue(jedis.exists(bfoo)); assertEquals(1L, jedis.del("foo")); assertEquals(1L, jedis.del(bfoo)); assertFalse(jedis.exists("foo")); assertFalse(jedis.exists(bfoo)); } @Test public void existsMany() { String status = jedis.set("foo1", "bar1"); assertEquals("OK", status); status = jedis.set("foo2", "bar2"); assertEquals("OK", status); assertEquals(2L, jedis.exists("foo1", "foo2")); assertEquals(1L, jedis.del("foo1")); assertEquals(1L, jedis.exists("foo1", "foo2")); } @Test public void del() { jedis.set("foo1", "bar1"); jedis.set("foo2", "bar2"); jedis.set("foo3", "bar3"); assertEquals(3L, jedis.del("foo1", "foo2", "foo3")); assertFalse(jedis.exists("foo1")); assertFalse(jedis.exists("foo2")); assertFalse(jedis.exists("foo3")); jedis.set("foo1", "bar1"); assertEquals(1L, jedis.del("foo1", "foo2")); assertEquals(0L, jedis.del("foo1", "foo2")); // Binary ... jedis.set(bfoo1, bbar1); jedis.set(bfoo2, bbar2); jedis.set(bfoo3, bbar3); assertEquals(3L, jedis.del(bfoo1, bfoo2, bfoo3)); assertFalse(jedis.exists(bfoo1)); assertFalse(jedis.exists(bfoo2)); assertFalse(jedis.exists(bfoo3)); jedis.set(bfoo1, bbar1); assertEquals(1, jedis.del(bfoo1, bfoo2)); assertEquals(0, jedis.del(bfoo1, bfoo2)); } @Test public void unlink() { jedis.set("foo1", "bar1"); jedis.set("foo2", "bar2"); jedis.set("foo3", "bar3"); assertEquals(3, jedis.unlink("foo1", "foo2", "foo3")); assertEquals(0, jedis.exists("foo1", "foo2", "foo3")); jedis.set("foo1", "bar1"); assertEquals(1, jedis.unlink("foo1", "foo2")); assertEquals(0, jedis.unlink("foo1", "foo2")); jedis.set("foo", "bar"); assertEquals(1, jedis.unlink("foo")); assertFalse(jedis.exists("foo")); // Binary jedis.set(bfoo1, bbar1); jedis.set(bfoo2, bbar2); jedis.set(bfoo3, bbar3); assertEquals(3, jedis.unlink(bfoo1, bfoo2, bfoo3)); assertEquals(0, jedis.exists(bfoo1, bfoo2, bfoo3)); jedis.set(bfoo1, bbar1); assertEquals(1, jedis.unlink(bfoo1, bfoo2)); assertEquals(0, jedis.unlink(bfoo1, bfoo2)); jedis.set(bfoo, bbar); assertEquals(1, jedis.unlink(bfoo)); assertFalse(jedis.exists(bfoo)); } @Test public void type() { jedis.set("foo", "bar"); assertEquals("string", jedis.type("foo")); // Binary jedis.set(bfoo, bbar); assertEquals("string", jedis.type(bfoo)); } @Test public void keys() { jedis.set("foo", "bar"); jedis.set("foobar", "bar"); Set keys = jedis.keys("foo*"); AssertUtil.assertCollectionContains(keys, "foo"); AssertUtil.assertCollectionContains(keys, "foobar"); assertEquals(Collections.emptySet(), jedis.keys("bar*")); // Binary jedis.set(bfoo, bbar); jedis.set(bfoobar, bbar); Set bkeys = jedis.keys(bfoostar); AssertUtil.assertByteArrayCollectionContains(bkeys, bfoo); AssertUtil.assertByteArrayCollectionContains(bkeys, bfoobar); assertEquals(Collections.emptySet(), jedis.keys(bbarstar)); } @Test public void randomKey() { assertNull(jedis.randomKey()); for (int i = 0; i < 100; i++) { jedis.set("foo" + i, "bar"+i); } String key = jedis.randomKey(); assertNotNull(key); assertTrue(key.startsWith("foo")); assertEquals(key.replace("foo", "bar"), jedis.get(key)); } @Test @ConditionalOnEnv(value = TestEnvUtil.ENV_REDIS_ENTERPRISE, enabled = false) public void rename() { jedis.set("foo", "bar"); String status = jedis.rename("foo", "bar"); assertEquals("OK", status); assertNull(jedis.get("foo")); assertEquals("bar", jedis.get("bar")); // Binary jedis.set(bfoo, bbar); String bstatus = jedis.rename(bfoo, bbar); assertEquals("OK", bstatus); assertNull(jedis.get(bfoo)); assertArrayEquals(bbar, jedis.get(bbar)); } @Test public void renameOldAndNewAreTheSame() { assertEquals("OK", jedis.set("foo", "bar")); assertEquals("OK", jedis.rename("foo", "foo")); // Binary assertEquals("OK", jedis.set(bfoo, bbar)); assertEquals("OK", jedis.rename(bfoo, bfoo)); } @Test @ConditionalOnEnv(value = TestEnvUtil.ENV_REDIS_ENTERPRISE, enabled = false) public void renamenx() { jedis.set("foo", "bar"); assertEquals(1, jedis.renamenx("foo", "bar")); jedis.set("foo", "bar"); assertEquals(0, jedis.renamenx("foo", "bar")); // Binary jedis.set(bfoo, bbar); assertEquals(1, jedis.renamenx(bfoo, bbar)); jedis.set(bfoo, bbar); assertEquals(0, jedis.renamenx(bfoo, bbar)); } @Test public void dbSize() { assertEquals(0, jedis.dbSize()); jedis.set("foo", "bar"); assertEquals(1, jedis.dbSize()); // Binary jedis.set(bfoo, bbar); assertEquals(2, jedis.dbSize()); } @Test @SinceRedisVersion(value="7.0.0", message = "Starting with Redis version 7.0.0: Added options: NX, XX, GT and LT.") public void expire() { assertEquals(0, jedis.expire("foo", 20L)); jedis.set("foo", "bar"); assertEquals(1, jedis.expire("foo", 20L)); assertEquals(0, jedis.expire("foo", 20L, ExpiryOption.NX)); // Binary assertEquals(0, jedis.expire(bfoo, 20L)); jedis.set(bfoo, bbar); assertEquals(1, jedis.expire(bfoo, 20L)); assertEquals(0, jedis.expire(bfoo, 20L, ExpiryOption.NX)); } @Test @SinceRedisVersion(value="7.0.0", message = "Starting with Redis version 7.0.0: Added options: NX, XX, GT and LT.") public void expireAt() { long unixTime = (System.currentTimeMillis() / 1000L) + 20; assertEquals(0, jedis.expireAt("foo", unixTime)); jedis.set("foo", "bar"); unixTime = (System.currentTimeMillis() / 1000L) + 20; assertEquals(1, jedis.expireAt("foo", unixTime)); assertEquals(1, jedis.expireAt("foo", unixTime, ExpiryOption.XX)); // Binary assertEquals(0, jedis.expireAt(bfoo, unixTime)); jedis.set(bfoo, bbar); unixTime = (System.currentTimeMillis() / 1000L) + 20; assertEquals(1, jedis.expireAt(bfoo, unixTime)); assertEquals(1, jedis.expireAt(bfoo, unixTime, ExpiryOption.XX)); } @Test @SinceRedisVersion(value="7.0.0") public void expireTime() { long unixTime; jedis.set("foo", "bar"); unixTime = (System.currentTimeMillis() / 1000L) + 20; jedis.expireAt("foo", unixTime); assertEquals(unixTime, jedis.expireTime("foo"), 0.0001); // Binary jedis.set(bfoo, bbar); unixTime = (System.currentTimeMillis() / 1000L) + 20; jedis.expireAt(bfoo, unixTime); assertEquals(unixTime, jedis.expireTime(bfoo), 0.0001); } @Test public void ttl() { assertEquals(-2, jedis.ttl("foo")); jedis.set("foo", "bar"); assertEquals(-1, jedis.ttl("foo")); jedis.expire("foo", 20); long ttl = jedis.ttl("foo"); assertTrue(ttl >= 0 && ttl <= 20); // Binary assertEquals(-2, jedis.ttl(bfoo)); jedis.set(bfoo, bbar); assertEquals(-1, jedis.ttl(bfoo)); jedis.expire(bfoo, 20); long bttl = jedis.ttl(bfoo); assertTrue(bttl >= 0 && bttl <= 20); } @Test public void touch() throws Exception { assertEquals(0, jedis.touch("foo1", "foo2", "foo3")); jedis.set("foo1", "bar1"); Thread.sleep(1100); // little over 1 sec assertTrue(jedis.objectIdletime("foo1") > 0); assertEquals(1, jedis.touch("foo1")); assertEquals(0L, jedis.objectIdletime("foo1").longValue()); assertEquals(1, jedis.touch("foo1", "foo2", "foo3")); jedis.set("foo2", "bar2"); jedis.set("foo3", "bar3"); assertEquals(3, jedis.touch("foo1", "foo2", "foo3")); // Binary assertEquals(0, jedis.touch(bfoo1, bfoo2, bfoo3)); jedis.set(bfoo1, bbar1); Thread.sleep(1100); // little over 1 sec assertTrue(jedis.objectIdletime(bfoo1) > 0); assertEquals(1, jedis.touch(bfoo1)); assertEquals(0L, jedis.objectIdletime(bfoo1).longValue()); assertEquals(1, jedis.touch(bfoo1, bfoo2, bfoo3)); jedis.set(bfoo2, bbar2); jedis.set(bfoo3, bbar3); assertEquals(3, jedis.touch(bfoo1, bfoo2, bfoo3)); } @Test public void persist() { jedis.setex("foo", 60 * 60, "bar"); assertTrue(jedis.ttl("foo") > 0); assertEquals(1, jedis.persist("foo")); assertEquals(-1, jedis.ttl("foo")); // Binary jedis.setex(bfoo, 60 * 60, bbar); assertTrue(jedis.ttl(bfoo) > 0); assertEquals(1, jedis.persist(bfoo)); assertEquals(-1, jedis.ttl(bfoo)); } @Test public void dumpAndRestore() { jedis.set("foo1", "bar"); byte[] sv = jedis.dump("foo1"); jedis.restore("foo2", 0, sv); assertEquals("bar", jedis.get("foo2")); jedis.set(bfoo1, bbar); sv = jedis.dump(bfoo1); jedis.restore(bfoo2, 0, sv); assertArrayEquals(bbar, jedis.get(bfoo2)); } @Test @Disabled(value = "TODO: Regression in 8.0-M02 discarding restore idle time.") public void restoreParams() { jedis.set("foo", "bar"); jedis.set("from", "a"); byte[] serialized = jedis.dump("from"); try { jedis.restore("foo", 0, serialized); fail("Simple restore on a existing key should fail"); } catch (JedisDataException e) { // should be here } try { jedis.restore("foo", 0, serialized, RestoreParams.restoreParams()); fail("Simple restore on a existing key should fail"); } catch (JedisDataException e) { // should be here } assertEquals("bar", jedis.get("foo")); jedis.restore("foo", 1000, serialized, RestoreParams.restoreParams().replace()); assertEquals("a", jedis.get("foo")); assertTrue(jedis.pttl("foo") <= 1000); jedis.restore("bar", System.currentTimeMillis() + 1000, serialized, RestoreParams.restoreParams().replace().absTtl()); assertTrue(jedis.pttl("bar") <= 1000); jedis.restore("bar1", 1000, serialized, RestoreParams.restoreParams().replace().idleTime(1000)); assertEquals(1000, jedis.objectIdletime("bar1").longValue()); } @Test @SinceRedisVersion(value="7.0.0", message = "Starting with Redis version 7.0.0: Added options: NX, XX, GT and LT.") public void pexpire() { assertEquals(0, jedis.pexpire("foo", 10000)); jedis.set("foo1", "bar1"); assertEquals(1, jedis.pexpire("foo1", 10000)); jedis.set("foo2", "bar2"); assertEquals(1, jedis.pexpire("foo2", 200000000000L)); assertEquals(0, jedis.pexpire("foo2", 10000000, ExpiryOption.NX)); assertEquals(1, jedis.pexpire("foo2", 10000000, ExpiryOption.XX)); assertEquals(0, jedis.pexpire("foo2", 10000, ExpiryOption.GT)); assertEquals(1, jedis.pexpire("foo2", 10000, ExpiryOption.LT)); long pttl = jedis.pttl("foo2"); assertTrue(pttl > 100L); // Binary assertEquals(0, jedis.pexpire(bfoo, 10000)); jedis.set(bfoo, bbar); assertEquals(1, jedis.pexpire(bfoo, 10000)); assertEquals(0, jedis.pexpire(bfoo, 10000, ExpiryOption.NX)); } @Test public void pexpireAt() { long unixTime = (System.currentTimeMillis()) + 10000; assertEquals(0, jedis.pexpireAt("foo", unixTime)); jedis.set("foo", "bar"); assertEquals(1, jedis.pexpireAt("foo", unixTime)); // Binary assertEquals(0, jedis.pexpireAt(bfoo, unixTime)); jedis.set(bfoo, bbar); assertEquals(1, jedis.pexpireAt(bfoo, unixTime)); } @Test @SinceRedisVersion(value="7.0.0") public void pexpireTime() { long unixTime = (System.currentTimeMillis()) + 10000; jedis.set("foo", "bar"); jedis.pexpireAt("foo", unixTime); assertEquals(unixTime, jedis.pexpireTime("foo"), 0.0001); // Binary jedis.set(bfoo, bbar); jedis.pexpireAt(bfoo, unixTime); assertEquals(unixTime, jedis.pexpireTime(bfoo), 0.0001); } @Test public void pttl() { assertEquals(-2, jedis.pttl("foo")); jedis.set("foo", "bar"); assertEquals(-1, jedis.pttl("foo")); jedis.pexpire("foo", 20000); long pttl = jedis.pttl("foo"); assertTrue(pttl >= 0 && pttl <= 20000); // Binary assertEquals(-2, jedis.pttl(bfoo)); jedis.set(bfoo, bbar); assertEquals(-1, jedis.pttl(bfoo)); jedis.pexpire(bfoo, 20000); pttl = jedis.pttl(bfoo); assertTrue(pttl >= 0 && pttl <= 20000); } @Test public void psetex() { long pttl; jedis.psetex("foo", 200000000000L, "bar"); pttl = jedis.pttl("foo"); assertTrue(pttl > 100000000000L); // Binary jedis.psetex(bfoo, 200000000000L, bbar); pttl = jedis.pttl(bfoo); assertTrue(pttl > 100000000000L); } @Test public void scan() { jedis.set("b", "b"); jedis.set("a", "a"); ScanResult result = jedis.scan(SCAN_POINTER_START); assertEquals(SCAN_POINTER_START, result.getCursor()); assertFalse(result.getResult().isEmpty()); // binary ScanResult bResult = jedis.scan(SCAN_POINTER_START_BINARY); assertArrayEquals(SCAN_POINTER_START_BINARY, bResult.getCursorAsBytes()); assertFalse(bResult.getResult().isEmpty()); } @Test public void scanMatch() { ScanParams params = new ScanParams(); params.match("a*"); jedis.set("b", "b"); jedis.set("a", "a"); jedis.set("aa", "aa"); ScanResult result = jedis.scan(SCAN_POINTER_START, params); assertEquals(SCAN_POINTER_START, result.getCursor()); assertFalse(result.getResult().isEmpty()); // binary params = new ScanParams(); params.match(bfoostar); jedis.set(bfoo1, bbar); jedis.set(bfoo2, bbar); jedis.set(bfoo3, bbar); ScanResult bResult = jedis.scan(SCAN_POINTER_START_BINARY, params); assertArrayEquals(SCAN_POINTER_START_BINARY, bResult.getCursorAsBytes()); assertFalse(bResult.getResult().isEmpty()); } @Test public void scanCount() { ScanParams params = new ScanParams(); params.count(2); for (int i = 0; i < 10; i++) { jedis.set("a" + i, "a" + i); } ScanResult result = jedis.scan(SCAN_POINTER_START, params); assertFalse(result.getResult().isEmpty()); // binary params = new ScanParams(); params.count(2); jedis.set(bfoo1, bbar); jedis.set(bfoo2, bbar); jedis.set(bfoo3, bbar); ScanResult bResult = jedis.scan(SCAN_POINTER_START_BINARY, params); assertFalse(bResult.getResult().isEmpty()); } @Test public void scanType() { ScanParams noParams = new ScanParams(); ScanParams pagingParams = new ScanParams().count(4); jedis.set("a", "a"); jedis.hset("b", "b", "b"); jedis.set("c", "c"); jedis.sadd("d", "d"); jedis.set("e", "e"); jedis.zadd("f", 0d, "f"); jedis.set("g", "g"); // string ScanResult scanResult; scanResult = jedis.scan(SCAN_POINTER_START, pagingParams, "string"); assertFalse(scanResult.isCompleteIteration()); int page1Count = scanResult.getResult().size(); scanResult = jedis.scan(scanResult.getCursor(), pagingParams, "string"); assertTrue(scanResult.isCompleteIteration()); int page2Count = scanResult.getResult().size(); assertEquals(4, page1Count + page2Count); scanResult = jedis.scan(SCAN_POINTER_START, noParams, "hash"); assertEquals(Collections.singletonList("b"), scanResult.getResult()); scanResult = jedis.scan(SCAN_POINTER_START, noParams, "set"); assertEquals(Collections.singletonList("d"), scanResult.getResult()); scanResult = jedis.scan(SCAN_POINTER_START, noParams, "zset"); assertEquals(Collections.singletonList("f"), scanResult.getResult()); // binary final byte[] string = "string".getBytes(); final byte[] hash = "hash".getBytes(); final byte[] set = "set".getBytes(); final byte[] zset = "zset".getBytes(); ScanResult binaryResult; jedis.set("a", "a"); jedis.hset("b", "b", "b"); jedis.set("c", "c"); jedis.sadd("d", "d"); jedis.set("e", "e"); jedis.zadd("f", 0d, "f"); jedis.set("g", "g"); binaryResult = jedis.scan(SCAN_POINTER_START_BINARY, pagingParams, string); assertFalse(binaryResult.isCompleteIteration()); page1Count = binaryResult.getResult().size(); binaryResult = jedis.scan(binaryResult.getCursorAsBytes(), pagingParams, string); assertTrue(binaryResult.isCompleteIteration()); page2Count = binaryResult.getResult().size(); assertEquals(4, page1Count + page2Count); binaryResult = jedis.scan(SCAN_POINTER_START_BINARY, noParams, hash); AssertUtil.assertByteArrayListEquals(Collections.singletonList(new byte[]{98}), binaryResult.getResult()); binaryResult = jedis.scan(SCAN_POINTER_START_BINARY, noParams, set); AssertUtil.assertByteArrayListEquals(Collections.singletonList(new byte[]{100}), binaryResult.getResult()); binaryResult = jedis.scan(SCAN_POINTER_START_BINARY, noParams, zset); AssertUtil.assertByteArrayListEquals(Collections.singletonList(new byte[]{102}), binaryResult.getResult()); } @Test public void scanIsCompleteIteration() { for (int i = 0; i < 100; i++) { jedis.set("a" + i, "a" + i); } ScanResult result = jedis.scan(SCAN_POINTER_START); // note: in theory Redis would be allowed to already return all results on the 1st scan, // but in practice this never happens for data sets greater than a few tens // see: https://redis.io/commands/scan#number-of-elements-returned-at-every-scan-call assertFalse(result.isCompleteIteration()); result = scanCompletely(result.getCursor()); assertNotNull(result); assertTrue(result.isCompleteIteration()); } private ScanResult scanCompletely(String cursor) { ScanResult scanResult; do { scanResult = jedis.scan(cursor); cursor = scanResult.getCursor(); } while (!SCAN_POINTER_START.equals(scanResult.getCursor())); return scanResult; } @Test public void setNxExAndGet() { assertEquals("OK", jedis.set("hello", "world", SetParams.setParams().nx().ex(expireSeconds))); assertEquals("world", jedis.get("hello")); assertNull(jedis.set("hello", "bar", SetParams.setParams().nx().ex(expireSeconds))); assertEquals("world", jedis.get("hello")); long ttl = jedis.ttl("hello"); assertTrue(ttl > 0 && ttl <= expireSeconds); // binary byte[] bworld = { 0x77, 0x6F, 0x72, 0x6C, 0x64 }; byte[] bhello = { 0x68, 0x65, 0x6C, 0x6C, 0x6F }; assertEquals("OK", jedis.set(bworld, bhello, SetParams.setParams().nx().ex(expireSeconds))); assertArrayEquals(bhello, jedis.get(bworld)); assertNull(jedis.set(bworld, bbar, SetParams.setParams().nx().ex(expireSeconds))); assertArrayEquals(bhello, jedis.get(bworld)); long bttl = jedis.ttl(bworld); assertTrue(bttl > 0 && bttl <= expireSeconds); } @Test public void setGetOptionTest() { assertEquals("OK", jedis.set("hello", "world")); // GET old value assertEquals("world", jedis.setGet("hello", "jedis")); assertEquals("jedis", jedis.get("hello")); // GET null value assertNull(jedis.setGet("key", "value")); } @Test public void setGet() { assertEquals("OK", jedis.set("hello", "world")); // GET old value assertEquals("world", jedis.setGet("hello", "jedis", SetParams.setParams())); assertEquals("jedis", jedis.get("hello")); // GET null value assertNull(jedis.setGet("key", "value", SetParams.setParams())); } /** * Tests the executeCommand method with CommandArguments for proper cluster routing. * This test uses explicit key marking through CommandArguments.key() for cluster compatibility. */ @Test public void executeCommandTest() { // Test SET command with proper key marking Object obj = jedis.executeCommand(new CommandArguments(SET).key("x").add("1")); String returnValue = encode((byte[]) obj); assertEquals("OK", returnValue); // Test GET command with proper key marking obj = jedis.executeCommand(new CommandArguments(GET).key("x")); returnValue = encode((byte[]) obj); assertEquals("1", returnValue); // Test RPUSH commands with proper key marking jedis.executeCommand(new CommandArguments(RPUSH).key("foo").add("a")); jedis.executeCommand(new CommandArguments(RPUSH).key("foo").add("b")); jedis.executeCommand(new CommandArguments(RPUSH).key("foo").add("c")); // Test LRANGE command with proper key marking obj = jedis.executeCommand(new CommandArguments(LRANGE).key("foo").add("0").add("2")); List list = (List) obj; List expected = new ArrayList<>(3); expected.add("a".getBytes()); expected.add("b".getBytes()); expected.add("c".getBytes()); for (int i = 0; i < 3; i++) assertArrayEquals(expected.get(i), list.get(i)); // Test PING command (keyless command) assertEquals("PONG", encode((byte[]) jedis.executeCommand(new CommandArguments(PING)))); } /** * Tests the executeCommand method with blocking CommandArguments for proper cluster routing. * This test uses explicit key marking through CommandArguments.key() and .blocking() for * cluster compatibility with blocking operations. */ @Test public void executeBlockingCommandTest() { // Test BLPOP on empty list - should return null after timeout assertNull(jedis.executeCommand( new CommandArguments(BLPOP).key("foo").add(Long.toString(1L)).blocking())); // Setup: push an element to the list using executeCommand with proper key marking jedis.executeCommand(new CommandArguments(RPUSH).key("foo").add("bar")); // Test BLPOP with data - should return the key and value assertEquals(Arrays.asList("foo", "bar"), encodeObject(jedis.executeCommand( new CommandArguments(BLPOP).key("foo").add(Long.toString(1L)).blocking()))); // Test BLPOP on now-empty list - should return null after timeout assertNull(jedis.executeCommand( new CommandArguments(BLPOP).key("foo").add(Long.toString(1L)).blocking())); } @Test public void encodeCompleteResponsePing() { assertEquals("PONG", SafeEncoder.encodeObject(jedis.sendCommand(PING))); } @Test public void encodeCompleteResponseHgetall() { assumeFalse(protocol == RedisProtocol.RESP3); HashMap entries = new HashMap<>(); entries.put("foo", "bar"); entries.put("foo2", "bar2"); jedis.hset("hash:test:encode", entries); List encodeObj = (List) SafeEncoder.encodeObject(jedis.executeCommand(new CommandArguments(HGETALL).key("hash:test:encode"))); assertEquals(4, encodeObj.size()); entries.forEach((k, v) -> { assertThat((Iterable) encodeObj, Matchers.hasItem(k)); assertEquals(v, findValueFromMapAsList(encodeObj, k)); }); } @Test public void encodeCompleteResponseHgetallResp3() { assumeTrue(protocol == RedisProtocol.RESP3); HashMap entries = new HashMap<>(); entries.put("foo", "bar"); entries.put("foo2", "bar2"); jedis.hset("hash:test:encode", entries); List encodeObj = (List) SafeEncoder.encodeObject(jedis.executeCommand(new CommandArguments(HGETALL).key("hash:test:encode"))); assertEquals(2, encodeObj.size()); encodeObj.forEach(kv -> { assertThat(entries, Matchers.hasEntry(kv.getKey(), kv.getValue())); }); } @Test public void encodeCompleteResponseXinfoStream() { assumeFalse(protocol == RedisProtocol.RESP3); HashMap entry = new HashMap<>(); entry.put("foo", "bar"); StreamEntryID entryID = jedis.xadd("mystream", StreamEntryID.NEW_ENTRY, entry); jedis.xgroupCreate("mystream", "mygroup", null, false); Object obj = jedis.executeCommand(new CommandArguments(Protocol.Command.XINFO).add("STREAM").key("mystream")); List encodeObj = (List) SafeEncoder.encodeObject(obj); assertThat(encodeObj.size(), Matchers.greaterThanOrEqualTo(14)); assertEquals( 0, encodeObj.size() % 2, "must have even number of elements"); // must be even assertEquals(1L, findValueFromMapAsList(encodeObj, "length")); assertEquals(entryID.toString(), findValueFromMapAsList(encodeObj, "last-generated-id")); List entryAsList = new ArrayList<>(2); entryAsList.add("foo"); entryAsList.add("bar"); assertEquals(entryAsList, ((List) findValueFromMapAsList(encodeObj, "first-entry")).get(1)); assertEquals(entryAsList, ((List) findValueFromMapAsList(encodeObj, "last-entry")).get(1)); } @Test public void encodeCompleteResponseXinfoStreamResp3() { assumeTrue(protocol == RedisProtocol.RESP3); HashMap entry = new HashMap<>(); entry.put("foo", "bar"); StreamEntryID entryID = jedis.xadd("mystream", StreamEntryID.NEW_ENTRY, entry); jedis.xgroupCreate("mystream", "mygroup", null, false); Object obj = jedis.executeCommand(new CommandArguments(XINFO).add("STREAM").key("mystream")); List encodeObj = (List) SafeEncoder.encodeObject(obj); assertThat(encodeObj.size(), Matchers.greaterThanOrEqualTo(7)); assertEquals(1L, findValueFromMapAsKeyValueList(encodeObj, "length")); assertEquals(entryID.toString(), findValueFromMapAsKeyValueList(encodeObj, "last-generated-id")); List entryAsList = new ArrayList<>(2); entryAsList.add("foo"); entryAsList.add("bar"); assertEquals(entryAsList, ((List) findValueFromMapAsKeyValueList(encodeObj, "first-entry")).get(1)); assertEquals(entryAsList, ((List) findValueFromMapAsKeyValueList(encodeObj, "last-entry")).get(1)); } private Object findValueFromMapAsList(List list, Object key) { for (int i = 0; i < list.size(); i += 2) { if (key.equals(list.get(i))) { return list.get(i + 1); } } return null; } private Object findValueFromMapAsKeyValueList(List list, Object key) { for (KeyValue kv : list) { if (key.equals(kv.getKey())) { return kv.getValue(); } } return null; } @Test @ConditionalOnEnv(value = TestEnvUtil.ENV_REDIS_ENTERPRISE, enabled = false) public void copy() { assertFalse(jedis.copy("unknown", "foo", false)); jedis.set("foo1", "bar"); assertTrue(jedis.copy("foo1", "foo2", false)); assertEquals("bar", jedis.get("foo2")); // replace jedis.set("foo1", "bar1"); assertTrue(jedis.copy("foo1", "foo2", true)); assertEquals("bar1", jedis.get("foo2")); // Binary assertFalse(jedis.copy(bfoobar, bfoo, false)); jedis.set(bfoo1, bbar); assertTrue(jedis.copy(bfoo1, bfoo2, false)); assertArrayEquals(bbar, jedis.get(bfoo2)); // replace jedis.set(bfoo1, bbar1); assertTrue(jedis.copy(bfoo1, bfoo2, true)); assertArrayEquals(bbar1, jedis.get(bfoo2)); } @Test public void scanIteration() { Set allIn = new HashSet<>(26 * 26); char[] arr = new char[2]; for (int i = 0; i < 26; i++) { arr[0] = (char) ('a' + i); for (int j = 0; j < 26; j++) { arr[1] = (char) ('a' + j); String str = new String(arr); jedis.incr(str); allIn.add(str); } } Set allScan = new HashSet<>(); ScanIteration scan = jedis.scanIteration(10, "*"); while (!scan.isIterationCompleted()) { ScanResult batch = scan.nextBatch(); allScan.addAll(batch.getResult()); } assertEquals(allIn, allScan); } @Test @EnabledOnCommand("DELEX") public void set_ex_ifeq_then_delex() { String k = "k:set-ex-ifeq"; // Initial set with EX assertEquals("OK", jedis.set(k, "v1", SetParams.setParams().ex(100))); assertTrue(jedis.ttl(k) > 0); // Conditional update with IFEQ + EX assertEquals("OK", jedis.set(k, "v2", SetParams.setParams().ex(200).condition(CompareCondition.valueEq("v1")))); assertEquals("v2", jedis.get(k)); assertTrue(jedis.ttl(k) > 100); // Delete with DELEX using value condition assertEquals(0, jedis.delex(k, CompareCondition.valueEq("wrong"))); assertEquals(1, jedis.delex(k, CompareCondition.valueEq("v2"))); assertFalse(jedis.exists(k)); } @Test @EnabledOnCommand("DELEX") public void set_exAt_ifne_then_delex() { String k = "k:set-exat-ifne"; long expiryTimestamp = (System.currentTimeMillis() / 1000) + 300; // Initial set jedis.set(k, "v1"); // Conditional update with IFNE + EXAT assertEquals("OK", jedis.set(k, "v2", SetParams.setParams().exAt(expiryTimestamp).condition(CompareCondition.valueNe("v2")))); assertEquals("v2", jedis.get(k)); assertTrue(jedis.ttl(k) > 200); // Delete with DELEX using value condition assertEquals(0, jedis.delex(k, CompareCondition.valueNe("v2"))); assertEquals(1, jedis.delex(k, CompareCondition.valueNe("wrong"))); assertFalse(jedis.exists(k)); } @Test @EnabledOnCommand("DELEX") public void setGet_px_ifdne_then_delex() { String k = "k:setget-px-ifdne"; String wrongKey = "wrong"; // Initial set jedis.set(k, "A"); jedis.set(wrongKey, "wrong"); String digestBefore = jedis.digestKey(k); String digestWrong = jedis.digestKey(wrongKey); // digest for different value // Conditional setGet with IFDNE + PX (digest not equal) assertEquals("A", jedis.setGet(k, "B", SetParams.setParams().px(100000).condition(CompareCondition.digestNe(digestWrong)))); assertEquals("B", jedis.get(k)); assertTrue(jedis.pttl(k) > 90000); // Delete with DELEX using digest condition String digestAfter = jedis.digestKey(k); assertEquals(0, jedis.delex(k, CompareCondition.digestNe(digestAfter))); assertEquals(1, jedis.delex(k, CompareCondition.digestNe(digestBefore))); assertFalse(jedis.exists(k)); } @Test @EnabledOnCommand("DELEX") public void setGet_pxAt_ifdeq_then_delex() { String k = "k:setget-pxat-ifdeq"; long expiryTimestampMs = System.currentTimeMillis() + 300000; // Initial set jedis.set(k, "X"); String digestX = jedis.digestKey(k); // Conditional setGet with IFDEQ + PXAT (digest equal) assertEquals("X", jedis.setGet(k, "Y", SetParams.setParams().pxAt(expiryTimestampMs) .condition(CompareCondition.digestEq(digestX)))); assertEquals("Y", jedis.get(k)); assertTrue(jedis.pttl(k) > 200000); // Delete with DELEX using digest condition String digestY = jedis.digestKey(k); assertEquals(0, jedis.delex(k, CompareCondition.digestEq(digestX))); assertEquals(1, jedis.delex(k, CompareCondition.digestEq(digestY))); assertFalse(jedis.exists(k)); } } ================================================ FILE: src/test/java/redis/clients/jedis/commands/unified/BinaryValuesCommandsTestBase.java ================================================ package redis.clients.jedis.commands.unified; import static org.junit.jupiter.api.Assertions.assertArrayEquals; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertNull; import static org.junit.jupiter.api.Assertions.assertThrows; import static org.junit.jupiter.api.Assertions.assertTrue; import static redis.clients.jedis.Protocol.Command.BLPOP; import static redis.clients.jedis.Protocol.Command.GET; import static redis.clients.jedis.Protocol.Command.LRANGE; import static redis.clients.jedis.Protocol.Command.RPUSH; import static redis.clients.jedis.Protocol.Command.SET; import static redis.clients.jedis.params.SetParams.setParams; import static redis.clients.jedis.util.AssertUtil.assertByteArrayListEquals; import java.util.ArrayList; import java.util.List; import java.util.stream.Stream; import io.redis.test.annotations.ConditionalOnEnv; import io.redis.test.annotations.EnabledOnCommand; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.Arguments; import org.junit.jupiter.params.provider.MethodSource; import redis.clients.jedis.CommandArguments; import redis.clients.jedis.Protocol; import redis.clients.jedis.RedisProtocol; import redis.clients.jedis.exceptions.JedisDataException; import redis.clients.jedis.params.GetExParams; import redis.clients.jedis.params.MSetExParams; import redis.clients.jedis.util.SafeEncoder; import redis.clients.jedis.util.TestEnvUtil; public abstract class BinaryValuesCommandsTestBase extends UnifiedJedisCommandsTestBase { protected byte[] bfoo = { 0x01, 0x02, 0x03, 0x04 }; protected byte[] bbar = { 0x05, 0x06, 0x07, 0x08 }; protected byte[] bxx = { 0x78, 0x78 }; protected byte[] bnx = { 0x6E, 0x78 }; protected byte[] bex = { 0x65, 0x78 }; protected byte[] bpx = { 0x70, 0x78 }; protected int expireSeconds = 2; protected long expireMillis = expireSeconds * 1000; protected byte[] binaryValue; public BinaryValuesCommandsTestBase(RedisProtocol protocol) { super(protocol); } @BeforeEach public void startUp() { StringBuilder sb = new StringBuilder(); for (int n = 0; n < 1000; n++) { sb.append("A"); } binaryValue = sb.toString().getBytes(); } @Test public void setAndGet() { assertEquals("OK", jedis.set(bfoo, binaryValue)); assertArrayEquals(binaryValue, jedis.get(bfoo)); assertNull(jedis.get(bbar)); } @Test public void setNxExAndGet() { assertEquals("OK", jedis.set(bfoo, binaryValue, setParams().nx().ex(expireSeconds))); assertArrayEquals(binaryValue, jedis.get(bfoo)); assertNull(jedis.get(bbar)); } @Test public void setIfNotExistAndGet() { assertEquals("OK", jedis.set(bfoo, binaryValue)); // nx should fail if value exists assertNull(jedis.set(bfoo, binaryValue, setParams().nx().ex(expireSeconds))); assertArrayEquals(binaryValue, jedis.get(bfoo)); assertNull(jedis.get(bbar)); } @Test public void setIfExistAndGet() { assertEquals("OK", jedis.set(bfoo, binaryValue)); // nx should fail if value exists assertEquals("OK", jedis.set(bfoo, binaryValue, setParams().xx().ex(expireSeconds))); byte[] value = jedis.get(bfoo); assertArrayEquals(binaryValue, value); assertNull(jedis.get(bbar)); } @Test public void setFailIfNotExistAndGet() { // xx should fail if value does NOT exists assertNull(jedis.set(bfoo, binaryValue, setParams().xx().ex(expireSeconds))); } @Test public void setAndExpireMillis() { assertEquals("OK", jedis.set(bfoo, binaryValue, setParams().nx().px(expireMillis))); long ttl = jedis.ttl(bfoo); assertTrue(ttl > 0 && ttl <= expireSeconds); } @Test public void setAndExpire() { assertEquals("OK", jedis.set(bfoo, binaryValue, setParams().nx().ex(expireSeconds))); long ttl = jedis.ttl(bfoo); assertTrue(ttl > 0 && ttl <= expireSeconds); } @Test public void setAndKeepttl() { assertEquals("OK", jedis.set(bfoo, binaryValue, setParams().nx().ex(expireSeconds))); assertEquals("OK", jedis.set(bfoo, binaryValue, setParams().keepttl())); assertEquals("OK", jedis.set(bfoo, binaryValue, setParams().keepTtl())); long ttl = jedis.ttl(bfoo); assertTrue(0 < ttl && ttl <= expireSeconds); jedis.set(bfoo, binaryValue); ttl = jedis.ttl(bfoo); assertTrue(ttl < 0); } @Test public void setAndPxat() { assertEquals("OK", jedis.set(bfoo, binaryValue, setParams().nx().pxAt(System.currentTimeMillis() + expireMillis))); long ttl = jedis.ttl(bfoo); assertTrue(ttl > 0 && ttl <= expireSeconds); } @Test public void setAndExat() { assertEquals("OK", jedis.set(bfoo, binaryValue, setParams().nx().exAt(System.currentTimeMillis() / 1000 + expireSeconds))); long ttl = jedis.ttl(bfoo); assertTrue(ttl > 0 && ttl <= expireSeconds); } @Test public void getSet() { assertNull(jedis.getSet(bfoo, binaryValue)); assertArrayEquals(binaryValue, jedis.get(bfoo)); } @Test public void getDel() { assertEquals("OK", jedis.set(bfoo, bbar)); assertArrayEquals(bbar, jedis.getDel(bfoo)); assertNull(jedis.get(bfoo)); } @Test public void getEx() { assertNull(jedis.getEx(bfoo, GetExParams.getExParams().ex(1))); jedis.set(bfoo, bbar); assertArrayEquals(bbar, jedis.getEx(bfoo, GetExParams.getExParams().ex(10))); long ttl = jedis.ttl(bfoo); assertTrue(ttl > 0 && ttl <= 10); assertArrayEquals(bbar, jedis.getEx(bfoo, GetExParams.getExParams().px(20000l))); ttl = jedis.ttl(bfoo); assertTrue(ttl > 10 && ttl <= 20); assertArrayEquals(bbar, jedis.getEx(bfoo, GetExParams.getExParams().exAt(System.currentTimeMillis() / 1000 + 30))); ttl = jedis.ttl(bfoo); assertTrue(ttl > 20 && ttl <= 30); assertArrayEquals(bbar, jedis.getEx(bfoo, GetExParams.getExParams().pxAt(System.currentTimeMillis() + 40000l))); ttl = jedis.ttl(bfoo); assertTrue(ttl > 30 && ttl <= 40); assertArrayEquals(bbar, jedis.getEx(bfoo, GetExParams.getExParams().persist())); assertEquals(-1L, jedis.ttl(bfoo)); } @Test public void mget() { List values = jedis.mget(bfoo, bbar); List expected = new ArrayList<>(); expected.add(null); expected.add(null); assertByteArrayListEquals(expected, values); jedis.set(bfoo, binaryValue); expected = new ArrayList<>(); expected.add(binaryValue); expected.add(null); assertByteArrayListEquals(expected, jedis.mget(bfoo, bbar)); jedis.set(bbar, bfoo); expected = new ArrayList<>(); expected.add(binaryValue); expected.add(bfoo); assertByteArrayListEquals(expected, jedis.mget(bfoo, bbar)); } @Test public void setnx() { assertEquals(1, jedis.setnx(bfoo, binaryValue)); assertArrayEquals(binaryValue, jedis.get(bfoo)); assertEquals(0, jedis.setnx(bfoo, bbar)); assertArrayEquals(binaryValue, jedis.get(bfoo)); } @Test public void setex() { assertEquals("OK", jedis.setex(bfoo, 20, binaryValue)); long ttl = jedis.ttl(bfoo); assertTrue(ttl > 0 && ttl <= 20); } @Test public void mset() { assertEquals("OK", jedis.mset(bfoo, binaryValue, bbar, bfoo)); assertArrayEquals(binaryValue, jedis.get(bfoo)); assertArrayEquals(bfoo, jedis.get(bbar)); } @Test @ConditionalOnEnv(value = TestEnvUtil.ENV_REDIS_ENTERPRISE, enabled = false) public void msetnx() { assertEquals(1, jedis.msetnx(bfoo, binaryValue, bbar, bfoo)); assertArrayEquals(binaryValue, jedis.get(bfoo)); assertArrayEquals(bfoo, jedis.get(bbar)); assertEquals(0, jedis.msetnx(bfoo, bbar, "bar2".getBytes(), "foo2".getBytes())); assertArrayEquals(binaryValue, jedis.get(bfoo)); assertArrayEquals(bfoo, jedis.get(bbar)); } @Test public void incr() { assertEquals(1, jedis.incr(bfoo)); assertEquals(2, jedis.incr(bfoo)); } @Test public void incrWrongValue() { jedis.set(bfoo, binaryValue); assertThrows(JedisDataException.class, () -> jedis.incr(bfoo)); } @Test public void incrBy() { assertEquals(2, jedis.incrBy(bfoo, 2)); assertEquals(4, jedis.incrBy(bfoo, 2)); } @Test public void incrByWrongValue() { jedis.set(bfoo, binaryValue); assertThrows(JedisDataException.class, () -> jedis.incrBy(bfoo, 2)); } @Test public void incrByFloat() { assertEquals(10.5, jedis.incrByFloat(bfoo, 10.5), 0.0); assertEquals(10.6, jedis.incrByFloat(bfoo, 0.1), 0.0); } @Test public void decr() { assertEquals(-1, jedis.decr(bfoo)); assertEquals(-2, jedis.decr(bfoo)); } @Test public void decrWrongValue() { jedis.set(bfoo, binaryValue); assertThrows(JedisDataException.class, () -> jedis.decr(bfoo)); } @Test public void decrBy() { assertEquals(-2, jedis.decrBy(bfoo, 2)); assertEquals(-4, jedis.decrBy(bfoo, 2)); } @Test public void decrByWrongValue() { jedis.set(bfoo, binaryValue); assertThrows(JedisDataException.class, () -> jedis.decrBy(bfoo, 2)); } @Test public void append() { byte[] first512 = new byte[512]; System.arraycopy(binaryValue, 0, first512, 0, 512); assertEquals(512, jedis.append(bfoo, first512)); assertArrayEquals(first512, jedis.get(bfoo)); byte[] rest = new byte[binaryValue.length - 512]; System.arraycopy(binaryValue, 512, rest, 0, binaryValue.length - 512); assertEquals(binaryValue.length, jedis.append(bfoo, rest)); assertArrayEquals(binaryValue, jedis.get(bfoo)); } @Test @ConditionalOnEnv(value = TestEnvUtil.ENV_REDIS_ENTERPRISE, enabled = false) public void substr() { jedis.set(bfoo, binaryValue); byte[] first512 = new byte[512]; System.arraycopy(binaryValue, 0, first512, 0, 512); byte[] rfirst512 = jedis.substr(bfoo, 0, 511); assertArrayEquals(first512, rfirst512); byte[] last512 = new byte[512]; System.arraycopy(binaryValue, binaryValue.length - 512, last512, 0, 512); assertArrayEquals(last512, jedis.substr(bfoo, -512, -1)); assertArrayEquals(binaryValue, jedis.substr(bfoo, 0, -1)); assertArrayEquals(last512, jedis.substr(bfoo, binaryValue.length - 512, 100000)); } @Test public void strlen() { jedis.set(bfoo, binaryValue); assertEquals(binaryValue.length, jedis.strlen(bfoo)); } @Test public void setGet() { assertEquals("OK", jedis.set(bfoo, bbar)); // GET old value assertArrayEquals(bbar, jedis.setGet(bfoo, binaryValue)); assertArrayEquals(binaryValue, jedis.get(bfoo)); // GET null value assertNull(jedis.setGet(bbar, bfoo)); } @Test public void setGetWithParams() { jedis.del(bfoo); // no previous, return null assertNull(jedis.setGet(bfoo, bbar, setParams().nx())); // key already exists, new value should not be set, previous value should be bbar assertArrayEquals(bbar, jedis.setGet(bfoo, binaryValue, setParams().nx())); assertArrayEquals(bbar, jedis.setGet(bfoo, binaryValue, setParams().xx())); } /** * Tests the executeCommand method with CommandArguments for proper cluster routing. This test * uses explicit key marking through CommandArguments.key() for cluster compatibility. */ @Test public void executeCommandTest() { // Test SET command with proper key marking Object obj = jedis .executeCommand(new CommandArguments(SET).key("x".getBytes()).add("1".getBytes())); String returnValue = SafeEncoder.encode((byte[]) obj); assertEquals("OK", returnValue); // Test GET command with proper key marking obj = jedis.executeCommand(new CommandArguments(GET).key("x".getBytes())); returnValue = SafeEncoder.encode((byte[]) obj); assertEquals("1", returnValue); // Test RPUSH commands with proper key marking jedis.executeCommand(new CommandArguments(RPUSH).key("foo".getBytes()).add("a".getBytes())); jedis.executeCommand(new CommandArguments(RPUSH).key("foo".getBytes()).add("b".getBytes())); jedis.executeCommand(new CommandArguments(RPUSH).key("foo".getBytes()).add("c".getBytes())); // Test LRANGE command with proper key marking obj = jedis.executeCommand( new CommandArguments(LRANGE).key("foo".getBytes()).add("0".getBytes()).add("2".getBytes())); List list = (List) obj; List expected = new ArrayList<>(3); expected.add("a".getBytes()); expected.add("b".getBytes()); expected.add("c".getBytes()); for (int i = 0; i < 3; i++) assertArrayEquals(expected.get(i), list.get(i)); } /** * Tests the executeCommand method with blocking CommandArguments for proper cluster routing. This * test uses explicit key marking through CommandArguments.key() and .blocking() for cluster * compatibility with blocking operations. */ @Test public void executeBlockingCommandTest() { // Test BLPOP on empty list - should return null after timeout assertNull(jedis.executeCommand( new CommandArguments(BLPOP).key(bfoo).add(Protocol.toByteArray(1L)).blocking())); // Setup: push an element to the list using executeCommand with proper key marking jedis.executeCommand(new CommandArguments(RPUSH).key(bfoo).add(bbar)); // Test BLPOP with data - should return the key and value List blpop = (List) jedis.executeCommand( new CommandArguments(BLPOP).key(bfoo).add(Protocol.toByteArray(1L)).blocking()); assertEquals(2, blpop.size()); assertArrayEquals(bfoo, blpop.get(0)); assertArrayEquals(bbar, blpop.get(1)); // Test BLPOP on now-empty list - should return null after timeout assertNull(jedis.executeCommand( new CommandArguments(BLPOP).key(bfoo).add(Protocol.toByteArray(1L)).blocking())); } // MSETEX NX + expiration matrix (binary) static Stream msetexNxArgsProvider() { return Stream.of(Arguments.of("EX", new MSetExParams().nx().ex(5)), Arguments.of("PX", new MSetExParams().nx().px(5000)), Arguments.of("EXAT", new MSetExParams().nx().exAt(System.currentTimeMillis() / 1000 + 5)), Arguments.of("PXAT", new MSetExParams().nx().pxAt(System.currentTimeMillis() + 5000)), Arguments.of("KEEPTTL", new MSetExParams().nx().keepTtl())); } @ParameterizedTest(name = "MSETEX NX + {0} (binary)") @MethodSource("msetexNxArgsProvider") @EnabledOnCommand("MSETEX") public void msetexNx_binary_parametrized(String optionLabel, MSetExParams params) { byte[] k1 = "{t}msetex:unifiedb:k1".getBytes(); byte[] k2 = "{t}msetex:unifiedb:k2".getBytes(); boolean result = jedis.msetex(params, k1, "v1".getBytes(), k2, "v2".getBytes()); assertTrue(result); long ttl = jedis.ttl(k1); if ("KEEPTTL".equals(optionLabel)) { assertEquals(-1L, ttl); } else { assertTrue(ttl > 0L); } } } ================================================ FILE: src/test/java/redis/clients/jedis/commands/unified/BitCommandsTestBase.java ================================================ package redis.clients.jedis.commands.unified; import java.util.List; import io.redis.test.annotations.ConditionalOnEnv; import io.redis.test.annotations.SinceRedisVersion; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Tag; import redis.clients.jedis.Protocol; import redis.clients.jedis.RedisProtocol; import redis.clients.jedis.args.BitCountOption; import redis.clients.jedis.args.BitOP; import redis.clients.jedis.exceptions.JedisDataException; import redis.clients.jedis.params.BitPosParams; import redis.clients.jedis.util.SafeEncoder; import redis.clients.jedis.util.TestEnvUtil; import static org.junit.jupiter.api.Assertions.assertArrayEquals; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertThrows; import static org.junit.jupiter.api.Assertions.assertTrue; import static org.junit.jupiter.api.Assertions.fail; @Tag("integration") public abstract class BitCommandsTestBase extends UnifiedJedisCommandsTestBase { public BitCommandsTestBase(RedisProtocol protocol) { super(protocol); } @Test public void setAndgetbit() { assertFalse(jedis.setbit("foo", 0, true)); assertTrue(jedis.getbit("foo", 0)); // Binary assertFalse(jedis.setbit("bfoo".getBytes(), 0, true)); assertTrue(jedis.getbit("bfoo".getBytes(), 0)); } @Test public void bitpos() { String foo = "foo"; jedis.set(foo, String.valueOf(0)); // string "0" with bits: 0011 0000 jedis.setbit(foo, 3, true); jedis.setbit(foo, 7, true); jedis.setbit(foo, 13, true); jedis.setbit(foo, 39, true); /* * bit: 00110001 / 00000100 / 00000000 / 00000000 / 00000001 * byte: 0 1 2 3 4 */ long offset = jedis.bitpos(foo, true); assertEquals(2, offset); offset = jedis.bitpos(foo, false); assertEquals(0, offset); offset = jedis.bitpos(foo, true, new BitPosParams(1)); assertEquals(13, offset); offset = jedis.bitpos(foo, false, new BitPosParams(1)); assertEquals(8, offset); offset = jedis.bitpos(foo, true, new BitPosParams(2, 3)); assertEquals(-1, offset); offset = jedis.bitpos(foo, false, new BitPosParams(2, 3)); assertEquals(16, offset); offset = jedis.bitpos(foo, true, new BitPosParams(3, 4)); assertEquals(39, offset); } @Test public void bitposBinary() { // binary byte[] bfoo = { 0x01, 0x02, 0x03, 0x04 }; jedis.set(bfoo, Protocol.toByteArray(0)); // bits: 0011 0000 jedis.setbit(bfoo, 3, true); jedis.setbit(bfoo, 7, true); jedis.setbit(bfoo, 13, true); jedis.setbit(bfoo, 39, true); /* * bit: 00110001 / 00000100 / 00000000 / 00000000 / 00000001 * byte: 0 1 2 3 4 */ long offset = jedis.bitpos(bfoo, true); assertEquals(2, offset); offset = jedis.bitpos(bfoo, false); assertEquals(0, offset); offset = jedis.bitpos(bfoo, true, new BitPosParams(1)); assertEquals(13, offset); offset = jedis.bitpos(bfoo, false, new BitPosParams(1)); assertEquals(8, offset); offset = jedis.bitpos(bfoo, true, new BitPosParams(2, 3)); assertEquals(-1, offset); offset = jedis.bitpos(bfoo, false, new BitPosParams(2, 3)); assertEquals(16, offset); offset = jedis.bitpos(bfoo, true, new BitPosParams(3, 4)); assertEquals(39, offset); } @Test public void bitposWithNoMatchingBitExist() { String foo = "foo"; jedis.set(foo, String.valueOf(0)); for (int idx = 0; idx < 8; idx++) { jedis.setbit(foo, idx, true); } /* * bit: 11111111 * byte: 0 */ long offset = jedis.bitpos(foo, false); // offset should be last index + 1 assertEquals(8, offset); } @Test public void bitposWithNoMatchingBitExistWithinRange() { String foo = "foo"; jedis.set(foo, String.valueOf(0)); for (int idx = 0; idx < 8 * 5; idx++) { jedis.setbit(foo, idx, true); } /* * bit: 11111111 / 11111111 / 11111111 / 11111111 / 11111111 * byte: 0 1 2 3 4 */ long offset = jedis.bitpos(foo, false, new BitPosParams(2, 3)); // offset should be -1 assertEquals(-1, offset); } @Test @SinceRedisVersion(value="7.0.0", message="Starting with Redis version 7.0.0: Added the BYTE|BIT option.") public void bitposModifier() { jedis.set("mykey", "\\x00\\xff\\xf0"); assertEquals(0, jedis.bitpos("mykey", false)); assertEquals(1, jedis.bitpos("mykey", true)); assertEquals(1, jedis.bitpos("mykey", true, BitPosParams.bitPosParams())); assertEquals(18, jedis.bitpos("mykey", true, BitPosParams.bitPosParams().start(2))); assertEquals(18, jedis.bitpos("mykey", true, BitPosParams.bitPosParams().start(2).end(-1))); assertEquals(18, jedis.bitpos("mykey", true, BitPosParams.bitPosParams().start(2).end(-1) .modifier(BitCountOption.BYTE))); assertEquals(9, jedis.bitpos("mykey", true, BitPosParams.bitPosParams().start(7).end(15) .modifier(BitCountOption.BIT))); } @Test public void setAndgetrange() { jedis.set("key1", "Hello World"); assertEquals(11, jedis.setrange("key1", 6, "Jedis")); assertEquals("Hello Jedis", jedis.get("key1")); assertEquals("Hello", jedis.getrange("key1", 0, 4)); assertEquals("Jedis", jedis.getrange("key1", 6, 11)); } @Test @SinceRedisVersion(value="7.0.0", message="Starting with Redis version 7.0.0: Added the BYTE|BIT option.") public void bitCount() { jedis.setbit("foo", 16, true); jedis.setbit("foo", 24, true); jedis.setbit("foo", 40, true); jedis.setbit("foo", 56, true); assertEquals(4, (long) jedis.bitcount("foo")); assertEquals(4, (long) jedis.bitcount("foo".getBytes())); assertEquals(3, (long) jedis.bitcount("foo", 2L, 5L)); assertEquals(3, (long) jedis.bitcount("foo".getBytes(), 2L, 5L)); assertEquals(3, (long) jedis.bitcount("foo", 2L, 5L, BitCountOption.BYTE)); assertEquals(3, (long) jedis.bitcount("foo".getBytes(), 2L, 5L, BitCountOption.BYTE)); assertEquals(0, (long) jedis.bitcount("foo", 2L, 5L, BitCountOption.BIT)); assertEquals(0, (long) jedis.bitcount("foo".getBytes(), 2L, 5L, BitCountOption.BIT)); } @Test @ConditionalOnEnv(value = TestEnvUtil.ENV_REDIS_ENTERPRISE, enabled = false) public void bitOp() { jedis.set("key1", "\u0060"); jedis.set("key2", "\u0044"); jedis.bitop(BitOP.AND, "resultAnd", "key1", "key2"); String resultAnd = jedis.get("resultAnd"); assertEquals("\u0040", resultAnd); jedis.bitop(BitOP.OR, "resultOr", "key1", "key2"); String resultOr = jedis.get("resultOr"); assertEquals("\u0064", resultOr); jedis.bitop(BitOP.XOR, "resultXor", "key1", "key2"); String resultXor = jedis.get("resultXor"); assertEquals("\u0024", resultXor); } @Test @ConditionalOnEnv(value = TestEnvUtil.ENV_REDIS_ENTERPRISE, enabled = false) public void bitOpNot() { jedis.setbit("key", 0, true); jedis.setbit("key", 4, true); jedis.bitop(BitOP.NOT, "resultNot", "key"); String resultNot = jedis.get("resultNot"); assertEquals("\u0077", resultNot); } @Test @ConditionalOnEnv(value = TestEnvUtil.ENV_REDIS_ENTERPRISE, enabled = false) public void bitOpBinary() { byte[] dest = {0x0}; byte[] key1 = {0x1}; byte[] key2 = {0x2}; jedis.set(key1, new byte[]{0x6}); jedis.set(key2, new byte[]{0x3}); jedis.bitop(BitOP.AND, dest, key1, key2); assertArrayEquals(new byte[]{0x2}, jedis.get(dest)); jedis.bitop(BitOP.OR, dest, key1, key2); assertArrayEquals(new byte[]{0x7}, jedis.get(dest)); jedis.bitop(BitOP.XOR, dest, key1, key2); assertArrayEquals(new byte[]{0x5}, jedis.get(dest)); jedis.setbit(key1, 0, true); jedis.bitop(BitOP.NOT, dest, key1); assertArrayEquals(new byte[]{0x79}, jedis.get(dest)); } @Test public void bitOpNotMultiSourceShouldFail() { assertThrows(JedisDataException.class, () -> jedis.bitop(BitOP.NOT, "dest", "src1", "src2")); } @Test public void testBitfield() { List responses = jedis.bitfield("mykey", "INCRBY", "i5", "100", "1", "GET", "u4", "0"); assertEquals(1L, responses.get(0).longValue()); assertEquals(0L, responses.get(1).longValue()); } @Test public void testBitfieldReadonly() { List responses = jedis.bitfield("mykey", "INCRBY", "i5", "100", "1", "GET", "u4", "0"); assertEquals(1L, responses.get(0).longValue()); assertEquals(0L, responses.get(1).longValue()); List responses2 = jedis.bitfieldReadonly("mykey", "GET", "i5", "100"); assertEquals(1L, responses2.get(0).longValue()); try { jedis.bitfieldReadonly("mykey", "INCRBY", "i5", "100", "1", "GET", "u4", "0"); fail("Readonly command shouldn't allow INCRBY"); } catch (JedisDataException e) { } } @Test public void testBinaryBitfield() { List responses = jedis.bitfield(SafeEncoder.encode("mykey"), SafeEncoder.encode("INCRBY"), SafeEncoder.encode("i5"), SafeEncoder.encode("100"), SafeEncoder.encode("1"), SafeEncoder.encode("GET"), SafeEncoder.encode("u4"), SafeEncoder.encode("0")); assertEquals(1L, responses.get(0).longValue()); assertEquals(0L, responses.get(1).longValue()); } @Test public void testBinaryBitfieldReadonly() { List responses = jedis.bitfield("mykey", "INCRBY", "i5", "100", "1", "GET", "u4", "0"); assertEquals(1L, responses.get(0).longValue()); assertEquals(0L, responses.get(1).longValue()); List responses2 = jedis.bitfieldReadonly(SafeEncoder.encode("mykey"), SafeEncoder.encode("GET"), SafeEncoder.encode("i5"), SafeEncoder.encode("100")); assertEquals(1L, responses2.get(0).longValue()); } @Test @SinceRedisVersion("8.1.240") public void bitOpDiff() { // Use single-byte values for simplicity byte[] key1 = new byte[] { (byte) 0x07 }; // 00000111 - bits 0,1,2 set byte[] key2 = new byte[] { (byte) 0x02 }; // 00000010 - bit 1 set byte[] key3 = new byte[] { (byte) 0x04 }; // 00000100 - bit 2 set String destKey = "{bitOp:key:}resultDiff"; // Set keys using byte arrays jedis.set("{bitOp:key:}1".getBytes(), key1); jedis.set("{bitOp:key:}2".getBytes(), key2); jedis.set("{bitOp:key:}3".getBytes(), key3); // DIFF(key1, key2, key3) = key1 AND NOT(key2 OR key3) // key1 = 00000111 (bits 0,1,2 set) // key2 = 00000010 (bit 1 set) // key3 = 00000100 (bit 2 set) // key2 OR key3 = 00000110 (bits 1,2 set) // NOT(key2 OR key3) = 11111001 (all bits except 1,2 set) // key1 AND NOT(key2 OR key3) = 00000001 (only bit 0 set) jedis.bitop(BitOP.DIFF, destKey, "{bitOp:key:}1", "{bitOp:key:}2", "{bitOp:key:}3"); // Get result as bytes byte[] resultBytes = jedis.get(destKey).getBytes(); // Expected result: 00000001 (only bit 0 set) byte[] expectedBytes = new byte[] { (byte) 0x01 }; // Verify the result assertArrayEquals(expectedBytes, resultBytes); } @Test @SinceRedisVersion("8.1.240") public void bitOpDiff1() { // Use single-byte values for simplicity byte[] key1 = new byte[] { (byte) 0x07 }; // 00000111 - bits 0,1,2 set byte[] key2 = new byte[] { (byte) 0x02 }; // 00000010 - bit 1 set byte[] key3 = new byte[] { (byte) 0x04 }; // 00000100 - bit 2 set String destKey = "{bitOp:key:}resultDiff1"; // Set keys using byte arrays jedis.set("{bitOp:key:}1".getBytes(), key1); jedis.set("{bitOp:key:}2".getBytes(), key2); jedis.set("{bitOp:key:}3".getBytes(), key3); // DIFF1(key1, key2, key3) = NOT(key1) AND (key2 OR key3) // key1 = 00000111 (bits 0,1,2 set) // key2 = 00000010 (bit 1 set) // key3 = 00000100 (bit 2 set) // key2 OR key3 = 00000110 (bits 1,2 set) // NOT(key1) = 11111000 (all bits except 0,1,2 set) // NOT(key1) AND (key2 OR key3) = 00000000 (no bits set) jedis.bitop(BitOP.DIFF1, destKey, "{bitOp:key:}1", "{bitOp:key:}2", "{bitOp:key:}3"); // Get result as bytes byte[] resultBytes = jedis.get(destKey).getBytes(); // Expected result: 00000000 (no bits set) byte[] expectedBytes = new byte[] { (byte) 0x00 }; // Verify the result assertArrayEquals(expectedBytes, resultBytes); } @Test @SinceRedisVersion("8.1.240") public void bitOpAndor() { // Use single-byte values for simplicity byte[] key1 = new byte[] { (byte) 0x07 }; // 00000111 - bits 0,1,2 set byte[] key2 = new byte[] { (byte) 0x02 }; // 00000010 - bit 1 set byte[] key3 = new byte[] { (byte) 0x04 }; // 00000100 - bit 2 set String destKey = "{bitOp:key:}resultAndor"; // Set keys using byte arrays jedis.set("{bitOp:key:}1".getBytes(), key1); jedis.set("{bitOp:key:}2".getBytes(), key2); jedis.set("{bitOp:key:}3".getBytes(), key3); // ANDOR(key1, key2, key3) = key1 AND (key2 OR key3) // key1 = 00000111 (bits 0,1,2 set) // key2 = 00000010 (bit 1 set) // key3 = 00000100 (bit 2 set) // key2 OR key3 = 00000110 (bits 1,2 set) // key1 AND (key2 OR key3) = 00000110 (bits 1,2 set) jedis.bitop(BitOP.ANDOR, destKey, "{bitOp:key:}1", "{bitOp:key:}2", "{bitOp:key:}3"); // Get result as bytes byte[] resultBytes = jedis.get(destKey).getBytes(); // Expected result: 00000110 (bits 1,2 set) byte[] expectedBytes = new byte[] { (byte) 0x06 }; // Verify the result assertArrayEquals(expectedBytes, resultBytes); } @Test @SinceRedisVersion("8.1.240") public void bitOpOne() { // Use single-byte values for simplicity byte[] key1 = new byte[] { (byte) 0x01 }; // 00000001 - bit 0 set byte[] key2 = new byte[] { (byte) 0x02 }; // 00000010 - bit 1 set byte[] key3 = new byte[] { (byte) 0x04 }; // 00000100 - bit 2 set String destKey = "{bitOp:key:}resultOne"; // Set keys using byte arrays jedis.set("{bitOp:key:}1".getBytes(), key1); jedis.set("{bitOp:key:}2".getBytes(), key2); jedis.set("{bitOp:key:}3".getBytes(), key3); // ONE(key1, key2, key3) = bits set in exactly one of the inputs // key1 = 00000001 (bit 0 set) // key2 = 00000010 (bit 1 set) // key3 = 00000100 (bit 2 set) // Result = 00000111 (bits 0,1,2 set - each in exactly one input) jedis.bitop(BitOP.ONE, destKey, "{bitOp:key:}1", "{bitOp:key:}2", "{bitOp:key:}3"); // Get result as bytes byte[] resultBytes = jedis.get(destKey).getBytes(); // Expected result: 00000111 (bits 0,1,2 set) byte[] expectedBytes = new byte[] { (byte) 0x07 }; // Verify the result assertArrayEquals(expectedBytes, resultBytes); } @Test @SinceRedisVersion("8.1.240") public void bitOpDiffBinary() { byte[] key1 = { (byte) 0x07 }; // 00000111 - bits 0,1,2 set byte[] key2 = { (byte) 0x02 }; // 00000010 - bit 1 set byte[] key3 = { (byte) 0x04 }; // 00000100 - bit 2 set byte[] dest = "{bitOp:key:}resultDiffBinary".getBytes(); jedis.set("{bitOp:key:}1".getBytes(), key1); jedis.set("{bitOp:key:}2".getBytes(), key2); jedis.set("{bitOp:key:}3".getBytes(), key3); // DIFF(key1, key2, key3) = key1 AND NOT(key2 OR key3) // key1 = 00000111 (bits 0,1,2 set) // key2 = 00000010 (bit 1 set) // key3 = 00000100 (bit 2 set) // key2 OR key3 = 00000110 (bits 1,2 set) // NOT(key2 OR key3) = 11111001 (all bits except 1,2 set) // key1 AND NOT(key2 OR key3) = 00000001 (only bit 0 set) jedis.bitop(BitOP.DIFF, dest, "{bitOp:key:}1".getBytes(), "{bitOp:key:}2".getBytes(), "{bitOp:key:}3".getBytes()); // Expected result: 00000001 (only bit 0 set) byte[] expectedBytes = new byte[] { (byte) 0x01 }; // Verify the result assertArrayEquals(expectedBytes, jedis.get(dest)); } @Test @SinceRedisVersion("8.1.240") public void bitOpDiff1Binary() { byte[] key1 = { (byte) 0x07 }; // 00000111 - bits 0,1,2 set byte[] key2 = { (byte) 0x02 }; // 00000010 - bit 1 set byte[] key3 = { (byte) 0x04 }; // 00000100 - bit 2 set byte[] dest = "{bitOp:key:}resultDiff1Binary".getBytes(); jedis.set("{bitOp:key:}1".getBytes(), key1); jedis.set("{bitOp:key:}2".getBytes(), key2); jedis.set("{bitOp:key:}3".getBytes(), key3); // DIFF1(key1, key2, key3) = NOT(key1) AND (key2 OR key3) // key1 = 00000111 (bits 0,1,2 set) // key2 = 00000010 (bit 1 set) // key3 = 00000100 (bit 2 set) // key2 OR key3 = 00000110 (bits 1,2 set) // NOT(key1) = 11111000 (all bits except 0,1,2 set) // NOT(key1) AND (key2 OR key3) = 00000000 (no bits set) jedis.bitop(BitOP.DIFF1, dest, "{bitOp:key:}1".getBytes(), "{bitOp:key:}2".getBytes(), "{bitOp:key:}3".getBytes()); // Expected result: 00000000 (no bits set) byte[] expectedBytes = new byte[] { (byte) 0x00 }; // Verify the result assertArrayEquals(expectedBytes, jedis.get(dest)); } @Test @SinceRedisVersion("8.1.240") public void bitOpAndorBinary() { byte[] key1 = { (byte) 0x07 }; // 00000111 - bits 0,1,2 set byte[] key2 = { (byte) 0x02 }; // 00000010 - bit 1 set byte[] key3 = { (byte) 0x04 }; // 00000100 - bit 2 set byte[] dest = "{bitOp:key:}resultAndorBinary".getBytes(); jedis.set("{bitOp:key:}1".getBytes(), key1); jedis.set("{bitOp:key:}2".getBytes(), key2); jedis.set("{bitOp:key:}3".getBytes(), key3); // ANDOR(key1, key2, key3) = key1 AND (key2 OR key3) // key1 = 00000111 (bits 0,1,2 set) // key2 = 00000010 (bit 1 set) // key3 = 00000100 (bit 2 set) // key2 OR key3 = 00000110 (bits 1,2 set) // key1 AND (key2 OR key3) = 00000110 (bits 1,2 set) jedis.bitop(BitOP.ANDOR, dest, "{bitOp:key:}1".getBytes(), "{bitOp:key:}2".getBytes(), "{bitOp:key:}3".getBytes()); // Expected result: 00000110 (bits 1,2 set) byte[] expectedBytes = new byte[] { (byte) 0x06 }; // Verify the result assertArrayEquals(expectedBytes, jedis.get(dest)); } @Test @SinceRedisVersion("8.1.240") public void bitOpOneBinary() { byte[] key1 = { (byte) 0x01 }; // 00000001 - bit 0 set byte[] key2 = { (byte) 0x02 }; // 00000010 - bit 1 set byte[] key3 = { (byte) 0x04 }; // 00000100 - bit 2 set byte[] dest = "{bitOp:key:}resultOneBinary".getBytes(); jedis.set("{bitOp:key:}1".getBytes(), key1); jedis.set("{bitOp:key:}2".getBytes(), key2); jedis.set("{bitOp:key:}3".getBytes(), key3); // ONE(key1, key2, key3) = bits set in exactly one of the inputs // key1 = 00000001 (bit 0 set) // key2 = 00000010 (bit 1 set) // key3 = 00000100 (bit 2 set) // Result = 00000111 (bits 0,1,2 set - each in exactly one input) jedis.bitop(BitOP.ONE, dest, "{bitOp:key:}1".getBytes(), "{bitOp:key:}2".getBytes(), "{bitOp:key:}3".getBytes()); // Expected result: 00000111 (bits 0,1,2 set) byte[] expectedBytes = new byte[] { (byte) 0x07 }; // Verify the result assertArrayEquals(expectedBytes, jedis.get(dest)); } @Test @SinceRedisVersion("8.1.240") public void bitOpDiffSingleSourceShouldFail() { assertThrows(JedisDataException.class, () -> jedis.bitop(BitOP.DIFF, "{bitOp:key:}dest", "{bitOp:key:}{bitOp:key:}src1")); } @Test @SinceRedisVersion("8.1.240") public void bitOpDiff1SingleSourceShouldFail() { assertThrows(JedisDataException.class, () -> jedis.bitop(BitOP.DIFF1, "{bitOp:key:}dest", "{bitOp:key:}src1")); } @Test @SinceRedisVersion("8.1.240") public void bitOpAndorSingleSourceShouldFail() { assertThrows(JedisDataException.class, () -> jedis.bitop(BitOP.ANDOR, "{bitOp:key:}dest", "{bitOp:key:}src1")); } @Test @SinceRedisVersion("8.1.240") public void bitOpDiffBinarySingleSourceShouldFail() { byte[] dest = "{bitOp:key:}dest".getBytes(); byte[] src1 = "{bitOp:key:}src1".getBytes(); assertThrows(JedisDataException.class, () -> jedis.bitop(BitOP.DIFF, dest, src1)); } @Test @SinceRedisVersion("8.1.240") public void bitOpDiff1BinarySingleSourceShouldFail() { byte[] dest = "{bitOp:key:}dest".getBytes(); byte[] src1 = "{bitOp:key:}src1".getBytes(); assertThrows(JedisDataException.class, () -> jedis.bitop(BitOP.DIFF1, dest, src1)); } @Test @SinceRedisVersion("8.1.240") public void bitOpAndorBinarySingleSourceShouldFail() { byte[] dest = "{bitOp:key:}dest".getBytes(); byte[] src1 = "{bitOp:key:}src1".getBytes(); assertThrows(JedisDataException.class, () -> jedis.bitop(BitOP.ANDOR, dest, src1)); } } ================================================ FILE: src/test/java/redis/clients/jedis/commands/unified/ExtendedVectorSetCommandsTestBase.java ================================================ package redis.clients.jedis.commands.unified; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertNotNull; import static org.junit.jupiter.api.Assertions.assertNull; import static org.junit.jupiter.api.Assertions.assertTrue; import java.util.Arrays; import java.util.List; import java.util.Map; import io.redis.test.annotations.SinceRedisVersion; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Tag; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.TestInfo; import redis.clients.jedis.RedisProtocol; import redis.clients.jedis.params.VAddParams; import redis.clients.jedis.params.VSimParams; /** * Integration tests for Redis Vector Sets based on the examples from the Redis documentation. * @see Redis Vector Sets * Documentation */ @Tag("integration") @Tag("vector-set") public abstract class ExtendedVectorSetCommandsTestBase extends UnifiedJedisCommandsTestBase { private String POINTS_KEY; public ExtendedVectorSetCommandsTestBase(RedisProtocol protocol) { super(protocol); } @BeforeEach public void setUp(TestInfo testInfo) { POINTS_KEY = testInfo.getDisplayName() + ":points"; jedis.flushAll(); // Add the example points from the Redis documentation // A: (1.0, 1.0), B: (-1.0, -1.0), C: (-1.0, 1.0), D: (1.0, -1.0), and E: (1.0, 0) jedis.vadd(POINTS_KEY, new float[] { 1.0f, 1.0f }, "pt:A"); jedis.vadd(POINTS_KEY, new float[] { -1.0f, -1.0f }, "pt:B"); jedis.vadd(POINTS_KEY, new float[] { -1.0f, 1.0f }, "pt:C"); jedis.vadd(POINTS_KEY, new float[] { 1.0f, -1.0f }, "pt:D"); jedis.vadd(POINTS_KEY, new float[] { 1.0f, 0.0f }, "pt:E"); } /** * Test basic vector set operations as shown in the Redis documentation. */ @Test @SinceRedisVersion("8.0.0") public void testBasicOperations() { jedis.type(POINTS_KEY); assertEquals("vectorset", jedis.type(POINTS_KEY)); // Check the cardinality of the vector set long count = jedis.vcard(POINTS_KEY); assertEquals(5L, count); // Check the dimensionality of the vectors long dim = jedis.vdim(POINTS_KEY); assertEquals(2L, dim); // Retrieve the vectors for each point List vectorA = jedis.vemb(POINTS_KEY, "pt:A"); assertEquals(2, vectorA.size()); assertEquals(1.0, vectorA.get(0), 0.001); assertEquals(1.0, vectorA.get(1), 0.001); List vectorB = jedis.vemb(POINTS_KEY, "pt:B"); assertEquals(2, vectorB.size()); assertEquals(-1.0, vectorB.get(0), 0.001); assertEquals(-1.0, vectorB.get(1), 0.001); List vectorC = jedis.vemb(POINTS_KEY, "pt:C"); assertEquals(2, vectorC.size()); assertEquals(-1.0, vectorC.get(0), 0.001); assertEquals(1.0, vectorC.get(1), 0.001); List vectorD = jedis.vemb(POINTS_KEY, "pt:D"); assertEquals(2, vectorD.size()); assertEquals(1.0, vectorD.get(0), 0.001); assertEquals(-1.0, vectorD.get(1), 0.001); List vectorE = jedis.vemb(POINTS_KEY, "pt:E"); assertEquals(2, vectorE.size()); assertEquals(1.0, vectorE.get(0), 0.001); assertEquals(0.0, vectorE.get(1), 0.001); } /** * Test adding and removing elements from a vector set. */ @Test @SinceRedisVersion("8.0.0") public void testAddAndRemoveElements() { // Add a new point F at (0, 0) boolean result = jedis.vadd(POINTS_KEY, new float[] { 0.0f, 0.0f }, "pt:F"); assertTrue(result); // Check the updated cardinality long count = jedis.vcard(POINTS_KEY); assertEquals(6L, count); // Remove point F result = jedis.vrem(POINTS_KEY, "pt:F"); assertTrue(result); // Check the cardinality after removal count = jedis.vcard(POINTS_KEY); assertEquals(5L, count); } /** * Test vector similarity search as shown in the Redis documentation. */ @Test @SinceRedisVersion("8.0.0") public void testVectorSimilaritySearch() { // Search for vectors similar to (0.9, 0.1) List similar = jedis.vsim(POINTS_KEY, new float[] { 0.9f, 0.1f }); assertNotNull(similar); assertFalse(similar.isEmpty()); // The expected order based on similarity to (0.9, 0.1) should be: // E (1.0, 0.0), A (1.0, 1.0), D (1.0, -1.0), C (-1.0, 1.0), B (-1.0, -1.0) assertEquals("pt:E", similar.get(0)); assertEquals("pt:A", similar.get(1)); assertEquals("pt:D", similar.get(2)); assertEquals("pt:C", similar.get(3)); assertEquals("pt:B", similar.get(4)); // Search for vectors similar to point A with scores VSimParams params = new VSimParams(); Map similarWithScores = jedis.vsimByElementWithScores(POINTS_KEY, "pt:A", params); assertNotNull(similarWithScores); assertFalse(similarWithScores.isEmpty()); // Point A should have a perfect similarity score of 1.0 with itself assertEquals(1.0, similarWithScores.get("pt:A"), 0.001); // Limit the number of results to 4 params = new VSimParams().count(4); similar = jedis.vsimByElement(POINTS_KEY, "pt:A", params); assertEquals(4, similar.size()); } /** * Test random sampling from a vector set. */ @Test @SinceRedisVersion("8.0.0") public void testRandomSampling() { // Get a single random element String randomElement = jedis.vrandmember(POINTS_KEY); assertNotNull(randomElement); assertTrue(randomElement.startsWith("pt:")); // Get multiple random elements List randomElements = jedis.vrandmember(POINTS_KEY, 3); assertEquals(3, randomElements.size()); for (String element : randomElements) { assertTrue(element.startsWith("pt:")); } } /** * Test HNSW graph links. */ @Test @SinceRedisVersion("8.0.0") public void testHnswGraphLinks() { // Get links for point A List> links = jedis.vlinks(POINTS_KEY, "pt:A"); assertNotNull(links); // Get links with scores List> linksWithScores = jedis.vlinksWithScores(POINTS_KEY, "pt:A"); assertNotNull(linksWithScores); } @Test @SinceRedisVersion("8.0.0") public void testAttributeOperations() { // Set attributes for point A String attributes = "{\"name\":\"Point A\",\"description\":\"First point added\"}"; boolean result = jedis.vsetattr(POINTS_KEY, "pt:A", attributes); assertTrue(result); // Get attributes for point A String retrievedAttributes = jedis.vgetattr(POINTS_KEY, "pt:A"); assertTrue(retrievedAttributes.contains("\"name\":\"Point A\"")); assertTrue(retrievedAttributes.contains("\"description\":\"First point added\"")); // Delete attributes by setting an empty string result = jedis.vsetattr(POINTS_KEY, "pt:A", ""); assertTrue(result); // Verify attributes are deleted retrievedAttributes = jedis.vgetattr(POINTS_KEY, "pt:A"); assertNull(retrievedAttributes); } @Test @SinceRedisVersion("8.0.0") public void testFilteredVectorSimilaritySearch() { // Set attributes for all points jedis.vsetattr(POINTS_KEY, "pt:A", "{\"size\":\"large\",\"price\":18.99}"); jedis.vsetattr(POINTS_KEY, "pt:B", "{\"size\":\"large\",\"price\":35.99}"); jedis.vsetattr(POINTS_KEY, "pt:C", "{\"size\":\"large\",\"price\":25.99}"); jedis.vsetattr(POINTS_KEY, "pt:D", "{\"size\":\"small\",\"price\":21.00}"); jedis.vsetattr(POINTS_KEY, "pt:E", "{\"size\":\"small\",\"price\":17.75}"); // Filter by size = "large" VSimParams params = new VSimParams().filter(".size == \"large\""); List similar = jedis.vsimByElement(POINTS_KEY, "pt:A", params); assertEquals(3, similar.size()); assertEquals(Arrays.asList("pt:A", "pt:C", "pt:B"), similar); // Filter by size = "large" AND price > 20.00 params = new VSimParams().filter(".size == \"large\" && .price > 20.00"); similar = jedis.vsimByElement(POINTS_KEY, "pt:A", params); assertEquals(2, similar.size()); assertEquals(Arrays.asList("pt:C", "pt:B"), similar); } @Test @SinceRedisVersion("8.0.0") public void testQuantizationTypes() { // Test Q8 quantization VAddParams q8Params = new VAddParams().q8(); jedis.vadd("quantSetQ8", new float[] { 1.262185f, 1.958231f }, "quantElement", q8Params); List q8Vector = jedis.vemb("quantSetQ8", "quantElement"); assertEquals(2, q8Vector.size()); // Test NOQUANT (no quantization) VAddParams noQuantParams = new VAddParams().noQuant(); jedis.vadd("quantSetNoQ", new float[] { 1.262185f, 1.958231f }, "quantElement", noQuantParams); List noQuantVector = jedis.vemb("quantSetNoQ", "quantElement"); assertEquals(2, noQuantVector.size()); assertEquals(1.262185, noQuantVector.get(0), 0.0001); assertEquals(1.958231, noQuantVector.get(1), 0.0001); // Test BIN (binary) quantization VAddParams binParams = new VAddParams().bin(); jedis.vadd("quantSetBin", new float[] { 1.262185f, 1.958231f }, "quantElement", binParams); List binVector = jedis.vemb("quantSetBin", "quantElement"); assertEquals(2, binVector.size()); // Binary quantization will convert values to either 1 or -1 assertTrue(binVector.get(0) == 1.0 || binVector.get(0) == -1.0); assertTrue(binVector.get(1) == 1.0 || binVector.get(1) == -1.0); } } ================================================ FILE: src/test/java/redis/clients/jedis/commands/unified/FunctionCommandsTestBase.java ================================================ package redis.clients.jedis.commands.unified; import io.redis.test.annotations.SinceRedisVersion; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Tag; import org.junit.jupiter.api.TestInfo; import redis.clients.jedis.RedisProtocol; import redis.clients.jedis.args.FlushMode; import redis.clients.jedis.args.FunctionRestorePolicy; import redis.clients.jedis.exceptions.JedisException; import redis.clients.jedis.resps.FunctionStats; import redis.clients.jedis.resps.LibraryInfo; import java.util.ArrayList; import java.util.List; import java.util.Map; import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.containsString; import static org.hamcrest.Matchers.empty; import static org.hamcrest.Matchers.equalTo; import static org.hamcrest.Matchers.hasEntry; import static org.hamcrest.Matchers.hasKey; import static org.hamcrest.Matchers.hasSize; import static org.hamcrest.Matchers.notNullValue; import static org.hamcrest.Matchers.nullValue; import static org.junit.jupiter.api.Assertions.assertThrows; @Tag("integration") public abstract class FunctionCommandsTestBase extends UnifiedJedisCommandsTestBase { final String libraryName = "mylib"; final String TEST_LUA_SCRIPT_TMPL = "#!lua name=%s\n" + "redis.register_function('%s', function(keys, args) return %s end)"; private String functionName; public FunctionCommandsTestBase(RedisProtocol protocol) { super(protocol); } @BeforeEach public void setUp(TestInfo info) { functionName = info.getDisplayName().replaceAll("[^a-zA-Z0-9]", "_"); jedis.functionLoad(String.format(TEST_LUA_SCRIPT_TMPL, libraryName, functionName, "42")); } @AfterEach public void cleanUpFunctions() { if (jedis == null) { return; } try { jedis.functionDelete(libraryName); } catch (JedisException e) { // ignore } } @Test @SinceRedisVersion(value = "7.0.0") public void testFunctionDeletion() { List listResponse = jedis.functionList(); assertThat(listResponse, hasSize(1)); assertThat(listResponse.get(0).getLibraryName(), equalTo(libraryName)); assertThat(listResponse.get(0).getFunctions(), hasSize(1)); assertThat(listResponse.get(0).getFunctions().get(0), hasEntry("name", functionName)); String delete = jedis.functionDelete(libraryName); assertThat(delete, equalTo("OK")); listResponse = jedis.functionList(); assertThat(listResponse, empty()); } @Test @SinceRedisVersion(value = "7.0.0") public void testFunctionDeletionBinary() { List listResponse = jedis.functionList(); assertThat(listResponse, hasSize(1)); assertThat(listResponse.get(0).getLibraryName(), equalTo(libraryName)); assertThat(listResponse.get(0).getFunctions(), hasSize(1)); assertThat(listResponse.get(0).getFunctions().get(0), hasEntry("name", functionName)); String deleteBinary = jedis.functionDelete(libraryName.getBytes()); assertThat(deleteBinary, equalTo("OK")); listResponse = jedis.functionList(); assertThat(listResponse, empty()); } @Test @SinceRedisVersion(value = "7.0.0") public void testFunctionListing() { List list = jedis.functionList(); assertThat(list, hasSize(1)); assertThat(list.get(0).getLibraryName(), equalTo(libraryName)); assertThat(list.get(0).getFunctions(), hasSize(1)); assertThat(list.get(0).getFunctions().get(0), hasEntry("name", functionName)); assertThat(list.get(0).getLibraryCode(), nullValue()); List listBinary = jedis.functionListBinary(); assertThat(listBinary, hasSize(1)); List listLibrary = jedis.functionList(libraryName); assertThat(listLibrary, hasSize(1)); assertThat(listLibrary.get(0).getLibraryName(), equalTo(libraryName)); assertThat(listLibrary.get(0).getFunctions(), hasSize(1)); assertThat(listLibrary.get(0).getFunctions().get(0), hasEntry("name", functionName)); assertThat(listLibrary.get(0).getLibraryCode(), nullValue()); List listLibraryBinary = jedis.functionList(libraryName.getBytes()); assertThat(listLibraryBinary, hasSize(1)); List listWithCode = jedis.functionListWithCode(); assertThat(listWithCode, hasSize(1)); assertThat(listWithCode.get(0).getLibraryName(), equalTo(libraryName)); assertThat(listWithCode.get(0).getFunctions(), hasSize(1)); assertThat(listWithCode.get(0).getFunctions().get(0), hasEntry("name", functionName)); assertThat(listWithCode.get(0).getLibraryCode(), notNullValue()); List listWithCodeBinary = jedis.functionListWithCodeBinary(); assertThat(listWithCodeBinary, hasSize(1)); List listWithCodeLibrary = jedis.functionListWithCode(libraryName); assertThat(listWithCodeLibrary, hasSize(1)); assertThat(listWithCodeLibrary.get(0).getLibraryName(), equalTo(libraryName)); assertThat(listWithCodeLibrary.get(0).getFunctions(), hasSize(1)); assertThat(listWithCodeLibrary.get(0).getFunctions().get(0), hasEntry("name", functionName)); assertThat(listWithCodeLibrary.get(0).getLibraryCode(), notNullValue()); List listWithCodeLibraryBinary = jedis.functionListWithCode(libraryName.getBytes()); assertThat(listWithCodeLibraryBinary, hasSize(1)); } @Test @SinceRedisVersion(value = "7.0.0") public void testFunctionReload() { Object result = jedis.fcall(functionName.getBytes(), new ArrayList<>(), new ArrayList<>()); assertThat(result, equalTo(42L)); String luaScriptChanged = String.format(TEST_LUA_SCRIPT_TMPL, libraryName, functionName, "52"); String replaceResult = jedis.functionLoadReplace(luaScriptChanged); assertThat(replaceResult, equalTo("mylib")); Object resultAfter = jedis.fcall(functionName.getBytes(), new ArrayList<>(), new ArrayList<>()); assertThat(resultAfter, equalTo(52L)); } @Test @SinceRedisVersion(value = "7.0.0") public void testFunctionReloadBinary() { Object result = jedis.fcall(functionName.getBytes(), new ArrayList<>(), new ArrayList<>()); assertThat(result, equalTo(42L)); String luaScriptChanged = String.format(TEST_LUA_SCRIPT_TMPL, libraryName, functionName, "52"); String replaceResult = jedis.functionLoadReplace(luaScriptChanged.getBytes()); assertThat(replaceResult, equalTo("mylib")); Object resultAfter = jedis.fcall(functionName.getBytes(), new ArrayList<>(), new ArrayList<>()); assertThat(resultAfter, equalTo(52L)); } @Test @SinceRedisVersion(value = "7.0.0") public void testFunctionStats() { for (int i = 0; i < 5; i++) { Object result = jedis.fcall(functionName.getBytes(), new ArrayList<>(), new ArrayList<>()); assertThat(result, equalTo(42L)); } FunctionStats stats = jedis.functionStats(); assertThat(stats, notNullValue()); assertThat(stats.getEngines(), hasKey("LUA")); Map luaStats = stats.getEngines().get("LUA"); assertThat(luaStats, hasEntry("libraries_count", 1L)); assertThat(luaStats, hasEntry("functions_count", 1L)); Object statsBinary = jedis.functionStatsBinary(); assertThat(statsBinary, notNullValue()); } @Test @SinceRedisVersion(value = "7.0.0") public void testFunctionDumpFlushRestore() { List list = jedis.functionList(); assertThat(list, hasSize(1)); assertThat(list.get(0).getLibraryName(), equalTo(libraryName)); assertThat(list.get(0).getFunctions(), hasSize(1)); assertThat(list.get(0).getFunctions().get(0), hasEntry("name", functionName)); byte[] dump = jedis.functionDump(); assertThat(dump, notNullValue()); String flush = jedis.functionFlush(); assertThat(flush, equalTo("OK")); list = jedis.functionList(); assertThat(list, empty()); String restore = jedis.functionRestore(dump); assertThat(restore, equalTo("OK")); list = jedis.functionList(); assertThat(list, hasSize(1)); assertThat(list.get(0).getLibraryName(), equalTo(libraryName)); assertThat(list.get(0).getFunctions(), hasSize(1)); assertThat(list.get(0).getFunctions().get(0), hasEntry("name", functionName)); } @Test @SinceRedisVersion(value = "7.0.0") public void testFunctionDumpFlushRestoreWithPolicy() { List list = jedis.functionList(); assertThat(list, hasSize(1)); assertThat(list.get(0).getLibraryName(), equalTo(libraryName)); assertThat(list.get(0).getFunctions(), hasSize(1)); assertThat(list.get(0).getFunctions().get(0), hasEntry("name", functionName)); byte[] dump = jedis.functionDump(); assertThat(dump, notNullValue()); String flush = jedis.functionFlush(); assertThat(flush, equalTo("OK")); list = jedis.functionList(); assertThat(list, empty()); String restore = jedis.functionRestore(dump, FunctionRestorePolicy.REPLACE); assertThat(restore, equalTo("OK")); list = jedis.functionList(); assertThat(list, hasSize(1)); assertThat(list.get(0).getLibraryName(), equalTo(libraryName)); assertThat(list.get(0).getFunctions(), hasSize(1)); assertThat(list.get(0).getFunctions().get(0), hasEntry("name", functionName)); } @Test @SinceRedisVersion(value = "7.0.0") public void testFunctionFlushWithMode() { List list = jedis.functionList(); assertThat(list, hasSize(1)); assertThat(list.get(0).getLibraryName(), equalTo(libraryName)); assertThat(list.get(0).getFunctions(), hasSize(1)); assertThat(list.get(0).getFunctions().get(0), hasEntry("name", functionName)); String flush = jedis.functionFlush(FlushMode.SYNC); assertThat(flush, equalTo("OK")); list = jedis.functionList(); assertThat(list, empty()); } @Test @SinceRedisVersion(value = "7.0.0") public void testFunctionKill() { JedisException e = assertThrows(JedisException.class, () -> jedis.functionKill()); assertThat(e.getMessage(), containsString("No scripts in execution right now")); } } ================================================ FILE: src/test/java/redis/clients/jedis/commands/unified/GeoCommandsTestBase.java ================================================ package redis.clients.jedis.commands.unified; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; import io.redis.test.annotations.ConditionalOnEnv; import org.junit.jupiter.api.Test; import redis.clients.jedis.GeoCoordinate; import redis.clients.jedis.RedisProtocol; import redis.clients.jedis.args.GeoUnit; import redis.clients.jedis.params.GeoSearchParam; import redis.clients.jedis.resps.GeoRadiusResponse; import redis.clients.jedis.params.GeoAddParams; import redis.clients.jedis.params.GeoRadiusParam; import redis.clients.jedis.params.GeoRadiusStoreParam; import redis.clients.jedis.util.AssertUtil; import redis.clients.jedis.util.GeoCoordinateMatcher; import redis.clients.jedis.util.SafeEncoder; import redis.clients.jedis.util.TestEnvUtil; import static org.hamcrest.MatcherAssert.assertThat; import static org.junit.jupiter.api.Assertions.assertArrayEquals; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertNull; import static org.junit.jupiter.api.Assertions.assertTrue; import static org.junit.jupiter.api.Assertions.fail; public abstract class GeoCommandsTestBase extends UnifiedJedisCommandsTestBase { protected final byte[] bfoo = { 0x01, 0x02, 0x03, 0x04 }; protected final byte[] bA = { 0x0A }; protected final byte[] bB = { 0x0B }; protected final byte[] bC = { 0x0C }; protected final byte[] bD = { 0x0D }; protected final byte[] bNotexist = { 0x0F }; private static final double EPSILON = 1e-5; public GeoCommandsTestBase(RedisProtocol protocol) { super(protocol); } @Test public void geoadd() { assertEquals(1, jedis.geoadd("foo", 1, 2, "a")); assertEquals(0, jedis.geoadd("foo", 2, 3, "a")); Map coordinateMap = new HashMap<>(); coordinateMap.put("a", new GeoCoordinate(3, 4)); coordinateMap.put("b", new GeoCoordinate(2, 3)); coordinateMap.put("c", new GeoCoordinate(3.314, 2.3241)); assertEquals(2, jedis.geoadd("foo", coordinateMap)); // binary assertEquals(1, jedis.geoadd(bfoo, 1, 2, bA)); assertEquals(0, jedis.geoadd(bfoo, 2, 3, bA)); Map bcoordinateMap = new HashMap<>(); bcoordinateMap.put(bA, new GeoCoordinate(3, 4)); bcoordinateMap.put(bB, new GeoCoordinate(2, 3)); bcoordinateMap.put(bC, new GeoCoordinate(3.314, 2.3241)); assertEquals(2, jedis.geoadd(bfoo, bcoordinateMap)); } @Test public void geoaddWithParams() { assertEquals(1, jedis.geoadd("foo", 1, 2, "a")); Map coordinateMap = new HashMap<>(); coordinateMap.put("a", new GeoCoordinate(3, 4)); assertEquals(0, jedis.geoadd("foo", GeoAddParams.geoAddParams().nx(), coordinateMap)); assertEquals(1, jedis.geoadd("foo", GeoAddParams.geoAddParams().xx().ch(), coordinateMap)); coordinateMap.clear(); coordinateMap.put("b", new GeoCoordinate(6, 7)); // never add elements. assertEquals(0, jedis.geoadd("foo", GeoAddParams.geoAddParams().xx(), coordinateMap)); assertEquals(1, jedis.geoadd("foo", GeoAddParams.geoAddParams().nx(), coordinateMap)); // binary assertEquals(1, jedis.geoadd(bfoo, 1, 2, bA)); Map bcoordinateMap = new HashMap<>(); bcoordinateMap.put(bA, new GeoCoordinate(3, 4)); assertEquals(0, jedis.geoadd(bfoo, GeoAddParams.geoAddParams().nx(), bcoordinateMap)); assertEquals(1, jedis.geoadd(bfoo, GeoAddParams.geoAddParams().xx().ch(), bcoordinateMap)); bcoordinateMap.clear(); bcoordinateMap.put(bB, new GeoCoordinate(6, 7)); // never add elements. assertEquals(0, jedis.geoadd(bfoo, GeoAddParams.geoAddParams().xx(), bcoordinateMap)); assertEquals(1, jedis.geoadd(bfoo, GeoAddParams.geoAddParams().nx(), bcoordinateMap)); } @Test public void geodist() { prepareGeoData(); Double dist = jedis.geodist("foo", "a", "b"); assertEquals(157149, dist.intValue()); dist = jedis.geodist("foo", "a", "b", GeoUnit.KM); assertEquals(157, dist.intValue()); dist = jedis.geodist("foo", "a", "b", GeoUnit.MI); assertEquals(97, dist.intValue()); dist = jedis.geodist("foo", "a", "b", GeoUnit.FT); assertEquals(515583, dist.intValue()); // binary dist = jedis.geodist(bfoo, bA, bB); assertEquals(157149, dist.intValue()); dist = jedis.geodist(bfoo, bA, bB, GeoUnit.KM); assertEquals(157, dist.intValue()); dist = jedis.geodist(bfoo, bA, bB, GeoUnit.MI); assertEquals(97, dist.intValue()); dist = jedis.geodist(bfoo, bA, bB, GeoUnit.FT); assertEquals(515583, dist.intValue()); } @Test public void geohash() { prepareGeoData(); List hashes = jedis.geohash("foo", "a", "b", "notexist"); assertEquals(3, hashes.size()); assertEquals("s0dnu20t9j0", hashes.get(0)); assertEquals("s093jd0k720", hashes.get(1)); assertNull(hashes.get(2)); // binary List bhashes = jedis.geohash(bfoo, bA, bB, bNotexist); assertEquals(3, bhashes.size()); assertArrayEquals(SafeEncoder.encode("s0dnu20t9j0"), bhashes.get(0)); assertArrayEquals(SafeEncoder.encode("s093jd0k720"), bhashes.get(1)); assertNull(bhashes.get(2)); } @Test public void geopos() { prepareGeoData(); List coordinates = jedis.geopos("foo", "a", "b", "notexist"); assertEquals(3, coordinates.size()); assertEquals(3.0, coordinates.get(0).getLongitude(), EPSILON); assertEquals(4.0, coordinates.get(0).getLatitude(), EPSILON); assertEquals(2.0, coordinates.get(1).getLongitude(), EPSILON); assertEquals(3.0, coordinates.get(1).getLatitude(), EPSILON); assertNull(coordinates.get(2)); List bcoordinates = jedis.geopos(bfoo, bA, bB, bNotexist); assertEquals(3, bcoordinates.size()); assertEquals(3.0, bcoordinates.get(0).getLongitude(), EPSILON); assertEquals(4.0, bcoordinates.get(0).getLatitude(), EPSILON); assertEquals(2.0, bcoordinates.get(1).getLongitude(), EPSILON); assertEquals(3.0, bcoordinates.get(1).getLatitude(), EPSILON); assertNull(bcoordinates.get(2)); } @Test public void georadius() { // prepare datas Map coordinateMap = new HashMap<>(); coordinateMap.put("Palermo", new GeoCoordinate(13.361389, 38.115556)); coordinateMap.put("Catania", new GeoCoordinate(15.087269, 37.502669)); jedis.geoadd("Sicily", coordinateMap); List members = jedis.georadius("Sicily", 15, 37, 200, GeoUnit.KM); assertEquals(2, members.size()); // sort members = jedis.georadius("Sicily", 15, 37, 200, GeoUnit.KM, GeoRadiusParam.geoRadiusParam() .sortDescending()); assertEquals(2, members.size()); assertEquals("Catania", members.get(1).getMemberByString()); assertEquals("Palermo", members.get(0).getMemberByString()); // sort, count 1 members = jedis.georadius("Sicily", 15, 37, 200, GeoUnit.KM, GeoRadiusParam.geoRadiusParam() .sortAscending().count(1)); assertEquals(1, members.size()); // sort, count 1, withdist, withcoord members = jedis.georadius("Sicily", 15, 37, 200, GeoUnit.KM, GeoRadiusParam.geoRadiusParam() .sortAscending().count(1).withCoord().withDist().withHash()); assertEquals(1, members.size()); GeoRadiusResponse response = members.get(0); assertEquals(56.4413, response.getDistance(), EPSILON); assertEquals(15.087269, response.getCoordinate().getLongitude(), EPSILON); assertEquals(37.502669, response.getCoordinate().getLatitude(), EPSILON); assertEquals(3479447370796909L, response.getRawScore()); // sort, count 1, with hash members = jedis.georadius("Sicily", 15, 37, 200, GeoUnit.KM, GeoRadiusParam.geoRadiusParam() .sortAscending().count(1).withHash()); assertEquals(1, members.size()); response = members.get(0); assertEquals(3479447370796909L, response.getRawScore()); // sort, count 1, any members = jedis.georadius("Sicily", 15, 37, 200, GeoUnit.KM, GeoRadiusParam.geoRadiusParam() .sortDescending().count(1, true)); assertEquals(1, members.size()); response = members.get(0); assertTrue(coordinateMap.containsKey(response.getMemberByString())); } @Test @ConditionalOnEnv(value = TestEnvUtil.ENV_REDIS_ENTERPRISE, enabled = false) public void georadiusStore() { // prepare datas Map coordinateMap = new HashMap<>(); coordinateMap.put("Palermo", new GeoCoordinate(13.361389, 38.115556)); coordinateMap.put("Catania", new GeoCoordinate(15.087269, 37.502669)); jedis.geoadd("Sicily", coordinateMap); long size = jedis.georadiusStore("Sicily", 15, 37, 200, GeoUnit.KM, GeoRadiusParam.geoRadiusParam(), GeoRadiusStoreParam.geoRadiusStoreParam().store("SicilyStore")); assertEquals(2, size); List expected = new ArrayList<>(); expected.add("Palermo"); expected.add("Catania"); assertEquals(expected, jedis.zrange("SicilyStore", 0, -1)); } @Test public void georadiusReadonly() { // prepare datas Map coordinateMap = new HashMap<>(); coordinateMap.put("Palermo", new GeoCoordinate(13.361389, 38.115556)); coordinateMap.put("Catania", new GeoCoordinate(15.087269, 37.502669)); jedis.geoadd("Sicily", coordinateMap); List members = jedis.georadiusReadonly("Sicily", 15, 37, 200, GeoUnit.KM); assertEquals(2, members.size()); // sort members = jedis.georadiusReadonly("Sicily", 15, 37, 200, GeoUnit.KM, GeoRadiusParam.geoRadiusParam().sortAscending()); assertEquals(2, members.size()); assertEquals("Catania", members.get(0).getMemberByString()); assertEquals("Palermo", members.get(1).getMemberByString()); // sort, count 1 members = jedis.georadiusReadonly("Sicily", 15, 37, 200, GeoUnit.KM, GeoRadiusParam.geoRadiusParam().sortAscending().count(1)); assertEquals(1, members.size()); // sort, count 1, withdist, withcoord members = jedis.georadiusReadonly("Sicily", 15, 37, 200, GeoUnit.KM, GeoRadiusParam.geoRadiusParam().sortAscending().count(1).withCoord().withDist()); assertEquals(1, members.size()); GeoRadiusResponse response = members.get(0); assertEquals(56.4413, response.getDistance(), EPSILON); assertEquals(15.087269, response.getCoordinate().getLongitude(), EPSILON); assertEquals(37.502669, response.getCoordinate().getLatitude(), EPSILON); } @Test public void georadiusBinary() { // prepare datas Map bcoordinateMap = new HashMap<>(); bcoordinateMap.put(bA, new GeoCoordinate(13.361389, 38.115556)); bcoordinateMap.put(bB, new GeoCoordinate(15.087269, 37.502669)); jedis.geoadd(bfoo, bcoordinateMap); List members = jedis.georadius(bfoo, 15, 37, 200, GeoUnit.KM); assertEquals(2, members.size()); // sort members = jedis.georadius(bfoo, 15, 37, 200, GeoUnit.KM, GeoRadiusParam.geoRadiusParam() .sortAscending()); assertEquals(2, members.size()); assertArrayEquals(bB, members.get(0).getMember()); assertArrayEquals(bA, members.get(1).getMember()); // sort, count 1 members = jedis.georadius(bfoo, 15, 37, 200, GeoUnit.KM, GeoRadiusParam.geoRadiusParam() .sortAscending().count(1)); assertEquals(1, members.size()); // sort, count 1, withdist, withcoord members = jedis.georadius(bfoo, 15, 37, 200, GeoUnit.KM, GeoRadiusParam.geoRadiusParam() .sortAscending().count(1).withCoord().withDist()); assertEquals(1, members.size()); GeoRadiusResponse response = members.get(0); assertEquals(56.4413, response.getDistance(), EPSILON); assertEquals(15.087269, response.getCoordinate().getLongitude(), EPSILON); assertEquals(37.502669, response.getCoordinate().getLatitude(), EPSILON); } @Test @ConditionalOnEnv(value = TestEnvUtil.ENV_REDIS_ENTERPRISE, enabled = false) public void georadiusStoreBinary() { // prepare datas Map bcoordinateMap = new HashMap<>(); bcoordinateMap.put(bA, new GeoCoordinate(13.361389, 38.115556)); bcoordinateMap.put(bB, new GeoCoordinate(15.087269, 37.502669)); jedis.geoadd(bfoo, bcoordinateMap); long size = jedis.georadiusStore(bfoo, 15, 37, 200, GeoUnit.KM, GeoRadiusParam.geoRadiusParam(), GeoRadiusStoreParam.geoRadiusStoreParam().store("SicilyStore")); assertEquals(2, size); List bexpected = new ArrayList<>(); bexpected.add(bA); bexpected.add(bB); AssertUtil.assertByteArrayListEquals(bexpected, jedis.zrange("SicilyStore".getBytes(), 0, -1)); } @Test public void georadiusReadonlyBinary() { // prepare datas Map bcoordinateMap = new HashMap<>(); bcoordinateMap.put(bA, new GeoCoordinate(13.361389, 38.115556)); bcoordinateMap.put(bB, new GeoCoordinate(15.087269, 37.502669)); jedis.geoadd(bfoo, bcoordinateMap); List members = jedis.georadiusReadonly(bfoo, 15, 37, 200, GeoUnit.KM); assertEquals(2, members.size()); // sort members = jedis.georadiusReadonly(bfoo, 15, 37, 200, GeoUnit.KM, GeoRadiusParam.geoRadiusParam().sortAscending()); assertEquals(2, members.size()); assertArrayEquals(bB, members.get(0).getMember()); assertArrayEquals(bA, members.get(1).getMember()); // sort, count 1 members = jedis.georadiusReadonly(bfoo, 15, 37, 200, GeoUnit.KM, GeoRadiusParam.geoRadiusParam().sortAscending().count(1)); assertEquals(1, members.size()); // sort, count 1, withdist, withcoord members = jedis.georadiusReadonly(bfoo, 15, 37, 200, GeoUnit.KM, GeoRadiusParam.geoRadiusParam().sortAscending().count(1).withCoord().withDist()); assertEquals(1, members.size()); GeoRadiusResponse response = members.get(0); assertEquals(56.4413, response.getDistance(), EPSILON); assertEquals(15.087269, response.getCoordinate().getLongitude(), EPSILON); assertEquals(37.502669, response.getCoordinate().getLatitude(), EPSILON); } @Test public void georadiusByMember() { jedis.geoadd("Sicily", 13.583333, 37.316667, "Agrigento"); jedis.geoadd("Sicily", 13.361389, 38.115556, "Palermo"); jedis.geoadd("Sicily", 15.087269, 37.502669, "Catania"); List members = jedis.georadiusByMember("Sicily", "Agrigento", 100, GeoUnit.KM); assertEquals(2, members.size()); members = jedis.georadiusByMember("Sicily", "Agrigento", 100, GeoUnit.KM, GeoRadiusParam .geoRadiusParam().sortAscending()); assertEquals(2, members.size()); assertEquals("Agrigento", members.get(0).getMemberByString()); assertEquals("Palermo", members.get(1).getMemberByString()); members = jedis.georadiusByMember("Sicily", "Agrigento", 100, GeoUnit.KM, GeoRadiusParam .geoRadiusParam().sortAscending().count(1).withCoord().withDist()); assertEquals(1, members.size()); GeoRadiusResponse member = members.get(0); assertEquals("Agrigento", member.getMemberByString()); assertEquals(0, member.getDistance(), EPSILON); assertEquals(13.583333, member.getCoordinate().getLongitude(), EPSILON); assertEquals(37.316667, member.getCoordinate().getLatitude(), EPSILON); } @Test @ConditionalOnEnv(value = TestEnvUtil.ENV_REDIS_ENTERPRISE, enabled = false) public void georadiusByMemberStore() { jedis.geoadd("Sicily", 13.583333, 37.316667, "Agrigento"); jedis.geoadd("Sicily", 13.361389, 38.115556, "Palermo"); jedis.geoadd("Sicily", 15.087269, 37.502669, "Catania"); long size = jedis.georadiusByMemberStore("Sicily", "Agrigento", 100, GeoUnit.KM, GeoRadiusParam.geoRadiusParam(), GeoRadiusStoreParam.geoRadiusStoreParam().store("SicilyStore")); assertEquals(2, size); List expected = new ArrayList<>(); expected.add("Agrigento"); expected.add("Palermo"); assertEquals(expected, jedis.zrange("SicilyStore", 0, -1)); } @Test public void georadiusByMemberReadonly() { jedis.geoadd("Sicily", 13.583333, 37.316667, "Agrigento"); jedis.geoadd("Sicily", 13.361389, 38.115556, "Palermo"); jedis.geoadd("Sicily", 15.087269, 37.502669, "Catania"); List members = jedis.georadiusByMemberReadonly("Sicily", "Agrigento", 100, GeoUnit.KM); assertEquals(2, members.size()); members = jedis.georadiusByMemberReadonly("Sicily", "Agrigento", 100, GeoUnit.KM, GeoRadiusParam.geoRadiusParam().sortAscending()); assertEquals(2, members.size()); assertEquals("Agrigento", members.get(0).getMemberByString()); assertEquals("Palermo", members.get(1).getMemberByString()); members = jedis.georadiusByMemberReadonly("Sicily", "Agrigento", 100, GeoUnit.KM, GeoRadiusParam.geoRadiusParam().sortAscending().count(1).withCoord().withDist()); assertEquals(1, members.size()); GeoRadiusResponse member = members.get(0); assertEquals("Agrigento", member.getMemberByString()); assertEquals(0, member.getDistance(), EPSILON); assertEquals(13.583333, member.getCoordinate().getLongitude(), EPSILON); assertEquals(37.316667, member.getCoordinate().getLatitude(), EPSILON); } @Test public void georadiusByMemberBinary() { jedis.geoadd(bfoo, 13.583333, 37.316667, bA); jedis.geoadd(bfoo, 13.361389, 38.115556, bB); jedis.geoadd(bfoo, 15.087269, 37.502669, bC); List members = jedis.georadiusByMember(bfoo, bA, 100, GeoUnit.KM); assertEquals(2, members.size()); members = jedis.georadiusByMember(bfoo, bA, 100, GeoUnit.KM, GeoRadiusParam.geoRadiusParam().sortAscending()); assertEquals(2, members.size()); assertArrayEquals(bA, members.get(0).getMember()); assertArrayEquals(bB, members.get(1).getMember()); members = jedis.georadiusByMember(bfoo, bA, 100, GeoUnit.KM, GeoRadiusParam.geoRadiusParam().sortAscending().count(1).withCoord().withDist()); assertEquals(1, members.size()); GeoRadiusResponse member = members.get(0); assertArrayEquals(bA, member.getMember()); assertEquals(0, member.getDistance(), EPSILON); assertEquals(13.583333, member.getCoordinate().getLongitude(), EPSILON); assertEquals(37.316667, member.getCoordinate().getLatitude(), EPSILON); } @Test @ConditionalOnEnv(value = TestEnvUtil.ENV_REDIS_ENTERPRISE, enabled = false) public void georadiusByMemberStoreBinary() { jedis.geoadd(bfoo, 13.583333, 37.316667, bA); jedis.geoadd(bfoo, 13.361389, 38.115556, bB); jedis.geoadd(bfoo, 15.087269, 37.502669, bC); assertEquals(2, jedis.georadiusByMemberStore(bfoo, bA, 100, GeoUnit.KM, GeoRadiusParam.geoRadiusParam(), GeoRadiusStoreParam.geoRadiusStoreParam().store("SicilyStore"))); List bexpected = new ArrayList<>(); bexpected.add(bA); bexpected.add(bB); AssertUtil.assertByteArrayListEquals(bexpected, jedis.zrange("SicilyStore".getBytes(), 0, -1)); } @Test public void georadiusByMemberReadonlyBinary() { jedis.geoadd(bfoo, 13.583333, 37.316667, bA); jedis.geoadd(bfoo, 13.361389, 38.115556, bB); jedis.geoadd(bfoo, 15.087269, 37.502669, bC); List members = jedis.georadiusByMemberReadonly(bfoo, bA, 100, GeoUnit.KM); assertEquals(2, members.size()); members = jedis.georadiusByMemberReadonly(bfoo, bA, 100, GeoUnit.KM, GeoRadiusParam.geoRadiusParam().sortAscending()); assertEquals(2, members.size()); assertArrayEquals(bA, members.get(0).getMember()); assertArrayEquals(bB, members.get(1).getMember()); members = jedis.georadiusByMemberReadonly(bfoo, bA, 100, GeoUnit.KM, GeoRadiusParam.geoRadiusParam().sortAscending().count(1).withCoord().withDist()); assertEquals(1, members.size()); GeoRadiusResponse member = members.get(0); assertArrayEquals(bA, member.getMember()); assertEquals(0, member.getDistance(), EPSILON); assertEquals(13.583333, member.getCoordinate().getLongitude(), EPSILON); assertEquals(37.316667, member.getCoordinate().getLatitude(), EPSILON); } @Test public void geosearch() { jedis.geoadd("barcelona", 2.1909389952632d, 41.433791470673d, "place1"); jedis.geoadd("barcelona", 2.1873744593677d, 41.406342043777d, "place2"); jedis.geoadd("barcelona", 2.583333d, 41.316667d, "place3"); // FROMLONLAT and BYRADIUS List members = jedis.geosearch("barcelona", new GeoCoordinate(2.191d,41.433d), 1000, GeoUnit.M); assertEquals(1, members.size()); assertEquals("place1", members.get(0).getMemberByString()); // using Params members = jedis.geosearch("barcelona", new GeoSearchParam().byRadius(3000, GeoUnit.M) .fromLonLat(2.191d,41.433d).desc()); assertEquals(2, members.size()); assertEquals("place2", members.get(0).getMemberByString()); // FROMMEMBER and BYRADIUS members = jedis.geosearch("barcelona","place3", 100, GeoUnit.KM); assertEquals(3, members.size()); // using Params members = jedis.geosearch("barcelona", new GeoSearchParam().fromMember("place1") .byRadius(100, GeoUnit.KM).withDist().withCoord().withHash().count(2)); assertEquals(2, members.size()); assertEquals("place1", members.get(0).getMemberByString()); GeoRadiusResponse res2 = members.get(1); assertEquals("place2", res2.getMemberByString()); assertEquals(3.0674157, res2.getDistance(), 5); assertEquals(new GeoCoordinate(2.187376320362091, 41.40634178640635), res2.getCoordinate()); // FROMMEMBER and BYBOX members = jedis.geosearch("barcelona","place3", 100, 100, GeoUnit.KM); assertEquals(3, members.size()); // using Params members = jedis.geosearch("barcelona", new GeoSearchParam().fromMember("place3") .byBox(100, 100, GeoUnit.KM).asc().count(1, true)); assertEquals(1, members.size()); // FROMLONLAT and BYBOX members = jedis.geosearch("barcelona", new GeoCoordinate(2.191, 41.433), 1, 1, GeoUnit.KM); assertEquals(1, members.size()); // using Params members = jedis.geosearch("barcelona", new GeoSearchParam().byBox(1,1, GeoUnit.KM) .fromLonLat(2.191, 41.433).withDist().withCoord()); assertEquals(1, members.size()); assertEquals("place1", members.get(0).getMemberByString()); assertEquals(0.0881, members.get(0).getDistance(), 10); assertThat(members.get(0).getCoordinate(), GeoCoordinateMatcher.atCoordinates(2.19093829393386841, 41.43379028184083523)); } @Test public void geosearchNegative() { // combine byradius and bybox try { jedis.geosearch("barcelona", new GeoSearchParam() .byRadius(3000, GeoUnit.M) .byBox(300, 300, GeoUnit.M)); fail(); } catch (IllegalArgumentException ignored) { } // without byradius and without bybox try { jedis.geosearch("barcelona", new GeoSearchParam().fromMember("foobar")); fail(); } catch (IllegalArgumentException ignored) { } // combine frommember and fromlonlat try { jedis.geosearch("barcelona", new GeoSearchParam() .fromMember("foobar") .fromLonLat(10,10)); fail(); } catch (IllegalArgumentException ignored) { } // without frommember and without fromlonlat try { jedis.geosearch("barcelona", new GeoSearchParam().byRadius(10, GeoUnit.MI)); fail(); } catch (IllegalArgumentException ignored) { } } @Test @ConditionalOnEnv(value = TestEnvUtil.ENV_REDIS_ENTERPRISE, enabled = false) public void geosearchstore() { jedis.geoadd("barcelona", 2.1909389952632d, 41.433791470673d, "place1"); jedis.geoadd("barcelona", 2.1873744593677d, 41.406342043777d, "place2"); jedis.geoadd("barcelona", 2.583333d, 41.316667d, "place3"); // FROMLONLAT and BYRADIUS long members = jedis.geosearchStore("tel-aviv", "barcelona", new GeoCoordinate(2.191d,41.433d), 1000, GeoUnit.M); assertEquals(1, members); List expected = new ArrayList<>(); expected.add("place1"); assertEquals(expected, jedis.zrange("tel-aviv", 0, -1)); members = jedis.geosearchStore("tel-aviv","barcelona", new GeoSearchParam() .byRadius(3000, GeoUnit.M) .fromLonLat(new GeoCoordinate(2.191d,41.433d))); assertEquals(2, members); assertEquals(2, members); // FROMMEMBER and BYRADIUS members = jedis.geosearchStore("tel-aviv", "barcelona","place3", 100, GeoUnit.KM); assertEquals(3, members); // FROMMEMBER and BYBOX members = jedis.geosearchStore("tel-aviv","barcelona","place3", 100, 100, GeoUnit.KM); assertEquals(3, members); // FROMLONLAT and BYBOX members = jedis.geosearchStore("tel-aviv","barcelona", new GeoCoordinate(2.191, 41.433), 1, 1, GeoUnit.KM); assertEquals(1, members); } @Test @ConditionalOnEnv(value = TestEnvUtil.ENV_REDIS_ENTERPRISE, enabled = false) public void geosearchstoreWithdist() { jedis.geoadd("barcelona", 2.1909389952632d, 41.433791470673d, "place1"); jedis.geoadd("barcelona", 2.1873744593677d, 41.406342043777d, "place2"); long members = jedis.geosearchStoreStoreDist("tel-aviv","barcelona", new GeoSearchParam().byRadius(3000, GeoUnit.M) .fromLonLat(2.191d,41.433d)); assertEquals(2, members); assertEquals(88.05060698409301, jedis.zscore("tel-aviv", "place1"), 5); } private void prepareGeoData() { Map coordinateMap = new HashMap<>(); coordinateMap.put("a", new GeoCoordinate(3, 4)); coordinateMap.put("b", new GeoCoordinate(2, 3)); coordinateMap.put("c", new GeoCoordinate(3.314, 2.3241)); assertEquals(3, jedis.geoadd("foo", coordinateMap)); Map bcoordinateMap = new HashMap<>(); bcoordinateMap.put(bA, new GeoCoordinate(3, 4)); bcoordinateMap.put(bB, new GeoCoordinate(2, 3)); bcoordinateMap.put(bC, new GeoCoordinate(3.314, 2.3241)); assertEquals(3, jedis.geoadd(bfoo, bcoordinateMap)); } } ================================================ FILE: src/test/java/redis/clients/jedis/commands/unified/HashesCommandsTestBase.java ================================================ package redis.clients.jedis.commands.unified; import static java.util.Arrays.asList; import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.both; import static org.hamcrest.Matchers.contains; import static org.hamcrest.Matchers.containsInAnyOrder; import static org.hamcrest.Matchers.equalTo; import static org.hamcrest.Matchers.greaterThan; import static org.hamcrest.Matchers.greaterThanOrEqualTo; import static org.hamcrest.Matchers.lessThanOrEqualTo; import static org.junit.jupiter.api.Assertions.assertArrayEquals; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertNull; import static org.junit.jupiter.api.Assertions.assertTrue; import static redis.clients.jedis.params.ScanParams.SCAN_POINTER_START; import static redis.clients.jedis.params.ScanParams.SCAN_POINTER_START_BINARY; import static redis.clients.jedis.util.AssertUtil.assertByteArrayListEquals; import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; import java.util.HashMap; import java.util.LinkedHashMap; import java.util.LinkedHashSet; import java.util.List; import java.util.Map; import java.util.Set; import java.util.stream.Collectors; import io.redis.test.annotations.SinceRedisVersion; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Tag; import redis.clients.jedis.RedisProtocol; import redis.clients.jedis.args.ExpiryOption; import redis.clients.jedis.params.HGetExParams; import redis.clients.jedis.params.HSetExParams; import redis.clients.jedis.params.ScanParams; import redis.clients.jedis.resps.ScanResult; import redis.clients.jedis.util.AssertUtil; import redis.clients.jedis.util.JedisByteHashMap; @Tag("integration") public abstract class HashesCommandsTestBase extends UnifiedJedisCommandsTestBase { final byte[] bfoo = { 0x01, 0x02, 0x03, 0x04 }; final byte[] bbar = { 0x05, 0x06, 0x07, 0x08 }; final byte[] bcar = { 0x09, 0x0A, 0x0B, 0x0C }; final byte[] bbare = { 0x05, 0x06, 0x07, 0x08, 0x09 }; final byte[] bcare = { 0x09, 0x0A, 0x0B, 0x0C, 0x0D }; final byte[] bbar1 = { 0x05, 0x06, 0x07, 0x08, 0x0A }; final byte[] bbar2 = { 0x05, 0x06, 0x07, 0x08, 0x0B }; final byte[] bbar3 = { 0x05, 0x06, 0x07, 0x08, 0x0C }; final byte[] bbarstar = { 0x05, 0x06, 0x07, 0x08, '*' }; public HashesCommandsTestBase(RedisProtocol protocol) { super(protocol); } @Test public void hset() { assertEquals(1, jedis.hset("foo", "bar", "car")); assertEquals(0, jedis.hset("foo", "bar", "foo")); // Binary assertEquals(1, jedis.hset(bfoo, bbar, bcar)); assertEquals(0, jedis.hset(bfoo, bbar, bfoo)); } @Test public void hget() { jedis.hset("foo", "bar", "car"); assertNull(jedis.hget("bar", "foo")); assertNull(jedis.hget("foo", "car")); assertEquals("car", jedis.hget("foo", "bar")); // Binary jedis.hset(bfoo, bbar, bcar); assertNull(jedis.hget(bbar, bfoo)); assertNull(jedis.hget(bfoo, bcar)); assertArrayEquals(bcar, jedis.hget(bfoo, bbar)); } @Test @SinceRedisVersion("7.9.0") public void hgetex() { long seconds = 20; jedis.hset("foo", "bar", "car"); assertEquals(asList("car"), jedis.hgetex("foo", HGetExParams.hGetExParams().ex(seconds), "bar")); assertThat(jedis.httl("foo", "bar").get(0), greaterThanOrEqualTo(seconds - 1)); jedis.hset("foo", "bar", "car"); assertEquals(asList("car"), jedis.hgetex("foo", HGetExParams.hGetExParams().persist(), "bar")); assertEquals(Long.valueOf(-1), jedis.httl("foo", "bar").get(0)); jedis.hset("foo", "bar", "car"); jedis.hset("foo", "bare", "care"); assertEquals(asList("car", "care"), jedis.hgetex("foo", HGetExParams.hGetExParams().ex(seconds), "bar", "bare")); assertThat(jedis.httl("foo", "bar").get(0), greaterThanOrEqualTo(seconds - 1)); assertThat(jedis.httl("foo", "bare").get(0), greaterThanOrEqualTo(seconds - 1)); // Binary jedis.hset(bfoo, bbar, bcar); assertByteArrayListEquals(asList(bcar), jedis.hgetex(bfoo, HGetExParams.hGetExParams().ex(seconds), bbar)); assertThat(jedis.httl(bfoo, bbar).get(0), greaterThanOrEqualTo(seconds - 1)); jedis.hset(bfoo, bbar, bcar); assertByteArrayListEquals(asList(bcar), jedis.hgetex(bfoo, HGetExParams.hGetExParams().persist(), bbar)); assertEquals(Long.valueOf(-1), jedis.httl(bfoo, bbar).get(0)); jedis.hset(bfoo, bbar, bcar); jedis.hset(bfoo, bbare, bcare); assertByteArrayListEquals(asList(bcar, bcare), jedis.hgetex(bfoo, HGetExParams.hGetExParams().ex(seconds), bbar, bbare)); assertThat(jedis.httl(bfoo, bbar).get(0), greaterThanOrEqualTo(seconds - 1)); assertThat(jedis.httl(bfoo, bbare).get(0), greaterThanOrEqualTo(seconds - 1)); } @Test @SinceRedisVersion("7.9.0") public void hgetdel() { jedis.hset("foo", "bar", "car"); assertEquals(asList("car"), jedis.hgetdel("foo", "bar")); assertEquals(null, jedis.hget("foo", "bar")); jedis.hset("foo", "bar", "car"); jedis.hset("foo", "bare", "care"); assertEquals(asList("car", "care"), jedis.hgetdel("foo", "bar", "bare")); assertEquals(null, jedis.hget("foo", "bar")); assertEquals(null, jedis.hget("foo", "bare")); // Binary jedis.hset(bfoo, bbar, bcar); assertByteArrayListEquals(asList(bcar), jedis.hgetdel(bfoo, bbar)); assertEquals(null, jedis.hget(bfoo, bbar)); jedis.hset(bfoo, bbar, bcar); jedis.hset(bfoo, bbare, bcare); assertByteArrayListEquals(asList(bcar, bcare), jedis.hgetdel(bfoo, bbar, bbare)); assertEquals(null, jedis.hget(bfoo, bbar)); assertEquals(null, jedis.hget(bfoo, bbare)); } @Test @SinceRedisVersion("7.9.0") public void hsetex() { long seconds = 20; jedis.del("foo"); assertEquals(1, jedis.hsetex("foo", HSetExParams.hSetExParams().ex(seconds).fnx(), "bar", "car")); assertThat(jedis.httl("foo", "bar").get(0), greaterThanOrEqualTo(seconds - 1)); assertEquals(1, jedis.hsetex("foo", HSetExParams.hSetExParams().keepTtl(), "bar", "car")); assertThat(jedis.httl("foo", "bar").get(0), greaterThanOrEqualTo(seconds - 1)); assertEquals(1, jedis.hsetex("foo", HSetExParams.hSetExParams().fxx(), "bar", "car")); assertEquals(Long.valueOf(-1), jedis.httl("foo", "bar").get(0)); HashMap hash = new HashMap(); hash.put("bar", "car"); hash.put("bare", "care"); jedis.del("foo"); assertEquals(1, jedis.hsetex("foo", HSetExParams.hSetExParams().ex(seconds).fnx(), hash)); assertThat(jedis.httl("foo", "bar").get(0), greaterThanOrEqualTo(seconds - 1)); assertThat(jedis.httl("foo", "bare").get(0), greaterThanOrEqualTo(seconds - 1)); assertEquals(1, jedis.hsetex("foo", HSetExParams.hSetExParams().keepTtl(), hash)); assertThat(jedis.httl("foo", "bar").get(0), greaterThanOrEqualTo(seconds - 1)); assertThat(jedis.httl("foo", "bare").get(0), greaterThanOrEqualTo(seconds - 1)); assertEquals(1, jedis.hsetex("foo", HSetExParams.hSetExParams().fxx(), hash)); assertEquals(Long.valueOf(-1), jedis.httl("foo", "bar").get(0)); assertEquals(Long.valueOf(-1), jedis.httl("foo", "bare").get(0)); // Binary jedis.del(bfoo); assertEquals(1, jedis.hsetex(bfoo, HSetExParams.hSetExParams().ex(seconds).fnx(), bbar, bcar)); assertThat(jedis.httl(bfoo, bbar).get(0), greaterThanOrEqualTo(seconds - 1)); assertEquals(1, jedis.hsetex(bfoo, HSetExParams.hSetExParams().keepTtl(), bbar, bcar)); assertThat(jedis.httl(bfoo, bbar).get(0), greaterThanOrEqualTo(seconds - 1)); assertEquals(1, jedis.hsetex(bfoo, HSetExParams.hSetExParams().fxx(), bbar, bcar)); assertEquals(Long.valueOf(-1), jedis.httl(bfoo, bbar).get(0)); HashMap bhash = new HashMap(); bhash.put(bbar, bcar); bhash.put(bbare, bcare); jedis.del(bfoo); assertEquals(1, jedis.hsetex(bfoo, HSetExParams.hSetExParams().ex(seconds).fnx(), bhash)); assertThat(jedis.httl(bfoo, bbar).get(0), greaterThanOrEqualTo(seconds - 1)); assertThat(jedis.httl(bfoo, bbare).get(0), greaterThanOrEqualTo(seconds - 1)); assertEquals(1, jedis.hsetex(bfoo, HSetExParams.hSetExParams().keepTtl(), bhash)); assertThat(jedis.httl(bfoo, bbar).get(0), greaterThanOrEqualTo(seconds - 1)); assertThat(jedis.httl(bfoo, bbare).get(0), greaterThanOrEqualTo(seconds - 1)); assertEquals(1, jedis.hsetex(bfoo, HSetExParams.hSetExParams().fxx(), bhash)); assertEquals(Long.valueOf(-1), jedis.httl(bfoo, bbar).get(0)); assertEquals(Long.valueOf(-1), jedis.httl(bfoo, bbare).get(0)); } @Test public void hsetnx() { assertEquals(1, jedis.hsetnx("foo", "bar", "car")); assertEquals("car", jedis.hget("foo", "bar")); assertEquals(0, jedis.hsetnx("foo", "bar", "foo")); assertEquals("car", jedis.hget("foo", "bar")); assertEquals(1, jedis.hsetnx("foo", "car", "bar")); assertEquals("bar", jedis.hget("foo", "car")); // Binary assertEquals(1, jedis.hsetnx(bfoo, bbar, bcar)); assertArrayEquals(bcar, jedis.hget(bfoo, bbar)); assertEquals(0, jedis.hsetnx(bfoo, bbar, bfoo)); assertArrayEquals(bcar, jedis.hget(bfoo, bbar)); assertEquals(1, jedis.hsetnx(bfoo, bcar, bbar)); assertArrayEquals(bbar, jedis.hget(bfoo, bcar)); } @Test public void hmset() { Map hash = new HashMap(); hash.put("bar", "car"); hash.put("car", "bar"); assertEquals("OK", jedis.hmset("foo", hash)); assertEquals("car", jedis.hget("foo", "bar")); assertEquals("bar", jedis.hget("foo", "car")); // Binary Map bhash = new HashMap(); bhash.put(bbar, bcar); bhash.put(bcar, bbar); assertEquals("OK", jedis.hmset(bfoo, bhash)); assertArrayEquals(bcar, jedis.hget(bfoo, bbar)); assertArrayEquals(bbar, jedis.hget(bfoo, bcar)); } @Test public void hsetVariadic() { Map hash = new HashMap(); hash.put("bar", "car"); hash.put("car", "bar"); assertEquals(2, jedis.hset("foo", hash)); assertEquals("car", jedis.hget("foo", "bar")); assertEquals("bar", jedis.hget("foo", "car")); // Binary Map bhash = new HashMap(); bhash.put(bbar, bcar); bhash.put(bcar, bbar); assertEquals(2, jedis.hset(bfoo, bhash)); assertArrayEquals(bcar, jedis.hget(bfoo, bbar)); assertArrayEquals(bbar, jedis.hget(bfoo, bcar)); } @Test public void hmget() { Map hash = new HashMap(); hash.put("bar", "car"); hash.put("car", "bar"); jedis.hmset("foo", hash); List values = jedis.hmget("foo", "bar", "car", "foo"); List expected = new ArrayList(); expected.add("car"); expected.add("bar"); expected.add(null); assertEquals(expected, values); // Binary Map bhash = new HashMap(); bhash.put(bbar, bcar); bhash.put(bcar, bbar); jedis.hmset(bfoo, bhash); List bvalues = jedis.hmget(bfoo, bbar, bcar, bfoo); List bexpected = new ArrayList(); bexpected.add(bcar); bexpected.add(bbar); bexpected.add(null); AssertUtil.assertByteArrayListEquals(bexpected, bvalues); } @Test public void hincrBy() { assertEquals(1, jedis.hincrBy("foo", "bar", 1)); assertEquals(0, jedis.hincrBy("foo", "bar", -1)); assertEquals(-10, jedis.hincrBy("foo", "bar", -10)); // Binary assertEquals(1, jedis.hincrBy(bfoo, bbar, 1)); assertEquals(0, jedis.hincrBy(bfoo, bbar, -1)); assertEquals(-10, jedis.hincrBy(bfoo, bbar, -10)); } @Test public void hincrByFloat() { assertEquals(1.5d, jedis.hincrByFloat("foo", "bar", 1.5d), 0); assertEquals(0d, jedis.hincrByFloat("foo", "bar", -1.5d), 0); assertEquals(-10.7d, jedis.hincrByFloat("foo", "bar", -10.7d), 0); // Binary assertEquals(1.5d, jedis.hincrByFloat(bfoo, bbar, 1.5d), 0d); assertEquals(0d, jedis.hincrByFloat(bfoo, bbar, -1.5d), 0d); assertEquals(-10.7d, jedis.hincrByFloat(bfoo, bbar, -10.7d), 0d); } @Test public void hexists() { Map hash = new HashMap(); hash.put("bar", "car"); hash.put("car", "bar"); jedis.hmset("foo", hash); assertFalse(jedis.hexists("bar", "foo")); assertFalse(jedis.hexists("foo", "foo")); assertTrue(jedis.hexists("foo", "bar")); // Binary Map bhash = new HashMap(); bhash.put(bbar, bcar); bhash.put(bcar, bbar); jedis.hmset(bfoo, bhash); assertFalse(jedis.hexists(bbar, bfoo)); assertFalse(jedis.hexists(bfoo, bfoo)); assertTrue(jedis.hexists(bfoo, bbar)); } @Test public void hdel() { Map hash = new HashMap(); hash.put("bar", "car"); hash.put("car", "bar"); jedis.hmset("foo", hash); assertEquals(0, jedis.hdel("bar", "foo")); assertEquals(0, jedis.hdel("foo", "foo")); assertEquals(1, jedis.hdel("foo", "bar")); assertNull(jedis.hget("foo", "bar")); // Binary Map bhash = new HashMap(); bhash.put(bbar, bcar); bhash.put(bcar, bbar); jedis.hmset(bfoo, bhash); assertEquals(0, jedis.hdel(bbar, bfoo)); assertEquals(0, jedis.hdel(bfoo, bfoo)); assertEquals(1, jedis.hdel(bfoo, bbar)); assertNull(jedis.hget(bfoo, bbar)); } @Test public void hlen() { Map hash = new HashMap(); hash.put("bar", "car"); hash.put("car", "bar"); jedis.hmset("foo", hash); assertEquals(0, jedis.hlen("bar")); assertEquals(2, jedis.hlen("foo")); // Binary Map bhash = new HashMap(); bhash.put(bbar, bcar); bhash.put(bcar, bbar); jedis.hmset(bfoo, bhash); assertEquals(0, jedis.hlen(bbar)); assertEquals(2, jedis.hlen(bfoo)); } @Test public void hkeys() { Map hash = new LinkedHashMap(); hash.put("bar", "car"); hash.put("car", "bar"); jedis.hmset("foo", hash); Set keys = jedis.hkeys("foo"); Set expected = new LinkedHashSet(); expected.add("bar"); expected.add("car"); assertEquals(expected, keys); // Binary Map bhash = new LinkedHashMap(); bhash.put(bbar, bcar); bhash.put(bcar, bbar); jedis.hmset(bfoo, bhash); Set bkeys = jedis.hkeys(bfoo); Set bexpected = new LinkedHashSet(); bexpected.add(bbar); bexpected.add(bcar); AssertUtil.assertByteArraySetEquals(bexpected, bkeys); } @Test public void hvals() { Map hash = new LinkedHashMap(); hash.put("bar", "car"); hash.put("car", "bar"); jedis.hmset("foo", hash); List vals = jedis.hvals("foo"); assertEquals(2, vals.size()); AssertUtil.assertCollectionContains(vals, "bar"); AssertUtil.assertCollectionContains(vals, "car"); // Binary Map bhash = new LinkedHashMap(); bhash.put(bbar, bcar); bhash.put(bcar, bbar); jedis.hmset(bfoo, bhash); List bvals = jedis.hvals(bfoo); assertEquals(2, bvals.size()); AssertUtil.assertByteArrayCollectionContains(bvals, bbar); AssertUtil.assertByteArrayCollectionContains(bvals, bcar); } @Test public void hgetAll() { Map h = new HashMap(); h.put("bar", "car"); h.put("car", "bar"); jedis.hmset("foo", h); Map hash = jedis.hgetAll("foo"); assertEquals(2, hash.size()); assertEquals("car", hash.get("bar")); assertEquals("bar", hash.get("car")); // Binary Map bh = new HashMap(); bh.put(bbar, bcar); bh.put(bcar, bbar); jedis.hmset(bfoo, bh); Map bhash = jedis.hgetAll(bfoo); assertEquals(2, bhash.size()); assertArrayEquals(bcar, bhash.get(bbar)); assertArrayEquals(bbar, bhash.get(bcar)); } @Test public void hscan() { jedis.hset("foo", "b", "y"); jedis.hset("foo", "a", "x"); ScanResult> result = jedis.hscan("foo", SCAN_POINTER_START); assertEquals(SCAN_POINTER_START, result.getCursor()); assertEquals(2, result.getResult().size()); assertThat( result.getResult().stream().map(Map.Entry::getKey).collect(Collectors.toList()), containsInAnyOrder("a", "b")); assertThat( result.getResult().stream().map(Map.Entry::getValue).collect(Collectors.toList()), containsInAnyOrder("x", "y")); // binary jedis.hset(bfoo, bbar, bcar); ScanResult> bResult = jedis.hscan(bfoo, SCAN_POINTER_START_BINARY); assertArrayEquals(SCAN_POINTER_START_BINARY, bResult.getCursorAsBytes()); assertEquals(1, bResult.getResult().size()); assertThat( bResult.getResult().stream().map(Map.Entry::getKey).collect(Collectors.toList()), containsInAnyOrder(bbar)); assertThat( bResult.getResult().stream().map(Map.Entry::getValue).collect(Collectors.toList()), containsInAnyOrder(bcar)); } @Test public void hscanMatch() { ScanParams params = new ScanParams(); params.match("a*"); jedis.hset("foo", "b", "y"); jedis.hset("foo", "a", "x"); jedis.hset("foo", "aa", "xx"); ScanResult> result = jedis.hscan("foo", SCAN_POINTER_START, params); assertEquals(SCAN_POINTER_START, result.getCursor()); assertEquals(2, result.getResult().size()); assertThat( result.getResult().stream().map(Map.Entry::getKey).collect(Collectors.toList()), containsInAnyOrder("a", "aa")); assertThat( result.getResult().stream().map(Map.Entry::getValue).collect(Collectors.toList()), containsInAnyOrder("x", "xx")); // binary params = new ScanParams(); params.match(bbarstar); jedis.hset(bfoo, bbar, bcar); jedis.hset(bfoo, bbar1, bcar); jedis.hset(bfoo, bbar2, bcar); jedis.hset(bfoo, bbar3, bcar); ScanResult> bResult = jedis.hscan(bfoo, SCAN_POINTER_START_BINARY, params); assertArrayEquals(SCAN_POINTER_START_BINARY, bResult.getCursorAsBytes()); assertEquals(4, bResult.getResult().size()); assertThat( bResult.getResult().stream().map(Map.Entry::getKey).collect(Collectors.toList()), containsInAnyOrder(bbar, bbar1, bbar2, bbar3)); assertThat( bResult.getResult().stream().map(Map.Entry::getValue).collect(Collectors.toList()), containsInAnyOrder(bcar, bcar, bcar, bcar)); } @Test public void hscanCount() { ScanParams params = new ScanParams(); params.count(2); for (int i = 0; i < 10; i++) { jedis.hset("foo", "a" + i, "x" + i); } ScanResult> result = jedis.hscan("foo", SCAN_POINTER_START, params); assertFalse(result.getResult().isEmpty()); assertThat( result.getResult().stream().map(Map.Entry::getKey).map(s -> s.substring(0, 1)).collect(Collectors.toSet()), containsInAnyOrder("a")); assertThat( result.getResult().stream().map(Map.Entry::getValue).map(s -> s.substring(0, 1)).collect(Collectors.toSet()), containsInAnyOrder("x")); // binary params = new ScanParams(); params.count(2); jedis.hset(bfoo, bbar, bcar); jedis.hset(bfoo, bbar1, bcar); jedis.hset(bfoo, bbar2, bcar); jedis.hset(bfoo, bbar3, bcar); ScanResult> bResult = jedis.hscan(bfoo, SCAN_POINTER_START_BINARY, params); assertFalse(bResult.getResult().isEmpty()); assertThat( bResult.getResult().stream().map(Map.Entry::getKey) .map(a -> Arrays.copyOfRange(a, 0, 4)).map(Arrays::toString).collect(Collectors.toSet()), containsInAnyOrder(Arrays.toString(bbar))); assertThat( bResult.getResult().stream().map(Map.Entry::getValue) .map(a -> Arrays.copyOfRange(a, 0, 4)).map(Arrays::toString).collect(Collectors.toSet()), containsInAnyOrder(Arrays.toString(bcar))); } @Test @SinceRedisVersion(value = "7.4.0", message = "NOVALUES flag (since Redis 7.4)") public void hscanNoValues() { jedis.hset("foo", "b", "y"); jedis.hset("foo", "a", "x"); ScanResult result = jedis.hscanNoValues("foo", SCAN_POINTER_START); assertEquals(SCAN_POINTER_START, result.getCursor()); assertEquals(2, result.getResult().size()); assertThat(result.getResult(), containsInAnyOrder("a", "b")); // binary jedis.hset(bfoo, bbar, bcar); ScanResult bResult = jedis.hscanNoValues(bfoo, SCAN_POINTER_START_BINARY); assertArrayEquals(SCAN_POINTER_START_BINARY, bResult.getCursorAsBytes()); assertEquals(1, bResult.getResult().size()); assertThat(bResult.getResult(), containsInAnyOrder(bbar)); } @Test @SinceRedisVersion(value = "7.4.0", message = "NOVALUES flag (since Redis 7.4)") public void hscanNoValuesMatch() { ScanParams params = new ScanParams(); params.match("a*"); jedis.hset("foo", "b", "y"); jedis.hset("foo", "a", "x"); jedis.hset("foo", "aa", "xx"); ScanResult result = jedis.hscanNoValues("foo", SCAN_POINTER_START, params); assertEquals(SCAN_POINTER_START, result.getCursor()); assertEquals(2, result.getResult().size()); assertThat(result.getResult(), containsInAnyOrder("a", "aa")); // binary params = new ScanParams(); params.match(bbarstar); jedis.hset(bfoo, bbar, bcar); jedis.hset(bfoo, bbar1, bcar); jedis.hset(bfoo, bbar2, bcar); jedis.hset(bfoo, bbar3, bcar); ScanResult bResult = jedis.hscanNoValues(bfoo, SCAN_POINTER_START_BINARY, params); assertArrayEquals(SCAN_POINTER_START_BINARY, bResult.getCursorAsBytes()); assertEquals(4, bResult.getResult().size()); assertThat(bResult.getResult(), containsInAnyOrder(bbar, bbar1, bbar2, bbar3)); } @Test @SinceRedisVersion(value = "7.4.0", message = "NOVALUES flag (since Redis 7.4)") public void hscanNoValuesCount() { ScanParams params = new ScanParams(); params.count(2); for (int i = 0; i < 10; i++) { jedis.hset("foo", "a" + i, "a" + i); } ScanResult result = jedis.hscanNoValues("foo", SCAN_POINTER_START, params); assertFalse(result.getResult().isEmpty()); assertThat( result.getResult().stream().map(s -> s.substring(0, 1)).collect(Collectors.toSet()), containsInAnyOrder("a")); // binary params = new ScanParams(); params.count(2); jedis.hset(bfoo, bbar, bcar); jedis.hset(bfoo, bbar1, bcar); jedis.hset(bfoo, bbar2, bcar); jedis.hset(bfoo, bbar3, bcar); ScanResult bResult = jedis.hscanNoValues(bfoo, SCAN_POINTER_START_BINARY, params); assertFalse(bResult.getResult().isEmpty()); assertThat( bResult.getResult().stream() .map(a -> Arrays.copyOfRange(a, 0, 4)).map(Arrays::toString).collect(Collectors.toSet()), containsInAnyOrder(Arrays.toString(bbar))); } @Test public void testHstrLen_EmptyHash() { Long response = jedis.hstrlen("myhash", "k1"); assertEquals(0l, response.longValue()); } @Test public void testHstrLen() { Map values = new HashMap<>(); values.put("key", "value"); jedis.hmset("myhash", values); Long response = jedis.hstrlen("myhash", "key"); assertEquals(5l, response.longValue()); } @Test public void testBinaryHstrLen() { Map values = new HashMap<>(); values.put(bbar, bcar); jedis.hmset(bfoo, values); Long response = jedis.hstrlen(bfoo, bbar); assertEquals(4l, response.longValue()); } @Test public void hrandfield() { assertNull(jedis.hrandfield("foo")); assertEquals(Collections.emptyList(), jedis.hrandfield("foo", 1)); assertEquals(Collections.emptyList(), jedis.hrandfieldWithValues("foo", 1)); assertEquals(Collections.emptyList(), jedis.hrandfieldWithValues("foo", -1)); Map hash = new LinkedHashMap<>(); hash.put("bar", "bar"); hash.put("car", "car"); hash.put("bar1", "bar1"); jedis.hset("foo", hash); assertTrue(hash.containsKey(jedis.hrandfield("foo"))); assertEquals(2, jedis.hrandfield("foo", 2).size()); List> actual = jedis.hrandfieldWithValues("foo", 2); assertEquals(2, actual.size()); actual.forEach(e -> assertEquals(hash.get(e.getKey()), e.getValue())); actual = jedis.hrandfieldWithValues("foo", 5); assertEquals(3, actual.size()); actual.forEach(e -> assertEquals(hash.get(e.getKey()), e.getValue())); actual = jedis.hrandfieldWithValues("foo", -5); assertEquals(5, actual.size()); actual.forEach(e -> assertEquals(hash.get(e.getKey()), e.getValue())); // binary assertNull(jedis.hrandfield(bfoo)); assertEquals(Collections.emptyList(), jedis.hrandfield(bfoo, 1)); assertEquals(Collections.emptyList(), jedis.hrandfieldWithValues(bfoo, 1)); assertEquals(Collections.emptyList(), jedis.hrandfieldWithValues(bfoo, -1)); Map bhash = new JedisByteHashMap(); bhash.put(bbar, bbar); bhash.put(bcar, bcar); bhash.put(bbar1, bbar1); jedis.hset(bfoo, bhash); assertTrue(bhash.containsKey(jedis.hrandfield(bfoo))); assertEquals(2, jedis.hrandfield(bfoo, 2).size()); List> bactual = jedis.hrandfieldWithValues(bfoo, 2); assertEquals(2, bactual.size()); bactual.forEach(e -> assertArrayEquals(bhash.get(e.getKey()), e.getValue())); bactual = jedis.hrandfieldWithValues(bfoo, 5); assertEquals(3, bactual.size()); bactual.forEach(e -> assertArrayEquals(bhash.get(e.getKey()), e.getValue())); bactual = jedis.hrandfieldWithValues(bfoo, -5); assertEquals(5, bactual.size()); bactual.forEach(e -> assertArrayEquals(bhash.get(e.getKey()), e.getValue())); } @Test @SinceRedisVersion("7.4.0") public void hexpireAndHttl() { long seconds1 = 20; long seconds2 = 10; jedis.hset("foo", "bar", "car"); jedis.hset("foo", "bare", "care"); assertEquals(asList(1L, -2L), jedis.hexpire("foo", seconds1, "bar", "bared")); jedis.hset("foo", "bared", "cared"); assertEquals(asList(0L, 1L), jedis.hexpire("foo", seconds2, ExpiryOption.NX, "bar", "bared")); assertThat(jedis.httl("foo", "bar", "bare", "bared"), contains(greaterThanOrEqualTo(seconds1 - 1), equalTo(-1L), both(lessThanOrEqualTo(seconds2)).and(greaterThanOrEqualTo(seconds2 - 1)))); } @Test @SinceRedisVersion("7.4.0") public void hexpireAndHttlBinary() { long seconds1 = 20; long seconds2 = 10; jedis.hset(bfoo, bbar1, bcar); jedis.hset(bfoo, bbar2, bcar); assertEquals(asList(1L, -2L), jedis.hexpire(bfoo, seconds1, bbar1, bbar3)); jedis.hset(bfoo, bbar3, bcar); assertEquals(asList(0L, 1L), jedis.hexpire(bfoo, seconds2, ExpiryOption.NX, bbar1, bbar3)); assertThat(jedis.httl(bfoo, bbar1, bbar2, bbar3), contains(greaterThanOrEqualTo(seconds1 - 1), equalTo(-1L), both(lessThanOrEqualTo(seconds2)).and(greaterThanOrEqualTo(seconds2 - 1)))); } @Test @SinceRedisVersion("7.4.0") public void hpexpireAndHpttl() { long millis1 = 20_000; long millis2 = 10_000; jedis.hset("foo", "bar", "car"); assertEquals(asList(1L, -2L), jedis.hpexpire("foo", millis1, "bar", "bared")); jedis.hset("foo", "bared", "cared"); assertEquals(asList(1L, 0L), jedis.hpexpire("foo", millis2, ExpiryOption.XX, "bar", "bared")); assertThat(jedis.hpttl("foo", "bar", "bare", "bared"), contains(both(lessThanOrEqualTo(millis2)).and(greaterThan(millis2 - 1000)), equalTo(-2L), equalTo(-1L))); } @Test @SinceRedisVersion("7.4.0") public void hpexpireAndHpttlBinary() { long millis1 = 20_000; long millis2 = 10_000; jedis.hset(bfoo, bbar1, bcar); assertEquals(asList(1L, -2L), jedis.hpexpire(bfoo, millis1, bbar1, bbar3)); jedis.hset(bfoo, bbar3, bcar); assertEquals(asList(1L, 0L), jedis.hpexpire(bfoo, millis2, ExpiryOption.XX, bbar1, bbar3)); assertThat(jedis.hpttl(bfoo, bbar1, bbar2, bbar3), contains(both(lessThanOrEqualTo(millis2)).and(greaterThan(millis2 - 1000)), equalTo(-2L), equalTo(-1L))); } @Test @SinceRedisVersion("7.4.0") public void hexpireAtAndExpireTime() { long currSeconds = System.currentTimeMillis() / 1000; long seconds1 = currSeconds + 20; long seconds2 = currSeconds + 10; jedis.hset("foo", "bar", "car"); jedis.hset("foo", "bare", "care"); assertEquals(asList(1L, -2L), jedis.hexpireAt("foo", seconds1, "bar", "bared")); jedis.hset("foo", "bared", "cared"); assertEquals(asList(1L, 1L), jedis.hexpireAt("foo", seconds2, ExpiryOption.LT, "bar", "bared")); assertThat(jedis.hexpireTime("foo", "bar", "bare", "bared"), contains(both(lessThanOrEqualTo(seconds2)).and(greaterThanOrEqualTo(seconds2 - 1)), equalTo(-1L), both(lessThanOrEqualTo(seconds2)).and(greaterThanOrEqualTo(seconds2 - 1)))); } @Test @SinceRedisVersion("7.4.0") public void hexpireAtAndExpireTimeBinary() { long currSeconds = System.currentTimeMillis() / 1000; long seconds1 = currSeconds + 20; long seconds2 = currSeconds + 10; jedis.hset(bfoo, bbar1, bcar); jedis.hset(bfoo, bbar2, bcar); assertEquals(asList(1L, -2L), jedis.hexpireAt(bfoo, seconds1, bbar1, bbar3)); jedis.hset(bfoo, bbar3, bcar); assertEquals(asList(1L, 1L), jedis.hexpireAt(bfoo, seconds2, ExpiryOption.LT, bbar1, bbar3)); assertThat(jedis.hexpireTime(bfoo, bbar1, bbar2, bbar3), contains(both(lessThanOrEqualTo(seconds2)).and(greaterThanOrEqualTo(seconds2 - 1)), equalTo(-1L), both(lessThanOrEqualTo(seconds2)).and(greaterThanOrEqualTo(seconds2 - 1)))); } @Test @SinceRedisVersion("7.4.0") public void hpexpireAtAndPexpireTime() { long currMillis = System.currentTimeMillis(); long unixMillis = currMillis + 20_000; jedis.hset("foo", "bar", "car"); assertEquals(asList(1L, -2L), jedis.hpexpireAt("foo", unixMillis - 100, "bar", "bared")); jedis.hset("foo", "bared", "cared"); assertEquals(asList(1L, 0L), jedis.hpexpireAt("foo", unixMillis, ExpiryOption.GT, "bar", "bared")); assertThat(jedis.hpexpireTime("foo", "bar", "bare", "bared"), contains(equalTo(unixMillis), equalTo(-2L), equalTo(-1L))); } @Test @SinceRedisVersion("7.4.0") public void hpexpireAtAndPexpireTimeBinary() { long currMillis = System.currentTimeMillis(); long unixMillis = currMillis + 20_000; jedis.hset(bfoo, bbar1, bcar); assertEquals(asList(1L, -2L), jedis.hpexpireAt(bfoo, unixMillis - 100, bbar1, bbar3)); jedis.hset(bfoo, bbar3, bcar); assertEquals(asList(1L, 0L), jedis.hpexpireAt(bfoo, unixMillis, ExpiryOption.GT, bbar1, bbar3)); assertThat(jedis.hpexpireTime(bfoo, bbar1, bbar2, bbar3), contains(equalTo(unixMillis), equalTo(-2L), equalTo(-1L))); } @Test @SinceRedisVersion("7.4.0") public void hpersist() { long seconds = 20; jedis.hset("foo", "bar", "car"); jedis.hset("foo", "bare", "care"); assertEquals(asList(1L, -2L), jedis.hexpire("foo", seconds, "bar", "bared")); assertEquals(asList(1L, -1L, -2L), jedis.hpersist("foo", "bar", "bare", "bared")); assertThat(jedis.httl("foo", "bar", "bare", "bared"), contains(equalTo(-1L), equalTo(-1L), equalTo(-2L))); } @Test @SinceRedisVersion("7.4.0") public void hpersistBinary() { long seconds = 20; jedis.hset(bfoo, bbar1, bcar); jedis.hset(bfoo, bbar2, bcar); assertEquals(asList(1L, -2L), jedis.hexpire(bfoo, seconds, bbar1, bbar3)); assertEquals(asList(1L, -1L, -2L), jedis.hpersist(bfoo, bbar1, bbar2, bbar3)); assertThat(jedis.httl(bfoo, bbar1, bbar2, bbar3), contains(equalTo(-1L), equalTo(-1L), equalTo(-2L))); } } ================================================ FILE: src/test/java/redis/clients/jedis/commands/unified/HotkeysCommandsTestBase.java ================================================ package redis.clients.jedis.commands.unified; import io.redis.test.annotations.ConditionalOnEnv; import io.redis.test.annotations.EnabledOnCommand; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Tag; import redis.clients.jedis.RedisProtocol; import redis.clients.jedis.util.TestEnvUtil; @Tag("integration") @EnabledOnCommand("HOTKEYS") @ConditionalOnEnv(value = TestEnvUtil.ENV_OSS_DOCKER, enabled = true) public abstract class HotkeysCommandsTestBase extends UnifiedJedisCommandsTestBase { public HotkeysCommandsTestBase(RedisProtocol protocol) { super(protocol); } @BeforeEach void setUp() { clearState(); } @AfterEach void tearDown() { clearState(); } private void clearState() { if (jedis != null) { jedis.flushAll(); jedis.hotkeysStop(); jedis.hotkeysReset(); } } } ================================================ FILE: src/test/java/redis/clients/jedis/commands/unified/HyperLogLogCommandsTestBase.java ================================================ package redis.clients.jedis.commands.unified; import io.redis.test.annotations.ConditionalOnEnv; import org.junit.jupiter.api.Test; import redis.clients.jedis.RedisProtocol; import redis.clients.jedis.util.SafeEncoder; import redis.clients.jedis.util.TestEnvUtil; import static org.junit.jupiter.api.Assertions.assertEquals; public abstract class HyperLogLogCommandsTestBase extends UnifiedJedisCommandsTestBase { public HyperLogLogCommandsTestBase(RedisProtocol protocol) { super(protocol); } @Test public void pfadd() { long status = jedis.pfadd("foo", "a"); assertEquals(1, status); status = jedis.pfadd("foo", "a"); assertEquals(0, status); } @Test public void pfaddBinary() { byte[] bFoo = SafeEncoder.encode("foo"); byte[] bBar = SafeEncoder.encode("bar"); byte[] bBar2 = SafeEncoder.encode("bar2"); long status = jedis.pfadd(bFoo, bBar, bBar2); assertEquals(1, status); status = jedis.pfadd(bFoo, bBar, bBar2); assertEquals(0, status); } @Test public void pfcount() { long status = jedis.pfadd("hll", "foo", "bar", "zap"); assertEquals(1, status); status = jedis.pfadd("hll", "zap", "zap", "zap"); assertEquals(0, status); status = jedis.pfadd("hll", "foo", "bar"); assertEquals(0, status); status = jedis.pfcount("hll"); assertEquals(3, status); } @Test @ConditionalOnEnv(value = TestEnvUtil.ENV_REDIS_ENTERPRISE, enabled = false) public void pfcounts() { long status = jedis.pfadd("hll_1", "foo", "bar", "zap"); assertEquals(1, status); status = jedis.pfadd("hll_2", "foo", "bar", "zap"); assertEquals(1, status); status = jedis.pfadd("hll_3", "foo", "bar", "baz"); assertEquals(1, status); status = jedis.pfcount("hll_1"); assertEquals(3, status); status = jedis.pfcount("hll_2"); assertEquals(3, status); status = jedis.pfcount("hll_3"); assertEquals(3, status); status = jedis.pfcount("hll_1", "hll_2"); assertEquals(3, status); status = jedis.pfcount("hll_1", "hll_2", "hll_3"); assertEquals(4, status); } @Test public void pfcountBinary() { byte[] bHll = SafeEncoder.encode("hll"); byte[] bFoo = SafeEncoder.encode("foo"); byte[] bBar = SafeEncoder.encode("bar"); byte[] bZap = SafeEncoder.encode("zap"); long status = jedis.pfadd(bHll, bFoo, bBar, bZap); assertEquals(1, status); status = jedis.pfadd(bHll, bZap, bZap, bZap); assertEquals(0, status); status = jedis.pfadd(bHll, bFoo, bBar); assertEquals(0, status); status = jedis.pfcount(bHll); assertEquals(3, status); } @Test @ConditionalOnEnv(value = TestEnvUtil.ENV_REDIS_ENTERPRISE, enabled = false) public void pfmerge() { long status = jedis.pfadd("hll1", "foo", "bar", "zap", "a"); assertEquals(1, status); status = jedis.pfadd("hll2", "a", "b", "c", "foo"); assertEquals(1, status); String mergeStatus = jedis.pfmerge("hll3", "hll1", "hll2"); assertEquals("OK", mergeStatus); status = jedis.pfcount("hll3"); assertEquals(6, status); } @Test @ConditionalOnEnv(value = TestEnvUtil.ENV_REDIS_ENTERPRISE, enabled = false) public void pfmergeBinary() { byte[] bHll1 = SafeEncoder.encode("hll1"); byte[] bHll2 = SafeEncoder.encode("hll2"); byte[] bHll3 = SafeEncoder.encode("hll3"); byte[] bFoo = SafeEncoder.encode("foo"); byte[] bBar = SafeEncoder.encode("bar"); byte[] bZap = SafeEncoder.encode("zap"); byte[] bA = SafeEncoder.encode("a"); byte[] bB = SafeEncoder.encode("b"); byte[] bC = SafeEncoder.encode("c"); long status = jedis.pfadd(bHll1, bFoo, bBar, bZap, bA); assertEquals(1, status); status = jedis.pfadd(bHll2, bA, bB, bC, bFoo); assertEquals(1, status); String mergeStatus = jedis.pfmerge(bHll3, bHll1, bHll2); assertEquals("OK", mergeStatus); status = jedis.pfcount(bHll3); assertEquals(6, status); } } ================================================ FILE: src/test/java/redis/clients/jedis/commands/unified/ListCommandsTestBase.java ================================================ package redis.clients.jedis.commands.unified; import static org.junit.jupiter.api.Assertions.assertArrayEquals; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertNotNull; import static org.junit.jupiter.api.Assertions.assertNull; import static org.junit.jupiter.api.Assertions.fail; import static redis.clients.jedis.util.AssertUtil.assertByteArrayListEquals; import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; import java.util.List; import io.redis.test.annotations.ConditionalOnEnv; import io.redis.test.annotations.SinceRedisVersion; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Tag; import org.junit.jupiter.api.Timeout; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import redis.clients.jedis.RedisProtocol; import redis.clients.jedis.args.ListPosition; import redis.clients.jedis.args.ListDirection; import redis.clients.jedis.exceptions.JedisDataException; import redis.clients.jedis.params.LPosParams; import redis.clients.jedis.util.KeyValue; import redis.clients.jedis.util.TestEnvUtil; @Tag("integration") public abstract class ListCommandsTestBase extends UnifiedJedisCommandsTestBase { private final Logger logger = LoggerFactory.getLogger(getClass()); protected final byte[] bfoo = { 0x01, 0x02, 0x03, 0x04 }; protected final byte[] bfoo1 = { 0x01, 0x02, 0x03, 0x04, 0x05 }; protected final byte[] bbar = { 0x05, 0x06, 0x07, 0x08 }; protected final byte[] bcar = { 0x09, 0x0A, 0x0B, 0x0C }; protected final byte[] bA = { 0x0A }; protected final byte[] bB = { 0x0B }; protected final byte[] bC = { 0x0C }; protected final byte[] b1 = { 0x01 }; protected final byte[] b2 = { 0x02 }; protected final byte[] b3 = { 0x03 }; protected final byte[] bhello = { 0x04, 0x02 }; protected final byte[] bx = { 0x02, 0x04 }; protected final byte[] bdst = { 0x11, 0x12, 0x13, 0x14 }; public ListCommandsTestBase(RedisProtocol protocol) { super(protocol); } @Test public void rpush() { assertEquals(1, jedis.rpush("foo", "bar")); assertEquals(2, jedis.rpush("foo", "foo")); assertEquals(4, jedis.rpush("foo", "bar", "foo")); // Binary assertEquals(1, jedis.rpush(bfoo, bbar)); assertEquals(2, jedis.rpush(bfoo, bfoo)); assertEquals(4, jedis.rpush(bfoo, bbar, bfoo)); } @Test public void lpush() { assertEquals(1, jedis.lpush("foo", "bar")); assertEquals(2, jedis.lpush("foo", "foo")); assertEquals(4, jedis.lpush("foo", "bar", "foo")); // Binary assertEquals(1, jedis.lpush(bfoo, bbar)); assertEquals(2, jedis.lpush(bfoo, bfoo)); assertEquals(4, jedis.lpush(bfoo, bbar, bfoo)); } @Test public void llen() { assertEquals(0, jedis.llen("foo")); jedis.lpush("foo", "bar"); jedis.lpush("foo", "car"); assertEquals(2, jedis.llen("foo")); // Binary assertEquals(0, jedis.llen(bfoo)); jedis.lpush(bfoo, bbar); jedis.lpush(bfoo, bcar); assertEquals(2, jedis.llen(bfoo)); } @Test public void llenNotOnList() { try { jedis.set("foo", "bar"); jedis.llen("foo"); fail("JedisDataException expected"); } catch (final JedisDataException e) { } // Binary try { jedis.set(bfoo, bbar); jedis.llen(bfoo); fail("JedisDataException expected"); } catch (final JedisDataException e) { } } @Test public void lrange() { jedis.rpush("foo", "a"); jedis.rpush("foo", "b"); jedis.rpush("foo", "c"); List expected = new ArrayList(); expected.add("a"); expected.add("b"); expected.add("c"); List range = jedis.lrange("foo", 0, 2); assertEquals(expected, range); range = jedis.lrange("foo", 0, 20); assertEquals(expected, range); expected = new ArrayList(); expected.add("b"); expected.add("c"); range = jedis.lrange("foo", 1, 2); assertEquals(expected, range); range = jedis.lrange("foo", 2, 1); assertEquals(Collections. emptyList(), range); // Binary jedis.rpush(bfoo, bA); jedis.rpush(bfoo, bB); jedis.rpush(bfoo, bC); List bexpected = new ArrayList(); bexpected.add(bA); bexpected.add(bB); bexpected.add(bC); List brange = jedis.lrange(bfoo, 0, 2); assertByteArrayListEquals(bexpected, brange); brange = jedis.lrange(bfoo, 0, 20); assertByteArrayListEquals(bexpected, brange); bexpected = new ArrayList(); bexpected.add(bB); bexpected.add(bC); brange = jedis.lrange(bfoo, 1, 2); assertByteArrayListEquals(bexpected, brange); brange = jedis.lrange(bfoo, 2, 1); assertByteArrayListEquals(Collections. emptyList(), brange); } @Test public void ltrim() { jedis.lpush("foo", "1"); jedis.lpush("foo", "2"); jedis.lpush("foo", "3"); String status = jedis.ltrim("foo", 0, 1); List expected = new ArrayList(); expected.add("3"); expected.add("2"); assertEquals("OK", status); assertEquals(2, jedis.llen("foo")); assertEquals(expected, jedis.lrange("foo", 0, 100)); // Binary jedis.lpush(bfoo, b1); jedis.lpush(bfoo, b2); jedis.lpush(bfoo, b3); String bstatus = jedis.ltrim(bfoo, 0, 1); List bexpected = new ArrayList(); bexpected.add(b3); bexpected.add(b2); assertEquals("OK", bstatus); assertEquals(2, jedis.llen(bfoo)); assertByteArrayListEquals(bexpected, jedis.lrange(bfoo, 0, 100)); } @Test public void lset() { jedis.lpush("foo", "1"); jedis.lpush("foo", "2"); jedis.lpush("foo", "3"); List expected = new ArrayList(); expected.add("3"); expected.add("bar"); expected.add("1"); String status = jedis.lset("foo", 1, "bar"); assertEquals("OK", status); assertEquals(expected, jedis.lrange("foo", 0, 100)); // Binary jedis.lpush(bfoo, b1); jedis.lpush(bfoo, b2); jedis.lpush(bfoo, b3); List bexpected = new ArrayList(); bexpected.add(b3); bexpected.add(bbar); bexpected.add(b1); String bstatus = jedis.lset(bfoo, 1, bbar); assertEquals("OK", bstatus); assertByteArrayListEquals(bexpected, jedis.lrange(bfoo, 0, 100)); } @Test public void lindex() { jedis.lpush("foo", "1"); jedis.lpush("foo", "2"); jedis.lpush("foo", "3"); assertEquals("3", jedis.lindex("foo", 0)); assertNull(jedis.lindex("foo", 100)); // Binary jedis.lpush(bfoo, b1); jedis.lpush(bfoo, b2); jedis.lpush(bfoo, b3); assertArrayEquals(b3, jedis.lindex(bfoo, 0)); assertNull(jedis.lindex(bfoo, 100)); } @Test public void lrem() { jedis.lpush("foo", "hello"); jedis.lpush("foo", "hello"); jedis.lpush("foo", "x"); jedis.lpush("foo", "hello"); jedis.lpush("foo", "c"); jedis.lpush("foo", "b"); jedis.lpush("foo", "a"); List expected = new ArrayList(); expected.add("a"); expected.add("b"); expected.add("c"); expected.add("hello"); expected.add("x"); assertEquals(2, jedis.lrem("foo", -2, "hello")); assertEquals(expected, jedis.lrange("foo", 0, 1000)); assertEquals(0, jedis.lrem("bar", 100, "foo")); // Binary jedis.lpush(bfoo, bhello); jedis.lpush(bfoo, bhello); jedis.lpush(bfoo, bx); jedis.lpush(bfoo, bhello); jedis.lpush(bfoo, bC); jedis.lpush(bfoo, bB); jedis.lpush(bfoo, bA); List bexpected = new ArrayList(); bexpected.add(bA); bexpected.add(bB); bexpected.add(bC); bexpected.add(bhello); bexpected.add(bx); assertEquals(2, jedis.lrem(bfoo, -2, bhello)); assertByteArrayListEquals(bexpected, jedis.lrange(bfoo, 0, 1000)); assertEquals(0, jedis.lrem(bbar, 100, bfoo)); } @Test public void lpop() { assertNull(jedis.lpop("foo")); assertNull(jedis.lpop("foo", 0)); jedis.rpush("foo", "a"); jedis.rpush("foo", "b"); jedis.rpush("foo", "c"); assertEquals("a", jedis.lpop("foo")); assertEquals(Arrays.asList("b", "c"), jedis.lpop("foo", 10)); assertNull(jedis.lpop("foo")); assertNull(jedis.lpop("foo", 1)); // Binary assertNull(jedis.lpop(bfoo)); assertNull(jedis.lpop(bfoo, 0)); jedis.rpush(bfoo, bA); jedis.rpush(bfoo, bB); jedis.rpush(bfoo, bC); assertArrayEquals(bA, jedis.lpop(bfoo)); assertByteArrayListEquals(Arrays.asList(bB, bC), jedis.lpop(bfoo, 10)); assertNull(jedis.lpop(bfoo)); assertNull(jedis.lpop(bfoo, 1)); } @Test public void rpop() { assertNull(jedis.rpop("foo")); assertNull(jedis.rpop("foo", 0)); jedis.rpush("foo", "a"); jedis.rpush("foo", "b"); jedis.rpush("foo", "c"); assertEquals("c", jedis.rpop("foo")); assertEquals(Arrays.asList("b", "a"), jedis.rpop("foo", 10)); assertNull(jedis.rpop("foo")); assertNull(jedis.rpop("foo", 1)); // Binary assertNull(jedis.rpop(bfoo)); assertNull(jedis.rpop(bfoo, 0)); jedis.rpush(bfoo, bA); jedis.rpush(bfoo, bB); jedis.rpush(bfoo, bC); assertArrayEquals(bC, jedis.rpop(bfoo)); assertByteArrayListEquals(Arrays.asList(bB, bA), jedis.rpop(bfoo, 10)); assertNull(jedis.rpop(bfoo)); assertNull(jedis.rpop(bfoo, 1)); } @Test @ConditionalOnEnv(value = TestEnvUtil.ENV_REDIS_ENTERPRISE, enabled = false) public void rpoplpush() { jedis.rpush("foo", "a"); jedis.rpush("foo", "b"); jedis.rpush("foo", "c"); jedis.rpush("dst", "foo"); jedis.rpush("dst", "bar"); String element = jedis.rpoplpush("foo", "dst"); assertEquals("c", element); List srcExpected = new ArrayList(); srcExpected.add("a"); srcExpected.add("b"); List dstExpected = new ArrayList(); dstExpected.add("c"); dstExpected.add("foo"); dstExpected.add("bar"); assertEquals(srcExpected, jedis.lrange("foo", 0, 1000)); assertEquals(dstExpected, jedis.lrange("dst", 0, 1000)); // Binary jedis.rpush(bfoo, bA); jedis.rpush(bfoo, bB); jedis.rpush(bfoo, bC); jedis.rpush(bdst, bfoo); jedis.rpush(bdst, bbar); byte[] belement = jedis.rpoplpush(bfoo, bdst); assertArrayEquals(bC, belement); List bsrcExpected = new ArrayList(); bsrcExpected.add(bA); bsrcExpected.add(bB); List bdstExpected = new ArrayList(); bdstExpected.add(bC); bdstExpected.add(bfoo); bdstExpected.add(bbar); assertByteArrayListEquals(bsrcExpected, jedis.lrange(bfoo, 0, 1000)); assertByteArrayListEquals(bdstExpected, jedis.lrange(bdst, 0, 1000)); } @Test @ConditionalOnEnv(value = TestEnvUtil.ENV_REDIS_ENTERPRISE, enabled = false) public void blpop() throws InterruptedException { List result = jedis.blpop(1, "foo"); assertNull(result); jedis.lpush("foo", "bar"); result = jedis.blpop(1, "foo"); assertNotNull(result); assertEquals(2, result.size()); assertEquals("foo", result.get(0)); assertEquals("bar", result.get(1)); // Multi keys result = jedis.blpop(1, "foo", "foo1"); assertNull(result); jedis.lpush("foo", "bar"); jedis.lpush("foo1", "bar1"); result = jedis.blpop(1, "foo1", "foo"); assertNotNull(result); assertEquals(2, result.size()); assertEquals("foo1", result.get(0)); assertEquals("bar1", result.get(1)); // Binary jedis.lpush(bfoo, bbar); List bresult = jedis.blpop(1, bfoo); assertNotNull(bresult); assertEquals(2, bresult.size()); assertArrayEquals(bfoo, bresult.get(0)); assertArrayEquals(bbar, bresult.get(1)); // Binary Multi keys bresult = jedis.blpop(1, bfoo, bfoo1); assertNull(bresult); jedis.lpush(bfoo, bbar); jedis.lpush(bfoo1, bcar); bresult = jedis.blpop(1, bfoo, bfoo1); assertNotNull(bresult); assertEquals(2, bresult.size()); assertArrayEquals(bfoo, bresult.get(0)); assertArrayEquals(bbar, bresult.get(1)); } @Test @ConditionalOnEnv(value = TestEnvUtil.ENV_REDIS_ENTERPRISE, enabled = false) public void blpopDouble() throws InterruptedException { KeyValue result = jedis.blpop(0.1, "foo"); assertNull(result); jedis.lpush("foo", "bar"); result = jedis.blpop(3.2, "foo"); assertNotNull(result); assertEquals("foo", result.getKey()); assertEquals("bar", result.getValue()); // Multi keys result = jedis.blpop(0.18, "foo", "foo1"); assertNull(result); jedis.lpush("foo", "bar"); jedis.lpush("foo1", "bar1"); result = jedis.blpop(1d, "foo1", "foo"); assertNotNull(result); assertEquals("foo1", result.getKey()); assertEquals("bar1", result.getValue()); // Binary jedis.lpush(bfoo, bbar); KeyValue bresult = jedis.blpop(3.12, bfoo); assertNotNull(bresult); assertArrayEquals(bfoo, bresult.getKey()); assertArrayEquals(bbar, bresult.getValue()); // Binary Multi keys bresult = jedis.blpop(0.11, bfoo, bfoo1); assertNull(bresult); jedis.lpush(bfoo, bbar); jedis.lpush(bfoo1, bcar); bresult = jedis.blpop(1d, bfoo, bfoo1); assertNotNull(bresult); assertArrayEquals(bfoo, bresult.getKey()); assertArrayEquals(bbar, bresult.getValue()); } @Test @Timeout(5) public void blpopDoubleWithSleep() { KeyValue result = jedis.blpop(0.04, "foo"); assertNull(result); new Thread(() -> { try { Thread.sleep(30); } catch(InterruptedException e) { logger.error("", e); } jedis.lpush("foo", "bar"); }).start(); result = jedis.blpop(1.2, "foo"); assertNotNull(result); assertEquals("foo", result.getKey()); assertEquals("bar", result.getValue()); } @Test @ConditionalOnEnv(value = TestEnvUtil.ENV_REDIS_ENTERPRISE, enabled = false) public void brpop() throws InterruptedException { List result = jedis.brpop(1, "foo"); assertNull(result); jedis.lpush("foo", "bar"); result = jedis.brpop(1, "foo"); assertNotNull(result); assertEquals(2, result.size()); assertEquals("foo", result.get(0)); assertEquals("bar", result.get(1)); // Multi keys result = jedis.brpop(1, "foo", "foo1"); assertNull(result); jedis.lpush("foo", "bar"); jedis.lpush("foo1", "bar1"); result = jedis.brpop(1, "foo1", "foo"); assertNotNull(result); assertEquals(2, result.size()); assertEquals("foo1", result.get(0)); assertEquals("bar1", result.get(1)); // Binary jedis.lpush(bfoo, bbar); List bresult = jedis.brpop(1, bfoo); assertNotNull(bresult); assertEquals(2, bresult.size()); assertArrayEquals(bfoo, bresult.get(0)); assertArrayEquals(bbar, bresult.get(1)); // Binary Multi keys bresult = jedis.brpop(1, bfoo, bfoo1); assertNull(bresult); jedis.lpush(bfoo, bbar); jedis.lpush(bfoo1, bcar); bresult = jedis.brpop(1, bfoo, bfoo1); assertNotNull(bresult); assertEquals(2, bresult.size()); assertArrayEquals(bfoo, bresult.get(0)); assertArrayEquals(bbar, bresult.get(1)); } @Test @ConditionalOnEnv(value = TestEnvUtil.ENV_REDIS_ENTERPRISE, enabled = false) public void brpopDouble() throws InterruptedException { KeyValue result = jedis.brpop(0.1, "foo"); assertNull(result); jedis.lpush("foo", "bar"); result = jedis.brpop(3.2, "foo"); assertNotNull(result); assertEquals("foo", result.getKey()); assertEquals("bar", result.getValue()); // Multi keys result = jedis.brpop(0.18, "foo", "foo1"); assertNull(result); jedis.lpush("foo", "bar"); jedis.lpush("foo1", "bar1"); result = jedis.brpop(1d, "foo1", "foo"); assertNotNull(result); assertEquals("foo1", result.getKey()); assertEquals("bar1", result.getValue()); // Binary jedis.lpush(bfoo, bbar); KeyValue bresult = jedis.brpop(3.12, bfoo); assertNotNull(bresult); assertArrayEquals(bfoo, bresult.getKey()); assertArrayEquals(bbar, bresult.getValue()); // Binary Multi keys bresult = jedis.brpop(0.11, bfoo, bfoo1); assertNull(bresult); jedis.lpush(bfoo, bbar); jedis.lpush(bfoo1, bcar); bresult = jedis.brpop(1d, bfoo, bfoo1); assertNotNull(bresult); assertArrayEquals(bfoo, bresult.getKey()); assertArrayEquals(bbar, bresult.getValue()); } @Test @Timeout(5) public void brpopDoubleWithSleep() { KeyValue result = jedis.brpop(0.04, "foo"); assertNull(result); new Thread(() -> { try { Thread.sleep(30); } catch(InterruptedException e) { logger.error("", e); } jedis.lpush("foo", "bar"); }).start(); result = jedis.brpop(1.2, "foo"); assertNotNull(result); assertEquals("foo", result.getKey()); assertEquals("bar", result.getValue()); } @Test public void lpushx() { assertEquals(0, jedis.lpushx("foo", "bar")); jedis.lpush("foo", "a"); assertEquals(2, jedis.lpushx("foo", "b")); // Binary assertEquals(0, jedis.lpushx(bfoo, bbar)); jedis.lpush(bfoo, bA); assertEquals(2, jedis.lpushx(bfoo, bB)); } @Test public void rpushx() { assertEquals(0, jedis.rpushx("foo", "bar")); jedis.lpush("foo", "a"); assertEquals(2, jedis.rpushx("foo", "b")); // Binary assertEquals(0, jedis.rpushx(bfoo, bbar)); jedis.lpush(bfoo, bA); assertEquals(2, jedis.rpushx(bfoo, bB)); } @Test public void linsert() { assertEquals(0, jedis.linsert("foo", ListPosition.BEFORE, "bar", "car")); jedis.lpush("foo", "a"); assertEquals(2, jedis.linsert("foo", ListPosition.AFTER, "a", "b")); List expected = new ArrayList(); expected.add("a"); expected.add("b"); assertEquals(expected, jedis.lrange("foo", 0, 100)); assertEquals(-1, jedis.linsert("foo", ListPosition.BEFORE, "bar", "car")); // Binary assertEquals(0, jedis.linsert(bfoo, ListPosition.BEFORE, bbar, bcar)); jedis.lpush(bfoo, bA); assertEquals(2, jedis.linsert(bfoo, ListPosition.AFTER, bA, bB)); List bexpected = new ArrayList(); bexpected.add(bA); bexpected.add(bB); assertByteArrayListEquals(bexpected, jedis.lrange(bfoo, 0, 100)); assertEquals(-1, jedis.linsert(bfoo, ListPosition.BEFORE, bbar, bcar)); } @Test @ConditionalOnEnv(value = TestEnvUtil.ENV_REDIS_ENTERPRISE, enabled = false) public void brpoplpush() { new Thread(new Runnable() { @Override public void run() { try { Thread.sleep(100); } catch (InterruptedException e) { logger.error("", e); } jedis.lpush("foo", "a"); } }).start(); String element = jedis.brpoplpush("foo", "bar", 0); assertEquals("a", element); assertEquals(1, jedis.llen("bar")); assertEquals("a", jedis.lrange("bar", 0, -1).get(0)); // Binary new Thread(new Runnable() { @Override public void run() { try { Thread.sleep(100); } catch (InterruptedException e) { logger.error("", e); } jedis.lpush(bfoo, bA); } }).start(); byte[] belement = jedis.brpoplpush(bfoo, bbar, 0); assertArrayEquals(bA, belement); assertEquals(1, jedis.llen("bar")); assertArrayEquals(bA, jedis.lrange(bbar, 0, -1).get(0)); } @Test public void lpos() { jedis.rpush("foo", "a"); jedis.rpush("foo", "b"); jedis.rpush("foo", "c"); Long pos = jedis.lpos("foo", "b"); assertEquals(1, pos.intValue()); pos = jedis.lpos("foo", "d"); assertNull(pos); jedis.rpush("foo", "a"); jedis.rpush("foo", "b"); jedis.rpush("foo", "b"); pos = jedis.lpos("foo", "b", LPosParams.lPosParams()); assertEquals(1, pos.intValue()); pos = jedis.lpos("foo", "b", LPosParams.lPosParams().rank(3)); assertEquals(5, pos.intValue()); pos = jedis.lpos("foo", "b", LPosParams.lPosParams().rank(-2)); assertEquals(4, pos.intValue()); pos = jedis.lpos("foo", "b", LPosParams.lPosParams().rank(-5)); assertNull(pos); pos = jedis.lpos("foo", "b", LPosParams.lPosParams().rank(1).maxlen(2)); assertEquals(1, pos.intValue()); pos = jedis.lpos("foo", "b", LPosParams.lPosParams().rank(2).maxlen(2)); assertNull(pos); pos = jedis.lpos("foo", "b", LPosParams.lPosParams().rank(-2).maxlen(2)); assertEquals(4, pos.intValue()); List expected = new ArrayList(); expected.add(1L); expected.add(4L); expected.add(5L); List posList = jedis.lpos("foo", "b", LPosParams.lPosParams(), 2); assertEquals(expected.subList(0, 2), posList); posList = jedis.lpos("foo", "b", LPosParams.lPosParams(), 0); assertEquals(expected, posList); posList = jedis.lpos("foo", "b", LPosParams.lPosParams().rank(2), 0); assertEquals(expected.subList(1, 3), posList); posList = jedis.lpos("foo", "b", LPosParams.lPosParams().rank(2).maxlen(5), 0); assertEquals(expected.subList(1, 2), posList); Collections.reverse(expected); posList = jedis.lpos("foo", "b", LPosParams.lPosParams().rank(-2), 0); assertEquals(expected.subList(1, 3), posList); posList = jedis.lpos("foo", "b", LPosParams.lPosParams().rank(-1).maxlen(5), 2); assertEquals(expected.subList(0, 2), posList); // Binary jedis.rpush(bfoo, bA); jedis.rpush(bfoo, bB); jedis.rpush(bfoo, bC); pos = jedis.lpos(bfoo, bB); assertEquals(1, pos.intValue()); pos = jedis.lpos(bfoo, b3); assertNull(pos); jedis.rpush(bfoo, bA); jedis.rpush(bfoo, bB); jedis.rpush(bfoo, bA); pos = jedis.lpos(bfoo, bB, LPosParams.lPosParams().rank(2)); assertEquals(4, pos.intValue()); pos = jedis.lpos(bfoo, bB, LPosParams.lPosParams().rank(-2).maxlen(5)); assertEquals(1, pos.intValue()); expected.clear(); expected.add(0L); expected.add(3L); expected.add(5L); posList = jedis.lpos(bfoo, bA, LPosParams.lPosParams().maxlen(6), 0); assertEquals(expected, posList); posList = jedis.lpos(bfoo, bA, LPosParams.lPosParams().maxlen(6).rank(2), 1); assertEquals(expected.subList(1, 2), posList); } @Test @ConditionalOnEnv(value = TestEnvUtil.ENV_REDIS_ENTERPRISE, enabled = false) public void lmove() { jedis.rpush("foo", "bar1", "bar2", "bar3"); assertEquals("bar3", jedis.lmove("foo", "bar", ListDirection.RIGHT, ListDirection.LEFT)); assertEquals(Collections.singletonList("bar3"), jedis.lrange("bar", 0, -1)); assertEquals(Arrays.asList("bar1", "bar2"), jedis.lrange("foo", 0, -1)); // Binary jedis.rpush(bfoo, b1, b2, b3); assertArrayEquals(b3, jedis.lmove(bfoo, bbar, ListDirection.RIGHT, ListDirection.LEFT)); assertByteArrayListEquals(Collections.singletonList(b3), jedis.lrange(bbar, 0, -1)); assertByteArrayListEquals(Arrays.asList(b1, b2), jedis.lrange(bfoo, 0, -1)); } @Test @ConditionalOnEnv(value = TestEnvUtil.ENV_REDIS_ENTERPRISE, enabled = false) public void blmove() { new Thread(() -> { try { Thread.sleep(100); } catch (InterruptedException e) { logger.error("", e); } jedis.rpush("foo", "bar1", "bar2", "bar3"); }).start(); assertEquals("bar3", jedis.blmove("foo", "bar", ListDirection.RIGHT, ListDirection.LEFT, 0)); assertEquals(Collections.singletonList("bar3"), jedis.lrange("bar", 0, -1)); assertEquals(Arrays.asList("bar1", "bar2"), jedis.lrange("foo", 0, -1)); // Binary new Thread(() -> { try { Thread.sleep(100); } catch (InterruptedException e) { logger.error("", e); } jedis.rpush(bfoo, b1, b2, b3); }).start(); assertArrayEquals(b3, jedis.blmove(bfoo, bbar, ListDirection.RIGHT, ListDirection.LEFT, 0)); assertByteArrayListEquals(Collections.singletonList(b3), jedis.lrange(bbar, 0, -1)); assertByteArrayListEquals(Arrays.asList(b1, b2), jedis.lrange(bfoo, 0, -1)); } @Test @SinceRedisVersion(value="7.0.0") @ConditionalOnEnv(value = TestEnvUtil.ENV_REDIS_ENTERPRISE, enabled = false) public void lmpop() { String mylist1 = "mylist1"; String mylist2 = "mylist2"; // add elements to list jedis.lpush(mylist1, "one", "two", "three", "four", "five"); jedis.lpush(mylist2, "one", "two", "three", "four", "five"); KeyValue> elements = jedis.lmpop(ListDirection.LEFT, mylist1, mylist2); assertEquals(mylist1, elements.getKey()); assertEquals(1, elements.getValue().size()); elements = jedis.lmpop(ListDirection.LEFT, 5, mylist1, mylist2); assertEquals(mylist1, elements.getKey()); assertEquals(4, elements.getValue().size()); elements = jedis.lmpop(ListDirection.RIGHT, 100, mylist1, mylist2); assertEquals(mylist2, elements.getKey()); assertEquals(5, elements.getValue().size()); elements = jedis.lmpop(ListDirection.RIGHT, mylist1, mylist2); assertNull(elements); } @Test @SinceRedisVersion(value="7.0.0") @ConditionalOnEnv(value = TestEnvUtil.ENV_REDIS_ENTERPRISE, enabled = false) public void blmpopSimple() { String mylist1 = "mylist1"; String mylist2 = "mylist2"; // add elements to list jedis.lpush(mylist1, "one", "two", "three", "four", "five"); jedis.lpush(mylist2, "one", "two", "three", "four", "five"); KeyValue> elements = jedis.blmpop(1L, ListDirection.LEFT, mylist1, mylist2); assertEquals(mylist1, elements.getKey()); assertEquals(1, elements.getValue().size()); elements = jedis.blmpop(1L, ListDirection.LEFT, 5, mylist1, mylist2); assertEquals(mylist1, elements.getKey()); assertEquals(4, elements.getValue().size()); elements = jedis.blmpop(1L, ListDirection.RIGHT, 100, mylist1, mylist2); assertEquals(mylist2, elements.getKey()); assertEquals(5, elements.getValue().size()); elements = jedis.blmpop(1L, ListDirection.RIGHT, mylist1, mylist2); assertNull(elements); } } ================================================ FILE: src/test/java/redis/clients/jedis/commands/unified/SetCommandsTestBase.java ================================================ package redis.clients.jedis.commands.unified; import static org.junit.jupiter.api.Assertions.assertArrayEquals; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertNull; import static org.junit.jupiter.api.Assertions.assertTrue; import static redis.clients.jedis.params.ScanParams.SCAN_POINTER_START; import static redis.clients.jedis.params.ScanParams.SCAN_POINTER_START_BINARY; import static redis.clients.jedis.util.AssertUtil.assertByteArrayCollectionContainsAll; import static redis.clients.jedis.util.AssertUtil.assertByteArraySetEquals; import static redis.clients.jedis.util.AssertUtil.assertCollectionContainsAll; import static redis.clients.jedis.util.ByteArrayUtil.byteArrayCollectionRemoveAll; import java.util.Arrays; import java.util.Comparator; import java.util.HashSet; import java.util.List; import java.util.Set; import io.redis.test.annotations.ConditionalOnEnv; import io.redis.test.annotations.SinceRedisVersion; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Tag; import redis.clients.jedis.RedisProtocol; import redis.clients.jedis.params.ScanParams; import redis.clients.jedis.resps.ScanResult; import redis.clients.jedis.util.TestEnvUtil; @Tag("integration") public abstract class SetCommandsTestBase extends UnifiedJedisCommandsTestBase { final byte[] bfoo = { 0x01, 0x02, 0x03, 0x04 }; final byte[] bbar = { 0x05, 0x06, 0x07, 0x08 }; final byte[] bcar = { 0x09, 0x0A, 0x0B, 0x0C }; final byte[] ba = { 0x0A }; final byte[] bb = { 0x0B }; final byte[] bc = { 0x0C }; final byte[] bd = { 0x0D }; final byte[] bx = { 0x42 }; final byte[] bbar1 = { 0x05, 0x06, 0x07, 0x08, 0x0A }; final byte[] bbar2 = { 0x05, 0x06, 0x07, 0x08, 0x0B }; final byte[] bbar3 = { 0x05, 0x06, 0x07, 0x08, 0x0C }; final byte[] bbarstar = { 0x05, 0x06, 0x07, 0x08, '*' }; public SetCommandsTestBase(RedisProtocol protocol) { super(protocol); } @Test public void sadd() { long status = jedis.sadd("foo", "a"); assertEquals(1, status); status = jedis.sadd("foo", "a"); assertEquals(0, status); long bstatus = jedis.sadd(bfoo, ba); assertEquals(1, bstatus); bstatus = jedis.sadd(bfoo, ba); assertEquals(0, bstatus); } @Test public void smembers() { jedis.sadd("foo", "a"); jedis.sadd("foo", "b"); Set expected = new HashSet(); expected.add("a"); expected.add("b"); Set members = jedis.smembers("foo"); assertEquals(expected, members); // Binary jedis.sadd(bfoo, ba); jedis.sadd(bfoo, bb); Set bexpected = new HashSet(); bexpected.add(bb); bexpected.add(ba); Set bmembers = jedis.smembers(bfoo); assertByteArraySetEquals(bexpected, bmembers); } @Test public void srem() { jedis.sadd("foo", "a"); jedis.sadd("foo", "b"); long status = jedis.srem("foo", "a"); Set expected = new HashSet(); expected.add("b"); assertEquals(1, status); assertEquals(expected, jedis.smembers("foo")); status = jedis.srem("foo", "bar"); assertEquals(0, status); // Binary jedis.sadd(bfoo, ba); jedis.sadd(bfoo, bb); long bstatus = jedis.srem(bfoo, ba); Set bexpected = new HashSet(); bexpected.add(bb); assertEquals(1, bstatus); assertByteArraySetEquals(bexpected, jedis.smembers(bfoo)); bstatus = jedis.srem(bfoo, bbar); assertEquals(0, bstatus); } @Test public void spop() { jedis.sadd("foo", "a"); jedis.sadd("foo", "b"); String member = jedis.spop("foo"); assertTrue("a".equals(member) || "b".equals(member)); assertEquals(1, jedis.smembers("foo").size()); member = jedis.spop("bar"); assertNull(member); // Binary jedis.sadd(bfoo, ba); jedis.sadd(bfoo, bb); byte[] bmember = jedis.spop(bfoo); assertTrue(Arrays.equals(ba, bmember) || Arrays.equals(bb, bmember)); assertEquals(1, jedis.smembers(bfoo).size()); bmember = jedis.spop(bbar); assertNull(bmember); } @Test public void spopWithCount() { jedis.sadd("foo", "a"); jedis.sadd("foo", "b"); jedis.sadd("foo", "c"); Set superSet = new HashSet(); superSet.add("c"); superSet.add("b"); superSet.add("a"); Set members = jedis.spop("foo", 2); assertEquals(2, members.size()); assertCollectionContainsAll(superSet, members); superSet.removeAll(members); members = jedis.spop("foo", 2); assertEquals(1, members.size()); assertEquals(superSet, members); assertTrue(jedis.spop("foo", 2).isEmpty()); // Binary jedis.sadd(bfoo, ba); jedis.sadd(bfoo, bb); jedis.sadd(bfoo, bc); Set bsuperSet = new HashSet(); bsuperSet.add(bc); bsuperSet.add(bb); bsuperSet.add(ba); Set bmembers = jedis.spop(bfoo, 2); assertEquals(2, bmembers.size()); assertByteArrayCollectionContainsAll(bsuperSet, bmembers); byteArrayCollectionRemoveAll(bsuperSet, bmembers); bmembers = jedis.spop(bfoo, 2); assertEquals(1, bmembers.size()); assertByteArraySetEquals(bsuperSet, bmembers); assertTrue(jedis.spop(bfoo, 2).isEmpty()); } @Test @ConditionalOnEnv(value = TestEnvUtil.ENV_REDIS_ENTERPRISE, enabled = false) public void smove() { jedis.sadd("foo", "a"); jedis.sadd("foo", "b"); jedis.sadd("bar", "c"); long status = jedis.smove("foo", "bar", "a"); Set expectedSrc = new HashSet(); expectedSrc.add("b"); Set expectedDst = new HashSet(); expectedDst.add("c"); expectedDst.add("a"); assertEquals(status, 1); assertEquals(expectedSrc, jedis.smembers("foo")); assertEquals(expectedDst, jedis.smembers("bar")); status = jedis.smove("foo", "bar", "a"); assertEquals(status, 0); // Binary jedis.sadd(bfoo, ba); jedis.sadd(bfoo, bb); jedis.sadd(bbar, bc); long bstatus = jedis.smove(bfoo, bbar, ba); Set bexpectedSrc = new HashSet(); bexpectedSrc.add(bb); Set bexpectedDst = new HashSet(); bexpectedDst.add(bc); bexpectedDst.add(ba); assertEquals(bstatus, 1); assertByteArraySetEquals(bexpectedSrc, jedis.smembers(bfoo)); assertByteArraySetEquals(bexpectedDst, jedis.smembers(bbar)); bstatus = jedis.smove(bfoo, bbar, ba); assertEquals(bstatus, 0); } @Test public void scard() { jedis.sadd("foo", "a"); jedis.sadd("foo", "b"); long card = jedis.scard("foo"); assertEquals(2, card); card = jedis.scard("bar"); assertEquals(0, card); // Binary jedis.sadd(bfoo, ba); jedis.sadd(bfoo, bb); long bcard = jedis.scard(bfoo); assertEquals(2, bcard); bcard = jedis.scard(bbar); assertEquals(0, bcard); } @Test public void sismember() { jedis.sadd("foo", "a"); jedis.sadd("foo", "b"); assertTrue(jedis.sismember("foo", "a")); assertFalse(jedis.sismember("foo", "c")); // Binary jedis.sadd(bfoo, ba); jedis.sadd(bfoo, bb); assertTrue(jedis.sismember(bfoo, ba)); assertFalse(jedis.sismember(bfoo, bc)); } @Test public void smismember() { jedis.sadd("foo", "a", "b"); assertEquals(Arrays.asList(true, false), jedis.smismember("foo", "a", "c")); // Binary jedis.sadd(bfoo, ba, bb); assertEquals(Arrays.asList(true, false), jedis.smismember(bfoo, ba, bc)); } @Test @ConditionalOnEnv(value = TestEnvUtil.ENV_REDIS_ENTERPRISE, enabled = false) public void sinter() { jedis.sadd("foo", "a"); jedis.sadd("foo", "b"); jedis.sadd("bar", "b"); jedis.sadd("bar", "c"); Set expected = new HashSet(); expected.add("b"); Set intersection = jedis.sinter("foo", "bar"); assertEquals(expected, intersection); // Binary jedis.sadd(bfoo, ba); jedis.sadd(bfoo, bb); jedis.sadd(bbar, bb); jedis.sadd(bbar, bc); Set bexpected = new HashSet(); bexpected.add(bb); Set bintersection = jedis.sinter(bfoo, bbar); assertByteArraySetEquals(bexpected, bintersection); } @Test @ConditionalOnEnv(value = TestEnvUtil.ENV_REDIS_ENTERPRISE, enabled = false) public void sinterstore() { jedis.sadd("foo", "a"); jedis.sadd("foo", "b"); jedis.sadd("bar", "b"); jedis.sadd("bar", "c"); Set expected = new HashSet(); expected.add("b"); long status = jedis.sinterstore("car", "foo", "bar"); assertEquals(1, status); assertEquals(expected, jedis.smembers("car")); // Binary jedis.sadd(bfoo, ba); jedis.sadd(bfoo, bb); jedis.sadd(bbar, bb); jedis.sadd(bbar, bc); Set bexpected = new HashSet(); bexpected.add(bb); long bstatus = jedis.sinterstore(bcar, bfoo, bbar); assertEquals(1, bstatus); assertByteArraySetEquals(bexpected, jedis.smembers(bcar)); } @Test @SinceRedisVersion(value="7.0.0") @ConditionalOnEnv(value = TestEnvUtil.ENV_REDIS_ENTERPRISE, enabled = false) public void sintercard() { jedis.sadd("foo", "a"); jedis.sadd("foo", "b"); jedis.sadd("bar", "a"); jedis.sadd("bar", "b"); jedis.sadd("bar", "c"); long card = jedis.sintercard("foo", "bar"); assertEquals(2, card); long limitedCard = jedis.sintercard(1, "foo", "bar"); assertEquals(1, limitedCard); // Binary jedis.sadd(bfoo, ba); jedis.sadd(bfoo, bb); jedis.sadd(bbar, ba); jedis.sadd(bbar, bb); jedis.sadd(bbar, bc); long bcard = jedis.sintercard(bfoo, bbar); assertEquals(2, bcard); long blimitedCard = jedis.sintercard(1, bfoo, bbar); assertEquals(1, blimitedCard); } @Test @ConditionalOnEnv(value = TestEnvUtil.ENV_REDIS_ENTERPRISE, enabled = false) public void sunion() { jedis.sadd("foo", "a"); jedis.sadd("foo", "b"); jedis.sadd("bar", "b"); jedis.sadd("bar", "c"); Set expected = new HashSet(); expected.add("a"); expected.add("b"); expected.add("c"); Set union = jedis.sunion("foo", "bar"); assertEquals(expected, union); // Binary jedis.sadd(bfoo, ba); jedis.sadd(bfoo, bb); jedis.sadd(bbar, bb); jedis.sadd(bbar, bc); Set bexpected = new HashSet(); bexpected.add(bb); bexpected.add(bc); bexpected.add(ba); Set bunion = jedis.sunion(bfoo, bbar); assertByteArraySetEquals(bexpected, bunion); } @Test @ConditionalOnEnv(value = TestEnvUtil.ENV_REDIS_ENTERPRISE, enabled = false) public void sunionstore() { jedis.sadd("foo", "a"); jedis.sadd("foo", "b"); jedis.sadd("bar", "b"); jedis.sadd("bar", "c"); Set expected = new HashSet(); expected.add("a"); expected.add("b"); expected.add("c"); long status = jedis.sunionstore("car", "foo", "bar"); assertEquals(3, status); assertEquals(expected, jedis.smembers("car")); // Binary jedis.sadd(bfoo, ba); jedis.sadd(bfoo, bb); jedis.sadd(bbar, bb); jedis.sadd(bbar, bc); Set bexpected = new HashSet(); bexpected.add(bb); bexpected.add(bc); bexpected.add(ba); long bstatus = jedis.sunionstore(bcar, bfoo, bbar); assertEquals(3, bstatus); assertByteArraySetEquals(bexpected, jedis.smembers(bcar)); } @Test @ConditionalOnEnv(value = TestEnvUtil.ENV_REDIS_ENTERPRISE, enabled = false) public void sdiff() { jedis.sadd("foo", "x"); jedis.sadd("foo", "a"); jedis.sadd("foo", "b"); jedis.sadd("foo", "c"); jedis.sadd("bar", "c"); jedis.sadd("car", "a"); jedis.sadd("car", "d"); Set expected = new HashSet(); expected.add("x"); expected.add("b"); Set diff = jedis.sdiff("foo", "bar", "car"); assertEquals(expected, diff); // Binary jedis.sadd(bfoo, bx); jedis.sadd(bfoo, ba); jedis.sadd(bfoo, bb); jedis.sadd(bfoo, bc); jedis.sadd(bbar, bc); jedis.sadd(bcar, ba); jedis.sadd(bcar, bd); Set bexpected = new HashSet(); bexpected.add(bb); bexpected.add(bx); Set bdiff = jedis.sdiff(bfoo, bbar, bcar); assertByteArraySetEquals(bexpected, bdiff); } @Test @ConditionalOnEnv(value = TestEnvUtil.ENV_REDIS_ENTERPRISE, enabled = false) public void sdiffstore() { jedis.sadd("foo", "x"); jedis.sadd("foo", "a"); jedis.sadd("foo", "b"); jedis.sadd("foo", "c"); jedis.sadd("bar", "c"); jedis.sadd("car", "a"); jedis.sadd("car", "d"); Set expected = new HashSet(); expected.add("x"); expected.add("b"); long status = jedis.sdiffstore("tar", "foo", "bar", "car"); assertEquals(2, status); assertEquals(expected, jedis.smembers("tar")); // Binary jedis.sadd(bfoo, bx); jedis.sadd(bfoo, ba); jedis.sadd(bfoo, bb); jedis.sadd(bfoo, bc); jedis.sadd(bbar, bc); jedis.sadd(bcar, ba); jedis.sadd(bcar, bd); Set bexpected = new HashSet(); bexpected.add(bx); bexpected.add(bb); long bstatus = jedis.sdiffstore("tar".getBytes(), bfoo, bbar, bcar); assertEquals(2, bstatus); assertByteArraySetEquals(bexpected, jedis.smembers("tar".getBytes())); } @Test public void srandmember() { jedis.sadd("foo", "a"); jedis.sadd("foo", "b"); String member = jedis.srandmember("foo"); assertTrue("a".equals(member) || "b".equals(member)); assertEquals(2, jedis.smembers("foo").size()); List members = jedis.srandmember("foo", 2); members.sort(Comparator.naturalOrder()); assertEquals( Arrays.asList("a", "b"), members); member = jedis.srandmember("bar"); assertNull(member); members = jedis.srandmember("bar", 2); assertEquals(0, members.size()); // Binary jedis.sadd(bfoo, ba); jedis.sadd(bfoo, bb); byte[] bmember = jedis.srandmember(bfoo); assertTrue(Arrays.equals(ba, bmember) || Arrays.equals(bb, bmember)); assertEquals(2, jedis.smembers(bfoo).size()); List bmembers = jedis.srandmember(bfoo, 2); assertEquals(2, bmembers.size()); bmember = jedis.srandmember(bbar); assertNull(bmember); members = jedis.srandmember("bbar", 2); assertEquals(0, members.size()); } @Test public void sscan() { jedis.sadd("foo", "a", "b"); ScanResult result = jedis.sscan("foo", SCAN_POINTER_START); assertEquals(SCAN_POINTER_START, result.getCursor()); assertFalse(result.getResult().isEmpty()); // binary jedis.sadd(bfoo, ba, bb); ScanResult bResult = jedis.sscan(bfoo, SCAN_POINTER_START_BINARY); assertArrayEquals(SCAN_POINTER_START_BINARY, bResult.getCursorAsBytes()); assertFalse(bResult.getResult().isEmpty()); } @Test public void sscanMatch() { ScanParams params = new ScanParams(); params.match("a*"); jedis.sadd("foo", "b", "a", "aa"); ScanResult result = jedis.sscan("foo", SCAN_POINTER_START, params); assertEquals(SCAN_POINTER_START, result.getCursor()); assertFalse(result.getResult().isEmpty()); // binary params = new ScanParams(); params.match(bbarstar); jedis.sadd(bfoo, bbar1, bbar2, bbar3); ScanResult bResult = jedis.sscan(bfoo, SCAN_POINTER_START_BINARY, params); assertArrayEquals(SCAN_POINTER_START_BINARY, bResult.getCursorAsBytes()); assertFalse(bResult.getResult().isEmpty()); } @Test public void sscanCount() { ScanParams params = new ScanParams(); params.count(2); jedis.sadd("foo", "a1", "a2", "a3", "a4", "a5"); ScanResult result = jedis.sscan("foo", SCAN_POINTER_START, params); assertFalse(result.getResult().isEmpty()); // binary params = new ScanParams(); params.count(2); jedis.sadd(bfoo, bbar1, bbar2, bbar3); ScanResult bResult = jedis.sscan(bfoo, SCAN_POINTER_START_BINARY, params); assertFalse(bResult.getResult().isEmpty()); } } ================================================ FILE: src/test/java/redis/clients/jedis/commands/unified/SortedSetCommandsTestBase.java ================================================ package redis.clients.jedis.commands.unified; import static java.util.Collections.singletonList; import static org.junit.jupiter.api.Assertions.assertArrayEquals; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertNotNull; import static org.junit.jupiter.api.Assertions.assertNull; import static redis.clients.jedis.params.ScanParams.SCAN_POINTER_START; import static redis.clients.jedis.params.ScanParams.SCAN_POINTER_START_BINARY; import static redis.clients.jedis.util.AssertUtil.assertByteArrayListEquals; import java.util.*; import io.redis.test.annotations.ConditionalOnEnv; import io.redis.test.annotations.SinceRedisVersion; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Tag; import redis.clients.jedis.RedisProtocol; import redis.clients.jedis.args.SortedSetOption; import redis.clients.jedis.params.*; import redis.clients.jedis.resps.ScanResult; import redis.clients.jedis.resps.Tuple; import redis.clients.jedis.util.AssertUtil; import redis.clients.jedis.util.KeyValue; import redis.clients.jedis.util.SafeEncoder; import redis.clients.jedis.util.TestEnvUtil; @Tag("integration") public abstract class SortedSetCommandsTestBase extends UnifiedJedisCommandsTestBase { final byte[] bfoo = { 0x01, 0x02, 0x03, 0x04 }; final byte[] bbar = { 0x05, 0x06, 0x07, 0x08 }; final byte[] bcar = { 0x09, 0x0A, 0x0B, 0x0C }; final byte[] ba = { 0x0A }; final byte[] bb = { 0x0B }; final byte[] bc = { 0x0C }; final byte[] bInclusiveB = { 0x5B, 0x0B }; final byte[] bExclusiveC = { 0x28, 0x0C }; final byte[] bLexMinusInf = { 0x2D }; final byte[] bLexPlusInf = { 0x2B }; final byte[] bbar1 = { 0x05, 0x06, 0x07, 0x08, 0x0A }; final byte[] bbar2 = { 0x05, 0x06, 0x07, 0x08, 0x0B }; final byte[] bbar3 = { 0x05, 0x06, 0x07, 0x08, 0x0C }; final byte[] bbarstar = { 0x05, 0x06, 0x07, 0x08, '*' }; public SortedSetCommandsTestBase(RedisProtocol protocol) { super(protocol); } @Test public void zadd() { assertEquals(1, jedis.zadd("foo", 1d, "a")); assertEquals(1, jedis.zadd("foo", 10d, "b")); assertEquals(1, jedis.zadd("foo", 0.1d, "c")); assertEquals(0, jedis.zadd("foo", 2d, "a")); // Binary assertEquals(1, jedis.zadd(bfoo, 1d, ba)); assertEquals(1, jedis.zadd(bfoo, 10d, bb)); assertEquals(1, jedis.zadd(bfoo, 0.1d, bc)); assertEquals(0, jedis.zadd(bfoo, 2d, ba)); } @Test public void zaddWithParams() { jedis.del("foo"); // xx: never add new member assertEquals(0L, jedis.zadd("foo", 1d, "a", ZAddParams.zAddParams().xx())); jedis.zadd("foo", 1d, "a"); // nx: never update current member assertEquals(0L, jedis.zadd("foo", 2d, "a", ZAddParams.zAddParams().nx())); assertEquals(Double.valueOf(1d), jedis.zscore("foo", "a")); Map scoreMembers = new HashMap(); scoreMembers.put("a", 2d); scoreMembers.put("b", 1d); // ch: return count of members not only added, but also updated assertEquals(2L, jedis.zadd("foo", scoreMembers, ZAddParams.zAddParams().ch())); // lt: only update existing elements if the new score is less than the current score. jedis.zadd("foo", 3d, "a", ZAddParams.zAddParams().lt()); assertEquals(Double.valueOf(2d), jedis.zscore("foo", "a")); jedis.zadd("foo", 1d, "a", ZAddParams.zAddParams().lt()); assertEquals(Double.valueOf(1d), jedis.zscore("foo", "a")); // gt: only update existing elements if the new score is greater than the current score. jedis.zadd("foo", 0d, "b", ZAddParams.zAddParams().gt()); assertEquals(Double.valueOf(1d), jedis.zscore("foo", "b")); jedis.zadd("foo", 2d, "b", ZAddParams.zAddParams().gt()); assertEquals(Double.valueOf(2d), jedis.zscore("foo", "b")); // incr: don't update already existing elements. assertNull(jedis.zaddIncr("foo", 1d, "b", ZAddParams.zAddParams().nx())); assertEquals(Double.valueOf(2d), jedis.zscore("foo", "b")); // incr: update elements that already exist. assertEquals(Double.valueOf(3d), jedis.zaddIncr("foo", 1d,"b", ZAddParams.zAddParams().xx())); assertEquals(Double.valueOf(3d), jedis.zscore("foo", "b")); // binary jedis.del(bfoo); // xx: never add new member assertEquals(0L, jedis.zadd(bfoo, 1d, ba, ZAddParams.zAddParams().xx())); jedis.zadd(bfoo, 1d, ba); // nx: never update current member assertEquals(0L, jedis.zadd(bfoo, 2d, ba, ZAddParams.zAddParams().nx())); assertEquals(Double.valueOf(1d), jedis.zscore(bfoo, ba)); Map binaryScoreMembers = new HashMap(); binaryScoreMembers.put(ba, 2d); binaryScoreMembers.put(bb, 1d); // ch: return count of members not only added, but also updated assertEquals(2L, jedis.zadd(bfoo, binaryScoreMembers, ZAddParams.zAddParams().ch())); // lt: only update existing elements if the new score is less than the current score. jedis.zadd(bfoo, 3d, ba, ZAddParams.zAddParams().lt()); assertEquals(Double.valueOf(2d), jedis.zscore(bfoo, ba)); jedis.zadd(bfoo, 1d, ba, ZAddParams.zAddParams().lt()); assertEquals(Double.valueOf(1d), jedis.zscore(bfoo, ba)); // gt: only update existing elements if the new score is greater than the current score. jedis.zadd(bfoo, 0d, bb, ZAddParams.zAddParams().gt()); assertEquals(Double.valueOf(1d), jedis.zscore(bfoo, bb)); jedis.zadd(bfoo, 2d, bb, ZAddParams.zAddParams().gt()); assertEquals(Double.valueOf(2d), jedis.zscore(bfoo, bb)); // incr: don't update already existing elements. assertNull(jedis.zaddIncr(bfoo, 1d, bb, ZAddParams.zAddParams().nx())); assertEquals(Double.valueOf(2d), jedis.zscore(bfoo, bb)); // incr: update elements that already exist. assertEquals(Double.valueOf(3d), jedis.zaddIncr(bfoo, 1d, bb, ZAddParams.zAddParams().xx())); assertEquals(Double.valueOf(3d), jedis.zscore(bfoo, bb)); } @Test public void zrange() { jedis.zadd("foo", 1d, "a"); jedis.zadd("foo", 10d, "b"); jedis.zadd("foo", 0.1d, "c"); jedis.zadd("foo", 2d, "a"); List expected = new ArrayList(); expected.add("c"); expected.add("a"); List range = jedis.zrange("foo", 0, 1); assertEquals(expected, range); expected.add("b"); range = jedis.zrange("foo", 0, 100); assertEquals(expected, range); // Binary jedis.zadd(bfoo, 1d, ba); jedis.zadd(bfoo, 10d, bb); jedis.zadd(bfoo, 0.1d, bc); jedis.zadd(bfoo, 2d, ba); List bexpected = new ArrayList(); bexpected.add(bc); bexpected.add(ba); List brange = jedis.zrange(bfoo, 0, 1); assertByteArrayListEquals(bexpected, brange); bexpected.add(bb); brange = jedis.zrange(bfoo, 0, 100); assertByteArrayListEquals(bexpected, brange); } @Test public void zrangeByLex() { jedis.zadd("foo", 1, "aa"); jedis.zadd("foo", 1, "c"); jedis.zadd("foo", 1, "bb"); jedis.zadd("foo", 1, "d"); List expected = new ArrayList(); expected.add("bb"); expected.add("c"); // exclusive aa ~ inclusive c assertEquals(expected, jedis.zrangeByLex("foo", "(aa", "[c")); expected.clear(); expected.add("bb"); expected.add("c"); // with LIMIT assertEquals(expected, jedis.zrangeByLex("foo", "-", "+", 1, 2)); } @Test public void zrangeByLexBinary() { // binary jedis.zadd(bfoo, 1, ba); jedis.zadd(bfoo, 1, bc); jedis.zadd(bfoo, 1, bb); List bExpected = new ArrayList(); bExpected.add(bb); assertByteArrayListEquals(bExpected, jedis.zrangeByLex(bfoo, bInclusiveB, bExclusiveC)); bExpected.clear(); bExpected.add(ba); bExpected.add(bb); // with LIMIT assertByteArrayListEquals(bExpected, jedis.zrangeByLex(bfoo, bLexMinusInf, bLexPlusInf, 0, 2)); } @Test public void zrevrangeByLex() { jedis.zadd("foo", 1, "aa"); jedis.zadd("foo", 1, "c"); jedis.zadd("foo", 1, "bb"); jedis.zadd("foo", 1, "d"); List expected = new ArrayList(); expected.add("c"); expected.add("bb"); // exclusive aa ~ inclusive c assertEquals(expected, jedis.zrevrangeByLex("foo", "[c", "(aa")); expected.clear(); expected.add("c"); expected.add("bb"); // with LIMIT assertEquals(expected, jedis.zrevrangeByLex("foo", "+", "-", 1, 2)); } @Test public void zrevrangeByLexBinary() { // binary jedis.zadd(bfoo, 1, ba); jedis.zadd(bfoo, 1, bc); jedis.zadd(bfoo, 1, bb); List bExpected = new ArrayList(); bExpected.add(bb); assertByteArrayListEquals(bExpected, jedis.zrevrangeByLex(bfoo, bExclusiveC, bInclusiveB)); bExpected.clear(); bExpected.add(bc); bExpected.add(bb); // with LIMIT assertByteArrayListEquals(bExpected, jedis.zrevrangeByLex(bfoo, bLexPlusInf, bLexMinusInf, 0, 2)); } @Test public void zrevrange() { jedis.zadd("foo", 1d, "a"); jedis.zadd("foo", 10d, "b"); jedis.zadd("foo", 0.1d, "c"); jedis.zadd("foo", 2d, "a"); List expected = new ArrayList(); expected.add("b"); expected.add("a"); List range = jedis.zrevrange("foo", 0, 1); assertEquals(expected, range); expected.add("c"); range = jedis.zrevrange("foo", 0, 100); assertEquals(expected, range); // Binary jedis.zadd(bfoo, 1d, ba); jedis.zadd(bfoo, 10d, bb); jedis.zadd(bfoo, 0.1d, bc); jedis.zadd(bfoo, 2d, ba); List bexpected = new ArrayList(); bexpected.add(bb); bexpected.add(ba); List brange = jedis.zrevrange(bfoo, 0, 1); assertByteArrayListEquals(bexpected, brange); bexpected.add(bc); brange = jedis.zrevrange(bfoo, 0, 100); assertByteArrayListEquals(bexpected, brange); } @Test public void zrangeParams() { jedis.zadd("foo", 1, "aa"); jedis.zadd("foo", 1, "c"); jedis.zadd("foo", 1, "bb"); jedis.zadd("foo", 1, "d"); List expected = new ArrayList(); expected.add("c"); expected.add("bb"); assertEquals(expected, jedis.zrange("foo", ZRangeParams.zrangeByLexParams("[c", "(aa").rev())); assertNotNull(jedis.zrangeWithScores("foo", ZRangeParams.zrangeByScoreParams(0, 1))); // Binary jedis.zadd(bfoo, 1, ba); jedis.zadd(bfoo, 1, bc); jedis.zadd(bfoo, 1, bb); List bExpected = new ArrayList(); bExpected.add(bb); assertByteArrayListEquals(bExpected, jedis.zrange(bfoo, ZRangeParams.zrangeByLexParams(bExclusiveC, bInclusiveB).rev())); assertNotNull(jedis.zrangeWithScores(bfoo, ZRangeParams.zrangeByScoreParams(0, 1).limit(0, 3))); } @Test public void zrangeParamsLongMinMax() { long min = 0; long max = 1; jedis.zadd("foo", 1, "a"); jedis.zadd("foo", 2, "b"); List expected = new ArrayList(); expected.add("b"); expected.add("a"); assertEquals(expected, jedis.zrange("foo", ZRangeParams.zrangeParams(min, max).rev())); } @Test @ConditionalOnEnv(value = TestEnvUtil.ENV_REDIS_ENTERPRISE, enabled = false) public void zrangestore() { jedis.zadd("foo", 1, "aa"); jedis.zadd("foo", 2, "c"); jedis.zadd("foo", 3, "bb"); long stored = jedis.zrangestore("bar", "foo", ZRangeParams.zrangeByScoreParams(1, 2)); assertEquals(2, stored); List range = jedis.zrange("bar", 0, -1); List expected = new ArrayList<>(); expected.add("aa"); expected.add("c"); assertEquals(expected, range); // Binary jedis.zadd(bfoo, 1d, ba); jedis.zadd(bfoo, 10d, bb); jedis.zadd(bfoo, 0.1d, bc); jedis.zadd(bfoo, 2d, ba); long bstored = jedis.zrangestore(bbar, bfoo, ZRangeParams.zrangeParams(0, 1).rev()); assertEquals(2, bstored); List brange = jedis.zrevrange(bbar, 0, 1); List bexpected = new ArrayList<>(); bexpected.add(bb); bexpected.add(ba); assertByteArrayListEquals(bexpected, brange); } @Test public void zrem() { jedis.zadd("foo", 1d, "a"); jedis.zadd("foo", 2d, "b"); assertEquals(1, jedis.zrem("foo", "a")); List expected = new ArrayList(); expected.add("b"); assertEquals(expected, jedis.zrange("foo", 0, 100)); assertEquals(0, jedis.zrem("foo", "bar")); // Binary jedis.zadd(bfoo, 1d, ba); jedis.zadd(bfoo, 2d, bb); assertEquals(1, jedis.zrem(bfoo, ba)); List bexpected = new ArrayList(); bexpected.add(bb); assertByteArrayListEquals(bexpected, jedis.zrange(bfoo, 0, 100)); assertEquals(0, jedis.zrem(bfoo, bbar)); } @Test public void zincrby() { jedis.zadd("foo", 1d, "a"); jedis.zadd("foo", 2d, "b"); assertEquals(3d, jedis.zincrby("foo", 2d, "a"), 0); List expected = new ArrayList(); expected.add("b"); expected.add("a"); assertEquals(expected, jedis.zrange("foo", 0, 100)); // Binary jedis.zadd(bfoo, 1d, ba); jedis.zadd(bfoo, 2d, bb); assertEquals(3d, jedis.zincrby(bfoo, 2d, ba), 0); List bexpected = new ArrayList(); bexpected.add(bb); bexpected.add(ba); assertByteArrayListEquals(bexpected, jedis.zrange(bfoo, 0, 100)); } @Test public void zincrbyWithParams() { jedis.del("foo"); // xx: never add new member assertNull(jedis.zincrby("foo", 2d, "a", ZIncrByParams.zIncrByParams().xx())); jedis.zadd("foo", 2d, "a"); // nx: never update current member assertNull(jedis.zincrby("foo", 1d, "a", ZIncrByParams.zIncrByParams().nx())); assertEquals(Double.valueOf(2d), jedis.zscore("foo", "a")); // Binary jedis.del(bfoo); // xx: never add new member assertNull(jedis.zincrby(bfoo, 2d, ba, ZIncrByParams.zIncrByParams().xx())); jedis.zadd(bfoo, 2d, ba); // nx: never update current member assertNull(jedis.zincrby(bfoo, 1d, ba, ZIncrByParams.zIncrByParams().nx())); assertEquals(Double.valueOf(2d), jedis.zscore(bfoo, ba)); } @Test public void zrank() { jedis.zadd("foo", 1d, "a"); jedis.zadd("foo", 2d, "b"); long rank = jedis.zrank("foo", "a"); assertEquals(0, rank); rank = jedis.zrank("foo", "b"); assertEquals(1, rank); assertNull(jedis.zrank("car", "b")); // Binary jedis.zadd(bfoo, 1d, ba); jedis.zadd(bfoo, 2d, bb); long brank = jedis.zrank(bfoo, ba); assertEquals(0, brank); brank = jedis.zrank(bfoo, bb); assertEquals(1, brank); assertNull(jedis.zrank(bcar, bb)); } @Test @SinceRedisVersion(value="7.2.0") public void zrankWithScore() { jedis.zadd("foo", 1d, "a"); jedis.zadd("foo", 2d, "b"); KeyValue keyValue = jedis.zrankWithScore("foo", "a"); assertEquals(Long.valueOf(0), keyValue.getKey()); assertEquals(Double.valueOf(1d), keyValue.getValue()); keyValue = jedis.zrankWithScore("foo", "b"); assertEquals(Long.valueOf(1), keyValue.getKey()); assertEquals(Double.valueOf(2d), keyValue.getValue()); assertNull(jedis.zrankWithScore("car", "b")); // Binary jedis.zadd(bfoo, 1d, ba); jedis.zadd(bfoo, 2d, bb); keyValue = jedis.zrankWithScore(bfoo, ba); assertEquals(Long.valueOf(0), keyValue.getKey()); assertEquals(Double.valueOf(1d), keyValue.getValue()); keyValue = jedis.zrankWithScore(bfoo, bb); assertEquals(Long.valueOf(1), keyValue.getKey()); assertEquals(Double.valueOf(2d), keyValue.getValue()); assertNull(jedis.zrankWithScore(bcar, bb)); } @Test public void zrevrank() { jedis.zadd("foo", 1d, "a"); jedis.zadd("foo", 2d, "b"); long rank = jedis.zrevrank("foo", "a"); assertEquals(1, rank); rank = jedis.zrevrank("foo", "b"); assertEquals(0, rank); // Binary jedis.zadd(bfoo, 1d, ba); jedis.zadd(bfoo, 2d, bb); long brank = jedis.zrevrank(bfoo, ba); assertEquals(1, brank); brank = jedis.zrevrank(bfoo, bb); assertEquals(0, brank); } @Test public void zrangeWithScores() { jedis.zadd("foo", 1d, "a"); jedis.zadd("foo", 10d, "b"); jedis.zadd("foo", 0.1d, "c"); jedis.zadd("foo", 2d, "a"); List expected = new ArrayList(); expected.add(new Tuple("c", 0.1d)); expected.add(new Tuple("a", 2d)); List range = jedis.zrangeWithScores("foo", 0, 1); assertEquals(expected, range); expected.add(new Tuple("b", 10d)); range = jedis.zrangeWithScores("foo", 0, 100); assertEquals(expected, range); // Binary jedis.zadd(bfoo, 1d, ba); jedis.zadd(bfoo, 10d, bb); jedis.zadd(bfoo, 0.1d, bc); jedis.zadd(bfoo, 2d, ba); List bexpected = new ArrayList(); bexpected.add(new Tuple(bc, 0.1d)); bexpected.add(new Tuple(ba, 2d)); List brange = jedis.zrangeWithScores(bfoo, 0, 1); assertEquals(bexpected, brange); bexpected.add(new Tuple(bb, 10d)); brange = jedis.zrangeWithScores(bfoo, 0, 100); assertEquals(bexpected, brange); } @Test public void zrevrangeWithScores() { jedis.zadd("foo", 1d, "a"); jedis.zadd("foo", 10d, "b"); jedis.zadd("foo", 0.1d, "c"); jedis.zadd("foo", 2d, "a"); List expected = new ArrayList(); expected.add(new Tuple("b", 10d)); expected.add(new Tuple("a", 2d)); List range = jedis.zrevrangeWithScores("foo", 0, 1); assertEquals(expected, range); expected.add(new Tuple("c", 0.1d)); range = jedis.zrevrangeWithScores("foo", 0, 100); assertEquals(expected, range); // Binary jedis.zadd(bfoo, 1d, ba); jedis.zadd(bfoo, 10d, bb); jedis.zadd(bfoo, 0.1d, bc); jedis.zadd(bfoo, 2d, ba); List bexpected = new ArrayList(); bexpected.add(new Tuple(bb, 10d)); bexpected.add(new Tuple(ba, 2d)); List brange = jedis.zrevrangeWithScores(bfoo, 0, 1); assertEquals(bexpected, brange); bexpected.add(new Tuple(bc, 0.1d)); brange = jedis.zrevrangeWithScores(bfoo, 0, 100); assertEquals(bexpected, brange); } @Test public void zcard() { jedis.zadd("foo", 1d, "a"); jedis.zadd("foo", 10d, "b"); jedis.zadd("foo", 0.1d, "c"); jedis.zadd("foo", 2d, "a"); assertEquals(3, jedis.zcard("foo")); // Binary jedis.zadd(bfoo, 1d, ba); jedis.zadd(bfoo, 10d, bb); jedis.zadd(bfoo, 0.1d, bc); jedis.zadd(bfoo, 2d, ba); assertEquals(3, jedis.zcard(bfoo)); } @Test public void zscore() { jedis.zadd("foo", 1d, "a"); jedis.zadd("foo", 10d, "b"); jedis.zadd("foo", 0.1d, "c"); jedis.zadd("foo", 2d, "a"); assertEquals((Double) 10d, jedis.zscore("foo", "b")); assertEquals((Double) 0.1d, jedis.zscore("foo", "c")); assertNull(jedis.zscore("foo", "s")); // Binary jedis.zadd(bfoo, 1d, ba); jedis.zadd(bfoo, 10d, bb); jedis.zadd(bfoo, 0.1d, bc); jedis.zadd(bfoo, 2d, ba); assertEquals((Double) 10d, jedis.zscore(bfoo, bb)); assertEquals((Double) 0.1d, jedis.zscore(bfoo, bc)); assertNull(jedis.zscore(bfoo, SafeEncoder.encode("s"))); } @Test public void zmscore() { jedis.zadd("foo", 1d, "a"); jedis.zadd("foo", 10d, "b"); jedis.zadd("foo", 0.1d, "c"); jedis.zadd("foo", 2d, "a"); assertEquals(Arrays.asList(10d, 0.1d, null), jedis.zmscore("foo", "b", "c", "s")); // Binary jedis.zadd(bfoo, 1d, ba); jedis.zadd(bfoo, 10d, bb); jedis.zadd(bfoo, 0.1d, bc); jedis.zadd(bfoo, 2d, ba); assertEquals(Arrays.asList(10d, 0.1d, null), jedis.zmscore(bfoo, bb, bc, SafeEncoder.encode("s"))); } @Test public void zpopmax() { jedis.zadd("foo", 1d, "a"); jedis.zadd("foo", 10d, "b"); jedis.zadd("foo", 0.1d, "c"); jedis.zadd("foo", 2d, "d"); Tuple actual = jedis.zpopmax("foo"); Tuple expected = new Tuple("b", 10d); assertEquals(expected, actual); actual = jedis.zpopmax("foo"); expected = new Tuple("d", 2d); assertEquals(expected, actual); actual = jedis.zpopmax("foo"); expected = new Tuple("a", 1d); assertEquals(expected, actual); actual = jedis.zpopmax("foo"); expected = new Tuple("c", 0.1d); assertEquals(expected, actual); // Empty actual = jedis.zpopmax("foo"); assertNull(actual); // Binary jedis.zadd(bfoo, 1d, ba); jedis.zadd(bfoo, 10d, bb); jedis.zadd(bfoo, 0.1d, bc); jedis.zadd(bfoo, 2d, ba); // First actual = jedis.zpopmax(bfoo); expected = new Tuple(bb, 10d); assertEquals(expected, actual); // Second actual = jedis.zpopmax(bfoo); expected = new Tuple(ba, 2d); assertEquals(expected, actual); // Third actual = jedis.zpopmax(bfoo); expected = new Tuple(bc, 0.1d); assertEquals(expected, actual); // Empty actual = jedis.zpopmax(bfoo); assertNull(actual); } @Test public void zpopmaxWithCount() { jedis.zadd("foo", 1d, "a"); jedis.zadd("foo", 10d, "b"); jedis.zadd("foo", 0.1d, "c"); jedis.zadd("foo", 2d, "d"); jedis.zadd("foo", 0.03, "e"); List actual = jedis.zpopmax("foo", 2); assertEquals(2, actual.size()); List expected = new ArrayList(); expected.add(new Tuple("b", 10d)); expected.add(new Tuple("d", 2d)); assertEquals(expected, actual); actual = jedis.zpopmax("foo", 3); assertEquals(3, actual.size()); expected.clear(); expected.add(new Tuple("a", 1d)); expected.add(new Tuple("c", 0.1d)); expected.add(new Tuple("e", 0.03d)); assertEquals(expected, actual); // Empty actual = jedis.zpopmax("foo", 1); expected.clear(); assertEquals(expected, actual); // Binary jedis.zadd(bfoo, 1d, ba); jedis.zadd(bfoo, 10d, bb); jedis.zadd(bfoo, 0.1d, bc); jedis.zadd(bfoo, 2d, ba); // First actual = jedis.zpopmax(bfoo, 1); expected.clear(); expected.add(new Tuple(bb, 10d)); assertEquals(expected, actual); // Second actual = jedis.zpopmax(bfoo, 1); expected.clear(); expected.add(new Tuple(ba, 2d)); assertEquals(expected, actual); // Last 2 (just 1, because 1 was overwritten) actual = jedis.zpopmax(bfoo, 1); expected.clear(); expected.add(new Tuple(bc, 0.1d)); assertEquals(expected, actual); // Empty actual = jedis.zpopmax(bfoo, 1); expected.clear(); assertEquals(expected, actual); } @Test public void zpopmin() { jedis.zadd("foo", 1d, "a", ZAddParams.zAddParams().nx()); jedis.zadd("foo", 10d, "b", ZAddParams.zAddParams().nx()); jedis.zadd("foo", 0.1d, "c", ZAddParams.zAddParams().nx()); jedis.zadd("foo", 2d, "a", ZAddParams.zAddParams().nx()); List range = jedis.zpopmin("foo", 2); List expected = new ArrayList(); expected.add(new Tuple("c", 0.1d)); expected.add(new Tuple("a", 1d)); assertEquals(expected, range); assertEquals(new Tuple("b", 10d), jedis.zpopmin("foo")); // Binary jedis.zadd(bfoo, 1d, ba); jedis.zadd(bfoo, 10d, bb); jedis.zadd(bfoo, 0.1d, bc); jedis.zadd(bfoo, 2d, ba); List brange = jedis.zpopmin(bfoo, 2); List bexpected = new ArrayList(); bexpected.add(new Tuple(bc, 0.1d)); bexpected.add(new Tuple(ba, 2d)); assertEquals(bexpected, brange); assertEquals(new Tuple(bb, 10d), jedis.zpopmin(bfoo)); } @Test public void zcount() { jedis.zadd("foo", 1d, "a"); jedis.zadd("foo", 10d, "b"); jedis.zadd("foo", 0.1d, "c"); jedis.zadd("foo", 2d, "a"); assertEquals(2, jedis.zcount("foo", 0.01d, 2.1d)); assertEquals(3, jedis.zcount("foo", "(0.01", "+inf")); // Binary jedis.zadd(bfoo, 1d, ba); jedis.zadd(bfoo, 10d, bb); jedis.zadd(bfoo, 0.1d, bc); jedis.zadd(bfoo, 2d, ba); assertEquals(2, jedis.zcount(bfoo, 0.01d, 2.1d)); assertEquals(3, jedis.zcount(bfoo, SafeEncoder.encode("(0.01"), SafeEncoder.encode("+inf"))); } @Test public void zlexcount() { jedis.zadd("foo", 1, "a"); jedis.zadd("foo", 1, "b"); jedis.zadd("foo", 1, "c"); jedis.zadd("foo", 1, "aa"); assertEquals(2, jedis.zlexcount("foo", "[aa", "(c")); assertEquals(4, jedis.zlexcount("foo", "-", "+")); assertEquals(3, jedis.zlexcount("foo", "-", "(c")); assertEquals(3, jedis.zlexcount("foo", "[aa", "+")); } @Test public void zlexcountBinary() { // Binary jedis.zadd(bfoo, 1, ba); jedis.zadd(bfoo, 1, bc); jedis.zadd(bfoo, 1, bb); assertEquals(1, jedis.zlexcount(bfoo, bInclusiveB, bExclusiveC)); assertEquals(3, jedis.zlexcount(bfoo, bLexMinusInf, bLexPlusInf)); } @Test public void zrangebyscore() { jedis.zadd("foo", 1d, "a"); jedis.zadd("foo", 10d, "b"); jedis.zadd("foo", 0.1d, "c"); jedis.zadd("foo", 2d, "a"); List range = jedis.zrangeByScore("foo", 0d, 2d); List expected = new ArrayList(); expected.add("c"); expected.add("a"); assertEquals(expected, range); range = jedis.zrangeByScore("foo", 0d, 2d, 0, 1); expected = new ArrayList(); expected.add("c"); assertEquals(expected, range); range = jedis.zrangeByScore("foo", 0d, 2d, 1, 1); List range2 = jedis.zrangeByScore("foo", "-inf", "(2"); assertEquals(expected, range2); expected = new ArrayList(); expected.add("a"); assertEquals(expected, range); // Binary jedis.zadd(bfoo, 1d, ba); jedis.zadd(bfoo, 10d, bb); jedis.zadd(bfoo, 0.1d, bc); jedis.zadd(bfoo, 2d, ba); List brange = jedis.zrangeByScore(bfoo, 0d, 2d); List bexpected = new ArrayList(); bexpected.add(bc); bexpected.add(ba); assertByteArrayListEquals(bexpected, brange); brange = jedis.zrangeByScore(bfoo, 0d, 2d, 0, 1); bexpected = new ArrayList(); bexpected.add(bc); assertByteArrayListEquals(bexpected, brange); brange = jedis.zrangeByScore(bfoo, 0d, 2d, 1, 1); List brange2 = jedis.zrangeByScore(bfoo, SafeEncoder.encode("-inf"), SafeEncoder.encode("(2")); assertByteArrayListEquals(bexpected, brange2); bexpected = new ArrayList(); bexpected.add(ba); assertByteArrayListEquals(bexpected, brange); } @Test public void zrevrangebyscore() { jedis.zadd("foo", 1.0d, "a"); jedis.zadd("foo", 2.0d, "b"); jedis.zadd("foo", 3.0d, "c"); jedis.zadd("foo", 4.0d, "d"); jedis.zadd("foo", 5.0d, "e"); List range = jedis.zrevrangeByScore("foo", 3d, Double.NEGATIVE_INFINITY, 0, 1); List expected = new ArrayList(); expected.add("c"); assertEquals(expected, range); range = jedis.zrevrangeByScore("foo", 3.5d, Double.NEGATIVE_INFINITY, 0, 2); expected = new ArrayList(); expected.add("c"); expected.add("b"); assertEquals(expected, range); range = jedis.zrevrangeByScore("foo", 3.5d, Double.NEGATIVE_INFINITY, 1, 1); expected = new ArrayList(); expected.add("b"); assertEquals(expected, range); range = jedis.zrevrangeByScore("foo", 4d, 2d); expected = new ArrayList(); expected.add("d"); expected.add("c"); expected.add("b"); assertEquals(expected, range); range = jedis.zrevrangeByScore("foo", "4", "2", 0, 2); expected = new ArrayList(); expected.add("d"); expected.add("c"); assertEquals(expected, range); range = jedis.zrevrangeByScore("foo", "+inf", "(4"); expected = new ArrayList(); expected.add("e"); assertEquals(expected, range); // Binary jedis.zadd(bfoo, 1d, ba); jedis.zadd(bfoo, 10d, bb); jedis.zadd(bfoo, 0.1d, bc); jedis.zadd(bfoo, 2d, ba); List brange = jedis.zrevrangeByScore(bfoo, 2d, 0d); List bexpected = new ArrayList(); bexpected.add(ba); bexpected.add(bc); assertByteArrayListEquals(bexpected, brange); brange = jedis.zrevrangeByScore(bfoo, 2d, 0d, 0, 1); bexpected = new ArrayList(); bexpected.add(ba); assertByteArrayListEquals(bexpected, brange); List brange2 = jedis.zrevrangeByScore(bfoo, SafeEncoder.encode("+inf"), SafeEncoder.encode("(2")); bexpected = new ArrayList(); bexpected.add(bb); assertByteArrayListEquals(bexpected, brange2); brange = jedis.zrevrangeByScore(bfoo, 2d, 0d, 1, 1); bexpected = new ArrayList(); bexpected.add(bc); assertByteArrayListEquals(bexpected, brange); } @Test public void zrangebyscoreWithScores() { jedis.zadd("foo", 1d, "a"); jedis.zadd("foo", 10d, "b"); jedis.zadd("foo", 0.1d, "c"); jedis.zadd("foo", 2d, "a"); List range = jedis.zrangeByScoreWithScores("foo", 0d, 2d); List expected = new ArrayList(); expected.add(new Tuple("c", 0.1d)); expected.add(new Tuple("a", 2d)); assertEquals(expected, range); range = jedis.zrangeByScoreWithScores("foo", 0d, 2d, 0, 1); expected = new ArrayList(); expected.add(new Tuple("c", 0.1d)); assertEquals(expected, range); range = jedis.zrangeByScoreWithScores("foo", 0d, 2d, 1, 1); expected = new ArrayList(); expected.add(new Tuple("a", 2d)); assertEquals(expected, range); // Binary jedis.zadd(bfoo, 1d, ba); jedis.zadd(bfoo, 10d, bb); jedis.zadd(bfoo, 0.1d, bc); jedis.zadd(bfoo, 2d, ba); List brange = jedis.zrangeByScoreWithScores(bfoo, 0d, 2d); List bexpected = new ArrayList(); bexpected.add(new Tuple(bc, 0.1d)); bexpected.add(new Tuple(ba, 2d)); assertEquals(bexpected, brange); brange = jedis.zrangeByScoreWithScores(bfoo, 0d, 2d, 0, 1); bexpected = new ArrayList(); bexpected.add(new Tuple(bc, 0.1d)); assertEquals(bexpected, brange); brange = jedis.zrangeByScoreWithScores(bfoo, 0d, 2d, 1, 1); bexpected = new ArrayList(); bexpected.add(new Tuple(ba, 2d)); assertEquals(bexpected, brange); } @Test public void zrevrangebyscoreWithScores() { jedis.zadd("foo", 1.0d, "a"); jedis.zadd("foo", 2.0d, "b"); jedis.zadd("foo", 3.0d, "c"); jedis.zadd("foo", 4.0d, "d"); jedis.zadd("foo", 5.0d, "e"); List range = jedis.zrevrangeByScoreWithScores("foo", 3d, Double.NEGATIVE_INFINITY, 0, 1); List expected = new ArrayList(); expected.add(new Tuple("c", 3.0d)); assertEquals(expected, range); range = jedis.zrevrangeByScoreWithScores("foo", 3.5d, Double.NEGATIVE_INFINITY, 0, 2); expected = new ArrayList(); expected.add(new Tuple("c", 3.0d)); expected.add(new Tuple("b", 2.0d)); assertEquals(expected, range); range = jedis.zrevrangeByScoreWithScores("foo", 3.5d, Double.NEGATIVE_INFINITY, 1, 1); expected = new ArrayList(); expected.add(new Tuple("b", 2.0d)); assertEquals(expected, range); range = jedis.zrevrangeByScoreWithScores("foo", 4d, 2d); expected = new ArrayList(); expected.add(new Tuple("d", 4.0d)); expected.add(new Tuple("c", 3.0d)); expected.add(new Tuple("b", 2.0d)); assertEquals(expected, range); // Binary jedis.zadd(bfoo, 1d, ba); jedis.zadd(bfoo, 10d, bb); jedis.zadd(bfoo, 0.1d, bc); jedis.zadd(bfoo, 2d, ba); List brange = jedis.zrevrangeByScoreWithScores(bfoo, 2d, 0d); List bexpected = new ArrayList(); bexpected.add(new Tuple(ba, 2d)); bexpected.add(new Tuple(bc, 0.1d)); assertEquals(bexpected, brange); brange = jedis.zrevrangeByScoreWithScores(bfoo, 2d, 0d, 0, 1); bexpected = new ArrayList(); bexpected.add(new Tuple(ba, 2d)); assertEquals(bexpected, brange); brange = jedis.zrevrangeByScoreWithScores(bfoo, 2d, 0d, 1, 1); bexpected = new ArrayList(); bexpected.add(new Tuple(bc, 0.1d)); assertEquals(bexpected, brange); } @Test public void zremrangeByRank() { jedis.zadd("foo", 1d, "a"); jedis.zadd("foo", 10d, "b"); jedis.zadd("foo", 0.1d, "c"); jedis.zadd("foo", 2d, "a"); assertEquals(1, jedis.zremrangeByRank("foo", 0, 0)); List expected = new ArrayList(); expected.add("a"); expected.add("b"); assertEquals(expected, jedis.zrange("foo", 0, 100)); // Binary jedis.zadd(bfoo, 1d, ba); jedis.zadd(bfoo, 10d, bb); jedis.zadd(bfoo, 0.1d, bc); jedis.zadd(bfoo, 2d, ba); assertEquals(1, jedis.zremrangeByRank(bfoo, 0, 0)); List bexpected = new ArrayList(); bexpected.add(ba); bexpected.add(bb); assertByteArrayListEquals(bexpected, jedis.zrange(bfoo, 0, 100)); } @Test public void zremrangeByScore() { jedis.zadd("foo", 1d, "a"); jedis.zadd("foo", 10d, "b"); jedis.zadd("foo", 0.1d, "c"); jedis.zadd("foo", 2d, "a"); assertEquals(2, jedis.zremrangeByScore("foo", 0, 2)); List expected = new ArrayList(); expected.add("b"); assertEquals(expected, jedis.zrange("foo", 0, 100)); // Binary jedis.zadd(bfoo, 1d, ba); jedis.zadd(bfoo, 10d, bb); jedis.zadd(bfoo, 0.1d, bc); jedis.zadd(bfoo, 2d, ba); assertEquals(2, jedis.zremrangeByScore(bfoo, 0, 2)); List bexpected = new ArrayList(); bexpected.add(bb); assertByteArrayListEquals(bexpected, jedis.zrange(bfoo, 0, 100)); } @Test public void zremrangeByScoreExclusive() { jedis.zadd("foo", 1d, "a"); jedis.zadd("foo", 0d, "c"); jedis.zadd("foo", 2d, "b"); assertEquals(1, jedis.zremrangeByScore("foo", "(0", "(2")); // Binary jedis.zadd(bfoo, 1d, ba); jedis.zadd(bfoo, 0d, bc); jedis.zadd(bfoo, 2d, bb); assertEquals(1, jedis.zremrangeByScore(bfoo, "(0".getBytes(), "(2".getBytes())); } @Test public void zremrangeByLex() { jedis.zadd("foo", 1, "a"); jedis.zadd("foo", 1, "b"); jedis.zadd("foo", 1, "c"); jedis.zadd("foo", 1, "aa"); assertEquals(2, jedis.zremrangeByLex("foo", "[aa", "(c")); List expected = new ArrayList(); expected.add("a"); expected.add("c"); assertEquals(expected, jedis.zrangeByLex("foo", "-", "+")); } @Test public void zremrangeByLexBinary() { jedis.zadd(bfoo, 1, ba); jedis.zadd(bfoo, 1, bc); jedis.zadd(bfoo, 1, bb); assertEquals(1, jedis.zremrangeByLex(bfoo, bInclusiveB, bExclusiveC)); List bexpected = new ArrayList(); bexpected.add(ba); bexpected.add(bc); assertByteArrayListEquals(bexpected, jedis.zrangeByLex(bfoo, bLexMinusInf, bLexPlusInf)); } @Test @ConditionalOnEnv(value = TestEnvUtil.ENV_REDIS_ENTERPRISE, enabled = false) public void zunion() { jedis.zadd("foo", 1, "a"); jedis.zadd("foo", 2, "b"); jedis.zadd("bar", 2, "a"); jedis.zadd("bar", 2, "b"); ZParams params = new ZParams(); params.weights(2, 2.5); params.aggregate(ZParams.Aggregate.SUM); List expected = new ArrayList<>(); expected.add("a"); expected.add("b"); assertEquals(expected, jedis.zunion(params, "foo", "bar")); List expectedTuple = new ArrayList<>(); expectedTuple.add(new Tuple("a", new Double(7))); expectedTuple.add(new Tuple("b", new Double(9))); assertEquals(expectedTuple, jedis.zunionWithScores(params, "foo", "bar")); // Binary jedis.zadd(bfoo, 1, ba); jedis.zadd(bfoo, 2, bb); jedis.zadd(bbar, 2, ba); jedis.zadd(bbar, 2, bb); List bexpected = new ArrayList<>(); bexpected.add(ba); bexpected.add(bb); AssertUtil.assertByteArrayListEquals(bexpected, jedis.zunion(params, bfoo, bbar)); List bexpectedTuple = new ArrayList<>(); bexpectedTuple.add(new Tuple(ba, new Double(7))); bexpectedTuple.add(new Tuple(bb, new Double(9))); assertEquals(bexpectedTuple, jedis.zunionWithScores(params, bfoo, bbar)); } @Test @ConditionalOnEnv(value = TestEnvUtil.ENV_REDIS_ENTERPRISE, enabled = false) public void zunionstore() { jedis.zadd("foo", 1, "a"); jedis.zadd("foo", 2, "b"); jedis.zadd("bar", 2, "a"); jedis.zadd("bar", 2, "b"); assertEquals(2, jedis.zunionstore("dst", "foo", "bar")); List expected = new ArrayList(); expected.add(new Tuple("a", new Double(3))); expected.add(new Tuple("b", new Double(4))); assertEquals(expected, jedis.zrangeWithScores("dst", 0, 100)); // Binary jedis.zadd(bfoo, 1, ba); jedis.zadd(bfoo, 2, bb); jedis.zadd(bbar, 2, ba); jedis.zadd(bbar, 2, bb); assertEquals(2, jedis.zunionstore(SafeEncoder.encode("dst"), bfoo, bbar)); List bexpected = new ArrayList(); bexpected.add(new Tuple(ba, new Double(3))); bexpected.add(new Tuple(bb, new Double(4))); assertEquals(bexpected, jedis.zrangeWithScores(SafeEncoder.encode("dst"), 0, 100)); } @Test @ConditionalOnEnv(value = TestEnvUtil.ENV_REDIS_ENTERPRISE, enabled = false) public void zunionstoreParams() { jedis.zadd("foo", 1, "a"); jedis.zadd("foo", 2, "b"); jedis.zadd("bar", 2, "a"); jedis.zadd("bar", 2, "b"); ZParams params = new ZParams(); params.weights(2, 2.5); params.aggregate(ZParams.Aggregate.SUM); assertEquals(2, jedis.zunionstore("dst", params, "foo", "bar")); List expected = new ArrayList(); expected.add(new Tuple("a", new Double(7))); expected.add(new Tuple("b", new Double(9))); assertEquals(expected, jedis.zrangeWithScores("dst", 0, 100)); // Binary jedis.zadd(bfoo, 1, ba); jedis.zadd(bfoo, 2, bb); jedis.zadd(bbar, 2, ba); jedis.zadd(bbar, 2, bb); ZParams bparams = new ZParams(); bparams.weights(2, 2.5); bparams.aggregate(ZParams.Aggregate.SUM); assertEquals(2, jedis.zunionstore(SafeEncoder.encode("dst"), bparams, bfoo, bbar)); List bexpected = new ArrayList(); bexpected.add(new Tuple(ba, new Double(7))); bexpected.add(new Tuple(bb, new Double(9))); assertEquals(bexpected, jedis.zrangeWithScores(SafeEncoder.encode("dst"), 0, 100)); } @Test @ConditionalOnEnv(value = TestEnvUtil.ENV_REDIS_ENTERPRISE, enabled = false) public void zinter() { jedis.zadd("foo", 1, "a"); jedis.zadd("foo", 2, "b"); jedis.zadd("bar", 2, "a"); ZParams params = new ZParams(); params.weights(2, 2.5); params.aggregate(ZParams.Aggregate.SUM); assertEquals(singletonList("a"), jedis.zinter(params, "foo", "bar")); assertEquals(singletonList(new Tuple("a", new Double(7))), jedis.zinterWithScores(params, "foo", "bar")); // Binary jedis.zadd(bfoo, 1, ba); jedis.zadd(bfoo, 2, bb); jedis.zadd(bbar, 2, ba); ZParams bparams = new ZParams(); bparams.weights(2, 2.5); bparams.aggregate(ZParams.Aggregate.SUM); AssertUtil.assertByteArrayListEquals(singletonList(ba), jedis.zinter(params, bfoo, bbar)); assertEquals(singletonList(new Tuple(ba, new Double(7))), jedis.zinterWithScores(bparams, bfoo, bbar)); } @Test @ConditionalOnEnv(value = TestEnvUtil.ENV_REDIS_ENTERPRISE, enabled = false) public void zinterstore() { jedis.zadd("foo", 1, "a"); jedis.zadd("foo", 2, "b"); jedis.zadd("bar", 2, "a"); assertEquals(1, jedis.zinterstore("dst", "foo", "bar")); assertEquals(singletonList(new Tuple("a", new Double(3))), jedis.zrangeWithScores("dst", 0, 100)); // Binary jedis.zadd(bfoo, 1, ba); jedis.zadd(bfoo, 2, bb); jedis.zadd(bbar, 2, ba); assertEquals(1, jedis.zinterstore(SafeEncoder.encode("dst"), bfoo, bbar)); assertEquals(singletonList(new Tuple(ba, new Double(3))), jedis.zrangeWithScores(SafeEncoder.encode("dst"), 0, 100)); } @Test @ConditionalOnEnv(value = TestEnvUtil.ENV_REDIS_ENTERPRISE, enabled = false) public void zintertoreParams() { jedis.zadd("foo", 1, "a"); jedis.zadd("foo", 2, "b"); jedis.zadd("bar", 2, "a"); ZParams params = new ZParams(); params.weights(2, 2.5); params.aggregate(ZParams.Aggregate.SUM); assertEquals(1, jedis.zinterstore("dst", params, "foo", "bar")); assertEquals(singletonList(new Tuple("a", new Double(7))), jedis.zrangeWithScores("dst", 0, 100)); // Binary jedis.zadd(bfoo, 1, ba); jedis.zadd(bfoo, 2, bb); jedis.zadd(bbar, 2, ba); ZParams bparams = new ZParams(); bparams.weights(2, 2.5); bparams.aggregate(ZParams.Aggregate.SUM); assertEquals(1, jedis.zinterstore(SafeEncoder.encode("dst"), bparams, bfoo, bbar)); assertEquals(singletonList(new Tuple(ba, new Double(7))), jedis.zrangeWithScores(SafeEncoder.encode("dst"), 0, 100)); } @Test @SinceRedisVersion(value="7.0.0") @ConditionalOnEnv(value = TestEnvUtil.ENV_REDIS_ENTERPRISE, enabled = false) public void zintercard() { jedis.zadd("foo", 1, "a"); jedis.zadd("foo", 2, "b"); jedis.zadd("bar", 2, "a"); jedis.zadd("bar", 1, "b"); assertEquals(2, jedis.zintercard("foo", "bar")); assertEquals(1, jedis.zintercard(1, "foo", "bar")); // Binary jedis.zadd(bfoo, 1, ba); jedis.zadd(bfoo, 2, bb); jedis.zadd(bbar, 2, ba); jedis.zadd(bbar, 2, bb); assertEquals(2, jedis.zintercard(bfoo, bbar)); assertEquals(1, jedis.zintercard(1, bfoo, bbar)); } @Test public void zscan() { jedis.zadd("foo", 1, "a"); jedis.zadd("foo", 2, "b"); ScanResult result = jedis.zscan("foo", SCAN_POINTER_START); assertEquals(SCAN_POINTER_START, result.getCursor()); assertFalse(result.getResult().isEmpty()); // binary jedis.zadd(bfoo, 1, ba); jedis.zadd(bfoo, 1, bb); ScanResult bResult = jedis.zscan(bfoo, SCAN_POINTER_START_BINARY); assertArrayEquals(SCAN_POINTER_START_BINARY, bResult.getCursorAsBytes()); assertFalse(bResult.getResult().isEmpty()); } @Test public void zscanMatch() { ScanParams params = new ScanParams(); params.match("a*"); jedis.zadd("foo", 2, "b"); jedis.zadd("foo", 1, "a"); jedis.zadd("foo", 11, "aa"); ScanResult result = jedis.zscan("foo", SCAN_POINTER_START, params); assertEquals(SCAN_POINTER_START, result.getCursor()); assertFalse(result.getResult().isEmpty()); // binary params = new ScanParams(); params.match(bbarstar); jedis.zadd(bfoo, 2, bbar1); jedis.zadd(bfoo, 1, bbar2); jedis.zadd(bfoo, 11, bbar3); ScanResult bResult = jedis.zscan(bfoo, SCAN_POINTER_START_BINARY, params); assertArrayEquals(SCAN_POINTER_START_BINARY, bResult.getCursorAsBytes()); assertFalse(bResult.getResult().isEmpty()); } @Test public void zscanCount() { ScanParams params = new ScanParams(); params.count(2); jedis.zadd("foo", 1, "a1"); jedis.zadd("foo", 2, "a2"); jedis.zadd("foo", 3, "a3"); jedis.zadd("foo", 4, "a4"); jedis.zadd("foo", 5, "a5"); ScanResult result = jedis.zscan("foo", SCAN_POINTER_START, params); assertFalse(result.getResult().isEmpty()); // binary params = new ScanParams(); params.count(2); jedis.zadd(bfoo, 2, bbar1); jedis.zadd(bfoo, 1, bbar2); jedis.zadd(bfoo, 11, bbar3); ScanResult bResult = jedis.zscan(bfoo, SCAN_POINTER_START_BINARY, params); assertFalse(bResult.getResult().isEmpty()); } @Test public void infinity() { jedis.zadd("key", Double.POSITIVE_INFINITY, "pos"); assertEquals(Double.POSITIVE_INFINITY, jedis.zscore("key", "pos"), 0d); jedis.zadd("key", Double.NEGATIVE_INFINITY, "neg"); assertEquals(Double.NEGATIVE_INFINITY, jedis.zscore("key", "neg"), 0d); jedis.zadd("key", 0d, "zero"); List set = jedis.zrangeWithScores("key", 0, -1); Iterator itr = set.iterator(); assertEquals(Double.NEGATIVE_INFINITY, itr.next().getScore(), 0d); assertEquals(0d, itr.next().getScore(), 0d); assertEquals(Double.POSITIVE_INFINITY, itr.next().getScore(), 0d); } @Test @ConditionalOnEnv(value = TestEnvUtil.ENV_REDIS_ENTERPRISE, enabled = false) public void bzpopmax() { assertNull(jedis.bzpopmax(1, "foo", "bar")); jedis.zadd("foo", 1d, "a", ZAddParams.zAddParams().nx()); jedis.zadd("foo", 10d, "b", ZAddParams.zAddParams().nx()); jedis.zadd("bar", 0.1d, "c", ZAddParams.zAddParams().nx()); assertEquals(new KeyValue<>("foo", new Tuple("b", 10d)), jedis.bzpopmax(0, "foo", "bar")); // Binary assertNull(jedis.bzpopmax(1, bfoo, bbar)); jedis.zadd(bfoo, 1d, ba); jedis.zadd(bfoo, 10d, bb); jedis.zadd(bbar, 0.1d, bc); KeyValue actual = jedis.bzpopmax(0, bfoo, bbar); assertArrayEquals(bfoo, actual.getKey()); assertEquals(new Tuple(bb, 10d), actual.getValue()); } @Test @ConditionalOnEnv(value = TestEnvUtil.ENV_REDIS_ENTERPRISE, enabled = false) public void bzpopmin() { assertNull(jedis.bzpopmin(1, "bar", "foo")); jedis.zadd("foo", 1d, "a", ZAddParams.zAddParams().nx()); jedis.zadd("foo", 10d, "b", ZAddParams.zAddParams().nx()); jedis.zadd("bar", 0.1d, "c", ZAddParams.zAddParams().nx()); assertEquals(new KeyValue<>("bar", new Tuple("c", 0.1)), jedis.bzpopmin(0, "bar", "foo")); // Binary assertNull(jedis.bzpopmin(1, bbar, bfoo)); jedis.zadd(bfoo, 1d, ba); jedis.zadd(bfoo, 10d, bb); jedis.zadd(bbar, 0.1d, bc); KeyValue actual = jedis.bzpopmin(0, bbar, bfoo); assertArrayEquals(bbar, (byte[]) actual.getKey()); assertEquals(new Tuple(bc, 0.1), actual.getValue()); } @Test @ConditionalOnEnv(value = TestEnvUtil.ENV_REDIS_ENTERPRISE, enabled = false) public void zdiff() { jedis.zadd("foo", 1.0, "a"); jedis.zadd("foo", 2.0, "b"); jedis.zadd("bar", 1.0, "a"); assertEquals(0, jedis.zdiff("bar1", "bar2").size()); assertEquals(singletonList("b"), jedis.zdiff("foo", "bar")); assertEquals(singletonList(new Tuple("b", 2.0d)), jedis.zdiffWithScores("foo", "bar")); // binary jedis.zadd(bfoo, 1.0, ba); jedis.zadd(bfoo, 2.0, bb); jedis.zadd(bbar, 1.0, ba); assertEquals(0, jedis.zdiff(bbar1, bbar2).size()); List bactual = jedis.zdiff(bfoo, bbar); assertEquals(1, bactual.size()); assertArrayEquals(bb, bactual.iterator().next()); assertEquals(singletonList(new Tuple(bb, 2.0d)), jedis.zdiffWithScores(bfoo, bbar)); } @Test @ConditionalOnEnv(value = TestEnvUtil.ENV_REDIS_ENTERPRISE, enabled = false) public void zdiffstore() { jedis.zadd("foo", 1.0, "a"); jedis.zadd("foo", 2.0, "b"); jedis.zadd("bar", 1.0, "a"); assertEquals(0, jedis.zdiffstore("bar3", "bar1", "bar2")); assertEquals(1, jedis.zdiffstore("bar3", "foo", "bar")); assertEquals(singletonList("b"), jedis.zrange("bar3", 0, -1)); // binary jedis.zadd(bfoo, 1.0, ba); jedis.zadd(bfoo, 2.0, bb); jedis.zadd(bbar, 1.0, ba); assertEquals(0, jedis.zdiffstore(bbar3, bbar1, bbar2)); assertEquals(1, jedis.zdiffstore(bbar3, bfoo, bbar)); List bactual = jedis.zrange(bbar3, 0, -1); assertArrayEquals(bb, bactual.iterator().next()); } @Test public void zrandmember() { assertNull(jedis.zrandmember("foo")); assertEquals(Collections.emptyList(), jedis.zrandmember("foo", 1)); assertEquals(Collections.emptyList(), jedis.zrandmemberWithScores("foo", 1)); Map hash = new HashMap<>(); hash.put("bar1", 1d); hash.put("bar2", 10d); hash.put("bar3", 0.1d); jedis.zadd("foo", hash); AssertUtil.assertCollectionContains(hash.keySet(), jedis.zrandmember("foo")); assertEquals(2, jedis.zrandmember("foo", 2).size()); List actual = jedis.zrandmemberWithScores("foo", 2); assertEquals(2, actual.size()); actual.forEach(t -> assertEquals(hash.get(t.getElement()), t.getScore(), 0d)); // Binary assertNull(jedis.zrandmember(bfoo)); assertEquals(Collections.emptyList(), jedis.zrandmember(bfoo, 1)); assertEquals(Collections.emptyList(), jedis.zrandmemberWithScores(bfoo, 1)); Map bhash = new HashMap<>(); bhash.put(bbar1, 1d); bhash.put(bbar2, 10d); bhash.put(bbar3, 0.1d); jedis.zadd(bfoo, bhash); AssertUtil.assertByteArrayCollectionContains(bhash.keySet(), jedis.zrandmember(bfoo)); assertEquals(2, jedis.zrandmember(bfoo, 2).size()); List bactual = jedis.zrandmemberWithScores(bfoo, 2); assertEquals(2, bactual.size()); bactual.forEach(t -> assertEquals(getScoreFromByteMap(bhash, t.getBinaryElement()), t.getScore(), 0d)); } private Double getScoreFromByteMap(Map bhash, byte[] key) { for (Map.Entry en : bhash.entrySet()) { if (Arrays.equals(en.getKey(), key)) { return en.getValue(); } } return null; } @Test @SinceRedisVersion(value="7.0.0") public void zmpop() { jedis.zadd("foo", 1d, "a", ZAddParams.zAddParams().nx()); jedis.zadd("foo", 10d, "b", ZAddParams.zAddParams().nx()); jedis.zadd("foo", 0.1d, "c", ZAddParams.zAddParams().nx()); jedis.zadd("foo", 2d, "a", ZAddParams.zAddParams().nx()); KeyValue> single = jedis.zmpop(SortedSetOption.MAX, "foo"); KeyValue> range = jedis.zmpop(SortedSetOption.MIN, 2, "foo"); assertEquals(new Tuple("b", 10d), single.getValue().get(0)); assertEquals(2, range.getValue().size()); assertNull(jedis.zmpop(SortedSetOption.MAX, "foo")); } @Test @SinceRedisVersion(value="7.0.0") public void bzmpopSimple() { jedis.zadd("foo", 1d, "a", ZAddParams.zAddParams().nx()); jedis.zadd("foo", 10d, "b", ZAddParams.zAddParams().nx()); jedis.zadd("foo", 0.1d, "c", ZAddParams.zAddParams().nx()); jedis.zadd("foo", 2d, "a", ZAddParams.zAddParams().nx()); KeyValue> single = jedis.bzmpop(1L, SortedSetOption.MAX, "foo"); KeyValue> range = jedis.bzmpop(1L, SortedSetOption.MIN, 2, "foo"); assertEquals(new Tuple("b", 10d), single.getValue().get(0)); assertEquals(2, range.getValue().size()); assertNull(jedis.bzmpop(1L, SortedSetOption.MAX, "foo")); } } ================================================ FILE: src/test/java/redis/clients/jedis/commands/unified/StreamsBinaryCommandsTestBase.java ================================================ package redis.clients.jedis.commands.unified; import io.redis.test.annotations.EnabledOnCommand; import io.redis.test.annotations.SinceRedisVersion; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Tag; import redis.clients.jedis.RedisProtocol; import redis.clients.jedis.StreamEntryID; import redis.clients.jedis.args.StreamDeletionPolicy; import redis.clients.jedis.exceptions.JedisDataException; import redis.clients.jedis.params.XAddParams; import redis.clients.jedis.params.XCfgSetParams; import redis.clients.jedis.params.XReadGroupParams; import redis.clients.jedis.params.XReadParams; import redis.clients.jedis.params.XTrimParams; import redis.clients.jedis.resps.StreamEntryBinary; import redis.clients.jedis.resps.StreamEntryDeletionResult; import java.util.ArrayList; import java.util.HashMap; import java.util.LinkedHashMap; import java.util.List; import java.util.Map; import static io.redis.test.utils.RedisVersion.V8_4_0_STRING; import static java.util.Collections.singletonMap; import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.hasSize; import static org.junit.jupiter.api.Assertions.assertArrayEquals; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertNotEquals; import static org.junit.jupiter.api.Assertions.assertNotNull; import static org.junit.jupiter.api.Assertions.assertNull; import static org.junit.jupiter.api.Assertions.assertTrue; import static redis.clients.jedis.StreamEntryID.XREADGROUP_UNDELIVERED_ENTRY; import static redis.clients.jedis.util.ByteArrayMapMatcher.contentEquals; import static redis.clients.jedis.util.StreamEntryBinaryListMatcher.equalsStreamEntries; @Tag("integration") public abstract class StreamsBinaryCommandsTestBase extends UnifiedJedisCommandsTestBase { protected static final byte[] STREAM_KEY_1 = "{binary-stream}-1".getBytes(); protected static final byte[] STREAM_KEY_2 = "{binary-stream}-2".getBytes(); protected static final byte[] GROUP_NAME = "group-1".getBytes(); protected static final byte[] CONSUMER_NAME = "consumer-1".getBytes(); protected static final byte[] FIELD_KEY_1 = "binary-field-1".getBytes(); // Test with invalid UTF-8 characters protected static final byte[] BINARY_VALUE_1 = new byte[] { 0x00, 0x01, 0x02, 0x03, (byte) 0xFF }; protected static final byte[] FIELD_KEY_2 = "binary-field-1".getBytes(); protected static final byte[] BINARY_VALUE_2 = "binary-value-2".getBytes(); protected static final Map HASH_1 = singletonMap(FIELD_KEY_1, BINARY_VALUE_1); protected static final Map HASH_2 = singletonMap(FIELD_KEY_2, BINARY_VALUE_2); protected static final List stream1Entries = new ArrayList<>(); protected static final List stream2Entries = new ArrayList<>(); static { stream1Entries.add(new StreamEntryBinary(new StreamEntryID("0-1"), HASH_1)); stream1Entries.add(new StreamEntryBinary(new StreamEntryID("0-3"), HASH_2)); stream2Entries.add(new StreamEntryBinary(new StreamEntryID("0-2"), HASH_1)); } public StreamsBinaryCommandsTestBase(RedisProtocol protocol) { super(protocol); } /** * Creates a map of stream keys to StreamEntryID objects. * @param streamOffsets Array of stream key and offset pairs * @return Map of stream keys to StreamEntryID objects */ public static Map offsets(Object... streamOffsets) { if (streamOffsets.length % 2 != 0) { throw new IllegalArgumentException("Stream offsets must be provided as key-value pairs"); } Map result = new HashMap<>(); for (int i = 0; i < streamOffsets.length; i += 2) { byte[] key = (byte[]) streamOffsets[i]; Object value = streamOffsets[i + 1]; StreamEntryID id; if (value instanceof String) { id = new StreamEntryID((String) value); } else if (value instanceof StreamEntryID) { id = (StreamEntryID) value; } else { throw new IllegalArgumentException("Offset must be a String or StreamEntryID"); } result.put(key, id); } return result; } @BeforeEach public void setUpTestStream() { setUpTestStream(StreamEntryID.XGROUP_LAST_ENTRY.toString().getBytes()); } private void setUpTestStream(byte[] startId) { jedis.del(STREAM_KEY_1); jedis.del(STREAM_KEY_2); try { jedis.xgroupCreate(STREAM_KEY_1, GROUP_NAME, startId, true); } catch (JedisDataException e) { if (!e.getMessage().contains("BUSYGROUP")) { throw e; } } try { jedis.xgroupCreate(STREAM_KEY_2, GROUP_NAME, startId, true); } catch (JedisDataException e) { if (!e.getMessage().contains("BUSYGROUP")) { throw e; } } } @Test public void xreadBinaryNoEntries() { List>> actualEntries = jedis.xreadBinary( XReadParams.xReadParams(), offsets(STREAM_KEY_1, "0-0")); assertNull(actualEntries); } @Test public void xreadBinary() { stream1Entries.forEach( entry -> jedis.xadd(STREAM_KEY_1, new XAddParams().id(entry.getID()), entry.getFields())); List>> actualEntries = jedis.xreadBinary( XReadParams.xReadParams(), offsets(STREAM_KEY_1, "0-0")); assertThat(actualEntries, hasSize(1)); assertArrayEquals(STREAM_KEY_1, actualEntries.get(0).getKey()); assertThat(actualEntries.get(0).getValue(), equalsStreamEntries(stream1Entries)); } @Test public void xreadBinaryCount() { stream1Entries.forEach( entry -> jedis.xadd(STREAM_KEY_1, new XAddParams().id(entry.getID()), entry.getFields())); List>> actualEntries = jedis.xreadBinary( XReadParams.xReadParams().count(1), offsets(STREAM_KEY_1, "0-0")); assertThat(actualEntries, hasSize(1)); assertArrayEquals(STREAM_KEY_1, actualEntries.get(0).getKey()); assertThat(actualEntries.get(0).getValue(), equalsStreamEntries(stream1Entries.subList(0, 1))); } @Test public void xreadBinaryAsMapNoEntries() { Map> actualEntries = jedis.xreadBinaryAsMap( XReadParams.xReadParams(), offsets(STREAM_KEY_1, "0-0")); assertNull(actualEntries); } @Test public void xreadBinaryAsMap() { stream1Entries.forEach( entry -> jedis.xadd(STREAM_KEY_1, new XAddParams().id(entry.getID()), entry.getFields())); Map> actualEntries = jedis.xreadBinaryAsMap( XReadParams.xReadParams(), offsets(STREAM_KEY_1, "0-0")); assertThat(actualEntries.entrySet(), hasSize(1)); assertThat(actualEntries.get(STREAM_KEY_1), equalsStreamEntries(stream1Entries)); } @Test public void xreadBinaryAsMapCount() { stream1Entries.forEach( entry -> jedis.xadd(STREAM_KEY_1, new XAddParams().id(entry.getID()), entry.getFields())); Map> actualEntries = jedis.xreadBinaryAsMap( XReadParams.xReadParams().count(1), offsets(STREAM_KEY_1, "0-0")); assertThat(actualEntries.entrySet(), hasSize(1)); assertThat(actualEntries.get(STREAM_KEY_1), equalsStreamEntries(stream1Entries.subList(0, 1))); } @Test public void xreadBinaryAsMapWithMultipleStreams() { // Add entries to the streams stream1Entries.forEach( entry -> jedis.xadd(STREAM_KEY_1, new XAddParams().id(entry.getID()), entry.getFields())); stream2Entries.forEach( entry -> jedis.xadd(STREAM_KEY_2, new XAddParams().id(entry.getID()), entry.getFields())); Map> actualEntries = jedis.xreadBinaryAsMap( XReadParams.xReadParams(), offsets(STREAM_KEY_1, "0-0", STREAM_KEY_2, "0-0")); assertThat(actualEntries.entrySet(), hasSize(2)); assertThat(actualEntries.get(STREAM_KEY_1), equalsStreamEntries(stream1Entries)); assertThat(actualEntries.get(STREAM_KEY_2), equalsStreamEntries(stream2Entries)); } @Test public void xreadGroupBinary() { // Add entries to the streams stream1Entries.forEach( entry -> jedis.xadd(STREAM_KEY_1, new XAddParams().id(entry.getID()), entry.getFields())); List>> actualEntries = jedis.xreadGroupBinary( GROUP_NAME, CONSUMER_NAME, XReadGroupParams.xReadGroupParams(), offsets(STREAM_KEY_1, XREADGROUP_UNDELIVERED_ENTRY)); // verify the result contains entries from one stream // and is under the expected stream key assertThat(actualEntries, hasSize(1)); assertArrayEquals(STREAM_KEY_1, actualEntries.get(0).getKey()); assertThat(actualEntries.get(0).getValue(), equalsStreamEntries(stream1Entries)); } @Test public void xreadGroupBinaryAsMap() { stream1Entries.forEach( entry -> jedis.xadd(STREAM_KEY_1, new XAddParams().id(entry.getID()), entry.getFields())); Map> actualEntries = jedis.xreadGroupBinaryAsMap(GROUP_NAME, CONSUMER_NAME, XReadGroupParams.xReadGroupParams(), offsets(STREAM_KEY_1, XREADGROUP_UNDELIVERED_ENTRY)); assertThat(actualEntries.entrySet(), hasSize(1)); assertThat(actualEntries.get(STREAM_KEY_1), equalsStreamEntries(stream1Entries)); } @Test public void xreadGroupBinaryAsMapMultipleStreams() { // Add entries to the streams stream1Entries.forEach( entry -> jedis.xadd(STREAM_KEY_1, new XAddParams().id(entry.getID()), entry.getFields())); stream2Entries.forEach( entry -> jedis.xadd(STREAM_KEY_2, new XAddParams().id(entry.getID()), entry.getFields())); Map> actualEntries = jedis.xreadGroupBinaryAsMap(GROUP_NAME, CONSUMER_NAME, XReadGroupParams.xReadGroupParams(), offsets(STREAM_KEY_1, XREADGROUP_UNDELIVERED_ENTRY, STREAM_KEY_2, XREADGROUP_UNDELIVERED_ENTRY)); assertThat(actualEntries.entrySet(), hasSize(2)); assertThat(actualEntries.get(STREAM_KEY_1), equalsStreamEntries(stream1Entries)); assertThat(actualEntries.get(STREAM_KEY_2), equalsStreamEntries(stream2Entries)); } // ========== XACKDEL Command Tests ========== @Test @SinceRedisVersion("8.1.240") public void testXackdel() { setUpTestStream(); // Add a message to the stream byte[] messageId = jedis.xadd(STREAM_KEY_1, new XAddParams().id("1-0"), HASH_1); assertNotNull(messageId); assertEquals(1L, jedis.xlen(STREAM_KEY_1)); // Read the message with consumer group to add it to PEL Map streams = offsets(STREAM_KEY_1, XREADGROUP_UNDELIVERED_ENTRY); List>> messages = jedis.xreadGroupBinary( GROUP_NAME, CONSUMER_NAME, XReadGroupParams.xReadGroupParams().count(1), streams); assertEquals(1, messages.size()); assertEquals(1, messages.get(0).getValue().size()); byte[] readMessageId = messages.get(0).getValue().get(0).getID().toString().getBytes(); // Test XACKDEL - should acknowledge and delete the message List results = jedis.xackdel(STREAM_KEY_1, GROUP_NAME, readMessageId); assertThat(results, hasSize(1)); assertEquals(StreamEntryDeletionResult.DELETED, results.get(0)); // Verify message is deleted from stream assertEquals(0L, jedis.xlen(STREAM_KEY_1)); } @Test @SinceRedisVersion("8.1.240") public void testXackdelWithTrimMode() { setUpTestStream(); // Add multiple messages jedis.xadd(STREAM_KEY_1, new XAddParams().id("1-0"), HASH_1); jedis.xadd(STREAM_KEY_1, new XAddParams().id("2-0"), HASH_2); assertEquals(2L, jedis.xlen(STREAM_KEY_1)); // Read the messages with consumer group Map streams = offsets(STREAM_KEY_1, XREADGROUP_UNDELIVERED_ENTRY); List>> messages = jedis.xreadGroupBinary( GROUP_NAME, CONSUMER_NAME, XReadGroupParams.xReadGroupParams().count(2), streams); assertEquals(1, messages.size()); assertEquals(2, messages.get(0).getValue().size()); // Test XACKDEL with KEEP_REFERENCES mode byte[] readId1 = messages.get(0).getValue().get(0).getID().toString().getBytes(); List results = jedis.xackdel(STREAM_KEY_1, GROUP_NAME, StreamDeletionPolicy.KEEP_REFERENCES, readId1); assertThat(results, hasSize(1)); assertEquals(StreamEntryDeletionResult.DELETED, results.get(0)); // Verify one message is deleted assertEquals(1L, jedis.xlen(STREAM_KEY_1)); } @Test @SinceRedisVersion("8.1.240") public void testXackdelUnreadMessages() { setUpTestStream(); // Add test entries but don't read them byte[] id1 = jedis.xadd(STREAM_KEY_1, new XAddParams().id("1-0"), HASH_1); // Test XACKDEL on unread messages - should return NOT_FOUND for PEL List results = jedis.xackdel(STREAM_KEY_1, GROUP_NAME, id1); assertThat(results, hasSize(1)); // Should return NOT_FOUND because message was never read by the consumer group assertEquals(StreamEntryDeletionResult.NOT_FOUND, results.get(0)); // Stream should still contain the message assertEquals(1L, jedis.xlen(STREAM_KEY_1)); } @Test @SinceRedisVersion("8.1.240") public void testXackdelMultipleMessages() { setUpTestStream(); // Add multiple messages jedis.xadd(STREAM_KEY_1, new XAddParams().id("1-0"), HASH_1); jedis.xadd(STREAM_KEY_1, new XAddParams().id("2-0"), HASH_2); jedis.xadd(STREAM_KEY_1, new XAddParams().id("3-0"), HASH_1); assertEquals(3L, jedis.xlen(STREAM_KEY_1)); // Read the messages with consumer group Map streams = offsets(STREAM_KEY_1, XREADGROUP_UNDELIVERED_ENTRY); List>> messages = jedis.xreadGroupBinary( GROUP_NAME, CONSUMER_NAME, XReadGroupParams.xReadGroupParams().count(3), streams); assertEquals(1, messages.size()); assertEquals(3, messages.get(0).getValue().size()); // Test XACKDEL with multiple IDs byte[] readId1 = messages.get(0).getValue().get(0).getID().toString().getBytes(); byte[] readId2 = messages.get(0).getValue().get(1).getID().toString().getBytes(); List results = jedis.xackdel(STREAM_KEY_1, GROUP_NAME, readId1, readId2); assertThat(results, hasSize(2)); assertEquals(StreamEntryDeletionResult.DELETED, results.get(0)); assertEquals(StreamEntryDeletionResult.DELETED, results.get(1)); // Verify two messages are deleted assertEquals(1L, jedis.xlen(STREAM_KEY_1)); } // ========== XDELEX Command Tests ========== @Test @SinceRedisVersion("8.1.240") public void testXdelex() { setUpTestStream(); // Add test entries byte[] id1 = jedis.xadd(STREAM_KEY_1, new XAddParams().id("1-0"), HASH_1); jedis.xadd(STREAM_KEY_1, new XAddParams().id("2-0"), HASH_2); assertEquals(2L, jedis.xlen(STREAM_KEY_1)); // Test basic XDELEX without parameters (should behave like XDEL with KEEP_REFERENCES) List results = jedis.xdelex(STREAM_KEY_1, id1); assertThat(results, hasSize(1)); assertEquals(StreamEntryDeletionResult.DELETED, results.get(0)); // Verify entry is deleted from stream assertEquals(1L, jedis.xlen(STREAM_KEY_1)); } @Test @SinceRedisVersion("8.1.240") public void testXdelexWithTrimMode() { setUpTestStream(); // Add test entries byte[] id1 = jedis.xadd(STREAM_KEY_1, new XAddParams().id("1-0"), HASH_1); jedis.xadd(STREAM_KEY_1, new XAddParams().id("2-0"), HASH_2); // Test XDELEX with DELETE_REFERENCES mode List results = jedis.xdelex(STREAM_KEY_1, StreamDeletionPolicy.DELETE_REFERENCES, id1); assertThat(results, hasSize(1)); assertEquals(StreamEntryDeletionResult.DELETED, results.get(0)); // Verify entry is deleted from stream assertEquals(1L, jedis.xlen(STREAM_KEY_1)); } @Test @SinceRedisVersion("8.1.240") public void testXdelexMultipleEntries() { setUpTestStream(); // Add test entries byte[] id1 = jedis.xadd(STREAM_KEY_1, new XAddParams().id("1-0"), HASH_1); jedis.xadd(STREAM_KEY_1, new XAddParams().id("2-0"), HASH_2); byte[] id3 = jedis.xadd(STREAM_KEY_1, new XAddParams().id("3-0"), HASH_1); assertEquals(3L, jedis.xlen(STREAM_KEY_1)); // Test XDELEX with multiple IDs List results = jedis.xdelex(STREAM_KEY_1, id1, id3); assertThat(results, hasSize(2)); assertEquals(StreamEntryDeletionResult.DELETED, results.get(0)); assertEquals(StreamEntryDeletionResult.DELETED, results.get(1)); // Verify two entries are deleted assertEquals(1L, jedis.xlen(STREAM_KEY_1)); } @Test @SinceRedisVersion("8.1.240") public void testXdelexNonExistentEntries() { setUpTestStream(); // Add one entry byte[] id1 = jedis.xadd(STREAM_KEY_1, new XAddParams().id("1-0"), HASH_1); assertEquals(1L, jedis.xlen(STREAM_KEY_1)); // Test XDELEX with mix of existing and non-existent IDs byte[] nonExistentId = "999-0".getBytes(); List results = jedis.xdelex(STREAM_KEY_1, id1, nonExistentId); assertThat(results, hasSize(2)); assertEquals(StreamEntryDeletionResult.DELETED, results.get(0)); // Existing entry assertEquals(StreamEntryDeletionResult.NOT_FOUND, results.get(1)); // Non-existent entry // Verify existing entry is deleted assertEquals(0L, jedis.xlen(STREAM_KEY_1)); } @Test @SinceRedisVersion("8.1.240") public void testXdelexWithConsumerGroups() { setUpTestStream(); // Add test entries jedis.xadd(STREAM_KEY_1, new XAddParams().id("1-0"), HASH_1); jedis.xadd(STREAM_KEY_1, new XAddParams().id("2-0"), HASH_2); assertEquals(2L, jedis.xlen(STREAM_KEY_1)); // Read messages with consumer group to add them to PEL Map streams = offsets(STREAM_KEY_1, XREADGROUP_UNDELIVERED_ENTRY); List>> messages = jedis.xreadGroupBinary( GROUP_NAME, CONSUMER_NAME, XReadGroupParams.xReadGroupParams().count(2), streams); assertEquals(1, messages.size()); assertEquals(2, messages.get(0).getValue().size()); // Acknowledge only the first message byte[] readId1 = messages.get(0).getValue().get(0).getID().toString().getBytes(); byte[] readId2 = messages.get(0).getValue().get(1).getID().toString().getBytes(); jedis.xack(STREAM_KEY_1, GROUP_NAME, readId1); // Test XDELEX with ACKNOWLEDGED mode - should only delete acknowledged entries List results = jedis.xdelex(STREAM_KEY_1, StreamDeletionPolicy.ACKNOWLEDGED, readId1, readId2); assertThat(results, hasSize(2)); assertEquals(StreamEntryDeletionResult.DELETED, results.get(0)); // id1 was acknowledged assertEquals(StreamEntryDeletionResult.NOT_DELETED_UNACKNOWLEDGED_OR_STILL_REFERENCED, results.get(1)); // id2 not acknowledged // Verify only acknowledged entry was deleted assertEquals(1L, jedis.xlen(STREAM_KEY_1)); } @Test @SinceRedisVersion("8.1.240") public void testXdelexEmptyStream() { setUpTestStream(); // Test XDELEX on empty stream byte[] nonExistentId = "1-0".getBytes(); List results = jedis.xdelex(STREAM_KEY_1, nonExistentId); assertThat(results, hasSize(1)); assertEquals(StreamEntryDeletionResult.NOT_FOUND, results.get(0)); } // ========== XTRIM Command Tests with trimmingMode ========== @Test @SinceRedisVersion("8.1.240") public void testXtrimWithKeepReferences() { setUpTestStream(); // Add test entries for (int i = 1; i <= 5; i++) { jedis.xadd(STREAM_KEY_1, new XAddParams().id(i + "-0"), HASH_1); } assertEquals(5L, jedis.xlen(STREAM_KEY_1)); // Read messages with consumer group to create PEL entries Map streams = offsets(STREAM_KEY_1, XREADGROUP_UNDELIVERED_ENTRY); jedis.xreadGroupBinary(GROUP_NAME, CONSUMER_NAME, XReadGroupParams.xReadGroupParams().count(3), streams); // Test XTRIM with KEEP_REFERENCES mode - should preserve PEL references long trimmed = jedis.xtrim(STREAM_KEY_1, XTrimParams.xTrimParams().maxLen(3).trimmingMode( StreamDeletionPolicy.KEEP_REFERENCES)); assertEquals(2L, trimmed); // Should trim 2 entries assertEquals(3L, jedis.xlen(STREAM_KEY_1)); } @Test @SinceRedisVersion("8.1.240") public void testXtrimWithAcknowledged() { setUpTestStream(); // Add test entries for (int i = 1; i <= 5; i++) { jedis.xadd(STREAM_KEY_1, new XAddParams().id(i + "-0"), HASH_1); } assertEquals(5L, jedis.xlen(STREAM_KEY_1)); // Read messages with consumer group Map streams = offsets(STREAM_KEY_1, XREADGROUP_UNDELIVERED_ENTRY); List>> messages = jedis.xreadGroupBinary( GROUP_NAME, CONSUMER_NAME, XReadGroupParams.xReadGroupParams().count(3), streams); assertEquals(1, messages.size()); assertEquals(3, messages.get(0).getValue().size()); // Acknowledge only the first 2 messages byte[] readId1 = messages.get(0).getValue().get(0).getID().toString().getBytes(); byte[] readId2 = messages.get(0).getValue().get(1).getID().toString().getBytes(); jedis.xack(STREAM_KEY_1, GROUP_NAME, readId1, readId2); // Test XTRIM with ACKNOWLEDGED mode - should only trim acknowledged entries long trimmed = jedis.xtrim(STREAM_KEY_1, XTrimParams.xTrimParams().maxLen(3).trimmingMode( StreamDeletionPolicy.ACKNOWLEDGED)); // The exact behavior depends on implementation, but it should respect acknowledgment status assertTrue(trimmed >= 0); assertTrue(jedis.xlen(STREAM_KEY_1) <= 5); // Should not exceed original length } // ========== XREADGROUP CLAIM Tests ========== @Test @SinceRedisVersion(V8_4_0_STRING) public void xreadgroupClaimReturnsMetadataOrdered() throws InterruptedException { setUpTestStream("0-0".getBytes()); final byte[] CONSUMER_1 = "consumer-1".getBytes(); final byte[] CONSUMER_2 = "consumer-2".getBytes(); final long IDLE_TIME_MS = 5; // Produce two entries jedis.xadd(STREAM_KEY_1, new XAddParams().id(StreamEntryID.NEW_ENTRY), HASH_1); jedis.xadd(STREAM_KEY_1, new XAddParams().id(StreamEntryID.NEW_ENTRY), HASH_1); Map streams = singletonMap(STREAM_KEY_1, StreamEntryID.XREADGROUP_UNDELIVERED_ENTRY); jedis.xreadGroupBinary(GROUP_NAME, CONSUMER_1, XReadGroupParams.xReadGroupParams().count(10), streams); // Ensure idle time so entries are claimable Thread.sleep(IDLE_TIME_MS); // Produce fresh entries that are NOT claimed (not pending) jedis.xadd(STREAM_KEY_1, new XAddParams().id(StreamEntryID.NEW_ENTRY), HASH_1); jedis.xadd(STREAM_KEY_1, new XAddParams().id(StreamEntryID.NEW_ENTRY), HASH_1); // Read with consumer-2 using CLAIM List>> consumer2Result = jedis.xreadGroupBinary( GROUP_NAME, CONSUMER_2, XReadGroupParams.xReadGroupParams().claim(IDLE_TIME_MS).count(10), streams); assertNotNull(consumer2Result); assertEquals(1, consumer2Result.size()); List entries = consumer2Result.get(0).getValue(); assertEquals(4, entries.size()); long claimedCount = entries.stream().filter(StreamEntryBinary::isClaimed).count(); long freshCount = entries.size() - claimedCount; assertEquals(2, claimedCount); assertEquals(2, freshCount); // Assert order: pending entries are first StreamEntryBinary first = entries.get(0); StreamEntryBinary second = entries.get(1); StreamEntryBinary third = entries.get(2); StreamEntryBinary fourth = entries.get(3); // Claimed entries assertTrue(first.isClaimed()); assertTrue(second.isClaimed()); assertTrue(first.getMillisElapsedFromDelivery() >= IDLE_TIME_MS); assertTrue(second.getMillisElapsedFromDelivery() >= IDLE_TIME_MS); assertThat(first.getFields(), contentEquals(HASH_1)); // Fresh entries assertFalse(third.isClaimed()); assertFalse(fourth.isClaimed()); assertEquals(Long.valueOf(0), third.getDeliveredCount()); assertEquals(Long.valueOf(0), fourth.getDeliveredCount()); assertEquals(Long.valueOf(0), third.getMillisElapsedFromDelivery()); assertEquals(Long.valueOf(0), fourth.getMillisElapsedFromDelivery()); assertThat(fourth.getFields(), contentEquals(HASH_1)); } @Test public void xreadGroupPreservesFieldOrder() { byte[] streamKey = "field-order-stream".getBytes(); byte[] groupName = "field-order-group".getBytes(); byte[] consumerName = "field-order-consumer".getBytes(); // Use LinkedHashMap to ensure insertion order: a, z, m Map fields = new LinkedHashMap<>(); fields.put("a".getBytes(), "1".getBytes()); fields.put("z".getBytes(), "4".getBytes()); fields.put("m".getBytes(), "2".getBytes()); jedis.xadd(streamKey, XAddParams.xAddParams().id(StreamEntryID.NEW_ENTRY), fields); jedis.xgroupCreate(streamKey, groupName, "0-0".getBytes(), false); Map streamQuery = singletonMap(streamKey, StreamEntryID.XREADGROUP_UNDELIVERED_ENTRY); List>> result = jedis.xreadGroupBinary(groupName, consumerName, XReadGroupParams.xReadGroupParams().count(1), streamQuery); assertEquals(1, result.size()); assertEquals(1, result.get(0).getValue().size()); StreamEntryBinary entry = result.get(0).getValue().get(0); Map returnedFields = entry.getFields(); // Verify field order is preserved - this will fail with HashMap, pass with LinkedHashMap byte[][] expectedOrder = {"a".getBytes(), "z".getBytes(), "m".getBytes()}; byte[][] actualOrder = returnedFields.keySet().toArray(new byte[0][]); assertEquals(expectedOrder.length, actualOrder.length, "Field count should match"); for (int i = 0; i < expectedOrder.length; i++) { assertArrayEquals(expectedOrder[i], actualOrder[i], String.format("Field order mismatch at position %d: expected '%s' but got '%s'. " + "Full order: expected [a, z, m], actual %s", i, new String(expectedOrder[i]), new String(actualOrder[i]), java.util.Arrays.toString(java.util.Arrays.stream(actualOrder).map(String::new).toArray()))); } } @Test public void xreadBinaryAsMapPreservesStreamOrder() { // Test that xreadBinaryAsMap preserves the order of streams when reading from multiple streams byte[] streamKey1 = "{stream-order}-test-1".getBytes(); byte[] streamKey2 = "{stream-order}-test-2".getBytes(); byte[] streamKey3 = "{stream-order}-test-3".getBytes(); // Add entries to streams in specific order Map fields = new LinkedHashMap<>(); fields.put("field".getBytes(), "value1".getBytes()); jedis.xadd(streamKey1, XAddParams.xAddParams().id(StreamEntryID.NEW_ENTRY), fields); fields.put("field".getBytes(), "value2".getBytes()); jedis.xadd(streamKey2, XAddParams.xAddParams().id(StreamEntryID.NEW_ENTRY), fields); fields.put("field".getBytes(), "value3".getBytes()); jedis.xadd(streamKey3, XAddParams.xAddParams().id(StreamEntryID.NEW_ENTRY), fields); // Read from multiple streams in specific order Map streams = new LinkedHashMap<>(); streams.put(streamKey1, new StreamEntryID("0-0")); streams.put(streamKey2, new StreamEntryID("0-0")); streams.put(streamKey3, new StreamEntryID("0-0")); Map> result = jedis.xreadBinaryAsMap( XReadParams.xReadParams().count(10), streams); assertNotNull(result); assertEquals(3, result.size()); // Verify that the order of streams in the result matches the order in the request byte[][] expectedOrder = {streamKey1, streamKey2, streamKey3}; byte[][] actualOrder = result.keySet().toArray(new byte[0][]); assertEquals(expectedOrder.length, actualOrder.length, "Stream count should match"); for (int i = 0; i < expectedOrder.length; i++) { assertArrayEquals(expectedOrder[i], actualOrder[i], String.format("Stream order mismatch at position %d: expected '%s' but got '%s'", i, new String(expectedOrder[i]), new String(actualOrder[i]))); } } // ========== Idempotent Producer Tests ========== @Test @EnabledOnCommand("XCFGSET") public void testXaddIdmpAuto() { // Add entry with IDMPAUTO Map message = new HashMap<>(); message.put("order".getBytes(), "12345".getBytes()); message.put("amount".getBytes(), "100.00".getBytes()); byte[] id1 = jedis.xadd(STREAM_KEY_1, XAddParams.xAddParams().idmpAuto("producer-1".getBytes()), message); assertNotNull(id1); assertEquals(1L, jedis.xlen(STREAM_KEY_1)); // Add same message again with same producer - should return same ID (duplicate detected) byte[] id2 = jedis.xadd(STREAM_KEY_1, XAddParams.xAddParams().idmpAuto("producer-1".getBytes()), message); assertArrayEquals(id1, id2); // Duplicate returns same ID assertEquals(1L, jedis.xlen(STREAM_KEY_1)); // Stream length unchanged // Add same message with different producer - should succeed byte[] id3 = jedis.xadd(STREAM_KEY_1, XAddParams.xAddParams().idmpAuto("producer-2".getBytes()), message); assertNotNull(id3); assertEquals(2L, jedis.xlen(STREAM_KEY_1)); // Add different message with same producer - should succeed Map message2 = new HashMap<>(); message2.put("order".getBytes(), "67890".getBytes()); message2.put("amount".getBytes(), "200.00".getBytes()); byte[] id4 = jedis.xadd(STREAM_KEY_1, XAddParams.xAddParams().idmpAuto("producer-1".getBytes()), message2); assertNotNull(id4); assertEquals(3L, jedis.xlen(STREAM_KEY_1)); } @Test @EnabledOnCommand("XCFGSET") public void testXaddIdmp() { // Add entry with explicit idempotent ID byte[] id1 = jedis.xadd(STREAM_KEY_1, XAddParams.xAddParams().idmp("producer-1".getBytes(), "iid-001".getBytes()), HASH_1); assertNotNull(id1); assertEquals(1L, jedis.xlen(STREAM_KEY_1)); // Add with same producer and idempotent ID - should return same ID (duplicate detected) byte[] id2 = jedis.xadd(STREAM_KEY_1, XAddParams.xAddParams().idmp("producer-1".getBytes(), "iid-001".getBytes()), HASH_2); assertArrayEquals(id1, id2); // Duplicate returns same ID assertEquals(1L, jedis.xlen(STREAM_KEY_1)); // Add with same producer but different idempotent ID - should succeed byte[] id3 = jedis.xadd(STREAM_KEY_1, XAddParams.xAddParams().idmp("producer-1".getBytes(), "iid-002".getBytes()), HASH_1); assertNotNull(id3); assertEquals(2L, jedis.xlen(STREAM_KEY_1)); // Add with different producer but same idempotent ID - should succeed byte[] id4 = jedis.xadd(STREAM_KEY_1, XAddParams.xAddParams().idmp("producer-2".getBytes(), "iid-001".getBytes()), HASH_1); assertNotNull(id4); assertEquals(3L, jedis.xlen(STREAM_KEY_1)); } @Test @EnabledOnCommand("XCFGSET") public void testXcfgset() { // Add an entry to create the stream jedis.xadd(STREAM_KEY_1, new XAddParams().id(StreamEntryID.NEW_ENTRY), HASH_1); // Configure idempotent producer settings byte[] result = jedis.xcfgset(STREAM_KEY_1, XCfgSetParams.xCfgSetParams().idmpDuration(1000).idmpMaxsize(500)); assertArrayEquals("OK".getBytes(), result); } @Test @EnabledOnCommand("XCFGSET") public void testXaddIdmpWithTrimming() { // Add first entry with IDMPAUTO and trimming byte[] id1 = jedis.xadd(STREAM_KEY_1, XAddParams.xAddParams().idmpAuto("producer-1".getBytes()).maxLen(2), HASH_1); assertNotNull(id1); assertEquals(1, jedis.xlen(STREAM_KEY_1)); // Add duplicate - should return same ID and not add new entry byte[] id2 = jedis.xadd(STREAM_KEY_1, XAddParams.xAddParams().idmpAuto("producer-1".getBytes()).maxLen(2), HASH_1); assertArrayEquals(id1, id2); // Duplicate returns same ID assertEquals(1, jedis.xlen(STREAM_KEY_1)); // Still 1 entry // Add different message - should add new entry byte[] id3 = jedis.xadd(STREAM_KEY_1, XAddParams.xAddParams().idmpAuto("producer-1".getBytes()).maxLen(2), HASH_2); assertNotNull(id3); assertNotEquals(new String(id1), new String(id3)); // Different IDs assertEquals(2, jedis.xlen(STREAM_KEY_1)); // Now 2 entries } } ================================================ FILE: src/test/java/redis/clients/jedis/commands/unified/StreamsCommandsTestBase.java ================================================ package redis.clients.jedis.commands.unified; import io.redis.test.annotations.EnabledOnCommand; import io.redis.test.annotations.SinceRedisVersion; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Tag; import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.CsvSource; import redis.clients.jedis.RedisProtocol; import redis.clients.jedis.StreamEntryID; import redis.clients.jedis.args.StreamDeletionPolicy; import redis.clients.jedis.exceptions.JedisDataException; import redis.clients.jedis.params.*; import redis.clients.jedis.resps.*; import java.util.HashMap; import java.util.LinkedHashMap; import java.util.List; import java.util.Map; import static io.redis.test.utils.RedisVersion.V8_4_0_STRING; import static java.util.Collections.singletonMap; import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.hasSize; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertNotEquals; import static org.junit.jupiter.api.Assertions.assertNotNull; import static org.junit.jupiter.api.Assertions.assertNull; import static org.junit.jupiter.api.Assertions.assertTrue; import static org.junit.jupiter.api.Assertions.fail; @Tag("integration") public abstract class StreamsCommandsTestBase extends UnifiedJedisCommandsTestBase { protected static final String STREAM_KEY_1 = "{stream}-1"; protected static final String STREAM_KEY_2 = "{stream}-2"; protected static final String GROUP_NAME = "group-1"; protected static final String CONSUMER_NAME = "consumer-1"; protected static final String FIELD_KEY_1 = "field-1"; protected static final String VALUE_1 = "value-1"; protected static final String FIELD_KEY_2 = "field-2"; protected static final String VALUE_2 = "value-2"; protected static final Map HASH_1 = singletonMap(FIELD_KEY_1, VALUE_1); protected static final Map HASH_2 = singletonMap(FIELD_KEY_2, VALUE_2); public StreamsCommandsTestBase(RedisProtocol protocol) { super(protocol); } /** * Populates a test stream with values using the i-0 format * @param streamKey The stream key to populate * @param count Number of entries to add * @param map Map of field-value pairs for each entry */ protected void populateTestStreamWithValues(String streamKey, int count, Map map) { for (int i = 1; i <= count; i++) { jedis.xadd(streamKey, XAddParams.xAddParams().id(new StreamEntryID(i + "-0")), map); } assertEquals(count, jedis.xlen(streamKey)); } @BeforeEach public void setUp() { setUpTestStream(); } private void setUpTestStream() { setUpTestStream(StreamEntryID.XGROUP_LAST_ENTRY); } private void setUpTestStream(StreamEntryID startId) { jedis.del(STREAM_KEY_1); jedis.del(STREAM_KEY_2); try { jedis.xgroupCreate(STREAM_KEY_1, GROUP_NAME, startId, true); } catch (JedisDataException e) { if (!e.getMessage().contains("BUSYGROUP")) { throw e; } } try { jedis.xgroupCreate(STREAM_KEY_2, GROUP_NAME, startId, true); } catch (JedisDataException e) { if (!e.getMessage().contains("BUSYGROUP")) { throw e; } } } // ========== XADD Command Tests ========== @Test public void xaddBasic() { setUpTestStream(); // Test basic XADD with auto-generated ID StreamEntryID id1 = jedis.xadd(STREAM_KEY_1, StreamEntryID.NEW_ENTRY, HASH_1); assertNotNull(id1); assertEquals(1L, jedis.xlen(STREAM_KEY_1)); // Test XADD with multiple fields Map multiFieldHash = new HashMap<>(); multiFieldHash.put("field1", "value1"); multiFieldHash.put("field2", "value2"); multiFieldHash.put("field3", "value3"); StreamEntryID id2 = jedis.xadd(STREAM_KEY_1, StreamEntryID.NEW_ENTRY, multiFieldHash); assertNotNull(id2); assertTrue(id2.compareTo(id1) > 0); assertEquals(2L, jedis.xlen(STREAM_KEY_1)); } @Test public void xaddWithSpecificId() { setUpTestStream(); // Test XADD with specific ID StreamEntryID specificId = new StreamEntryID("1000-0"); StreamEntryID resultId = jedis.xadd(STREAM_KEY_1, specificId, HASH_1); assertEquals(specificId, resultId); assertEquals(1L, jedis.xlen(STREAM_KEY_1)); // Test XADD with ID that must be greater than previous StreamEntryID nextId = new StreamEntryID("1001-0"); StreamEntryID resultId2 = jedis.xadd(STREAM_KEY_1, nextId, HASH_2); assertEquals(nextId, resultId2); assertEquals(2L, jedis.xlen(STREAM_KEY_1)); } @Test public void xaddWithParams() { setUpTestStream(); // Test XADD with maxLen parameter populateTestStreamWithValues(STREAM_KEY_1, 5, HASH_1); // Add with maxLen=3, should trim to 3 entries StreamEntryID id6 = jedis.xadd(STREAM_KEY_1, XAddParams.xAddParams().id(new StreamEntryID("6-0")).maxLen(3), HASH_2); assertNotNull(id6); assertEquals(3L, jedis.xlen(STREAM_KEY_1)); } @Test public void xaddErrorCases() { setUpTestStream(); // Test XADD with empty hash should fail try { Map emptyHash = new HashMap<>(); jedis.xadd(STREAM_KEY_1, StreamEntryID.NEW_ENTRY, emptyHash); fail("Should throw JedisDataException for empty hash"); } catch (JedisDataException expected) { assertTrue(expected.getMessage().contains("wrong number of arguments")); } // Test XADD with noMkStream on non-existent stream StreamEntryID result = jedis.xadd("non-existent-stream", XAddParams.xAddParams().noMkStream(), HASH_1); assertNull(result); } @ParameterizedTest @CsvSource({ "KEEP_REFERENCES,3", "DELETE_REFERENCES,0" }) @SinceRedisVersion("8.1.240") public void xaddWithTrimmingMode(StreamDeletionPolicy trimMode, int expected) { setUpTestStream(); Map map = singletonMap("field", "value"); // Add initial entries to the stream populateTestStreamWithValues(STREAM_KEY_1, 5, map); // Create consumer group and read messages to create PEL entries Map streamQuery = singletonMap(STREAM_KEY_1, StreamEntryID.XREADGROUP_UNDELIVERED_ENTRY); jedis.xreadGroup(GROUP_NAME, CONSUMER_NAME, XReadGroupParams.xReadGroupParams().count(3), streamQuery); // Verify PEL has entries List pendingBefore = jedis.xpending(STREAM_KEY_1, GROUP_NAME, XPendingParams.xPendingParams().count(10)); assertEquals(3, pendingBefore.size()); // Add new entry with maxLen=3 and KEEP_REFERENCES mode StreamEntryID newId = jedis.xadd(STREAM_KEY_1, XAddParams.xAddParams().id(new StreamEntryID("6-0")).maxLen(3).trimmingMode(trimMode), map); assertNotNull(newId); // Stream should be trimmed to 3 entries assertEquals(3L, jedis.xlen(STREAM_KEY_1)); List pendingAfter = jedis.xpending(STREAM_KEY_1, GROUP_NAME, XPendingParams.xPendingParams().count(10)); assertEquals(expected, pendingAfter.size()); } @Test @SinceRedisVersion("8.1.240") public void xaddWithTrimmingModeAcknowledged() { setUpTestStream(); Map map = singletonMap("field", "value"); // Add initial entries to the stream populateTestStreamWithValues(STREAM_KEY_1, 5, map); // Create consumer group and read messages Map streamQuery = singletonMap(STREAM_KEY_1, StreamEntryID.XREADGROUP_UNDELIVERED_ENTRY); List>> messages = jedis.xreadGroup(GROUP_NAME, CONSUMER_NAME, XReadGroupParams.xReadGroupParams().count(3), streamQuery); // Acknowledge the first 2 messages StreamEntryID id1 = messages.get(0).getValue().get(0).getID(); StreamEntryID id2 = messages.get(0).getValue().get(1).getID(); jedis.xack(STREAM_KEY_1, GROUP_NAME, id1, id2); // Verify PEL state List pendingBefore = jedis.xpending(STREAM_KEY_1, GROUP_NAME, XPendingParams.xPendingParams().count(10)); assertEquals(1, pendingBefore.size()); // Only 1 unacknowledged message // Add new entry with maxLen=3 and ACKNOWLEDGED mode StreamEntryID newId = jedis.xadd(STREAM_KEY_1, XAddParams.xAddParams() .id(new StreamEntryID("6-0")).maxLen(3).trimmingMode(StreamDeletionPolicy.ACKNOWLEDGED), map); assertNotNull(newId); // Stream length should respect acknowledgment status long streamLen = jedis.xlen(STREAM_KEY_1); assertEquals(4, streamLen); // Should not trim unacknowledged entries aggressively // PEL should still contain unacknowledged entries List pendingAfter = jedis.xpending(STREAM_KEY_1, GROUP_NAME, XPendingParams.xPendingParams().count(10)); assertEquals(1, pendingAfter.size()); // Unacknowledged entries should remain } // ========== XTRIM Command Tests ========== @Test public void xtrimBasic() { setUpTestStream(); // Add test entries populateTestStreamWithValues(STREAM_KEY_1, 5, HASH_1); // Test basic XTRIM with maxLen long trimmed = jedis.xtrim(STREAM_KEY_1, 3, false); assertEquals(2L, trimmed); // Should trim 2 entries assertEquals(3L, jedis.xlen(STREAM_KEY_1)); } @Test public void xtrimWithParams() { setUpTestStream(); // Add test entries with specific IDs populateTestStreamWithValues(STREAM_KEY_1, 5, HASH_1); // Test XTRIM with XTrimParams and exact trimming long trimmed = jedis.xtrim(STREAM_KEY_1, XTrimParams.xTrimParams().maxLen(3).exactTrimming()); assertEquals(2L, trimmed); assertEquals(3L, jedis.xlen(STREAM_KEY_1)); // Test XTRIM with minId - use "4-0" since we have entries 1-0, 2-0, 3-0, 4-0, 5-0 long trimmed2 = jedis.xtrim(STREAM_KEY_1, XTrimParams.xTrimParams().minId("4-0").exactTrimming()); assertEquals(1L, trimmed2); // Should trim entries with ID < 4-0 (only 3-0 should be trimmed) assertEquals(2L, jedis.xlen(STREAM_KEY_1)); } @Test public void xtrimApproximate() { setUpTestStream(); // Add many entries populateTestStreamWithValues(STREAM_KEY_1, 10, HASH_1); // Test approximate trimming long trimmed = jedis.xtrim(STREAM_KEY_1, 5, true); assertTrue(trimmed >= 0); // Approximate trimming may trim different amounts assertTrue(jedis.xlen(STREAM_KEY_1) <= 10); // Should not exceed original length } @ParameterizedTest @CsvSource({ "KEEP_REFERENCES,3", "DELETE_REFERENCES,1" }) @SinceRedisVersion("8.1.240") public void xaddWithMinIdTrimmingMode(StreamDeletionPolicy trimMode, int expected) { setUpTestStream(); Map map = singletonMap("field", "value"); // Add initial entries with specific IDs populateTestStreamWithValues(STREAM_KEY_1, 5, map); // Create consumer group and read messages to create PEL entries Map streamQuery = singletonMap(STREAM_KEY_1, StreamEntryID.XREADGROUP_UNDELIVERED_ENTRY); jedis.xreadGroup(GROUP_NAME, CONSUMER_NAME, XReadGroupParams.xReadGroupParams().count(3), streamQuery); // Verify PEL has entries List pendingBefore = jedis.xpending(STREAM_KEY_1, GROUP_NAME, XPendingParams.xPendingParams().count(10)); assertEquals(3, pendingBefore.size()); // Add new entry with minId="3-0" and specified trimming mode StreamEntryID newId = jedis.xadd(STREAM_KEY_1, XAddParams.xAddParams().id(new StreamEntryID("6-0")).minId("3-0").trimmingMode(trimMode), map); assertNotNull(newId); // Stream should have entries >= 3-0 plus the new entry long streamLen = jedis.xlen(STREAM_KEY_1); assertTrue(streamLen >= 3); // Check PEL entries based on trimming mode List pendingAfter = jedis.xpending(STREAM_KEY_1, GROUP_NAME, XPendingParams.xPendingParams().count(10)); assertEquals(expected, pendingAfter.size()); } @Test @SinceRedisVersion("8.1.240") public void xaddWithApproximateTrimmingAndTrimmingMode() { setUpTestStream(); Map map = singletonMap("field", "value"); // Add initial entries populateTestStreamWithValues(STREAM_KEY_1, 10, map); // Create consumer group and read messages Map streamQuery = singletonMap(STREAM_KEY_1, StreamEntryID.XREADGROUP_UNDELIVERED_ENTRY); jedis.xreadGroup(GROUP_NAME, CONSUMER_NAME, XReadGroupParams.xReadGroupParams().count(5), streamQuery); // Add new entry with approximate trimming and KEEP_REFERENCES mode StreamEntryID newId = jedis.xadd(STREAM_KEY_1, XAddParams.xAddParams().id(new StreamEntryID("11-0")).maxLen(5).approximateTrimming() .trimmingMode(StreamDeletionPolicy.KEEP_REFERENCES), map); assertNotNull(newId); // With approximate trimming, the exact length may vary but should be around the target long streamLen = jedis.xlen(STREAM_KEY_1); assertTrue(streamLen >= 5); // Should be approximately 5, but may be more due to approximation // PEL should preserve references List pendingAfter = jedis.xpending(STREAM_KEY_1, GROUP_NAME, XPendingParams.xPendingParams().count(10)); assertEquals(5, pendingAfter.size()); // All read messages should remain in PEL } @Test @SinceRedisVersion("8.1.240") public void xaddWithExactTrimmingAndTrimmingMode() { setUpTestStream(); Map map = singletonMap("field", "value"); // Add initial entries populateTestStreamWithValues(STREAM_KEY_1, 5, map); // Create consumer group and read messages Map streamQuery = singletonMap(STREAM_KEY_1, StreamEntryID.XREADGROUP_UNDELIVERED_ENTRY); jedis.xreadGroup(GROUP_NAME, CONSUMER_NAME, XReadGroupParams.xReadGroupParams().count(3), streamQuery); // Add new entry with exact trimming and DELETE_REFERENCES mode StreamEntryID newId = jedis.xadd(STREAM_KEY_1, XAddParams.xAddParams().id(new StreamEntryID("6-0")).maxLen(3).exactTrimming() .trimmingMode(StreamDeletionPolicy.DELETE_REFERENCES), map); assertNotNull(newId); // With exact trimming, stream should be exactly 3 entries assertEquals(3L, jedis.xlen(STREAM_KEY_1)); // PEL references should be cleaned up for trimmed entries List pendingAfter = jedis.xpending(STREAM_KEY_1, GROUP_NAME, XPendingParams.xPendingParams().count(10)); // Only entries that still exist in the stream should remain in PEL assertTrue(pendingAfter.size() <= 3); } @Test @SinceRedisVersion("8.1.240") public void xaddWithLimitAndTrimmingMode() { setUpTestStream(); Map map = singletonMap("field", "value"); // Add initial entries populateTestStreamWithValues(STREAM_KEY_1, 10, map); Map streamQuery = singletonMap(STREAM_KEY_1, StreamEntryID.XREADGROUP_UNDELIVERED_ENTRY); jedis.xreadGroup(GROUP_NAME, CONSUMER_NAME, XReadGroupParams.xReadGroupParams().count(5), streamQuery); // Add new entry with limit and KEEP_REFERENCES mode (limit requires approximate trimming) StreamEntryID newId = jedis.xadd(STREAM_KEY_1, XAddParams.xAddParams().id(new StreamEntryID("11-0")).maxLen(5).approximateTrimming() // Required // for // limit // to // work .limit(2) // Limit the number of entries to examine for trimming .trimmingMode(StreamDeletionPolicy.KEEP_REFERENCES), map); assertNotNull(newId); // With limit, trimming may be less aggressive long streamLen = jedis.xlen(STREAM_KEY_1); assertTrue(streamLen >= 5); // Should be at least 5, but may be more due to limit // PEL should preserve references List pendingAfter = jedis.xpending(STREAM_KEY_1, GROUP_NAME, XPendingParams.xPendingParams().count(10)); assertEquals(5, pendingAfter.size()); // All read messages should remain in PEL } // ========== XACK Command Tests ========== @Test public void xackBasic() { setUpTestStream(); // Add a message to the stream StreamEntryID messageId = jedis.xadd(STREAM_KEY_1, StreamEntryID.NEW_ENTRY, HASH_1); assertNotNull(messageId); // Consumer group already created in setUpTestStream(), just read message Map streams = singletonMap(STREAM_KEY_1, StreamEntryID.XREADGROUP_UNDELIVERED_ENTRY); List>> messages = jedis.xreadGroup(GROUP_NAME, CONSUMER_NAME, XReadGroupParams.xReadGroupParams().count(1), streams); assertEquals(1, messages.size()); assertEquals(1, messages.get(0).getValue().size()); StreamEntryID readMessageId = messages.get(0).getValue().get(0).getID(); // Test XACK long acked = jedis.xack(STREAM_KEY_1, GROUP_NAME, readMessageId); assertEquals(1L, acked); } @Test public void xackMultipleMessages() { setUpTestStream(); // Add multiple messages StreamEntryID id1 = jedis.xadd(STREAM_KEY_1, new StreamEntryID("1-0"), HASH_1); StreamEntryID id2 = jedis.xadd(STREAM_KEY_1, new StreamEntryID("2-0"), HASH_2); // Consumer group already created in setUpTestStream(), just read messages Map streams = singletonMap(STREAM_KEY_1, StreamEntryID.XREADGROUP_UNDELIVERED_ENTRY); List>> messages = jedis.xreadGroup(GROUP_NAME, CONSUMER_NAME, XReadGroupParams.xReadGroupParams().count(2), streams); assertEquals(1, messages.size()); assertEquals(2, messages.get(0).getValue().size()); // Test XACK with multiple IDs StreamEntryID readId1 = messages.get(0).getValue().get(0).getID(); StreamEntryID readId2 = messages.get(0).getValue().get(1).getID(); long acked = jedis.xack(STREAM_KEY_1, GROUP_NAME, readId1, readId2); assertEquals(2L, acked); } @Test public void xackNonExistentMessage() { setUpTestStream(); // Consumer group already created in setUpTestStream() // Test XACK with non-existent message ID StreamEntryID nonExistentId = new StreamEntryID("999-0"); long acked = jedis.xack(STREAM_KEY_1, GROUP_NAME, nonExistentId); assertEquals(0L, acked); // Should return 0 for non-existent message } // ========== XDEL Command Tests ========== @Test public void xdelBasic() { setUpTestStream(); // Add test entries StreamEntryID id1 = jedis.xadd(STREAM_KEY_1, new StreamEntryID("1-0"), HASH_1); StreamEntryID id2 = jedis.xadd(STREAM_KEY_1, new StreamEntryID("2-0"), HASH_2); assertEquals(2L, jedis.xlen(STREAM_KEY_1)); // Test XDEL with single ID long deleted = jedis.xdel(STREAM_KEY_1, id1); assertEquals(1L, deleted); assertEquals(1L, jedis.xlen(STREAM_KEY_1)); } @Test public void xdelMultipleEntries() { setUpTestStream(); // Add test entries StreamEntryID id1 = jedis.xadd(STREAM_KEY_1, new StreamEntryID("1-0"), HASH_1); StreamEntryID id2 = jedis.xadd(STREAM_KEY_1, new StreamEntryID("2-0"), HASH_2); StreamEntryID id3 = jedis.xadd(STREAM_KEY_1, new StreamEntryID("3-0"), HASH_1); assertEquals(3L, jedis.xlen(STREAM_KEY_1)); // Test XDEL with multiple IDs long deleted = jedis.xdel(STREAM_KEY_1, id1, id3); assertEquals(2L, deleted); assertEquals(1L, jedis.xlen(STREAM_KEY_1)); } @Test public void xdelNonExistentEntries() { setUpTestStream(); // Add one entry StreamEntryID id1 = jedis.xadd(STREAM_KEY_1, new StreamEntryID("1-0"), HASH_1); assertEquals(1L, jedis.xlen(STREAM_KEY_1)); // Test XDEL with mix of existing and non-existent IDs StreamEntryID nonExistentId = new StreamEntryID("999-0"); long deleted = jedis.xdel(STREAM_KEY_1, id1, nonExistentId); assertEquals(1L, deleted); // Should only delete the existing entry assertEquals(0L, jedis.xlen(STREAM_KEY_1)); } @Test public void xdelEmptyStream() { setUpTestStream(); // Test XDEL on empty stream StreamEntryID nonExistentId = new StreamEntryID("1-0"); long deleted = jedis.xdel(STREAM_KEY_1, nonExistentId); assertEquals(0L, deleted); } // ========== XACKDEL Command Tests ========== @Test @SinceRedisVersion("8.1.240") public void xackdelBasic() { setUpTestStream(); // Add a message to the stream StreamEntryID messageId = jedis.xadd(STREAM_KEY_1, new StreamEntryID("1-0"), HASH_1); assertNotNull(messageId); // Consumer group already created in setUpTestStream(), read message Map streams = singletonMap(STREAM_KEY_1, StreamEntryID.XREADGROUP_UNDELIVERED_ENTRY); List>> messages = jedis.xreadGroup(GROUP_NAME, CONSUMER_NAME, XReadGroupParams.xReadGroupParams().count(1), streams); assertEquals(1, messages.size()); assertEquals(1, messages.get(0).getValue().size()); StreamEntryID readMessageId = messages.get(0).getValue().get(0).getID(); // Test XACKDEL - should acknowledge and delete the message List results = jedis.xackdel(STREAM_KEY_1, GROUP_NAME, readMessageId); assertThat(results, hasSize(1)); assertEquals(StreamEntryDeletionResult.DELETED, results.get(0)); // Verify message is deleted from stream assertEquals(0L, jedis.xlen(STREAM_KEY_1)); } @Test @SinceRedisVersion("8.1.240") public void xackdelWithTrimMode() { setUpTestStream(); // Add multiple messages StreamEntryID id1 = jedis.xadd(STREAM_KEY_1, new StreamEntryID("1-0"), HASH_1); StreamEntryID id2 = jedis.xadd(STREAM_KEY_1, new StreamEntryID("2-0"), HASH_2); // Consumer group already created, read messages Map streams = singletonMap(STREAM_KEY_1, StreamEntryID.XREADGROUP_UNDELIVERED_ENTRY); List>> messages = jedis.xreadGroup(GROUP_NAME, CONSUMER_NAME, XReadGroupParams.xReadGroupParams().count(2), streams); assertEquals(1, messages.size()); assertEquals(2, messages.get(0).getValue().size()); // Test XACKDEL with KEEP_REFERENCES mode StreamEntryID readId1 = messages.get(0).getValue().get(0).getID(); List results = jedis.xackdel(STREAM_KEY_1, GROUP_NAME, StreamDeletionPolicy.KEEP_REFERENCES, readId1); assertThat(results, hasSize(1)); assertEquals(StreamEntryDeletionResult.DELETED, results.get(0)); // Verify one message is deleted assertEquals(1L, jedis.xlen(STREAM_KEY_1)); } @Test @SinceRedisVersion("8.1.240") public void xackdelUnreadMessages() { setUpTestStream(); // Add test entries but don't read them StreamEntryID id1 = jedis.xadd(STREAM_KEY_1, new StreamEntryID("1-0"), HASH_1); // Test XACKDEL on unread messages - should return NOT_FOUND for PEL List results = jedis.xackdel(STREAM_KEY_1, GROUP_NAME, id1); assertThat(results, hasSize(1)); // Should return NOT_FOUND because message was never read by the consumer group assertEquals(StreamEntryDeletionResult.NOT_FOUND, results.get(0)); // Stream should still contain the message assertEquals(1L, jedis.xlen(STREAM_KEY_1)); } @Test @SinceRedisVersion("8.1.240") public void xackdelMultipleMessages() { setUpTestStream(); // Add multiple messages StreamEntryID id1 = jedis.xadd(STREAM_KEY_1, new StreamEntryID("1-0"), HASH_1); StreamEntryID id2 = jedis.xadd(STREAM_KEY_1, new StreamEntryID("2-0"), HASH_2); StreamEntryID id3 = jedis.xadd(STREAM_KEY_1, new StreamEntryID("3-0"), HASH_1); // Read all messages Map streams = singletonMap(STREAM_KEY_1, StreamEntryID.XREADGROUP_UNDELIVERED_ENTRY); List>> messages = jedis.xreadGroup(GROUP_NAME, CONSUMER_NAME, XReadGroupParams.xReadGroupParams().count(3), streams); assertEquals(1, messages.size()); assertEquals(3, messages.get(0).getValue().size()); // Test XACKDEL with multiple IDs StreamEntryID readId1 = messages.get(0).getValue().get(0).getID(); StreamEntryID readId2 = messages.get(0).getValue().get(1).getID(); List results = jedis.xackdel(STREAM_KEY_1, GROUP_NAME, readId1, readId2); assertThat(results, hasSize(2)); assertEquals(StreamEntryDeletionResult.DELETED, results.get(0)); assertEquals(StreamEntryDeletionResult.DELETED, results.get(1)); // Verify two messages are deleted assertEquals(1L, jedis.xlen(STREAM_KEY_1)); } // ========== XDELEX Command Tests ========== @Test @SinceRedisVersion("8.1.240") public void xdelexBasic() { setUpTestStream(); // Add test entries StreamEntryID id1 = jedis.xadd(STREAM_KEY_1, new StreamEntryID("1-0"), HASH_1); StreamEntryID id2 = jedis.xadd(STREAM_KEY_1, new StreamEntryID("2-0"), HASH_2); assertEquals(2L, jedis.xlen(STREAM_KEY_1)); // Test basic XDELEX without parameters (should behave like XDEL with KEEP_REFERENCES) List results = jedis.xdelex(STREAM_KEY_1, id1); assertThat(results, hasSize(1)); assertEquals(StreamEntryDeletionResult.DELETED, results.get(0)); // Verify entry is deleted from stream assertEquals(1L, jedis.xlen(STREAM_KEY_1)); } @Test @SinceRedisVersion("8.1.240") public void xdelexWithTrimMode() { setUpTestStream(); // Add test entries StreamEntryID id1 = jedis.xadd(STREAM_KEY_1, new StreamEntryID("1-0"), HASH_1); StreamEntryID id2 = jedis.xadd(STREAM_KEY_1, new StreamEntryID("2-0"), HASH_2); // Test XDELEX with DELETE_REFERENCES mode List results = jedis.xdelex(STREAM_KEY_1, StreamDeletionPolicy.DELETE_REFERENCES, id1); assertThat(results, hasSize(1)); assertEquals(StreamEntryDeletionResult.DELETED, results.get(0)); // Verify entry is deleted from stream assertEquals(1L, jedis.xlen(STREAM_KEY_1)); } @Test @SinceRedisVersion("8.1.240") public void xdelexMultipleEntries() { setUpTestStream(); // Add test entries StreamEntryID id1 = jedis.xadd(STREAM_KEY_1, new StreamEntryID("1-0"), HASH_1); StreamEntryID id2 = jedis.xadd(STREAM_KEY_1, new StreamEntryID("2-0"), HASH_2); StreamEntryID id3 = jedis.xadd(STREAM_KEY_1, new StreamEntryID("3-0"), HASH_1); assertEquals(3L, jedis.xlen(STREAM_KEY_1)); // Test XDELEX with multiple IDs List results = jedis.xdelex(STREAM_KEY_1, id1, id3); assertThat(results, hasSize(2)); assertEquals(StreamEntryDeletionResult.DELETED, results.get(0)); assertEquals(StreamEntryDeletionResult.DELETED, results.get(1)); // Verify two entries are deleted assertEquals(1L, jedis.xlen(STREAM_KEY_1)); } @Test @SinceRedisVersion("8.1.240") public void xdelexNonExistentEntries() { setUpTestStream(); // Add one entry StreamEntryID id1 = jedis.xadd(STREAM_KEY_1, new StreamEntryID("1-0"), HASH_1); assertEquals(1L, jedis.xlen(STREAM_KEY_1)); // Test XDELEX with mix of existing and non-existent IDs StreamEntryID nonExistentId = new StreamEntryID("999-0"); List results = jedis.xdelex(STREAM_KEY_1, id1, nonExistentId); assertThat(results, hasSize(2)); assertEquals(StreamEntryDeletionResult.DELETED, results.get(0)); // Existing entry assertEquals(StreamEntryDeletionResult.NOT_FOUND, results.get(1)); // Non-existent entry // Verify existing entry is deleted assertEquals(0L, jedis.xlen(STREAM_KEY_1)); } @Test @SinceRedisVersion("8.1.240") public void xdelexWithConsumerGroups() { setUpTestStream(); // Add test entries StreamEntryID id1 = jedis.xadd(STREAM_KEY_1, new StreamEntryID("1-0"), HASH_1); StreamEntryID id2 = jedis.xadd(STREAM_KEY_1, new StreamEntryID("2-0"), HASH_2); // Read messages to add them to PEL Map streams = singletonMap(STREAM_KEY_1, StreamEntryID.XREADGROUP_UNDELIVERED_ENTRY); List>> messages = jedis.xreadGroup(GROUP_NAME, CONSUMER_NAME, XReadGroupParams.xReadGroupParams().count(2), streams); assertEquals(1, messages.size()); assertEquals(2, messages.get(0).getValue().size()); // Acknowledge only the first message StreamEntryID readId1 = messages.get(0).getValue().get(0).getID(); StreamEntryID readId2 = messages.get(0).getValue().get(1).getID(); jedis.xack(STREAM_KEY_1, GROUP_NAME, readId1); // Test XDELEX with ACKNOWLEDGED mode - should only delete acknowledged entries List results = jedis.xdelex(STREAM_KEY_1, StreamDeletionPolicy.ACKNOWLEDGED, readId1, readId2); assertThat(results, hasSize(2)); assertEquals(StreamEntryDeletionResult.DELETED, results.get(0)); // id1 was acknowledged assertEquals(StreamEntryDeletionResult.NOT_DELETED_UNACKNOWLEDGED_OR_STILL_REFERENCED, results.get(1)); // id2 not // acknowledged // Verify only acknowledged entry was deleted assertEquals(1L, jedis.xlen(STREAM_KEY_1)); } @Test @SinceRedisVersion("8.1.240") public void xdelexEmptyStream() { setUpTestStream(); // Test XDELEX on empty stream StreamEntryID nonExistentId = new StreamEntryID("1-0"); List results = jedis.xdelex(STREAM_KEY_1, nonExistentId); assertThat(results, hasSize(1)); assertEquals(StreamEntryDeletionResult.NOT_FOUND, results.get(0)); } @Test @SinceRedisVersion("8.1.240") public void xdelexNotAcknowledged() { setUpTestStream(); String groupName = "test_group"; // Add initial entries and create consumer group Map entry1 = singletonMap("field1", "value1"); jedis.xadd(STREAM_KEY_1, new StreamEntryID("1-0"), entry1); jedis.xgroupCreate(STREAM_KEY_1, groupName, new StreamEntryID("0-0"), true); // Read one message to create PEL entry String consumerName = "consumer1"; Map streamQuery = singletonMap(STREAM_KEY_1, StreamEntryID.XREADGROUP_UNDELIVERED_ENTRY); jedis.xreadGroup(groupName, consumerName, XReadGroupParams.xReadGroupParams().count(1), streamQuery); // Add a new entry that was never delivered to any consumer Map entry2 = singletonMap("field4", "value4"); StreamEntryID id2 = jedis.xadd(STREAM_KEY_1, new StreamEntryID("2-0"), entry2); // Verify initial state StreamPendingSummary pending = jedis.xpending(STREAM_KEY_1, groupName); assertEquals(1L, pending.getTotal()); // Only id1 is in PEL StreamInfo info = jedis.xinfoStream(STREAM_KEY_1); assertEquals(2L, info.getLength()); // Stream has 2 entries // Test XDELEX with ACKNOWLEDGED policy on entry that was never delivered // This should return NOT_DELETED_UNACKNOWLEDGED_OR_STILL_REFERENCED since id2 was never // delivered to any consumer List result = jedis.xdelex(STREAM_KEY_1, StreamDeletionPolicy.ACKNOWLEDGED, id2); assertThat(result, hasSize(1)); assertEquals(StreamEntryDeletionResult.NOT_DELETED_UNACKNOWLEDGED_OR_STILL_REFERENCED, result.get(0)); } // ========== XREADGROUP CLAIM Tests ========== private static final String CONSUMER_1 = "consumer-1"; private static final String CONSUMER_2 = "consumer-2"; private static final long IDLE_TIME_MS = 5; Map beforeEachClaimTest() throws InterruptedException { setUpTestStream(new StreamEntryID("0-0")); // Produce two entries jedis.xadd(STREAM_KEY_1, StreamEntryID.NEW_ENTRY, HASH_1); jedis.xadd(STREAM_KEY_1, StreamEntryID.NEW_ENTRY, HASH_1); Map streams = singletonMap(STREAM_KEY_1, StreamEntryID.XREADGROUP_UNDELIVERED_ENTRY); jedis.xreadGroup(GROUP_NAME, CONSUMER_1, XReadGroupParams.xReadGroupParams().count(10), streams); // Ensure idle time so entries are claimable Thread.sleep(IDLE_TIME_MS); return streams; } @Test @SinceRedisVersion(V8_4_0_STRING) public void xreadgroupClaimReturnsMetadataOrdered() throws InterruptedException { Map streams = beforeEachClaimTest(); // Produce fresh entries that are NOT claimed (not pending) jedis.xadd(STREAM_KEY_1, StreamEntryID.NEW_ENTRY, HASH_1); jedis.xadd(STREAM_KEY_1, StreamEntryID.NEW_ENTRY, HASH_1); // Read with consumer-2 using CLAIM List>> consumer2Result = jedis.xreadGroup(GROUP_NAME, CONSUMER_2, XReadGroupParams.xReadGroupParams().claim(IDLE_TIME_MS).count(10), streams); assertNotNull(consumer2Result); assertEquals(1, consumer2Result.size()); List entries = consumer2Result.get(0).getValue(); assertEquals(4, entries.size()); long claimedCount = entries.stream().filter(StreamEntry::isClaimed).count(); long freshCount = entries.size() - claimedCount; assertEquals(2, claimedCount); assertEquals(2, freshCount); // Assert order: pending entries are first StreamEntry first = entries.get(0); StreamEntry second = entries.get(1); StreamEntry third = entries.get(2); StreamEntry fourth = entries.get(3); // Claimed entries assertTrue(first.isClaimed()); assertTrue(second.isClaimed()); assertTrue(first.getMillisElapsedFromDelivery() >= IDLE_TIME_MS); assertTrue(second.getMillisElapsedFromDelivery() >= IDLE_TIME_MS); assertEquals(HASH_1, first.getFields()); // Fresh entries assertFalse(third.isClaimed()); assertFalse(fourth.isClaimed()); assertEquals(Long.valueOf(0), third.getDeliveredCount()); assertEquals(Long.valueOf(0), fourth.getDeliveredCount()); assertEquals(Long.valueOf(0), third.getMillisElapsedFromDelivery()); assertEquals(Long.valueOf(0), fourth.getMillisElapsedFromDelivery()); assertEquals(HASH_1, fourth.getFields()); } @Test @SinceRedisVersion(V8_4_0_STRING) public void xreadgroupClaimMovesPendingFromC1ToC2AndRemainsPendingUntilAck() throws InterruptedException { Map streams = beforeEachClaimTest(); // Verify pending belongs to consumer-1 StreamPendingSummary before = jedis.xpending(STREAM_KEY_1, GROUP_NAME); assertEquals(2L, before.getTotal()); assertEquals(2L, before.getConsumerMessageCount().getOrDefault(CONSUMER_1, 0L).longValue()); // Claim with consumer-2 List>> res = jedis.xreadGroup(GROUP_NAME, CONSUMER_2, XReadGroupParams.xReadGroupParams().claim(IDLE_TIME_MS).count(10), streams); assertNotNull(res); assertEquals(1, res.size()); List entries = res.get(0).getValue(); long claimed = entries.stream().filter(StreamEntry::isClaimed).count(); assertEquals(2, claimed); // After claim: entries are pending for consumer-2 (moved), not acked yet StreamPendingSummary afterClaim = jedis.xpending(STREAM_KEY_1, GROUP_NAME); assertEquals(2L, afterClaim.getTotal()); assertEquals(0L, afterClaim.getConsumerMessageCount().getOrDefault(CONSUMER_1, 0L).longValue()); assertEquals(2L, afterClaim.getConsumerMessageCount().getOrDefault(CONSUMER_2, 0L).longValue()); // XACK the claimed entries -> PEL should become empty long acked = jedis.xack(STREAM_KEY_1, GROUP_NAME, entries.get(0).getID(), entries.get(1).getID()); assertEquals(2, acked); StreamPendingSummary afterAck = jedis.xpending(STREAM_KEY_1, GROUP_NAME); assertEquals(0L, afterAck.getTotal()); } @Test @SinceRedisVersion(V8_4_0_STRING) public void xreadgroupClaimWithNoackDoesNotCreatePendingAndRemovesClaimedFromPel() throws InterruptedException { Map streams = beforeEachClaimTest(); // Verify pending belongs to consumer-1 StreamPendingSummary before = jedis.xpending(STREAM_KEY_1, GROUP_NAME); assertEquals(2L, before.getTotal()); assertEquals(2L, before.getConsumerMessageCount().getOrDefault(CONSUMER_1, 0L).longValue()); assertEquals(0L, before.getConsumerMessageCount().getOrDefault(CONSUMER_2, 0L).longValue()); // Also produce fresh entries that should not be added to PEL when NOACK is set jedis.xadd(STREAM_KEY_1, StreamEntryID.NEW_ENTRY, HASH_1); jedis.xadd(STREAM_KEY_1, StreamEntryID.NEW_ENTRY, HASH_1); // Claim with NOACK using consumer-2 List>> res = jedis.xreadGroup(GROUP_NAME, CONSUMER_2, XReadGroupParams.xReadGroupParams().claim(IDLE_TIME_MS).noAck().count(10), streams); assertNotNull(res); assertEquals(1, res.size()); List entries = res.get(0).getValue(); long claimedCount = entries.stream().filter(StreamEntry::isClaimed).count(); long freshCount = entries.size() - claimedCount; assertEquals(2, claimedCount); assertEquals(2, freshCount); // After NOACK read, previously pending entries remain pending (NOACK does not remove them) StreamPendingSummary afterNoack = jedis.xpending(STREAM_KEY_1, GROUP_NAME); assertEquals(2L, afterNoack.getTotal()); // Claimed entries remain pending and are now owned by consumer-2 (CLAIM reassigns ownership). // Fresh entries were not added to PEL. assertEquals(0L, afterNoack.getConsumerMessageCount().getOrDefault(CONSUMER_1, 0L).longValue()); assertEquals(2L, afterNoack.getConsumerMessageCount().getOrDefault(CONSUMER_2, 0L).longValue()); } @Test public void xreadGroupPreservesFieldOrder() { String streamKey = "field-order-stream"; String groupName = "field-order-group"; String consumerName = "field-order-consumer"; // Use LinkedHashMap to ensure insertion order: a, z, m Map fields = new LinkedHashMap<>(); fields.put("a", "1"); fields.put("z", "4"); fields.put("m", "2"); jedis.xadd(streamKey, StreamEntryID.NEW_ENTRY, fields); jedis.xgroupCreate(streamKey, groupName, new StreamEntryID("0-0"), false); Map streamQuery = singletonMap(streamKey, StreamEntryID.XREADGROUP_UNDELIVERED_ENTRY); List>> result = jedis.xreadGroup(groupName, consumerName, XReadGroupParams.xReadGroupParams().count(1), streamQuery); assertEquals(1, result.size()); assertEquals(1, result.get(0).getValue().size()); StreamEntry entry = result.get(0).getValue().get(0); Map returnedFields = entry.getFields(); // Verify field order is preserved - this will fail with HashMap, pass with LinkedHashMap String[] expectedOrder = {"a", "z", "m"}; String[] actualOrder = returnedFields.keySet().toArray(new String[0]); assertEquals(expectedOrder.length, actualOrder.length, "Field count should match"); for (int i = 0; i < expectedOrder.length; i++) { assertEquals(expectedOrder[i], actualOrder[i], String.format("Field order mismatch at position %d: expected '%s' but got '%s'. " + "Full order: expected [a, z, m], actual %s", i, expectedOrder[i], actualOrder[i], java.util.Arrays.toString(actualOrder))); } } @Test public void xreadAsMapPreservesStreamOrder() { // Test that xreadAsMap preserves the order of streams when reading from multiple streams String streamKey1 = "{stream-order}-test-1"; String streamKey2 = "{stream-order}-test-2"; String streamKey3 = "{stream-order}-test-3"; // Add entries to streams in specific order Map fields = new LinkedHashMap<>(); fields.put("field", "value1"); jedis.xadd(streamKey1, StreamEntryID.NEW_ENTRY, fields); fields.put("field", "value2"); jedis.xadd(streamKey2, StreamEntryID.NEW_ENTRY, fields); fields.put("field", "value3"); jedis.xadd(streamKey3, StreamEntryID.NEW_ENTRY, fields); // Read from multiple streams in specific order Map streams = new LinkedHashMap<>(); streams.put(streamKey1, new StreamEntryID("0-0")); streams.put(streamKey2, new StreamEntryID("0-0")); streams.put(streamKey3, new StreamEntryID("0-0")); Map> result = jedis.xreadAsMap( XReadParams.xReadParams().count(10), streams); assertNotNull(result); assertEquals(3, result.size()); // Verify that the order of streams in the result matches the order in the request String[] expectedOrder = {streamKey1, streamKey2, streamKey3}; String[] actualOrder = result.keySet().toArray(new String[0]); assertEquals(expectedOrder.length, actualOrder.length, "Stream count should match"); for (int i = 0; i < expectedOrder.length; i++) { assertEquals(expectedOrder[i], actualOrder[i], String.format("Stream order mismatch at position %d: expected '%s' but got '%s'", i, expectedOrder[i], actualOrder[i])); } } // ========== Idempotent Producer Tests ========== @Test @EnabledOnCommand("XCFGSET") public void testXaddIdmpAuto() { // Add entry with IDMPAUTO Map message = new HashMap<>(); message.put("order", "12345"); message.put("amount", "100.00"); StreamEntryID id1 = jedis.xadd(STREAM_KEY_1, XAddParams.xAddParams().idmpAuto("producer-1"), message); assertNotNull(id1); assertEquals(1L, jedis.xlen(STREAM_KEY_1)); // Add same message again with same producer - should be rejected as duplicate StreamEntryID id2 = jedis.xadd(STREAM_KEY_1, XAddParams.xAddParams().idmpAuto("producer-1"), message); assertEquals(id1, id2); // Duplicate returns same ID assertEquals(1L, jedis.xlen(STREAM_KEY_1)); // Stream length unchanged // Add same message with different producer - should succeed StreamEntryID id3 = jedis.xadd(STREAM_KEY_1, XAddParams.xAddParams().idmpAuto("producer-2"), message); assertNotNull(id3); assertEquals(2L, jedis.xlen(STREAM_KEY_1)); // Add different message with same producer - should succeed Map message2 = new HashMap<>(); message2.put("order", "67890"); message2.put("amount", "200.00"); StreamEntryID id4 = jedis.xadd(STREAM_KEY_1, XAddParams.xAddParams().idmpAuto("producer-1"), message2); assertNotNull(id4); assertEquals(3L, jedis.xlen(STREAM_KEY_1)); } @Test @EnabledOnCommand("XCFGSET") public void testXaddIdmp() { // Add entry with explicit idempotent ID StreamEntryID id1 = jedis.xadd(STREAM_KEY_1, XAddParams.xAddParams().idmp("producer-1", "iid-001"), HASH_1); assertNotNull(id1); assertEquals(1L, jedis.xlen(STREAM_KEY_1)); // Add with same producer and idempotent ID - should be rejected StreamEntryID id2 = jedis.xadd(STREAM_KEY_1, XAddParams.xAddParams().idmp("producer-1", "iid-001"), HASH_2); // Different content, but same IDs assertEquals(id1, id2); // Duplicate returns same ID assertEquals(1L, jedis.xlen(STREAM_KEY_1)); // Add with same producer but different idempotent ID - should succeed StreamEntryID id3 = jedis.xadd(STREAM_KEY_1, XAddParams.xAddParams().idmp("producer-1", "iid-002"), HASH_1); assertNotNull(id3); assertEquals(2L, jedis.xlen(STREAM_KEY_1)); // Add with different producer but same idempotent ID - should succeed StreamEntryID id4 = jedis.xadd(STREAM_KEY_1, XAddParams.xAddParams().idmp("producer-2", "iid-001"), HASH_1); assertNotNull(id4); assertEquals(3L, jedis.xlen(STREAM_KEY_1)); } @Test @EnabledOnCommand("XCFGSET") public void testXcfgset() { // Configure idempotent producer settings String result = jedis.xcfgset(STREAM_KEY_1, redis.clients.jedis.params.XCfgSetParams.xCfgSetParams().idmpDuration(1000) .idmpMaxsize(500)); assertEquals("OK", result); // Verify settings via XINFO STREAM StreamInfo info = jedis.xinfoStream(STREAM_KEY_1); assertEquals(Long.valueOf(1000), info.getIdmpDuration()); assertEquals(Long.valueOf(500), info.getIdmpMaxsize()); } @Test @EnabledOnCommand("XCFGSET") public void testXinfoStreamIdempotentFields() { // Configure idempotent settings jedis.xcfgset(STREAM_KEY_1, redis.clients.jedis.params.XCfgSetParams.xCfgSetParams().idmpDuration(100) .idmpMaxsize(100)); // Add some entries with idempotent IDs jedis.xadd(STREAM_KEY_1, XAddParams.xAddParams().idmp("producer-1", "iid-001"), HASH_1); jedis.xadd(STREAM_KEY_1, XAddParams.xAddParams().idmp("producer-1", "iid-002"), HASH_2); jedis.xadd(STREAM_KEY_1, XAddParams.xAddParams().idmp("producer-2", "iid-001"), HASH_1); // Try to add a duplicate jedis.xadd(STREAM_KEY_1, XAddParams.xAddParams().idmp("producer-1", "iid-001"), HASH_2); // Check XINFO STREAM response StreamInfo info = jedis.xinfoStream(STREAM_KEY_1); // Verify idempotent configuration fields assertEquals(Long.valueOf(100), info.getIdmpDuration()); assertEquals(Long.valueOf(100), info.getIdmpMaxsize()); // Verify idempotent statistics fields assertEquals(Long.valueOf(2), info.getPidsTracked()); // 2 producers assertEquals(Long.valueOf(3), info.getIidsTracked()); // 3 unique IDs assertEquals(Long.valueOf(3), info.getIidsAdded()); // 3 entries added assertEquals(Long.valueOf(1), info.getIidsDuplicates()); // 1 duplicate rejected } @Test @EnabledOnCommand("XCFGSET") public void testXaddIdmpWithTrimming() { // Add first entry with IDMPAUTO and trimming StreamEntryID id1 = jedis.xadd(STREAM_KEY_1, XAddParams.xAddParams().idmpAuto("producer-1").maxLen(2), HASH_1); assertNotNull(id1); // Add duplicate - should return same ID and not add new entry StreamEntryID id2 = jedis.xadd(STREAM_KEY_1, XAddParams.xAddParams().idmpAuto("producer-1").maxLen(2), HASH_1); assertEquals(id1, id2); // Duplicate returns same ID assertEquals(1, jedis.xlen(STREAM_KEY_1)); // Still 1 entry // Add different message - should add new entry and trim StreamEntryID id3 = jedis.xadd(STREAM_KEY_1, XAddParams.xAddParams().idmpAuto("producer-1").maxLen(2), HASH_2); assertNotNull(id3); assertNotEquals(id1, id3); // Different IDs assertEquals(2, jedis.xlen(STREAM_KEY_1)); // Now 2 entries } @Test @EnabledOnCommand("XCFGSET") public void testXcfgsetDefaults() { jedis.xadd(STREAM_KEY_1, StreamEntryID.NEW_ENTRY, HASH_1); // Verify default values StreamInfo info = jedis.xinfoStream(STREAM_KEY_1); assertEquals(100L, info.getIdmpDuration()); assertEquals(100L, info.getIdmpMaxsize()); assertEquals("OK", jedis.xcfgset(STREAM_KEY_1, XCfgSetParams.xCfgSetParams().idmpDuration(200).idmpMaxsize(200))); StreamInfo infoAfter = jedis.xinfoStream(STREAM_KEY_1); assertEquals(200L, infoAfter.getIdmpDuration()); assertEquals(200L, infoAfter.getIdmpMaxsize()); } } ================================================ FILE: src/test/java/redis/clients/jedis/commands/unified/StringValuesCommandsTestBase.java ================================================ package redis.clients.jedis.commands.unified; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertNull; import static org.junit.jupiter.api.Assertions.assertThrows; import static org.junit.jupiter.api.Assertions.assertTrue; import static redis.clients.jedis.params.SetParams.setParams; import java.util.ArrayList; import java.util.List; import java.util.stream.Stream; import io.redis.test.annotations.ConditionalOnEnv; import io.redis.test.annotations.EnabledOnCommand; import io.redis.test.annotations.SinceRedisVersion; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Tag; import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.Arguments; import org.junit.jupiter.params.provider.MethodSource; import redis.clients.jedis.RedisProtocol; import redis.clients.jedis.params.LCSParams; import redis.clients.jedis.params.MSetExParams; import redis.clients.jedis.resps.LCSMatchResult; import redis.clients.jedis.exceptions.JedisDataException; import redis.clients.jedis.params.GetExParams; import redis.clients.jedis.util.TestEnvUtil; @Tag("integration") public abstract class StringValuesCommandsTestBase extends UnifiedJedisCommandsTestBase { public StringValuesCommandsTestBase(RedisProtocol protocol) { super(protocol); } @Test public void setAndGet() { String status = jedis.set("foo", "bar"); assertEquals("OK", status); String value = jedis.get("foo"); assertEquals("bar", value); assertNull(jedis.get("bar")); } @Test public void getSet() { String value = jedis.getSet("foo", "bar"); assertNull(value); value = jedis.get("foo"); assertEquals("bar", value); } @Test public void setGetWithParams() { jedis.del("foo"); // no previous, return null assertNull(jedis.setGet("foo", "bar", setParams().nx())); // key already exists, new value should not be set, previous value should be bbar assertEquals("bar", jedis.setGet("foo", "foobar", setParams().nx())); assertEquals("bar", jedis.setGet("foo", "foobar", setParams().xx())); } @Test public void getDel() { String status = jedis.set("foo", "bar"); assertEquals("OK", status); String value = jedis.getDel("foo"); assertEquals("bar", value); assertNull(jedis.get("foo")); } @Test public void getEx() { assertNull(jedis.getEx("foo", GetExParams.getExParams().ex(1))); jedis.set("foo", "bar"); assertEquals("bar", jedis.getEx("foo", GetExParams.getExParams().ex(10))); long ttl = jedis.ttl("foo"); assertTrue(ttl > 0 && ttl <= 10); assertEquals("bar", jedis.getEx("foo", GetExParams.getExParams().px(20000L))); ttl = jedis.ttl("foo"); assertTrue(ttl > 10 && ttl <= 20); assertEquals("bar", jedis.getEx("foo", GetExParams.getExParams().exAt(System.currentTimeMillis() / 1000 + 30))); ttl = jedis.ttl("foo"); assertTrue(ttl > 20 && ttl <= 30); assertEquals("bar", jedis.getEx("foo", GetExParams.getExParams().pxAt(System.currentTimeMillis() + 40000L))); ttl = jedis.ttl("foo"); assertTrue(ttl > 30 && ttl <= 40); assertEquals("bar", jedis.getEx("foo", GetExParams.getExParams().persist())); assertEquals(-1, jedis.ttl("foo")); } @Test public void mget() { List values = jedis.mget("foo", "bar"); List expected = new ArrayList(); expected.add(null); expected.add(null); assertEquals(expected, values); jedis.set("foo", "bar"); expected = new ArrayList(); expected.add("bar"); expected.add(null); values = jedis.mget("foo", "bar"); assertEquals(expected, values); jedis.set("bar", "foo"); expected = new ArrayList(); expected.add("bar"); expected.add("foo"); values = jedis.mget("foo", "bar"); assertEquals(expected, values); } @Test public void setnx() { assertEquals(1, jedis.setnx("foo", "bar")); assertEquals("bar", jedis.get("foo")); assertEquals(0, jedis.setnx("foo", "bar2")); assertEquals("bar", jedis.get("foo")); } @Test public void setex() { String status = jedis.setex("foo", 20, "bar"); assertEquals("OK", status); long ttl = jedis.ttl("foo"); assertTrue(ttl > 0 && ttl <= 20); } @Test public void mset() { String status = jedis.mset("foo", "bar", "bar", "foo"); assertEquals("OK", status); assertEquals("bar", jedis.get("foo")); assertEquals("foo", jedis.get("bar")); } @Test @ConditionalOnEnv(value = TestEnvUtil.ENV_REDIS_ENTERPRISE, enabled = false) public void msetnx() { assertEquals(1, jedis.msetnx("foo", "bar", "bar", "foo")); assertEquals("bar", jedis.get("foo")); assertEquals("foo", jedis.get("bar")); assertEquals(0, jedis.msetnx("foo", "bar1", "bar2", "foo2")); assertEquals("bar", jedis.get("foo")); assertEquals("foo", jedis.get("bar")); } @Test public void incr() { assertEquals(1, jedis.incr("foo")); assertEquals(2, jedis.incr("foo")); } @Test public void incrWrongValue() { jedis.set("foo", "bar"); assertThrows(JedisDataException.class, () -> jedis.incr("foo")); } @Test public void incrBy() { assertEquals(2, jedis.incrBy("foo", 2)); assertEquals(5, jedis.incrBy("foo", 3)); } @Test public void incrByWrongValue() { jedis.set("foo", "bar"); assertThrows(JedisDataException.class, () -> jedis.incrBy("foo", 2)); } @Test public void incrByFloat() { assertEquals(10.5, jedis.incrByFloat("foo", 10.5), 0.0); assertEquals(10.6, jedis.incrByFloat("foo", 0.1), 0.0); } @Test public void incrByFloatWrongValue() { jedis.set("foo", "bar"); assertThrows(JedisDataException.class, () -> jedis.incrByFloat("foo", 2d)); } @Test public void decrWrongValue() { jedis.set("foo", "bar"); assertThrows(JedisDataException.class, () -> jedis.decr("foo")); } @Test public void decr() { assertEquals(-1, jedis.decr("foo")); assertEquals(-2, jedis.decr("foo")); } @Test public void decrBy() { assertEquals(-2, jedis.decrBy("foo", 2)); assertEquals(-4, jedis.decrBy("foo", 2)); } @Test public void decrByWrongValue() { jedis.set("foo", "bar"); assertThrows(JedisDataException.class, () -> jedis.decrBy("foo", 2)); } @Test public void append() { assertEquals(3, jedis.append("foo", "bar")); assertEquals("bar", jedis.get("foo")); assertEquals(6, jedis.append("foo", "bar")); assertEquals("barbar", jedis.get("foo")); } @Test @ConditionalOnEnv(value = TestEnvUtil.ENV_REDIS_ENTERPRISE, enabled = false) public void substr() { jedis.set("s", "This is a string"); assertEquals("This", jedis.substr("s", 0, 3)); assertEquals("ing", jedis.substr("s", -3, -1)); assertEquals("This is a string", jedis.substr("s", 0, -1)); assertEquals(" string", jedis.substr("s", 9, 100000)); } @Test public void strlen() { String str = "This is a string"; jedis.set("s", str); assertEquals(str.length(), jedis.strlen("s")); } @Test public void incrLargeNumbers() { assertEquals(1, jedis.incr("foo")); assertEquals(1L + Integer.MAX_VALUE, jedis.incrBy("foo", Integer.MAX_VALUE)); } @Test public void incrReallyLargeNumbers() { jedis.set("foo", Long.toString(Long.MAX_VALUE)); assertThrows(JedisDataException.class, () -> jedis.incr("foo")); // Should throw an exception } @Test public void psetex() { String status = jedis.psetex("foo", 20000, "bar"); assertEquals("OK", status); long ttl = jedis.ttl("foo"); assertTrue(ttl > 0 && ttl <= 20000); } @Test @SinceRedisVersion(value = "7.0.0") @ConditionalOnEnv(value = TestEnvUtil.ENV_REDIS_ENTERPRISE, enabled = false) public void lcs() { jedis.mset("key1", "ohmytext", "key2", "mynewtext"); LCSMatchResult stringMatchResult = jedis.lcs("key1", "key2", LCSParams.LCSParams()); assertEquals("mytext", stringMatchResult.getMatchString()); stringMatchResult = jedis.lcs("key1", "key2", LCSParams.LCSParams().idx().withMatchLen()); assertEquals(stringMatchResult.getLen(), 6); assertEquals(2, stringMatchResult.getMatches().size()); stringMatchResult = jedis.lcs("key1", "key2", LCSParams.LCSParams().idx().minMatchLen(10)); assertEquals(0, stringMatchResult.getMatches().size()); } // MSETEX NX + expiration matrix static Stream msetexNxArgsProvider() { return Stream.of(Arguments.of("EX", new MSetExParams().nx().ex(5)), Arguments.of("PX", new MSetExParams().nx().px(5000)), Arguments.of("EXAT", new MSetExParams().nx().exAt(System.currentTimeMillis() / 1000 + 5)), Arguments.of("PXAT", new MSetExParams().nx().pxAt(System.currentTimeMillis() + 5000)), Arguments.of("KEEPTTL", new MSetExParams().nx().keepTtl())); } @ParameterizedTest(name = "MSETEX NX + {0}") @MethodSource("msetexNxArgsProvider") @EnabledOnCommand("MSETEX") public void msetexNx_parametrized(String optionLabel, MSetExParams params) { String k1 = "{t}msetex:unified:k1"; String k2 = "{t}msetex:unified:k2"; boolean result = jedis.msetex(params, k1, "v1", k2, "v2"); assertTrue(result); long ttl = jedis.ttl(k1); if ("KEEPTTL".equals(optionLabel)) { assertEquals(-1L, ttl); } else { assertTrue(ttl > 0L); } } @Test @EnabledOnCommand("MSETEX") public void msetexXxExAt() { String k1 = "{t}msetex:unified:xx:k1"; String k2 = "{t}msetex:unified:xx:k2"; // First set the keys so they exist (XX requires existing keys) jedis.set(k1, "initial1"); jedis.set(k2, "initial2"); // Now use MSETEX with XX and EXAT long expiryTimestamp = System.currentTimeMillis() / 1000 + 5; MSetExParams params = new MSetExParams().xx().exAt(expiryTimestamp); boolean result = jedis.msetex(params, k1, "v1", k2, "v2"); assertTrue(result); // Verify values were updated assertEquals("v1", jedis.get(k1)); assertEquals("v2", jedis.get(k2)); // Verify TTL is set long ttl = jedis.ttl(k1); assertTrue(ttl > 0L); } } ================================================ FILE: src/test/java/redis/clients/jedis/commands/unified/UnifiedJedisCommandsTestBase.java ================================================ package redis.clients.jedis.commands.unified; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Tag; import org.junit.jupiter.api.extension.RegisterExtension; import org.junit.jupiter.api.BeforeAll; import redis.clients.jedis.EndpointConfig; import redis.clients.jedis.Endpoints; import redis.clients.jedis.RedisProtocol; import redis.clients.jedis.UnifiedJedis; import redis.clients.jedis.commands.CommandsTestsParameters; import redis.clients.jedis.util.EnabledOnCommandCondition; import redis.clients.jedis.util.EnvCondition; import redis.clients.jedis.util.RedisVersionCondition; @Tag("integration") public abstract class UnifiedJedisCommandsTestBase { /** * Input data for parameterized tests. In principle all subclasses of this * class should be parameterized tests, to run with several versions of RESP. * * @see CommandsTestsParameters#respVersions() */ protected final RedisProtocol protocol; protected UnifiedJedis jedis; protected static EndpointConfig endpoint; @RegisterExtension public RedisVersionCondition versionCondition = new RedisVersionCondition( () -> endpoint); @RegisterExtension public EnabledOnCommandCondition enabledOnCommandCondition = new EnabledOnCommandCondition( () -> endpoint); @RegisterExtension public static EnvCondition envCondition = new EnvCondition(); @BeforeAll public static void prepareEndpoint() { endpoint = Endpoints.getRedisEndpoint("standalone0"); } /** * The RESP protocol is to be injected by the subclasses, usually via JUnit * parameterized tests, because most of the subclassed tests are meant to be * executed against multiple RESP versions. For the special cases where a single * RESP version is relevant, we still force the subclass to be explicit and * call this constructor. * * @param protocol The RESP protocol to use during the tests. */ public UnifiedJedisCommandsTestBase(RedisProtocol protocol) { this.protocol = protocol; } /** * Subclasses provide specific UnifiedJedis setup. */ protected abstract UnifiedJedis createTestClient(); protected void clearData() { if (jedis != null) { jedis.flushAll(); } } @BeforeEach void setUpBase() { jedis = createTestClient(); clearData(); } @AfterEach void tearDownBase() { if (jedis != null) { jedis.close(); } } } ================================================ FILE: src/test/java/redis/clients/jedis/commands/unified/VectorSetCommandsTestBase.java ================================================ package redis.clients.jedis.commands.unified; import static java.util.Arrays.asList; import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.empty; import static org.hamcrest.Matchers.everyItem; import static org.hamcrest.Matchers.greaterThan; import static org.hamcrest.Matchers.greaterThanOrEqualTo; import static org.hamcrest.Matchers.hasItems; import static org.hamcrest.Matchers.is; import static org.hamcrest.Matchers.not; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertNotNull; import static org.junit.jupiter.api.Assertions.assertNull; import static org.junit.jupiter.api.Assertions.assertThrows; import static org.junit.jupiter.api.Assertions.assertTrue; import static redis.clients.jedis.util.VectorTestUtils.floatArrayToFP32Bytes; import io.redis.test.annotations.SinceRedisVersion; import org.junit.jupiter.api.Tag; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.TestInfo; import redis.clients.jedis.RedisProtocol; import redis.clients.jedis.exceptions.JedisDataException; import redis.clients.jedis.params.VAddParams; import redis.clients.jedis.params.VSimParams; import redis.clients.jedis.resps.RawVector; import redis.clients.jedis.resps.VSimScoreAttribs; import redis.clients.jedis.resps.VectorInfo; import redis.clients.jedis.util.SafeEncoder; import redis.clients.jedis.util.VectorTestUtils; import java.util.Arrays; import java.util.List; import java.util.Map; import static org.hamcrest.Matchers.*; @Tag("integration") @Tag("vector-set") public abstract class VectorSetCommandsTestBase extends UnifiedJedisCommandsTestBase { public VectorSetCommandsTestBase(RedisProtocol protocol) { super(protocol); } /** * Test the basic VADD method with float array. Overload 1: vadd(String key, float[] vector, * String element) */ @Test @SinceRedisVersion("8.0.0") public void testVaddWithFloatArray(TestInfo testInfo) { String testKey = testInfo.getDisplayName() + ":test:vector:set"; String elementId = "point:F"; float[] vector = { 1.0f, 2.0f }; // Add a new element boolean result = jedis.vadd(testKey, vector, elementId); assertTrue(result); // Verify cardinality and dimension assertEquals(1L, jedis.vcard(testKey)); assertEquals(2L, jedis.vdim(testKey)); // Verify the vector was stored correctly List storedVector = jedis.vemb(testKey, elementId); assertEquals(2, storedVector.size()); assertEquals(1.0, storedVector.get(0), 0.01); assertEquals(2.0, storedVector.get(1), 0.01); // Test duplicate addition - should return false result = jedis.vadd(testKey, vector, elementId); assertFalse(result); // Cardinality should remain the same assertEquals(1L, jedis.vcard(testKey)); } /** * Test VADD method with float array and parameters. Overload 2: vadd(String key, float[] vector, * String element, VAddParams params) */ @Test @SinceRedisVersion("8.0.0") public void testVaddWithFloatArrayAndParams(TestInfo testInfo) { String testKey = testInfo.getDisplayName() + ":test:vector:set"; String elementId = "point:G"; float[] vector = { 1.0f, 2.0f }; // Create parameters VAddParams params = new VAddParams(); // Add a new element with parameters boolean result = jedis.vadd(testKey, vector, elementId, params); assertTrue(result); // Verify cardinality and dimension assertEquals(1L, jedis.vcard(testKey)); assertEquals(2L, jedis.vdim(testKey)); // Verify the vector was stored correctly List storedVector = jedis.vemb(testKey, elementId); assertEquals(2, storedVector.size()); assertEquals(1.0, storedVector.get(0), 0.01); assertEquals(2.0, storedVector.get(1), 0.01); } /** * Test VADD method with FP32 byte blob. Overload 3: vaddFP32(String key, byte[] vectorBlob, * String element) */ @Test @SinceRedisVersion("8.0.0") public void testVaddWithFP32ByteBlob(TestInfo testInfo) { String testKey = testInfo.getDisplayName() + ":test:vector:set"; String elementId = "point:H"; float[] vector = { 1.0f, 2.0f }; // Convert float array to FP32 byte blob byte[] vectorBlob = floatArrayToFP32Bytes(vector); // Add a new element with FP32 byte blob boolean result = jedis.vaddFP32(testKey, vectorBlob, elementId); assertTrue(result); // Verify cardinality and dimension assertEquals(1L, jedis.vcard(testKey)); assertEquals(2L, jedis.vdim(testKey)); // Verify the vector was stored correctly List storedVector = jedis.vemb(testKey, elementId); assertEquals(2, storedVector.size()); assertEquals(1.0, storedVector.get(0), 0.01); assertEquals(2.0, storedVector.get(1), 0.01); } /** * Test VADD method with FP32 byte blob and parameters. Overload 4: vaddFP32(String key, byte[] * vectorBlob, String element, VAddParams params) */ @Test @SinceRedisVersion("8.0.0") public void testVaddWithFP32ByteBlobAndParams(TestInfo testInfo) { String testKey = testInfo.getDisplayName() + ":test:vector:set"; String elementId = "point:I"; float[] vector = { 1.0f, 2.0f }; // Convert float array to FP32 byte blob byte[] vectorBlob = floatArrayToFP32Bytes(vector); // Create parameters VAddParams params = new VAddParams(); // Add a new element with FP32 byte blob and parameters boolean result = jedis.vaddFP32(testKey, vectorBlob, elementId, params); assertTrue(result); // Verify cardinality and dimension assertEquals(1L, jedis.vcard(testKey)); assertEquals(2L, jedis.vdim(testKey)); // Verify the vector was stored correctly List storedVector = jedis.vemb(testKey, elementId); assertEquals(2, storedVector.size()); assertEquals(1.0, storedVector.get(0), 0.01); assertEquals(2.0, storedVector.get(1), 0.01); } /** * Test VADD with quantization parameters. Demonstrates how quantization parameters can be used * with VADD. */ @Test @SinceRedisVersion("8.0.0") public void testVaddWithQuantization(TestInfo testInfo) { String baseKey = testInfo.getDisplayName() + ":test:vector:set"; float[] vector = { 1.0f, 2.0f }; // Test with basic VADD first to establish a baseline String defaultKey = baseKey + ":default"; jedis.del(defaultKey); boolean result = jedis.vadd(defaultKey, vector, "point:DEFAULT"); assertTrue(result); List defaultVector = jedis.vemb(defaultKey, "point:DEFAULT"); assertEquals(2, defaultVector.size()); assertEquals(1.0, defaultVector.get(0), 0.01); assertEquals(2.0, defaultVector.get(1), 0.01); assertEquals(1L, jedis.vcard(defaultKey)); // Test with Q8 quantization parameters String q8Key = baseKey + ":q8"; VAddParams quantParams = new VAddParams().q8(); jedis.del(q8Key); result = jedis.vadd(q8Key, vector, "point:Q8", quantParams); assertTrue(result); List quantVector = jedis.vemb(q8Key, "point:Q8"); assertEquals(2, quantVector.size()); assertEquals(1.0, quantVector.get(0), 0.01); assertEquals(2.0, quantVector.get(1), 0.01); assertEquals(1L, jedis.vcard(q8Key)); // Test with NOQUANT quantization parameters String noQuantKey = baseKey + ":noQuant"; VAddParams noQuantParams = new VAddParams().q8(); jedis.del(noQuantKey); result = jedis.vadd(noQuantKey, vector, "point:NOQUANT", noQuantParams); assertTrue(result); List noQuantVector = jedis.vemb(noQuantKey, "point:NOQUANT"); assertEquals(2, noQuantVector.size()); assertEquals(1.0, noQuantVector.get(0), 0.01); assertEquals(2.0, noQuantVector.get(1), 0.01); assertEquals(1L, jedis.vcard(noQuantKey)); } /** * Test VADD with dimension reduction using float array. Verifies that high-dimensional vectors * are reduced to target dimensions. */ @Test @SinceRedisVersion("8.0.0") public void testVaddWithReduceDimension(TestInfo testInfo) { String testKey = testInfo.getDisplayName() + ":test:vector:set"; String elementId = "point:REDUCED"; // Use a 4-dimensional vector that will be reduced to 2 dimensions float[] highDimVector = { 1.0f, 2.0f, 3.0f, 4.0f }; int targetDim = 2; // Create parameters for dimension reduction VAddParams params = new VAddParams(); // Add element with dimension reduction boolean result = jedis.vadd(testKey, highDimVector, elementId, targetDim, params); assertTrue(result); // Verify cardinality assertEquals(1L, jedis.vcard(testKey)); // Verify the vector was reduced to target dimensions assertEquals(targetDim, jedis.vdim(testKey)); // Retrieve and verify the reduced vector List reducedVector = jedis.vemb(testKey, elementId); assertEquals(targetDim, reducedVector.size()); // The values will be different due to random projection, but should exist assertNotNull(reducedVector.get(0)); assertNotNull(reducedVector.get(1)); } /** * Test vaddFP32 with dimension reduction using byte blob. Verifies that FP32 format vectors are * properly reduced. */ @Test @SinceRedisVersion("8.0.0") public void testVaddFP32WithReduceDimension(TestInfo testInfo) { String testKey = testInfo.getDisplayName() + ":test:vector:set"; String elementId = "point:FP32_REDUCED"; // Use a 4-dimensional vector that will be reduced to 2 dimensions float[] highDimVector = { 1.0f, 2.0f, 3.0f, 4.0f }; int targetDim = 2; // Convert to FP32 byte blob byte[] vectorBlob = floatArrayToFP32Bytes(highDimVector); // Create parameters for dimension reduction VAddParams params = new VAddParams(); // Add element with dimension reduction using FP32 format boolean result = jedis.vaddFP32(testKey, vectorBlob, elementId, targetDim, params); assertTrue(result); // Verify cardinality assertEquals(1L, jedis.vcard(testKey)); // Verify the vector was reduced to target dimensions assertEquals(targetDim, jedis.vdim(testKey)); // Retrieve and verify the reduced vector List reducedVector = jedis.vemb(testKey, elementId); assertEquals(targetDim, reducedVector.size()); // The values will be different due to random projection, but should exist assertNotNull(reducedVector.get(0)); assertNotNull(reducedVector.get(1)); } /** * Test VADD with dimension reduction and additional parameters. Verifies that REDUCE works * alongside other VAddParams. */ @Test @SinceRedisVersion("8.0.0") public void testVaddWithReduceDimensionAndParams(TestInfo testInfo) { String testKey = testInfo.getDisplayName() + ":test:vector:set"; String elementId = "point:REDUCED_WITH_PARAMS"; // Use a 6-dimensional vector that will be reduced to 3 dimensions float[] highDimVector = { 1.0f, 2.0f, 3.0f, 4.0f, 5.0f, 6.0f }; int targetDim = 3; // Create parameters with quantization and dimension reduction VAddParams params = new VAddParams().q8().ef(100); // Add element with dimension reduction and additional parameters boolean result = jedis.vadd(testKey, highDimVector, elementId, targetDim, params); assertTrue(result); // Verify cardinality assertEquals(1L, jedis.vcard(testKey)); // Verify the vector was reduced to target dimensions assertEquals(targetDim, jedis.vdim(testKey)); // Retrieve and verify the reduced vector List reducedVector = jedis.vemb(testKey, elementId); assertEquals(targetDim, reducedVector.size()); // All dimensions should have values (may be quantized) for (Double value : reducedVector) { assertNotNull(value); } } /** * Test VADD with SETATTR parameter. Verifies that attributes can be set when adding elements to * vector sets. */ @Test @SinceRedisVersion("8.0.0") public void testVaddWithSetAttr(TestInfo testInfo) { String testKey = testInfo.getDisplayName() + ":test:vector:set"; String elementId = "point:WITH_ATTR"; float[] vector = { 1.0f, 2.0f }; // Create simple text attributes for the element String attributes = "category=test,priority=high,score=95.5"; // Create parameters with attributes VAddParams params = new VAddParams().setAttr(attributes); // Add element with attributes boolean result = jedis.vadd(testKey, vector, elementId, params); assertTrue(result); // Verify cardinality and dimension assertEquals(1L, jedis.vcard(testKey)); assertEquals(2L, jedis.vdim(testKey)); // Verify the vector was stored correctly List storedVector = jedis.vemb(testKey, elementId); assertEquals(2, storedVector.size()); assertEquals(1.0, storedVector.get(0), 0.01); assertEquals(2.0, storedVector.get(1), 0.01); // Verify the attributes were stored correctly using VGETATTR String retrievedAttrs = jedis.vgetattr(testKey, elementId); assertNotNull(retrievedAttrs); assertEquals(attributes, retrievedAttrs); } /** * Test VADD with SETATTR and other parameters combined. Verifies that SETATTR works alongside * quantization and other options. */ @Test @SinceRedisVersion("8.0.0") public void testVaddWithSetAttrAndQuantization(TestInfo testInfo) { String testKey = testInfo.getDisplayName() + ":test:vector:set"; String elementId = "point:ATTR_QUANT"; float[] vector = { 1.0f, 2.0f }; // Create simple text attributes String attributes = "type=quantized,method=Q8,timestamp=2024-01-01"; // Create parameters with both attributes and quantization VAddParams params = new VAddParams().setAttr(attributes).q8().ef(100); // Add element with attributes and quantization boolean result = jedis.vadd(testKey, vector, elementId, params); assertTrue(result); // Verify cardinality and dimension assertEquals(1L, jedis.vcard(testKey)); assertEquals(2L, jedis.vdim(testKey)); // Verify the vector was stored (may be quantized) List storedVector = jedis.vemb(testKey, elementId); assertEquals(2, storedVector.size()); assertEquals(1.0, storedVector.get(0), 0.1); // Larger tolerance for quantization assertEquals(2.0, storedVector.get(1), 0.1); // Verify the attributes were stored correctly String retrievedAttrs = jedis.vgetattr(testKey, elementId); assertNotNull(retrievedAttrs); assertEquals(attributes, retrievedAttrs); } /** * Test VADD with SETATTR using FP32 format. Verifies that attributes work with binary vector * format. */ @Test @SinceRedisVersion("8.0.0") public void testVaddFP32WithSetAttr(TestInfo testInfo) { String testKey = testInfo.getDisplayName() + ":test:vector:set"; String elementId = "point:FP32_ATTR"; float[] vector = { 1.0f, 2.0f }; // Convert to FP32 byte blob byte[] vectorBlob = floatArrayToFP32Bytes(vector); // Create simple text attributes String attributes = "format=FP32,source=binary,validated=true"; // Create parameters with attributes VAddParams params = new VAddParams().setAttr(attributes); // Add element with FP32 format and attributes boolean result = jedis.vaddFP32(testKey, vectorBlob, elementId, params); assertTrue(result); // Verify cardinality and dimension assertEquals(1L, jedis.vcard(testKey)); assertEquals(2L, jedis.vdim(testKey)); // Verify the vector was stored correctly List storedVector = jedis.vemb(testKey, elementId); assertEquals(2, storedVector.size()); assertEquals(1.0, storedVector.get(0), 0.01); assertEquals(2.0, storedVector.get(1), 0.01); // Verify the attributes were stored correctly String retrievedAttrs = jedis.vgetattr(testKey, elementId); assertNotNull(retrievedAttrs); assertEquals(attributes, retrievedAttrs); } /** * Test VGETATTR command functionality. Verifies that attributes can be retrieved from vector set * elements. */ @Test @SinceRedisVersion("8.0.0") public void testVgetattr(TestInfo testInfo) { String testKey = testInfo.getDisplayName() + ":test:vector:set"; String elementId = "point:GETATTR_TEST"; float[] vector = { 1.0f, 2.0f }; // First add an element without attributes boolean result = jedis.vadd(testKey, vector, elementId); assertTrue(result); // VGETATTR should return null for element without attributes String attrs = jedis.vgetattr(testKey, elementId); assertNull(attrs); // Now add an element with attributes String elementWithAttrs = "point:WITH_ATTRS"; String attributes = "name=test_point,value=42,active=true"; VAddParams params = new VAddParams().setAttr(attributes); result = jedis.vadd(testKey, vector, elementWithAttrs, params); assertTrue(result); // VGETATTR should return the attributes String retrievedAttrs = jedis.vgetattr(testKey, elementWithAttrs); assertNotNull(retrievedAttrs); assertEquals(attributes, retrievedAttrs); // Test VGETATTR with non-existent element String nonExistentAttrs = jedis.vgetattr(testKey, "non_existent_element"); assertNull(nonExistentAttrs); } /** * Test VGETATTR with binary key and element. Verifies that VGETATTR works with byte array keys * and elements. */ @Test @SinceRedisVersion("8.0.0") public void testVgetattrBinary(TestInfo testInfo) { byte[] testKey = (testInfo.getDisplayName() + ":test:vector:set:binary").getBytes(); byte[] elementId = "binary_element_with_attrs".getBytes(); float[] vector = { 1.0f, 2.0f }; // VGETATTR should return null for element without attributes assertNull(jedis.vgetattr(testKey, elementId)); // Now add an element with attributes using binary key and element String attributes = "name=binary_test_point,value=42,active=true"; VAddParams params = new VAddParams().setAttr(attributes); boolean result = jedis.vadd(testKey, vector, elementId, params); assertTrue(result); // VGETATTR should return the attributes as byte array byte[] retrievedAttrs = jedis.vgetattr(testKey, elementId); assertNotNull(retrievedAttrs); // Convert byte array back to string and verify content String retrievedAttrsString = SafeEncoder.encode(retrievedAttrs); assertEquals(attributes, retrievedAttrsString); } /** * Test VSETATTR command functionality. Verifies that attributes can be set on vector set * elements. */ @Test @SinceRedisVersion("8.0.0") public void testVsetattr(TestInfo testInfo) { String testKey = testInfo.getDisplayName() + ":test:vector:set"; String elementId = "point:SETATTR_TEST"; float[] vector = { 1.0f, 2.0f }; // First add an element without attributes boolean result = jedis.vadd(testKey, vector, elementId); assertTrue(result); // Set attributes using VSETATTR String attributes = "name=test_point,value=42,active=true"; boolean setResult = jedis.vsetattr(testKey, elementId, attributes); assertTrue(setResult); // Verify attributes were set using VGETATTR String retrievedAttrs = jedis.vgetattr(testKey, elementId); assertNotNull(retrievedAttrs); assertEquals(attributes, retrievedAttrs); // Update attributes with new values String updatedAttributes = "name=updated_point,value=100,active=false,new_field=added"; setResult = jedis.vsetattr(testKey, elementId, updatedAttributes); assertTrue(setResult); // Verify updated attributes retrievedAttrs = jedis.vgetattr(testKey, elementId); assertNotNull(retrievedAttrs); assertEquals(updatedAttributes, retrievedAttrs); } /** * Test VSETATTR with binary key and element. Verifies that VSETATTR works with byte array keys * and elements. */ @Test @SinceRedisVersion("8.0.0") public void testVsetattrBinary(TestInfo testInfo) { byte[] testKey = (testInfo.getDisplayName() + ":test:vector:set:binary").getBytes(); byte[] elementId = "binary_setattr_element".getBytes(); float[] vector = { 1.0f, 2.0f }; // First add an element without attributes boolean result = jedis.vadd(testKey, vector, elementId); assertTrue(result); // Set attributes using binary VSETATTR String attributes = "name=binary_test_point,value=42,active=true"; byte[] attributesBytes = attributes.getBytes(); boolean setResult = jedis.vsetattr(testKey, elementId, attributesBytes); assertTrue(setResult); // Verify attributes were set using binary VGETATTR byte[] retrievedAttrs = jedis.vgetattr(testKey, elementId); assertNotNull(retrievedAttrs); // Convert back to string and verify String retrievedAttrsString = SafeEncoder.encode(retrievedAttrs); assertEquals(attributes, retrievedAttrsString); // Update attributes with new values using binary VSETATTR String updatedAttributes = "name=updated_binary_point,value=100,active=false,new_field=added"; byte[] updatedAttributesBytes = updatedAttributes.getBytes(); setResult = jedis.vsetattr(testKey, elementId, updatedAttributesBytes); assertTrue(setResult); // Verify updated attributes using binary VGETATTR retrievedAttrs = jedis.vgetattr(testKey, elementId); assertNotNull(retrievedAttrs); String updatedRetrievedString = SafeEncoder.encode(retrievedAttrs); assertEquals(updatedAttributes, updatedRetrievedString); } /** * Test VLINKS command functionality. Verifies that vector set links can be retrieved correctly. */ @Test @SinceRedisVersion("8.0.0") public void testVlinks(TestInfo testInfo) { String testKey = testInfo.getDisplayName() + ":test:vector:set"; // Add some vectors to create a vector set with links float[] vector1 = { 1.0f, 0.0f }; float[] vector2 = { 0.0f, 1.0f }; float[] vector3 = { 1.0f, 1.0f }; jedis.vadd(testKey, vector1, "element1"); jedis.vadd(testKey, vector2, "element2"); jedis.vadd(testKey, vector3, "element3"); // Get links for element1 List> links = jedis.vlinks(testKey, "element1"); assertNotNull(links); assertFalse(links.isEmpty()); for (List linkList : links) { for (String rawLink : linkList) { assertTrue(rawLink.equals("element2") || rawLink.equals("element3")); } } } /** * Test VLINKS command functionality. Verifies that vector set links can be retrieved correctly. */ @Test @SinceRedisVersion("8.0.0") public void testVlinksWithScores(TestInfo testInfo) { String testKey = testInfo.getDisplayName() + ":test:vector:set"; // Add some vectors to create a vector set with links float[] vector1 = { 1.0f, 0.0f }; float[] vector2 = { 0.0f, 1.0f }; float[] vector3 = { 1.0f, 1.0f }; jedis.vadd(testKey, vector1, "element1"); jedis.vadd(testKey, vector2, "element2"); jedis.vadd(testKey, vector3, "element3"); // Get links for element1 List> links = jedis.vlinksWithScores(testKey, "element1"); assertNotNull(links); assertFalse(links.isEmpty()); for (Map scores : links) { for (String element : scores.keySet()) { assertTrue(element.equals("element2") || element.equals("element3")); assertTrue(scores.get(element) > 0.0); } } } /** * Test VLINKS with binary key and element. Verifies that VLINKS works with byte array keys and * elements. */ @Test @SinceRedisVersion("8.0.0") public void testVlinksBinary(TestInfo testInfo) { byte[] testKey = (testInfo.getDisplayName() + ":test:vector:set:binary").getBytes(); byte[] elementId = "binary_element".getBytes(); // Add vectors using binary key and elements float[] vector1 = { 1.0f, 0.0f }; float[] vector2 = { 0.0f, 1.0f }; jedis.vadd(testKey, vector1, elementId); jedis.vadd(testKey, vector2, "element2".getBytes()); // Get links using binary VLINKS List> binaryLinks = jedis.vlinks(testKey, elementId); assertNotNull(binaryLinks); assertThat(binaryLinks.size(), is(greaterThan(0))); // If there are links, verify they are valid strings for (List linkList : binaryLinks) { for (byte[] rawLink : linkList) { String link = SafeEncoder.encode(rawLink); assertThat(link, is(notNullValue())); assertThat(link, not(emptyString())); } } } /** * Test VLINKS command functionality. Verifies that vector set links can be retrieved correctly. */ @Test @SinceRedisVersion("8.0.0") public void testVlinksBinaryWithScores(TestInfo testInfo) { byte[] testKey = (testInfo.getDisplayName() + ":test:vector:set:binary").getBytes(); // Add some vectors to create a vector set with links float[] vector1 = { 1.0f, 0.0f }; float[] vector2 = { 0.0f, 1.0f }; float[] vector3 = { 1.0f, 1.0f }; jedis.vadd(testKey, vector1, "element1".getBytes()); jedis.vadd(testKey, vector2, "element2".getBytes()); jedis.vadd(testKey, vector3, "element3".getBytes()); // Get links for element1 List> links = jedis.vlinksWithScores(testKey, "element1".getBytes()); assertNotNull(links); assertFalse(links.isEmpty()); for (Map scores : links) { for (byte[] element : scores.keySet()) { assertTrue(Arrays.equals(element, "element2".getBytes()) || Arrays.equals(element, "element3".getBytes())); assertTrue(scores.get(element) > 0.0); } } } /** * Test VLINKS with non-existent element. Verifies that VLINKS handles non-existent elements * correctly. */ @Test @SinceRedisVersion("8.0.0") public void testVlinksNonExistent(TestInfo testInfo) { String testKey = testInfo.getDisplayName() + ":test:vector:set:nonexistent"; // Add a vector first float[] vector = { 1.0f, 2.0f }; jedis.vadd(testKey, vector, "existing_element"); // Try to get links for non-existent element List> links = jedis.vlinks(testKey, "non_existent_element"); // Should return empty list or null for non-existent elements // Exact behavior depends on Redis implementation assertTrue(links == null || links.isEmpty()); } /** * Test VRANDMEMBER command functionality. Verifies that random vector set members can be * retrieved correctly. */ @Test @SinceRedisVersion("8.0.0") public void testVrandmember(TestInfo testInfo) { String testKey = testInfo.getDisplayName() + ":test:vector:set"; // Add some vectors to the set float[] vector1 = { 1.0f, 0.0f }; float[] vector2 = { 0.0f, 1.0f }; float[] vector3 = { 1.0f, 1.0f }; jedis.vadd(testKey, vector1, "element1"); jedis.vadd(testKey, vector2, "element2"); jedis.vadd(testKey, vector3, "element3"); // Get a single random member String randomMember = jedis.vrandmember(testKey); assertNotNull(randomMember); // Should be one of the added elements assertTrue(randomMember.equals("element1") || randomMember.equals("element2") || randomMember.equals("element3")); } /** * Test VRANDMEMBER with count parameter. Verifies that multiple random members can be retrieved. */ @Test @SinceRedisVersion("8.0.0") public void testVrandmemberWithCount(TestInfo testInfo) { String testKey = testInfo.getDisplayName() + ":test:vector:set:count"; // Add multiple vectors for (int i = 1; i <= 5; i++) { float[] vector = { (float) i, (float) i }; jedis.vadd(testKey, vector, "element" + i); } // Get 3 random members List randomMembers = jedis.vrandmember(testKey, 3); assertNotNull(randomMembers); assertEquals(3, randomMembers.size()); // All returned members should be valid element IDs String[] validElements = { "element1", "element2", "element3", "element4", "element5" }; for (String member : randomMembers) { assertTrue(asList(validElements).contains(member)); } // Test with count larger than set size List allMembers = jedis.vrandmember(testKey, 10); assertNotNull(allMembers); assertTrue(allMembers.size() <= 5); // Should not exceed actual set size } /** * Test VRANDMEMBER with binary key. Verifies that VRANDMEMBER works with byte array keys. */ @Test @SinceRedisVersion("8.0.0") public void testVrandmemberBinary(TestInfo testInfo) { byte[] testKey = (testInfo.getDisplayName() + ":test:vector:set:binary").getBytes(); // Add vectors using binary key float[] vector1 = { 1.0f, 0.0f }; float[] vector2 = { 0.0f, 1.0f }; jedis.vadd(testKey, vector1, "binary_element1".getBytes()); jedis.vadd(testKey, vector2, "binary_element2".getBytes()); // Get random member using binary key byte[] randomMember = jedis.vrandmember(testKey); assertNotNull(randomMember); // Convert to string for comparison String randomMemberStr = SafeEncoder.encode(randomMember); assertTrue( randomMemberStr.equals("binary_element1") || randomMemberStr.equals("binary_element2")); // Test with count using binary key List randomMembers = jedis.vrandmember(testKey, 2); assertNotNull(randomMembers); assertTrue(randomMembers.size() <= 2); // Verify all returned members are valid for (byte[] member : randomMembers) { assertNotNull(member); String memberStr = SafeEncoder.encode(member); assertTrue(memberStr.equals("binary_element1") || memberStr.equals("binary_element2")); } } /** * Test VRANDMEMBER with empty vector set. Verifies that VRANDMEMBER handles empty sets correctly. */ @Test @SinceRedisVersion("8.0.0") public void testVrandmemberEmptySet(TestInfo testInfo) { String testKey = testInfo.getDisplayName() + ":test:empty:vector:set"; // Try to get random member from empty/non-existent set String randomMember = jedis.vrandmember(testKey); // Should return null for empty set assertNull(randomMember); // Test with count on empty set List randomMembers = jedis.vrandmember(testKey, 5); // Should return empty list for empty set assertTrue(randomMembers.isEmpty()); } /** * Test VRANDMEMBER with single element. Verifies that VRANDMEMBER works correctly with only one * element. */ @Test @SinceRedisVersion("8.0.0") public void testVrandmemberSingleElement(TestInfo testInfo) { String testKey = testInfo.getDisplayName() + ":test:vector:set:single"; // Add only one vector float[] vector = { 1.0f, 2.0f }; jedis.vadd(testKey, vector, "single_element"); // Get random member (should always be the single element) String randomMember = jedis.vrandmember(testKey); assertNotNull(randomMember); assertEquals("single_element", randomMember); // Test with count List randomMembers = jedis.vrandmember(testKey, 3); assertNotNull(randomMembers); assertEquals(1, randomMembers.size()); // Should only return the single element assertEquals("single_element", randomMembers.get(0)); } /** * Test VRANDMEMBER with negative count. Verifies that VRANDMEMBER handles negative count (allows * duplicates). */ @Test @SinceRedisVersion("8.0.0") public void testVrandmemberNegativeCount(TestInfo testInfo) { String testKey = testInfo.getDisplayName() + ":test:vector:set:negative"; // Add some vectors float[] vector1 = { 1.0f, 0.0f }; float[] vector2 = { 0.0f, 1.0f }; jedis.vadd(testKey, vector1, "element1"); jedis.vadd(testKey, vector2, "element2"); // Get random members with negative count (allows duplicates) List randomMembers = jedis.vrandmember(testKey, -5); assertNotNull(randomMembers); assertEquals(5, randomMembers.size()); // Should return exactly 5 elements (with possible // duplicates) // All returned members should be valid element IDs for (String member : randomMembers) { assertTrue(member.equals("element1") || member.equals("element2")); } } /** * Test VREM command functionality. Verifies that vector set elements can be removed correctly. */ @Test @SinceRedisVersion("8.0.0") public void testVrem(TestInfo testInfo) { String testKey = testInfo.getDisplayName() + ":test:vector:set"; // Add some vectors to the set float[] vector1 = { 1.0f, 0.0f }; float[] vector2 = { 0.0f, 1.0f }; float[] vector3 = { 1.0f, 1.0f }; jedis.vadd(testKey, vector1, "element1"); jedis.vadd(testKey, vector2, "element2"); jedis.vadd(testKey, vector3, "element3"); // Verify initial cardinality assertEquals(3L, jedis.vcard(testKey)); // Remove one element boolean removed = jedis.vrem(testKey, "element2"); assertTrue(removed); assertEquals(2L, jedis.vcard(testKey)); // Try to remove the same element again (should return false) removed = jedis.vrem(testKey, "element2"); assertFalse(removed); assertEquals(2L, jedis.vcard(testKey)); // Remove remaining elements removed = jedis.vrem(testKey, "element1"); assertTrue(removed); assertEquals(1L, jedis.vcard(testKey)); removed = jedis.vrem(testKey, "element3"); assertTrue(removed); assertEquals(0L, jedis.vcard(testKey)); } /** * Test VREM with binary key and elements. Verifies that VREM works with byte array keys and * elements. */ @Test @SinceRedisVersion("8.0.0") public void testVremBinary(TestInfo testInfo) { byte[] testKey = (testInfo.getDisplayName() + ":test:vector:set:binary").getBytes(); // Add vectors using binary key and elements float[] vector1 = { 1.0f, 0.0f }; float[] vector2 = { 0.0f, 1.0f }; float[] vector3 = { 1.0f, 1.0f }; jedis.vadd(testKey, vector1, "binary_element1".getBytes()); jedis.vadd(testKey, vector2, "binary_element2".getBytes()); jedis.vadd(testKey, vector3, "binary_element3".getBytes()); // Verify initial cardinality assertEquals(3L, jedis.vcard(testKey)); // Remove element using binary VREM boolean removed = jedis.vrem(testKey, "binary_element2".getBytes()); assertTrue(removed); assertEquals(2L, jedis.vcard(testKey)); // Remove remaining elements using binary VREM boolean removed1 = jedis.vrem(testKey, "binary_element1".getBytes()); boolean removed3 = jedis.vrem(testKey, "binary_element3".getBytes()); assertTrue(removed1); assertTrue(removed3); assertEquals(0L, jedis.vcard(testKey)); } /** * Test VREM with non-existent elements. Verifies that VREM handles non-existent elements * correctly. */ @Test @SinceRedisVersion("8.0.0") public void testVremNonExistent(TestInfo testInfo) { String testKey = testInfo.getDisplayName() + ":test:vector:set:nonexistent"; // Add one vector float[] vector = { 1.0f, 2.0f }; jedis.vadd(testKey, vector, "existing_element"); // Try to remove non-existent element boolean removed = jedis.vrem(testKey, "non_existent_element"); assertFalse(removed); assertEquals(1L, jedis.vcard(testKey)); // Cardinality should remain unchanged // Try to remove from non-existent vector set String nonExistentKey = testInfo.getDisplayName() + ":non:existent:key"; removed = jedis.vrem(nonExistentKey, "any_element"); assertFalse(removed); } /** * Test VSETATTR with empty attributes (attribute deletion). Verifies that setting empty * attributes removes them. */ @Test @SinceRedisVersion("8.0.0") public void testVsetattrDelete(TestInfo testInfo) { String testKey = testInfo.getDisplayName() + ":test:vector:set"; String elementId = "point:DELETE_ATTR"; float[] vector = { 1.0f, 2.0f }; // Add element with attributes String attributes = "category=test,priority=high"; VAddParams params = new VAddParams().setAttr(attributes); boolean result = jedis.vadd(testKey, vector, elementId, params); assertTrue(result); // Verify attributes exist String retrievedAttrs = jedis.vgetattr(testKey, elementId); assertNotNull(retrievedAttrs); assertEquals(attributes, retrievedAttrs); // Delete attributes by setting empty string boolean setResult = jedis.vsetattr(testKey, elementId, ""); assertTrue(setResult); // Verify attributes are deleted (should return null or empty) retrievedAttrs = jedis.vgetattr(testKey, elementId); assertNull(retrievedAttrs); } /** * Test VINFO command functionality. Verifies that vector set information can be retrieved. */ @Test @SinceRedisVersion("8.0.0") public void testVinfo(TestInfo testInfo) { String testKey = testInfo.getDisplayName() + ":test:vector:set"; float[] vector1 = { 1.0f, 2.0f }; float[] vector2 = { 3.0f, 4.0f }; // Add some elements to the vector set VAddParams params = new VAddParams().setAttr("{\"type\": \"fruit\", \"color\": \"red\"}"); boolean result1 = jedis.vadd(testKey, vector1, "element1", params); assertTrue(result1); boolean result2 = jedis.vadd(testKey, vector2, "element2"); assertTrue(result2); // Get vector set information VectorInfo info = jedis.vinfo(testKey); assertNotNull(info); // Verify basic information is present assertNotNull(info.getVectorInfo()); assertFalse(info.getVectorInfo().isEmpty()); assertEquals(2, info.getDimensionality()); assertEquals("int8", info.getType()); assertEquals(2L, info.getSize()); assertEquals(16L, info.getMaxNodes()); assertThat(info.getMaxNodeUid(), greaterThan(0L)); assertThat(info.getVSetUid(), greaterThan(0L)); assertEquals(0L, info.getProjectionInputDim()); assertEquals(1L, info.getAttributesCount()); assertNotNull(info.getMaxLevel()); } /** * Test VINFO with empty vector set. Verifies behavior when vector set doesn't exist. */ @Test @SinceRedisVersion("8.0.0") public void testVinfoNotExistingSet(TestInfo testInfo) { String testKey = testInfo.getDisplayName() + ":test:empty:vector:set"; VectorInfo info = jedis.vinfo(testKey); assertNull(info); } /** * Test VCARD command functionality. Verifies that vector set cardinality can be retrieved * correctly. */ @Test @SinceRedisVersion("8.0.0") public void testVcard(TestInfo testInfo) { String testKey = testInfo.getDisplayName() + ":test:vector:set"; float[] vector1 = { 1.0f, 2.0f }; float[] vector2 = { 3.0f, 4.0f }; // Initially, cardinality should be 0 for non-existent vector set assertEquals(0L, jedis.vcard(testKey)); // Add first element boolean result1 = jedis.vadd(testKey, vector1, "element1"); assertTrue(result1); assertEquals(1L, jedis.vcard(testKey)); assertEquals(1L, jedis.vcard(testKey.getBytes())); // Add second element boolean result2 = jedis.vadd(testKey, vector2, "element2"); assertTrue(result2); assertEquals(2L, jedis.vcard(testKey)); assertEquals(2L, jedis.vcard(testKey.getBytes())); // Try to add duplicate element (should not increase cardinality) boolean result3 = jedis.vadd(testKey, vector1, "element1"); assertFalse(result3); // Should return false for duplicate assertEquals(2L, jedis.vcard(testKey)); // Cardinality should remain 3 assertEquals(2L, jedis.vcard(testKey.getBytes())); // Remove an element boolean removed = jedis.vrem(testKey, "element2"); assertTrue(removed); assertEquals(1L, jedis.vcard(testKey)); assertEquals(1L, jedis.vcard(testKey.getBytes())); // Remove last element removed = jedis.vrem(testKey, "element1"); assertTrue(removed); assertEquals(0L, jedis.vcard(testKey)); assertEquals(0L, jedis.vcard(testKey.getBytes())); } /** * Test VCARD with non-existent vector set. */ @Test @SinceRedisVersion("8.0.0") public void testVcardNotExistingSet(TestInfo testInfo) { String testKey = testInfo.getDisplayName() + ":test:empty:vector:set"; // VCARD should return 0 for non-existent vector set assertEquals(0L, jedis.vcard(testKey)); } /** * Test VDIM command functionality. Verifies that vector set dimension can be retrieved correctly. */ @Test @SinceRedisVersion("8.0.0") public void testVdim(TestInfo testInfo) { String testKey = testInfo.getDisplayName() + ":test:vector:set"; // Add 2D vector float[] vector2D = { 1.0f, 2.0f }; boolean result = jedis.vadd(testKey, vector2D, "element1"); assertTrue(result); assertEquals(2L, jedis.vdim(testKey)); assertEquals(2L, jedis.vdim(testKey.getBytes())); // Test different dimensions String testKey3D = testInfo.getDisplayName() + ":test:vector:set:3d"; float[] vector3D = { 1.0f, 2.0f, 3.0f }; jedis.vadd(testKey3D, vector3D, "element3d"); assertEquals(3L, jedis.vdim(testKey3D)); assertEquals(3L, jedis.vdim(testKey3D.getBytes())); } @Test @SinceRedisVersion("8.0.0") public void testVdimNotExistingSet(TestInfo testInfo) { String testKey = testInfo.getDisplayName() + ":test:empty:vector:set"; JedisDataException thrown = assertThrows(JedisDataException.class, () -> jedis.vdim(testKey)); assertThat(thrown.getMessage(), is("ERR key does not exist")); thrown = assertThrows(JedisDataException.class, () -> jedis.vdim(testKey.getBytes())); assertThat(thrown.getMessage(), is("ERR key does not exist")); } // Test VDIM with empty set @Test @SinceRedisVersion("8.0.0") public void testVdimWithEmptySet(TestInfo testInfo) { String testKey = testInfo.getDisplayName() + ":test:empty:vector:set"; // Add 2D vector float[] vector2D = { 1.0f, 2.0f }; assertTrue(jedis.vadd(testKey, vector2D, "element1")); assertTrue(jedis.vrem(testKey, "element1")); assertEquals(0L, (jedis.vcard(testKey))); JedisDataException thrown = assertThrows(JedisDataException.class, () -> jedis.vdim(testKey)); assertThat(thrown.getMessage(), is("ERR key does not exist")); thrown = assertThrows(JedisDataException.class, () -> jedis.vdim(testKey.getBytes())); assertThat(thrown.getMessage(), is("ERR key does not exist")); } /** * Test VDIM with dimension reduction. Verifies that VDIM returns the reduced dimension when * REDUCE is used. */ @Test @SinceRedisVersion("8.0.0") public void testVdimWithDimensionReduction(TestInfo testInfo) { String testKey = testInfo.getDisplayName() + ":test:vector:set:reduced"; // Add 4D vector with dimension reduction to 2D float[] vector4D = { 1.0f, 2.0f, 3.0f, 4.0f }; VAddParams params = new VAddParams(); boolean result = jedis.vadd(testKey, vector4D, "element_reduced", 2, params); assertTrue(result); // VDIM should return the reduced dimension (2), not the original (4) assertEquals(2L, jedis.vdim(testKey)); assertEquals(2L, jedis.vdim(testKey.getBytes())); } /** * Test VEMB command functionality. Verifies that vector embeddings can be retrieved correctly. */ @Test @SinceRedisVersion("8.0.0") public void testVemb(TestInfo testInfo) { String testKey = testInfo.getDisplayName() + ":test:vector:set"; // Add vector to the set float[] originalVector = { 1.0f, 2.0f }; VAddParams params = new VAddParams().noQuant(); boolean result = jedis.vadd(testKey, originalVector, "element1", params); assertTrue(result); // Retrieve the vector using VEMB List retrievedVector = jedis.vemb(testKey, "element1"); assertNotNull(retrievedVector); assertEquals(2, retrievedVector.size()); // Verify vector values (with small tolerance for floating point precision) assertEquals(1.0f, retrievedVector.get(0), 0.001); assertEquals(2.0f, retrievedVector.get(1), 0.001); } /** * Test VEMB with binary key and element. Verifies that VEMB works with byte array keys and * elements. */ @Test @SinceRedisVersion("8.0.0") public void testVembBinary(TestInfo testInfo) { byte[] testKey = (testInfo.getDisplayName() + ":test:vector:set:binary").getBytes(); byte[] elementId = "binary_element".getBytes(); // Add vector to the set using binary key and element float[] originalVector = { 0.0f, 1.0f }; boolean result = jedis.vadd(testKey, originalVector, elementId); assertTrue(result); // Retrieve the vector using binary VEMB List retrievedVector = jedis.vemb(testKey, elementId); assertNotNull(retrievedVector); assertEquals(2, retrievedVector.size()); // Verify vector values assertEquals(0.0, retrievedVector.get(0), 0.001); assertEquals(1.0, retrievedVector.get(1), 0.001); } /** * Test VEMB with RAW option. Verifies that VEMB can return raw vector data when RAW flag is used * with FP32 format. */ @Test @SinceRedisVersion("8.0.0") public void testVembRaw(TestInfo testInfo) { String testKey = testInfo.getDisplayName() + ":test:vector:set:raw"; // Add vector to the set using FP32 format float[] originalVector = { 1.0f, 2.0f, 3.0f, 4.0f, 5.0f }; byte[] vectorBlob = floatArrayToFP32Bytes(originalVector); VAddParams params = new VAddParams().noQuant(); boolean result = jedis.vaddFP32(testKey, vectorBlob, "raw_element", params); assertTrue(result); // Retrieve the vector using VEMB with RAW option RawVector rawVector = jedis.vembRaw(testKey, "raw_element"); assertNotNull(rawVector); // Verify the raw data length matches the original vector length byte[] rawData = rawVector.getRawData(); int expectedLength = originalVector.length * 4; // 4 bytes per float assertEquals(expectedLength, rawData.length); // Verify the quantization type is FP32 assertEquals("f32", rawVector.getQuantizationType()); // Verify the norm is present (L2 norm of the vector) assertNotNull(rawVector.getNorm()); assertTrue(rawVector.getNorm() > 0); // Verify the raw data contains the correct float values by converting back // IEEE 754 32-bit floats are stored in little-endian format List reconstructedVector = VectorTestUtils.fp32BytesToFloatArray(rawData); // Verify the reconstructed vector matches the original assertEquals(originalVector.length, reconstructedVector.size()); for (int i = 0; i < originalVector.length; i++) { assertEquals(originalVector[i] / rawVector.getNorm(), reconstructedVector.get(i), 0.001f); } } /** * Test VEMB with RAW option. Verifies that VEMB can return raw vector data when RAW flag is used * with FP32 format. */ @Test @SinceRedisVersion("8.0.0") public void testVembRawBinary(TestInfo testInfo) { String testKey = testInfo.getDisplayName() + ":test:vector:set:raw"; // Add vector to the set using FP32 format float[] originalVector = { 1.0f, 2.0f, 3.0f, 4.0f, 5.0f }; byte[] vectorBlob = floatArrayToFP32Bytes(originalVector); VAddParams params = new VAddParams().noQuant(); boolean result = jedis.vaddFP32(testKey, vectorBlob, "raw_element", params); assertTrue(result); // Retrieve the vector using VEMB with RAW option RawVector rawVector = jedis.vembRaw(testKey.getBytes(), "raw_element".getBytes()); assertNotNull(rawVector); // Verify the raw data length matches the original vector length byte[] rawData = rawVector.getRawData(); int expectedLength = originalVector.length * 4; // 4 bytes per float assertEquals(expectedLength, rawData.length); // Verify the quantization type is FP32 assertEquals("f32", rawVector.getQuantizationType()); // Verify the norm is present (L2 norm of the vector) assertNotNull(rawVector.getNorm()); assertTrue(rawVector.getNorm() > 0); // Verify the raw data contains the correct float values by converting back // IEEE 754 32-bit floats are stored in little-endian format List reconstructedVector = VectorTestUtils.fp32BytesToFloatArray(rawData); // Verify the reconstructed vector matches the original assertEquals(originalVector.length, reconstructedVector.size()); for (int i = 0; i < originalVector.length; i++) { assertEquals(originalVector[i] / rawVector.getNorm(), reconstructedVector.get(i), 0.001f); } } /** * Test VEMB with non-existent element. Verifies that VEMB handles non-existent elements * correctly. */ @Test @SinceRedisVersion("8.0.0") public void testVembNonExistent(TestInfo testInfo) { String testKey = testInfo.getDisplayName() + ":test:vector:set:nonexistent"; // Add a vector first float[] vector = { 1.0f, 2.0f }; jedis.vadd(testKey, vector, "existing_element"); // Try to retrieve non-existent element assertNull(jedis.vemb(testKey, "non_existent_element")); } /** * Test VSIM command functionality. Verifies vector similarity search with vectors and elements. */ @Test @SinceRedisVersion("8.0.0") public void testVsim(TestInfo testInfo) { String testKey = testInfo.getDisplayName() + ":test:vector:set"; setupVSimTestSet(testKey); // Test vsim with vector List similar = jedis.vsim(testKey, new float[] { 0.15f, 0.25f, 0.35f }); assertNotNull(similar); assertThat(similar, is(not(empty()))); assertThat(similar.size(), is(4)); assertThat(similar, hasItems("element1", "element2", "element3", "element4")); // Test vsim with element similar = jedis.vsimByElement(testKey, "element1"); assertNotNull(similar); assertThat(similar, is(not(empty()))); assertThat(similar.size(), is(4)); assertThat(similar, hasItems("element1", "element2", "element3", "element4")); // Test vsim with vector and parameters VSimParams params = new VSimParams().count(2); similar = jedis.vsim(testKey, new float[] { 0.15f, 0.25f, 0.35f }, params); assertNotNull(similar); assertThat(similar.size(), is(2)); // Test vsim with element and parameters similar = jedis.vsimByElement(testKey, "element1", params); assertNotNull(similar); assertThat(similar.size(), is(2)); } private void setupVSimTestSet(String testKey) { // Add test vectors float[] vector1 = { 0.1f, 0.2f, 0.3f }; float[] vector2 = { 0.2f, 0.3f, 0.4f }; float[] vector3 = { 0.3f, 0.4f, 0.5f }; float[] vector4 = { -0.1f, -0.2f, -0.3f }; jedis.vadd(testKey, vector1, "element1"); jedis.vadd(testKey, vector2, "element2"); jedis.vadd(testKey, vector3, "element3"); jedis.vadd(testKey, vector4, "element4"); } /** * Test VSIM command with scores functionality. Verifies vector similarity search returns scores * correctly. */ @Test @SinceRedisVersion("8.0.0") public void testVsimWithScores(TestInfo testInfo) { String testKey = testInfo.getDisplayName() + ":test:vector:set:scores"; setupVSimTestSet(testKey); // Test vsim with vector and scores VSimParams params = new VSimParams(); Map similarWithScores = jedis.vsimWithScores(testKey, new float[] { 0.15f, 0.25f, 0.35f }, params); assertThat(similarWithScores.keySet(), hasItems("element1", "element2", "element3")); assertThat(similarWithScores.values(), everyItem(greaterThanOrEqualTo(0.0))); // Test vsim with element and scores similarWithScores = jedis.vsimByElementWithScores(testKey, "element1", params); assertThat(similarWithScores.keySet(), hasItems("element1", "element2", "element3")); assertThat(similarWithScores.get("element1"), closeTo(1, 0.01)); assertThat(similarWithScores.get("element4"), closeTo(0, 0.01)); assertEquals(1.0, similarWithScores.get("element1"), 0.001); // Test with count parameter params = new VSimParams().count(2); similarWithScores = jedis.vsimWithScores(testKey, new float[] { 0.15f, 0.25f, 0.35f }, params); assertThat(similarWithScores.keySet(), hasItems("element1", "element2")); assertThat(similarWithScores.values(), everyItem(greaterThan(0.0))); // Test with epsilon parameter (distance-based filtering) params = new VSimParams().epsilon(0.2); // Only elements with similarity >= 0.8 similarWithScores = jedis.vsimWithScores(testKey, new float[] { -0.1f, -0.2f, -0.3f }, params); assertNotNull(similarWithScores); assertThat(similarWithScores.keySet(), hasItems("element4")); // Verify all returned scores meet the epsilon threshold for (Double score : similarWithScores.values()) { assertTrue(score >= (1.0 - 0.2)); // score >= 0.8 } } /** * Test VSIM with scores and attributes. */ @Test @SinceRedisVersion("8.2.0") public void testVsimWithScoresAndAttribs(TestInfo testInfo) { String testKey = testInfo.getDisplayName() + ":test:vector:set:scores:attribs"; // Add test vectors with attributes VAddParams addParams1 = new VAddParams().setAttr("category=test,priority=high"); jedis.vadd(testKey, new float[] { 0.1f, 0.2f, 0.3f }, "element1", addParams1); VAddParams addParams2 = new VAddParams().setAttr("category=prod,priority=low"); jedis.vadd(testKey, new float[] { 0.15f, 0.25f, 0.35f }, "element2", addParams2); // Add element without attributes jedis.vadd(testKey, new float[] { 0.9f, 0.8f, 0.7f }, "element3"); VSimParams params = new VSimParams(); Map similarWithScoresAndAttribs = jedis .vsimWithScoresAndAttribs(testKey, new float[] { 0.15f, 0.25f, 0.35f }, params); assertNotNull(similarWithScoresAndAttribs); assertThat(similarWithScoresAndAttribs, is(not(anEmptyMap()))); // Verify scores and attributes are present for (Map.Entry entry : similarWithScoresAndAttribs.entrySet()) { String element = entry.getKey(); VSimScoreAttribs data = entry.getValue(); assertNotNull(data); assertNotNull(data.getScore()); assertThat(data.getScore(), is(both(greaterThanOrEqualTo(0.0)).and(lessThanOrEqualTo(1.0)))); // Check attributes based on element if ("element1".equals(element)) { assertEquals("category=test,priority=high", data.getAttributes()); } else if ("element2".equals(element)) { assertEquals("category=prod,priority=low", data.getAttributes()); } else if ("element3".equals(element)) { assertNull(data.getAttributes()); // No attributes set } } } /** * Test VSIM by element with scores and attributes. */ @Test @SinceRedisVersion("8.2.0") public void testVsimByElementWithScoresAndAttribs(TestInfo testInfo) { String testKey = testInfo.getDisplayName() + ":test:vector:set:element:scores:attribs"; // Add test vectors with attributes VAddParams addParams1 = new VAddParams().setAttr("type=reference,quality=high"); jedis.vadd(testKey, new float[] { 0.1f, 0.2f, 0.3f }, "reference", addParams1); VAddParams addParams2 = new VAddParams().setAttr("type=similar,quality=medium"); jedis.vadd(testKey, new float[] { 0.12f, 0.22f, 0.32f }, "similar1", addParams2); VAddParams addParams3 = new VAddParams().setAttr("type=different,quality=low"); jedis.vadd(testKey, new float[] { 0.9f, 0.8f, 0.7f }, "different", addParams3); VSimParams params = new VSimParams(); Map similarWithScoresAndAttribs = jedis .vsimByElementWithScoresAndAttribs(testKey, "reference", params); assertNotNull(similarWithScoresAndAttribs); assertThat(similarWithScoresAndAttribs, is(not(anEmptyMap()))); // Reference element should have perfect similarity with itself assertTrue(similarWithScoresAndAttribs.containsKey("reference")); VSimScoreAttribs referenceData = similarWithScoresAndAttribs.get("reference"); assertThat(referenceData.getScore(), is(closeTo(1.0, 0.001))); assertEquals("type=reference,quality=high", referenceData.getAttributes()); } /** * Test VSIM command with binary keys and elements. Verifies vector similarity search works with * byte arrays. */ @Test @SinceRedisVersion("8.0.0") public void testVsimBinary(TestInfo testInfo) { byte[] testKey = (testInfo.getDisplayName() + ":test:vector:set:binary").getBytes(); setupVSimTestSetBinary(testKey); // Test vsim with vector (binary) List similar = jedis.vsim(testKey, new float[] { 0.15f, 0.25f, 0.35f }); assertNotNull(similar); assertThat(similar, is(not(empty()))); assertThat(similar.size(), is(3)); assertThat(getBinaryElementNames(similar), hasItems("element1", "element2", "element3")); // Test vsim with element (binary) similar = jedis.vsimByElement(testKey, "element1".getBytes()); assertNotNull(similar); assertThat(similar, is(not(empty()))); assertThat(similar.size(), is(3)); assertThat(getBinaryElementNames(similar), hasItems("element1", "element2", "element3")); // Test vsim with vector and parameters (binary) VSimParams params = new VSimParams().count(2); similar = jedis.vsim(testKey, new float[] { 0.15f, 0.25f, 0.35f }, params); assertNotNull(similar); assertThat(similar.size(), is(2)); // Test vsim with element and parameters (binary) similar = jedis.vsimByElement(testKey, "element1".getBytes(), params); assertNotNull(similar); assertThat(similar.size(), is(2)); } /** * Test VSIM command with binary keys and scores. Verifies vector similarity search returns scores * with binary data. */ @Test @SinceRedisVersion("8.0.0") public void testVsimBinaryWithScores(TestInfo testInfo) { byte[] testKey = (testInfo.getDisplayName() + ":test:vector:set:binary:scores").getBytes(); setupVSimTestSetBinary(testKey); // Test vsim with vector and scores (binary) VSimParams params = new VSimParams(); Map similarWithScores = jedis.vsimWithScores(testKey, new float[] { 0.15f, 0.25f, 0.35f }, params); assertNotNull(similarWithScores); assertThat(similarWithScores, is(not(anEmptyMap()))); // Verify scores are present and valid for (Map.Entry entry : similarWithScores.entrySet()) { assertNotNull(entry.getKey()); assertNotNull(entry.getValue()); assertThat(entry.getValue(), is(greaterThan(0.0))); } // Test vsim with element and scores (binary) similarWithScores = jedis.vsimByElementWithScores(testKey, "element1".getBytes(), params); assertNotNull(similarWithScores); assertThat(similarWithScores, is(not(anEmptyMap()))); // Element1 should have perfect similarity with itself Double element1Score = similarWithScores.get("element1".getBytes()); assertNotNull(element1Score); assertThat(element1Score, is(closeTo(1.0, 0.001))); // Test with count parameter (binary) params = new VSimParams().count(2); similarWithScores = jedis.vsimWithScores(testKey, new float[] { 0.15f, 0.25f, 0.35f }, params); assertNotNull(similarWithScores); assertThat(similarWithScores.size(), is(2)); } /** * Helper method to set up test vector set for binary VSIM tests. */ private void setupVSimTestSetBinary(byte[] testKey) { // Add test vectors - same as non-binary version float[] vector1 = { 0.1f, 0.2f, 0.3f }; float[] vector2 = { 0.15f, 0.25f, 0.35f }; float[] vector3 = { 0.9f, 0.8f, 0.7f }; jedis.vadd(testKey, vector1, "element1".getBytes()); jedis.vadd(testKey, vector2, "element2".getBytes()); jedis.vadd(testKey, vector3, "element3".getBytes()); } /** * Helper method to convert binary element list to string names for assertions. */ private List getBinaryElementNames(List binaryElements) { return binaryElements.stream().map(String::new).collect(java.util.stream.Collectors.toList()); } /** * Test VSIM command with binary keys and scores and attributes. Verifies vector similarity search * returns scores and attributes with binary data. */ @Test @SinceRedisVersion("8.2.0") public void testVsimBinaryWithScoresAndAttribs(TestInfo testInfo) { byte[] testKey = (testInfo.getDisplayName() + ":test:vector:set:binary:scores:attribs") .getBytes(); setupVSimTestSetBinaryWithAttribs(testKey); // Test vsim with vector, scores and attributes (binary) VSimParams params = new VSimParams(); Map similarWithScoresAndAttribs = jedis .vsimWithScoresAndAttribs(testKey, new float[] { 0.15f, 0.25f, 0.35f }, params); assertNotNull(similarWithScoresAndAttribs); assertThat(similarWithScoresAndAttribs, is(not(anEmptyMap()))); // Verify scores and attributes are present and valid for (Map.Entry entry : similarWithScoresAndAttribs.entrySet()) { byte[] element = entry.getKey(); VSimScoreAttribs data = entry.getValue(); assertNotNull(data); assertNotNull(data.getScore()); assertThat(data.getScore(), is(both(greaterThanOrEqualTo(0.0)).and(lessThanOrEqualTo(1.0)))); // Check attributes based on element String elementName = new String(element); if ("element1".equals(elementName)) { assertEquals("category=test,priority=high", data.getAttributes()); } else if ("element2".equals(elementName)) { assertEquals("category=prod,priority=low", data.getAttributes()); } else if ("element3".equals(elementName)) { assertNull(data.getAttributes()); // No attributes set } } } /** * Test VSIM by element command with binary keys and scores and attributes. Verifies element-based * vector similarity search returns scores and attributes with binary data. */ @Test @SinceRedisVersion("8.2.0") public void testVsimByElementBinaryWithScoresAndAttribs(TestInfo testInfo) { byte[] testKey = (testInfo.getDisplayName() + ":test:vector:set:element:binary:scores:attribs") .getBytes(); // Add test vectors with attributes byte[] referenceElement = "reference".getBytes(); VAddParams addParams1 = new VAddParams().setAttr("type=reference,quality=high"); jedis.vadd(testKey, new float[] { 0.1f, 0.2f, 0.3f }, referenceElement, addParams1); byte[] similar1Element = "similar1".getBytes(); VAddParams addParams2 = new VAddParams().setAttr("type=similar,quality=medium"); jedis.vadd(testKey, new float[] { 0.12f, 0.22f, 0.32f }, "similar1".getBytes(), addParams2); byte[] differentElement = "different".getBytes(); VAddParams addParams3 = new VAddParams().setAttr("type=different,quality=low"); jedis.vadd(testKey, new float[] { 0.9f, 0.8f, 0.7f }, "different".getBytes(), addParams3); VSimParams params = new VSimParams(); Map similarWithScoresAndAttribs = jedis .vsimByElementWithScoresAndAttribs(testKey, referenceElement, params); assertNotNull(similarWithScoresAndAttribs); assertThat(similarWithScoresAndAttribs, is(not(anEmptyMap()))); // Reference element should have perfect similarity with itself VSimScoreAttribs referenceData = similarWithScoresAndAttribs.get(referenceElement); assertThat(referenceData.getScore(), is(closeTo(1.0, 0.001))); assertEquals("type=reference,quality=high", referenceData.getAttributes()); } /** * Helper method to set up test vector set for binary VSIM tests with attributes. */ private void setupVSimTestSetBinaryWithAttribs(byte[] testKey) { // Add test vectors with attributes - same as non-binary version float[] vector1 = { 0.1f, 0.2f, 0.3f }; float[] vector2 = { 0.15f, 0.25f, 0.35f }; float[] vector3 = { 0.9f, 0.8f, 0.7f }; VAddParams addParams1 = new VAddParams().setAttr("category=test,priority=high"); jedis.vadd(testKey, vector1, "element1".getBytes(), addParams1); VAddParams addParams2 = new VAddParams().setAttr("category=prod,priority=low"); jedis.vadd(testKey, vector2, "element2".getBytes(), addParams2); // Element without attributes jedis.vadd(testKey, vector3, "element3".getBytes()); } } ================================================ FILE: src/test/java/redis/clients/jedis/commands/unified/client/RedisClientAllKindOfValuesCommandsTest.java ================================================ package redis.clients.jedis.commands.unified.client; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.extension.RegisterExtension; import org.junit.jupiter.params.ParameterizedClass; import org.junit.jupiter.params.provider.MethodSource; import redis.clients.jedis.RedisProtocol; import redis.clients.jedis.UnifiedJedis; import redis.clients.jedis.commands.unified.AllKindOfValuesCommandsTestBase; import redis.clients.jedis.util.EnabledOnCommandCondition; import redis.clients.jedis.util.RedisVersionCondition; @ParameterizedClass @MethodSource("redis.clients.jedis.commands.CommandsTestsParameters#respVersions") public class RedisClientAllKindOfValuesCommandsTest extends AllKindOfValuesCommandsTestBase { @RegisterExtension public RedisVersionCondition versionCondition = new RedisVersionCondition( RedisClientCommandsTestHelper::getEndpointConfig); @RegisterExtension public EnabledOnCommandCondition enabledOnCommandCondition = new EnabledOnCommandCondition( RedisClientCommandsTestHelper::getEndpointConfig); public RedisClientAllKindOfValuesCommandsTest(RedisProtocol protocol) { super(protocol); } @Override protected UnifiedJedis createTestClient() { return RedisClientCommandsTestHelper.getClient(protocol); } @BeforeEach public void setUp() { RedisClientCommandsTestHelper.clearData(); } } ================================================ FILE: src/test/java/redis/clients/jedis/commands/unified/client/RedisClientBinaryValuesCommandsTest.java ================================================ package redis.clients.jedis.commands.unified.client; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.params.ParameterizedClass; import org.junit.jupiter.params.provider.MethodSource; import redis.clients.jedis.RedisProtocol; import redis.clients.jedis.UnifiedJedis; import redis.clients.jedis.commands.unified.BinaryValuesCommandsTestBase; @ParameterizedClass @MethodSource("redis.clients.jedis.commands.CommandsTestsParameters#respVersions") public class RedisClientBinaryValuesCommandsTest extends BinaryValuesCommandsTestBase { public RedisClientBinaryValuesCommandsTest(RedisProtocol protocol) { super(protocol); } @Override protected UnifiedJedis createTestClient() { return RedisClientCommandsTestHelper.getClient(protocol); } @BeforeEach public void setUp() { RedisClientCommandsTestHelper.clearData(); } } ================================================ FILE: src/test/java/redis/clients/jedis/commands/unified/client/RedisClientBitCommandsTest.java ================================================ package redis.clients.jedis.commands.unified.client; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.extension.RegisterExtension; import org.junit.jupiter.params.ParameterizedClass; import org.junit.jupiter.params.provider.MethodSource; import redis.clients.jedis.UnifiedJedis; import redis.clients.jedis.util.EnabledOnCommandCondition; import redis.clients.jedis.util.RedisVersionCondition; import redis.clients.jedis.RedisProtocol; import redis.clients.jedis.commands.unified.BitCommandsTestBase; @ParameterizedClass @MethodSource("redis.clients.jedis.commands.CommandsTestsParameters#respVersions") public class RedisClientBitCommandsTest extends BitCommandsTestBase { @RegisterExtension public RedisVersionCondition versionCondition = new RedisVersionCondition( RedisClientCommandsTestHelper::getEndpointConfig); @RegisterExtension public EnabledOnCommandCondition enabledOnCommandCondition = new EnabledOnCommandCondition( RedisClientCommandsTestHelper::getEndpointConfig); public RedisClientBitCommandsTest(RedisProtocol protocol) { super(protocol); } @Override protected UnifiedJedis createTestClient() { return RedisClientCommandsTestHelper.getClient(protocol); } @BeforeEach public void setUp() { RedisClientCommandsTestHelper.clearData(); } } ================================================ FILE: src/test/java/redis/clients/jedis/commands/unified/client/RedisClientCommandsTestHelper.java ================================================ package redis.clients.jedis.commands.unified.client; import redis.clients.jedis.*; public class RedisClientCommandsTestHelper { private static EndpointConfig endpoint; private static EndpointConfig getEndpointImpl() { if (endpoint == null) { endpoint = Endpoints.getRedisEndpoint("standalone0"); } return endpoint; } /** * Returns the endpoint configuration for standalone0. * This method lazily initializes the endpoint to avoid class loading issues. */ public static EndpointConfig getEndpointConfig() { return getEndpointImpl(); } public static RedisClient getClient(RedisProtocol redisProtocol) { EndpointConfig info = getEndpointImpl(); return RedisClient.builder().hostAndPort(info.getHostAndPort()).clientConfig(info.getClientConfigBuilder() .protocol(redisProtocol).build()).build(); } public static RedisClient getClient(RedisProtocol redisProtocol, EndpointConfig endpoint) { return RedisClient.builder().hostAndPort(endpoint.getHostAndPort()).clientConfig(endpoint.getClientConfigBuilder() .protocol(redisProtocol).build()).build(); } public static void clearData() { EndpointConfig info = getEndpointImpl(); try (Jedis node = new Jedis(info.getHostAndPort())) { node.auth(info.getPassword()); node.flushAll(); } } } ================================================ FILE: src/test/java/redis/clients/jedis/commands/unified/client/RedisClientExtendedVectorSetCommandsTest.java ================================================ package redis.clients.jedis.commands.unified.client; import org.junit.jupiter.api.extension.RegisterExtension; import org.junit.jupiter.params.ParameterizedClass; import org.junit.jupiter.params.provider.MethodSource; import redis.clients.jedis.RedisProtocol; import redis.clients.jedis.UnifiedJedis; import redis.clients.jedis.commands.unified.ExtendedVectorSetCommandsTestBase; import redis.clients.jedis.util.EnabledOnCommandCondition; import redis.clients.jedis.util.RedisVersionCondition; @ParameterizedClass @MethodSource("redis.clients.jedis.commands.CommandsTestsParameters#respVersions") public class RedisClientExtendedVectorSetCommandsTest extends ExtendedVectorSetCommandsTestBase { @RegisterExtension public RedisVersionCondition versionCondition = new RedisVersionCondition( RedisClientCommandsTestHelper::getEndpointConfig); @RegisterExtension public EnabledOnCommandCondition enabledOnCommandCondition = new EnabledOnCommandCondition( RedisClientCommandsTestHelper::getEndpointConfig); public RedisClientExtendedVectorSetCommandsTest(RedisProtocol protocol) { super(protocol); } @Override protected UnifiedJedis createTestClient() { return RedisClientCommandsTestHelper.getClient(protocol); } @Override protected void clearData() { RedisClientCommandsTestHelper.clearData(); } } ================================================ FILE: src/test/java/redis/clients/jedis/commands/unified/client/RedisClientGeoCommandsTest.java ================================================ package redis.clients.jedis.commands.unified.client; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.params.ParameterizedClass; import org.junit.jupiter.params.provider.MethodSource; import redis.clients.jedis.RedisProtocol; import redis.clients.jedis.UnifiedJedis; import redis.clients.jedis.commands.unified.GeoCommandsTestBase; @ParameterizedClass @MethodSource("redis.clients.jedis.commands.CommandsTestsParameters#respVersions") public class RedisClientGeoCommandsTest extends GeoCommandsTestBase { public RedisClientGeoCommandsTest(RedisProtocol protocol) { super(protocol); } @Override protected UnifiedJedis createTestClient() { return RedisClientCommandsTestHelper.getClient(protocol); } @BeforeEach public void setUp() { RedisClientCommandsTestHelper.clearData(); } } ================================================ FILE: src/test/java/redis/clients/jedis/commands/unified/client/RedisClientHashesCommandsTest.java ================================================ package redis.clients.jedis.commands.unified.client; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.extension.RegisterExtension; import org.junit.jupiter.params.ParameterizedClass; import org.junit.jupiter.params.provider.MethodSource; import redis.clients.jedis.RedisProtocol; import redis.clients.jedis.UnifiedJedis; import redis.clients.jedis.commands.unified.HashesCommandsTestBase; import redis.clients.jedis.util.EnabledOnCommandCondition; import redis.clients.jedis.util.RedisVersionCondition; @ParameterizedClass @MethodSource("redis.clients.jedis.commands.CommandsTestsParameters#respVersions") public class RedisClientHashesCommandsTest extends HashesCommandsTestBase { @RegisterExtension public RedisVersionCondition versionCondition = new RedisVersionCondition( RedisClientCommandsTestHelper::getEndpointConfig); @RegisterExtension public EnabledOnCommandCondition enabledOnCommandCondition = new EnabledOnCommandCondition( RedisClientCommandsTestHelper::getEndpointConfig); public RedisClientHashesCommandsTest(RedisProtocol protocol) { super(protocol); } @Override protected UnifiedJedis createTestClient() { return RedisClientCommandsTestHelper.getClient(protocol); } @BeforeEach public void setUp() { RedisClientCommandsTestHelper.clearData(); } } ================================================ FILE: src/test/java/redis/clients/jedis/commands/unified/client/RedisClientHotkeysCommandsTest.java ================================================ package redis.clients.jedis.commands.unified.client; import static org.awaitility.Awaitility.await; import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.greaterThan; import static org.hamcrest.Matchers.greaterThanOrEqualTo; import static org.hamcrest.Matchers.lessThan; import static org.hamcrest.Matchers.lessThanOrEqualTo; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertNotNull; import static org.junit.jupiter.api.Assertions.assertNull; import static org.junit.jupiter.api.Assertions.assertTrue; import static redis.clients.jedis.util.RedisVersionUtil.getRedisVersion; import java.time.Duration; import io.redis.test.utils.RedisVersion; import org.junit.jupiter.api.Test; import org.junit.jupiter.params.ParameterizedClass; import org.junit.jupiter.params.provider.MethodSource; import redis.clients.jedis.RedisProtocol; import redis.clients.jedis.UnifiedJedis; import redis.clients.jedis.args.HotkeysMetric; import redis.clients.jedis.commands.unified.HotkeysCommandsTestBase; import redis.clients.jedis.params.HotkeysParams; import redis.clients.jedis.resps.HotkeysInfo; import redis.clients.jedis.util.RedisVersionUtil; import redis.clients.jedis.util.TestDataUtil; @ParameterizedClass @MethodSource("redis.clients.jedis.commands.CommandsTestsParameters#respVersions") public class RedisClientHotkeysCommandsTest extends HotkeysCommandsTestBase { public RedisClientHotkeysCommandsTest(RedisProtocol protocol) { super(protocol); } @Override protected UnifiedJedis createTestClient() { return RedisClientCommandsTestHelper.getClient(protocol); } @Test public void hotkeysGetBeforeStart() { HotkeysInfo reply = jedis.hotkeysGet(); assertNull(reply); } @Test public void hotkeysLifecycle() { String startResult = jedis .hotkeysStart(HotkeysParams.hotkeysParams().metrics(HotkeysMetric.CPU)); assertEquals("OK", startResult); jedis.set("key1", "value1"); jedis.get("key1"); HotkeysInfo reply = jedis.hotkeysGet(); assertNotNull(reply); assertTrue(reply.isTrackingActive()); String stopResult = jedis.hotkeysStop(); assertEquals("OK", stopResult); reply = jedis.hotkeysGet(); assertNotNull(reply); assertFalse(reply.isTrackingActive()); assertTrue(reply.getByCpuTimeUs().containsKey("key1")); jedis.hotkeysStart(HotkeysParams.hotkeysParams().metrics(HotkeysMetric.CPU)); jedis.set("key2", "val2"); reply = jedis.hotkeysGet(); assertTrue(reply.isTrackingActive()); assertTrue(reply.getByCpuTimeUs().containsKey("key2")); assertFalse(reply.getByCpuTimeUs().containsKey("key1")); jedis.hotkeysStop(); String resetResult = jedis.hotkeysReset(); assertEquals("OK", resetResult); reply = jedis.hotkeysGet(); assertNull(reply); } @Test public void hotkeysBothMetrics() { jedis.hotkeysStart( HotkeysParams.hotkeysParams().metrics(HotkeysMetric.CPU, HotkeysMetric.NET).sample(1)); String cpuHot = "stats:counter"; for (int i = 0; i < 20; i++) { jedis.incr(cpuHot); } String netHot = "blob:data"; jedis.set(netHot, TestDataUtil.generateString(6000)); jedis.get(netHot); HotkeysInfo reply = jedis.hotkeysGet(); assertNotNull(reply); assertTrue(reply.getByCpuTimeUs().containsKey(cpuHot)); assertThat(reply.getByCpuTimeUs().get(cpuHot), greaterThan(0L)); assertTrue(reply.getByNetBytes().containsKey(netHot)); assertThat(reply.getByNetBytes().get(netHot), greaterThan(6000L)); } @Test public void hotkeysStartOptionsSample() { jedis.hotkeysStart(HotkeysParams.hotkeysParams().metrics(HotkeysMetric.CPU).sample(5)); for (int i = 0; i < 20; i++) { jedis.set("samplekey" + i, "value" + i); } HotkeysInfo reply = jedis.hotkeysGet(); assertNotNull(reply); assertEquals(5, reply.getSampleRatio()); assertThat(reply.getByCpuTimeUs().size(), lessThan(20)); } @Test public void hotkeysStartOptionsCount() { jedis.hotkeysStart(HotkeysParams.hotkeysParams().metrics(HotkeysMetric.CPU).count(10)); for (int i = 1; i <= 25; i++) { jedis.set("countkey" + i, "value" + i); } HotkeysInfo reply = jedis.hotkeysGet(); assertNotNull(reply); assertThat(reply.getByCpuTimeUs().size(), lessThanOrEqualTo(10)); } @Test public void hotkeysDurationOption() { jedis.hotkeysStart(HotkeysParams.hotkeysParams().metrics(HotkeysMetric.CPU).duration(1)); jedis.set("durationkey", "testvalue"); await().atMost(Duration.ofSeconds(2)).until(() -> { HotkeysInfo info = jedis.hotkeysGet(); return info != null && !info.isTrackingActive(); }); HotkeysInfo reply = jedis.hotkeysGet(); assertNotNull(reply); assertFalse(reply.isTrackingActive()); assertThat(reply.getCollectionDurationMs(), greaterThanOrEqualTo(1000L)); assertTrue(reply.getByCpuTimeUs().containsKey("durationkey")); } @Test public void hotkeysResponseFields() { jedis.hotkeysStart(HotkeysParams.hotkeysParams().metrics(HotkeysMetric.CPU, HotkeysMetric.NET)); jedis.set("testkey", "testvalue"); jedis.get("testkey"); HotkeysInfo reply = jedis.hotkeysGet(); assertNotNull(reply); assertTrue(reply.isTrackingActive()); assertEquals(1, reply.getSampleRatio()); assertNotNull(reply.getSelectedSlots()); // In standalone mode, server returns slot ranges (e.g., [[0, 16383]] for all slots) assertThat(reply.getCollectionStartTimeUnixMs(), greaterThan(0L)); assertThat(reply.getCollectionDurationMs(), greaterThanOrEqualTo(0L)); assertNotNull(reply.getByCpuTimeUs()); assertNotNull(reply.getByNetBytes()); assertThat(reply.getTotalCpuTimeUserMs(), greaterThanOrEqualTo(0L)); assertThat(reply.getTotalCpuTimeSysMs(), greaterThanOrEqualTo(0L)); assertThat(reply.getTotalNetBytes(), greaterThanOrEqualTo(0L)); assertNull(reply.getSampledCommandSelectedSlotsUs()); assertNull(reply.getAllCommandsSelectedSlotsUs()); assertThat(reply.getAllCommandsAllSlotsUs(), greaterThanOrEqualTo(0L)); assertNull(reply.getNetBytesSampledCommandsSelectedSlots()); assertNull(reply.getNetBytesAllCommandsSelectedSlots()); assertThat(reply.getNetBytesAllCommandsAllSlots(), greaterThanOrEqualTo(0L)); } @Test public void infoHotkeysSection() { boolean isRedis8_6_1OrHigher = getRedisVersion(jedis) .isGreaterThanOrEqualTo(RedisVersion.of("8.6.1")); String info = jedis.info(); // Hotkeys section is displayed in info even when empty starting from Redis 8.6.1+ if (isRedis8_6_1OrHigher) { assertTrue(info.contains("# Hotkeys")); } else { assertFalse(info.contains("# Hotkeys")); } jedis.hotkeysStart(HotkeysParams.hotkeysParams().metrics(HotkeysMetric.CPU)); info = jedis.info(); assertTrue(info.contains("# Hotkeys")); assertTrue(info.contains("hotkeys-tracking-active:1")); jedis.hotkeysStop(); info = jedis.info(); assertTrue(info.contains("# Hotkeys")); assertTrue(info.contains("hotkeys-tracking-active:0")); jedis.hotkeysReset(); info = jedis.info(); // Hotkeys section is displayed in info even when empty starting from Redis 8.6.1+ if (isRedis8_6_1OrHigher) { assertTrue(info.contains("# Hotkeys")); } else { assertFalse(info.contains("# Hotkeys")); } } } ================================================ FILE: src/test/java/redis/clients/jedis/commands/unified/client/RedisClientHyperLogLogCommandsTest.java ================================================ package redis.clients.jedis.commands.unified.client; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.params.ParameterizedClass; import org.junit.jupiter.params.provider.MethodSource; import redis.clients.jedis.RedisProtocol; import redis.clients.jedis.UnifiedJedis; import redis.clients.jedis.commands.unified.HyperLogLogCommandsTestBase; @ParameterizedClass @MethodSource("redis.clients.jedis.commands.CommandsTestsParameters#respVersions") public class RedisClientHyperLogLogCommandsTest extends HyperLogLogCommandsTestBase { public RedisClientHyperLogLogCommandsTest(RedisProtocol protocol) { super(protocol); } @Override protected UnifiedJedis createTestClient() { return RedisClientCommandsTestHelper.getClient(protocol); } @BeforeEach public void setUp() { RedisClientCommandsTestHelper.clearData(); } } ================================================ FILE: src/test/java/redis/clients/jedis/commands/unified/client/RedisClientListCommandsTest.java ================================================ package redis.clients.jedis.commands.unified.client; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.extension.RegisterExtension; import org.junit.jupiter.params.ParameterizedClass; import org.junit.jupiter.params.provider.MethodSource; import redis.clients.jedis.RedisProtocol; import redis.clients.jedis.UnifiedJedis; import redis.clients.jedis.commands.unified.ListCommandsTestBase; import redis.clients.jedis.util.EnabledOnCommandCondition; import redis.clients.jedis.util.RedisVersionCondition; @ParameterizedClass @MethodSource("redis.clients.jedis.commands.CommandsTestsParameters#respVersions") public class RedisClientListCommandsTest extends ListCommandsTestBase { @RegisterExtension public RedisVersionCondition versionCondition = new RedisVersionCondition( RedisClientCommandsTestHelper::getEndpointConfig); @RegisterExtension public EnabledOnCommandCondition enabledOnCommandCondition = new EnabledOnCommandCondition( RedisClientCommandsTestHelper::getEndpointConfig); public RedisClientListCommandsTest(RedisProtocol protocol) { super(protocol); } @Override protected UnifiedJedis createTestClient() { return RedisClientCommandsTestHelper.getClient(protocol); } @BeforeEach public void setUp() { RedisClientCommandsTestHelper.clearData(); } } ================================================ FILE: src/test/java/redis/clients/jedis/commands/unified/client/RedisClientMiscellaneousTest.java ================================================ package redis.clients.jedis.commands.unified.client; import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.notNullValue; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertThrows; import java.util.ArrayList; import java.util.Arrays; import java.util.List; import io.redis.test.annotations.SinceRedisVersion; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Tag; import org.junit.jupiter.api.extension.RegisterExtension; import org.junit.jupiter.params.ParameterizedClass; import org.junit.jupiter.params.provider.MethodSource; import redis.clients.jedis.AbstractPipeline; import redis.clients.jedis.RedisProtocol; import redis.clients.jedis.Response; import redis.clients.jedis.UnifiedJedis; import redis.clients.jedis.commands.unified.UnifiedJedisCommandsTestBase; import redis.clients.jedis.exceptions.JedisDataException; import redis.clients.jedis.util.EnabledOnCommandCondition; import redis.clients.jedis.util.RedisVersionCondition; @ParameterizedClass @MethodSource("redis.clients.jedis.commands.CommandsTestsParameters#respVersions") @Tag("integration") public class RedisClientMiscellaneousTest extends UnifiedJedisCommandsTestBase { @RegisterExtension public RedisVersionCondition versionCondition = new RedisVersionCondition( RedisClientCommandsTestHelper::getEndpointConfig); @RegisterExtension public EnabledOnCommandCondition enabledOnCommandCondition = new EnabledOnCommandCondition( RedisClientCommandsTestHelper::getEndpointConfig); public RedisClientMiscellaneousTest(RedisProtocol protocol) { super(protocol); } @Override protected UnifiedJedis createTestClient() { return RedisClientCommandsTestHelper.getClient(protocol); } @BeforeEach public void setUp() { RedisClientCommandsTestHelper.clearData(); } @Test public void pipeline() { final int count = 10; int totalCount = 0; for (int i = 0; i < count; i++) { jedis.set("foo" + i, "bar" + i); } totalCount += count; for (int i = 0; i < count; i++) { jedis.rpush("foobar" + i, "foo" + i, "bar" + i); } totalCount += count; List> responses = new ArrayList<>(totalCount); List expected = new ArrayList<>(totalCount); try (AbstractPipeline pipeline = jedis.pipelined()) { for (int i = 0; i < count; i++) { responses.add(pipeline.get("foo" + i)); expected.add("bar" + i); } for (int i = 0; i < count; i++) { responses.add(pipeline.lrange("foobar" + i, 0, -1)); expected.add(Arrays.asList("foo" + i, "bar" + i)); } pipeline.sync(); } for (int i = 0; i < totalCount; i++) { assertEquals(expected.get(i), responses.get(i).get()); } } @Test public void broadcast() { String script_1 = "return 'jedis'"; String sha1_1 = jedis.scriptLoad(script_1); String script_2 = "return 79"; String sha1_2 = jedis.scriptLoad(script_2); assertEquals(Arrays.asList(true, true), jedis.scriptExists(Arrays.asList(sha1_1, sha1_2))); jedis.scriptFlush(); assertEquals(Arrays.asList(false, false), jedis.scriptExists(Arrays.asList(sha1_1, sha1_2))); } @Test @SinceRedisVersion(value="7.0.0") public void broadcastWithError() { JedisDataException error = assertThrows(JedisDataException.class, () -> jedis.functionDelete("xyz")); assertEquals("ERR Library not found", error.getMessage()); } @Test public void info() { String info = jedis.info(); assertThat(info, notNullValue()); info = jedis.info("server"); assertThat(info, notNullValue()); } } ================================================ FILE: src/test/java/redis/clients/jedis/commands/unified/client/RedisClientSetCommandsTest.java ================================================ package redis.clients.jedis.commands.unified.client; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.extension.RegisterExtension; import org.junit.jupiter.params.ParameterizedClass; import org.junit.jupiter.params.provider.MethodSource; import redis.clients.jedis.RedisProtocol; import redis.clients.jedis.UnifiedJedis; import redis.clients.jedis.commands.unified.SetCommandsTestBase; import redis.clients.jedis.util.EnabledOnCommandCondition; import redis.clients.jedis.util.RedisVersionCondition; @ParameterizedClass @MethodSource("redis.clients.jedis.commands.CommandsTestsParameters#respVersions") public class RedisClientSetCommandsTest extends SetCommandsTestBase { @RegisterExtension public RedisVersionCondition versionCondition = new RedisVersionCondition( RedisClientCommandsTestHelper::getEndpointConfig); @RegisterExtension public EnabledOnCommandCondition enabledOnCommandCondition = new EnabledOnCommandCondition( RedisClientCommandsTestHelper::getEndpointConfig); public RedisClientSetCommandsTest(RedisProtocol protocol) { super(protocol); } @Override protected UnifiedJedis createTestClient() { return RedisClientCommandsTestHelper.getClient(protocol); } @BeforeEach public void setUp() { RedisClientCommandsTestHelper.clearData(); } } ================================================ FILE: src/test/java/redis/clients/jedis/commands/unified/client/RedisClientSortedSetCommandsTest.java ================================================ package redis.clients.jedis.commands.unified.client; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.extension.RegisterExtension; import org.junit.jupiter.params.ParameterizedClass; import org.junit.jupiter.params.provider.MethodSource; import redis.clients.jedis.RedisProtocol; import redis.clients.jedis.UnifiedJedis; import redis.clients.jedis.commands.unified.SortedSetCommandsTestBase; import redis.clients.jedis.util.EnabledOnCommandCondition; import redis.clients.jedis.util.RedisVersionCondition; @ParameterizedClass @MethodSource("redis.clients.jedis.commands.CommandsTestsParameters#respVersions") public class RedisClientSortedSetCommandsTest extends SortedSetCommandsTestBase { @RegisterExtension public RedisVersionCondition versionCondition = new RedisVersionCondition( RedisClientCommandsTestHelper::getEndpointConfig); @RegisterExtension public EnabledOnCommandCondition enabledOnCommandCondition = new EnabledOnCommandCondition( RedisClientCommandsTestHelper::getEndpointConfig); public RedisClientSortedSetCommandsTest(RedisProtocol protocol) { super(protocol); } @Override protected UnifiedJedis createTestClient() { return RedisClientCommandsTestHelper.getClient(protocol); } @BeforeEach public void setUp() { RedisClientCommandsTestHelper.clearData(); } } ================================================ FILE: src/test/java/redis/clients/jedis/commands/unified/client/RedisClientStreamsBinaryCommandsTest.java ================================================ package redis.clients.jedis.commands.unified.client; import org.junit.jupiter.api.extension.RegisterExtension; import org.junit.jupiter.params.ParameterizedClass; import org.junit.jupiter.params.provider.MethodSource; import redis.clients.jedis.RedisProtocol; import redis.clients.jedis.UnifiedJedis; import redis.clients.jedis.commands.unified.StreamsBinaryCommandsTestBase; import redis.clients.jedis.util.EnabledOnCommandCondition; import redis.clients.jedis.util.RedisVersionCondition; @ParameterizedClass @MethodSource("redis.clients.jedis.commands.CommandsTestsParameters#respVersions") public class RedisClientStreamsBinaryCommandsTest extends StreamsBinaryCommandsTestBase { @RegisterExtension public RedisVersionCondition versionCondition = new RedisVersionCondition( RedisClientCommandsTestHelper::getEndpointConfig); @RegisterExtension public EnabledOnCommandCondition enabledOnCommandCondition = new EnabledOnCommandCondition( RedisClientCommandsTestHelper::getEndpointConfig); public RedisClientStreamsBinaryCommandsTest(RedisProtocol protocol) { super(protocol); } @Override protected UnifiedJedis createTestClient() { return RedisClientCommandsTestHelper.getClient(protocol); } } ================================================ FILE: src/test/java/redis/clients/jedis/commands/unified/client/RedisClientStreamsCommandsTest.java ================================================ package redis.clients.jedis.commands.unified.client; import org.junit.jupiter.api.extension.RegisterExtension; import org.junit.jupiter.params.ParameterizedClass; import org.junit.jupiter.params.provider.MethodSource; import redis.clients.jedis.RedisProtocol; import redis.clients.jedis.UnifiedJedis; import redis.clients.jedis.commands.unified.StreamsCommandsTestBase; import redis.clients.jedis.util.EnabledOnCommandCondition; import redis.clients.jedis.util.RedisVersionCondition; @ParameterizedClass @MethodSource("redis.clients.jedis.commands.CommandsTestsParameters#respVersions") public class RedisClientStreamsCommandsTest extends StreamsCommandsTestBase { @RegisterExtension public RedisVersionCondition versionCondition = new RedisVersionCondition( RedisClientCommandsTestHelper::getEndpointConfig); @RegisterExtension public EnabledOnCommandCondition enabledOnCommandCondition = new EnabledOnCommandCondition( RedisClientCommandsTestHelper::getEndpointConfig); public RedisClientStreamsCommandsTest(RedisProtocol protocol) { super(protocol); } @Override protected UnifiedJedis createTestClient() { return RedisClientCommandsTestHelper.getClient(protocol); } } ================================================ FILE: src/test/java/redis/clients/jedis/commands/unified/client/RedisClientStringValuesCommandsTest.java ================================================ package redis.clients.jedis.commands.unified.client; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.extension.RegisterExtension; import org.junit.jupiter.params.ParameterizedClass; import org.junit.jupiter.params.provider.MethodSource; import redis.clients.jedis.RedisProtocol; import redis.clients.jedis.UnifiedJedis; import redis.clients.jedis.commands.unified.StringValuesCommandsTestBase; import redis.clients.jedis.util.EnabledOnCommandCondition; import redis.clients.jedis.util.RedisVersionCondition; @ParameterizedClass @MethodSource("redis.clients.jedis.commands.CommandsTestsParameters#respVersions") public class RedisClientStringValuesCommandsTest extends StringValuesCommandsTestBase { @RegisterExtension public RedisVersionCondition versionCondition = new RedisVersionCondition( RedisClientCommandsTestHelper::getEndpointConfig); @RegisterExtension public EnabledOnCommandCondition enabledOnCommandCondition = new EnabledOnCommandCondition( RedisClientCommandsTestHelper::getEndpointConfig); public RedisClientStringValuesCommandsTest(RedisProtocol protocol) { super(protocol); } @Override protected UnifiedJedis createTestClient() { return RedisClientCommandsTestHelper.getClient(protocol); } @BeforeEach public void setUp() { RedisClientCommandsTestHelper.clearData(); } } ================================================ FILE: src/test/java/redis/clients/jedis/commands/unified/client/RedisClientTransactionIT.java ================================================ package redis.clients.jedis.commands.unified.client; import io.redis.test.annotations.ConditionalOnEnv; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Nested; import org.junit.jupiter.api.Test; import org.junit.jupiter.params.ParameterizedClass; import org.junit.jupiter.params.provider.MethodSource; import redis.clients.jedis.AbstractTransaction; import redis.clients.jedis.RedisProtocol; import redis.clients.jedis.Response; import redis.clients.jedis.UnifiedJedis; import redis.clients.jedis.commands.unified.UnifiedJedisCommandsTestBase; import redis.clients.jedis.exceptions.JedisDataException; import redis.clients.jedis.util.TestEnvUtil; import java.util.ArrayList; import java.util.Arrays; import java.util.List; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertNull; import static org.junit.jupiter.api.Assertions.assertThrows; import static org.junit.jupiter.api.Assertions.assertTrue; @ParameterizedClass @MethodSource("redis.clients.jedis.commands.CommandsTestsParameters#respVersions") public class RedisClientTransactionIT extends UnifiedJedisCommandsTestBase { public RedisClientTransactionIT(RedisProtocol protocol) { super(protocol); } @Override protected UnifiedJedis createTestClient() { return RedisClientCommandsTestHelper.getClient(protocol); } @BeforeEach public void setUp() { RedisClientCommandsTestHelper.clearData(); } @Test @ConditionalOnEnv(value = TestEnvUtil.ENV_REDIS_ENTERPRISE, enabled = false) public void transaction() { final int count = 10; int totalCount = 0; for (int i = 0; i < count; i++) { jedis.set("foo" + i, "bar" + i); } totalCount += count; for (int i = 0; i < count; i++) { jedis.rpush("foobar" + i, "foo" + i, "bar" + i); } totalCount += count; List responses; List expected = new ArrayList<>(totalCount); try (AbstractTransaction transaction = jedis.multi()) { for (int i = 0; i < count; i++) { transaction.get("foo" + i); expected.add("bar" + i); } for (int i = 0; i < count; i++) { transaction.lrange("foobar" + i, 0, -1); expected.add(Arrays.asList("foo" + i, "bar" + i)); } responses = transaction.exec(); } for (int i = 0; i < totalCount; i++) { assertEquals(expected.get(i), responses.get(i)); } } @Test @ConditionalOnEnv(value = TestEnvUtil.ENV_REDIS_ENTERPRISE, enabled = false) public void watch() { try (AbstractTransaction tx = jedis.transaction(false)) { assertEquals("OK", tx.watch("mykey", "somekey")); tx.multi(); jedis.set("mykey", "bar"); tx.set("mykey", "foo"); assertNull(tx.exec()); assertEquals("bar", jedis.get("mykey")); } } /** * Verify manual multi and commands sent before sending multi does not cause out of order * responses */ @Test public void transactionManualWithCommandsBeforeMulti() { try (AbstractTransaction tx = jedis.transaction(false)) { // command before multi Response txSetBeforeMulti = tx.set("mykey", "before_multi"); Response txGetBeforeMulti = tx.get("mykey"); assertEquals("OK", txSetBeforeMulti.get()); assertEquals("before_multi", txGetBeforeMulti.get()); tx.multi(); Response txSet = tx.set("mykey", "foo"); Response txGet = tx.get("mykey"); List txResp = tx.exec(); assertEquals("OK", txSet.get()); assertEquals("foo", txGet.get()); assertEquals(2, txResp.size()); assertEquals("OK", txResp.get(0)); assertEquals("foo", txResp.get(1)); } } @Test public void publishInTransaction() { try (AbstractTransaction tx = jedis.multi()) { Response p1 = tx.publish("foo", "bar"); Response p2 = tx.publish("foo".getBytes(), "bar".getBytes()); tx.exec(); assertEquals(0, p1.get().longValue()); assertEquals(0, p2.get().longValue()); } } @Nested class ResponseHandlingIT { @BeforeEach public void setUp() { RedisClientCommandsTestHelper.clearData(); } @Test public void notInTransactionResponseReturnsExpectedValue() { // Commands executed before multi() should return immediate responses try (AbstractTransaction tx = jedis.transaction(false)) { Response setResponse = tx.set("key1", "value1"); Response getResponse = tx.get("key1"); // Responses should be available immediately (not in transaction yet) assertEquals("OK", setResponse.get()); assertEquals("value1", getResponse.get()); } } @Test public void notInTransactionResponsePropagatesException() { // Commands executed before multi() that fail should propagate exceptions try (AbstractTransaction tx = jedis.transaction(false)) { Response setResponse = tx.set("key1", "not_a_number"); Response incrResponse = tx.incr("key1"); // Set should succeed assertEquals("OK", setResponse.get()); // Incr should fail and propagate exception immediately JedisDataException ex = assertThrows(JedisDataException.class, incrResponse::get); assertEquals("ERR value is not an integer or out of range", ex.getMessage()); } } @Test public void inTransactionResponseThrowsBeforeExec() { // Calling response.get() before exec() should throw IllegalStateException try (AbstractTransaction tx = jedis.multi()) { Response response = tx.set("key1", "value1"); // Attempting to get response before exec() should throw IllegalStateException ex = assertThrows(IllegalStateException.class, response::get); assertTrue(ex.getMessage() .contains("Please close pipeline or multi block before calling this method")); // Now exec the transaction tx.exec(); // After exec, response should be available assertEquals("OK", response.get()); } } @Test public void afterExecResponseContainsActualResults() { // After exec(), Response objects should contain actual results from Redis try (AbstractTransaction tx = jedis.multi()) { Response setResponse = tx.set("key1", "value1"); Response getResponse = tx.get("key1"); tx.exec(); // Verify Response objects contain correct values assertEquals("OK", setResponse.get()); assertEquals("value1", getResponse.get()); } } @Test public void execReturnsListWithAllResultsInOrder() { // exec() should return List with all command results in order try (AbstractTransaction tx = jedis.multi()) { tx.set("key1", "value1"); tx.get("key1"); tx.del("key1"); List results = tx.exec(); // Verify all results are in the correct order assertEquals(3, results.size()); assertEquals("OK", results.get(0)); // set key1 assertEquals("value1", results.get(1)); // get key1 assertEquals(1L, results.get(2)); // del key1 } } } } ================================================ FILE: src/test/java/redis/clients/jedis/commands/unified/client/RedisClientVectorSetCommandsTest.java ================================================ package redis.clients.jedis.commands.unified.client; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.extension.RegisterExtension; import org.junit.jupiter.params.ParameterizedClass; import org.junit.jupiter.params.provider.MethodSource; import redis.clients.jedis.RedisProtocol; import redis.clients.jedis.UnifiedJedis; import redis.clients.jedis.commands.unified.VectorSetCommandsTestBase; import redis.clients.jedis.util.EnabledOnCommandCondition; import redis.clients.jedis.util.RedisVersionCondition; @ParameterizedClass @MethodSource("redis.clients.jedis.commands.CommandsTestsParameters#respVersions") public class RedisClientVectorSetCommandsTest extends VectorSetCommandsTestBase { @RegisterExtension public RedisVersionCondition versionCondition = new RedisVersionCondition( RedisClientCommandsTestHelper::getEndpointConfig); @RegisterExtension public EnabledOnCommandCondition enabledOnCommandCondition = new EnabledOnCommandCondition( RedisClientCommandsTestHelper::getEndpointConfig); public RedisClientVectorSetCommandsTest(RedisProtocol protocol) { super(protocol); } @Override protected UnifiedJedis createTestClient() { return RedisClientCommandsTestHelper.getClient(protocol); } @BeforeEach public void setUp() { RedisClientCommandsTestHelper.clearData(); } } ================================================ FILE: src/test/java/redis/clients/jedis/commands/unified/client/search/FTHybridRedisClientCommandsTest.java ================================================ package redis.clients.jedis.commands.unified.client.search; import org.junit.jupiter.params.ParameterizedClass; import org.junit.jupiter.params.provider.MethodSource; import redis.clients.jedis.RedisProtocol; import redis.clients.jedis.UnifiedJedis; import redis.clients.jedis.commands.unified.client.RedisClientCommandsTestHelper; import redis.clients.jedis.commands.unified.search.FTHybridCommandsTestBase; @ParameterizedClass @MethodSource("redis.clients.jedis.commands.CommandsTestsParameters#respVersions") public class FTHybridRedisClientCommandsTest extends FTHybridCommandsTestBase { public FTHybridRedisClientCommandsTest(RedisProtocol protocol) { super(protocol); } @Override protected UnifiedJedis createTestClient() { return RedisClientCommandsTestHelper.getClient(protocol, endpoint); } } ================================================ FILE: src/test/java/redis/clients/jedis/commands/unified/cluster/ClusterAllKindOfValuesCommandsTest.java ================================================ package redis.clients.jedis.commands.unified.cluster; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertNull; import static org.junit.jupiter.api.Assertions.assertThrows; import static org.junit.jupiter.api.Assertions.assertTrue; import static redis.clients.jedis.params.ScanParams.SCAN_POINTER_START; import java.util.Collections; import java.util.HashSet; import java.util.Set; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.Test; import org.junit.jupiter.params.ParameterizedClass; import org.junit.jupiter.params.provider.MethodSource; import redis.clients.jedis.RedisProtocol; import redis.clients.jedis.UnifiedJedis; import redis.clients.jedis.commands.unified.AllKindOfValuesCommandsTestBase; import redis.clients.jedis.params.ScanParams; import redis.clients.jedis.resps.ScanResult; @ParameterizedClass @MethodSource("redis.clients.jedis.commands.CommandsTestsParameters#respVersions") public class ClusterAllKindOfValuesCommandsTest extends AllKindOfValuesCommandsTestBase { public ClusterAllKindOfValuesCommandsTest(RedisProtocol protocol) { super(protocol); } @Override protected UnifiedJedis createTestClient() { return ClusterCommandsTestHelper.getCleanCluster(protocol); } @AfterEach public void tearDown() { ClusterCommandsTestHelper.clearClusterData(); } @Test @Override public void existsMany() { String status = jedis.set("{foo}1", "bar1"); assertEquals("OK", status); status = jedis.set("{foo}2", "bar2"); assertEquals("OK", status); assertEquals(2L, jedis.exists("{foo}1", "{foo}2")); assertEquals(1L, jedis.del("{foo}1")); assertEquals(1L, jedis.exists("{foo}1", "{foo}2")); } @Test @Override public void del() { jedis.set("{foo}1", "bar1"); jedis.set("{foo}2", "bar2"); jedis.set("{foo}3", "bar3"); assertEquals(3L, jedis.del("{foo}1", "{foo}2", "{foo}3")); assertFalse(jedis.exists("{foo}1")); assertFalse(jedis.exists("{foo}2")); assertFalse(jedis.exists("{foo}3")); jedis.set("{foo}1", "bar1"); assertEquals(1L, jedis.del("{foo}1", "{foo}2")); assertEquals(0L, jedis.del("{foo}1", "{foo}2")); } @Test @Override public void unlink() { jedis.set("{foo}1", "bar1"); jedis.set("{foo}2", "bar2"); jedis.set("{foo}3", "bar3"); assertEquals(3, jedis.unlink("{foo}1", "{foo}2", "{foo}3")); assertEquals(0, jedis.exists("{foo}1", "{foo}2", "{foo}3")); jedis.set("{foo}1", "bar1"); assertEquals(1, jedis.unlink("{foo}1", "{foo}2")); assertEquals(0, jedis.unlink("{foo}1", "{foo}2")); jedis.set("{foo}", "bar"); assertEquals(1, jedis.unlink("{foo}")); assertFalse(jedis.exists("{foo}")); } @Test @Override public void keys() { jedis.set("foo", "bar"); jedis.set("foobar", "bar"); Set keys = jedis.keys("foo*"); Set expected = new HashSet<>(); expected.add("foo"); expected.add("foobar"); assertEquals(expected, keys); expected.clear(); keys = jedis.keys("bar*"); assertEquals(expected, keys); } @Test @Override public void rename() { jedis.set("foo{#}", "bar"); String status = jedis.rename("foo{#}", "bar{#}"); assertEquals("OK", status); assertNull(jedis.get("foo{#}")); assertEquals("bar", jedis.get("bar{#}")); } @Test @Override public void renamenx() { jedis.set("foo{&}", "bar"); assertEquals(1, jedis.renamenx("foo{&}", "bar{&}")); jedis.set("foo{&}", "bar"); assertEquals(0, jedis.renamenx("foo{&}", "bar{&}")); } @Test @Override public void dbSize() { // In cluster mode, dbSize returns the sum of keys across all nodes assertEquals(0, jedis.dbSize()); jedis.set("foo", "bar"); assertEquals(1, jedis.dbSize()); // Binary jedis.set(bfoo, bbar); assertEquals(2, jedis.dbSize()); } @Test @Override public void touch() throws Exception { assertEquals(0, jedis.touch("{foo}1", "{foo}2", "{foo}3")); jedis.set("{foo}1", "bar1"); Thread.sleep(1100); // little over 1 sec assertTrue(jedis.objectIdletime("{foo}1") > 0); assertEquals(1, jedis.touch("{foo}1")); assertEquals(0L, jedis.objectIdletime("{foo}1").longValue()); assertEquals(1, jedis.touch("{foo}1", "{foo}2", "{foo}3")); jedis.set("{foo}2", "bar2"); jedis.set("{foo}3", "bar3"); assertEquals(3, jedis.touch("{foo}1", "{foo}2", "{foo}3")); } @Test @Override public void scan() { jedis.set("{%}b", "b"); jedis.set("a{%}", "a"); ScanResult result = jedis.scan(SCAN_POINTER_START, new ScanParams().match("*{%}*")); assertEquals(SCAN_POINTER_START, result.getCursor()); assertFalse(result.getResult().isEmpty()); } @Test @Override public void scanMatch() { ScanParams params = new ScanParams(); params.match("a{-}*"); jedis.set("b{-}", "b"); jedis.set("a{-}", "a"); jedis.set("a{-}a", "aa"); ScanResult result = jedis.scan(SCAN_POINTER_START, params); assertEquals(SCAN_POINTER_START, result.getCursor()); assertFalse(result.getResult().isEmpty()); } @Test @Override public void scanCount() { ScanParams params = new ScanParams(); params.match("{a}*"); params.count(2); for (int i = 0; i < 10; i++) { jedis.set("{a}" + i, "a" + i); } ScanResult result = jedis.scan(SCAN_POINTER_START, params); assertTrue(result.getResult().size() >= 2); } @Test @Override public void scanType() { ScanParams noCount = new ScanParams().match("*{+}*"); ScanParams pagingParams = new ScanParams().match("*{+}*").count(4); jedis.set("{+}a", "a"); jedis.hset("{+}b", "b", "b"); jedis.set("c{+}", "c"); jedis.sadd("d{+}", "d"); jedis.set("e{+}", "e"); jedis.zadd("{+}f", 0d, "f"); jedis.set("{+}g", "g"); // string ScanResult scanResult; scanResult = jedis.scan(SCAN_POINTER_START, pagingParams, "string"); assertFalse(scanResult.isCompleteIteration()); int page1Count = scanResult.getResult().size(); scanResult = jedis.scan(scanResult.getCursor(), pagingParams, "string"); assertTrue(scanResult.isCompleteIteration()); int page2Count = scanResult.getResult().size(); assertEquals(4, page1Count + page2Count); scanResult = jedis.scan(SCAN_POINTER_START, noCount, "hash"); assertEquals(Collections.singletonList("{+}b"), scanResult.getResult()); scanResult = jedis.scan(SCAN_POINTER_START, noCount, "set"); assertEquals(Collections.singletonList("d{+}"), scanResult.getResult()); scanResult = jedis.scan(SCAN_POINTER_START, noCount, "zset"); assertEquals(Collections.singletonList("{+}f"), scanResult.getResult()); } @Test @Override public void scanIsCompleteIteration() { assertThrows( IllegalArgumentException.class, super::scanIsCompleteIteration); } @Test @Override public void copy() { assertFalse(jedis.copy("unkn{o}wn", "f{o}o", false)); jedis.set("{foo}1", "bar"); assertTrue(jedis.copy("{foo}1", "{foo}2", false)); assertEquals("bar", jedis.get("{foo}2")); // replace jedis.set("{foo}1", "bar1"); assertTrue(jedis.copy("{foo}1", "{foo}2", true)); assertEquals("bar1", jedis.get("{foo}2")); } } ================================================ FILE: src/test/java/redis/clients/jedis/commands/unified/cluster/ClusterBinaryValuesCommandsTest.java ================================================ package redis.clients.jedis.commands.unified.cluster; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.Disabled; import org.junit.jupiter.params.ParameterizedClass; import org.junit.jupiter.params.provider.MethodSource; import redis.clients.jedis.RedisProtocol; import redis.clients.jedis.UnifiedJedis; import redis.clients.jedis.commands.unified.BinaryValuesCommandsTestBase; @ParameterizedClass @MethodSource("redis.clients.jedis.commands.CommandsTestsParameters#respVersions") public class ClusterBinaryValuesCommandsTest extends BinaryValuesCommandsTestBase { public ClusterBinaryValuesCommandsTest(RedisProtocol protocol) { super(protocol); } @Override protected UnifiedJedis createTestClient() { return ClusterCommandsTestHelper.getCleanCluster(protocol); } @AfterEach public void tearDown() { ClusterCommandsTestHelper.clearClusterData(); } @Disabled @Override public void mget() { } @Disabled @Override public void mset() { } @Disabled @Override public void msetnx() { } } ================================================ FILE: src/test/java/redis/clients/jedis/commands/unified/cluster/ClusterBitCommandsTest.java ================================================ package redis.clients.jedis.commands.unified.cluster; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.Disabled; import org.junit.jupiter.api.Test; import org.junit.jupiter.params.ParameterizedClass; import org.junit.jupiter.params.provider.MethodSource; import redis.clients.jedis.UnifiedJedis; import redis.clients.jedis.RedisProtocol; import redis.clients.jedis.args.BitOP; import redis.clients.jedis.commands.unified.BitCommandsTestBase; import redis.clients.jedis.exceptions.JedisDataException; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertThrows; @ParameterizedClass @MethodSource("redis.clients.jedis.commands.CommandsTestsParameters#respVersions") public class ClusterBitCommandsTest extends BitCommandsTestBase { public ClusterBitCommandsTest(RedisProtocol protocol) { super(protocol); } @Override protected UnifiedJedis createTestClient() { return ClusterCommandsTestHelper.getCleanCluster(protocol); } @AfterEach public void tearDown() { ClusterCommandsTestHelper.clearClusterData(); } @Test @Override public void bitOp() { jedis.set("{key}1", "\u0060"); jedis.set("{key}2", "\u0044"); jedis.bitop(BitOP.AND, "resultAnd{key}", "{key}1", "{key}2"); String resultAnd = jedis.get("resultAnd{key}"); assertEquals("\u0040", resultAnd); jedis.bitop(BitOP.OR, "resultOr{key}", "{key}1", "{key}2"); String resultOr = jedis.get("resultOr{key}"); assertEquals("\u0064", resultOr); jedis.bitop(BitOP.XOR, "resultXor{key}", "{key}1", "{key}2"); String resultXor = jedis.get("resultXor{key}"); assertEquals("\u0024", resultXor); } @Test @Override public void bitOpNot() { jedis.setbit("key", 0, true); jedis.setbit("key", 4, true); jedis.bitop(BitOP.NOT, "resultNot{key}", "key"); String resultNot = jedis.get("resultNot{key}"); assertEquals("\u0077", resultNot); } @Disabled @Override public void bitOpBinary() { } @Test @Override public void bitOpNotMultiSourceShouldFail() { assertThrows(JedisDataException.class, () -> jedis.bitop(BitOP.NOT, "{!}dest", "{!}src1", "{!}src2")); } } ================================================ FILE: src/test/java/redis/clients/jedis/commands/unified/cluster/ClusterCommandsTestHelper.java ================================================ package redis.clients.jedis.commands.unified.cluster; import java.util.HashSet; import redis.clients.jedis.*; public class ClusterCommandsTestHelper { public static RedisClusterClient getCleanCluster(RedisProtocol protocol) { EndpointConfig endpoint = Endpoints.getRedisEndpoint("cluster-stable"); RedisClusterClient client = RedisClusterClient.builder() .nodes(new HashSet<>(endpoint.getHostsAndPorts())) .clientConfig(endpoint.getClientConfigBuilder().protocol(protocol).build()).build(); client.flushAll(); return client; } public static RedisClusterClient getCleanCluster(RedisProtocol protocol, EndpointConfig endpoint) { RedisClusterClient client = RedisClusterClient.builder() .nodes(new HashSet<>(endpoint.getHostsAndPorts())) .clientConfig(endpoint.getClientConfigBuilder().protocol(protocol).build()).build(); client.flushAll(); return client; } public static void clearClusterData() { getCleanCluster(RedisProtocol.RESP2); } } ================================================ FILE: src/test/java/redis/clients/jedis/commands/unified/cluster/ClusterExtendedVectorSetCommandsTest.java ================================================ package redis.clients.jedis.commands.unified.cluster; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.params.ParameterizedClass; import org.junit.jupiter.params.provider.MethodSource; import redis.clients.jedis.RedisProtocol; import redis.clients.jedis.UnifiedJedis; import redis.clients.jedis.commands.unified.ExtendedVectorSetCommandsTestBase; @ParameterizedClass @MethodSource("redis.clients.jedis.commands.CommandsTestsParameters#respVersions") public class ClusterExtendedVectorSetCommandsTest extends ExtendedVectorSetCommandsTestBase { public ClusterExtendedVectorSetCommandsTest(RedisProtocol protocol) { super(protocol); } @Override protected UnifiedJedis createTestClient() { return ClusterCommandsTestHelper.getCleanCluster(protocol); } @AfterEach public void tearDown() { ClusterCommandsTestHelper.clearClusterData(); } } ================================================ FILE: src/test/java/redis/clients/jedis/commands/unified/cluster/ClusterFunctionCommandsTest.java ================================================ package redis.clients.jedis.commands.unified.cluster; import io.redis.test.annotations.SinceRedisVersion; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Tag; import org.junit.jupiter.params.ParameterizedClass; import org.junit.jupiter.params.provider.MethodSource; import redis.clients.jedis.RedisProtocol; import redis.clients.jedis.UnifiedJedis; import redis.clients.jedis.commands.unified.FunctionCommandsTestBase; import redis.clients.jedis.exceptions.JedisBroadcastException; import redis.clients.jedis.exceptions.JedisException; import java.util.List; import java.util.stream.Collectors; import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.containsString; import static org.hamcrest.Matchers.equalTo; import static org.hamcrest.Matchers.everyItem; import static org.hamcrest.Matchers.instanceOf; import static org.junit.jupiter.api.Assertions.assertThrows; @ParameterizedClass @MethodSource("redis.clients.jedis.commands.CommandsTestsParameters#respVersions") @Tag("integration") public class ClusterFunctionCommandsTest extends FunctionCommandsTestBase { public ClusterFunctionCommandsTest(RedisProtocol protocol) { super(protocol); } @Override protected UnifiedJedis createTestClient() { return ClusterCommandsTestHelper.getCleanCluster(protocol); } @AfterEach public void tearDown() { ClusterCommandsTestHelper.clearClusterData(); } @Test @SinceRedisVersion(value = "7.0.0") @Override public void testFunctionKill() { JedisException e = assertThrows(JedisException.class, () -> jedis.functionKill()); assertThat(e, instanceOf(JedisBroadcastException.class)); JedisBroadcastException jbe = (JedisBroadcastException) e; List replies = jbe.getReplies().values().stream().map(e1 -> (Exception) e1) .map(Exception::getMessage).collect(Collectors.toList()); assertThat(replies.size(), equalTo(3)); assertThat(replies, everyItem(containsString("No scripts in execution right now"))); } } ================================================ FILE: src/test/java/redis/clients/jedis/commands/unified/cluster/ClusterGeoCommandsTest.java ================================================ package redis.clients.jedis.commands.unified.cluster; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Disabled; import org.junit.jupiter.api.Test; import org.junit.jupiter.params.ParameterizedClass; import org.junit.jupiter.params.provider.MethodSource; import redis.clients.jedis.GeoCoordinate; import redis.clients.jedis.RedisProtocol; import redis.clients.jedis.UnifiedJedis; import redis.clients.jedis.args.GeoUnit; import redis.clients.jedis.commands.unified.GeoCommandsTestBase; import redis.clients.jedis.params.GeoRadiusParam; import redis.clients.jedis.params.GeoRadiusStoreParam; import static org.junit.jupiter.api.Assertions.assertEquals; @ParameterizedClass @MethodSource("redis.clients.jedis.commands.CommandsTestsParameters#respVersions") public class ClusterGeoCommandsTest extends GeoCommandsTestBase { public ClusterGeoCommandsTest(RedisProtocol protocol) { super(protocol); } @Override protected UnifiedJedis createTestClient() { return ClusterCommandsTestHelper.getCleanCluster(protocol); } @AfterEach public void tearDown() { ClusterCommandsTestHelper.clearClusterData(); } @Test @Override public void georadiusStore() { // prepare datas Map coordinateMap = new HashMap<>(); coordinateMap.put("Palermo", new GeoCoordinate(13.361389, 38.115556)); coordinateMap.put("Catania", new GeoCoordinate(15.087269, 37.502669)); jedis.geoadd("Sicily {ITA}", coordinateMap); long size = jedis.georadiusStore("Sicily {ITA}", 15, 37, 200, GeoUnit.KM, GeoRadiusParam.geoRadiusParam(), GeoRadiusStoreParam.geoRadiusStoreParam().store("{ITA} SicilyStore")); assertEquals(2, size); List expected = new ArrayList<>(); expected.add("Palermo"); expected.add("Catania"); assertEquals(expected, jedis.zrange("{ITA} SicilyStore", 0, -1)); } @Disabled @Override public void georadiusStoreBinary() { } @Test @Override public void georadiusByMemberStore() { jedis.geoadd("Sicily {ITA}", 13.583333, 37.316667, "Agrigento"); jedis.geoadd("Sicily {ITA}", 13.361389, 38.115556, "Palermo"); jedis.geoadd("Sicily {ITA}", 15.087269, 37.502669, "Catania"); long size = jedis.georadiusByMemberStore("Sicily {ITA}", "Agrigento", 100, GeoUnit.KM, GeoRadiusParam.geoRadiusParam(), GeoRadiusStoreParam.geoRadiusStoreParam().store("{ITA} SicilyStore")); assertEquals(2, size); List expected = new ArrayList<>(); expected.add("Agrigento"); expected.add("Palermo"); assertEquals(expected, jedis.zrange("{ITA} SicilyStore", 0, -1)); } @Disabled @Override public void georadiusByMemberStoreBinary() { } @Disabled @Override public void geosearchstore() { } @Disabled @Override public void geosearchstoreWithdist() { } } ================================================ FILE: src/test/java/redis/clients/jedis/commands/unified/cluster/ClusterHashesCommandsTest.java ================================================ package redis.clients.jedis.commands.unified.cluster; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.params.ParameterizedClass; import org.junit.jupiter.params.provider.MethodSource; import redis.clients.jedis.RedisProtocol; import redis.clients.jedis.UnifiedJedis; import redis.clients.jedis.commands.unified.HashesCommandsTestBase; @ParameterizedClass @MethodSource("redis.clients.jedis.commands.CommandsTestsParameters#respVersions") public class ClusterHashesCommandsTest extends HashesCommandsTestBase { public ClusterHashesCommandsTest(RedisProtocol protocol) { super(protocol); } @Override protected UnifiedJedis createTestClient() { return ClusterCommandsTestHelper.getCleanCluster(protocol); } @AfterEach public void tearDown() { ClusterCommandsTestHelper.clearClusterData(); } } ================================================ FILE: src/test/java/redis/clients/jedis/commands/unified/cluster/ClusterHotkeysCommandsTest.java ================================================ package redis.clients.jedis.commands.unified.cluster; import static org.junit.jupiter.api.Assertions.assertThrows; import io.redis.test.annotations.EnabledOnCommand; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Tag; import org.junit.jupiter.params.ParameterizedClass; import org.junit.jupiter.params.provider.MethodSource; import redis.clients.jedis.RedisProtocol; import redis.clients.jedis.UnifiedJedis; import redis.clients.jedis.args.HotkeysMetric; import redis.clients.jedis.params.HotkeysParams; /** * Tests that HOTKEYS commands are not supported in cluster mode. */ @Tag("integration") @EnabledOnCommand("HOTKEYS") public class ClusterHotkeysCommandsTest { protected UnifiedJedis jedis; protected RedisProtocol protocol; public ClusterHotkeysCommandsTest() { this.protocol = RedisProtocol.RESP3; } @BeforeEach public void setUp() { jedis = ClusterCommandsTestHelper.getCleanCluster(protocol); } @AfterEach public void tearDown() { if (jedis != null) { jedis.close(); } } @Test public void hotkeysStartNotSupportedInCluster() { assertThrows(UnsupportedOperationException.class, () -> jedis.hotkeysStart(HotkeysParams.hotkeysParams().metrics(HotkeysMetric.CPU))); } @Test public void hotkeysStopNotSupportedInCluster() { assertThrows(UnsupportedOperationException.class, () -> jedis.hotkeysStop()); } @Test public void hotkeysResetNotSupportedInCluster() { assertThrows(UnsupportedOperationException.class, () -> jedis.hotkeysReset()); } @Test public void hotkeysGetNotSupportedInCluster() { assertThrows(UnsupportedOperationException.class, () -> jedis.hotkeysGet()); } } ================================================ FILE: src/test/java/redis/clients/jedis/commands/unified/cluster/ClusterHyperLogLogCommandsTest.java ================================================ package redis.clients.jedis.commands.unified.cluster; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Disabled; import org.junit.jupiter.api.Test; import org.junit.jupiter.params.ParameterizedClass; import org.junit.jupiter.params.provider.MethodSource; import redis.clients.jedis.RedisProtocol; import redis.clients.jedis.UnifiedJedis; import redis.clients.jedis.commands.unified.HyperLogLogCommandsTestBase; import static org.junit.jupiter.api.Assertions.assertEquals; @ParameterizedClass @MethodSource("redis.clients.jedis.commands.CommandsTestsParameters#respVersions") public class ClusterHyperLogLogCommandsTest extends HyperLogLogCommandsTestBase { public ClusterHyperLogLogCommandsTest(RedisProtocol protocol) { super(protocol); } @Override protected UnifiedJedis createTestClient() { return ClusterCommandsTestHelper.getCleanCluster(protocol); } @AfterEach public void tearDown() { ClusterCommandsTestHelper.clearClusterData(); } @Test @Override public void pfcounts() { long status = jedis.pfadd("{hll}_1", "foo", "bar", "zap"); assertEquals(1, status); status = jedis.pfadd("{hll}_2", "foo", "bar", "zap"); assertEquals(1, status); status = jedis.pfadd("{hll}_3", "foo", "bar", "baz"); assertEquals(1, status); status = jedis.pfcount("{hll}_1"); assertEquals(3, status); status = jedis.pfcount("{hll}_2"); assertEquals(3, status); status = jedis.pfcount("{hll}_3"); assertEquals(3, status); status = jedis.pfcount("{hll}_1", "{hll}_2"); assertEquals(3, status); status = jedis.pfcount("{hll}_1", "{hll}_2", "{hll}_3"); assertEquals(4, status); } @Test @Override public void pfmerge() { long status = jedis.pfadd("{hll}1", "foo", "bar", "zap", "a"); assertEquals(1, status); status = jedis.pfadd("{hll}2", "a", "b", "c", "foo"); assertEquals(1, status); String mergeStatus = jedis.pfmerge("{hll}3", "{hll}1", "{hll}2"); assertEquals("OK", mergeStatus); status = jedis.pfcount("{hll}3"); assertEquals(6, status); } @Disabled @Override public void pfmergeBinary() { } } ================================================ FILE: src/test/java/redis/clients/jedis/commands/unified/cluster/ClusterListCommandsTest.java ================================================ package redis.clients.jedis.commands.unified.cluster; import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; import java.util.List; import io.redis.test.annotations.SinceRedisVersion; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Tag; import org.junit.jupiter.params.ParameterizedClass; import org.junit.jupiter.params.provider.MethodSource; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import redis.clients.jedis.RedisProtocol; import redis.clients.jedis.UnifiedJedis; import redis.clients.jedis.args.ListDirection; import redis.clients.jedis.commands.unified.ListCommandsTestBase; import redis.clients.jedis.util.KeyValue; import static org.junit.jupiter.api.Assertions.assertArrayEquals; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertNotNull; import static org.junit.jupiter.api.Assertions.assertNull; @ParameterizedClass @MethodSource("redis.clients.jedis.commands.CommandsTestsParameters#respVersions") @Tag("integration") public class ClusterListCommandsTest extends ListCommandsTestBase { private final Logger logger = LoggerFactory.getLogger(getClass()); public ClusterListCommandsTest(RedisProtocol protocol) { super(protocol); } @Override protected UnifiedJedis createTestClient() { return ClusterCommandsTestHelper.getCleanCluster(protocol); } @AfterEach public void tearDown() { ClusterCommandsTestHelper.clearClusterData(); } @Test @Override public void rpoplpush() { jedis.rpush("foo{|}", "a"); jedis.rpush("foo{|}", "b"); jedis.rpush("foo{|}", "c"); jedis.rpush("dst{|}", "foo"); jedis.rpush("dst{|}", "bar"); String element = jedis.rpoplpush("foo{|}", "dst{|}"); assertEquals("c", element); List srcExpected = new ArrayList<>(); srcExpected.add("a"); srcExpected.add("b"); List dstExpected = new ArrayList<>(); dstExpected.add("c"); dstExpected.add("foo"); dstExpected.add("bar"); assertEquals(srcExpected, jedis.lrange("foo{|}", 0, 1000)); assertEquals(dstExpected, jedis.lrange("dst{|}", 0, 1000)); } @Test @Override public void blpop() throws InterruptedException { List result = jedis.blpop(1, "foo"); assertNull(result); jedis.lpush("foo", "bar"); result = jedis.blpop(1, "foo"); assertNotNull(result); assertEquals(2, result.size()); assertEquals("foo", result.get(0)); assertEquals("bar", result.get(1)); // Multi keys result = jedis.blpop(1, "{foo}", "{foo}1"); assertNull(result); jedis.lpush("{foo}", "bar"); jedis.lpush("{foo}1", "bar1"); result = jedis.blpop(1, "{foo}1", "{foo}"); assertNotNull(result); assertEquals(2, result.size()); assertEquals("{foo}1", result.get(0)); assertEquals("bar1", result.get(1)); // Binary jedis.lpush(bfoo, bbar); List bresult = jedis.blpop(1, bfoo); assertNotNull(bresult); assertEquals(2, bresult.size()); assertArrayEquals(bfoo, bresult.get(0)); assertArrayEquals(bbar, bresult.get(1)); } @Test @Override public void blpopDouble() throws InterruptedException { KeyValue result = jedis.blpop(0.1, "foo"); assertNull(result); jedis.lpush("foo", "bar"); result = jedis.blpop(3.2, "foo"); assertNotNull(result); assertEquals("foo", result.getKey()); assertEquals("bar", result.getValue()); // Multi keys result = jedis.blpop(0.18, "{foo}", "{foo}1"); assertNull(result); jedis.lpush("{foo}", "bar"); jedis.lpush("{foo}1", "bar1"); result = jedis.blpop(1d, "{foo}1", "{foo}"); assertNotNull(result); assertEquals("{foo}1", result.getKey()); assertEquals("bar1", result.getValue()); // Binary jedis.lpush(bfoo, bbar); KeyValue bresult = jedis.blpop(3.12, bfoo); assertNotNull(bresult); assertArrayEquals(bfoo, bresult.getKey()); assertArrayEquals(bbar, bresult.getValue()); } @Test @Override public void brpop() throws InterruptedException { List result = jedis.brpop(1, "foo"); assertNull(result); jedis.lpush("foo", "bar"); result = jedis.brpop(1, "foo"); assertNotNull(result); assertEquals(2, result.size()); assertEquals("foo", result.get(0)); assertEquals("bar", result.get(1)); // Multi keys result = jedis.brpop(1, "{foo}", "{foo}1"); assertNull(result); jedis.lpush("{foo}", "bar"); jedis.lpush("{foo}1", "bar1"); result = jedis.brpop(1, "{foo}1", "{foo}"); assertNotNull(result); assertEquals(2, result.size()); assertEquals("{foo}1", result.get(0)); assertEquals("bar1", result.get(1)); // Binary jedis.lpush(bfoo, bbar); List bresult = jedis.brpop(1, bfoo); assertNotNull(bresult); assertEquals(2, bresult.size()); assertArrayEquals(bfoo, bresult.get(0)); assertArrayEquals(bbar, bresult.get(1)); } @Test @Override public void brpopDouble() throws InterruptedException { KeyValue result = jedis.brpop(0.1, "foo"); assertNull(result); jedis.lpush("foo", "bar"); result = jedis.brpop(3.2, "foo"); assertNotNull(result); assertEquals("foo", result.getKey()); assertEquals("bar", result.getValue()); // Multi keys result = jedis.brpop(0.18, "{foo}", "{foo}1"); assertNull(result); jedis.lpush("{foo}", "bar"); jedis.lpush("{foo}1", "bar1"); result = jedis.brpop(1d, "{foo}1", "{foo}"); assertNotNull(result); assertEquals("{foo}1", result.getKey()); assertEquals("bar1", result.getValue()); // Binary jedis.lpush(bfoo, bbar); KeyValue bresult = jedis.brpop(3.12, bfoo); assertNotNull(bresult); assertArrayEquals(bfoo, bresult.getKey()); assertArrayEquals(bbar, bresult.getValue()); } @Test @Override public void brpoplpush() { new Thread(new Runnable() { @Override public void run() { try { Thread.sleep(100); } catch (InterruptedException e) { logger.error("", e); } jedis.lpush("foo{|}", "a"); } }).start(); String element = jedis.brpoplpush("foo{|}", "bar{|}", 0); assertEquals("a", element); assertEquals(1, jedis.llen("bar{|}")); assertEquals("a", jedis.lrange("bar{|}", 0, -1).get(0)); } @Test @Override public void lmove() { jedis.rpush("{|}foo", "bar1", "bar2", "bar3"); assertEquals("bar3", jedis.lmove("{|}foo", "{|}bar", ListDirection.RIGHT, ListDirection.LEFT)); assertEquals(Collections.singletonList("bar3"), jedis.lrange("{|}bar", 0, -1)); assertEquals(Arrays.asList("bar1", "bar2"), jedis.lrange("{|}foo", 0, -1)); } @Test @Override public void blmove() { new Thread(() -> { try { Thread.sleep(100); } catch (InterruptedException e) { logger.error("", e); } jedis.rpush("{|}foo", "bar1", "bar2", "bar3"); }).start(); assertEquals("bar3", jedis.blmove("{|}foo", "{|}bar", ListDirection.RIGHT, ListDirection.LEFT, 0)); assertEquals(Collections.singletonList("bar3"), jedis.lrange("{|}bar", 0, -1)); assertEquals(Arrays.asList("bar1", "bar2"), jedis.lrange("{|}foo", 0, -1)); } @Test @SinceRedisVersion(value="7.0.0") public void lmpop() { String mylist1 = "mylist1{.}"; String mylist2 = "mylist2{.}"; // add elements to list jedis.lpush(mylist1, "one", "two", "three", "four", "five"); jedis.lpush(mylist2, "one", "two", "three", "four", "five"); KeyValue> elements = jedis.lmpop(ListDirection.LEFT, mylist1, mylist2); assertEquals(mylist1, elements.getKey()); assertEquals(1, elements.getValue().size()); elements = jedis.lmpop(ListDirection.LEFT, 5, mylist1, mylist2); assertEquals(mylist1, elements.getKey()); assertEquals(4, elements.getValue().size()); elements = jedis.lmpop(ListDirection.RIGHT, 100, mylist1, mylist2); assertEquals(mylist2, elements.getKey()); assertEquals(5, elements.getValue().size()); elements = jedis.lmpop(ListDirection.RIGHT, mylist1, mylist2); assertNull(elements); } @Test @SinceRedisVersion(value="7.0.0") public void blmpopSimple() { String mylist1 = "mylist1{.}"; String mylist2 = "mylist2{.}"; // add elements to list jedis.lpush(mylist1, "one", "two", "three", "four", "five"); jedis.lpush(mylist2, "one", "two", "three", "four", "five"); KeyValue> elements = jedis.blmpop(1L, ListDirection.LEFT, mylist1, mylist2); assertEquals(mylist1, elements.getKey()); assertEquals(1, elements.getValue().size()); elements = jedis.blmpop(1L, ListDirection.LEFT, 5, mylist1, mylist2); assertEquals(mylist1, elements.getKey()); assertEquals(4, elements.getValue().size()); elements = jedis.blmpop(1L, ListDirection.RIGHT, 100, mylist1, mylist2); assertEquals(mylist2, elements.getKey()); assertEquals(5, elements.getValue().size()); elements = jedis.blmpop(1L, ListDirection.RIGHT, mylist1, mylist2); assertNull(elements); } } ================================================ FILE: src/test/java/redis/clients/jedis/commands/unified/cluster/ClusterSetCommandsTest.java ================================================ package redis.clients.jedis.commands.unified.cluster; import java.util.HashSet; import java.util.Set; import io.redis.test.annotations.SinceRedisVersion; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Tag; import org.junit.jupiter.params.ParameterizedClass; import org.junit.jupiter.params.provider.MethodSource; import redis.clients.jedis.RedisProtocol; import redis.clients.jedis.UnifiedJedis; import redis.clients.jedis.commands.unified.SetCommandsTestBase; import static org.junit.jupiter.api.Assertions.assertEquals; @ParameterizedClass @MethodSource("redis.clients.jedis.commands.CommandsTestsParameters#respVersions") @Tag("integration") public class ClusterSetCommandsTest extends SetCommandsTestBase { final byte[] bfoo = { 0x01, 0x02, 0x03, 0x04 }; final byte[] bfoo_same_hashslot = { 0x01, 0x02, 0x03, 0x04, 0x03, 0x00, 0x03, 0x1b }; final byte[] ba = { 0x0A }; final byte[] bb = { 0x0B }; final byte[] bc = { 0x0C }; public ClusterSetCommandsTest(RedisProtocol protocol) { super(protocol); } @Override protected UnifiedJedis createTestClient() { return ClusterCommandsTestHelper.getCleanCluster(protocol); } @AfterEach public void tearDown() { ClusterCommandsTestHelper.clearClusterData(); } @Test @Override public void smove() { jedis.sadd("{.}foo", "a"); jedis.sadd("{.}foo", "b"); jedis.sadd("{.}bar", "c"); long status = jedis.smove("{.}foo", "{.}bar", "a"); assertEquals(status, 1); Set expectedSrc = new HashSet<>(); expectedSrc.add("b"); Set expectedDst = new HashSet<>(); expectedDst.add("c"); expectedDst.add("a"); assertEquals(expectedSrc, jedis.smembers("{.}foo")); assertEquals(expectedDst, jedis.smembers("{.}bar")); status = jedis.smove("{.}foo", "{.}bar", "a"); assertEquals(status, 0); } @Test @Override public void sinter() { jedis.sadd("foo{.}", "a"); jedis.sadd("foo{.}", "b"); jedis.sadd("bar{.}", "b"); jedis.sadd("bar{.}", "c"); Set expected = new HashSet<>(); expected.add("b"); Set intersection = jedis.sinter("foo{.}", "bar{.}"); assertEquals(expected, intersection); } @Test @Override public void sinterstore() { jedis.sadd("foo{.}", "a"); jedis.sadd("foo{.}", "b"); jedis.sadd("bar{.}", "b"); jedis.sadd("bar{.}", "c"); Set expected = new HashSet<>(); expected.add("b"); long status = jedis.sinterstore("car{.}", "foo{.}", "bar{.}"); assertEquals(1, status); assertEquals(expected, jedis.smembers("car{.}")); } @Test @Override public void sunion() { jedis.sadd("{.}foo", "a"); jedis.sadd("{.}foo", "b"); jedis.sadd("{.}bar", "b"); jedis.sadd("{.}bar", "c"); Set expected = new HashSet<>(); expected.add("a"); expected.add("b"); expected.add("c"); Set union = jedis.sunion("{.}foo", "{.}bar"); assertEquals(expected, union); } @Test @Override public void sunionstore() { jedis.sadd("{.}foo", "a"); jedis.sadd("{.}foo", "b"); jedis.sadd("{.}bar", "b"); jedis.sadd("{.}bar", "c"); Set expected = new HashSet<>(); expected.add("a"); expected.add("b"); expected.add("c"); long status = jedis.sunionstore("{.}car", "{.}foo", "{.}bar"); assertEquals(3, status); assertEquals(expected, jedis.smembers("{.}car")); } @Test @Override public void sdiff() { jedis.sadd("foo{.}", "x"); jedis.sadd("foo{.}", "a"); jedis.sadd("foo{.}", "b"); jedis.sadd("foo{.}", "c"); jedis.sadd("bar{.}", "c"); jedis.sadd("car{.}", "a"); jedis.sadd("car{.}", "d"); Set expected = new HashSet<>(); expected.add("x"); expected.add("b"); Set diff = jedis.sdiff("foo{.}", "bar{.}", "car{.}"); assertEquals(expected, diff); } @Test @Override public void sdiffstore() { jedis.sadd("foo{.}", "x"); jedis.sadd("foo{.}", "a"); jedis.sadd("foo{.}", "b"); jedis.sadd("foo{.}", "c"); jedis.sadd("bar{.}", "c"); jedis.sadd("car{.}", "a"); jedis.sadd("car{.}", "d"); Set expected = new HashSet<>(); expected.add("x"); expected.add("b"); long status = jedis.sdiffstore("tar{.}", "foo{.}", "bar{.}", "car{.}"); assertEquals(2, status); assertEquals(expected, jedis.smembers("tar{.}")); } @Test @SinceRedisVersion(value="7.0.0") public void sintercard() { jedis.sadd("foo{.}", "a"); jedis.sadd("foo{.}", "b"); jedis.sadd("bar{.}", "a"); jedis.sadd("bar{.}", "b"); jedis.sadd("bar{.}", "c"); long card = jedis.sintercard("foo{.}", "bar{.}"); assertEquals(2, card); long limitedCard = jedis.sintercard(1, "foo{.}", "bar{.}"); assertEquals(1, limitedCard); // Binary jedis.sadd(bfoo, ba); jedis.sadd(bfoo, bb); jedis.sadd(bfoo_same_hashslot, ba); jedis.sadd(bfoo_same_hashslot, bb); jedis.sadd(bfoo_same_hashslot, bc); long bcard = jedis.sintercard(bfoo, bfoo_same_hashslot); assertEquals(2, bcard); long blimitedCard = jedis.sintercard(1, bfoo, bfoo_same_hashslot); assertEquals(1, blimitedCard); } } ================================================ FILE: src/test/java/redis/clients/jedis/commands/unified/cluster/ClusterSortedSetCommandsTest.java ================================================ package redis.clients.jedis.commands.unified.cluster; import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.containsInAnyOrder; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertNull; import static redis.clients.jedis.util.AssertUtil.assertByteArrayListEquals; import java.util.ArrayList; import java.util.Collections; import java.util.List; import io.redis.test.annotations.SinceRedisVersion; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Tag; import org.junit.jupiter.params.ParameterizedClass; import org.junit.jupiter.params.provider.MethodSource; import redis.clients.jedis.RedisProtocol; import redis.clients.jedis.UnifiedJedis; import redis.clients.jedis.commands.unified.SortedSetCommandsTestBase; import redis.clients.jedis.params.ZAddParams; import redis.clients.jedis.params.ZParams; import redis.clients.jedis.params.ZRangeParams; import redis.clients.jedis.resps.Tuple; import redis.clients.jedis.util.KeyValue; @ParameterizedClass @MethodSource("redis.clients.jedis.commands.CommandsTestsParameters#respVersions") @Tag("integration") public class ClusterSortedSetCommandsTest extends SortedSetCommandsTestBase { final byte[] bfoo = { 0x01, 0x02, 0x03, 0x04 }; final byte[] bfoo_same_hashslot = { 0x01, 0x02, 0x03, 0x04, 0x03, 0x00, 0x03, 0x1b }; final byte[] ba = { 0x0A }; final byte[] bb = { 0x0B }; final byte[] bc = { 0x0C }; public ClusterSortedSetCommandsTest(RedisProtocol protocol) { super(protocol); } @Override protected UnifiedJedis createTestClient() { return ClusterCommandsTestHelper.getCleanCluster(protocol); } @AfterEach public void tearDown() { ClusterCommandsTestHelper.clearClusterData(); } @Test @Override public void zunion() { jedis.zadd("{:}foo", 1, "a"); jedis.zadd("{:}foo", 2, "b"); jedis.zadd("{:}bar", 2, "a"); jedis.zadd("{:}bar", 2, "b"); ZParams params = new ZParams(); params.weights(2, 2.5); params.aggregate(ZParams.Aggregate.SUM); assertThat(jedis.zunion(params, "{:}foo", "{:}bar"), containsInAnyOrder("a", "b")); assertThat(jedis.zunionWithScores(params, "{:}foo", "{:}bar"), containsInAnyOrder( new Tuple("b", new Double(9)), new Tuple("a", new Double(7)) )); } @Test @Override public void zunionstore() { jedis.zadd("{:}foo", 1, "a"); jedis.zadd("{:}foo", 2, "b"); jedis.zadd("{:}bar", 2, "a"); jedis.zadd("{:}bar", 2, "b"); assertEquals(2, jedis.zunionstore("{:}dst", "{:}foo", "{:}bar")); List expected = new ArrayList<>(); expected.add(new Tuple("a", new Double(3))); expected.add(new Tuple("b", new Double(4))); assertEquals(expected, jedis.zrangeWithScores("{:}dst", 0, 100)); } @Test @Override public void zunionstoreParams() { jedis.zadd("{:}foo", 1, "a"); jedis.zadd("{:}foo", 2, "b"); jedis.zadd("{:}bar", 2, "a"); jedis.zadd("{:}bar", 2, "b"); ZParams params = new ZParams(); params.weights(2, 2.5); params.aggregate(ZParams.Aggregate.SUM); assertEquals(2, jedis.zunionstore("{:}dst", params, "{:}foo", "{:}bar")); List expected = new ArrayList<>(); expected.add(new Tuple("a", new Double(7))); expected.add(new Tuple("b", new Double(9))); assertEquals(expected, jedis.zrangeWithScores("{:}dst", 0, 100)); } @Test @Override public void zinter() { jedis.zadd("foo{:}", 1, "a"); jedis.zadd("foo{:}", 2, "b"); jedis.zadd("bar{:}", 2, "a"); ZParams params = new ZParams(); params.weights(2, 2.5); params.aggregate(ZParams.Aggregate.SUM); assertThat(jedis.zinter(params, "foo{:}", "bar{:}"), containsInAnyOrder("a")); assertThat(jedis.zinterWithScores(params, "foo{:}", "bar{:}"), containsInAnyOrder(new Tuple("a", new Double(7)))); } @Test @Override public void zinterstore() { jedis.zadd("foo{:}", 1, "a"); jedis.zadd("foo{:}", 2, "b"); jedis.zadd("bar{:}", 2, "a"); assertEquals(1, jedis.zinterstore("dst{:}", "foo{:}", "bar{:}")); assertEquals(Collections.singletonList(new Tuple("a", new Double(3))), jedis.zrangeWithScores("dst{:}", 0, 100)); } @Test @Override public void zintertoreParams() { jedis.zadd("foo{:}", 1, "a"); jedis.zadd("foo{:}", 2, "b"); jedis.zadd("bar{:}", 2, "a"); ZParams params = new ZParams(); params.weights(2, 2.5); params.aggregate(ZParams.Aggregate.SUM); assertEquals(1, jedis.zinterstore("dst{:}", params, "foo{:}", "bar{:}")); assertEquals(Collections.singletonList(new Tuple("a", new Double(7))), jedis.zrangeWithScores("dst{:}", 0, 100)); } @Test @Override public void bzpopmax() { assertNull(jedis.bzpopmax(1, "f{:}oo", "b{:}ar")); jedis.zadd("f{:}oo", 1d, "a", ZAddParams.zAddParams().nx()); jedis.zadd("f{:}oo", 10d, "b", ZAddParams.zAddParams().nx()); jedis.zadd("b{:}ar", 0.1d, "c", ZAddParams.zAddParams().nx()); assertEquals(new KeyValue<>("f{:}oo", new Tuple("b", 10d)), jedis.bzpopmax(0, "f{:}oo", "b{:}ar")); } @Test @Override public void bzpopmin() { assertNull(jedis.bzpopmin(1, "ba{:}r", "fo{:}o")); jedis.zadd("fo{:}o", 1d, "a", ZAddParams.zAddParams().nx()); jedis.zadd("fo{:}o", 10d, "b", ZAddParams.zAddParams().nx()); jedis.zadd("ba{:}r", 0.1d, "c", ZAddParams.zAddParams().nx()); assertEquals(new KeyValue<>("ba{:}r", new Tuple("c", 0.1d)), jedis.bzpopmin(0, "ba{:}r", "fo{:}o")); } @Test @Override public void zdiff() { jedis.zadd("{:}foo", 1.0, "a"); jedis.zadd("{:}foo", 2.0, "b"); jedis.zadd("{:}bar", 1.0, "a"); assertEquals(0, jedis.zdiff("{bar}1", "{bar}2").size()); assertThat(jedis.zdiff("{:}foo", "{:}bar"), containsInAnyOrder("b")); assertThat(jedis.zdiffWithScores("{:}foo", "{:}bar"), containsInAnyOrder(new Tuple("b", 2.0d))); } @Test @Override public void zdiffstore() { jedis.zadd("foo{:}", 1.0, "a"); jedis.zadd("foo{:}", 2.0, "b"); jedis.zadd("bar{:}", 1.0, "a"); assertEquals(0, jedis.zdiffstore("{bar}3", "{bar}1", "{bar}2")); assertEquals(1, jedis.zdiffstore("bar{:}3", "foo{:}", "bar{:}")); assertEquals(Collections.singletonList("b"), jedis.zrange("bar{:}3", 0, -1)); } @Test public void zrangestore() { jedis.zadd("foo{.}", 1, "aa"); jedis.zadd("foo{.}", 2, "c"); jedis.zadd("foo{.}", 3, "bb"); long stored = jedis.zrangestore("bar{.}", "foo{.}", ZRangeParams.zrangeByScoreParams(1, 2)); assertEquals(2, stored); List range = jedis.zrange("bar{.}", 0, -1); List expected = new ArrayList<>(); expected.add("aa"); expected.add("c"); assertEquals(expected, range); // Binary jedis.zadd(bfoo, 1d, ba); jedis.zadd(bfoo, 10d, bb); jedis.zadd(bfoo, 0.1d, bc); jedis.zadd(bfoo, 2d, ba); long bstored = jedis.zrangestore(bfoo_same_hashslot, bfoo, ZRangeParams.zrangeParams(0, 1).rev()); assertEquals(2, bstored); List brange = jedis.zrevrange(bfoo_same_hashslot, 0, 1); List bexpected = new ArrayList<>(); bexpected.add(bb); bexpected.add(ba); assertByteArrayListEquals(bexpected, brange); } @Test @SinceRedisVersion(value="7.0.0") public void zintercard() { jedis.zadd("foo{.}", 1, "a"); jedis.zadd("foo{.}", 2, "b"); jedis.zadd("bar{.}", 2, "a"); jedis.zadd("bar{.}", 1, "b"); assertEquals(2, jedis.zintercard("foo{.}", "bar{.}")); assertEquals(1, jedis.zintercard(1, "foo{.}", "bar{.}")); // Binary jedis.zadd(bfoo, 1, ba); jedis.zadd(bfoo, 2, bb); jedis.zadd(bfoo_same_hashslot, 2, ba); jedis.zadd(bfoo_same_hashslot, 2, bb); assertEquals(2, jedis.zintercard(bfoo, bfoo_same_hashslot)); assertEquals(1, jedis.zintercard(1, bfoo, bfoo_same_hashslot)); } } ================================================ FILE: src/test/java/redis/clients/jedis/commands/unified/cluster/ClusterStreamsBinaryCommandsTest.java ================================================ package redis.clients.jedis.commands.unified.cluster; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.params.ParameterizedClass; import org.junit.jupiter.params.provider.MethodSource; import redis.clients.jedis.RedisProtocol; import redis.clients.jedis.UnifiedJedis; import redis.clients.jedis.commands.unified.StreamsBinaryCommandsTestBase; @ParameterizedClass @MethodSource("redis.clients.jedis.commands.CommandsTestsParameters#respVersions") public class ClusterStreamsBinaryCommandsTest extends StreamsBinaryCommandsTestBase { public ClusterStreamsBinaryCommandsTest(RedisProtocol protocol) { super(protocol); } @Override protected UnifiedJedis createTestClient() { return ClusterCommandsTestHelper.getCleanCluster(protocol); } @AfterEach public void tearDown() { ClusterCommandsTestHelper.clearClusterData(); } } ================================================ FILE: src/test/java/redis/clients/jedis/commands/unified/cluster/ClusterStreamsCommandsTest.java ================================================ package redis.clients.jedis.commands.unified.cluster; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.params.ParameterizedClass; import org.junit.jupiter.params.provider.MethodSource; import redis.clients.jedis.RedisProtocol; import redis.clients.jedis.UnifiedJedis; import redis.clients.jedis.commands.unified.StreamsCommandsTestBase; @ParameterizedClass @MethodSource("redis.clients.jedis.commands.CommandsTestsParameters#respVersions") public class ClusterStreamsCommandsTest extends StreamsCommandsTestBase { public ClusterStreamsCommandsTest(RedisProtocol protocol) { super(protocol); } @Override protected UnifiedJedis createTestClient() { return ClusterCommandsTestHelper.getCleanCluster(protocol); } @AfterEach public void tearDown() { ClusterCommandsTestHelper.clearClusterData(); } } ================================================ FILE: src/test/java/redis/clients/jedis/commands/unified/cluster/ClusterStringValuesCommandsTest.java ================================================ package redis.clients.jedis.commands.unified.cluster; import java.util.ArrayList; import java.util.List; import io.redis.test.annotations.EnabledOnCommand; import io.redis.test.annotations.SinceRedisVersion; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Tag; import org.junit.jupiter.params.ParameterizedClass; import org.junit.jupiter.params.provider.MethodSource; import redis.clients.jedis.RedisProtocol; import redis.clients.jedis.UnifiedJedis; import redis.clients.jedis.commands.unified.StringValuesCommandsTestBase; import redis.clients.jedis.params.LCSParams; import redis.clients.jedis.params.MSetExParams; import redis.clients.jedis.resps.LCSMatchResult; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertTrue; @ParameterizedClass @MethodSource("redis.clients.jedis.commands.CommandsTestsParameters#respVersions") @Tag("integration") public class ClusterStringValuesCommandsTest extends StringValuesCommandsTestBase { public ClusterStringValuesCommandsTest(RedisProtocol protocol) { super(protocol); } @Override protected UnifiedJedis createTestClient() { return ClusterCommandsTestHelper.getCleanCluster(protocol); } @AfterEach public void tearDown() { ClusterCommandsTestHelper.clearClusterData(); } @Test @Override public void mget() { List values = jedis.mget("foo{^}", "bar{^}"); List expected = new ArrayList<>(); expected.add(null); expected.add(null); assertEquals(expected, values); jedis.set("foo{^}", "bar"); expected = new ArrayList<>(); expected.add("bar"); expected.add(null); values = jedis.mget("foo{^}", "bar{^}"); assertEquals(expected, values); jedis.set("bar{^}", "foo"); expected = new ArrayList<>(); expected.add("bar"); expected.add("foo"); values = jedis.mget("foo{^}", "bar{^}"); assertEquals(expected, values); } @Test @Override public void mset() { String status = jedis.mset("{^}foo", "bar", "{^}bar", "foo"); assertEquals("OK", status); assertEquals("bar", jedis.get("{^}foo")); assertEquals("foo", jedis.get("{^}bar")); } @Test @Override public void msetnx() { assertEquals(1, jedis.msetnx("{^}foo", "bar", "{^}bar", "foo")); assertEquals("bar", jedis.get("{^}foo")); assertEquals("foo", jedis.get("{^}bar")); assertEquals(0, jedis.msetnx("{^}foo", "bar1", "{^}bar2", "foo2")); assertEquals("bar", jedis.get("{^}foo")); assertEquals("foo", jedis.get("{^}bar")); } @Test @SinceRedisVersion(value = "7.0.0") public void lcs() { jedis.mset("key1{.}", "ohmytext", "key2{.}", "mynewtext"); LCSMatchResult stringMatchResult = jedis.lcs("key1{.}", "key2{.}", LCSParams.LCSParams()); assertEquals("mytext", stringMatchResult.getMatchString()); stringMatchResult = jedis.lcs("key1{.}", "key2{.}", LCSParams.LCSParams().idx().withMatchLen()); assertEquals(stringMatchResult.getLen(), 6); assertEquals(2, stringMatchResult.getMatches().size()); stringMatchResult = jedis.lcs("key1{.}", "key2{.}", LCSParams.LCSParams().idx().minMatchLen(10)); assertEquals(0, stringMatchResult.getMatches().size()); } @Test @EnabledOnCommand("MSETEX") public void msetex_crossslot_works_with_client_side_splitting() { // Use keys without a hashtag so they map to different hash slots String k1 = "cross:k1"; String k2 = "other:k2"; MSetExParams params = new MSetExParams().nx().ex(5); // Cross-slot msetex should work - client splits by slot boolean result = jedis.msetex(params, k1, "v1", k2, "v2"); assertTrue(result); // Verify values were set assertEquals("v1", jedis.get(k1)); assertEquals("v2", jedis.get(k2)); // Verify TTL is set assertTrue(jedis.ttl(k1) > 0); assertTrue(jedis.ttl(k2) > 0); } } ================================================ FILE: src/test/java/redis/clients/jedis/commands/unified/cluster/ClusterVectorSetCommandsTest.java ================================================ package redis.clients.jedis.commands.unified.cluster; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.params.ParameterizedClass; import org.junit.jupiter.params.provider.MethodSource; import redis.clients.jedis.RedisProtocol; import redis.clients.jedis.UnifiedJedis; import redis.clients.jedis.commands.unified.VectorSetCommandsTestBase; @ParameterizedClass @MethodSource("redis.clients.jedis.commands.CommandsTestsParameters#respVersions") public class ClusterVectorSetCommandsTest extends VectorSetCommandsTestBase { public ClusterVectorSetCommandsTest(RedisProtocol protocol) { super(protocol); } @Override protected UnifiedJedis createTestClient() { return ClusterCommandsTestHelper.getCleanCluster(protocol); } @AfterEach public void tearDown() { ClusterCommandsTestHelper.clearClusterData(); } } ================================================ FILE: src/test/java/redis/clients/jedis/commands/unified/cluster/search/FTHybridCommandsClusterTest.java ================================================ package redis.clients.jedis.commands.unified.cluster.search; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.params.ParameterizedClass; import org.junit.jupiter.params.provider.MethodSource; import redis.clients.jedis.Endpoints; import redis.clients.jedis.RedisProtocol; import redis.clients.jedis.UnifiedJedis; import redis.clients.jedis.commands.unified.cluster.ClusterCommandsTestHelper; import redis.clients.jedis.commands.unified.search.FTHybridCommandsTestBase; @ParameterizedClass @MethodSource("redis.clients.jedis.commands.CommandsTestsParameters#respVersions") public class FTHybridCommandsClusterTest extends FTHybridCommandsTestBase { @BeforeAll public static void prepareEndpoint() { endpoint = Endpoints.getRedisEndpoint("cluster-stable"); } public FTHybridCommandsClusterTest(RedisProtocol protocol) { super(protocol); } @Override protected UnifiedJedis createTestClient() { UnifiedJedis cluster = ClusterCommandsTestHelper.getCleanCluster(protocol, endpoint); return cluster; } } ================================================ FILE: src/test/java/redis/clients/jedis/commands/unified/pipeline/BinaryStreamsPipelineCommandsTest.java ================================================ package redis.clients.jedis.commands.unified.pipeline; import io.redis.test.annotations.EnabledOnCommand; import io.redis.test.annotations.SinceRedisVersion; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.junit.jupiter.params.ParameterizedClass; import org.junit.jupiter.params.provider.MethodSource; import redis.clients.jedis.RedisProtocol; import redis.clients.jedis.Response; import redis.clients.jedis.StreamEntryID; import redis.clients.jedis.exceptions.JedisDataException; import redis.clients.jedis.params.XAddParams; import redis.clients.jedis.params.XCfgSetParams; import redis.clients.jedis.params.XReadGroupParams; import redis.clients.jedis.params.XReadParams; import redis.clients.jedis.resps.StreamEntryBinary; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; import static java.util.Collections.singletonMap; import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.hasSize; import static org.junit.jupiter.api.Assertions.assertArrayEquals; import static redis.clients.jedis.StreamEntryID.XREADGROUP_UNDELIVERED_ENTRY; import static redis.clients.jedis.util.StreamEntryBinaryListMatcher.equalsStreamEntries; @ParameterizedClass @MethodSource("redis.clients.jedis.commands.CommandsTestsParameters#respVersions") public class BinaryStreamsPipelineCommandsTest extends PipelineCommandsTestBase { protected static final byte[] STREAM_KEY_1 = "{binary-stream}-1".getBytes(); protected static final byte[] STREAM_KEY_2 = "{binary-stream}-2".getBytes(); protected static final byte[] GROUP_NAME = "group-1".getBytes(); protected static final byte[] CONSUMER_NAME = "consumer-1".getBytes(); protected static final byte[] FIELD_KEY_1 = "binary-field-1".getBytes(); protected static final byte[] BINARY_VALUE_1 = new byte[] { 0x00, 0x01, 0x02, 0x03, (byte) 0xFF }; protected static final byte[] FIELD_KEY_2 = "binary-field-1".getBytes(); protected static final byte[] BINARY_VALUE_2 = "binary-value-2".getBytes(); protected static final Map HASH_1 = singletonMap(FIELD_KEY_1, BINARY_VALUE_1); protected static final Map HASH_2 = singletonMap(FIELD_KEY_2, BINARY_VALUE_2); protected static final List stream1Entries = new ArrayList<>(); protected static final List stream2Entries = new ArrayList<>(); static { stream1Entries.add(new StreamEntryBinary(new StreamEntryID("0-1"), HASH_1)); stream1Entries.add(new StreamEntryBinary(new StreamEntryID("0-3"), HASH_2)); stream2Entries.add(new StreamEntryBinary(new StreamEntryID("0-2"), HASH_1)); } public BinaryStreamsPipelineCommandsTest(RedisProtocol protocol) { super(protocol); } /** * Creates a map of stream keys to StreamEntryID objects. * @param streamOffsets Array of stream key and offset pairs * @return Map of stream keys to StreamEntryID objects */ public static Map offsets(Object... streamOffsets) { if (streamOffsets.length % 2 != 0) { throw new IllegalArgumentException("Stream offsets must be provided as key-value pairs"); } Map result = new HashMap<>(); for (int i = 0; i < streamOffsets.length; i += 2) { byte[] key = (byte[]) streamOffsets[i]; Object value = streamOffsets[i + 1]; StreamEntryID id; if (value instanceof String) { id = new StreamEntryID((String) value); } else if (value instanceof StreamEntryID) { id = (StreamEntryID) value; } else { throw new IllegalArgumentException("Offset must be a String or StreamEntryID"); } result.put(key, id); } return result; } @BeforeEach public void setUpTestStream() { client.del(STREAM_KEY_1); client.del(STREAM_KEY_2); try { client.xgroupCreate(STREAM_KEY_1, GROUP_NAME, StreamEntryID.XGROUP_LAST_ENTRY.toString().getBytes(), true); } catch (JedisDataException e) { if (!e.getMessage().contains("BUSYGROUP")) { throw e; } } try { client.xgroupCreate(STREAM_KEY_2, GROUP_NAME, StreamEntryID.XGROUP_LAST_ENTRY.toString().getBytes(), true); } catch (JedisDataException e) { if (!e.getMessage().contains("BUSYGROUP")) { throw e; } } } @Test public void xreadBinary() { stream1Entries.forEach( entry -> client.xadd(STREAM_KEY_1, new XAddParams().id(entry.getID()), entry.getFields())); Response>>> response = pipe.xreadBinary( XReadParams.xReadParams(), offsets(STREAM_KEY_1, "0-0")); pipe.sync(); List>> actualEntries = response.get(); assertThat(actualEntries, hasSize(1)); assertArrayEquals(STREAM_KEY_1, actualEntries.get(0).getKey()); assertThat(actualEntries.get(0).getValue(), equalsStreamEntries(stream1Entries)); } @Test public void xreadBinaryAsMap() { stream1Entries.forEach( entry -> client.xadd(STREAM_KEY_1, new XAddParams().id(entry.getID()), entry.getFields())); Response>> response = pipe.xreadBinaryAsMap( XReadParams.xReadParams(), offsets(STREAM_KEY_1, "0-0")); pipe.sync(); Map> actualEntries = response.get(); assertThat(actualEntries.entrySet(), hasSize(1)); assertThat(actualEntries.get(STREAM_KEY_1), equalsStreamEntries(stream1Entries)); } @Test public void xreadBinaryAsMapWithMultipleStreams() { // Add entries to the streams stream1Entries.forEach( entry -> client.xadd(STREAM_KEY_1, new XAddParams().id(entry.getID()), entry.getFields())); stream2Entries.forEach( entry -> client.xadd(STREAM_KEY_2, new XAddParams().id(entry.getID()), entry.getFields())); Response>> response = pipe.xreadBinaryAsMap( XReadParams.xReadParams(), offsets(STREAM_KEY_1, "0-0", STREAM_KEY_2, "0-0")); pipe.sync(); Map> actualEntries = response.get(); assertThat(actualEntries.entrySet(), hasSize(2)); assertThat(actualEntries.get(STREAM_KEY_1), equalsStreamEntries(stream1Entries)); assertThat(actualEntries.get(STREAM_KEY_2), equalsStreamEntries(stream2Entries)); } @Test public void xreadGroupBinary() { // Add entries to the streams stream1Entries.forEach( entry -> client.xadd(STREAM_KEY_1, new XAddParams().id(entry.getID()), entry.getFields())); Response>>> response = pipe.xreadGroupBinary( GROUP_NAME, CONSUMER_NAME, XReadGroupParams.xReadGroupParams(), offsets(STREAM_KEY_1, XREADGROUP_UNDELIVERED_ENTRY)); pipe.sync(); List>> actualEntries = response.get(); // verify the result contains entries from one stream // and is under the expected stream key assertThat(actualEntries, hasSize(1)); assertArrayEquals(STREAM_KEY_1, actualEntries.get(0).getKey()); assertThat(actualEntries.get(0).getValue(), equalsStreamEntries(stream1Entries)); } @Test public void xreadGroupBinaryAsMap() { stream1Entries.forEach( entry -> client.xadd(STREAM_KEY_1, new XAddParams().id(entry.getID()), entry.getFields())); Response>> response = pipe.xreadGroupBinaryAsMap( GROUP_NAME, CONSUMER_NAME, XReadGroupParams.xReadGroupParams(), offsets(STREAM_KEY_1, XREADGROUP_UNDELIVERED_ENTRY)); pipe.sync(); Map> actualEntries = response.get(); assertThat(actualEntries.entrySet(), hasSize(1)); assertThat(actualEntries.get(STREAM_KEY_1), equalsStreamEntries(stream1Entries)); } @Test public void xreadGroupBinaryAsMapMultipleStreams() { // Add entries to the streams stream1Entries.forEach( entry -> client.xadd(STREAM_KEY_1, new XAddParams().id(entry.getID()), entry.getFields())); stream2Entries.forEach( entry -> client.xadd(STREAM_KEY_2, new XAddParams().id(entry.getID()), entry.getFields())); Response>> response = pipe.xreadGroupBinaryAsMap(GROUP_NAME, CONSUMER_NAME, XReadGroupParams.xReadGroupParams(), offsets(STREAM_KEY_1, XREADGROUP_UNDELIVERED_ENTRY, STREAM_KEY_2, XREADGROUP_UNDELIVERED_ENTRY)); pipe.sync(); Map> actualEntries = response.get(); assertThat(actualEntries.entrySet(), hasSize(2)); assertThat(actualEntries.get(STREAM_KEY_1), equalsStreamEntries(stream1Entries)); assertThat(actualEntries.get(STREAM_KEY_2), equalsStreamEntries(stream2Entries)); } @Test @EnabledOnCommand("XCFGSET") public void xcfgset() { // Add an entry to create the stream client.xadd(STREAM_KEY_1, new XAddParams().id(StreamEntryID.NEW_ENTRY), HASH_1); // Configure idempotent producer settings via pipeline Response response = pipe.xcfgset(STREAM_KEY_1, XCfgSetParams.xCfgSetParams().idmpDuration(1000).idmpMaxsize(500)); pipe.sync(); assertArrayEquals("OK".getBytes(), response.get()); } } ================================================ FILE: src/test/java/redis/clients/jedis/commands/unified/pipeline/GeoPipelineCommandsTest.java ================================================ package redis.clients.jedis.commands.unified.pipeline; import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.anyOf; import static org.hamcrest.Matchers.closeTo; import static org.hamcrest.Matchers.contains; import static org.hamcrest.Matchers.containsInAnyOrder; import static org.hamcrest.Matchers.equalTo; import static org.hamcrest.Matchers.nullValue; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertThrows; import static redis.clients.jedis.util.GeoCoordinateMatcher.atCoordinates; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.stream.Collectors; import io.redis.test.annotations.ConditionalOnEnv; import org.junit.jupiter.api.Test; import org.junit.jupiter.params.ParameterizedClass; import org.junit.jupiter.params.provider.MethodSource; import redis.clients.jedis.GeoCoordinate; import redis.clients.jedis.RedisProtocol; import redis.clients.jedis.Response; import redis.clients.jedis.args.GeoUnit; import redis.clients.jedis.params.GeoAddParams; import redis.clients.jedis.params.GeoRadiusParam; import redis.clients.jedis.params.GeoRadiusStoreParam; import redis.clients.jedis.params.GeoSearchParam; import redis.clients.jedis.resps.GeoRadiusResponse; import redis.clients.jedis.util.SafeEncoder; import redis.clients.jedis.util.TestEnvUtil; @ParameterizedClass @MethodSource("redis.clients.jedis.commands.CommandsTestsParameters#respVersions") public class GeoPipelineCommandsTest extends PipelineCommandsTestBase { protected final byte[] bfoo = { 0x01, 0x02, 0x03, 0x04 }; protected final byte[] bA = { 0x0A }; protected final byte[] bB = { 0x0B }; protected final byte[] bC = { 0x0C }; protected final byte[] bNotexist = { 0x0F }; private static final double EPSILON = 1e-5; public GeoPipelineCommandsTest(RedisProtocol protocol) { super(protocol); } @Test public void geoadd() { pipe.geoadd("foo", 1, 2, "a"); pipe.geoadd("foo", 2, 3, "a"); Map coordinateMap = new HashMap<>(); coordinateMap.put("a", new GeoCoordinate(3, 4)); coordinateMap.put("b", new GeoCoordinate(2, 3)); coordinateMap.put("c", new GeoCoordinate(3.314, 2.3241)); pipe.geoadd("foo", coordinateMap); // binary pipe.geoadd(bfoo, 1, 2, bA); pipe.geoadd(bfoo, 2, 3, bA); Map bcoordinateMap = new HashMap<>(); bcoordinateMap.put(bA, new GeoCoordinate(3, 4)); bcoordinateMap.put(bB, new GeoCoordinate(2, 3)); bcoordinateMap.put(bC, new GeoCoordinate(3.314, 2.3241)); pipe.geoadd(bfoo, bcoordinateMap); assertThat(pipe.syncAndReturnAll(), contains( 1L, 0L, 2L, 1L, 0L, 2L )); } @Test public void geoaddWithParams() { pipe.geoadd("foo", 1, 2, "a"); Map coordinateMap = new HashMap<>(); coordinateMap.put("a", new GeoCoordinate(3, 4)); pipe.geoadd("foo", GeoAddParams.geoAddParams().nx(), coordinateMap); pipe.geoadd("foo", GeoAddParams.geoAddParams().xx().ch(), coordinateMap); coordinateMap.clear(); coordinateMap.put("b", new GeoCoordinate(6, 7)); // never add elements. pipe.geoadd("foo", GeoAddParams.geoAddParams().xx(), coordinateMap); pipe.geoadd("foo", GeoAddParams.geoAddParams().nx(), coordinateMap); // binary pipe.geoadd(bfoo, 1, 2, bA); Map bcoordinateMap = new HashMap<>(); bcoordinateMap.put(bA, new GeoCoordinate(3, 4)); pipe.geoadd(bfoo, GeoAddParams.geoAddParams().nx(), bcoordinateMap); pipe.geoadd(bfoo, GeoAddParams.geoAddParams().xx().ch(), bcoordinateMap); bcoordinateMap.clear(); bcoordinateMap.put(bB, new GeoCoordinate(6, 7)); // never add elements. pipe.geoadd(bfoo, GeoAddParams.geoAddParams().xx(), bcoordinateMap); pipe.geoadd(bfoo, GeoAddParams.geoAddParams().nx(), bcoordinateMap); assertThat(pipe.syncAndReturnAll(), contains( 1L, 0L, 1L, 0L, 1L, 1L, 0L, 1L, 0L, 1L )); } @Test public void geodist() { prepareGeoData(); Response dist1 = pipe.geodist("foo", "a", "b"); Response dist2 = pipe.geodist("foo", "a", "b", GeoUnit.KM); Response dist3 = pipe.geodist("foo", "a", "b", GeoUnit.MI); Response dist4 = pipe.geodist("foo", "a", "b", GeoUnit.FT); // binary Response dist5 = pipe.geodist(bfoo, bA, bB); Response dist6 = pipe.geodist(bfoo, bA, bB, GeoUnit.KM); Response dist7 = pipe.geodist(bfoo, bA, bB, GeoUnit.MI); Response dist8 = pipe.geodist(bfoo, bA, bB, GeoUnit.FT); pipe.sync(); assertThat(dist1.get(), closeTo(157149.0, 1.0)); assertThat(dist2.get(), closeTo(157.0, 1.0)); assertThat(dist3.get(), closeTo(97.0, 1.0)); assertThat(dist4.get(), closeTo(515583.0, 1.0)); assertThat(dist5.get(), closeTo(157149.0, 1.0)); assertThat(dist6.get(), closeTo(157.0, 1.0)); assertThat(dist7.get(), closeTo(97.0, 1.0)); assertThat(dist8.get(), closeTo(515583.0, 1.0)); } @Test public void geohash() { prepareGeoData(); Response> hashes = pipe.geohash("foo", "a", "b", "notexist"); Response> bhashes = pipe.geohash(bfoo, bA, bB, bNotexist); pipe.sync(); assertThat(hashes.get(), contains( "s0dnu20t9j0", "s093jd0k720", null )); assertThat(bhashes.get(), contains( SafeEncoder.encode("s0dnu20t9j0"), SafeEncoder.encode("s093jd0k720"), null )); } @Test public void geopos() { prepareGeoData(); Response> coordinates = pipe.geopos("foo", "a", "b", "notexist"); Response> bcoordinates = pipe.geopos(bfoo, bA, bB, bNotexist); pipe.sync(); assertThat(coordinates.get(), contains(atCoordinates(3.0, 4.0), atCoordinates(2.0, 3.0), null )); assertThat(bcoordinates.get(), contains(atCoordinates(3.0, 4.0), atCoordinates(2.0, 3.0), null )); } @Test public void georadius() { // prepare data Map coordinateMap = new HashMap<>(); coordinateMap.put("Palermo", new GeoCoordinate(13.361389, 38.115556)); coordinateMap.put("Catania", new GeoCoordinate(15.087269, 37.502669)); client.geoadd("Sicily", coordinateMap); Response> members1 = pipe.georadius("Sicily", 15, 37, 200, GeoUnit.KM); // sort Response> members2 = pipe.georadius("Sicily", 15, 37, 200, GeoUnit.KM, GeoRadiusParam.geoRadiusParam().sortDescending()); // sort, count 1 Response> members3 = pipe.georadius("Sicily", 15, 37, 200, GeoUnit.KM, GeoRadiusParam.geoRadiusParam().sortAscending().count(1)); // sort, count 1, withdist, withcoord Response> members4 = pipe.georadius("Sicily", 15, 37, 200, GeoUnit.KM, GeoRadiusParam.geoRadiusParam().sortAscending().count(1).withCoord().withDist().withHash()); // sort, count 1, with hash Response> members5 = pipe.georadius("Sicily", 15, 37, 200, GeoUnit.KM, GeoRadiusParam.geoRadiusParam().sortAscending().count(1).withHash()); // sort, count 1, any Response> members6 = pipe.georadius("Sicily", 15, 37, 200, GeoUnit.KM, GeoRadiusParam.geoRadiusParam().sortDescending().count(1, true)); pipe.sync(); assertThat(members1.get().stream().map(GeoRadiusResponse::getMemberByString).collect(Collectors.toList()), containsInAnyOrder("Palermo", "Catania")); assertThat(members1.get().stream().map(GeoRadiusResponse::getDistance).collect(Collectors.toList()), contains(closeTo(0.0, EPSILON), closeTo(0.0, EPSILON))); assertThat(members1.get().stream().map(GeoRadiusResponse::getCoordinate).collect(Collectors.toList()), contains(nullValue(), nullValue())); assertThat(members1.get().stream().map(GeoRadiusResponse::getRawScore).collect(Collectors.toList()), contains(0L, 0L)); assertThat(members2.get().stream().map(GeoRadiusResponse::getMemberByString).collect(Collectors.toList()), contains("Palermo", "Catania")); assertThat(members2.get().stream().map(GeoRadiusResponse::getDistance).collect(Collectors.toList()), contains(closeTo(0.0, EPSILON), closeTo(0.0, EPSILON))); assertThat(members2.get().stream().map(GeoRadiusResponse::getCoordinate).collect(Collectors.toList()), contains(nullValue(), nullValue())); assertThat(members2.get().stream().map(GeoRadiusResponse::getRawScore).collect(Collectors.toList()), contains(0L, 0L)); assertThat(members3.get().stream().map(GeoRadiusResponse::getMemberByString).collect(Collectors.toList()), contains("Catania")); assertThat(members3.get().stream().map(GeoRadiusResponse::getDistance).collect(Collectors.toList()), contains(closeTo(0.0, EPSILON))); assertThat(members3.get().stream().map(GeoRadiusResponse::getCoordinate).collect(Collectors.toList()), contains(nullValue())); assertThat(members3.get().stream().map(GeoRadiusResponse::getRawScore).collect(Collectors.toList()), contains(0L)); assertThat(members4.get().stream().map(GeoRadiusResponse::getMemberByString).collect(Collectors.toList()), contains("Catania")); assertThat(members4.get().stream().map(GeoRadiusResponse::getDistance).collect(Collectors.toList()), contains(closeTo(56.4413, EPSILON))); assertThat(members4.get().stream().map(GeoRadiusResponse::getCoordinate).collect(Collectors.toList()), contains(atCoordinates(15.087269, 37.502669))); assertThat(members4.get().stream().map(GeoRadiusResponse::getRawScore).collect(Collectors.toList()), contains(3479447370796909L)); assertThat(members5.get().stream().map(GeoRadiusResponse::getMemberByString).collect(Collectors.toList()), contains("Catania")); assertThat(members5.get().stream().map(GeoRadiusResponse::getDistance).collect(Collectors.toList()), contains(closeTo(0.0, EPSILON))); assertThat(members5.get().stream().map(GeoRadiusResponse::getCoordinate).collect(Collectors.toList()), contains(nullValue())); assertThat(members5.get().stream().map(GeoRadiusResponse::getRawScore).collect(Collectors.toList()), contains(3479447370796909L)); assertThat(members6.get().stream().map(GeoRadiusResponse::getMemberByString).collect(Collectors.toList()), anyOf(contains("Catania"), contains("Palermo"))); assertThat(members6.get().stream().map(GeoRadiusResponse::getDistance).collect(Collectors.toList()), contains(closeTo(0.0, EPSILON))); assertThat(members6.get().stream().map(GeoRadiusResponse::getCoordinate).collect(Collectors.toList()), contains(nullValue())); assertThat(members6.get().stream().map(GeoRadiusResponse::getRawScore).collect(Collectors.toList()), contains(0L)); } @Test @ConditionalOnEnv(value = TestEnvUtil.ENV_REDIS_ENTERPRISE, enabled = false) public void georadiusStore() { // prepare data Map coordinateMap = new HashMap<>(); coordinateMap.put("Palermo", new GeoCoordinate(13.361389, 38.115556)); coordinateMap.put("Catania", new GeoCoordinate(15.087269, 37.502669)); client.geoadd("Sicily", coordinateMap); Response size = pipe.georadiusStore("Sicily", 15, 37, 200, GeoUnit.KM, GeoRadiusParam.geoRadiusParam(), GeoRadiusStoreParam.geoRadiusStoreParam().store("SicilyStore")); Response> items = pipe.zrange("SicilyStore", 0, -1); pipe.sync(); assertThat(size.get(), equalTo(2L)); assertThat(items.get(), contains("Palermo", "Catania")); } @Test public void georadiusReadonly() { // prepare data Map coordinateMap = new HashMap<>(); coordinateMap.put("Palermo", new GeoCoordinate(13.361389, 38.115556)); coordinateMap.put("Catania", new GeoCoordinate(15.087269, 37.502669)); client.geoadd("Sicily", coordinateMap); Response> members1 = pipe.georadiusReadonly("Sicily", 15, 37, 200, GeoUnit.KM); // sort Response> members2 = pipe.georadiusReadonly("Sicily", 15, 37, 200, GeoUnit.KM, GeoRadiusParam.geoRadiusParam().sortAscending()); // sort, count 1 Response> members3 = pipe.georadiusReadonly("Sicily", 15, 37, 200, GeoUnit.KM, GeoRadiusParam.geoRadiusParam().sortAscending().count(1)); // sort, count 1, withdist, withcoord Response> members4 = pipe.georadiusReadonly("Sicily", 15, 37, 200, GeoUnit.KM, GeoRadiusParam.geoRadiusParam().sortAscending().count(1).withCoord().withDist()); pipe.sync(); assertThat(members1.get().stream().map(GeoRadiusResponse::getMemberByString).collect(Collectors.toList()), containsInAnyOrder("Palermo", "Catania")); assertThat(members1.get().stream().map(GeoRadiusResponse::getDistance).collect(Collectors.toList()), contains(closeTo(0.0, EPSILON), closeTo(0.0, EPSILON))); assertThat(members1.get().stream().map(GeoRadiusResponse::getCoordinate).collect(Collectors.toList()), contains(nullValue(), nullValue())); assertThat(members1.get().stream().map(GeoRadiusResponse::getRawScore).collect(Collectors.toList()), contains(0L, 0L)); assertThat(members2.get().stream().map(GeoRadiusResponse::getMemberByString).collect(Collectors.toList()), contains("Catania", "Palermo")); assertThat(members2.get().stream().map(GeoRadiusResponse::getDistance).collect(Collectors.toList()), contains(closeTo(0.0, EPSILON), closeTo(0.0, EPSILON))); assertThat(members2.get().stream().map(GeoRadiusResponse::getCoordinate).collect(Collectors.toList()), contains(nullValue(), nullValue())); assertThat(members2.get().stream().map(GeoRadiusResponse::getRawScore).collect(Collectors.toList()), contains(0L, 0L)); assertThat(members3.get().stream().map(GeoRadiusResponse::getMemberByString).collect(Collectors.toList()), contains("Catania")); assertThat(members3.get().stream().map(GeoRadiusResponse::getDistance).collect(Collectors.toList()), contains(closeTo(0.0, EPSILON))); assertThat(members3.get().stream().map(GeoRadiusResponse::getCoordinate).collect(Collectors.toList()), contains(nullValue())); assertThat(members3.get().stream().map(GeoRadiusResponse::getRawScore).collect(Collectors.toList()), contains(0L)); assertThat(members4.get().stream().map(GeoRadiusResponse::getMemberByString).collect(Collectors.toList()), contains("Catania")); assertThat(members4.get().stream().map(GeoRadiusResponse::getDistance).collect(Collectors.toList()), contains(closeTo(56.4413, EPSILON))); assertThat(members4.get().stream().map(GeoRadiusResponse::getCoordinate).collect(Collectors.toList()), contains(atCoordinates(15.087269, 37.502669))); assertThat(members4.get().stream().map(GeoRadiusResponse::getRawScore).collect(Collectors.toList()), contains(0L)); } @Test public void georadiusBinary() { // prepare data Map bcoordinateMap = new HashMap<>(); bcoordinateMap.put(bA, new GeoCoordinate(13.361389, 38.115556)); bcoordinateMap.put(bB, new GeoCoordinate(15.087269, 37.502669)); client.geoadd(bfoo, bcoordinateMap); Response> members1 = pipe.georadius(bfoo, 15, 37, 200, GeoUnit.KM); // sort Response> members2 = pipe.georadius(bfoo, 15, 37, 200, GeoUnit.KM, GeoRadiusParam.geoRadiusParam().sortAscending()); // sort, count 1 Response> members3 = pipe.georadius(bfoo, 15, 37, 200, GeoUnit.KM, GeoRadiusParam.geoRadiusParam().sortAscending().count(1)); // sort, count 1, withdist, withcoord Response> members4 = pipe.georadius(bfoo, 15, 37, 200, GeoUnit.KM, GeoRadiusParam.geoRadiusParam().sortAscending().count(1).withCoord().withDist()); pipe.sync(); assertThat(members1.get().stream().map(GeoRadiusResponse::getMember).collect(Collectors.toList()), containsInAnyOrder(bA, bB)); assertThat(members1.get().stream().map(GeoRadiusResponse::getDistance).collect(Collectors.toList()), contains(closeTo(0.0, EPSILON), closeTo(0.0, EPSILON))); assertThat(members1.get().stream().map(GeoRadiusResponse::getCoordinate).collect(Collectors.toList()), contains(nullValue(), nullValue())); assertThat(members1.get().stream().map(GeoRadiusResponse::getRawScore).collect(Collectors.toList()), contains(0L, 0L)); assertThat(members2.get().stream().map(GeoRadiusResponse::getMember).collect(Collectors.toList()), contains(bB, bA)); assertThat(members2.get().stream().map(GeoRadiusResponse::getDistance).collect(Collectors.toList()), contains(closeTo(0.0, EPSILON), closeTo(0.0, EPSILON))); assertThat(members2.get().stream().map(GeoRadiusResponse::getCoordinate).collect(Collectors.toList()), contains(nullValue(), nullValue())); assertThat(members2.get().stream().map(GeoRadiusResponse::getRawScore).collect(Collectors.toList()), contains(0L, 0L)); assertThat(members3.get().stream().map(GeoRadiusResponse::getMember).collect(Collectors.toList()), contains(bB)); assertThat(members3.get().stream().map(GeoRadiusResponse::getDistance).collect(Collectors.toList()), contains(closeTo(0.0, EPSILON))); assertThat(members3.get().stream().map(GeoRadiusResponse::getCoordinate).collect(Collectors.toList()), contains(nullValue())); assertThat(members3.get().stream().map(GeoRadiusResponse::getRawScore).collect(Collectors.toList()), contains(0L)); assertThat(members4.get().stream().map(GeoRadiusResponse::getMember).collect(Collectors.toList()), contains(bB)); assertThat(members4.get().stream().map(GeoRadiusResponse::getDistance).collect(Collectors.toList()), contains(closeTo(56.4413, EPSILON))); assertThat(members4.get().stream().map(GeoRadiusResponse::getCoordinate).collect(Collectors.toList()), contains(atCoordinates(15.087269, 37.502669))); assertThat(members4.get().stream().map(GeoRadiusResponse::getRawScore).collect(Collectors.toList()), contains(0L)); } @Test @ConditionalOnEnv(value = TestEnvUtil.ENV_REDIS_ENTERPRISE, enabled = false) public void georadiusStoreBinary() { // prepare data Map bcoordinateMap = new HashMap<>(); bcoordinateMap.put(bA, new GeoCoordinate(13.361389, 38.115556)); bcoordinateMap.put(bB, new GeoCoordinate(15.087269, 37.502669)); client.geoadd(bfoo, bcoordinateMap); Response size = pipe.georadiusStore(bfoo, 15, 37, 200, GeoUnit.KM, GeoRadiusParam.geoRadiusParam(), GeoRadiusStoreParam.geoRadiusStoreParam().store("SicilyStore")); Response> items = pipe.zrange("SicilyStore".getBytes(), 0, -1); pipe.sync(); assertThat(size.get(), equalTo(2L)); assertThat(items.get(), contains(bA, bB)); } @Test public void georadiusReadonlyBinary() { // prepare data Map bcoordinateMap = new HashMap<>(); bcoordinateMap.put(bA, new GeoCoordinate(13.361389, 38.115556)); bcoordinateMap.put(bB, new GeoCoordinate(15.087269, 37.502669)); client.geoadd(bfoo, bcoordinateMap); Response> members1 = pipe.georadiusReadonly(bfoo, 15, 37, 200, GeoUnit.KM); // sort Response> members2 = pipe.georadiusReadonly(bfoo, 15, 37, 200, GeoUnit.KM, GeoRadiusParam.geoRadiusParam().sortAscending()); // sort, count 1 Response> members3 = pipe.georadiusReadonly(bfoo, 15, 37, 200, GeoUnit.KM, GeoRadiusParam.geoRadiusParam().sortAscending().count(1)); // sort, count 1, withdist, withcoord Response> members4 = pipe.georadiusReadonly(bfoo, 15, 37, 200, GeoUnit.KM, GeoRadiusParam.geoRadiusParam().sortAscending().count(1).withCoord().withDist()); pipe.sync(); assertThat(members1.get().stream().map(GeoRadiusResponse::getMember).collect(Collectors.toList()), containsInAnyOrder(bA, bB)); assertThat(members1.get().stream().map(GeoRadiusResponse::getDistance).collect(Collectors.toList()), contains(closeTo(0.0, EPSILON), closeTo(0.0, EPSILON))); assertThat(members1.get().stream().map(GeoRadiusResponse::getCoordinate).collect(Collectors.toList()), contains(nullValue(), nullValue())); assertThat(members1.get().stream().map(GeoRadiusResponse::getRawScore).collect(Collectors.toList()), contains(0L, 0L)); assertThat(members2.get().stream().map(GeoRadiusResponse::getMember).collect(Collectors.toList()), contains(bB, bA)); assertThat(members2.get().stream().map(GeoRadiusResponse::getDistance).collect(Collectors.toList()), contains(closeTo(0.0, EPSILON), closeTo(0.0, EPSILON))); assertThat(members2.get().stream().map(GeoRadiusResponse::getCoordinate).collect(Collectors.toList()), contains(nullValue(), nullValue())); assertThat(members2.get().stream().map(GeoRadiusResponse::getRawScore).collect(Collectors.toList()), contains(0L, 0L)); assertThat(members3.get().stream().map(GeoRadiusResponse::getMember).collect(Collectors.toList()), contains(bB)); assertThat(members3.get().stream().map(GeoRadiusResponse::getDistance).collect(Collectors.toList()), contains(closeTo(0.0, EPSILON))); assertThat(members3.get().stream().map(GeoRadiusResponse::getCoordinate).collect(Collectors.toList()), contains(nullValue())); assertThat(members3.get().stream().map(GeoRadiusResponse::getRawScore).collect(Collectors.toList()), contains(0L)); assertThat(members4.get().stream().map(GeoRadiusResponse::getMember).collect(Collectors.toList()), contains(bB)); assertThat(members4.get().stream().map(GeoRadiusResponse::getDistance).collect(Collectors.toList()), contains(closeTo(56.4413, EPSILON))); assertThat(members4.get().stream().map(GeoRadiusResponse::getCoordinate).collect(Collectors.toList()), contains(atCoordinates(15.087269, 37.502669))); assertThat(members4.get().stream().map(GeoRadiusResponse::getRawScore).collect(Collectors.toList()), contains(0L)); } @Test public void georadiusByMember() { client.geoadd("Sicily", 13.583333, 37.316667, "Agrigento"); client.geoadd("Sicily", 13.361389, 38.115556, "Palermo"); client.geoadd("Sicily", 15.087269, 37.502669, "Catania"); Response> members1 = pipe.georadiusByMember("Sicily", "Agrigento", 100, GeoUnit.KM); Response> members2 = pipe.georadiusByMember("Sicily", "Agrigento", 100, GeoUnit.KM, GeoRadiusParam.geoRadiusParam().sortAscending()); Response> members3 = pipe.georadiusByMember("Sicily", "Agrigento", 100, GeoUnit.KM, GeoRadiusParam.geoRadiusParam().sortAscending().count(1).withCoord().withDist()); pipe.sync(); assertThat(members1.get().stream().map(GeoRadiusResponse::getMemberByString).collect(Collectors.toList()), containsInAnyOrder("Agrigento", "Palermo")); assertThat(members1.get().stream().map(GeoRadiusResponse::getDistance).collect(Collectors.toList()), contains(closeTo(0.0, EPSILON), closeTo(0.0, EPSILON))); assertThat(members1.get().stream().map(GeoRadiusResponse::getCoordinate).collect(Collectors.toList()), contains(nullValue(), nullValue())); assertThat(members1.get().stream().map(GeoRadiusResponse::getRawScore).collect(Collectors.toList()), contains(0L, 0L)); assertThat(members2.get().stream().map(GeoRadiusResponse::getMemberByString).collect(Collectors.toList()), contains("Agrigento", "Palermo")); assertThat(members2.get().stream().map(GeoRadiusResponse::getDistance).collect(Collectors.toList()), contains(closeTo(0.0, EPSILON), closeTo(0.0, EPSILON))); assertThat(members2.get().stream().map(GeoRadiusResponse::getCoordinate).collect(Collectors.toList()), contains(nullValue(), nullValue())); assertThat(members2.get().stream().map(GeoRadiusResponse::getRawScore).collect(Collectors.toList()), contains(0L, 0L)); assertThat(members3.get().stream().map(GeoRadiusResponse::getMemberByString).collect(Collectors.toList()), contains("Agrigento")); assertThat(members3.get().stream().map(GeoRadiusResponse::getDistance).collect(Collectors.toList()), contains(closeTo(0.0, EPSILON))); assertThat(members3.get().stream().map(GeoRadiusResponse::getCoordinate).collect(Collectors.toList()), contains(atCoordinates(13.583333, 37.316667))); assertThat(members3.get().stream().map(GeoRadiusResponse::getRawScore).collect(Collectors.toList()), contains(0L)); } @Test @ConditionalOnEnv(value = TestEnvUtil.ENV_REDIS_ENTERPRISE, enabled = false) public void georadiusByMemberStore() { client.geoadd("Sicily", 13.583333, 37.316667, "Agrigento"); client.geoadd("Sicily", 13.361389, 38.115556, "Palermo"); client.geoadd("Sicily", 15.087269, 37.502669, "Catania"); Response size = pipe.georadiusByMemberStore("Sicily", "Agrigento", 100, GeoUnit.KM, GeoRadiusParam.geoRadiusParam(), GeoRadiusStoreParam.geoRadiusStoreParam().store("SicilyStore")); Response> items = pipe.zrange("SicilyStore", 0, -1); pipe.sync(); assertThat(size.get(), equalTo(2L)); assertThat(items.get(), contains("Agrigento", "Palermo")); } @Test public void georadiusByMemberReadonly() { client.geoadd("Sicily", 13.583333, 37.316667, "Agrigento"); client.geoadd("Sicily", 13.361389, 38.115556, "Palermo"); client.geoadd("Sicily", 15.087269, 37.502669, "Catania"); Response> members1 = pipe.georadiusByMemberReadonly("Sicily", "Agrigento", 100, GeoUnit.KM); Response> members2 = pipe.georadiusByMemberReadonly("Sicily", "Agrigento", 100, GeoUnit.KM, GeoRadiusParam.geoRadiusParam().sortAscending()); Response> members3 = pipe.georadiusByMemberReadonly("Sicily", "Agrigento", 100, GeoUnit.KM, GeoRadiusParam.geoRadiusParam().sortAscending().count(1).withCoord().withDist()); pipe.sync(); assertThat(members1.get().stream().map(GeoRadiusResponse::getMemberByString).collect(Collectors.toList()), containsInAnyOrder("Agrigento", "Palermo")); assertThat(members1.get().stream().map(GeoRadiusResponse::getDistance).collect(Collectors.toList()), contains(closeTo(0.0, EPSILON), closeTo(0.0, EPSILON))); assertThat(members1.get().stream().map(GeoRadiusResponse::getCoordinate).collect(Collectors.toList()), contains(nullValue(), nullValue())); assertThat(members1.get().stream().map(GeoRadiusResponse::getRawScore).collect(Collectors.toList()), contains(0L, 0L)); assertThat(members2.get().stream().map(GeoRadiusResponse::getMemberByString).collect(Collectors.toList()), contains("Agrigento", "Palermo")); assertThat(members2.get().stream().map(GeoRadiusResponse::getDistance).collect(Collectors.toList()), contains(closeTo(0.0, EPSILON), closeTo(0.0, EPSILON))); assertThat(members2.get().stream().map(GeoRadiusResponse::getCoordinate).collect(Collectors.toList()), contains(nullValue(), nullValue())); assertThat(members2.get().stream().map(GeoRadiusResponse::getRawScore).collect(Collectors.toList()), contains(0L, 0L)); assertThat(members3.get().stream().map(GeoRadiusResponse::getMemberByString).collect(Collectors.toList()), contains("Agrigento")); assertThat(members3.get().stream().map(GeoRadiusResponse::getDistance).collect(Collectors.toList()), contains(closeTo(0.0, EPSILON))); assertThat(members3.get().stream().map(GeoRadiusResponse::getCoordinate).collect(Collectors.toList()), contains(atCoordinates(13.583333, 37.316667))); assertThat(members3.get().stream().map(GeoRadiusResponse::getRawScore).collect(Collectors.toList()), contains(0L)); } @Test public void georadiusByMemberBinary() { client.geoadd(bfoo, 13.583333, 37.316667, bA); client.geoadd(bfoo, 13.361389, 38.115556, bB); client.geoadd(bfoo, 15.087269, 37.502669, bC); Response> members1 = pipe.georadiusByMember(bfoo, bA, 100, GeoUnit.KM); Response> members2 = pipe.georadiusByMember(bfoo, bA, 100, GeoUnit.KM, GeoRadiusParam.geoRadiusParam().sortAscending()); Response> members3 = pipe.georadiusByMember(bfoo, bA, 100, GeoUnit.KM, GeoRadiusParam.geoRadiusParam().sortAscending().count(1).withCoord().withDist()); pipe.sync(); assertThat(members1.get().stream().map(GeoRadiusResponse::getMember).collect(Collectors.toList()), containsInAnyOrder(bA, bB)); assertThat(members1.get().stream().map(GeoRadiusResponse::getDistance).collect(Collectors.toList()), contains(closeTo(0.0, EPSILON), closeTo(0.0, EPSILON))); assertThat(members1.get().stream().map(GeoRadiusResponse::getCoordinate).collect(Collectors.toList()), contains(nullValue(), nullValue())); assertThat(members1.get().stream().map(GeoRadiusResponse::getRawScore).collect(Collectors.toList()), contains(0L, 0L)); assertThat(members2.get().stream().map(GeoRadiusResponse::getMember).collect(Collectors.toList()), contains(bA, bB)); assertThat(members2.get().stream().map(GeoRadiusResponse::getDistance).collect(Collectors.toList()), contains(closeTo(0.0, EPSILON), closeTo(0.0, EPSILON))); assertThat(members2.get().stream().map(GeoRadiusResponse::getCoordinate).collect(Collectors.toList()), contains(nullValue(), nullValue())); assertThat(members2.get().stream().map(GeoRadiusResponse::getRawScore).collect(Collectors.toList()), contains(0L, 0L)); assertThat(members3.get().stream().map(GeoRadiusResponse::getMember).collect(Collectors.toList()), contains(bA)); assertThat(members3.get().stream().map(GeoRadiusResponse::getDistance).collect(Collectors.toList()), contains(closeTo(0.0, EPSILON))); assertThat(members3.get().stream().map(GeoRadiusResponse::getCoordinate).collect(Collectors.toList()), contains(atCoordinates(13.583333, 37.316667))); assertThat(members3.get().stream().map(GeoRadiusResponse::getRawScore).collect(Collectors.toList()), contains(0L)); } @Test @ConditionalOnEnv(value = TestEnvUtil.ENV_REDIS_ENTERPRISE, enabled = false) public void georadiusByMemberStoreBinary() { client.geoadd(bfoo, 13.583333, 37.316667, bA); client.geoadd(bfoo, 13.361389, 38.115556, bB); client.geoadd(bfoo, 15.087269, 37.502669, bC); Response size = pipe.georadiusByMemberStore(bfoo, bA, 100, GeoUnit.KM, GeoRadiusParam.geoRadiusParam(), GeoRadiusStoreParam.geoRadiusStoreParam().store("SicilyStore")); Response> items = pipe.zrange("SicilyStore".getBytes(), 0, -1); pipe.sync(); assertThat(size.get(), equalTo(2L)); assertThat(items.get(), contains(bA, bB)); } @Test public void georadiusByMemberReadonlyBinary() { client.geoadd(bfoo, 13.583333, 37.316667, bA); client.geoadd(bfoo, 13.361389, 38.115556, bB); client.geoadd(bfoo, 15.087269, 37.502669, bC); Response> members1 = pipe.georadiusByMemberReadonly(bfoo, bA, 100, GeoUnit.KM); Response> members2 = pipe.georadiusByMemberReadonly(bfoo, bA, 100, GeoUnit.KM, GeoRadiusParam.geoRadiusParam().sortAscending()); Response> members3 = pipe.georadiusByMemberReadonly(bfoo, bA, 100, GeoUnit.KM, GeoRadiusParam.geoRadiusParam().sortAscending().count(1).withCoord().withDist()); pipe.sync(); assertThat(members1.get().stream().map(GeoRadiusResponse::getMember).collect(Collectors.toList()), containsInAnyOrder(bA, bB)); assertThat(members1.get().stream().map(GeoRadiusResponse::getDistance).collect(Collectors.toList()), contains(closeTo(0.0, EPSILON), closeTo(0.0, EPSILON))); assertThat(members1.get().stream().map(GeoRadiusResponse::getCoordinate).collect(Collectors.toList()), contains(nullValue(), nullValue())); assertThat(members1.get().stream().map(GeoRadiusResponse::getRawScore).collect(Collectors.toList()), contains(0L, 0L)); assertThat(members2.get().stream().map(GeoRadiusResponse::getMember).collect(Collectors.toList()), contains(bA, bB)); assertThat(members2.get().stream().map(GeoRadiusResponse::getDistance).collect(Collectors.toList()), contains(closeTo(0.0, EPSILON), closeTo(0.0, EPSILON))); assertThat(members2.get().stream().map(GeoRadiusResponse::getCoordinate).collect(Collectors.toList()), contains(nullValue(), nullValue())); assertThat(members2.get().stream().map(GeoRadiusResponse::getRawScore).collect(Collectors.toList()), contains(0L, 0L)); assertThat(members3.get().stream().map(GeoRadiusResponse::getMember).collect(Collectors.toList()), contains(bA)); assertThat(members3.get().stream().map(GeoRadiusResponse::getDistance).collect(Collectors.toList()), contains(closeTo(0.0, EPSILON))); assertThat(members3.get().stream().map(GeoRadiusResponse::getCoordinate).collect(Collectors.toList()), contains(atCoordinates(13.583333, 37.316667))); assertThat(members3.get().stream().map(GeoRadiusResponse::getRawScore).collect(Collectors.toList()), contains(0L)); } @Test public void geosearch() { client.geoadd("barcelona", 2.1909389952632d, 41.433791470673d, "place1"); client.geoadd("barcelona", 2.1873744593677d, 41.406342043777d, "place2"); client.geoadd("barcelona", 2.583333d, 41.316667d, "place3"); // FROMLONLAT and BYRADIUS Response> members1 = pipe.geosearch("barcelona", new GeoCoordinate(2.191d, 41.433d), 1000, GeoUnit.M); // using Params Response> members2 = pipe.geosearch("barcelona", new GeoSearchParam().byRadius(3000, GeoUnit.M) .fromLonLat(2.191d, 41.433d).desc()); // FROMMEMBER and BYRADIUS Response> members3 = pipe.geosearch("barcelona", "place3", 100, GeoUnit.KM); // using Params Response> members4 = pipe.geosearch("barcelona", new GeoSearchParam().fromMember("place1") .byRadius(100, GeoUnit.KM).withDist().withCoord().withHash().count(2)); // FROMMEMBER and BYBOX Response> members5 = pipe.geosearch("barcelona", "place3", 100, 100, GeoUnit.KM); // using Params Response> members6 = pipe.geosearch("barcelona", new GeoSearchParam().fromMember("place3") .byBox(100, 100, GeoUnit.KM).asc().count(1, true)); // FROMLONLAT and BYBOX Response> members7 = pipe.geosearch("barcelona", new GeoCoordinate(2.191, 41.433), 1, 1, GeoUnit.KM); // using Params Response> members8 = pipe.geosearch("barcelona", new GeoSearchParam().byBox(1, 1, GeoUnit.KM) .fromLonLat(2.191, 41.433).withDist().withCoord()); pipe.sync(); assertThat(members1.get().stream().map(GeoRadiusResponse::getMemberByString).collect(Collectors.toList()), contains("place1")); assertThat(members1.get().stream().map(GeoRadiusResponse::getDistance).collect(Collectors.toList()), contains(closeTo(0.0, EPSILON))); assertThat(members1.get().stream().map(GeoRadiusResponse::getCoordinate).collect(Collectors.toList()), contains(nullValue())); assertThat(members1.get().stream().map(GeoRadiusResponse::getRawScore).collect(Collectors.toList()), contains(0L)); assertThat(members2.get().stream().map(GeoRadiusResponse::getMemberByString).collect(Collectors.toList()), contains("place2", "place1")); assertThat(members3.get().stream().map(GeoRadiusResponse::getMemberByString).collect(Collectors.toList()), contains("place2", "place1", "place3")); assertThat(members4.get().stream().map(GeoRadiusResponse::getMemberByString).collect(Collectors.toList()), contains("place1", "place2")); assertThat(members4.get().stream().map(GeoRadiusResponse::getDistance).collect(Collectors.toList()), contains(closeTo(0.0, EPSILON), closeTo(3.0674, EPSILON))); assertThat(members4.get().stream().map(GeoRadiusResponse::getCoordinate).collect(Collectors.toList()), contains(atCoordinates(2.1909389952632d, 41.433791470673d), atCoordinates(2.1873744593677d, 41.406342043777d))); assertThat(members4.get().stream().map(GeoRadiusResponse::getRawScore).collect(Collectors.toList()), contains(3471609698139488L, 3471609625421029L)); assertThat(members5.get().stream().map(GeoRadiusResponse::getMemberByString).collect(Collectors.toList()), contains("place2", "place1", "place3")); assertThat(members6.get().stream().map(GeoRadiusResponse::getMemberByString).collect(Collectors.toList()), contains("place2")); assertThat(members7.get().stream().map(GeoRadiusResponse::getMemberByString).collect(Collectors.toList()), contains("place1")); assertThat(members8.get().stream().map(GeoRadiusResponse::getMemberByString).collect(Collectors.toList()), contains("place1")); assertThat(members8.get().stream().map(GeoRadiusResponse::getDistance).collect(Collectors.toList()), contains(closeTo(0.0881, EPSILON))); assertThat(members8.get().stream().map(GeoRadiusResponse::getCoordinate).collect(Collectors.toList()), contains(atCoordinates(2.1909389952632d, 41.433791470673d))); assertThat(members8.get().stream().map(GeoRadiusResponse::getRawScore).collect(Collectors.toList()), contains(0L)); } @Test public void geosearchSearchParamCombineFromMemberAndFromLonLat() { assertThrows(IllegalArgumentException.class, () -> pipe.geosearch("barcelona", new GeoSearchParam().fromMember("foobar").fromLonLat(10, 10))); } @Test public void geosearchSearchParamWithoutFromMemberAndFromLonLat() { assertThrows(IllegalArgumentException.class, () -> pipe.geosearch("barcelona", new GeoSearchParam().byRadius(10, GeoUnit.MI))); } @Test public void geosearchSearchParamCombineByRadiousAndByBox() { assertThrows(IllegalArgumentException.class, () -> pipe.geosearch("barcelona", new GeoSearchParam().byRadius(3000, GeoUnit.M).byBox(300, 300, GeoUnit.M))); } @Test public void geosearchSearchParamWithoutByRadiousAndByBox() { assertThrows(IllegalArgumentException.class, () -> pipe.geosearch("barcelona", new GeoSearchParam().fromMember("foobar"))); } @Test @ConditionalOnEnv(value = TestEnvUtil.ENV_REDIS_ENTERPRISE, enabled = false) public void geosearchstore() { client.geoadd("barcelona", 2.1909389952632d, 41.433791470673d, "place1"); client.geoadd("barcelona", 2.1873744593677d, 41.406342043777d, "place2"); client.geoadd("barcelona", 2.583333d, 41.316667d, "place3"); // FROMLONLAT and BYRADIUS Response membersCount1 = pipe.geosearchStore("tel-aviv", "barcelona", new GeoCoordinate(2.191d, 41.433d), 1000, GeoUnit.M); Response> members1 = pipe.zrange("tel-aviv", 0, -1); Response membersCount2 = pipe.geosearchStore("tel-aviv", "barcelona", new GeoSearchParam() .byRadius(3000, GeoUnit.M) .fromLonLat(new GeoCoordinate(2.191d, 41.433d))); // FROMMEMBER and BYRADIUS Response membersCount3 = pipe.geosearchStore("tel-aviv", "barcelona", "place3", 100, GeoUnit.KM); // FROMMEMBER and BYBOX Response membersCount4 = pipe.geosearchStore("tel-aviv", "barcelona", "place3", 100, 100, GeoUnit.KM); // FROMLONLAT and BYBOX Response membersCount5 = pipe.geosearchStore("tel-aviv", "barcelona", new GeoCoordinate(2.191, 41.433), 1, 1, GeoUnit.KM); pipe.sync(); assertThat(membersCount1.get(), equalTo(1L)); assertThat(members1.get(), contains("place1")); assertThat(membersCount2.get(), equalTo(2L)); assertThat(membersCount3.get(), equalTo(3L)); assertThat(membersCount4.get(), equalTo(3L)); assertThat(membersCount5.get(), equalTo(1L)); } @Test @ConditionalOnEnv(value = TestEnvUtil.ENV_REDIS_ENTERPRISE, enabled = false) public void geosearchstoreWithdist() { client.geoadd("barcelona", 2.1909389952632d, 41.433791470673d, "place1"); client.geoadd("barcelona", 2.1873744593677d, 41.406342043777d, "place2"); Response members = pipe.geosearchStoreStoreDist("tel-aviv", "barcelona", new GeoSearchParam().byRadius(3000, GeoUnit.M).fromLonLat(2.191d, 41.433d)); Response score = pipe.zscore("tel-aviv", "place1"); pipe.sync(); assertThat(members.get(), equalTo(2L)); assertThat(score.get(), closeTo(88.05060698409301, 5)); } private void prepareGeoData() { Map coordinateMap = new HashMap<>(); coordinateMap.put("a", new GeoCoordinate(3, 4)); coordinateMap.put("b", new GeoCoordinate(2, 3)); coordinateMap.put("c", new GeoCoordinate(3.314, 2.3241)); assertEquals(3, client.geoadd("foo", coordinateMap)); Map bcoordinateMap = new HashMap<>(); bcoordinateMap.put(bA, new GeoCoordinate(3, 4)); bcoordinateMap.put(bB, new GeoCoordinate(2, 3)); bcoordinateMap.put(bC, new GeoCoordinate(3.314, 2.3241)); assertEquals(3, client.geoadd(bfoo, bcoordinateMap)); } } ================================================ FILE: src/test/java/redis/clients/jedis/commands/unified/pipeline/HashesPipelineCommandsTest.java ================================================ package redis.clients.jedis.commands.unified.pipeline; import static redis.clients.jedis.util.AssertUtil.assertPipelineSyncAll; import java.util.*; import org.junit.jupiter.api.Test; import org.junit.jupiter.params.ParameterizedClass; import org.junit.jupiter.params.provider.MethodSource; import redis.clients.jedis.RedisProtocol; @ParameterizedClass @MethodSource("redis.clients.jedis.commands.CommandsTestsParameters#respVersions") public class HashesPipelineCommandsTest extends PipelineCommandsTestBase { final byte[] bfoo = { 0x01, 0x02, 0x03, 0x04 }; final byte[] bbar = { 0x05, 0x06, 0x07, 0x08 }; final byte[] bcar = { 0x09, 0x0A, 0x0B, 0x0C }; final byte[] bbar1 = { 0x05, 0x06, 0x07, 0x08, 0x0A }; final byte[] bbar2 = { 0x05, 0x06, 0x07, 0x08, 0x0B }; final byte[] bbar3 = { 0x05, 0x06, 0x07, 0x08, 0x0C }; final byte[] bbarstar = { 0x05, 0x06, 0x07, 0x08, '*' }; public HashesPipelineCommandsTest(RedisProtocol protocol) { super(protocol); } @Test public void hset() { pipe.hset("foo", "bar", "car"); pipe.hset("foo", "bar", "foo"); // Binary pipe.hset(bfoo, bbar, bcar); pipe.hset(bfoo, bbar, bfoo); assertPipelineSyncAll( Arrays.asList(1L, 0L, 1L, 0L), pipe.syncAndReturnAll()); } @Test public void hget() { pipe.hset("foo", "bar", "car"); pipe.hget("bar", "foo"); pipe.hget("foo", "car"); pipe.hget("foo", "bar"); // Binary pipe.hset(bfoo, bbar, bcar); pipe.hget(bbar, bfoo); pipe.hget(bfoo, bcar); pipe.hget(bfoo, bbar); assertPipelineSyncAll( Arrays.asList( 1L, null, null, "car", 1L, null, null, bcar), pipe.syncAndReturnAll()); } @Test public void hsetnx() { pipe.hsetnx("foo", "bar", "car"); pipe.hget("foo", "bar"); pipe.hsetnx("foo", "bar", "foo"); pipe.hget("foo", "bar"); pipe.hsetnx("foo", "car", "bar"); pipe.hget("foo", "car"); // Binary pipe.hsetnx(bfoo, bbar, bcar); pipe.hget(bfoo, bbar); pipe.hsetnx(bfoo, bbar, bfoo); pipe.hget(bfoo, bbar); pipe.hsetnx(bfoo, bcar, bbar); pipe.hget(bfoo, bcar); assertPipelineSyncAll( Arrays.asList( 1L, "car", 0L, "car", 1L, "bar", 1L, bcar, 0L, bcar, 1L, bbar), pipe.syncAndReturnAll()); } @Test public void hmset() { Map hash = new HashMap<>(); hash.put("bar", "car"); hash.put("car", "bar"); pipe.hmset("foo", hash); pipe.hget("foo", "bar"); pipe.hget("foo", "car"); // Binary Map bhash = new HashMap<>(); bhash.put(bbar, bcar); bhash.put(bcar, bbar); pipe.hmset(bfoo, bhash); pipe.hget(bfoo, bbar); pipe.hget(bfoo, bcar); assertPipelineSyncAll( Arrays.asList("OK", "car", "bar", "OK", bcar, bbar), pipe.syncAndReturnAll()); } @Test public void hsetVariadic() { Map hash = new HashMap<>(); hash.put("bar", "car"); hash.put("car", "bar"); pipe.hset("foo", hash); pipe.hget("foo", "bar"); pipe.hget("foo", "car"); // Binary Map bhash = new HashMap<>(); bhash.put(bbar, bcar); bhash.put(bcar, bbar); pipe.hset(bfoo, bhash); pipe.hget(bfoo, bbar); pipe.hget(bfoo, bcar); assertPipelineSyncAll( Arrays.asList(2L, "car", "bar", 2L, bcar, bbar), pipe.syncAndReturnAll()); } @Test public void hmget() { Map hash = new HashMap<>(); hash.put("bar", "car"); hash.put("car", "bar"); pipe.hmset("foo", hash); pipe.hmget("foo", "bar", "car", "foo"); List expected = new ArrayList<>(); expected.add("car"); expected.add("bar"); expected.add(null); // Binary Map bhash = new HashMap<>(); bhash.put(bbar, bcar); bhash.put(bcar, bbar); pipe.hmset(bfoo, bhash); pipe.hmget(bfoo, bbar, bcar, bfoo); List bexpected = new ArrayList<>(); bexpected.add(bcar); bexpected.add(bbar); bexpected.add(null); assertPipelineSyncAll( Arrays.asList( "OK", Arrays.asList("car", "bar", null), "OK", Arrays.asList(bcar, bbar, null)), pipe.syncAndReturnAll()); } @Test public void hincrBy() { pipe.hincrBy("foo", "bar", 1); pipe.hincrBy("foo", "bar", -1); pipe.hincrBy("foo", "bar", -10); // Binary pipe.hincrBy(bfoo, bbar, 1); pipe.hincrBy(bfoo, bbar, -1); pipe.hincrBy(bfoo, bbar, -10); assertPipelineSyncAll( Arrays.asList(1L, 0L, -10L, 1L, 0L, -10L), pipe.syncAndReturnAll()); } @Test public void hincrByFloat() { pipe.hincrByFloat("foo", "bar", 1.5d); pipe.hincrByFloat("foo", "bar", -1.5d); pipe.hincrByFloat("foo", "bar", -10.7d); // Binary pipe.hincrByFloat(bfoo, bbar, 1.5d); pipe.hincrByFloat(bfoo, bbar, -1.5d); pipe.hincrByFloat(bfoo, bbar, -10.7d); assertPipelineSyncAll( Arrays.asList(1.5, 0d, -10.7, 1.5, 0d, -10.7), pipe.syncAndReturnAll()); } @Test public void hexists() { Map hash = new HashMap<>(); hash.put("bar", "car"); hash.put("car", "bar"); pipe.hset("foo", hash); pipe.hexists("bar", "foo"); pipe.hexists("foo", "foo"); pipe.hexists("foo", "bar"); // Binary Map bhash = new HashMap<>(); bhash.put(bbar, bcar); bhash.put(bcar, bbar); pipe.hset(bfoo, bhash); pipe.hexists(bbar, bfoo); pipe.hexists(bfoo, bfoo); pipe.hexists(bfoo, bbar); assertPipelineSyncAll( Arrays.asList( 2L, false, false, true, 2L, false, false, true), pipe.syncAndReturnAll()); } @Test public void hdel() { Map hash = new HashMap<>(); hash.put("bar", "car"); hash.put("car", "bar"); pipe.hset("foo", hash); pipe.hdel("bar", "foo"); pipe.hdel("foo", "foo"); pipe.hdel("foo", "bar"); pipe.hget("foo", "bar"); // Binary Map bhash = new HashMap<>(); bhash.put(bbar, bcar); bhash.put(bcar, bbar); pipe.hset(bfoo, bhash); pipe.hdel(bbar, bfoo); pipe.hdel(bfoo, bfoo); pipe.hdel(bfoo, bbar); pipe.hget(bfoo, bbar); assertPipelineSyncAll( Arrays.asList( 2L, 0L, 0L, 1L, null, 2L, 0L, 0L, 1L, null), pipe.syncAndReturnAll()); } @Test public void hlen() { Map hash = new HashMap<>(); hash.put("bar", "car"); hash.put("car", "bar"); pipe.hset("foo", hash); pipe.hlen("bar"); pipe.hlen("foo"); // Binary Map bhash = new HashMap<>(); bhash.put(bbar, bcar); bhash.put(bcar, bbar); pipe.hset(bfoo, bhash); pipe.hlen(bbar); pipe.hlen(bfoo); assertPipelineSyncAll( Arrays.asList(2L, 0L, 2L, 2L, 0L, 2L), pipe.syncAndReturnAll()); } @Test public void hkeys() { Map hash = new LinkedHashMap<>(); hash.put("bar", "car"); hash.put("car", "bar"); pipe.hset("foo", hash); pipe.hkeys("foo"); Set expected = new LinkedHashSet<>(); expected.add("bar"); expected.add("car"); // Binary Map bhash = new LinkedHashMap<>(); bhash.put(bbar, bcar); bhash.put(bcar, bbar); pipe.hset(bfoo, bhash); pipe.hkeys(bfoo); Set bexpected = new LinkedHashSet<>(); bexpected.add(bbar); bexpected.add(bcar); assertPipelineSyncAll( Arrays.asList( 2L, new HashSet<>(Arrays.asList("bar", "car")), 2L, new HashSet<>(Arrays.asList(bbar, bcar))), pipe.syncAndReturnAll()); } @Test public void hvals() { Map hash = new LinkedHashMap<>(); hash.put("bar", "car"); //hash.put("car", "bar"); pipe.hset("foo", hash); pipe.hvals("foo"); // Binary Map bhash = new LinkedHashMap<>(); bhash.put(bbar, bcar); //bhash.put(bcar, bbar); pipe.hset(bfoo, bhash); pipe.hvals(bfoo); assertPipelineSyncAll( Arrays.asList( //2L, Arrays.asList("bar", "car"), //2L, Arrays.asList(bbar, bcar)), 1L, Arrays.asList("car"), 1L, Arrays.asList(bcar)), pipe.syncAndReturnAll()); } @Test public void hgetAll() { Map hash = new HashMap<>(); hash.put("bar", "car"); //hash.put("car", "bar"); pipe.hset("foo", hash); pipe.hgetAll("foo"); // Binary Map bhash = new HashMap<>(); bhash.put(bbar, bcar); //bhash.put(bcar, bbar); pipe.hset(bfoo, bhash); pipe.hgetAll(bfoo); // assertPipelineSyncAll( // Arrays.asList( // 1L, hash, // 1L, bhash), // pipe.syncAndReturnAll()); pipe.syncAndReturnAll(); } @Test public void hstrlen() { pipe.hstrlen("foo", "key"); pipe.hset("foo", "key", "value"); pipe.hstrlen("foo", "key"); pipe.hstrlen(bfoo, bbar); pipe.hset(bfoo, bbar, bcar); pipe.hstrlen(bfoo, bbar); assertPipelineSyncAll( Arrays.asList(0L, 1L, 5L, 0L, 1L, 4L), pipe.syncAndReturnAll()); } } ================================================ FILE: src/test/java/redis/clients/jedis/commands/unified/pipeline/ListPipelineCommandsTest.java ================================================ package redis.clients.jedis.commands.unified.pipeline; import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.contains; import static org.hamcrest.Matchers.empty; import static org.hamcrest.Matchers.equalTo; import static org.hamcrest.Matchers.instanceOf; import static org.hamcrest.Matchers.nullValue; import java.util.List; import io.redis.test.annotations.ConditionalOnEnv; import io.redis.test.annotations.SinceRedisVersion; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Tag; import org.junit.jupiter.api.Timeout; import org.junit.jupiter.params.ParameterizedClass; import org.junit.jupiter.params.provider.MethodSource; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import redis.clients.jedis.RedisProtocol; import redis.clients.jedis.Response; import redis.clients.jedis.args.ListDirection; import redis.clients.jedis.args.ListPosition; import redis.clients.jedis.exceptions.JedisDataException; import redis.clients.jedis.params.LPosParams; import redis.clients.jedis.util.KeyValue; import redis.clients.jedis.util.TestEnvUtil; @ParameterizedClass @MethodSource("redis.clients.jedis.commands.CommandsTestsParameters#respVersions") @Tag("integration") public class ListPipelineCommandsTest extends PipelineCommandsTestBase { private final Logger logger = LoggerFactory.getLogger(getClass()); protected final byte[] bfoo = { 0x01, 0x02, 0x03, 0x04 }; protected final byte[] bfoo1 = { 0x01, 0x02, 0x03, 0x04, 0x05 }; protected final byte[] bbar = { 0x05, 0x06, 0x07, 0x08 }; protected final byte[] bcar = { 0x09, 0x0A, 0x0B, 0x0C }; protected final byte[] bA = { 0x0A }; protected final byte[] bB = { 0x0B }; protected final byte[] bC = { 0x0C }; protected final byte[] b1 = { 0x01 }; protected final byte[] b2 = { 0x02 }; protected final byte[] b3 = { 0x03 }; protected final byte[] bhello = { 0x04, 0x02 }; protected final byte[] bx = { 0x02, 0x04 }; protected final byte[] bdst = { 0x11, 0x12, 0x13, 0x14 }; public ListPipelineCommandsTest(RedisProtocol protocol) { super(protocol); } @Test public void rpush() { pipe.rpush("foo", "bar"); pipe.rpush("foo", "foo"); pipe.rpush("foo", "bar", "foo"); assertThat(pipe.syncAndReturnAll(), contains( 1L, 2L, 4L )); // Binary pipe.rpush(bfoo, bbar); pipe.rpush(bfoo, bfoo); pipe.rpush(bfoo, bbar, bfoo); assertThat(pipe.syncAndReturnAll(), contains( 1L, 2L, 4L )); } @Test public void lpush() { pipe.lpush("foo", "bar"); pipe.lpush("foo", "foo"); pipe.lpush("foo", "bar", "foo"); assertThat(pipe.syncAndReturnAll(), contains( 1L, 2L, 4L )); // Binary pipe.lpush(bfoo, bbar); pipe.lpush(bfoo, bfoo); pipe.lpush(bfoo, bbar, bfoo); assertThat(pipe.syncAndReturnAll(), contains( 1L, 2L, 4L )); } @Test public void llen() { pipe.llen("foo"); pipe.lpush("foo", "bar"); pipe.lpush("foo", "car"); pipe.llen("foo"); assertThat(pipe.syncAndReturnAll(), contains( 0L, 1L, 2L, 2L )); // Binary pipe.llen(bfoo); pipe.lpush(bfoo, bbar); pipe.lpush(bfoo, bcar); pipe.llen(bfoo); assertThat(pipe.syncAndReturnAll(), contains( 0L, 1L, 2L, 2L )); } @Test public void llenNotOnList() { pipe.set("foo", "bar"); pipe.llen("foo"); assertThat(pipe.syncAndReturnAll(), contains( equalTo("OK"), instanceOf(JedisDataException.class) )); // Binary pipe.set(bfoo, bbar); pipe.llen(bfoo); assertThat(pipe.syncAndReturnAll(), contains( equalTo("OK"), instanceOf(JedisDataException.class) )); } @Test public void lrange() { pipe.rpush("foo", "a"); pipe.rpush("foo", "b"); pipe.rpush("foo", "c"); Response> range1 = pipe.lrange("foo", 0, 2); Response> range2 = pipe.lrange("foo", 0, 20); Response> range3 = pipe.lrange("foo", 1, 2); Response> range4 = pipe.lrange("foo", 2, 1); pipe.sync(); assertThat(range1.get(), contains("a", "b", "c")); assertThat(range2.get(), contains("a", "b", "c")); assertThat(range3.get(), contains("b", "c")); assertThat(range4.get(), empty()); // Binary pipe.rpush(bfoo, bA); pipe.rpush(bfoo, bB); pipe.rpush(bfoo, bC); Response> brange1 = pipe.lrange(bfoo, 0, 2); Response> brange2 = pipe.lrange(bfoo, 0, 20); Response> brange3 = pipe.lrange(bfoo, 1, 2); Response> brange4 = pipe.lrange(bfoo, 2, 1); pipe.sync(); assertThat(brange1.get(), contains(bA, bB, bC)); assertThat(brange2.get(), contains(bA, bB, bC)); assertThat(brange3.get(), contains(bB, bC)); assertThat(brange4.get(), empty()); } @Test public void ltrim() { pipe.lpush("foo", "1"); pipe.lpush("foo", "2"); pipe.lpush("foo", "3"); Response status = pipe.ltrim("foo", 0, 1); Response len = pipe.llen("foo"); Response> range = pipe.lrange("foo", 0, 100); pipe.sync(); assertThat(status.get(), equalTo("OK")); assertThat(len.get(), equalTo(2L)); assertThat(range.get(), contains("3", "2")); // Binary pipe.lpush(bfoo, b1); pipe.lpush(bfoo, b2); pipe.lpush(bfoo, b3); Response bstatus = pipe.ltrim(bfoo, 0, 1); Response blen = pipe.llen(bfoo); Response> brange = pipe.lrange(bfoo, 0, 100); pipe.sync(); assertThat(bstatus.get(), equalTo("OK")); assertThat(blen.get(), equalTo(2L)); assertThat(brange.get(), contains(b3, b2)); } @Test public void lset() { pipe.lpush("foo", "1"); pipe.lpush("foo", "2"); pipe.lpush("foo", "3"); Response status = pipe.lset("foo", 1, "bar"); Response> range = pipe.lrange("foo", 0, 100); pipe.sync(); assertThat(status.get(), equalTo("OK")); assertThat(range.get(), contains("3", "bar", "1")); // Binary pipe.lpush(bfoo, b1); pipe.lpush(bfoo, b2); pipe.lpush(bfoo, b3); Response bstatus = pipe.lset(bfoo, 1, bbar); Response> brange = pipe.lrange(bfoo, 0, 100); pipe.sync(); assertThat(bstatus.get(), equalTo("OK")); assertThat(brange.get(), contains(b3, bbar, b1)); } @Test public void lindex() { pipe.lpush("foo", "1"); pipe.lpush("foo", "2"); pipe.lpush("foo", "3"); Response index1 = pipe.lindex("foo", 0); Response index2 = pipe.lindex("foo", 100); pipe.sync(); assertThat(index1.get(), equalTo("3")); assertThat(index2.get(), nullValue()); // Binary pipe.lpush(bfoo, b1); pipe.lpush(bfoo, b2); pipe.lpush(bfoo, b3); Response bindex1 = pipe.lindex(bfoo, 0); Response bindex2 = pipe.lindex(bfoo, 100); pipe.sync(); assertThat(bindex1.get(), equalTo(b3)); assertThat(bindex2.get(), nullValue()); } @Test public void lrem() { pipe.lpush("foo", "hello"); pipe.lpush("foo", "hello"); pipe.lpush("foo", "x"); pipe.lpush("foo", "hello"); pipe.lpush("foo", "c"); pipe.lpush("foo", "b"); pipe.lpush("foo", "a"); Response result1 = pipe.lrem("foo", -2, "hello"); Response> range = pipe.lrange("foo", 0, 1000); Response result2 = pipe.lrem("bar", 100, "foo"); pipe.sync(); assertThat(result1.get(), equalTo(2L)); assertThat(range.get(), contains("a", "b", "c", "hello", "x")); assertThat(result2.get(), equalTo(0L)); // Binary pipe.lpush(bfoo, bhello); pipe.lpush(bfoo, bhello); pipe.lpush(bfoo, bx); pipe.lpush(bfoo, bhello); pipe.lpush(bfoo, bC); pipe.lpush(bfoo, bB); pipe.lpush(bfoo, bA); Response bresult1 = pipe.lrem(bfoo, -2, bhello); Response> brange = pipe.lrange(bfoo, 0, 1000); Response bresult2 = pipe.lrem(bbar, 100, bfoo); pipe.sync(); assertThat(bresult1.get(), equalTo(2L)); assertThat(brange.get(), contains(bA, bB, bC, bhello, bx)); assertThat(bresult2.get(), equalTo(0L)); } @Test public void lpop() { Response response1 = pipe.lpop("foo"); Response> response2 = pipe.lpop("foo", 0); pipe.rpush("foo", "a"); pipe.rpush("foo", "b"); pipe.rpush("foo", "c"); Response response3 = pipe.lpop("foo"); Response> response4 = pipe.lpop("foo", 10); Response response5 = pipe.lpop("foo"); Response> response6 = pipe.lpop("foo", 1); pipe.sync(); assertThat(response1.get(), nullValue()); assertThat(response2.get(), nullValue()); assertThat(response3.get(), equalTo("a")); assertThat(response4.get(), contains("b", "c")); assertThat(response5.get(), nullValue()); assertThat(response6.get(), nullValue()); // Binary Response bresponse1 = pipe.lpop(bfoo); Response> bresponse2 = pipe.lpop(bfoo, 0); pipe.rpush(bfoo, bA); pipe.rpush(bfoo, bB); pipe.rpush(bfoo, bC); Response bresponse3 = pipe.lpop(bfoo); Response> bresponse4 = pipe.lpop(bfoo, 10); Response bresponse5 = pipe.lpop(bfoo); Response> bresponse6 = pipe.lpop(bfoo, 1); pipe.sync(); assertThat(bresponse1.get(), nullValue()); assertThat(bresponse2.get(), nullValue()); assertThat(bresponse3.get(), equalTo(bA)); assertThat(bresponse4.get(), contains(bB, bC)); assertThat(bresponse5.get(), nullValue()); assertThat(bresponse6.get(), nullValue()); } @Test public void rpop() { Response response1 = pipe.rpop("foo"); Response> response2 = pipe.rpop("foo", 0); pipe.rpush("foo", "a"); pipe.rpush("foo", "b"); pipe.rpush("foo", "c"); Response response3 = pipe.rpop("foo"); Response> response4 = pipe.rpop("foo", 10); Response response5 = pipe.rpop("foo"); Response> response6 = pipe.rpop("foo", 1); pipe.sync(); assertThat(response1.get(), nullValue()); assertThat(response2.get(), nullValue()); assertThat(response3.get(), equalTo("c")); assertThat(response4.get(), contains("b", "a")); assertThat(response5.get(), nullValue()); assertThat(response6.get(), nullValue()); // Binary Response bresponse1 = pipe.rpop(bfoo); Response> bresponse2 = pipe.rpop(bfoo, 0); pipe.rpush(bfoo, bA); pipe.rpush(bfoo, bB); pipe.rpush(bfoo, bC); Response bresponse3 = pipe.rpop(bfoo); Response> bresponse4 = pipe.rpop(bfoo, 10); Response bresponse5 = pipe.rpop(bfoo); Response> bresponse6 = pipe.rpop(bfoo, 1); pipe.sync(); assertThat(bresponse1.get(), nullValue()); assertThat(bresponse2.get(), nullValue()); assertThat(bresponse3.get(), equalTo(bC)); assertThat(bresponse4.get(), contains(bB, bA)); assertThat(bresponse5.get(), nullValue()); assertThat(bresponse6.get(), nullValue()); } @Test @ConditionalOnEnv(value = TestEnvUtil.ENV_REDIS_ENTERPRISE, enabled = false) public void rpoplpush() { pipe.rpush("foo", "a"); pipe.rpush("foo", "b"); pipe.rpush("foo", "c"); pipe.rpush("dst", "foo"); pipe.rpush("dst", "bar"); Response element = pipe.rpoplpush("foo", "dst"); Response> srcRange = pipe.lrange("foo", 0, 1000); Response> dstRange = pipe.lrange("dst", 0, 1000); pipe.sync(); assertThat(element.get(), equalTo("c")); assertThat(srcRange.get(), contains("a", "b")); assertThat(dstRange.get(), contains("c", "foo", "bar")); // Binary pipe.rpush(bfoo, bA); pipe.rpush(bfoo, bB); pipe.rpush(bfoo, bC); pipe.rpush(bdst, bfoo); pipe.rpush(bdst, bbar); Response belement = pipe.rpoplpush(bfoo, bdst); Response> bsrcRange = pipe.lrange(bfoo, 0, 1000); Response> bdstRange = pipe.lrange(bdst, 0, 1000); pipe.sync(); assertThat(belement.get(), equalTo(bC)); assertThat(bsrcRange.get(), contains(bA, bB)); assertThat(bdstRange.get(), contains(bC, bfoo, bbar)); } @Test @ConditionalOnEnv(value = TestEnvUtil.ENV_REDIS_ENTERPRISE, enabled = false) public void blpop() throws InterruptedException { Response> result1 = pipe.blpop(1, "foo"); pipe.lpush("foo", "bar"); Response> result2 = pipe.blpop(1, "foo"); // Multi keys Response> result3 = pipe.blpop(1, "foo", "foo1"); pipe.lpush("foo", "bar"); pipe.lpush("foo1", "bar1"); Response> result4 = pipe.blpop(1, "foo1", "foo"); pipe.sync(); assertThat(result1.get(), nullValue()); assertThat(result2.get(), contains("foo", "bar")); assertThat(result3.get(), nullValue()); assertThat(result4.get(), contains("foo1", "bar1")); // Binary pipe.lpush(bfoo, bbar); Response> bresult1 = pipe.blpop(1, bfoo); // Binary Multi keys Response> bresult2 = pipe.blpop(1, bfoo, bfoo1); pipe.lpush(bfoo, bbar); pipe.lpush(bfoo1, bcar); Response> bresult3 = pipe.blpop(1, bfoo1, bfoo); pipe.sync(); assertThat(bresult1.get(), contains(bfoo, bbar)); assertThat(bresult2.get(), nullValue()); assertThat(bresult3.get(), contains(bfoo1, bcar)); } @Test @ConditionalOnEnv(value = TestEnvUtil.ENV_REDIS_ENTERPRISE, enabled = false) public void blpopDouble() { Response> result1 = pipe.blpop(0.1, "foo"); pipe.lpush("foo", "bar"); Response> result2 = pipe.blpop(3.2, "foo"); // Multi keys Response> result3 = pipe.blpop(0.18, "foo", "foo1"); pipe.lpush("foo", "bar"); pipe.lpush("foo1", "bar1"); Response> result4 = pipe.blpop(1d, "foo1", "foo"); pipe.sync(); assertThat(result1.get(), nullValue()); assertThat(result2.get(), equalTo(new KeyValue<>("foo", "bar"))); assertThat(result3.get(), nullValue()); assertThat(result4.get(), equalTo(new KeyValue<>("foo1", "bar1"))); // Binary pipe.lpush(bfoo, bbar); Response> bresult1 = pipe.blpop(3.12, bfoo); // Binary Multi keys Response> bresult2 = pipe.blpop(0.11, bfoo, bfoo1); pipe.lpush(bfoo, bbar); pipe.lpush(bfoo1, bcar); Response> bresult3 = pipe.blpop(1d, bfoo1, bfoo); pipe.sync(); assertThat(bresult1.get().getKey(), equalTo(bfoo)); assertThat(bresult1.get().getValue(), equalTo(bbar)); assertThat(bresult2.get(), nullValue()); assertThat(bresult3.get().getKey(), equalTo(bfoo1)); assertThat(bresult3.get().getValue(), equalTo(bcar)); } @Test @Timeout(5) public void blpopDoubleWithSleep() { Response> result = pipe.blpop(0.04, "foo"); pipe.sync(); assertThat(result.get(), nullValue()); new Thread(() -> { try { Thread.sleep(30); } catch (InterruptedException e) { logger.error("", e); } client.lpush("foo", "bar"); }).start(); result = pipe.blpop(1.2, "foo"); pipe.sync(); assertThat(result.get().getKey(), equalTo("foo")); assertThat(result.get().getValue(), equalTo("bar")); } @Test @ConditionalOnEnv(value = TestEnvUtil.ENV_REDIS_ENTERPRISE, enabled = false) public void brpop() { Response> result1 = pipe.brpop(1, "foo"); pipe.lpush("foo", "bar"); Response> result2 = pipe.brpop(1, "foo"); // Multi keys Response> result3 = pipe.brpop(1, "foo", "foo1"); pipe.lpush("foo", "bar"); pipe.lpush("foo1", "bar1"); Response> result4 = pipe.brpop(1, "foo1", "foo"); pipe.sync(); assertThat(result1.get(), nullValue()); assertThat(result2.get(), contains("foo", "bar")); assertThat(result3.get(), nullValue()); assertThat(result4.get(), contains("foo1", "bar1")); // Binary pipe.lpush(bfoo, bbar); Response> bresult1 = pipe.brpop(1, bfoo); // Binary Multi keys Response> bresult2 = pipe.brpop(1, bfoo, bfoo1); pipe.lpush(bfoo, bbar); pipe.lpush(bfoo1, bcar); Response> bresult3 = pipe.brpop(1, bfoo1, bfoo); pipe.sync(); assertThat(bresult1.get(), contains(bfoo, bbar)); assertThat(bresult2.get(), nullValue()); assertThat(bresult3.get(), contains(bfoo1, bcar)); } @Test @ConditionalOnEnv(value = TestEnvUtil.ENV_REDIS_ENTERPRISE, enabled = false) public void brpopDouble() { Response> result1 = pipe.brpop(0.1, "foo"); pipe.lpush("foo", "bar"); Response> result2 = pipe.brpop(3.2, "foo"); // Multi keys Response> result3 = pipe.brpop(0.18, "foo", "foo1"); pipe.lpush("foo", "bar"); pipe.lpush("foo1", "bar1"); Response> result4 = pipe.brpop(1d, "foo1", "foo"); pipe.sync(); assertThat(result1.get(), nullValue()); assertThat(result2.get(), equalTo(new KeyValue<>("foo", "bar"))); assertThat(result3.get(), nullValue()); assertThat(result4.get(), equalTo(new KeyValue<>("foo1", "bar1"))); // Binary pipe.lpush(bfoo, bbar); Response> bresult1 = pipe.brpop(3.12, bfoo); // Binary Multi keys Response> bresult2 = pipe.brpop(0.11, bfoo, bfoo1); pipe.lpush(bfoo, bbar); pipe.lpush(bfoo1, bcar); Response> bresult3 = pipe.brpop(1d, bfoo1, bfoo); pipe.sync(); assertThat(bresult1.get().getKey(), equalTo(bfoo)); assertThat(bresult1.get().getValue(), equalTo(bbar)); assertThat(bresult2.get(), nullValue()); assertThat(bresult3.get().getKey(), equalTo(bfoo1)); assertThat(bresult3.get().getValue(), equalTo(bcar)); } @Test @Timeout(5) public void brpopDoubleWithSleep() { Response> result = pipe.brpop(0.04, "foo"); pipe.sync(); assertThat(result.get(), nullValue()); new Thread(() -> { try { Thread.sleep(30); } catch (InterruptedException e) { logger.error("", e); } client.lpush("foo", "bar"); }).start(); result = pipe.brpop(1.2, "foo"); pipe.sync(); assertThat(result.get().getKey(), equalTo("foo")); assertThat(result.get().getValue(), equalTo("bar")); } @Test public void lpushx() { pipe.lpushx("foo", "bar"); pipe.lpush("foo", "a"); pipe.lpushx("foo", "b"); assertThat(pipe.syncAndReturnAll(), contains( 0L, 1L, 2L )); // Binary pipe.lpushx(bfoo, bbar); pipe.lpush(bfoo, bA); pipe.lpushx(bfoo, bB); assertThat(pipe.syncAndReturnAll(), contains( 0L, 1L, 2L )); } @Test public void rpushx() { pipe.rpushx("foo", "bar"); pipe.lpush("foo", "a"); pipe.rpushx("foo", "b"); assertThat(pipe.syncAndReturnAll(), contains( 0L, 1L, 2L )); // Binary pipe.rpushx(bfoo, bbar); pipe.lpush(bfoo, bA); pipe.rpushx(bfoo, bB); assertThat(pipe.syncAndReturnAll(), contains( 0L, 1L, 2L )); } @Test public void linsert() { Response result1 = pipe.linsert("foo", ListPosition.BEFORE, "bar", "car"); pipe.lpush("foo", "a"); Response result2 = pipe.linsert("foo", ListPosition.AFTER, "a", "b"); Response> range = pipe.lrange("foo", 0, 100); Response result3 = pipe.linsert("foo", ListPosition.BEFORE, "bar", "car"); pipe.sync(); assertThat(result1.get(), equalTo(0L)); assertThat(result2.get(), equalTo(2L)); assertThat(range.get(), contains("a", "b")); assertThat(result3.get(), equalTo(-1L)); // Binary Response bresult1 = pipe.linsert(bfoo, ListPosition.BEFORE, bbar, bcar); pipe.lpush(bfoo, bA); Response bresult2 = pipe.linsert(bfoo, ListPosition.AFTER, bA, bB); Response> brange = pipe.lrange(bfoo, 0, 100); Response bresult3 = pipe.linsert(bfoo, ListPosition.BEFORE, bbar, bcar); pipe.sync(); assertThat(bresult1.get(), equalTo(0L)); assertThat(bresult2.get(), equalTo(2L)); assertThat(brange.get(), contains(bA, bB)); assertThat(bresult3.get(), equalTo(-1L)); } @Test @ConditionalOnEnv(value = TestEnvUtil.ENV_REDIS_ENTERPRISE, enabled = false) public void brpoplpush() { new Thread(() -> { try { Thread.sleep(100); } catch (InterruptedException e) { logger.error("", e); } client.lpush("foo", "a"); }).start(); Response element = pipe.brpoplpush("foo", "bar", 0); Response len = pipe.llen("bar"); Response> range = pipe.lrange("bar", 0, -1); pipe.sync(); assertThat(element.get(), equalTo("a")); assertThat(len.get(), equalTo(1L)); assertThat(range.get(), contains("a")); // Binary new Thread(() -> { try { Thread.sleep(100); } catch (InterruptedException e) { logger.error("", e); } client.lpush(bfoo, bA); }).start(); Response belement = pipe.brpoplpush(bfoo, bbar, 0); Response blen = pipe.llen(bbar); Response> brange = pipe.lrange(bbar, 0, -1); pipe.sync(); assertThat(belement.get(), equalTo(bA)); assertThat(blen.get(), equalTo(1L)); assertThat(brange.get(), contains(bA)); } @Test public void lpos() { pipe.rpush("foo", "a"); pipe.rpush("foo", "b"); pipe.rpush("foo", "c"); pipe.sync(); pipe.lpos("foo", "b"); pipe.lpos("foo", "d"); assertThat(pipe.syncAndReturnAll(), contains( equalTo(1L), nullValue() )); pipe.rpush("foo", "a"); pipe.rpush("foo", "b"); pipe.rpush("foo", "b"); pipe.sync(); pipe.lpos("foo", "b", LPosParams.lPosParams()); pipe.lpos("foo", "b", LPosParams.lPosParams().rank(3)); pipe.lpos("foo", "b", LPosParams.lPosParams().rank(-2)); pipe.lpos("foo", "b", LPosParams.lPosParams().rank(-5)); pipe.lpos("foo", "b", LPosParams.lPosParams().rank(1).maxlen(2)); pipe.lpos("foo", "b", LPosParams.lPosParams().rank(2).maxlen(2)); pipe.lpos("foo", "b", LPosParams.lPosParams().rank(-2).maxlen(2)); assertThat(pipe.syncAndReturnAll(), contains( equalTo(1L), equalTo(5L), equalTo(4L), nullValue(), equalTo(1L), nullValue(), equalTo(4L) )); Response> posList1 = pipe.lpos("foo", "b", LPosParams.lPosParams(), 2); Response> posList2 = pipe.lpos("foo", "b", LPosParams.lPosParams(), 0); Response> posList3 = pipe.lpos("foo", "b", LPosParams.lPosParams().rank(2), 0); Response> posList4 = pipe.lpos("foo", "b", LPosParams.lPosParams().rank(2).maxlen(5), 0); Response> posList5 = pipe.lpos("foo", "b", LPosParams.lPosParams().rank(-2), 0); Response> posList6 = pipe.lpos("foo", "b", LPosParams.lPosParams().rank(-1).maxlen(5), 2); pipe.sync(); assertThat(posList1.get(), contains(1L, 4L)); assertThat(posList2.get(), contains(1L, 4L, 5L)); assertThat(posList3.get(), contains(4L, 5L)); assertThat(posList4.get(), contains(4L)); assertThat(posList5.get(), contains(4L, 1L)); assertThat(posList6.get(), contains(5L, 4L)); // Binary pipe.rpush(bfoo, bA); pipe.rpush(bfoo, bB); pipe.rpush(bfoo, bC); pipe.sync(); pipe.lpos(bfoo, bB); pipe.lpos(bfoo, b3); assertThat(pipe.syncAndReturnAll(), contains( equalTo(1L), nullValue() )); pipe.rpush(bfoo, bA); pipe.rpush(bfoo, bB); pipe.rpush(bfoo, bA); pipe.sync(); pipe.lpos(bfoo, bB, LPosParams.lPosParams().rank(2)); pipe.lpos(bfoo, bB, LPosParams.lPosParams().rank(-2).maxlen(5)); assertThat(pipe.syncAndReturnAll(), contains( equalTo(4L), equalTo(1L) )); Response> bposList1 = pipe.lpos(bfoo, bA, LPosParams.lPosParams().maxlen(6), 0); Response> bposList2 = pipe.lpos(bfoo, bA, LPosParams.lPosParams().maxlen(6).rank(2), 1); pipe.sync(); assertThat(bposList1.get(), contains(0L, 3L, 5L)); assertThat(bposList2.get(), contains(3L)); } @Test @ConditionalOnEnv(value = TestEnvUtil.ENV_REDIS_ENTERPRISE, enabled = false) public void lmove() { pipe.rpush("foo", "bar1", "bar2", "bar3"); Response item1 = pipe.lmove("foo", "bar", ListDirection.RIGHT, ListDirection.LEFT); Response> range1 = pipe.lrange("bar", 0, -1); Response> range2 = pipe.lrange("foo", 0, -1); pipe.sync(); assertThat(item1.get(), equalTo("bar3")); assertThat(range1.get(), contains("bar3")); assertThat(range2.get(), contains("bar1", "bar2")); // Binary pipe.rpush(bfoo, b1, b2, b3); Response bitem1 = pipe.lmove(bfoo, bbar, ListDirection.RIGHT, ListDirection.LEFT); Response> brange1 = pipe.lrange(bbar, 0, -1); Response> brange2 = pipe.lrange(bfoo, 0, -1); pipe.sync(); assertThat(bitem1.get(), equalTo(b3)); assertThat(brange1.get(), contains(b3)); assertThat(brange2.get(), contains(b1, b2)); } @Test @ConditionalOnEnv(value = TestEnvUtil.ENV_REDIS_ENTERPRISE, enabled = false) public void blmove() { new Thread(() -> { try { Thread.sleep(100); } catch (InterruptedException e) { logger.error("", e); } client.rpush("foo", "bar1", "bar2", "bar3"); }).start(); Response response = pipe.blmove("foo", "bar", ListDirection.RIGHT, ListDirection.LEFT, 0); Response> range1 = pipe.lrange("bar", 0, -1); Response> range2 = pipe.lrange("foo", 0, -1); pipe.sync(); assertThat(response.get(), equalTo("bar3")); assertThat(range1.get(), contains("bar3")); assertThat(range2.get(), contains("bar1", "bar2")); // Binary new Thread(() -> { try { Thread.sleep(100); } catch (InterruptedException e) { logger.error("", e); } client.rpush(bfoo, b1, b2, b3); }).start(); Response bresponse = pipe.blmove(bfoo, bbar, ListDirection.RIGHT, ListDirection.LEFT, 0); Response> brange1 = pipe.lrange(bbar, 0, -1); Response> brange2 = pipe.lrange(bfoo, 0, -1); pipe.sync(); assertThat(bresponse.get(), equalTo(b3)); assertThat(brange1.get(), contains(b3)); assertThat(brange2.get(), contains(b1, b2)); } @Test @SinceRedisVersion(value="7.0.0") @ConditionalOnEnv(value = TestEnvUtil.ENV_REDIS_ENTERPRISE, enabled = false) public void lmpop() { String mylist1 = "mylist1"; String mylist2 = "mylist2"; // add elements to list pipe.lpush(mylist1, "one1", "two1", "three1", "four1", "five1"); pipe.lpush(mylist2, "one2", "two2", "three2", "four2", "five2"); Response>> elements1 = pipe.lmpop(ListDirection.LEFT, mylist1, mylist2); Response>> elements2 = pipe.lmpop(ListDirection.LEFT, 5, mylist1, mylist2); Response>> elements3 = pipe.lmpop(ListDirection.RIGHT, 100, mylist1, mylist2); Response>> elements4 = pipe.lmpop(ListDirection.RIGHT, mylist1, mylist2); pipe.sync(); assertThat(elements1.get().getKey(), equalTo(mylist1)); assertThat(elements1.get().getValue(), contains("five1")); assertThat(elements2.get().getKey(), equalTo(mylist1)); assertThat(elements2.get().getValue(), contains("four1", "three1", "two1", "one1")); assertThat(elements3.get().getKey(), equalTo(mylist2)); assertThat(elements3.get().getValue(), contains("one2", "two2", "three2", "four2", "five2")); assertThat(elements4.get(), nullValue()); } @Test @SinceRedisVersion(value="7.0.0") @ConditionalOnEnv(value = TestEnvUtil.ENV_REDIS_ENTERPRISE, enabled = false) public void blmpopSimple() { String mylist1 = "mylist1"; String mylist2 = "mylist2"; // add elements to list pipe.lpush(mylist1, "one1", "two1", "three1", "four1", "five1"); pipe.lpush(mylist2, "one2", "two2", "three2", "four2", "five2"); Response>> elements1 = pipe.blmpop(1L, ListDirection.LEFT, mylist1, mylist2); Response>> elements2 = pipe.blmpop(1L, ListDirection.LEFT, 5, mylist1, mylist2); Response>> elements3 = pipe.blmpop(1L, ListDirection.RIGHT, 100, mylist1, mylist2); Response>> elements4 = pipe.blmpop(1L, ListDirection.RIGHT, mylist1, mylist2); pipe.sync(); assertThat(elements1.get().getKey(), equalTo(mylist1)); assertThat(elements1.get().getValue(), contains("five1")); assertThat(elements2.get().getKey(), equalTo(mylist1)); assertThat(elements2.get().getValue(), contains("four1", "three1", "two1", "one1")); assertThat(elements3.get().getKey(), equalTo(mylist2)); assertThat(elements3.get().getValue(), contains("one2", "two2", "three2", "four2", "five2")); assertThat(elements4.get(), nullValue()); } } ================================================ FILE: src/test/java/redis/clients/jedis/commands/unified/pipeline/PipelineCommandsTestBase.java ================================================ package redis.clients.jedis.commands.unified.pipeline; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Tag; import org.junit.jupiter.api.extension.RegisterExtension; import redis.clients.jedis.util.EnabledOnCommandCondition; import redis.clients.jedis.util.EnvCondition; import redis.clients.jedis.util.RedisVersionCondition; import redis.clients.jedis.*; import redis.clients.jedis.commands.CommandsTestsParameters; import redis.clients.jedis.commands.unified.client.RedisClientCommandsTestHelper; @Tag("integration") public abstract class PipelineCommandsTestBase { protected RedisClient client; protected Pipeline pipe; /** * Input data for parameterized tests. In principle all subclasses of this class should be * parameterized tests, to run with several versions of RESP. * @see CommandsTestsParameters#respVersions() */ protected final RedisProtocol protocol; @RegisterExtension public RedisVersionCondition versionCondition = new RedisVersionCondition( RedisClientCommandsTestHelper::getEndpointConfig); @RegisterExtension public EnabledOnCommandCondition enabledOnCommandCondition = new EnabledOnCommandCondition( RedisClientCommandsTestHelper::getEndpointConfig); @RegisterExtension public static EnvCondition conditionalOnEnvCondition = new EnvCondition(); /** * The RESP protocol is to be injected by the subclasses, usually via JUnit * parameterized tests, because most of the subclassed tests are meant to be * executed against multiple RESP versions. For the special cases where a single * RESP version is relevant, we still force the subclass to be explicit and * call this constructor. * * @param protocol The RESP protocol to use during the tests. */ public PipelineCommandsTestBase(RedisProtocol protocol) { this.protocol = protocol; } @BeforeEach public void setUp() { client = RedisClientCommandsTestHelper.getClient(protocol); RedisClientCommandsTestHelper.clearData(); pipe = client.pipelined(); } @AfterEach public void tearDown() { pipe.close(); client.close(); } } ================================================ FILE: src/test/java/redis/clients/jedis/commands/unified/pipeline/SetPipelineCommandsTest.java ================================================ package redis.clients.jedis.commands.unified.pipeline; import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.anyOf; import static org.hamcrest.Matchers.contains; import static org.hamcrest.Matchers.containsInAnyOrder; import static org.hamcrest.Matchers.empty; import static org.hamcrest.Matchers.equalTo; import static org.hamcrest.Matchers.hasSize; import static org.hamcrest.Matchers.not; import static org.hamcrest.Matchers.nullValue; import static redis.clients.jedis.params.ScanParams.SCAN_POINTER_START; import static redis.clients.jedis.params.ScanParams.SCAN_POINTER_START_BINARY; import static redis.clients.jedis.util.AssertUtil.assertByteArrayCollectionContainsAll; import static redis.clients.jedis.util.AssertUtil.assertByteArraySetEquals; import static redis.clients.jedis.util.AssertUtil.assertCollectionContainsAll; import static redis.clients.jedis.util.ByteArrayUtil.byteArrayCollectionRemoveAll; import java.util.HashSet; import java.util.List; import java.util.Set; import io.redis.test.annotations.ConditionalOnEnv; import io.redis.test.annotations.SinceRedisVersion; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Tag; import org.junit.jupiter.params.ParameterizedClass; import org.junit.jupiter.params.provider.MethodSource; import redis.clients.jedis.RedisProtocol; import redis.clients.jedis.Response; import redis.clients.jedis.params.ScanParams; import redis.clients.jedis.resps.ScanResult; import redis.clients.jedis.util.TestEnvUtil; @ParameterizedClass @MethodSource("redis.clients.jedis.commands.CommandsTestsParameters#respVersions") @Tag("integration") public class SetPipelineCommandsTest extends PipelineCommandsTestBase { final byte[] bfoo = { 0x01, 0x02, 0x03, 0x04 }; final byte[] bbar = { 0x05, 0x06, 0x07, 0x08 }; final byte[] bcar = { 0x09, 0x0A, 0x0B, 0x0C }; final byte[] ba = { 0x0A }; final byte[] bb = { 0x0B }; final byte[] bc = { 0x0C }; final byte[] bd = { 0x0D }; final byte[] bx = { 0x42 }; final byte[] bbar1 = { 0x05, 0x06, 0x07, 0x08, 0x0A }; final byte[] bbar2 = { 0x05, 0x06, 0x07, 0x08, 0x0B }; final byte[] bbar3 = { 0x05, 0x06, 0x07, 0x08, 0x0C }; final byte[] bbarstar = { 0x05, 0x06, 0x07, 0x08, '*' }; public SetPipelineCommandsTest(RedisProtocol protocol) { super(protocol); } @Test public void sadd() { pipe.sadd("foo", "a"); pipe.sadd("foo", "a"); assertThat(pipe.syncAndReturnAll(), contains( 1L, 0L )); pipe.sadd(bfoo, ba); pipe.sadd(bfoo, ba); assertThat(pipe.syncAndReturnAll(), contains( 1L, 0L )); } @Test public void smembers() { pipe.sadd("foo", "a"); pipe.sadd("foo", "b"); Response> members = pipe.smembers("foo"); pipe.sync(); assertThat(members.get(), containsInAnyOrder("a", "b")); // Binary pipe.sadd(bfoo, ba); pipe.sadd(bfoo, bb); Response> bmembers = pipe.smembers(bfoo); pipe.sync(); assertThat(bmembers.get(), containsInAnyOrder(ba, bb)); } @Test public void srem() { pipe.sadd("foo", "a"); pipe.sadd("foo", "b"); Response status1 = pipe.srem("foo", "a"); Response> members = pipe.smembers("foo"); Response status2 = pipe.srem("foo", "bar"); pipe.sync(); assertThat(status1.get(), equalTo(1L)); assertThat(members.get(), containsInAnyOrder("b")); assertThat(status2.get(), equalTo(0L)); // Binary pipe.sadd(bfoo, ba); pipe.sadd(bfoo, bb); Response bstatus1 = pipe.srem(bfoo, ba); Response> bmembers = pipe.smembers(bfoo); Response bstatus2 = pipe.srem(bfoo, bbar); pipe.sync(); assertThat(bstatus1.get(), equalTo(1L)); assertThat(bmembers.get(), containsInAnyOrder(bb)); assertThat(bstatus2.get(), equalTo(0L)); } @Test public void spop() { pipe.sadd("foo", "a"); pipe.sadd("foo", "b"); Response member1 = pipe.spop("foo"); Response> members = pipe.smembers("foo"); Response member2 = pipe.spop("bar"); pipe.sync(); assertThat(member1.get(), anyOf(equalTo("a"), equalTo("b"))); assertThat(members.get(), hasSize(1)); assertThat(member2.get(), nullValue()); // Binary pipe.sadd(bfoo, ba); pipe.sadd(bfoo, bb); Response bmember1 = pipe.spop(bfoo); Response> bmembers = pipe.smembers(bfoo); Response bmember2 = pipe.spop(bbar); pipe.sync(); assertThat(bmember1.get(), anyOf(equalTo(ba), equalTo(bb))); assertThat(bmembers.get(), hasSize(1)); assertThat(bmember2.get(), nullValue()); } @Test public void spopWithCount() { pipe.sadd("foo", "a"); pipe.sadd("foo", "b"); pipe.sadd("foo", "c"); Response> members1 = pipe.spop("foo", 2); Response> members2 = pipe.spop("foo", 2); Response> members3 = pipe.spop("foo", 2); pipe.sync(); assertThat(members1.get(), hasSize(2)); assertThat(members2.get(), hasSize(1)); assertThat(members3.get(), empty()); Set superSet = new HashSet<>(); superSet.add("c"); superSet.add("b"); superSet.add("a"); assertCollectionContainsAll(superSet, members1.get()); superSet.removeAll(members1.get()); assertThat(members2.get(), equalTo(superSet)); // Binary pipe.sadd(bfoo, ba); pipe.sadd(bfoo, bb); pipe.sadd(bfoo, bc); Response> bmembers1 = pipe.spop(bfoo, 2); Response> bmembers2 = pipe.spop(bfoo, 2); Response> bmembers3 = pipe.spop(bfoo, 2); pipe.sync(); assertThat(bmembers1.get(), hasSize(2)); assertThat(bmembers2.get(), hasSize(1)); assertThat(bmembers3.get(), empty()); Set bsuperSet = new HashSet<>(); bsuperSet.add(bc); bsuperSet.add(bb); bsuperSet.add(ba); assertByteArrayCollectionContainsAll(bsuperSet, bmembers1.get()); byteArrayCollectionRemoveAll(bsuperSet, bmembers1.get()); assertByteArraySetEquals(bsuperSet, bmembers2.get()); } @Test @ConditionalOnEnv(value = TestEnvUtil.ENV_REDIS_ENTERPRISE, enabled = false) public void smove() { pipe.sadd("foo", "a"); pipe.sadd("foo", "b"); pipe.sadd("bar", "c"); Response status1 = pipe.smove("foo", "bar", "a"); Response> srcMembers = pipe.smembers("foo"); Response> dstMembers = pipe.smembers("bar"); Response status2 = pipe.smove("foo", "bar", "a"); pipe.sync(); assertThat(status1.get(), equalTo(1L)); assertThat(srcMembers.get(), containsInAnyOrder("b")); assertThat(dstMembers.get(), containsInAnyOrder("a", "c")); assertThat(status2.get(), equalTo(0L)); // Binary pipe.sadd(bfoo, ba); pipe.sadd(bfoo, bb); pipe.sadd(bbar, bc); Response bstatus1 = pipe.smove(bfoo, bbar, ba); Response> bsrcMembers = pipe.smembers(bfoo); Response> bdstMembers = pipe.smembers(bbar); Response bstatus2 = pipe.smove(bfoo, bbar, ba); pipe.sync(); assertThat(bstatus1.get(), equalTo(1L)); assertThat(bsrcMembers.get(), containsInAnyOrder(bb)); assertThat(bdstMembers.get(), containsInAnyOrder(ba, bc)); assertThat(bstatus2.get(), equalTo(0L)); } @Test public void scard() { pipe.sadd("foo", "a"); pipe.sadd("foo", "b"); pipe.sync(); pipe.scard("foo"); pipe.scard("bar"); assertThat(pipe.syncAndReturnAll(), contains( 2L, 0L )); // Binary pipe.sadd(bfoo, ba); pipe.sadd(bfoo, bb); pipe.sync(); pipe.scard(bfoo); pipe.scard(bbar); assertThat(pipe.syncAndReturnAll(), contains( 2L, 0L )); } @Test public void sismember() { pipe.sadd("foo", "a"); pipe.sadd("foo", "b"); pipe.sync(); pipe.sismember("foo", "a"); pipe.sismember("foo", "c"); assertThat(pipe.syncAndReturnAll(), contains( true, false )); // Binary pipe.sadd(bfoo, ba); pipe.sadd(bfoo, bb); pipe.sync(); pipe.sismember(bfoo, ba); pipe.sismember(bfoo, bc); assertThat(pipe.syncAndReturnAll(), contains( true, false )); } @Test public void smismember() { pipe.sadd("foo", "a", "b"); Response> response = pipe.smismember("foo", "a", "c"); pipe.sync(); assertThat(response.get(), contains(true, false)); // Binary pipe.sadd(bfoo, ba, bb); Response> bresponse = pipe.smismember(bfoo, ba, bc); pipe.sync(); assertThat(bresponse.get(), contains(true, false)); } @Test @ConditionalOnEnv(value = TestEnvUtil.ENV_REDIS_ENTERPRISE, enabled = false) public void sinter() { pipe.sadd("foo", "a"); pipe.sadd("foo", "b"); pipe.sadd("bar", "b"); pipe.sadd("bar", "c"); Response> intersection = pipe.sinter("foo", "bar"); pipe.sync(); assertThat(intersection.get(), containsInAnyOrder("b")); // Binary pipe.sadd(bfoo, ba); pipe.sadd(bfoo, bb); pipe.sadd(bbar, bb); pipe.sadd(bbar, bc); Response> bintersection = pipe.sinter(bfoo, bbar); pipe.sync(); assertThat(bintersection.get(), containsInAnyOrder(bb)); } @Test @ConditionalOnEnv(value = TestEnvUtil.ENV_REDIS_ENTERPRISE, enabled = false) public void sinterstore() { pipe.sadd("foo", "a"); pipe.sadd("foo", "b"); pipe.sadd("bar", "b"); pipe.sadd("bar", "c"); Response status = pipe.sinterstore("car", "foo", "bar"); Response> members = pipe.smembers("car"); pipe.sync(); assertThat(status.get(), equalTo(1L)); assertThat(members.get(), containsInAnyOrder("b")); // Binary pipe.sadd(bfoo, ba); pipe.sadd(bfoo, bb); pipe.sadd(bbar, bb); pipe.sadd(bbar, bc); Response bstatus = pipe.sinterstore(bcar, bfoo, bbar); Response> bmembers = pipe.smembers(bcar); pipe.sync(); assertThat(bstatus.get(), equalTo(1L)); assertThat(bmembers.get(), containsInAnyOrder(bb)); } @Test @SinceRedisVersion(value="7.0.0") @ConditionalOnEnv(value = TestEnvUtil.ENV_REDIS_ENTERPRISE, enabled = false) public void sintercard() { pipe.sadd("foo", "a"); pipe.sadd("foo", "b"); pipe.sadd("bar", "a"); pipe.sadd("bar", "b"); pipe.sadd("bar", "c"); Response card = pipe.sintercard("foo", "bar"); Response limitedCard = pipe.sintercard(1, "foo", "bar"); pipe.sync(); assertThat(card.get(), equalTo(2L)); assertThat(limitedCard.get(), equalTo(1L)); // Binary pipe.sadd(bfoo, ba); pipe.sadd(bfoo, bb); pipe.sadd(bbar, ba); pipe.sadd(bbar, bb); pipe.sadd(bbar, bc); Response bcard = pipe.sintercard(bfoo, bbar); Response blimitedCard = pipe.sintercard(1, bfoo, bbar); pipe.sync(); assertThat(bcard.get(), equalTo(2L)); assertThat(blimitedCard.get(), equalTo(1L)); } @Test @ConditionalOnEnv(value = TestEnvUtil.ENV_REDIS_ENTERPRISE, enabled = false) public void sunion() { pipe.sadd("foo", "a"); pipe.sadd("foo", "b"); pipe.sadd("bar", "b"); pipe.sadd("bar", "c"); Response> union = pipe.sunion("foo", "bar"); pipe.sync(); assertThat(union.get(), containsInAnyOrder("a", "b", "c")); // Binary pipe.sadd(bfoo, ba); pipe.sadd(bfoo, bb); pipe.sadd(bbar, bb); pipe.sadd(bbar, bc); Response> bunion = pipe.sunion(bfoo, bbar); pipe.sync(); assertThat(bunion.get(), containsInAnyOrder(ba, bb, bc)); } @Test @ConditionalOnEnv(value = TestEnvUtil.ENV_REDIS_ENTERPRISE, enabled = false) public void sunionstore() { pipe.sadd("foo", "a"); pipe.sadd("foo", "b"); pipe.sadd("bar", "b"); pipe.sadd("bar", "c"); Response status = pipe.sunionstore("car", "foo", "bar"); Response> members = pipe.smembers("car"); pipe.sync(); assertThat(status.get(), equalTo(3L)); assertThat(members.get(), containsInAnyOrder("a", "b", "c")); // Binary pipe.sadd(bfoo, ba); pipe.sadd(bfoo, bb); pipe.sadd(bbar, bb); pipe.sadd(bbar, bc); Response bstatus = pipe.sunionstore(bcar, bfoo, bbar); Response> bmembers = pipe.smembers(bcar); pipe.sync(); assertThat(bstatus.get(), equalTo(3L)); assertThat(bmembers.get(), containsInAnyOrder(ba, bb, bc)); } @Test @ConditionalOnEnv(value = TestEnvUtil.ENV_REDIS_ENTERPRISE, enabled = false) public void sdiff() { pipe.sadd("foo", "x"); pipe.sadd("foo", "a"); pipe.sadd("foo", "b"); pipe.sadd("foo", "c"); pipe.sadd("bar", "c"); pipe.sadd("car", "a"); pipe.sadd("car", "d"); Response> diff = pipe.sdiff("foo", "bar", "car"); pipe.sync(); assertThat(diff.get(), containsInAnyOrder("b", "x")); // Binary pipe.sadd(bfoo, bx); pipe.sadd(bfoo, ba); pipe.sadd(bfoo, bb); pipe.sadd(bfoo, bc); pipe.sadd(bbar, bc); pipe.sadd(bcar, ba); pipe.sadd(bcar, bd); Response> bdiff = pipe.sdiff(bfoo, bbar, bcar); pipe.sync(); assertThat(bdiff.get(), containsInAnyOrder(bb, bx)); } @Test @ConditionalOnEnv(value = TestEnvUtil.ENV_REDIS_ENTERPRISE, enabled = false) public void sdiffstore() { pipe.sadd("foo", "x"); pipe.sadd("foo", "a"); pipe.sadd("foo", "b"); pipe.sadd("foo", "c"); pipe.sadd("bar", "c"); pipe.sadd("car", "a"); pipe.sadd("car", "d"); Response status = pipe.sdiffstore("tar", "foo", "bar", "car"); Response> members = pipe.smembers("tar"); pipe.sync(); assertThat(status.get(), equalTo(2L)); assertThat(members.get(), containsInAnyOrder("b", "x")); // Binary pipe.sadd(bfoo, bx); pipe.sadd(bfoo, ba); pipe.sadd(bfoo, bb); pipe.sadd(bfoo, bc); pipe.sadd(bbar, bc); pipe.sadd(bcar, ba); pipe.sadd(bcar, bd); Response bstatus = pipe.sdiffstore("tar".getBytes(), bfoo, bbar, bcar); Response> bmembers = pipe.smembers("tar".getBytes()); pipe.sync(); assertThat(bstatus.get(), equalTo(2L)); assertThat(bmembers.get(), containsInAnyOrder(bb, bx)); } @Test public void srandmember() { pipe.sadd("foo", "a"); pipe.sadd("foo", "b"); Response member1 = pipe.srandmember("foo"); Response> allMembers = pipe.smembers("foo"); Response> members1 = pipe.srandmember("foo", 2); Response member2 = pipe.srandmember("bar"); Response> members2 = pipe.srandmember("bar", 2); pipe.sync(); assertThat(member1.get(), anyOf(equalTo("a"), equalTo("b"))); assertThat(allMembers.get(), containsInAnyOrder("a", "b")); assertThat(members1.get(), containsInAnyOrder("a", "b")); assertThat(member2.get(), nullValue()); assertThat(members2.get(), empty()); // Binary pipe.sadd(bfoo, ba); pipe.sadd(bfoo, bb); Response bmember1 = pipe.srandmember(bfoo); Response> bmembers1 = pipe.srandmember(bfoo, 2); Response bmember2 = pipe.srandmember(bbar); Response> bmembers2 = pipe.srandmember("bbar", 2); pipe.sync(); assertThat(bmember1.get(), anyOf(equalTo(ba), equalTo(bb))); assertThat(bmembers1.get(), containsInAnyOrder(ba, bb)); assertThat(bmember2.get(), nullValue()); assertThat(bmembers2.get(), empty()); } @Test public void sscan() { pipe.sadd("foo", "a", "b"); Response> result = pipe.sscan("foo", SCAN_POINTER_START); pipe.sync(); assertThat(result.get().getCursor(), equalTo(SCAN_POINTER_START)); assertThat(result.get().getResult(), not(empty())); // binary pipe.sadd(bfoo, ba, bb); Response> bResult = pipe.sscan(bfoo, SCAN_POINTER_START_BINARY); pipe.sync(); assertThat(bResult.get().getCursor(), equalTo(SCAN_POINTER_START)); assertThat(bResult.get().getResult(), not(empty())); } @Test public void sscanMatch() { ScanParams params = new ScanParams(); params.match("a*"); pipe.sadd("foo", "b", "a", "aa"); Response> result = pipe.sscan("foo", SCAN_POINTER_START, params); pipe.sync(); assertThat(result.get().getCursor(), equalTo(SCAN_POINTER_START)); assertThat(result.get().getResult(), not(empty())); // binary pipe.sadd(bfoo, bbar1, bbar2, bbar3); params = new ScanParams(); params.match(bbarstar); Response> bResult = pipe.sscan(bfoo, SCAN_POINTER_START_BINARY, params); pipe.sync(); assertThat(bResult.get().getCursor(), equalTo(SCAN_POINTER_START)); assertThat(bResult.get().getResult(), not(empty())); } @Test public void sscanCount() { ScanParams params = new ScanParams(); params.count(2); pipe.sadd("foo", "a1", "a2", "a3", "a4", "a5"); Response> result = pipe.sscan("foo", SCAN_POINTER_START, params); pipe.sync(); assertThat(result.get().getResult(), not(empty())); // binary pipe.sadd(bfoo, bbar1, bbar2, bbar3); params = new ScanParams(); params.count(2); Response> bResult = pipe.sscan(bfoo, SCAN_POINTER_START_BINARY, params); pipe.sync(); assertThat(bResult.get().getResult(), not(empty())); } } ================================================ FILE: src/test/java/redis/clients/jedis/commands/unified/pipeline/SortedSetPipelineCommandsTest.java ================================================ package redis.clients.jedis.commands.unified.pipeline; import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.closeTo; import static org.hamcrest.Matchers.contains; import static org.hamcrest.Matchers.containsInAnyOrder; import static org.hamcrest.Matchers.empty; import static org.hamcrest.Matchers.equalTo; import static org.hamcrest.Matchers.hasSize; import static org.hamcrest.Matchers.in; import static org.hamcrest.Matchers.not; import static org.hamcrest.Matchers.nullValue; import static org.junit.jupiter.api.Assertions.assertEquals; import static redis.clients.jedis.params.ScanParams.SCAN_POINTER_START; import static redis.clients.jedis.params.ScanParams.SCAN_POINTER_START_BINARY; import java.util.Arrays; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.stream.Collectors; import io.redis.test.annotations.ConditionalOnEnv; import io.redis.test.annotations.SinceRedisVersion; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Tag; import org.junit.jupiter.params.ParameterizedClass; import org.junit.jupiter.params.provider.MethodSource; import redis.clients.jedis.RedisProtocol; import redis.clients.jedis.Response; import redis.clients.jedis.args.SortedSetOption; import redis.clients.jedis.params.ScanParams; import redis.clients.jedis.params.ZAddParams; import redis.clients.jedis.params.ZIncrByParams; import redis.clients.jedis.params.ZParams; import redis.clients.jedis.params.ZRangeParams; import redis.clients.jedis.resps.ScanResult; import redis.clients.jedis.resps.Tuple; import redis.clients.jedis.util.AssertUtil; import redis.clients.jedis.util.KeyValue; import redis.clients.jedis.util.SafeEncoder; import redis.clients.jedis.util.TestEnvUtil; @ParameterizedClass @MethodSource("redis.clients.jedis.commands.CommandsTestsParameters#respVersions") @Tag("integration") public class SortedSetPipelineCommandsTest extends PipelineCommandsTestBase { final byte[] bfoo = { 0x01, 0x02, 0x03, 0x04 }; final byte[] bbar = { 0x05, 0x06, 0x07, 0x08 }; final byte[] bcar = { 0x09, 0x0A, 0x0B, 0x0C }; final byte[] ba = { 0x0A }; final byte[] bb = { 0x0B }; final byte[] bc = { 0x0C }; final byte[] bInclusiveB = { 0x5B, 0x0B }; final byte[] bExclusiveC = { 0x28, 0x0C }; final byte[] bLexMinusInf = { 0x2D }; final byte[] bLexPlusInf = { 0x2B }; final byte[] bbar1 = { 0x05, 0x06, 0x07, 0x08, 0x0A }; final byte[] bbar2 = { 0x05, 0x06, 0x07, 0x08, 0x0B }; final byte[] bbar3 = { 0x05, 0x06, 0x07, 0x08, 0x0C }; final byte[] bbarstar = { 0x05, 0x06, 0x07, 0x08, '*' }; public SortedSetPipelineCommandsTest(RedisProtocol protocol) { super(protocol); } @Test public void zadd() { pipe.zadd("foo", 1d, "a"); pipe.zadd("foo", 10d, "b"); pipe.zadd("foo", 0.1d, "c"); pipe.zadd("foo", 2d, "a"); // Binary pipe.zadd(bfoo, 1d, ba); pipe.zadd(bfoo, 10d, bb); pipe.zadd(bfoo, 0.1d, bc); pipe.zadd(bfoo, 2d, ba); assertThat(pipe.syncAndReturnAll(), contains( 1L, 1L, 1L, 0L, 1L, 1L, 1L, 0L )); } @Test public void zaddWithParams() { pipe.del("foo"); // xx: never add new member pipe.zadd("foo", 1d, "a", ZAddParams.zAddParams().xx()); pipe.zadd("foo", 1d, "a"); // nx: never update current member pipe.zadd("foo", 2d, "a", ZAddParams.zAddParams().nx()); pipe.zscore("foo", "a"); Map scoreMembers = new HashMap(); scoreMembers.put("a", 2d); scoreMembers.put("b", 1d); // ch: return count of members not only added, but also updated pipe.zadd("foo", scoreMembers, ZAddParams.zAddParams().ch()); assertThat(pipe.syncAndReturnAll(), contains( 0L, 0L, 1L, 0L, 1d, 2L )); // lt: only update existing elements if the new score is less than the current score. pipe.zadd("foo", 3d, "a", ZAddParams.zAddParams().lt()); pipe.zscore("foo", "a"); pipe.zadd("foo", 1d, "a", ZAddParams.zAddParams().lt()); pipe.zscore("foo", "a"); assertThat(pipe.syncAndReturnAll(), contains( 0L, 2d, 0L, 1d )); // gt: only update existing elements if the new score is greater than the current score. pipe.zadd("foo", 0d, "b", ZAddParams.zAddParams().gt()); pipe.zscore("foo", "b"); pipe.zadd("foo", 2d, "b", ZAddParams.zAddParams().gt()); pipe.zscore("foo", "b"); assertThat(pipe.syncAndReturnAll(), contains( 0L, 1d, 0L, 2d )); // incr: don't update already existing elements. pipe.zaddIncr("foo", 1d, "b", ZAddParams.zAddParams().nx()); pipe.zscore("foo", "b"); // incr: update elements that already exist. pipe.zaddIncr("foo", 1d, "b", ZAddParams.zAddParams().xx()); pipe.zscore("foo", "b"); assertThat(pipe.syncAndReturnAll(), contains( nullValue(), equalTo(2d), equalTo(3d), equalTo(3d) )); // binary pipe.del(bfoo); // xx: never add new member pipe.zadd(bfoo, 1d, ba, ZAddParams.zAddParams().xx()); pipe.zadd(bfoo, 1d, ba); // nx: never update current member pipe.zadd(bfoo, 2d, ba, ZAddParams.zAddParams().nx()); pipe.zscore(bfoo, ba); Map binaryScoreMembers = new HashMap<>(); binaryScoreMembers.put(ba, 2d); binaryScoreMembers.put(bb, 1d); // ch: return count of members not only added, but also updated pipe.zadd(bfoo, binaryScoreMembers, ZAddParams.zAddParams().ch()); assertThat(pipe.syncAndReturnAll(), contains( 0L, 0L, 1L, 0L, 1d, 2L )); // lt: only update existing elements if the new score is less than the current score. pipe.zadd(bfoo, 3d, ba, ZAddParams.zAddParams().lt()); pipe.zscore(bfoo, ba); pipe.zadd(bfoo, 1d, ba, ZAddParams.zAddParams().lt()); pipe.zscore(bfoo, ba); assertThat(pipe.syncAndReturnAll(), contains( 0L, 2d, 0L, 1d )); // gt: only update existing elements if the new score is greater than the current score. pipe.zadd(bfoo, 0d, bb, ZAddParams.zAddParams().gt()); pipe.zscore(bfoo, bb); pipe.zadd(bfoo, 2d, bb, ZAddParams.zAddParams().gt()); pipe.zscore(bfoo, bb); assertThat(pipe.syncAndReturnAll(), contains( 0L, 1d, 0L, 2d )); // incr: don't update already existing elements. pipe.zaddIncr(bfoo, 1d, bb, ZAddParams.zAddParams().nx()); pipe.zscore(bfoo, bb); // incr: update elements that already exist. pipe.zaddIncr(bfoo, 1d, bb, ZAddParams.zAddParams().xx()); pipe.zscore(bfoo, bb); assertThat(pipe.syncAndReturnAll(), contains( nullValue(), equalTo(2d), equalTo(3d), equalTo(3d) )); } @Test public void zrange() { pipe.zadd("foo", 1d, "a"); pipe.zadd("foo", 10d, "b"); pipe.zadd("foo", 0.1d, "c"); pipe.zadd("foo", 2d, "a"); Response> range1 = pipe.zrange("foo", 0, 1); Response> range2 = pipe.zrange("foo", 0, 100); pipe.sync(); assertThat(range1.get(), contains("c", "a")); assertThat(range2.get(), contains("c", "a", "b")); // Binary pipe.zadd(bfoo, 1d, ba); pipe.zadd(bfoo, 10d, bb); pipe.zadd(bfoo, 0.1d, bc); pipe.zadd(bfoo, 2d, ba); Response> brange1 = pipe.zrange(bfoo, 0, 1); Response> brange2 = pipe.zrange(bfoo, 0, 100); pipe.sync(); assertThat(brange1.get(), contains(bc, ba)); assertThat(brange2.get(), contains(bc, ba, bb)); } @Test public void zrangeByLex() { pipe.zadd("foo", 1, "aa"); pipe.zadd("foo", 1, "c"); pipe.zadd("foo", 1, "bb"); pipe.zadd("foo", 1, "d"); // exclusive aa ~ inclusive c Response> range1 = pipe.zrangeByLex("foo", "(aa", "[c"); // with LIMIT Response> range2 = pipe.zrangeByLex("foo", "-", "+", 1, 2); pipe.sync(); assertThat(range1.get(), contains("bb", "c")); assertThat(range2.get(), contains("bb", "c")); } @Test public void zrangeByLexBinary() { pipe.zadd(bfoo, 1, ba); pipe.zadd(bfoo, 1, bc); pipe.zadd(bfoo, 1, bb); Response> brange1 = pipe.zrangeByLex(bfoo, bInclusiveB, bExclusiveC); // with LIMIT Response> brange2 = pipe.zrangeByLex(bfoo, bLexMinusInf, bLexPlusInf, 0, 2); pipe.sync(); assertThat(brange1.get(), contains(bb)); assertThat(brange2.get(), contains(ba, bb)); } @Test public void zrevrangeByLex() { pipe.zadd("foo", 1, "aa"); pipe.zadd("foo", 1, "c"); pipe.zadd("foo", 1, "bb"); pipe.zadd("foo", 1, "d"); // exclusive aa ~ inclusive c Response> range1 = pipe.zrevrangeByLex("foo", "[c", "(aa"); // with LIMIT Response> range2 = pipe.zrevrangeByLex("foo", "+", "-", 1, 2); pipe.sync(); assertThat(range1.get(), contains("c", "bb")); assertThat(range2.get(), contains("c", "bb")); } @Test public void zrevrangeByLexBinary() { // binary pipe.zadd(bfoo, 1, ba); pipe.zadd(bfoo, 1, bc); pipe.zadd(bfoo, 1, bb); Response> brange1 = pipe.zrevrangeByLex(bfoo, bExclusiveC, bInclusiveB); // with LIMIT Response> brange2 = pipe.zrevrangeByLex(bfoo, bLexPlusInf, bLexMinusInf, 0, 2); pipe.sync(); assertThat(brange1.get(), contains(bb)); assertThat(brange2.get(), contains(bc, bb)); } @Test public void zrevrange() { pipe.zadd("foo", 1d, "a"); pipe.zadd("foo", 10d, "b"); pipe.zadd("foo", 0.1d, "c"); pipe.zadd("foo", 2d, "a"); Response> range1 = pipe.zrevrange("foo", 0, 1); Response> range2 = pipe.zrevrange("foo", 0, 100); pipe.sync(); assertThat(range1.get(), contains("b", "a")); assertThat(range2.get(), contains("b", "a", "c")); // Binary pipe.zadd(bfoo, 1d, ba); pipe.zadd(bfoo, 10d, bb); pipe.zadd(bfoo, 0.1d, bc); pipe.zadd(bfoo, 2d, ba); Response> brange1 = pipe.zrevrange(bfoo, 0, 1); Response> brange2 = pipe.zrevrange(bfoo, 0, 100); pipe.sync(); assertThat(brange1.get(), contains(bb, ba)); assertThat(brange2.get(), contains(bb, ba, bc)); } @Test public void zrangeParams() { pipe.zadd("foo", 1, "aa"); pipe.zadd("foo", 1, "c"); pipe.zadd("foo", 1, "bb"); pipe.zadd("foo", 1, "d"); Response> range1 = pipe.zrange("foo", ZRangeParams.zrangeByLexParams("[c", "(aa").rev()); Response> range2 = pipe.zrangeWithScores("foo", ZRangeParams.zrangeByScoreParams(0, 1)); pipe.sync(); assertThat(range1.get(), contains("c", "bb")); assertThat(range2.get().stream().map(Tuple::getElement).collect(Collectors.toList()), contains("aa", "bb", "c", "d")); // Binary pipe.zadd(bfoo, 1, ba); pipe.zadd(bfoo, 1, bc); pipe.zadd(bfoo, 1, bb); Response> brange1 = pipe.zrange(bfoo, ZRangeParams.zrangeByLexParams(bExclusiveC, bInclusiveB).rev()); Response> brange2 = pipe.zrangeWithScores(bfoo, ZRangeParams.zrangeByScoreParams(0, 1).limit(0, 3)); pipe.sync(); assertThat(brange1.get(), contains(bb)); assertThat(brange2.get().stream().map(Tuple::getBinaryElement).collect(Collectors.toList()), contains(ba, bb, bc)); } @Test @ConditionalOnEnv(value = TestEnvUtil.ENV_REDIS_ENTERPRISE, enabled = false) public void zrangestore() { pipe.zadd("foo", 1, "aa"); pipe.zadd("foo", 2, "c"); pipe.zadd("foo", 3, "bb"); Response stored = pipe.zrangestore("bar", "foo", ZRangeParams.zrangeByScoreParams(1, 2)); Response> range = pipe.zrange("bar", 0, -1); pipe.sync(); assertThat(stored.get(), equalTo(2L)); assertThat(range.get(), contains("aa", "c")); // Binary pipe.zadd(bfoo, 1d, ba); pipe.zadd(bfoo, 10d, bb); pipe.zadd(bfoo, 0.1d, bc); pipe.zadd(bfoo, 2d, ba); Response bstored = pipe.zrangestore(bbar, bfoo, ZRangeParams.zrangeParams(0, 1).rev()); Response> brange = pipe.zrevrange(bbar, 0, 1); pipe.sync(); assertThat(bstored.get(), equalTo(2L)); assertThat(brange.get(), contains(bb, ba)); } @Test public void zrem() { pipe.zadd("foo", 1d, "a"); pipe.zadd("foo", 2d, "b"); Response result1 = pipe.zrem("foo", "a"); Response> range = pipe.zrange("foo", 0, 100); Response result2 = pipe.zrem("foo", "bar"); pipe.sync(); assertThat(result1.get(), equalTo(1L)); assertThat(range.get(), contains("b")); assertThat(result2.get(), equalTo(0L)); // Binary pipe.zadd(bfoo, 1d, ba); pipe.zadd(bfoo, 2d, bb); Response bresult1 = pipe.zrem(bfoo, ba); Response> brange = pipe.zrange(bfoo, 0, 100); Response bresult2 = pipe.zrem(bfoo, bbar); pipe.sync(); assertThat(bresult1.get(), equalTo(1L)); assertThat(brange.get(), contains(bb)); assertThat(bresult2.get(), equalTo(0L)); } @Test public void zincrby() { pipe.zadd("foo", 1d, "a"); pipe.zadd("foo", 2d, "b"); Response result = pipe.zincrby("foo", 2d, "a"); Response> range = pipe.zrange("foo", 0, 100); pipe.sync(); assertThat(result.get(), closeTo(3d, 0.001)); assertThat(range.get(), contains("b", "a")); // Binary pipe.zadd(bfoo, 1d, ba); pipe.zadd(bfoo, 2d, bb); Response bresult = pipe.zincrby(bfoo, 2d, ba); Response> brange = pipe.zrange(bfoo, 0, 100); pipe.sync(); assertThat(bresult.get(), closeTo(3d, 0.001)); assertThat(brange.get(), contains(bb, ba)); } @Test public void zincrbyWithParams() { pipe.del("foo"); // xx: never add new member Response result1 = pipe.zincrby("foo", 2d, "a", ZIncrByParams.zIncrByParams().xx()); pipe.zadd("foo", 2d, "a"); // nx: never update current member Response result2 = pipe.zincrby("foo", 1d, "a", ZIncrByParams.zIncrByParams().nx()); Response result3 = pipe.zscore("foo", "a"); pipe.sync(); assertThat(result1.get(), nullValue()); assertThat(result2.get(), nullValue()); assertThat(result3.get(), closeTo(2d, 0.001)); // Binary pipe.del(bfoo); // xx: never add new member Response bresult1 = pipe.zincrby(bfoo, 2d, ba, ZIncrByParams.zIncrByParams().xx()); pipe.zadd(bfoo, 2d, ba); // nx: never update current member Response bresult2 = pipe.zincrby(bfoo, 1d, ba, ZIncrByParams.zIncrByParams().nx()); Response bresult3 = pipe.zscore(bfoo, ba); pipe.sync(); assertThat(bresult1.get(), nullValue()); assertThat(bresult2.get(), nullValue()); assertThat(bresult3.get(), closeTo(2d, 0.001)); } @Test public void zrank() { pipe.zadd("foo", 1d, "a"); pipe.zadd("foo", 2d, "b"); pipe.zrank("foo", "a"); pipe.zrank("foo", "b"); pipe.zrank("car", "b"); assertThat(pipe.syncAndReturnAll(), contains( equalTo(1L), equalTo(1L), equalTo(0L), equalTo(1L), nullValue() )); // Binary pipe.zadd(bfoo, 1d, ba); pipe.zadd(bfoo, 2d, bb); pipe.zrank(bfoo, ba); pipe.zrank(bfoo, bb); pipe.zrank(bcar, bb); assertThat(pipe.syncAndReturnAll(), contains( equalTo(1L), equalTo(1L), equalTo(0L), equalTo(1L), nullValue() )); } @Test @SinceRedisVersion(value="7.2.0", message = "Starting with Redis version 7.2.0: Added the optional WITHSCORE argument.") public void zrankWithScore() { pipe.zadd("foo", 1d, "a"); pipe.zadd("foo", 2d, "b"); Response> keyValue1 = pipe.zrankWithScore("foo", "a"); Response> keyValue2 = pipe.zrankWithScore("foo", "b"); Response> keyValue3 = pipe.zrankWithScore("car", "b"); pipe.sync(); assertThat(keyValue1.get(), equalTo(new KeyValue<>(0L, 1d))); assertThat(keyValue2.get(), equalTo(new KeyValue<>(1L, 2d))); assertThat(keyValue3.get(), nullValue()); // Binary pipe.zadd(bfoo, 1d, ba); pipe.zadd(bfoo, 2d, bb); Response> bKeyValue1 = pipe.zrankWithScore(bfoo, ba); Response> bKeyValue2 = pipe.zrankWithScore(bfoo, bb); Response> bKeyValue3 = pipe.zrankWithScore(bcar, bb); pipe.sync(); assertThat(bKeyValue1.get(), equalTo(new KeyValue<>(0L, 1d))); assertThat(bKeyValue2.get(), equalTo(new KeyValue<>(1L, 2d))); assertThat(bKeyValue3.get(), nullValue()); } @Test public void zrevrank() { pipe.zadd("foo", 1d, "a"); pipe.zadd("foo", 2d, "b"); pipe.zrevrank("foo", "a"); pipe.zrevrank("foo", "b"); assertThat(pipe.syncAndReturnAll(), contains( equalTo(1L), equalTo(1L), equalTo(1L), equalTo(0L) )); // Binary pipe.zadd(bfoo, 1d, ba); pipe.zadd(bfoo, 2d, bb); pipe.zrevrank(bfoo, ba); pipe.zrevrank(bfoo, bb); assertThat(pipe.syncAndReturnAll(), contains( equalTo(1L), equalTo(1L), equalTo(1L), equalTo(0L) )); } @Test public void zrangeWithScores() { pipe.zadd("foo", 1d, "a"); pipe.zadd("foo", 10d, "b"); pipe.zadd("foo", 0.1d, "c"); pipe.zadd("foo", 2d, "a"); Response> range1 = pipe.zrangeWithScores("foo", 0, 1); Response> range2 = pipe.zrangeWithScores("foo", 0, 100); pipe.sync(); assertThat(range1.get(), contains( new Tuple("c", 0.1d), new Tuple("a", 2d) )); assertThat(range2.get(), contains( new Tuple("c", 0.1d), new Tuple("a", 2d), new Tuple("b", 10d) )); // Binary pipe.zadd(bfoo, 1d, ba); pipe.zadd(bfoo, 10d, bb); pipe.zadd(bfoo, 0.1d, bc); pipe.zadd(bfoo, 2d, ba); Response> brange1 = pipe.zrangeWithScores(bfoo, 0, 1); Response> brange2 = pipe.zrangeWithScores(bfoo, 0, 100); pipe.sync(); assertThat(brange1.get(), contains( new Tuple(bc, 0.1d), new Tuple(ba, 2d) )); assertThat(brange2.get(), contains( new Tuple(bc, 0.1d), new Tuple(ba, 2d), new Tuple(bb, 10d) )); } @Test public void zrevrangeWithScores() { pipe.zadd("foo", 1d, "a"); pipe.zadd("foo", 10d, "b"); pipe.zadd("foo", 0.1d, "c"); pipe.zadd("foo", 2d, "a"); Response> range1 = pipe.zrevrangeWithScores("foo", 0, 1); Response> range2 = pipe.zrevrangeWithScores("foo", 0, 100); pipe.sync(); assertThat(range1.get(), contains( new Tuple("b", 10d), new Tuple("a", 2d) )); assertThat(range2.get(), contains( new Tuple("b", 10d), new Tuple("a", 2d), new Tuple("c", 0.1d) )); // Binary pipe.zadd(bfoo, 1d, ba); pipe.zadd(bfoo, 10d, bb); pipe.zadd(bfoo, 0.1d, bc); pipe.zadd(bfoo, 2d, ba); Response> brange1 = pipe.zrevrangeWithScores(bfoo, 0, 1); Response> brange2 = pipe.zrevrangeWithScores(bfoo, 0, 100); pipe.sync(); assertThat(brange1.get(), contains( new Tuple(bb, 10d), new Tuple(ba, 2d) )); assertThat(brange2.get(), contains( new Tuple(bb, 10d), new Tuple(ba, 2d), new Tuple(bc, 0.1d) )); } @Test public void zcard() { pipe.zadd("foo", 1d, "a"); pipe.zadd("foo", 10d, "b"); pipe.zadd("foo", 0.1d, "c"); pipe.zadd("foo", 2d, "a"); Response result = pipe.zcard("foo"); pipe.sync(); assertThat(result.get(), equalTo(3L)); // Binary pipe.zadd(bfoo, 1d, ba); pipe.zadd(bfoo, 10d, bb); pipe.zadd(bfoo, 0.1d, bc); pipe.zadd(bfoo, 2d, ba); Response bresult = pipe.zcard(bfoo); pipe.sync(); assertThat(bresult.get(), equalTo(3L)); } @Test public void zscore() { pipe.zadd("foo", 1d, "a"); pipe.zadd("foo", 10d, "b"); pipe.zadd("foo", 0.1d, "c"); pipe.zadd("foo", 2d, "a"); Response result1 = pipe.zscore("foo", "b"); Response result2 = pipe.zscore("foo", "c"); Response result3 = pipe.zscore("foo", "s"); pipe.sync(); assertThat(result1.get(), closeTo(10d, 0.001)); assertThat(result2.get(), closeTo(0.1d, 0.001)); assertThat(result3.get(), nullValue()); // Binary pipe.zadd(bfoo, 1d, ba); pipe.zadd(bfoo, 10d, bb); pipe.zadd(bfoo, 0.1d, bc); pipe.zadd(bfoo, 2d, ba); Response bresult1 = pipe.zscore(bfoo, bb); Response bresult2 = pipe.zscore(bfoo, bc); Response bresult3 = pipe.zscore(bfoo, SafeEncoder.encode("s")); pipe.sync(); assertThat(bresult1.get(), closeTo(10d, 0.001)); assertThat(bresult2.get(), closeTo(0.1d, 0.001)); assertThat(bresult3.get(), nullValue()); } @Test public void zmscore() { pipe.zadd("foo", 1d, "a"); pipe.zadd("foo", 10d, "b"); pipe.zadd("foo", 0.1d, "c"); pipe.zadd("foo", 2d, "a"); Response> score = pipe.zmscore("foo", "b", "c", "s"); pipe.sync(); assertThat(score.get(), contains(10d, 0.1d, null)); // Binary pipe.zadd(bfoo, 1d, ba); pipe.zadd(bfoo, 10d, bb); pipe.zadd(bfoo, 0.1d, bc); pipe.zadd(bfoo, 2d, ba); Response> bscore = pipe.zmscore(bfoo, bb, bc, SafeEncoder.encode("s")); pipe.sync(); assertThat(bscore.get(), contains(10d, 0.1d, null)); } @Test public void zpopmax() { pipe.zadd("foo", 1d, "a"); pipe.zadd("foo", 10d, "b"); pipe.zadd("foo", 0.1d, "c"); pipe.zadd("foo", 2d, "d"); pipe.sync(); pipe.zpopmax("foo"); pipe.zpopmax("foo"); pipe.zpopmax("foo"); pipe.zpopmax("foo"); pipe.zpopmax("foo"); assertThat(pipe.syncAndReturnAll(), contains( equalTo(new Tuple("b", 10d)), equalTo(new Tuple("d", 2d)), equalTo(new Tuple("a", 1d)), equalTo(new Tuple("c", 0.1d)), nullValue() )); // Binary pipe.zadd(bfoo, 1d, ba); pipe.zadd(bfoo, 10d, bb); pipe.zadd(bfoo, 0.1d, bc); pipe.zadd(bfoo, 2d, ba); pipe.sync(); pipe.zpopmax(bfoo); pipe.zpopmax(bfoo); pipe.zpopmax(bfoo); pipe.zpopmax(bfoo); assertThat(pipe.syncAndReturnAll(), contains( equalTo(new Tuple(bb, 10d)), equalTo(new Tuple(ba, 2d)), equalTo(new Tuple(bc, 0.1d)), nullValue() )); } @Test public void zpopmaxWithCount() { pipe.zadd("foo", 1d, "a"); pipe.zadd("foo", 10d, "b"); pipe.zadd("foo", 0.1d, "c"); pipe.zadd("foo", 2d, "d"); pipe.zadd("foo", 0.03, "e"); Response> actual1 = pipe.zpopmax("foo", 2); Response> actual2 = pipe.zpopmax("foo", 3); Response> actual3 = pipe.zpopmax("foo", 1); pipe.sync(); assertThat(actual1.get(), contains( new Tuple("b", 10d), new Tuple("d", 2d) )); assertThat(actual2.get(), contains( new Tuple("a", 1d), new Tuple("c", 0.1d), new Tuple("e", 0.03d) )); assertThat(actual3.get(), empty()); // Binary pipe.zadd(bfoo, 1d, ba); pipe.zadd(bfoo, 10d, bb); pipe.zadd(bfoo, 0.1d, bc); pipe.zadd(bfoo, 2d, ba); Response> bactual1 = pipe.zpopmax(bfoo, 1); Response> bactual2 = pipe.zpopmax(bfoo, 1); Response> bactual3 = pipe.zpopmax(bfoo, 1); Response> bactual4 = pipe.zpopmax(bfoo, 1); pipe.sync(); assertThat(bactual1.get(), contains( new Tuple(bb, 10d) )); assertThat(bactual2.get(), contains( new Tuple(ba, 2d) )); assertThat(bactual3.get(), contains( new Tuple(bc, 0.1d) )); assertThat(bactual4.get(), empty()); } @Test public void zpopmin() { pipe.zadd("foo", 1d, "a", ZAddParams.zAddParams().nx()); pipe.zadd("foo", 10d, "b", ZAddParams.zAddParams().nx()); pipe.zadd("foo", 0.1d, "c", ZAddParams.zAddParams().nx()); pipe.zadd("foo", 2d, "a", ZAddParams.zAddParams().nx()); Response> range = pipe.zpopmin("foo", 2); Response item = pipe.zpopmin("foo"); pipe.sync(); assertThat(range.get(), contains( new Tuple("c", 0.1d), new Tuple("a", 1d) )); assertThat(item.get(), equalTo(new Tuple("b", 10d))); // Binary pipe.zadd(bfoo, 1d, ba); pipe.zadd(bfoo, 10d, bb); pipe.zadd(bfoo, 0.1d, bc); pipe.zadd(bfoo, 2d, ba); Response> brange = pipe.zpopmin(bfoo, 2); Response bitem = pipe.zpopmin(bfoo); pipe.sync(); assertThat(brange.get(), contains( new Tuple(bc, 0.1d), new Tuple(ba, 2d) )); assertThat(bitem.get(), equalTo(new Tuple(bb, 10d))); } @Test public void zcount() { pipe.zadd("foo", 1d, "a"); pipe.zadd("foo", 10d, "b"); pipe.zadd("foo", 0.1d, "c"); pipe.zadd("foo", 2d, "a"); pipe.sync(); pipe.zcount("foo", 0.01d, 2.1d); pipe.zcount("foo", "(0.01", "+inf"); assertThat(pipe.syncAndReturnAll(), contains( 2L, 3L )); // Binary pipe.zadd(bfoo, 1d, ba); pipe.zadd(bfoo, 10d, bb); pipe.zadd(bfoo, 0.1d, bc); pipe.zadd(bfoo, 2d, ba); pipe.sync(); pipe.zcount(bfoo, 0.01d, 2.1d); pipe.zcount(bfoo, SafeEncoder.encode("(0.01"), SafeEncoder.encode("+inf")); assertThat(pipe.syncAndReturnAll(), contains( 2L, 3L )); } @Test public void zlexcount() { pipe.zadd("foo", 1, "a"); pipe.zadd("foo", 1, "b"); pipe.zadd("foo", 1, "c"); pipe.zadd("foo", 1, "aa"); pipe.sync(); pipe.zlexcount("foo", "[aa", "(c"); pipe.zlexcount("foo", "-", "+"); pipe.zlexcount("foo", "-", "(c"); pipe.zlexcount("foo", "[aa", "+"); assertThat(pipe.syncAndReturnAll(), contains( 2L, 4L, 3L, 3L )); } @Test public void zlexcountBinary() { // Binary pipe.zadd(bfoo, 1, ba); pipe.zadd(bfoo, 1, bc); pipe.zadd(bfoo, 1, bb); pipe.sync(); pipe.zlexcount(bfoo, bInclusiveB, bExclusiveC); pipe.zlexcount(bfoo, bLexMinusInf, bLexPlusInf); assertThat(pipe.syncAndReturnAll(), contains( 1L, 3L )); } @Test public void zrangebyscore() { pipe.zadd("foo", 1d, "a"); pipe.zadd("foo", 10d, "b"); pipe.zadd("foo", 0.1d, "c"); pipe.zadd("foo", 2d, "a"); Response> range1 = pipe.zrangeByScore("foo", 0d, 2d); Response> range2 = pipe.zrangeByScore("foo", 0d, 2d, 0, 1); Response> range3 = pipe.zrangeByScore("foo", 0d, 2d, 1, 1); Response> range4 = pipe.zrangeByScore("foo", "-inf", "(2"); pipe.sync(); assertThat(range1.get(), contains("c", "a")); assertThat(range2.get(), contains("c")); assertThat(range3.get(), contains("a")); assertThat(range4.get(), contains("c")); // Binary pipe.zadd(bfoo, 1d, ba); pipe.zadd(bfoo, 10d, bb); pipe.zadd(bfoo, 0.1d, bc); pipe.zadd(bfoo, 2d, ba); Response> brange1 = pipe.zrangeByScore(bfoo, 0d, 2d); Response> brange2 = pipe.zrangeByScore(bfoo, 0d, 2d, 0, 1); Response> brange3 = pipe.zrangeByScore(bfoo, 0d, 2d, 1, 1); Response> brange4 = pipe.zrangeByScore(bfoo, SafeEncoder.encode("-inf"), SafeEncoder.encode("(2")); pipe.sync(); assertThat(brange1.get(), contains(bc, ba)); assertThat(brange2.get(), contains(bc)); assertThat(brange3.get(), contains(ba)); assertThat(brange4.get(), contains(bc)); } @Test public void zrevrangebyscore() { pipe.zadd("foo", 1.0d, "a"); pipe.zadd("foo", 2.0d, "b"); pipe.zadd("foo", 3.0d, "c"); pipe.zadd("foo", 4.0d, "d"); pipe.zadd("foo", 5.0d, "e"); Response> range1 = pipe.zrevrangeByScore("foo", 3d, Double.NEGATIVE_INFINITY, 0, 1); Response> range2 = pipe.zrevrangeByScore("foo", 3.5d, Double.NEGATIVE_INFINITY, 0, 2); Response> range3 = pipe.zrevrangeByScore("foo", 3.5d, Double.NEGATIVE_INFINITY, 1, 1); Response> range4 = pipe.zrevrangeByScore("foo", 4d, 2d); Response> range5 = pipe.zrevrangeByScore("foo", "4", "2", 0, 2); Response> range6 = pipe.zrevrangeByScore("foo", "+inf", "(4"); pipe.sync(); assertThat(range1.get(), contains("c")); assertThat(range2.get(), contains("c", "b")); assertThat(range3.get(), contains("b")); assertThat(range4.get(), contains("d", "c", "b")); assertThat(range5.get(), contains("d", "c")); assertThat(range6.get(), contains("e")); // Binary pipe.zadd(bfoo, 1d, ba); pipe.zadd(bfoo, 10d, bb); pipe.zadd(bfoo, 0.1d, bc); pipe.zadd(bfoo, 2d, ba); Response> brange1 = pipe.zrevrangeByScore(bfoo, 2d, 0d); Response> brange2 = pipe.zrevrangeByScore(bfoo, 2d, 0d, 0, 1); Response> brange3 = pipe.zrevrangeByScore(bfoo, SafeEncoder.encode("+inf"), SafeEncoder.encode("(2")); Response> brange4 = pipe.zrevrangeByScore(bfoo, 2d, 0d, 1, 1); pipe.sync(); assertThat(brange1.get(), contains(ba, bc)); assertThat(brange2.get(), contains(ba)); assertThat(brange3.get(), contains(bb)); assertThat(brange4.get(), contains(bc)); } @Test public void zrangebyscoreWithScores() { pipe.zadd("foo", 1d, "a"); pipe.zadd("foo", 10d, "b"); pipe.zadd("foo", 0.1d, "c"); pipe.zadd("foo", 2d, "a"); Response> range1 = pipe.zrangeByScoreWithScores("foo", 0d, 2d); Response> range2 = pipe.zrangeByScoreWithScores("foo", 0d, 2d, 0, 1); Response> range3 = pipe.zrangeByScoreWithScores("foo", 0d, 2d, 1, 1); pipe.sync(); assertThat(range1.get(), contains( new Tuple("c", 0.1d), new Tuple("a", 2d) )); assertThat(range2.get(), contains( new Tuple("c", 0.1d) )); assertThat(range3.get(), contains( new Tuple("a", 2d) )); // Binary pipe.zadd(bfoo, 1d, ba); pipe.zadd(bfoo, 10d, bb); pipe.zadd(bfoo, 0.1d, bc); pipe.zadd(bfoo, 2d, ba); Response> brange1 = pipe.zrangeByScoreWithScores(bfoo, 0d, 2d); Response> brange2 = pipe.zrangeByScoreWithScores(bfoo, 0d, 2d, 0, 1); Response> brange3 = pipe.zrangeByScoreWithScores(bfoo, 0d, 2d, 1, 1); pipe.sync(); assertThat(brange1.get(), contains( new Tuple(bc, 0.1d), new Tuple(ba, 2d) )); assertThat(brange2.get(), contains( new Tuple(bc, 0.1d) )); assertThat(brange3.get(), contains( new Tuple(ba, 2d) )); } @Test public void zrevrangebyscoreWithScores() { pipe.zadd("foo", 1.0d, "a"); pipe.zadd("foo", 2.0d, "b"); pipe.zadd("foo", 3.0d, "c"); pipe.zadd("foo", 4.0d, "d"); pipe.zadd("foo", 5.0d, "e"); Response> range1 = pipe.zrevrangeByScoreWithScores("foo", 3d, Double.NEGATIVE_INFINITY, 0, 1); Response> range2 = pipe.zrevrangeByScoreWithScores("foo", 3.5d, Double.NEGATIVE_INFINITY, 0, 2); Response> range3 = pipe.zrevrangeByScoreWithScores("foo", 3.5d, Double.NEGATIVE_INFINITY, 1, 1); Response> range4 = pipe.zrevrangeByScoreWithScores("foo", 4d, 2d); pipe.sync(); assertThat(range1.get(), contains( new Tuple("c", 3.0d) )); assertThat(range2.get(), contains( new Tuple("c", 3.0d), new Tuple("b", 2.0d) )); assertThat(range3.get(), contains( new Tuple("b", 2.0d) )); assertThat(range4.get(), contains( new Tuple("d", 4.0d), new Tuple("c", 3.0d), new Tuple("b", 2.0d) )); // Binary pipe.zadd(bfoo, 1d, ba); pipe.zadd(bfoo, 10d, bb); pipe.zadd(bfoo, 0.1d, bc); pipe.zadd(bfoo, 2d, ba); Response> brange1 = pipe.zrevrangeByScoreWithScores(bfoo, 2d, 0d); Response> brange2 = pipe.zrevrangeByScoreWithScores(bfoo, 2d, 0d, 0, 1); Response> brange3 = pipe.zrevrangeByScoreWithScores(bfoo, 2d, 0d, 1, 1); pipe.sync(); assertThat(brange1.get(), contains( new Tuple(ba, 2d), new Tuple(bc, 0.1d) )); assertThat(brange2.get(), contains( new Tuple(ba, 2d) )); assertThat(brange3.get(), contains( new Tuple(bc, 0.1d) )); } @Test public void zremrangeByRank() { pipe.zadd("foo", 1d, "a"); pipe.zadd("foo", 10d, "b"); pipe.zadd("foo", 0.1d, "c"); pipe.zadd("foo", 2d, "a"); Response result = pipe.zremrangeByRank("foo", 0, 0); Response> items = pipe.zrange("foo", 0, 100); pipe.sync(); assertThat(result.get(), equalTo(1L)); assertThat(items.get(), contains("a", "b")); // Binary pipe.zadd(bfoo, 1d, ba); pipe.zadd(bfoo, 10d, bb); pipe.zadd(bfoo, 0.1d, bc); pipe.zadd(bfoo, 2d, ba); Response bresult = pipe.zremrangeByRank(bfoo, 0, 0); Response> bitems = pipe.zrange(bfoo, 0, 100); pipe.sync(); assertThat(bresult.get(), equalTo(1L)); assertThat(bitems.get(), contains(ba, bb)); } @Test public void zremrangeByScore() { pipe.zadd("foo", 1d, "a"); pipe.zadd("foo", 10d, "b"); pipe.zadd("foo", 0.1d, "c"); pipe.zadd("foo", 2d, "a"); Response result = pipe.zremrangeByScore("foo", 0, 2); Response> items = pipe.zrange("foo", 0, 100); pipe.sync(); assertThat(result.get(), equalTo(2L)); assertThat(items.get(), contains("b")); // Binary pipe.zadd(bfoo, 1d, ba); pipe.zadd(bfoo, 10d, bb); pipe.zadd(bfoo, 0.1d, bc); pipe.zadd(bfoo, 2d, ba); Response bresult = pipe.zremrangeByScore(bfoo, 0, 2); Response> bitems = pipe.zrange(bfoo, 0, 100); pipe.sync(); assertThat(bresult.get(), equalTo(2L)); assertThat(bitems.get(), contains(bb)); } @Test public void zremrangeByScoreExclusive() { pipe.zadd("foo", 1d, "a"); pipe.zadd("foo", 0d, "c"); pipe.zadd("foo", 2d, "b"); Response result = pipe.zremrangeByScore("foo", "(0", "(2"); pipe.sync(); assertThat(result.get(), equalTo(1L)); // Binary pipe.zadd(bfoo, 1d, ba); pipe.zadd(bfoo, 0d, bc); pipe.zadd(bfoo, 2d, bb); Response bresult = pipe.zremrangeByScore(bfoo, "(0".getBytes(), "(2".getBytes()); pipe.sync(); assertThat(bresult.get(), equalTo(1L)); } @Test public void zremrangeByLex() { pipe.zadd("foo", 1, "a"); pipe.zadd("foo", 1, "b"); pipe.zadd("foo", 1, "c"); pipe.zadd("foo", 1, "aa"); Response result = pipe.zremrangeByLex("foo", "[aa", "(c"); Response> items = pipe.zrangeByLex("foo", "-", "+"); pipe.sync(); assertThat(result.get(), equalTo(2L)); assertThat(items.get(), contains("a", "c")); } @Test public void zremrangeByLexBinary() { pipe.zadd(bfoo, 1, ba); pipe.zadd(bfoo, 1, bc); pipe.zadd(bfoo, 1, bb); Response bresult = pipe.zremrangeByLex(bfoo, bInclusiveB, bExclusiveC); Response> bitems = pipe.zrangeByLex(bfoo, bLexMinusInf, bLexPlusInf); pipe.sync(); assertThat(bresult.get(), equalTo(1L)); assertThat(bitems.get(), contains(ba, bc)); } @Test @ConditionalOnEnv(value = TestEnvUtil.ENV_REDIS_ENTERPRISE, enabled = false) public void zunion() { pipe.zadd("foo", 1, "a"); pipe.zadd("foo", 2, "b"); pipe.zadd("bar", 2, "a"); pipe.zadd("bar", 2, "b"); ZParams params = new ZParams(); params.weights(2, 2.5); params.aggregate(ZParams.Aggregate.SUM); Response> union1 = pipe.zunion(params, "foo", "bar"); Response> union2 = pipe.zunionWithScores(params, "foo", "bar"); pipe.sync(); assertThat(union1.get(), contains("a", "b")); assertThat(union2.get(), contains( new Tuple("a", new Double(7)), new Tuple("b", new Double(9)) )); // Binary pipe.zadd(bfoo, 1, ba); pipe.zadd(bfoo, 2, bb); pipe.zadd(bbar, 2, ba); pipe.zadd(bbar, 2, bb); Response> bunion1 = pipe.zunion(params, bfoo, bbar); Response> bunion2 = pipe.zunionWithScores(params, bfoo, bbar); pipe.sync(); assertThat(bunion1.get(), contains(ba, bb)); assertThat(bunion2.get(), contains( new Tuple(ba, new Double(7)), new Tuple(bb, new Double(9)) )); } @Test @ConditionalOnEnv(value = TestEnvUtil.ENV_REDIS_ENTERPRISE, enabled = false) public void zunionstore() { pipe.zadd("foo", 1, "a"); pipe.zadd("foo", 2, "b"); pipe.zadd("bar", 2, "a"); pipe.zadd("bar", 2, "b"); Response result = pipe.zunionstore("dst", "foo", "bar"); Response> items = pipe.zrangeWithScores("dst", 0, 100); pipe.sync(); assertThat(result.get(), equalTo(2L)); assertThat(items.get(), contains( new Tuple("a", new Double(3)), new Tuple("b", new Double(4)) )); // Binary pipe.zadd(bfoo, 1, ba); pipe.zadd(bfoo, 2, bb); pipe.zadd(bbar, 2, ba); pipe.zadd(bbar, 2, bb); Response bresult = pipe.zunionstore(SafeEncoder.encode("dst"), bfoo, bbar); Response> bitems = pipe.zrangeWithScores(SafeEncoder.encode("dst"), 0, 100); pipe.sync(); assertThat(bresult.get(), equalTo(2L)); assertThat(bitems.get(), contains( new Tuple(ba, new Double(3)), new Tuple(bb, new Double(4)) )); } @Test @ConditionalOnEnv(value = TestEnvUtil.ENV_REDIS_ENTERPRISE, enabled = false) public void zunionstoreParams() { pipe.zadd("foo", 1, "a"); pipe.zadd("foo", 2, "b"); pipe.zadd("bar", 2, "a"); pipe.zadd("bar", 2, "b"); ZParams params = new ZParams(); params.weights(2, 2.5); params.aggregate(ZParams.Aggregate.SUM); Response result = pipe.zunionstore("dst", params, "foo", "bar"); Response> items = pipe.zrangeWithScores("dst", 0, 100); pipe.sync(); assertThat(result.get(), equalTo(2L)); assertThat(items.get(), contains( new Tuple("a", new Double(7)), new Tuple("b", new Double(9)) )); // Binary pipe.zadd(bfoo, 1, ba); pipe.zadd(bfoo, 2, bb); pipe.zadd(bbar, 2, ba); pipe.zadd(bbar, 2, bb); ZParams bparams = new ZParams(); bparams.weights(2, 2.5); bparams.aggregate(ZParams.Aggregate.SUM); Response bresult = pipe.zunionstore(SafeEncoder.encode("dst"), bparams, bfoo, bbar); Response> bitems = pipe.zrangeWithScores(SafeEncoder.encode("dst"), 0, 100); pipe.sync(); assertThat(bresult.get(), equalTo(2L)); assertThat(bitems.get(), contains( new Tuple(ba, new Double(7)), new Tuple(bb, new Double(9)) )); } @Test @ConditionalOnEnv(value = TestEnvUtil.ENV_REDIS_ENTERPRISE, enabled = false) public void zinter() { pipe.zadd("foo", 1, "a"); pipe.zadd("foo", 2, "b"); pipe.zadd("bar", 2, "a"); ZParams params = new ZParams(); params.weights(2, 2.5); params.aggregate(ZParams.Aggregate.SUM); Response> inter1 = pipe.zinter(params, "foo", "bar"); Response> inter2 = pipe.zinterWithScores(params, "foo", "bar"); pipe.sync(); assertThat(inter1.get(), contains("a")); assertThat(inter2.get(), contains(new Tuple("a", new Double(7)))); // Binary pipe.zadd(bfoo, 1, ba); pipe.zadd(bfoo, 2, bb); pipe.zadd(bbar, 2, ba); ZParams bparams = new ZParams(); bparams.weights(2, 2.5); bparams.aggregate(ZParams.Aggregate.SUM); Response> binter1 = pipe.zinter(params, bfoo, bbar); Response> binter2 = pipe.zinterWithScores(bparams, bfoo, bbar); pipe.sync(); assertThat(binter1.get(), contains(ba)); assertThat(binter2.get(), contains(new Tuple(ba, new Double(7)))); } @Test @ConditionalOnEnv(value = TestEnvUtil.ENV_REDIS_ENTERPRISE, enabled = false) public void zinterstore() { pipe.zadd("foo", 1, "a"); pipe.zadd("foo", 2, "b"); pipe.zadd("bar", 2, "a"); Response result1 = pipe.zinterstore("dst", "foo", "bar"); Response> items1 = pipe.zrangeWithScores("dst", 0, 100); pipe.sync(); assertThat(result1.get(), equalTo(1L)); assertThat(items1.get(), contains(new Tuple("a", new Double(3)))); // Binary pipe.zadd(bfoo, 1, ba); pipe.zadd(bfoo, 2, bb); pipe.zadd(bbar, 2, ba); Response bresult1 = pipe.zinterstore(SafeEncoder.encode("dst"), bfoo, bbar); Response> bitems1 = pipe.zrangeWithScores(SafeEncoder.encode("dst"), 0, 100); pipe.sync(); assertThat(bresult1.get(), equalTo(1L)); assertThat(bitems1.get(), contains(new Tuple(ba, new Double(3)))); } @Test @ConditionalOnEnv(value = TestEnvUtil.ENV_REDIS_ENTERPRISE, enabled = false) public void zintertoreParams() { pipe.zadd("foo", 1, "a"); pipe.zadd("foo", 2, "b"); pipe.zadd("bar", 2, "a"); ZParams params = new ZParams(); params.weights(2, 2.5); params.aggregate(ZParams.Aggregate.SUM); Response result1 = pipe.zinterstore("dst", params, "foo", "bar"); Response> items1 = pipe.zrangeWithScores("dst", 0, 100); pipe.sync(); assertThat(result1.get(), equalTo(1L)); assertThat(items1.get(), contains(new Tuple("a", new Double(7)))); // Binary pipe.zadd(bfoo, 1, ba); pipe.zadd(bfoo, 2, bb); pipe.zadd(bbar, 2, ba); ZParams bparams = new ZParams(); bparams.weights(2, 2.5); bparams.aggregate(ZParams.Aggregate.SUM); Response bresult1 = pipe.zinterstore(SafeEncoder.encode("dst"), bparams, bfoo, bbar); Response> bitems1 = pipe.zrangeWithScores(SafeEncoder.encode("dst"), 0, 100); pipe.sync(); assertThat(bresult1.get(), equalTo(1L)); assertThat(bitems1.get(), contains(new Tuple(ba, new Double(7)))); } @Test @SinceRedisVersion(value="7.0.0") @ConditionalOnEnv(value = TestEnvUtil.ENV_REDIS_ENTERPRISE, enabled = false) public void zintercard() { pipe.zadd("foo", 1, "a"); pipe.zadd("foo", 2, "b"); pipe.zadd("bar", 2, "a"); pipe.zadd("bar", 1, "b"); Response result1 = pipe.zintercard("foo", "bar"); Response result2 = pipe.zintercard(1, "foo", "bar"); pipe.sync(); assertThat(result1.get(), equalTo(2L)); assertThat(result2.get(), equalTo(1L)); // Binary pipe.zadd(bfoo, 1, ba); pipe.zadd(bfoo, 2, bb); pipe.zadd(bbar, 2, ba); pipe.zadd(bbar, 2, bb); Response bresult1 = pipe.zintercard(bfoo, bbar); Response bresult2 = pipe.zintercard(1, bfoo, bbar); pipe.sync(); assertThat(bresult1.get(), equalTo(2L)); assertThat(bresult2.get(), equalTo(1L)); } @Test public void zscan() { pipe.zadd("foo", 1, "a"); pipe.zadd("foo", 2, "b"); Response> result = pipe.zscan("foo", SCAN_POINTER_START); pipe.sync(); assertThat(result.get().getCursor(), equalTo(SCAN_POINTER_START)); assertThat(result.get().getResult().stream().map(Tuple::getElement).collect(Collectors.toList()), containsInAnyOrder("a", "b")); // binary pipe.zadd(bfoo, 1, ba); pipe.zadd(bfoo, 1, bb); Response> bResult = pipe.zscan(bfoo, SCAN_POINTER_START_BINARY); pipe.sync(); assertThat(bResult.get().getCursor(), equalTo(SCAN_POINTER_START)); assertThat(bResult.get().getResult().stream().map(Tuple::getBinaryElement).collect(Collectors.toList()), containsInAnyOrder(ba, bb)); } @Test public void zscanMatch() { ScanParams params = new ScanParams(); params.match("a*"); pipe.zadd("foo", 2, "b"); pipe.zadd("foo", 1, "a"); pipe.zadd("foo", 11, "aa"); Response> result = pipe.zscan("foo", SCAN_POINTER_START, params); pipe.sync(); assertThat(result.get().getCursor(), equalTo(SCAN_POINTER_START)); assertThat(result.get().getResult().stream().map(Tuple::getElement).collect(Collectors.toList()), containsInAnyOrder("a", "aa")); // binary params = new ScanParams(); params.match(bbarstar); pipe.zadd(bfoo, 2, bbar1); pipe.zadd(bfoo, 1, bbar2); pipe.zadd(bfoo, 11, bbar3); Response> bResult = pipe.zscan(bfoo, SCAN_POINTER_START_BINARY, params); pipe.sync(); assertThat(bResult.get().getCursor(), equalTo(SCAN_POINTER_START)); assertThat(bResult.get().getResult().stream().map(Tuple::getBinaryElement).collect(Collectors.toList()), containsInAnyOrder(bbar1, bbar2, bbar3)); } @Test public void zscanCount() { ScanParams params = new ScanParams(); params.count(2); pipe.zadd("foo", 1, "a1"); pipe.zadd("foo", 2, "a2"); pipe.zadd("foo", 3, "a3"); pipe.zadd("foo", 4, "a4"); pipe.zadd("foo", 5, "a5"); Response> result = pipe.zscan("foo", SCAN_POINTER_START, params); pipe.sync(); assertThat(result.get().getResult(), not(empty())); // binary params = new ScanParams(); params.count(2); pipe.zadd(bfoo, 2, bbar1); pipe.zadd(bfoo, 1, bbar2); pipe.zadd(bfoo, 11, bbar3); Response> bResult = pipe.zscan(bfoo, SCAN_POINTER_START_BINARY, params); pipe.sync(); assertThat(bResult.get().getResult(), not(empty())); } @Test public void infinity() { pipe.zadd("key", Double.POSITIVE_INFINITY, "pos"); Response score1 = pipe.zscore("key", "pos"); pipe.zadd("key", Double.NEGATIVE_INFINITY, "neg"); Response score2 = pipe.zscore("key", "neg"); pipe.zadd("key", 0d, "zero"); Response> set = pipe.zrangeWithScores("key", 0, -1); pipe.sync(); assertThat(score1.get(), equalTo(Double.POSITIVE_INFINITY)); assertThat(score2.get(), equalTo(Double.NEGATIVE_INFINITY)); assertThat(set.get().stream().map(Tuple::getScore).collect(Collectors.toList()), contains(Double.NEGATIVE_INFINITY, 0d, Double.POSITIVE_INFINITY)); } @Test @ConditionalOnEnv(value = TestEnvUtil.ENV_REDIS_ENTERPRISE, enabled = false) public void bzpopmax() { pipe.zadd("foo", 1d, "a", ZAddParams.zAddParams().nx()); pipe.zadd("foo", 10d, "b", ZAddParams.zAddParams().nx()); pipe.zadd("bar", 0.1d, "c", ZAddParams.zAddParams().nx()); Response> item1 = pipe.bzpopmax(0, "foo", "bar"); pipe.sync(); assertThat(item1.get().getKey(), equalTo("foo")); assertThat(item1.get().getValue(), equalTo(new Tuple("b", 10d))); // Binary pipe.zadd(bfoo, 1d, ba); pipe.zadd(bfoo, 10d, bb); pipe.zadd(bbar, 0.1d, bc); Response> bitem1 = pipe.bzpopmax(0, bfoo, bbar); pipe.sync(); assertThat(bitem1.get().getKey(), equalTo(bfoo)); assertThat(bitem1.get().getValue(), equalTo(new Tuple(bb, 10d))); } @Test @ConditionalOnEnv(value = TestEnvUtil.ENV_REDIS_ENTERPRISE, enabled = false) public void bzpopmin() { pipe.zadd("foo", 1d, "a", ZAddParams.zAddParams().nx()); pipe.zadd("foo", 10d, "b", ZAddParams.zAddParams().nx()); pipe.zadd("bar", 0.1d, "c", ZAddParams.zAddParams().nx()); Response> item1 = pipe.bzpopmin(0, "bar", "foo"); pipe.sync(); assertThat(item1.get(), equalTo(new KeyValue<>("bar", new Tuple("c", 0.1)))); // Binary pipe.zadd(bfoo, 1d, ba); pipe.zadd(bfoo, 10d, bb); pipe.zadd(bbar, 0.1d, bc); Response> bitem1 = pipe.bzpopmin(0, bbar, bfoo); pipe.sync(); assertThat(bitem1.get().getKey(), equalTo(bbar)); assertThat(bitem1.get().getValue(), equalTo(new Tuple(bc, 0.1))); } @Test @ConditionalOnEnv(value = TestEnvUtil.ENV_REDIS_ENTERPRISE, enabled = false) public void zdiff() { pipe.zadd("foo", 1.0, "a"); pipe.zadd("foo", 2.0, "b"); pipe.zadd("bar", 1.0, "a"); Response> diff1 = pipe.zdiff("bar1", "bar2"); Response> diff2 = pipe.zdiff("foo", "bar"); Response> diff3 = pipe.zdiffWithScores("foo", "bar"); pipe.sync(); assertThat(diff1.get(), empty()); assertThat(diff2.get(), contains("b")); assertThat(diff3.get(), contains(new Tuple("b", 2.0d))); // binary pipe.zadd(bfoo, 1.0, ba); pipe.zadd(bfoo, 2.0, bb); pipe.zadd(bbar, 1.0, ba); Response> bdiff1 = pipe.zdiff(bbar1, bbar2); Response> bdiff2 = pipe.zdiff(bfoo, bbar); Response> bdiff3 = pipe.zdiffWithScores(bfoo, bbar); pipe.sync(); assertThat(bdiff1.get(), empty()); assertThat(bdiff2.get(), contains(bb)); assertThat(bdiff3.get(), contains(new Tuple(bb, 2.0d))); } @Test @ConditionalOnEnv(value = TestEnvUtil.ENV_REDIS_ENTERPRISE, enabled = false) public void zdiffstore() { pipe.zadd("foo", 1.0, "a"); pipe.zadd("foo", 2.0, "b"); pipe.zadd("bar", 1.0, "a"); Response result1 = pipe.zdiffstore("bar3", "bar1", "bar2"); Response result2 = pipe.zdiffstore("bar3", "foo", "bar"); Response> items = pipe.zrange("bar3", 0, -1); pipe.sync(); assertThat(result1.get(), equalTo(0L)); assertThat(result2.get(), equalTo(1L)); assertThat(items.get(), contains("b")); // binary pipe.zadd(bfoo, 1.0, ba); pipe.zadd(bfoo, 2.0, bb); pipe.zadd(bbar, 1.0, ba); Response bresult1 = pipe.zdiffstore(bbar3, bbar1, bbar2); Response bresult2 = pipe.zdiffstore(bbar3, bfoo, bbar); Response> bitems = pipe.zrange(bbar3, 0, -1); pipe.sync(); assertThat(bresult1.get(), equalTo(0L)); assertThat(bresult2.get(), equalTo(1L)); assertThat(bitems.get(), contains(bb)); } @Test public void zrandmember() { Response item1 = pipe.zrandmember("foo"); Response> items1 = pipe.zrandmember("foo", 1); Response> items2 = pipe.zrandmemberWithScores("foo", 1); pipe.sync(); assertThat(item1.get(), nullValue()); assertThat(items1.get(), empty()); assertThat(items2.get(), empty()); Map hash = new HashMap<>(); hash.put("bar1", 1d); hash.put("bar2", 10d); hash.put("bar3", 0.1d); pipe.zadd("foo", hash); Response item2 = pipe.zrandmember("foo"); Response> items3 = pipe.zrandmember("foo", 2); Response> items4 = pipe.zrandmemberWithScores("foo", 2); pipe.sync(); assertThat(item2.get(), in(hash.keySet())); assertThat(items3.get(), hasSize(2)); assertThat(items4.get(), hasSize(2)); items4.get().forEach(t -> assertEquals(hash.get(t.getElement()), t.getScore(), 0d)); // Binary Response bitem1 = pipe.zrandmember(bfoo); Response> bitems1 = pipe.zrandmember(bfoo, 1); Response> bitems2 = pipe.zrandmemberWithScores(bfoo, 1); pipe.sync(); assertThat(bitem1.get(), nullValue()); assertThat(bitems1.get(), empty()); assertThat(bitems2.get(), empty()); Map bhash = new HashMap<>(); bhash.put(bbar1, 1d); bhash.put(bbar2, 10d); bhash.put(bbar3, 0.1d); pipe.zadd(bfoo, bhash); Response bitem2 = pipe.zrandmember(bfoo); Response> bitems3 = pipe.zrandmember(bfoo, 2); Response> bitems4 = pipe.zrandmemberWithScores(bfoo, 2); pipe.sync(); AssertUtil.assertByteArrayCollectionContains(bhash.keySet(), bitem2.get()); assertThat(bitems3.get(), hasSize(2)); assertThat(bitems4.get(), hasSize(2)); bitems4.get().forEach(t -> assertEquals(getScoreFromByteMap(bhash, t.getBinaryElement()), t.getScore(), 0d)); } private Double getScoreFromByteMap(Map bhash, byte[] key) { for (Map.Entry en : bhash.entrySet()) { if (Arrays.equals(en.getKey(), key)) { return en.getValue(); } } return null; } @Test @SinceRedisVersion(value="7.0.0") public void zmpop() { pipe.zadd("foo", 1d, "a", ZAddParams.zAddParams().nx()); pipe.zadd("foo", 10d, "b", ZAddParams.zAddParams().nx()); pipe.zadd("foo", 0.1d, "c", ZAddParams.zAddParams().nx()); pipe.zadd("foo", 2d, "a", ZAddParams.zAddParams().nx()); Response>> single = pipe.zmpop(SortedSetOption.MAX, "foo"); Response>> range = pipe.zmpop(SortedSetOption.MIN, 2, "foo"); Response>> nullRange = pipe.zmpop(SortedSetOption.MAX, "foo"); pipe.sync(); assertThat(single.get().getValue(), contains(new Tuple("b", 10d))); assertThat(range.get().getValue(), contains( new Tuple("c", 0.1d), new Tuple("a", 1d) )); assertThat(nullRange.get(), nullValue()); } @Test @SinceRedisVersion(value="7.0.0") public void bzmpopSimple() { pipe.zadd("foo", 1d, "a", ZAddParams.zAddParams().nx()); pipe.zadd("foo", 10d, "b", ZAddParams.zAddParams().nx()); pipe.zadd("foo", 0.1d, "c", ZAddParams.zAddParams().nx()); pipe.zadd("foo", 2d, "a", ZAddParams.zAddParams().nx()); Response>> single = pipe.bzmpop(1L, SortedSetOption.MAX, "foo"); Response>> range = pipe.bzmpop(1L, SortedSetOption.MIN, 2, "foo"); Response>> nullRange = pipe.bzmpop(1L, SortedSetOption.MAX, "foo"); pipe.sync(); assertThat(single.get().getValue(), contains(new Tuple("b", 10d))); assertThat(range.get().getValue(), contains( new Tuple("c", 0.1d), new Tuple("a", 1d) )); assertThat(nullRange.get(), nullValue()); } } ================================================ FILE: src/test/java/redis/clients/jedis/commands/unified/pipeline/StreamsPipelineCommandsTest.java ================================================ package redis.clients.jedis.commands.unified.pipeline; import static java.util.Collections.singletonMap; import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.both; import static org.hamcrest.Matchers.contains; import static org.hamcrest.Matchers.containsString; import static org.hamcrest.Matchers.empty; import static org.hamcrest.Matchers.equalTo; import static org.hamcrest.Matchers.greaterThan; import static org.hamcrest.Matchers.hasSize; import static org.hamcrest.Matchers.hasToString; import static org.hamcrest.Matchers.instanceOf; import static org.hamcrest.Matchers.notNullValue; import static org.hamcrest.Matchers.nullValue; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertNotNull; import static org.junit.jupiter.api.Assertions.assertTrue; import java.time.Duration; import java.util.HashMap; import java.util.LinkedHashMap; import java.util.List; import java.util.Map; import java.util.Map.Entry; import java.util.concurrent.atomic.AtomicReference; import java.util.stream.Collectors; import io.redis.test.annotations.ConditionalOnEnv; import io.redis.test.annotations.EnabledOnCommand; import io.redis.test.annotations.SinceRedisVersion; import io.redis.test.utils.RedisVersion; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Tag; import org.junit.jupiter.params.ParameterizedClass; import org.junit.jupiter.params.provider.MethodSource; import redis.clients.jedis.util.RedisVersionUtil; import org.hamcrest.Matchers; import redis.clients.jedis.BuilderFactory; import redis.clients.jedis.Pipeline; import redis.clients.jedis.RedisProtocol; import redis.clients.jedis.Response; import redis.clients.jedis.StreamEntryID; import redis.clients.jedis.exceptions.JedisDataException; import redis.clients.jedis.params.XAddParams; import redis.clients.jedis.params.XAutoClaimParams; import redis.clients.jedis.params.XCfgSetParams; import redis.clients.jedis.params.XClaimParams; import redis.clients.jedis.params.XPendingParams; import redis.clients.jedis.params.XReadGroupParams; import redis.clients.jedis.params.XReadParams; import redis.clients.jedis.params.XTrimParams; import redis.clients.jedis.resps.StreamConsumerFullInfo; import redis.clients.jedis.resps.StreamConsumerInfo; import redis.clients.jedis.resps.StreamConsumersInfo; import redis.clients.jedis.resps.StreamEntry; import redis.clients.jedis.resps.StreamFullInfo; import redis.clients.jedis.resps.StreamGroupFullInfo; import redis.clients.jedis.resps.StreamGroupInfo; import redis.clients.jedis.resps.StreamInfo; import redis.clients.jedis.resps.StreamPendingEntry; import redis.clients.jedis.util.SafeEncoder; import redis.clients.jedis.util.TestEnvUtil; @ParameterizedClass @MethodSource("redis.clients.jedis.commands.CommandsTestsParameters#respVersions") @Tag("integration") public class StreamsPipelineCommandsTest extends PipelineCommandsTestBase { public StreamsPipelineCommandsTest(RedisProtocol protocol) { super(protocol); } @Test public void xaddWrongNumberOfArguments() { Map map1 = new HashMap<>(); pipe.xadd("stream1", (StreamEntryID) null, map1); assertThat(pipe.syncAndReturnAll(), contains( both(instanceOf(JedisDataException.class)).and(hasToString(containsString("wrong number of arguments"))) )); } @Test public void xadd() { Map map1 = new HashMap<>(); map1.put("f1", "v1"); pipe.xadd("xadd-stream1", (StreamEntryID) null, map1); Map map2 = new HashMap<>(); map2.put("f1", "v1"); map2.put("f2", "v2"); pipe.xadd("xadd-stream1", (StreamEntryID) null, map2); List results = pipe.syncAndReturnAll(); assertThat(results, contains( instanceOf(StreamEntryID.class), instanceOf(StreamEntryID.class) )); assertThat((StreamEntryID) results.get(1), greaterThan((StreamEntryID) results.get(0))); } @Test public void xaddMaxLen() { Map map4 = new HashMap<>(); map4.put("f2", "v2"); map4.put("f3", "v3"); StreamEntryID idIn = new StreamEntryID(1000, 1L); pipe.xadd("xadd-stream2", idIn, map4); Map map5 = new HashMap<>(); map5.put("f4", "v4"); map5.put("f5", "v5"); pipe.xadd("xadd-stream2", (StreamEntryID) null, map5); pipe.xlen("xadd-stream2"); Map map6 = new HashMap<>(); map6.put("f4", "v4"); map6.put("f5", "v5"); pipe.xadd("xadd-stream2", map6, XAddParams.xAddParams().maxLen(2)); pipe.xlen("xadd-stream2"); List results = pipe.syncAndReturnAll(); assertThat(results, contains( equalTo(idIn), instanceOf(StreamEntryID.class), equalTo(2L), instanceOf(StreamEntryID.class), equalTo(2L) )); assertThat((StreamEntryID) results.get(1), greaterThan((StreamEntryID) results.get(0))); assertThat((StreamEntryID) results.get(3), greaterThan((StreamEntryID) results.get(1))); } @Test public void xaddWithParamsWrongNumberOfArguments() { pipe.xadd("stream1", new HashMap<>(), XAddParams.xAddParams()); pipe.xadd("stream1", XAddParams.xAddParams(), new HashMap<>()); assertThat(pipe.syncAndReturnAll(), contains( both(instanceOf(JedisDataException.class)).and(hasToString(containsString("wrong number of arguments"))), both(instanceOf(JedisDataException.class)).and(hasToString(containsString("wrong number of arguments"))) )); } @Test public void xaddWithParams() { pipe.xadd("xadd-stream1", (StreamEntryID) null, singletonMap("f1", "v1")); Map map2 = new HashMap<>(); map2.put("f1", "v1"); map2.put("f2", "v2"); pipe.xadd("xadd-stream1", map2, XAddParams.xAddParams()); List results = pipe.syncAndReturnAll(); assertThat(results, contains( instanceOf(StreamEntryID.class), instanceOf(StreamEntryID.class) )); assertThat((StreamEntryID) results.get(1), greaterThan((StreamEntryID) results.get(0))); } @Test public void xaddWithParamsTrim() { Map map3 = new HashMap<>(); map3.put("f2", "v2"); map3.put("f3", "v3"); StreamEntryID idIn = new StreamEntryID(1000, 1L); pipe.xadd("xadd-stream2", XAddParams.xAddParams().id(idIn), map3); Map map4 = new HashMap<>(); map4.put("f2", "v2"); map4.put("f3", "v3"); StreamEntryID idIn2 = new StreamEntryID(2000, 1L); pipe.xadd("xadd-stream2", map4, XAddParams.xAddParams().id(idIn2)); Map map5 = new HashMap<>(); map5.put("f4", "v4"); map5.put("f5", "v5"); pipe.xadd("xadd-stream2", XAddParams.xAddParams(), map5); pipe.xlen("xadd-stream2"); Map map6 = new HashMap<>(); map6.put("f4", "v4"); map6.put("f5", "v5"); pipe.xadd("xadd-stream2", map6, XAddParams.xAddParams().maxLen(3).exactTrimming()); pipe.xlen("xadd-stream2"); List results = pipe.syncAndReturnAll(); assertThat(results, contains( equalTo(idIn), equalTo(idIn2), instanceOf(StreamEntryID.class), equalTo(3L), instanceOf(StreamEntryID.class), equalTo(3L) )); assertThat((StreamEntryID) results.get(2), greaterThan((StreamEntryID) results.get(1))); assertThat((StreamEntryID) results.get(4), greaterThan((StreamEntryID) results.get(2))); } @Test public void xaddWithParamsNoMkStream() { pipe.xadd("xadd-stream3", XAddParams.xAddParams().noMkStream().maxLen(3).exactTrimming(), singletonMap("f1", "v1")); assertThat(pipe.syncAndReturnAll(), contains( nullValue() )); assertFalse(client.exists("xadd-stream3")); } @Test public void xaddWithParamsMinId() { Map map6 = new HashMap<>(); map6.put("f4", "v4"); map6.put("f5", "v5"); StreamEntryID id = new StreamEntryID(2); pipe.xadd("xadd-stream3", map6, XAddParams.xAddParams().minId("2").id(id)); pipe.xlen("xadd-stream3"); StreamEntryID id1 = new StreamEntryID(3); pipe.xadd("xadd-stream3", XAddParams.xAddParams().minId("4").id(id1), map6); pipe.xlen("xadd-stream3"); List results = pipe.syncAndReturnAll(); assertThat(results, contains( equalTo(id), equalTo(1L), equalTo(id1), equalTo(0L) )); } @Test @SinceRedisVersion(value = "7.0.0", message = "Added support for XADD ID auto sequence is introduced in 7.0.0") public void xaddParamsId() { String key = "kk"; Map map = singletonMap("ff", "vv"); pipe.xadd(key, XAddParams.xAddParams().id(new StreamEntryID(0, 1)), map); pipe.xadd(key, XAddParams.xAddParams().id(2, 3), map); pipe.xadd(key, XAddParams.xAddParams().id(4), map); pipe.xadd(key, XAddParams.xAddParams().id("5-6"), map); pipe.xadd(key, XAddParams.xAddParams().id("7-8".getBytes()), map); pipe.xadd(key, XAddParams.xAddParams(), map); List results = pipe.syncAndReturnAll(); assertThat(results, contains( equalTo(new StreamEntryID(0, 1)), equalTo(new StreamEntryID(2, 3)), equalTo(new StreamEntryID(4, 0)), equalTo(new StreamEntryID(5, 6)), equalTo(new StreamEntryID(7, 8)), instanceOf(StreamEntryID.class) )); assertThat((StreamEntryID) results.get(5), greaterThan((StreamEntryID) results.get(4))); } @Test public void xdel() { Map map1 = new HashMap<>(); map1.put("f1", "v1"); pipe.xadd("xdel-stream", (StreamEntryID) null, map1); pipe.xadd("xdel-stream", (StreamEntryID) null, map1); List results = pipe.syncAndReturnAll(); assertThat(results, contains( instanceOf(StreamEntryID.class), instanceOf(StreamEntryID.class) )); StreamEntryID id1 = (StreamEntryID) results.get(1); pipe.xlen("xdel-stream"); pipe.xdel("xdel-stream", id1); pipe.xlen("xdel-stream"); assertThat(pipe.syncAndReturnAll(), contains( 2L, 1L, 1L )); } @Test public void xlen() { pipe.xlen("xlen-stream"); Map map = new HashMap<>(); map.put("f1", "v1"); pipe.xadd("xlen-stream", (StreamEntryID) null, map); pipe.xlen("xlen-stream"); pipe.xadd("xlen-stream", (StreamEntryID) null, map); pipe.xlen("xlen-stream"); assertThat(pipe.syncAndReturnAll(), contains( equalTo(0L), instanceOf(StreamEntryID.class), equalTo(1L), instanceOf(StreamEntryID.class), equalTo(2L) )); } @Test public void xrange() { Response> range = pipe.xrange("xrange-stream", null, (StreamEntryID) null, Integer.MAX_VALUE); Map map1 = singletonMap("f1", "v1"); Map map2 = singletonMap("f2", "v2"); Map map3 = singletonMap("f3", "v3"); Response id1Response = pipe.xadd("xrange-stream", (StreamEntryID) null, map1); Response id2Response = pipe.xadd("xrange-stream", (StreamEntryID) null, map2); pipe.sync(); assertThat(range.get(), empty()); assertThat(id1Response.get(), notNullValue()); assertThat(id2Response.get(), notNullValue()); StreamEntryID id1 = id1Response.get(); StreamEntryID id2 = id2Response.get(); Response> range2 = pipe.xrange("xrange-stream", null, (StreamEntryID) null, 3); Response> range3 = pipe.xrange("xrange-stream", id1, null, 2); Response> range4 = pipe.xrange("xrange-stream", id1, id2, 2); Response> range5 = pipe.xrange("xrange-stream", id1, id2, 1); Response> range6 = pipe.xrange("xrange-stream", id2, null, 4); Response id3Response = pipe.xadd("xrange-stream", (StreamEntryID) null, map3); pipe.sync(); assertThat(range2.get().stream().map(StreamEntry::getID).collect(Collectors.toList()), contains(id1, id2)); assertThat(range3.get().stream().map(StreamEntry::getID).collect(Collectors.toList()), contains(id1, id2)); assertThat(range4.get().stream().map(StreamEntry::getID).collect(Collectors.toList()), contains(id1, id2)); assertThat(range5.get().stream().map(StreamEntry::getID).collect(Collectors.toList()), contains(id1)); assertThat(range6.get().stream().map(StreamEntry::getID).collect(Collectors.toList()), contains(id2)); assertThat(id3Response.get(), notNullValue()); StreamEntryID id3 = id3Response.get(); Response> range7 = pipe.xrange("xrange-stream", id3, id3, 4); Response> range8 = pipe.xrange("xrange-stream", null, (StreamEntryID) null); Response> range9 = pipe.xrange("xrange-stream", StreamEntryID.MINIMUM_ID, StreamEntryID.MAXIMUM_ID); pipe.sync(); assertThat(range7.get().stream().map(StreamEntry::getID).collect(Collectors.toList()), contains(id3)); assertThat(range8.get().stream().map(StreamEntry::getID).collect(Collectors.toList()), contains(id1, id2, id3)); assertThat(range9.get().stream().map(StreamEntry::getID).collect(Collectors.toList()), contains(id1, id2, id3)); } @Test public void xrangeExclusive() { StreamEntryID id1 = client.xadd("xrange-stream", (StreamEntryID) null, singletonMap("f1", "v1")); StreamEntryID id2 = client.xadd("xrange-stream", (StreamEntryID) null, singletonMap("f2", "v2")); Response> range1 = pipe.xrange("xrange-stream", id1.toString(), "+", 2); Response> range2 = pipe.xrange("xrange-stream", "(" + id1, "+", 2); pipe.sync(); assertThat(range1.get().stream().map(StreamEntry::getID).collect(Collectors.toList()), contains(id1, id2)); assertThat(range2.get().stream().map(StreamEntry::getID).collect(Collectors.toList()), contains(id2)); } @Test @ConditionalOnEnv(value = TestEnvUtil.ENV_REDIS_ENTERPRISE, enabled = false) public void xreadWithParams() { final String stream1 = "xread-stream1"; final String stream2 = "xread-stream2"; Map streamQuery1 = singletonMap(stream1, new StreamEntryID()); // Before creating Stream pipe.xread(XReadParams.xReadParams().block(1), streamQuery1); pipe.xread(XReadParams.xReadParams(), streamQuery1); assertThat(pipe.syncAndReturnAll(), contains( nullValue(), nullValue() )); Map map1 = singletonMap("f1", "v1"); StreamEntryID id1 = client.xadd(stream1, (StreamEntryID) null, map1); Map map2 = singletonMap("f2", "v2"); StreamEntryID id2 = client.xadd(stream2, (StreamEntryID) null, map2); // Read only a single Stream Response>>> streams1 = pipe.xread(XReadParams.xReadParams().count(1).block(1), streamQuery1); Response>>> streams2 = pipe.xread(XReadParams.xReadParams().block(1), singletonMap(stream1, id1)); Response>>> streams3 = pipe.xread(XReadParams.xReadParams(), singletonMap(stream1, id1)); pipe.sync(); assertThat(streams1.get().stream().map(Entry::getKey).collect(Collectors.toList()), contains(stream1)); assertThat(streams1.get().stream().map(Entry::getValue).flatMap(List::stream) .map(StreamEntry::getID).collect(Collectors.toList()), contains(id1)); assertThat(streams1.get().stream().map(Entry::getValue).flatMap(List::stream) .map(StreamEntry::getFields).collect(Collectors.toList()), contains(map1)); assertThat(streams2.get(), nullValue()); assertThat(streams3.get(), nullValue()); // Read from two Streams Map streamQuery2 = new LinkedHashMap<>(); streamQuery2.put(stream1, new StreamEntryID()); streamQuery2.put(stream2, new StreamEntryID()); Response>>> streams4 = pipe.xread(XReadParams.xReadParams().count(2).block(1), streamQuery2); pipe.sync(); assertThat(streams4.get().stream().map(Entry::getKey).collect(Collectors.toList()), contains(stream1, stream2)); assertThat(streams4.get().stream().map(Entry::getValue).flatMap(List::stream) .map(StreamEntry::getID).collect(Collectors.toList()), contains(id1, id2)); assertThat(streams4.get().stream().map(Entry::getValue).flatMap(List::stream) .map(StreamEntry::getFields).collect(Collectors.toList()), contains(map1, map2)); } @Test public void xreadBlockZero() throws InterruptedException { final AtomicReference>>> readRef = new AtomicReference<>(); Thread t = new Thread(new Runnable() { @Override public void run() { long startTime = System.currentTimeMillis(); Pipeline blockPipe = client.pipelined(); Map streamQuery = singletonMap("block0-stream", new StreamEntryID()); Response>>> read = blockPipe.xread(XReadParams.xReadParams().block(0), streamQuery); blockPipe.sync(); long endTime = System.currentTimeMillis(); assertTrue(endTime - startTime > 500); assertNotNull(read); readRef.set(read.get()); } }, "xread-block-0-thread"); t.start(); Thread.sleep(1000); StreamEntryID addedId = client.xadd("block0-stream", (StreamEntryID) null, singletonMap("foo", "bar")); t.join(); assertThat(readRef.get().stream().map(Entry::getKey).collect(Collectors.toList()), contains("block0-stream")); assertThat(readRef.get().stream().map(Entry::getValue).flatMap(List::stream) .map(StreamEntry::getID).collect(Collectors.toList()), contains(addedId)); } @Test public void xtrim() { Map map1 = new HashMap(); map1.put("f1", "v1"); for (int i = 1; i <= 5; i++) { pipe.xadd("xtrim-stream", (StreamEntryID) null, map1); } pipe.xlen("xtrim-stream"); pipe.xtrim("xtrim-stream", 3, false); pipe.xlen("xtrim-stream"); assertThat(pipe.syncAndReturnAll(), contains( instanceOf(StreamEntryID.class), instanceOf(StreamEntryID.class), instanceOf(StreamEntryID.class), instanceOf(StreamEntryID.class), instanceOf(StreamEntryID.class), equalTo(5L), equalTo(2L), equalTo(3L) )); } @Test public void xtrimWithParams() { Map map1 = new HashMap<>(); map1.put("f1", "v1"); for (int i = 1; i <= 5; i++) { pipe.xadd("xtrim-stream", new StreamEntryID("0-" + i), map1); } pipe.xlen("xtrim-stream"); pipe.xtrim("xtrim-stream", XTrimParams.xTrimParams().maxLen(3).exactTrimming()); pipe.xlen("xtrim-stream"); // minId pipe.xtrim("xtrim-stream", XTrimParams.xTrimParams().minId("0-4").exactTrimming()); pipe.xlen("xtrim-stream"); assertThat(pipe.syncAndReturnAll(), contains( instanceOf(StreamEntryID.class), instanceOf(StreamEntryID.class), instanceOf(StreamEntryID.class), instanceOf(StreamEntryID.class), instanceOf(StreamEntryID.class), equalTo(5L), equalTo(2L), equalTo(3L), equalTo(1L), equalTo(2L) )); } @Test public void xrevrange() { Response> range = pipe.xrevrange("xrevrange-stream", null, (StreamEntryID) null, Integer.MAX_VALUE); Map map1 = singletonMap("f1", "v1"); Response id1Response = pipe.xadd("xrevrange-stream", (StreamEntryID) null, map1); Map map2 = singletonMap("f2", "v2"); Response id2Response = pipe.xadd("xrevrange-stream", (StreamEntryID) null, map2); pipe.sync(); assertThat(range.get(), empty()); assertThat(id1Response.get(), notNullValue()); assertThat(id2Response.get(), notNullValue()); StreamEntryID id1 = id1Response.get(); StreamEntryID id2 = id2Response.get(); Response> range2 = pipe.xrevrange("xrevrange-stream", null, (StreamEntryID) null, 3); Response> range3 = pipe.xrevrange("xrevrange-stream", null, id1, 2); Response> range4 = pipe.xrevrange("xrevrange-stream", id2, id1, 2); Response> range5 = pipe.xrevrange("xrevrange-stream", id2, id1, 1); Response> range6 = pipe.xrevrange("xrevrange-stream", null, id2, 4); Map map3 = singletonMap("f3", "v3"); Response id3Response = pipe.xadd("xrevrange-stream", (StreamEntryID) null, map3); pipe.sync(); assertThat(range2.get().stream().map(StreamEntry::getID).collect(Collectors.toList()), contains(id2, id1)); assertThat(range3.get().stream().map(StreamEntry::getID).collect(Collectors.toList()), contains(id2, id1)); assertThat(range4.get().stream().map(StreamEntry::getID).collect(Collectors.toList()), contains(id2, id1)); assertThat(range5.get().stream().map(StreamEntry::getID).collect(Collectors.toList()), contains(id2)); assertThat(range6.get().stream().map(StreamEntry::getID).collect(Collectors.toList()), contains(id2)); assertThat(id3Response.get(), notNullValue()); StreamEntryID id3 = id3Response.get(); Response> range7 = pipe.xrevrange("xrevrange-stream", id3, id3, 4); Response> range8 = pipe.xrevrange("xrevrange-stream", null, (StreamEntryID) null); Response> range9 = pipe.xrevrange("xrevrange-stream", StreamEntryID.MAXIMUM_ID, StreamEntryID.MINIMUM_ID); pipe.sync(); assertThat(range7.get().stream().map(StreamEntry::getID).collect(Collectors.toList()), contains(id3)); assertThat(range8.get().stream().map(StreamEntry::getID).collect(Collectors.toList()), contains(id3, id2, id1)); assertThat(range9.get().stream().map(StreamEntry::getID).collect(Collectors.toList()), contains(id3, id2, id1)); } @Test public void xrevrangeExclusive() { StreamEntryID id1 = client.xadd("xrange-stream", (StreamEntryID) null, singletonMap("f1", "v1")); StreamEntryID id2 = client.xadd("xrange-stream", (StreamEntryID) null, singletonMap("f2", "v2")); Response> range1 = pipe.xrevrange("xrange-stream", "+", id1.toString(), 2); Response> range2 = pipe.xrevrange("xrange-stream", "+", "(" + id1, 2); pipe.sync(); assertThat(range1.get().stream().map(StreamEntry::getID).collect(Collectors.toList()), contains(id2, id1)); assertThat(range2.get().stream().map(StreamEntry::getID).collect(Collectors.toList()), contains(id2)); } @Test public void xgroup() { StreamEntryID id1 = client.xadd("xgroup-stream", (StreamEntryID) null, singletonMap("f1", "v1")); pipe.xgroupCreate("xgroup-stream", "consumer-group-name", null, false); pipe.xgroupSetID("xgroup-stream", "consumer-group-name", id1); pipe.xgroupCreate("xgroup-stream", "consumer-group-name1", StreamEntryID.XGROUP_LAST_ENTRY, false); pipe.xgroupDestroy("xgroup-stream", "consumer-group-name"); pipe.xgroupDelConsumer("xgroup-stream", "consumer-group-name1", "myconsumer1"); pipe.xgroupCreateConsumer("xgroup-stream", "consumer-group-name1", "myconsumer2"); pipe.xgroupDelConsumer("xgroup-stream", "consumer-group-name1", "myconsumer2"); assertThat(pipe.syncAndReturnAll(), contains( "OK", "OK", "OK", 1L, 0L, true, 0L )); } @Test @ConditionalOnEnv(value = TestEnvUtil.ENV_REDIS_ENTERPRISE, enabled = false) public void xreadGroupWithParams() { // Simple xreadGroup with NOACK Map map1 = singletonMap("f1", "v1"); StreamEntryID id1 = client.xadd("xreadGroup-stream1", (StreamEntryID) null, map1); client.xgroupCreate("xreadGroup-stream1", "xreadGroup-group", null, false); Map streamQuery1 = singletonMap("xreadGroup-stream1", StreamEntryID.XREADGROUP_UNDELIVERED_ENTRY); Response>>> streams1 = pipe.xreadGroup("xreadGroup-group", "xreadGroup-consumer", XReadGroupParams.xReadGroupParams().count(1).noAck(), streamQuery1); pipe.sync(); assertThat(streams1.get().stream().map(Entry::getKey).collect(Collectors.toList()), contains("xreadGroup-stream1")); assertThat(streams1.get().stream().map(Entry::getValue).flatMap(List::stream) .map(StreamEntry::getID).collect(Collectors.toList()), contains(id1)); assertThat(streams1.get().stream().map(Entry::getValue).flatMap(List::stream) .map(StreamEntry::getFields).collect(Collectors.toList()), contains(map1)); Map map2 = singletonMap("f2", "v2"); StreamEntryID id2 = client.xadd("xreadGroup-stream1", (StreamEntryID) null, map2); Map map3 = singletonMap("f3", "v3"); StreamEntryID id3 = client.xadd("xreadGroup-stream2", (StreamEntryID) null, map3); client.xgroupCreate("xreadGroup-stream2", "xreadGroup-group", null, false); // Read only a single Stream Response>>> streams2 = pipe.xreadGroup("xreadGroup-group", "xreadGroup-consumer", XReadGroupParams.xReadGroupParams().count(1).block(1).noAck(), streamQuery1); // Read from two Streams Map streamQuery2 = new LinkedHashMap<>(); streamQuery2.put("xreadGroup-stream1", StreamEntryID.XREADGROUP_UNDELIVERED_ENTRY); streamQuery2.put("xreadGroup-stream2", StreamEntryID.XREADGROUP_UNDELIVERED_ENTRY); Response>>> streams3 = pipe.xreadGroup("xreadGroup-group", "xreadGroup-consumer", XReadGroupParams.xReadGroupParams().count(1).noAck(), streamQuery2); pipe.sync(); assertThat(streams2.get().stream().map(Entry::getKey).collect(Collectors.toList()), contains("xreadGroup-stream1")); assertThat(streams2.get().stream().map(Entry::getValue).flatMap(List::stream) .map(StreamEntry::getID).collect(Collectors.toList()), contains(id2)); assertThat(streams2.get().stream().map(Entry::getValue).flatMap(List::stream) .map(StreamEntry::getFields).collect(Collectors.toList()), contains(map2)); assertThat(streams3.get().stream().map(Entry::getKey).collect(Collectors.toList()), contains("xreadGroup-stream2")); assertThat(streams3.get().stream().map(Entry::getValue).flatMap(List::stream) .map(StreamEntry::getID).collect(Collectors.toList()), contains(id3)); assertThat(streams3.get().stream().map(Entry::getValue).flatMap(List::stream) .map(StreamEntry::getFields).collect(Collectors.toList()), contains(map3)); // Read only fresh messages Map map4 = singletonMap("f4", "v4"); StreamEntryID id4 = client.xadd("xreadGroup-stream1", (StreamEntryID) null, map4); Map streamQueryFresh = singletonMap("xreadGroup-stream1", StreamEntryID.XREADGROUP_UNDELIVERED_ENTRY); Response>>> streams4 = pipe.xreadGroup("xreadGroup-group", "xreadGroup-consumer", XReadGroupParams.xReadGroupParams().count(4).block(100).noAck(), streamQueryFresh); pipe.sync(); assertThat(streams4.get().stream().map(Entry::getKey).collect(Collectors.toList()), contains("xreadGroup-stream1")); assertThat(streams4.get().stream().map(Entry::getValue).flatMap(List::stream) .map(StreamEntry::getID).collect(Collectors.toList()), contains(id4)); assertThat(streams4.get().stream().map(Entry::getValue).flatMap(List::stream) .map(StreamEntry::getFields).collect(Collectors.toList()), contains(map4)); } @Test public void xreadGroupWithParamsWhenPendingMessageIsDiscarded() { // Add two message to stream Map map1 = singletonMap("f1", "v1"); XAddParams xAddParams = XAddParams.xAddParams().id(StreamEntryID.NEW_ENTRY).maxLen(2); StreamEntryID firstMessageEntryId = client.xadd("xreadGroup-discard-stream1", xAddParams, map1); client.xadd("xreadGroup-discard-stream1", xAddParams, singletonMap("f2", "v2")); pipe.xgroupCreate("xreadGroup-discard-stream1", "xreadGroup-group", null, false); Map streamQuery1 = singletonMap("xreadGroup-discard-stream1", StreamEntryID.XREADGROUP_UNDELIVERED_ENTRY); Response>>> streams1 = pipe.xreadGroup("xreadGroup-group", "xreadGroup-consumer", XReadGroupParams.xReadGroupParams().count(1), streamQuery1); pipe.sync(); assertThat(streams1.get().stream().map(Entry::getKey).collect(Collectors.toList()), contains("xreadGroup-discard-stream1")); assertThat(streams1.get().stream().map(Entry::getValue).flatMap(List::stream) .map(StreamEntry::getID).collect(Collectors.toList()), contains(firstMessageEntryId)); assertThat(streams1.get().stream().map(Entry::getValue).flatMap(List::stream) .map(StreamEntry::getFields).collect(Collectors.toList()), contains(map1)); // Add third message, the fields of pending message1 will be discarded by redis-server client.xadd("xreadGroup-discard-stream1", xAddParams, singletonMap("f3", "v3")); Map streamQueryPending = singletonMap("xreadGroup-discard-stream1", new StreamEntryID()); Response>>> pendingMessages = pipe.xreadGroup("xreadGroup-group", "xreadGroup-consumer", XReadGroupParams.xReadGroupParams().count(1).noAck(), streamQueryPending); pipe.sync(); assertThat(pendingMessages.get().stream().map(Entry::getKey).collect(Collectors.toList()), contains("xreadGroup-discard-stream1")); assertThat(pendingMessages.get().stream().map(Entry::getValue).flatMap(List::stream) .map(StreamEntry::getID).collect(Collectors.toList()), contains(firstMessageEntryId)); assertThat(pendingMessages.get().stream().map(Entry::getValue).flatMap(List::stream) .map(StreamEntry::getFields).collect(Collectors.toList()), contains(nullValue())); } @Test public void xack() { pipe.xadd("xack-stream", (StreamEntryID) null, singletonMap("f1", "v1")); pipe.xgroupCreate("xack-stream", "xack-group", null, false); Map streamQuery1 = singletonMap("xack-stream", StreamEntryID.XREADGROUP_UNDELIVERED_ENTRY); // Empty Stream Response>>> streams1 = pipe.xreadGroup("xack-group", "xack-consumer", XReadGroupParams.xReadGroupParams().count(1).block(1), streamQuery1); pipe.sync(); List ids = streams1.get().stream().map(Entry::getValue).flatMap(List::stream) .map(StreamEntry::getID).collect(Collectors.toList()); assertThat(ids, hasSize(1)); Response xackResponse = pipe.xack("xack-stream", "xack-group", ids.get(0)); pipe.sync(); assertThat(xackResponse.get(), equalTo(1L)); } @Test public void xpendingWithParams() { Map map = singletonMap("f1", "v1"); StreamEntryID id1 = client.xadd("xpending-stream", (StreamEntryID) null, map); assertEquals("OK", client.xgroupCreate("xpending-stream", "xpending-group", null, false)); Map streamQeury1 = singletonMap("xpending-stream", StreamEntryID.XREADGROUP_UNDELIVERED_ENTRY); // Read the event from Stream put it on pending Response>>> range = pipe.xreadGroup("xpending-group", "xpending-consumer", XReadGroupParams.xReadGroupParams().count(1).block(1), streamQeury1); // Get the pending event Response> pending1 = pipe.xpending("xpending-stream", "xpending-group", new XPendingParams().count(3).consumer("xpending-consumer")); // Without consumer Response> pending2 = pipe.xpending("xpending-stream", "xpending-group", new XPendingParams().count(3)); // with idle Response> pending3 = pipe.xpending("xpending-stream", "xpending-group", new XPendingParams().idle(Duration.ofMinutes(1).toMillis()).count(3)); pipe.sync(); assertThat(pending1.get().stream().map(StreamPendingEntry::getConsumerName).collect(Collectors.toList()), contains("xpending-consumer")); assertThat(pending1.get().stream().map(StreamPendingEntry::getID).collect(Collectors.toList()), contains(id1)); assertThat(pending1.get().stream().map(StreamPendingEntry::getDeliveredTimes).collect(Collectors.toList()), contains(1L)); assertThat(pending2.get().stream().map(StreamPendingEntry::getConsumerName).collect(Collectors.toList()), contains("xpending-consumer")); assertThat(pending2.get().stream().map(StreamPendingEntry::getID).collect(Collectors.toList()), contains(id1)); assertThat(pending2.get().stream().map(StreamPendingEntry::getDeliveredTimes).collect(Collectors.toList()), contains(1L)); assertThat(pending3.get(), empty()); } @Test public void xpendingRange() { StreamEntryID id1 = client.xadd("xpending-stream", (StreamEntryID) null, singletonMap("f1", "v1")); StreamEntryID id2 = client.xadd("xpending-stream", (StreamEntryID) null, singletonMap("f2", "v2")); pipe.xgroupCreate("xpending-stream", "xpending-group", null, false); // read 1 message from the group with each consumer Map streamQeury = singletonMap("xpending-stream", StreamEntryID.XREADGROUP_UNDELIVERED_ENTRY); pipe.xreadGroup("xpending-group", "consumer1", XReadGroupParams.xReadGroupParams().count(1), streamQeury); pipe.xreadGroup("xpending-group", "consumer2", XReadGroupParams.xReadGroupParams().count(1), streamQeury); Response> pending1 = pipe.xpending("xpending-stream", "xpending-group", XPendingParams.xPendingParams("(0", "+", 5)); Response> pending2 = pipe.xpending("xpending-stream", "xpending-group", XPendingParams.xPendingParams(StreamEntryID.MINIMUM_ID, StreamEntryID.MAXIMUM_ID, 5)); pipe.sync(); assertThat(pending1.get().stream().map(StreamPendingEntry::getConsumerName).collect(Collectors.toList()), contains("consumer1", "consumer2")); assertThat(pending1.get().stream().map(StreamPendingEntry::getID).collect(Collectors.toList()), contains(id1, id2)); assertThat(pending2.get().stream().map(StreamPendingEntry::getConsumerName).collect(Collectors.toList()), contains("consumer1", "consumer2")); assertThat(pending2.get().stream().map(StreamPendingEntry::getID).collect(Collectors.toList()), contains(id1, id2)); } @Test public void xclaimWithParams() throws InterruptedException { Map map1 = singletonMap("f1", "v1"); StreamEntryID id1 = client.xadd("xpending-stream", (StreamEntryID) null, map1); pipe.xgroupCreate("xpending-stream", "xpending-group", null, false); // Read the event from Stream put it on pending pipe.xreadGroup("xpending-group", "xpending-consumer", XReadGroupParams.xReadGroupParams().count(1).block(1), singletonMap("xpending-stream", StreamEntryID.XREADGROUP_UNDELIVERED_ENTRY)); // Get the pending event Response> pending = pipe.xpending("xpending-stream", "xpending-group", XPendingParams.xPendingParams().count(3).consumer("xpending-consumer")); // must sync before the sleep pipe.sync(); // Sleep a bit so we can claim events pending for more than 50ms Thread.sleep(100); Response> claimed = pipe.xclaim("xpending-stream", "xpending-group", "xpending-consumer2", 50, XClaimParams.xClaimParams().idle(0).retryCount(0), id1); pipe.sync(); assertThat(pending.get().stream().map(StreamPendingEntry::getConsumerName).collect(Collectors.toList()), contains("xpending-consumer")); assertThat(pending.get().stream().map(StreamPendingEntry::getID).collect(Collectors.toList()), contains(id1)); assertThat(claimed.get().stream().map(StreamEntry::getID).collect(Collectors.toList()), contains(id1)); assertThat(claimed.get().stream().map(StreamEntry::getFields).collect(Collectors.toList()), contains(map1)); } @Test public void xclaimJustId() throws InterruptedException { Map map1 = singletonMap("f1", "v1"); StreamEntryID id1 = client.xadd("xpending-stream", (StreamEntryID) null, map1); pipe.xgroupCreate("xpending-stream", "xpending-group", null, false); // Read the event from Stream put it on pending pipe.xreadGroup("xpending-group", "xpending-consumer", XReadGroupParams.xReadGroupParams().count(1).block(1), singletonMap("xpending-stream", StreamEntryID.XREADGROUP_UNDELIVERED_ENTRY)); // Get the pending event Response> pending = pipe.xpending("xpending-stream", "xpending-group", XPendingParams.xPendingParams().count(3).consumer("xpending-consumer")); // must sync before the sleep pipe.sync(); // Sleep for 100ms so we can claim events pending for more than 50ms Thread.sleep(100); Response> claimedIds = pipe.xclaimJustId("xpending-stream", "xpending-group", "xpending-consumer2", 50, XClaimParams.xClaimParams().idle(0).retryCount(0), id1); pipe.sync(); assertThat(pending.get().stream().map(StreamPendingEntry::getConsumerName).collect(Collectors.toList()), contains("xpending-consumer")); assertThat(pending.get().stream().map(StreamPendingEntry::getID).collect(Collectors.toList()), contains(id1)); assertThat(claimedIds.get(), contains(id1)); } @Test public void xautoclaim() throws InterruptedException { Map map1 = singletonMap("f1", "v1"); StreamEntryID id1 = client.xadd("xpending-stream", (StreamEntryID) null, map1); pipe.xgroupCreate("xpending-stream", "xpending-group", null, false); // Read the event from Stream put it on pending pipe.xreadGroup("xpending-group", "xpending-consumer", XReadGroupParams.xReadGroupParams().count(1).block(1), singletonMap("xpending-stream", StreamEntryID.XREADGROUP_UNDELIVERED_ENTRY)); // Get the pending event Response> pending = pipe.xpending("xpending-stream", "xpending-group", XPendingParams.xPendingParams().count(3).consumer("xpending-consumer")); pipe.sync(); // Sleep for 100ms so we can auto claim events pending for more than 50ms Thread.sleep(100); // Auto claim pending events to different consumer Response>> autoclaimed = pipe.xautoclaim("xpending-stream", "xpending-group", "xpending-consumer2", 50, new StreamEntryID(), new XAutoClaimParams().count(1)); pipe.sync(); assertThat(pending.get().stream().map(StreamPendingEntry::getConsumerName).collect(Collectors.toList()), contains("xpending-consumer")); assertThat(pending.get().stream().map(StreamPendingEntry::getID).collect(Collectors.toList()), contains(id1)); assertThat(autoclaimed.get().getValue().stream().map(StreamEntry::getID).collect(Collectors.toList()), contains(id1)); assertThat(autoclaimed.get().getValue().stream().map(StreamEntry::getFields).collect(Collectors.toList()), contains(map1)); } @Test public void xautoclaimBinary() throws InterruptedException { Map map1 = singletonMap("f1", "v1"); StreamEntryID id1 = client.xadd("xpending-stream", XAddParams.xAddParams(), map1); pipe.xgroupCreate("xpending-stream", "xpending-group", null, false); // Read the event from Stream put it on pending pipe.xreadGroup("xpending-group", "xpending-consumer", XReadGroupParams.xReadGroupParams().count(1).block(1), singletonMap("xpending-stream", StreamEntryID.XREADGROUP_UNDELIVERED_ENTRY)); // Get the pending event Response> pending = pipe.xpending("xpending-stream", "xpending-group", XPendingParams.xPendingParams().count(3).consumer("xpending-consumer")); pipe.sync(); // Sleep for 100ms so we can auto claim events pending for more than 50ms Thread.sleep(100); // Auto claim pending events to different consumer Response> autoclaimed = pipe.xautoclaim(SafeEncoder.encode("xpending-stream"), SafeEncoder.encode("xpending-group"), SafeEncoder.encode("xpending-consumer2"), 50, SafeEncoder.encode(new StreamEntryID().toString()), new XAutoClaimParams().count(1)); pipe.sync(); assertThat(pending.get().stream().map(StreamPendingEntry::getConsumerName).collect(Collectors.toList()), contains("xpending-consumer")); assertThat(pending.get().stream().map(StreamPendingEntry::getID).collect(Collectors.toList()), contains(id1)); Map.Entry> autoclaimedParsed = BuilderFactory.STREAM_AUTO_CLAIM_RESPONSE.build(autoclaimed.get()); assertThat(autoclaimedParsed.getValue().stream().map(StreamEntry::getID).collect(Collectors.toList()), contains(id1)); assertThat(autoclaimedParsed.getValue().stream().map(StreamEntry::getFields).collect(Collectors.toList()), contains(map1)); } @Test public void xautoclaimJustId() throws InterruptedException { Map map1 = singletonMap("f1", "v1"); StreamEntryID id1 = client.xadd("xpending-stream", XAddParams.xAddParams(), map1); pipe.xgroupCreate("xpending-stream", "xpending-group", null, false); // Read the event from Stream put it on pending pipe.xreadGroup("xpending-group", "xpending-consumer", XReadGroupParams.xReadGroupParams().count(1).block(1), singletonMap("xpending-stream", StreamEntryID.XREADGROUP_UNDELIVERED_ENTRY)); // Get the pending event Response> pending = pipe.xpending("xpending-stream", "xpending-group", XPendingParams.xPendingParams().count(3).consumer("xpending-consumer")); pipe.sync(); // Sleep for 100ms so we can auto claim events pending for more than 50ms Thread.sleep(100); // Auto claim pending events to different consumer Response>> claimedIds = pipe.xautoclaimJustId("xpending-stream", "xpending-group", "xpending-consumer2", 50, new StreamEntryID(), new XAutoClaimParams().count(1)); pipe.sync(); assertThat(pending.get().stream().map(StreamPendingEntry::getConsumerName).collect(Collectors.toList()), contains("xpending-consumer")); assertThat(pending.get().stream().map(StreamPendingEntry::getID).collect(Collectors.toList()), contains(id1)); assertThat(claimedIds.get().getValue(), contains(id1)); } @Test public void xautoclaimJustIdBinary() throws InterruptedException { Map map1 = singletonMap("f1", "v1"); StreamEntryID id1 = client.xadd("xpending-stream", XAddParams.xAddParams(), map1); pipe.xgroupCreate("xpending-stream", "xpending-group", null, false); // Read the event from Stream put it on pending pipe.xreadGroup("xpending-group", "xpending-consumer", XReadGroupParams.xReadGroupParams().count(1).block(1), singletonMap("xpending-stream", StreamEntryID.XREADGROUP_UNDELIVERED_ENTRY)); // Get the pending event Response> pending = pipe.xpending("xpending-stream", "xpending-group", XPendingParams.xPendingParams().count(3).consumer("xpending-consumer")); pipe.sync(); // Sleep for 100ms so we can auto claim events pending for more than 50ms Thread.sleep(100); // Auto claim pending events to different consumer Response> autoclaimed = pipe.xautoclaimJustId(SafeEncoder.encode("xpending-stream"), SafeEncoder.encode("xpending-group"), SafeEncoder.encode("xpending-consumer2"), 50, SafeEncoder.encode(new StreamEntryID().toString()), new XAutoClaimParams().count(1)); pipe.sync(); assertThat(pending.get().stream().map(StreamPendingEntry::getConsumerName).collect(Collectors.toList()), contains("xpending-consumer")); assertThat(pending.get().stream().map(StreamPendingEntry::getID).collect(Collectors.toList()), contains(id1)); Entry> autoclaimedParsed = BuilderFactory.STREAM_AUTO_CLAIM_JUSTID_RESPONSE.build(autoclaimed.get()); assertThat(autoclaimedParsed.getValue(), contains(id1)); } @Test public void xinfo() throws InterruptedException { final String STREAM_NAME = "xadd-stream1"; final String F1 = "f1"; final String V1 = "v1"; final String V2 = "v2"; final String G1 = "G1"; final String G2 = "G2"; final String MY_CONSUMER = "myConsumer"; final String MY_CONSUMER2 = "myConsumer2"; Map map1 = new HashMap<>(); map1.put(F1, V1); StreamEntryID id1 = client.xadd(STREAM_NAME, (StreamEntryID) null, map1); map1.put(F1, V2); StreamEntryID id2 = client.xadd(STREAM_NAME, (StreamEntryID) null, map1); Response streamInfoResponse = pipe.xinfoStream(STREAM_NAME); pipe.xgroupCreate(STREAM_NAME, G1, StreamEntryID.XGROUP_LAST_ENTRY, false); Map streamQuery1 = singletonMap(STREAM_NAME, new StreamEntryID("0-0")); pipe.xreadGroup(G1, MY_CONSUMER, XReadGroupParams.xReadGroupParams().count(1), streamQuery1); pipe.sync(); Thread.sleep(1); Response> groupInfoResponse = pipe.xinfoGroups(STREAM_NAME); Response> consumersInfoResponse = pipe.xinfoConsumers(STREAM_NAME, G1); Response> consumerInfoResponse = pipe.xinfoConsumers2(STREAM_NAME, G1); pipe.sync(); // Stream info test StreamInfo streamInfo = streamInfoResponse.get(); assertEquals(2L, streamInfo.getStreamInfo().get(StreamInfo.LENGTH)); assertEquals(1L, streamInfo.getStreamInfo().get(StreamInfo.RADIX_TREE_KEYS)); assertEquals(2L, streamInfo.getStreamInfo().get(StreamInfo.RADIX_TREE_NODES)); assertEquals(0L, streamInfo.getStreamInfo().get(StreamInfo.GROUPS)); assertEquals(V1, ((StreamEntry) streamInfo.getStreamInfo().get(StreamInfo.FIRST_ENTRY)).getFields().get(F1)); assertEquals(V2, ((StreamEntry) streamInfo.getStreamInfo().get(StreamInfo.LAST_ENTRY)).getFields().get(F1)); assertEquals(id2, streamInfo.getStreamInfo().get(StreamInfo.LAST_GENERATED_ID)); // Using getters assertEquals(2, streamInfo.getLength()); assertEquals(1, streamInfo.getRadixTreeKeys()); assertEquals(2, streamInfo.getRadixTreeNodes()); assertEquals(0, streamInfo.getGroups()); assertEquals(V1, streamInfo.getFirstEntry().getFields().get(F1)); assertEquals(V2, streamInfo.getLastEntry().getFields().get(F1)); assertEquals(id2, streamInfo.getLastGeneratedId()); // Group info test List groupInfo = groupInfoResponse.get(); assertEquals(1, groupInfo.size()); assertEquals(G1, groupInfo.get(0).getGroupInfo().get(StreamGroupInfo.NAME)); assertEquals(1L, groupInfo.get(0).getGroupInfo().get(StreamGroupInfo.CONSUMERS)); assertEquals(0L, groupInfo.get(0).getGroupInfo().get(StreamGroupInfo.PENDING)); assertEquals(id2, groupInfo.get(0).getGroupInfo().get(StreamGroupInfo.LAST_DELIVERED)); // Using getters assertEquals(1, groupInfo.size()); assertEquals(G1, groupInfo.get(0).getName()); assertEquals(1, groupInfo.get(0).getConsumers()); assertEquals(0, groupInfo.get(0).getPending()); assertEquals(id2, groupInfo.get(0).getLastDeliveredId()); // Consumers info test List consumersInfo = consumersInfoResponse.get(); assertEquals(MY_CONSUMER, consumersInfo.get(0).getConsumerInfo().get(StreamConsumersInfo.NAME)); assertEquals(0L, consumersInfo.get(0).getConsumerInfo().get(StreamConsumersInfo.PENDING)); assertTrue((Long) consumersInfo.get(0).getConsumerInfo().get(StreamConsumersInfo.IDLE) > 0); // Using getters assertEquals(MY_CONSUMER, consumersInfo.get(0).getName()); assertEquals(0L, consumersInfo.get(0).getPending()); assertThat(consumersInfo.get(0).getIdle(), Matchers.greaterThanOrEqualTo(0L)); if (RedisVersionUtil.getRedisVersion(client).isGreaterThanOrEqualTo(RedisVersion.V7_0_0)) { assertThat(consumersInfo.get(0).getInactive(), Matchers.any(Long.class)); } // Consumer info test List consumerInfo = consumerInfoResponse.get(); assertEquals(MY_CONSUMER, consumerInfo.get(0).getConsumerInfo().get(StreamConsumerInfo.NAME)); assertEquals(0L, consumerInfo.get(0).getConsumerInfo().get(StreamConsumerInfo.PENDING)); assertTrue((Long) consumerInfo.get(0).getConsumerInfo().get(StreamConsumerInfo.IDLE) > 0); // Using getters assertEquals(MY_CONSUMER, consumerInfo.get(0).getName()); assertEquals(0L, consumerInfo.get(0).getPending()); assertThat(consumerInfo.get(0).getIdle(), Matchers.greaterThanOrEqualTo(0L)); if (RedisVersionUtil.getRedisVersion(client).isGreaterThanOrEqualTo(RedisVersion.V7_0_0)) { assertThat(consumerInfo.get(0).getInactive(), Matchers.any(Long.class)); } // test with more groups and consumers pipe.xgroupCreate(STREAM_NAME, G2, StreamEntryID.XGROUP_LAST_ENTRY, false); pipe.xreadGroup(G1, MY_CONSUMER2, XReadGroupParams.xReadGroupParams().count(1), streamQuery1); pipe.xreadGroup(G2, MY_CONSUMER, XReadGroupParams.xReadGroupParams().count(1), streamQuery1); pipe.xreadGroup(G2, MY_CONSUMER2, XReadGroupParams.xReadGroupParams().count(1), streamQuery1); Response> manyGroupsInfoResponse = pipe.xinfoGroups(STREAM_NAME); Response> manyConsumersInfoResponse = pipe.xinfoConsumers(STREAM_NAME, G2); Response> manyConsumerInfoResponse = pipe.xinfoConsumers2(STREAM_NAME, G2); Response streamInfoFullResponse = pipe.xinfoStreamFull(STREAM_NAME); Response streamInfoFull10Response = pipe.xinfoStreamFull(STREAM_NAME, 10); pipe.sync(); List manyGroupsInfo = manyGroupsInfoResponse.get(); List manyConsumersInfo = manyConsumersInfoResponse.get(); List manyConsumerInfo = manyConsumerInfoResponse.get(); StreamFullInfo streamInfoFull = streamInfoFullResponse.get(); StreamFullInfo streamInfoFull10 = streamInfoFull10Response.get(); assertEquals(2, manyGroupsInfo.size()); assertEquals(2, manyConsumersInfo.size()); assertEquals(2, manyConsumerInfo.size()); assertEquals(2, streamInfoFull.getEntries().size()); assertEquals(2, streamInfoFull.getGroups().size()); assertEquals(2, streamInfoFull.getLength()); assertEquals(1, streamInfoFull.getRadixTreeKeys()); assertEquals(2, streamInfoFull.getRadixTreeNodes()); assertEquals(0, streamInfo.getGroups()); assertEquals(G1, streamInfoFull.getGroups().get(0).getName()); assertEquals(G2, streamInfoFull.getGroups().get(1).getName()); assertEquals(V1, streamInfoFull.getEntries().get(0).getFields().get(F1)); assertEquals(V2, streamInfoFull.getEntries().get(1).getFields().get(F1)); assertEquals(id2, streamInfoFull.getLastGeneratedId()); assertEquals(G1, streamInfoFull10.getGroups().get(0).getName()); assertEquals(G2, streamInfoFull10.getGroups().get(1).getName()); assertEquals(V1, streamInfoFull10.getEntries().get(0).getFields().get(F1)); assertEquals(V2, streamInfoFull10.getEntries().get(1).getFields().get(F1)); assertEquals(id2, streamInfoFull10.getLastGeneratedId()); // Not existing key - redis cli return error so we expect exception pipe.xinfoStream("random"); assertThat(pipe.syncAndReturnAll(), contains( both(instanceOf(JedisDataException.class)).and(hasToString(containsString("ERR no such key"))) )); } @Test public void xinfoStreamFullWithPending() { Map map = singletonMap("f1", "v1"); StreamEntryID id1 = client.xadd("streamfull2", (StreamEntryID) null, map); StreamEntryID id2 = client.xadd("streamfull2", (StreamEntryID) null, map); client.xgroupCreate("streamfull2", "xreadGroup-group", null, false); Map streamQeury1 = singletonMap("streamfull2", StreamEntryID.XREADGROUP_UNDELIVERED_ENTRY); Response>>> pending = pipe.xreadGroup("xreadGroup-group", "xreadGroup-consumer", XReadGroupParams.xReadGroupParams().count(1), streamQeury1); Response fullResult = pipe.xinfoStreamFull("streamfull2"); pipe.sync(); assertThat(pending.get(), hasSize(1)); StreamFullInfo full = fullResult.get(); assertEquals(1, full.getGroups().size()); StreamGroupFullInfo group = full.getGroups().get(0); assertEquals("xreadGroup-group", group.getName()); assertEquals(1, group.getPending().size()); List groupPendingEntry = group.getPending().get(0); assertEquals(id1, groupPendingEntry.get(0)); assertEquals("xreadGroup-consumer", groupPendingEntry.get(1)); assertEquals(1, group.getConsumers().size()); StreamConsumerFullInfo consumer = group.getConsumers().get(0); assertEquals("xreadGroup-consumer", consumer.getName()); assertThat(consumer.getSeenTime(), Matchers.greaterThanOrEqualTo(0L)); if (RedisVersionUtil.getRedisVersion(client).isGreaterThanOrEqualTo(RedisVersion.V7_0_0)) { assertThat(consumer.getActiveTime(), Matchers.greaterThanOrEqualTo(0L)); } assertEquals(1, consumer.getPending().size()); List consumerPendingEntry = consumer.getPending().get(0); assertEquals(id1, consumerPendingEntry.get(0)); } @Test @EnabledOnCommand("XCFGSET") public void xcfgset() { // Add an entry to create the stream client.xadd("xcfgset-stream", StreamEntryID.NEW_ENTRY, singletonMap("field", "value")); // Configure idempotent producer settings via pipeline Response response = pipe.xcfgset("xcfgset-stream", XCfgSetParams.xCfgSetParams().idmpDuration(1000).idmpMaxsize(500)); pipe.sync(); assertEquals("OK", response.get()); // Verify settings via XINFO STREAM StreamInfo info = client.xinfoStream("xcfgset-stream"); assertEquals(Long.valueOf(1000), info.getIdmpDuration()); assertEquals(Long.valueOf(500), info.getIdmpMaxsize()); } } ================================================ FILE: src/test/java/redis/clients/jedis/commands/unified/search/FTHybridCommandsTestBase.java ================================================ package redis.clients.jedis.commands.unified.search; import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.closeTo; import static org.hamcrest.Matchers.empty; import static org.hamcrest.Matchers.equalTo; import static org.hamcrest.Matchers.greaterThan; import static org.hamcrest.Matchers.greaterThanOrEqualTo; import static org.hamcrest.Matchers.not; import static org.hamcrest.Matchers.notNullValue; import static org.hamcrest.Matchers.nullValue; import static redis.clients.jedis.util.AssertUtil.assertOK; import java.nio.ByteBuffer; import java.nio.ByteOrder; import java.util.HashMap; import java.util.Map; import io.redis.test.annotations.SinceRedisVersion; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Nested; import org.junit.jupiter.api.Tag; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.TestInstance; import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.Arguments; import org.junit.jupiter.params.provider.MethodSource; import java.util.stream.Stream; import redis.clients.jedis.Endpoints; import redis.clients.jedis.RedisProtocol; import redis.clients.jedis.commands.unified.UnifiedJedisCommandsTestBase; import redis.clients.jedis.search.Document; import redis.clients.jedis.search.FTCreateParams; import redis.clients.jedis.search.IndexDataType; import redis.clients.jedis.search.Scorer; import redis.clients.jedis.search.Scorers; import redis.clients.jedis.search.aggr.Group; import redis.clients.jedis.search.aggr.Reducers; import redis.clients.jedis.search.aggr.SortedField; import redis.clients.jedis.search.Apply; import redis.clients.jedis.search.Filter; import redis.clients.jedis.search.Combiners; import redis.clients.jedis.search.hybrid.FTHybridParams; import redis.clients.jedis.search.hybrid.FTHybridPostProcessingParams; import redis.clients.jedis.search.hybrid.FTHybridSearchParams; import redis.clients.jedis.search.hybrid.FTHybridVectorParams; import redis.clients.jedis.search.hybrid.HybridResult; import redis.clients.jedis.search.Limit; import redis.clients.jedis.search.schemafields.NumericField; import redis.clients.jedis.search.schemafields.TagField; import redis.clients.jedis.search.schemafields.TextField; import redis.clients.jedis.search.schemafields.VectorField; /** * Base test class for FT.HYBRID command using the UnifiedJedis pattern. Tests hybrid search * functionality combining text search and vector similarity. */ @Tag("integration") @Tag("search") @SinceRedisVersion("8.4.0") public abstract class FTHybridCommandsTestBase extends UnifiedJedisCommandsTestBase { @BeforeAll public static void prepareEndpoint() { endpoint = Endpoints.getRedisEndpoint("modules-docker"); } private static final String INDEX_NAME = "hybrid-test-idx"; private static final String PREFIX = "product:hybrid:"; private static final int VECTOR_DIM = 10; protected byte[] queryVector; public FTHybridCommandsTestBase(RedisProtocol protocol) { super(protocol); } @BeforeEach public void setUpTestData() { if (!jedis.ftList().contains(INDEX_NAME)) { createHybridIndex(); createSampleProducts(); } // Always initialize query vector (cheap operation) queryVector = floatArrayToByteArray( new float[] { 0.1f, 0.2f, 0.3f, 0.4f, 0.5f, 0.6f, 0.7f, 0.8f, 0.9f, 1.0f }); } // ========== Setup Helper Methods ========== private void createHybridIndex() { Map vectorAttrs = new HashMap<>(); vectorAttrs.put("TYPE", "FLOAT32"); vectorAttrs.put("DIM", VECTOR_DIM); vectorAttrs.put("DISTANCE_METRIC", "COSINE"); assertOK(jedis.ftCreate(INDEX_NAME, FTCreateParams.createParams().on(IndexDataType.HASH).prefix(PREFIX), TextField.of("id"), TextField.of("title"), TagField.of("category"), TagField.of("brand"), NumericField.of("price"), NumericField.of("rating"), VectorField.builder().fieldName("image_embedding").algorithm(VectorField.VectorAlgorithm.HNSW) .attributes(vectorAttrs).build())); } private void createSampleProducts() { // Electronics - Apple products createProduct("1", "Apple iPhone 15 Pro smartphone with advanced camera", "electronics", "apple", "999", "4.8", new float[] { 0.1f, 0.2f, 0.3f, 0.4f, 0.5f, 0.6f, 0.7f, 0.8f, 0.9f, 1.0f }); createProduct("4", "Apple iPhone 15 Pro smartphone camera", "electronics", "apple", "999", "4.8", new float[] { 0.1f, 0.2f, 0.3f, 0.4f, 0.5f, 0.6f, 0.7f, 0.8f, 0.9f, 1.0f }); createProduct("10", "Apple iPhone 15 Pro smartphone camera", "electronics", "apple", "999", "4.8", new float[] { 0.1f, 0.2f, 0.3f, 0.4f, 0.5f, 0.6f, 0.7f, 0.8f, 0.9f, 1.0f }); // Electronics - Samsung products createProduct("2", "Samsung Galaxy S24 smartphone camera", "electronics", "samsung", "799", "4.6", new float[] { 0.15f, 0.25f, 0.35f, 0.45f, 0.55f, 0.65f, 0.75f, 0.85f, 0.95f, 0.9f }); createProduct("5", "Samsung Galaxy S24", "electronics", "samsung", "799", "4.6", new float[] { 0.15f, 0.25f, 0.35f, 0.45f, 0.55f, 0.65f, 0.75f, 0.85f, 0.95f, 0.9f }); // Electronics - Google products createProduct("3", "Google Pixel 8 Pro camera smartphone", "electronics", "google", "699", "4.5", new float[] { 0.2f, 0.3f, 0.4f, 0.5f, 0.6f, 0.7f, 0.8f, 0.9f, 1.0f, 0.8f }); createProduct("6", "Google Pixel 8 Pro", "electronics", "google", "699", "4.5", new float[] { 0.2f, 0.3f, 0.4f, 0.5f, 0.6f, 0.7f, 0.8f, 0.9f, 1.0f, 0.8f }); // Other categories createProduct("7", "Best T-shirt", "apparel", "denim", "255", "4.2", new float[] { 0.12f, 0.22f, 0.32f, 0.42f, 0.52f, 0.62f, 0.72f, 0.82f, 0.92f, 0.85f }); createProduct("8", "Best makeup", "beauty", "loreal", "155", "4.4", new float[] { 0.18f, 0.28f, 0.38f, 0.48f, 0.58f, 0.68f, 0.78f, 0.88f, 0.98f, 0.75f }); createProduct("9", "Best punching bag", "sports", "lonsdale", "733", "4.6", new float[] { 0.11f, 0.21f, 0.31f, 0.41f, 0.51f, 0.61f, 0.71f, 0.81f, 0.91f, 0.95f }); } private void createProduct(String id, String title, String category, String brand, String price, String rating, float[] embedding) { Map fields = new HashMap<>(); fields.put("id".getBytes(), id.getBytes()); fields.put("title".getBytes(), title.getBytes()); fields.put("category".getBytes(), category.getBytes()); fields.put("brand".getBytes(), brand.getBytes()); fields.put("price".getBytes(), price.getBytes()); fields.put("rating".getBytes(), rating.getBytes()); fields.put("image_embedding".getBytes(), floatArrayToByteArray(embedding)); jedis.hset((PREFIX + id).getBytes(), fields); } private byte[] floatArrayToByteArray(float[] floats) { ByteBuffer buffer = ByteBuffer.allocate(floats.length * 4).order(ByteOrder.LITTLE_ENDIAN); for (float f : floats) { buffer.putFloat(f); } return buffer.array(); } // ========== Test Methods ========== @Test public void testComprehensiveFtHybridWithAllFeatures() { FTHybridPostProcessingParams postProcessing = FTHybridPostProcessingParams.builder() .load("price", "brand", "@category") .groupBy(new Group("@brand").reduce(Reducers.sum("@price").as("sum")) .reduce(Reducers.count().as("count"))) .apply(Apply.of("@sum * 0.9", "discounted_price")) .sortBy(SortedField.asc("@sum"), SortedField.desc("@count")).filter(Filter.of("@sum > 700")) .limit(Limit.of(0, 20)).build(); FTHybridParams hybridArgs = FTHybridParams.builder() .search(FTHybridSearchParams.builder().query("@category:{electronics} smartphone camera") .scorer(Scorers.bm25std()).scoreAlias("text_score").build()) .vectorSearch(FTHybridVectorParams.builder().field("@image_embedding").vector("vector") .method(FTHybridVectorParams.Knn.of(20).efRuntime(150)) .filter("(@brand:{apple|samsung|google}) (@price:[500 1500]) (@category:{electronics})") .scoreAlias("vector_score").build()) .combine(Combiners.linear().alpha(0.7).beta(0.3).window(25)).postProcessing(postProcessing) .param("discount_rate", "0.9").param("vector", queryVector).build(); HybridResult reply = jedis.ftHybrid(INDEX_NAME, hybridArgs); assertThat(reply, notNullValue()); assertThat(reply.getDocuments(), not(empty())); assertThat(reply.getTotalResults(), equalTo(3L)); assertThat(reply.getDocuments().size(), equalTo(3)); assertThat(reply.getWarnings().size(), greaterThanOrEqualTo(0)); assertThat(reply.getExecutionTime(), greaterThan(0.0)); // Verify first result (google) - exact field values Document r1 = reply.getDocuments().get(0); assertThat(r1.get("brand"), equalTo("google")); assertThat(r1.get("count"), equalTo("2")); assertThat(r1.get("sum"), equalTo("1398")); assertThat(r1.get("discounted_price"), equalTo("1258.2")); // Verify second result (samsung) - exact field values Document r2 = reply.getDocuments().get(1); assertThat(r2.get("brand"), equalTo("samsung")); assertThat(r2.get("count"), equalTo("2")); assertThat(r2.get("sum"), equalTo("1598")); assertThat(r2.get("discounted_price"), equalTo("1438.2")); // Verify third result (apple) - exact field values Document r3 = reply.getDocuments().get(2); assertThat(r3.get("brand"), equalTo("apple")); assertThat(r3.get("count"), equalTo("3")); assertThat(r3.get("sum"), equalTo("2997")); assertThat(r3.get("discounted_price"), equalTo("2697.3")); } @Test public void testLoadSpecificFields() { // Test LOAD with specific fields FTHybridPostProcessingParams postProcessing = FTHybridPostProcessingParams.builder() .load("title", "@price", "brand").build(); FTHybridParams hybridArgs = FTHybridParams.builder() .search(FTHybridSearchParams.builder().query("@category:{electronics} smartphone") .scoreAlias("text_score").build()) .vectorSearch(FTHybridVectorParams.builder().field("@image_embedding").vector("vector") .method(FTHybridVectorParams.Knn.of(5)).scoreAlias("vector_score").build()) .combine(Combiners.linear().alpha(0.5).beta(0.5)).postProcessing(postProcessing) .param("vector", queryVector).build(); HybridResult reply = jedis.ftHybrid(INDEX_NAME, hybridArgs); assertThat(reply, notNullValue()); assertThat(reply.getDocuments(), not(empty())); // Result count assertions assertThat(reply.getTotalResults(), greaterThan(0L)); assertThat(reply.getDocuments().size(), greaterThan(0)); // Field count and content assertions Document firstResult = reply.getDocuments().get(0); // Loaded fields should be present assertThat(firstResult.hasProperty("title"), equalTo(true)); assertThat(firstResult.hasProperty("price"), equalTo(true)); assertThat(firstResult.hasProperty("brand"), equalTo(true)); // Non-loaded document fields should NOT be present assertThat(firstResult.hasProperty("category"), equalTo(false)); assertThat(firstResult.hasProperty("rating"), equalTo(false)); assertThat(firstResult.hasProperty("image_embedding"), equalTo(false)); } @Test @SinceRedisVersion("8.6.0") public void testLoadAllFields() { // Test LOAD * to load all fields FTHybridPostProcessingParams postProcessing = FTHybridPostProcessingParams.builder().loadAll() .build(); FTHybridParams hybridArgs = FTHybridParams.builder() .search(FTHybridSearchParams.builder().query("@category:{electronics}") .scoreAlias("text_score").build()) .vectorSearch(FTHybridVectorParams.builder().field("@image_embedding").vector("vector") .method(FTHybridVectorParams.Knn.of(3)).scoreAlias("vector_score").build()) .combine(Combiners.linear().alpha(0.5).beta(0.5)).postProcessing(postProcessing) .param("vector", queryVector).build(); HybridResult reply = jedis.ftHybrid(INDEX_NAME, hybridArgs); assertThat(reply, notNullValue()); assertThat(reply.getDocuments(), not(empty())); // Result count assertions assertThat(reply.getTotalResults(), greaterThan(0L)); assertThat(reply.getDocuments().size(), greaterThan(0)); // Field count and content assertions Document firstResult = reply.getDocuments().get(0); // All document fields should be present assertThat(firstResult.hasProperty("title"), equalTo(true)); assertThat(firstResult.hasProperty("category"), equalTo(true)); assertThat(firstResult.hasProperty("brand"), equalTo(true)); assertThat(firstResult.hasProperty("price"), equalTo(true)); assertThat(firstResult.hasProperty("rating"), equalTo(true)); // Vector field should also be present with LOAD * assertThat(firstResult.hasProperty("image_embedding"), equalTo(true)); // Score fields should be present assertThat(firstResult.hasProperty("text_score"), equalTo(true)); } @Test public void testLoadWithGroupBy() { // Test LOAD behavior with GROUPBY - loaded fields should be available for grouping FTHybridPostProcessingParams postProcessing = FTHybridPostProcessingParams.builder() .load("brand", "price", "category").groupBy(new Group("@brand") .reduce(Reducers.count().as("count")).reduce(Reducers.avg("@price").as("avg_price"))) .build(); FTHybridParams hybridArgs = FTHybridParams.builder() .search(FTHybridSearchParams.builder().query("@category:{electronics}") .scoreAlias("text_score").build()) .vectorSearch(FTHybridVectorParams.builder().field("@image_embedding").vector("vector") .method(FTHybridVectorParams.Knn.of(10)).scoreAlias("vector_score").build()) .combine(Combiners.linear().alpha(0.5).beta(0.5)).postProcessing(postProcessing) .param("vector", queryVector).build(); HybridResult reply = jedis.ftHybrid(INDEX_NAME, hybridArgs); assertThat(reply, notNullValue()); assertThat(reply.getDocuments(), not(empty())); // Result count assertions assertThat(reply.getTotalResults(), greaterThan(0L)); assertThat(reply.getDocuments().size(), greaterThan(0)); // Field count and content assertions Document firstResult = reply.getDocuments().get(0); // Grouping field should be present assertThat(firstResult.hasProperty("brand"), equalTo(true)); // Reducer results should be present assertThat(firstResult.hasProperty("count"), equalTo(true)); assertThat(firstResult.hasProperty("avg_price"), equalTo(true)); // Original loaded fields (price, category) should NOT be present after GROUPBY // GROUPBY transforms the results, only group keys and reducers remain assertThat(firstResult.hasProperty("price"), equalTo(false)); assertThat(firstResult.hasProperty("category"), equalTo(false)); assertThat(firstResult.hasProperty("title"), equalTo(false)); } @Test public void testLoadWithApply() { // Test LOAD with APPLY - loaded fields should be available for expressions FTHybridPostProcessingParams postProcessing = FTHybridPostProcessingParams.builder() .load("price", "rating") // with alias .apply(Apply.of("@price * @rating", "value_score")) // without alias returns field name as the expression itself .apply(Apply.of("@price * 0.9")).build(); FTHybridParams hybridArgs = FTHybridParams.builder() .search(FTHybridSearchParams.builder().query("smartphone").build()) .vectorSearch(FTHybridVectorParams.builder().field("@image_embedding").vector("vector") .method(FTHybridVectorParams.Knn.of(10)).scoreAlias("vector_score").build()) .combine(Combiners.linear().alpha(0.5).beta(0.5).window(25)).postProcessing(postProcessing) .param("vector", queryVector).build(); HybridResult reply = jedis.ftHybrid(INDEX_NAME, hybridArgs); assertThat(reply, notNullValue()); assertThat(reply.getDocuments(), not(empty())); // Result count assertions assertThat(reply.getTotalResults(), greaterThan(0L)); assertThat(reply.getDocuments().size(), greaterThan(0)); // Field count and content assertions Document firstResult = reply.getDocuments().get(0); // Loaded fields should be present assertThat(firstResult.hasProperty("price"), equalTo(true)); assertThat(firstResult.hasProperty("rating"), equalTo(true)); // Computed fields (from APPLY) should be present assertThat(firstResult.hasProperty("value_score"), equalTo(true)); assertThat(firstResult.hasProperty("@price * 0.9"), equalTo(true)); // Non-loaded document fields should NOT be present assertThat(firstResult.hasProperty("title"), equalTo(false)); } @Test public void testLoadWithFilter() { // Test LOAD with FILTER - loaded fields should be available for filtering FTHybridPostProcessingParams postProcessing = FTHybridPostProcessingParams.builder() .load("price", "brand", "title").filter(Filter.of("@price > 700")) .sortBy(SortedField.asc("@price")).build(); FTHybridParams hybridArgs = FTHybridParams.builder() .search(FTHybridSearchParams.builder().query("@category:{electronics}") .scoreAlias("text_score").build()) .vectorSearch(FTHybridVectorParams.builder().field("@image_embedding").vector("vector") .method(FTHybridVectorParams.Knn.of(10)).scoreAlias("vector_score").build()) .combine(Combiners.linear().alpha(0.5).beta(0.5)).postProcessing(postProcessing) .param("vector", queryVector).build(); HybridResult reply = jedis.ftHybrid(INDEX_NAME, hybridArgs); assertThat(reply, notNullValue()); assertThat(reply.getDocuments(), not(empty())); // Result count assertions assertThat(reply.getTotalResults(), greaterThan(0L)); assertThat(reply.getDocuments().size(), greaterThan(0)); // Verify loaded fields are present and FILTER condition is applied for (Document result : reply.getDocuments()) { // Loaded fields should be present assertThat(result.hasProperty("price"), equalTo(true)); assertThat(result.hasProperty("brand"), equalTo(true)); assertThat(result.hasProperty("title"), equalTo(true)); // FILTER condition: all results should have price > 700 double price = Double.parseDouble(result.getString("price")); assertThat(price, greaterThan(700.0)); // Non-loaded document fields should NOT be present assertThat(result.hasProperty("category"), equalTo(false)); assertThat(result.hasProperty("rating"), equalTo(false)); assertThat(result.hasProperty("image_embedding"), equalTo(false)); } } @Test public void testLoadNoFields() { // Test without LOAD - should return only document IDs and scores FTHybridPostProcessingParams postProcessing = FTHybridPostProcessingParams.builder() .limit(Limit.of(0, 5)).build(); FTHybridParams hybridArgs = FTHybridParams.builder() .search(FTHybridSearchParams.builder().query("smartphone").scoreAlias("text_score").build()) .vectorSearch(FTHybridVectorParams.builder().field("@image_embedding").vector("vector") .method(FTHybridVectorParams.Knn.of(5)).scoreAlias("vector_score").build()) .combine(Combiners.linear().alpha(0.5).beta(0.5)).postProcessing(postProcessing) .param("vector", queryVector).build(); HybridResult reply = jedis.ftHybrid(INDEX_NAME, hybridArgs); assertThat(reply, notNullValue()); assertThat(reply.getDocuments(), not(empty())); // Result count assertions assertThat(reply.getTotalResults(), greaterThan(0L)); assertThat(reply.getDocuments().size(), greaterThan(0)); // Without LOAD, results should contain only keys and scores, NO document fields Document firstResult = reply.getDocuments().get(0); // Document id and score should be present (extracted from __key and __score) assertThat(firstResult.getId(), notNullValue()); assertThat(firstResult.getScore(), notNullValue()); // Document fields should NOT be present when no LOAD is specified assertThat(firstResult.hasProperty("title"), equalTo(false)); assertThat(firstResult.hasProperty("category"), equalTo(false)); assertThat(firstResult.hasProperty("brand"), equalTo(false)); assertThat(firstResult.hasProperty("price"), equalTo(false)); assertThat(firstResult.hasProperty("rating"), equalTo(false)); assertThat(firstResult.hasProperty("image_embedding"), equalTo(false)); } @Test public void testNoSort() { // Test NOSORT - disables default sorting by score FTHybridPostProcessingParams postProcessing = FTHybridPostProcessingParams.builder() .load("title", "price").noSort().limit(Limit.of(0, 10)).build(); FTHybridParams hybridArgs = FTHybridParams.builder() .search(FTHybridSearchParams.builder().query("@category:{electronics}") .scoreAlias("text_score").build()) .vectorSearch(FTHybridVectorParams.builder().field("@image_embedding").vector("vector") .method(FTHybridVectorParams.Knn.of(10)).scoreAlias("vector_score").build()) .combine(Combiners.linear().alpha(0.5).beta(0.5)).postProcessing(postProcessing) .param("vector", queryVector).build(); HybridResult reply = jedis.ftHybrid(INDEX_NAME, hybridArgs); assertThat(reply, notNullValue()); assertThat(reply.getDocuments(), not(empty())); // Result count assertions assertThat(reply.getTotalResults(), greaterThan(0L)); assertThat(reply.getDocuments().size(), greaterThan(0)); // Loaded fields should be present Document firstResult = reply.getDocuments().get(0); assertThat(firstResult.hasProperty("title"), equalTo(true)); assertThat(firstResult.hasProperty("price"), equalTo(true)); } // ========== Scorer Tests ========== /** * Nested test class to verify all supported scorers work correctly with FT.HYBRID command. Tests * each scorer from {@link Scorers} to ensure proper integration. */ @Nested @Tag("integration") @TestInstance(TestInstance.Lifecycle.PER_CLASS) @SinceRedisVersion("8.4.0") class SupportedScorersTest { /** * Provides scorer instances and their expected scores for parameterized testing. Sccore values * might differ between cluster and standalone modes. To perform basic verification use same * values for both cluster/standalone with tolerance. * @return Stream of Arguments containing (Scorer, expectedScore, tolerance) */ Stream scorerProvider() { return Stream.of(Arguments.of(Scorers.tfidf(), 2.5, 0.5), Arguments.of(Scorers.tfidfDocnorm(), 0.2, 0.5), Arguments.of(Scorers.bm25stdNorm(), 1, 0.5), Arguments.of(Scorers.bm25std(), 1.3, 0.5), Arguments.of(Scorers.dismax(), 1.0, 0.5), Arguments.of(Scorers.docscore(), 1.0, 0.5), Arguments.of(Scorers.hamming(), 0.0, 0.5)); } @ParameterizedTest(name = "{index}: {0}") @MethodSource("scorerProvider") public void testScorer(Scorer scorer, double expectedScore, double tolerance) { // Create hybrid search with the provided scorer FTHybridParams hybridArgs = FTHybridParams.builder() .search(FTHybridSearchParams.builder().query("@id:1").scorer(scorer) .scoreAlias("text_score").build()) .vectorSearch(FTHybridVectorParams.builder().field("@image_embedding").vector("vector") .filter("@id:1").method(FTHybridVectorParams.Knn.of(5)).scoreAlias("vector_score") .build()) .param("vector", queryVector).build(); // Execute hybrid search HybridResult result = jedis.ftHybrid(INDEX_NAME, hybridArgs); // Verify results are returned assertThat(result, notNullValue()); assertThat(result.getTotalResults(), equalTo(1L)); // Verify scorer is working - text_score should be present Document firstDoc = result.getDocuments().get(0); assertThat(firstDoc.hasProperty("text_score"), equalTo(true)); // Verify score is valid and within expected range double scoreValue = Double.parseDouble(firstDoc.getString("text_score")); assertThat(scoreValue, closeTo(expectedScore, tolerance)); } } // ========== HybridResult Population Tests ========== /** * Verify that HybridResult is properly populated from FT.HYBRID command responses. Tests all * HybridResult fields and Document structure in various scenarios. */ @Nested @Tag("integration") @SinceRedisVersion("8.4.0") class HybridResultPopulationTest { /** * Verify : This command will only return document IDs (keyid) and scores to which the user has * read access. To retrieve entire documents, use projections with LOAD * or LOAD field */ @Test public void verifyHybridResultBasicFieldsNoLoad() { FTHybridParams hybridArgs = FTHybridParams.builder() .search(FTHybridSearchParams.builder().query("@id:1").build()) .vectorSearch(FTHybridVectorParams.builder().filter("@id:1").field("@image_embedding") .vector("vector").method(FTHybridVectorParams.Knn.of(5)).build()) .param("vector", queryVector).build(); HybridResult reply = jedis.ftHybrid(INDEX_NAME, hybridArgs); // Verify HybridResult is not null assertThat(reply, notNullValue()); // Verify totalResults is populated and > 0 assertThat(reply.getTotalResults(), equalTo(1L)); // Verify executionTime is populated and reasonable assertThat(reply.getExecutionTime(), greaterThan(0.0)); // Verify documents list is populated assertThat(reply.getDocuments(), notNullValue()); assertThat(reply.getDocuments(), not(empty())); Document doc = reply.getDocuments().get(0); assertThat(doc.getId(), equalTo("product:hybrid:1")); assertThat(doc.hasProperty("title"), equalTo(false)); assertThat(doc.getScore(), closeTo(0.03, 0.01)); // Verify warnings list is not null (may be empty) assertThat(reply.getWarnings(), notNullValue()); assertThat(reply.getWarnings(), empty()); } /** * Verify : This command will only return document IDs (keyid) and scores to which the user has * read access. To retrieve entire documents, use projections with LOAD * or LOAD field */ @Test @SinceRedisVersion("8.6.0") public void verifyHybridResultBasicFieldsWithLoadAll() { // Execute a simple hybrid search with known results FTHybridPostProcessingParams postProcessing = FTHybridPostProcessingParams.builder().loadAll() .build(); FTHybridParams hybridArgs = FTHybridParams.builder() .search(FTHybridSearchParams.builder().query("@id:1").build()) .vectorSearch(FTHybridVectorParams.builder().filter("@id:1").field("@image_embedding") .vector("vector").method(FTHybridVectorParams.Knn.of(5)).build()) .combine(Combiners.linear().alpha(1).beta(0)).postProcessing(postProcessing) .param("vector", queryVector).build(); HybridResult reply = jedis.ftHybrid(INDEX_NAME, hybridArgs); // Verify HybridResult is not null assertThat(reply, notNullValue()); // Verify totalResults is populated and > 0 assertThat(reply.getTotalResults(), equalTo(1L)); // Verify executionTime is populated and reasonable assertThat(reply.getExecutionTime(), greaterThan(0.0)); // Verify documents list is populated assertThat(reply.getDocuments(), notNullValue()); assertThat(reply.getDocuments(), not(empty())); Document doc = reply.getDocuments().get(0); assertThat(doc.getId(), nullValue()); assertThat(doc.hasProperty("title"), equalTo(true)); assertThat(doc.get("title"), equalTo("Apple iPhone 15 Pro smartphone with advanced camera")); assertThat(doc.getScore(), closeTo(0.5, 0.5)); // Verify warnings list is not null (may be empty) assertThat(reply.getWarnings(), notNullValue()); assertThat(reply.getWarnings(), empty()); } @Test public void verifyHybridResultWithEmptyResults() { // Execute hybrid search with filter that matches no documents FTHybridParams hybridArgs = FTHybridParams.builder() .search(FTHybridSearchParams.builder().query("@category:{nonexistent}").build()) .vectorSearch( FTHybridVectorParams.builder().filter("@id:nonexistent").field("@image_embedding") .vector("vector").method(FTHybridVectorParams.Knn.of(5)).build()) .param("vector", queryVector).build(); HybridResult reply = jedis.ftHybrid(INDEX_NAME, hybridArgs); // Verify HybridResult is not null even with empty results assertThat(reply, notNullValue()); // Verify totalResults is 0 assertThat(reply.getTotalResults(), equalTo(0L)); // Verify documents list is empty assertThat(reply.getDocuments(), notNullValue()); assertThat(reply.getDocuments(), empty()); // Verify executionTime is still populated (>= 0.0) assertThat(reply.getExecutionTime(), greaterThanOrEqualTo(0.0)); } } } ================================================ FILE: src/test/java/redis/clients/jedis/commands/unified/sentinel/SentinelAllKindOfValuesCommandsIT.java ================================================ package redis.clients.jedis.commands.unified.sentinel; import org.junit.jupiter.api.extension.RegisterExtension; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.params.ParameterizedClass; import org.junit.jupiter.params.provider.MethodSource; import redis.clients.jedis.*; import redis.clients.jedis.commands.unified.AllKindOfValuesCommandsTestBase; import redis.clients.jedis.util.EnabledOnCommandCondition; import redis.clients.jedis.util.RedisVersionCondition; import java.util.Arrays; import java.util.HashSet; import java.util.Set; @ParameterizedClass @MethodSource("redis.clients.jedis.commands.CommandsTestsParameters#respVersions") public class SentinelAllKindOfValuesCommandsIT extends AllKindOfValuesCommandsTestBase { static HostAndPort sentinel1; static HostAndPort sentinel2; static Set sentinels; static final JedisClientConfig sentinelClientConfig = DefaultJedisClientConfig.builder().build(); static EndpointConfig primary; @RegisterExtension public RedisVersionCondition versionCondition = new RedisVersionCondition( () -> Endpoints.getRedisEndpoint("standalone2-primary")); @RegisterExtension public EnabledOnCommandCondition enabledOnCommandCondition = new EnabledOnCommandCondition( () -> Endpoints.getRedisEndpoint("standalone2-primary")); @BeforeAll public static void prepareEndpoints() { sentinel1 = Endpoints.getRedisEndpoint("sentinel-standalone2-1").getHostAndPort(); sentinel2 = Endpoints.getRedisEndpoint("sentinel-standalone2-3").getHostAndPort(); sentinels = new HashSet<>(Arrays.asList(sentinel1, sentinel2)); primary = Endpoints.getRedisEndpoint("standalone2-primary"); } public SentinelAllKindOfValuesCommandsIT(RedisProtocol protocol) { super(protocol); } @Override protected UnifiedJedis createTestClient() { return RedisSentinelClient.builder() .clientConfig(primary.getClientConfigBuilder().protocol(protocol).build()) .sentinels(sentinels).sentinelClientConfig(sentinelClientConfig).masterName("mymaster") .build(); } } ================================================ FILE: src/test/java/redis/clients/jedis/csc/AllowAndDenyListCacheableTest.java ================================================ package redis.clients.jedis.csc; import static java.util.Collections.singleton; import static org.junit.jupiter.api.Assertions.assertEquals; import org.junit.jupiter.api.Test; import redis.clients.jedis.Protocol; import redis.clients.jedis.RedisClient; import redis.clients.jedis.csc.util.AllowAndDenyListWithStringKeys; public class AllowAndDenyListCacheableTest extends ClientSideCacheTestBase { private static CacheConfig createConfig(Cacheable cacheable) { return CacheConfig.builder().cacheable(cacheable).cacheClass(TestCache.class).build(); } @Test public void none() { try (RedisClient jedis = RedisClient.builder() .hostAndPort(hnp) .clientConfig(clientConfig.get()) .cacheConfig(createConfig(new AllowAndDenyListWithStringKeys(null, null, null, null))) .poolConfig(singleConnectionPoolConfig.get()) .build()) { Cache cache = jedis.getCache(); control.set("foo", "bar"); assertEquals(0, cache.getSize()); assertEquals("bar", jedis.get("foo")); assertEquals(1, cache.getSize()); } } @Test public void whiteListCommand() { try (RedisClient jedis = RedisClient.builder() .hostAndPort(hnp) .clientConfig(clientConfig.get()) .cacheConfig(createConfig(new AllowAndDenyListWithStringKeys(singleton(Protocol.Command.GET), null, null, null))) .poolConfig(singleConnectionPoolConfig.get()) .build()) { Cache cache = jedis.getCache(); control.set("foo", "bar"); assertEquals(0, cache.getSize()); assertEquals("bar", jedis.get("foo")); assertEquals(1, cache.getSize()); } } @Test public void blackListCommand() { try (RedisClient jedis = RedisClient.builder() .hostAndPort(hnp) .clientConfig(clientConfig.get()) .cacheConfig(createConfig(new AllowAndDenyListWithStringKeys(null, singleton(Protocol.Command.GET), null, null))) .poolConfig(singleConnectionPoolConfig.get()) .build()) { Cache cache = jedis.getCache(); control.set("foo", "bar"); assertEquals(0, cache.getSize()); assertEquals("bar", jedis.get("foo")); assertEquals(0, cache.getSize()); } } @Test public void whiteListKey() { try (RedisClient jedis = RedisClient.builder() .hostAndPort(hnp) .clientConfig(clientConfig.get()) .cacheConfig(createConfig(new AllowAndDenyListWithStringKeys(null, null, singleton("foo"), null))) .poolConfig(singleConnectionPoolConfig.get()) .build()) { control.set("foo", "bar"); Cache cache = jedis.getCache(); assertEquals(0, cache.getSize()); assertEquals("bar", jedis.get("foo")); assertEquals(1, cache.getSize()); } } @Test public void blackListKey() { try (RedisClient jedis = RedisClient.builder() .hostAndPort(hnp) .clientConfig(clientConfig.get()) .cacheConfig(createConfig(new AllowAndDenyListWithStringKeys(null, null, null, singleton("foo")))) .poolConfig(singleConnectionPoolConfig.get()) .build()) { Cache cache = jedis.getCache(); control.set("foo", "bar"); assertEquals(0, cache.getSize()); assertEquals("bar", jedis.get("foo")); assertEquals(0, cache.getSize()); } } } ================================================ FILE: src/test/java/redis/clients/jedis/csc/ClientSideCacheFunctionalityTest.java ================================================ package redis.clients.jedis.csc; import static org.awaitility.Awaitility.await; import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.aMapWithSize; import static org.hamcrest.Matchers.hasSize; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertNull; import static org.junit.jupiter.api.Assertions.assertTrue; import java.util.*; import java.util.concurrent.CountDownLatch; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicInteger; import java.util.concurrent.locks.ReentrantLock; import java.util.stream.Collectors; import io.redis.test.annotations.ConditionalOnEnv; import org.hamcrest.Matchers; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; import org.mockito.Mockito; import redis.clients.jedis.CommandObjects; import redis.clients.jedis.RedisClient; import redis.clients.jedis.UnifiedJedis; import redis.clients.jedis.util.TestEnvUtil; public class ClientSideCacheFunctionalityTest extends ClientSideCacheTestBase { @Test // T.5.1 public void flushAllTest() { final int count = 100; for (int i = 0; i < count; i++) { control.set("k" + i, "v" + i); } try (RedisClient jedis = RedisClient.builder() .hostAndPort(hnp) .clientConfig(clientConfig.get()) .cacheConfig(CacheConfig.builder().build()) .build()) { Cache cache = jedis.getCache(); for (int i = 0; i < count; i++) { jedis.get("k" + i); } assertEquals(count, cache.getSize()); cache.flush(); assertEquals(0, cache.getSize()); } } @Test // T.4.1 public void lruEvictionTest() { final int count = 100; final int extra = 10; // Add 100 + 10 keys to Redis for (int i = 0; i < count + extra; i++) { control.set("key:" + i, "value" + i); } Map map = new LinkedHashMap<>(count); Cache cache = new DefaultCache(count, map); try (RedisClient jedis = RedisClient.builder() .hostAndPort(hnp) .clientConfig(clientConfig.get()) .cache(cache) .build()) { // Retrieve the 100 keys in the same order for (int i = 0; i < count; i++) { jedis.get("key:" + i); } assertThat(map, aMapWithSize(count)); List earlierKeys = new ArrayList<>(map.keySet()).subList(0, extra); // earlier keys in map earlierKeys.forEach(cacheKey -> assertThat(map, Matchers.hasKey(cacheKey))); // Retrieve the 10 extra keys for (int i = count; i < count + extra; i++) { jedis.get("key:" + i); } // earlier keys NOT in map earlierKeys.forEach(cacheKey -> assertThat(map, Matchers.not(Matchers.hasKey(cacheKey)))); assertThat(map, aMapWithSize(count)); } } @Test // T.5.2 public void deleteByKeyUsingMGetTest() { try (RedisClient jedis = RedisClient.builder() .hostAndPort(hnp) .clientConfig(clientConfig.get()) .cacheConfig(CacheConfig.builder().build()) .build()) { Cache clientSideCache = jedis.getCache(); jedis.set("1", "one"); jedis.set("2", "two"); assertEquals(Arrays.asList("one", "two"), jedis.mget("1", "2")); assertEquals(1, clientSideCache.getSize()); assertThat(clientSideCache.deleteByRedisKey("1"), hasSize(1)); assertEquals(0, clientSideCache.getSize()); } } @Test // T.5.2 public void deleteByKeyTest() { final int count = 100; for (int i = 0; i < count; i++) { control.set("k" + i, "v" + i); } // By using LinkedHashMap, we can get the hashes (map keys) at the same order of the actual keys. LinkedHashMap map = new LinkedHashMap<>(); Cache clientSideCache = new TestCache(map); try (RedisClient jedis = RedisClient.builder() .hostAndPort(hnp) .clientConfig(clientConfig.get()) .cache(clientSideCache) .build()) { for (int i = 0; i < count; i++) { jedis.get("k" + i); } assertThat(map, aMapWithSize(count)); ArrayList cacheKeys = new ArrayList<>(map.keySet()); for (int i = 0; i < count; i++) { String key = "k" + i; CacheKey cacheKey = cacheKeys.get(i); assertTrue(map.containsKey(cacheKey)); assertThat(clientSideCache.deleteByRedisKey(key), hasSize(1)); assertFalse(map.containsKey(cacheKey)); assertThat(map, aMapWithSize(count - i - 1)); } assertThat(map, aMapWithSize(0)); } } @Test // T.5.2 public void deleteByKeysTest() { final int count = 100; final int delete = 10; for (int i = 0; i < count; i++) { control.set("k" + i, "v" + i); } // By using LinkedHashMap, we can get the hashes (map keys) at the same order of the actual keys. LinkedHashMap map = new LinkedHashMap<>(); Cache clientSideCache = new TestCache(map); try (RedisClient jedis = RedisClient.builder() .hostAndPort(hnp) .clientConfig(clientConfig.get()) .cache(clientSideCache) .build()) { for (int i = 0; i < count; i++) { jedis.get("k" + i); } assertThat(map, aMapWithSize(count)); List keysToDelete = new ArrayList<>(delete); for (int i = 0; i < delete; i++) { String key = "k" + i; keysToDelete.add(key); } assertThat(clientSideCache.deleteByRedisKeys(keysToDelete), hasSize(delete)); assertThat(map, aMapWithSize(count - delete)); } } @Test // T.5.3 public void deleteByEntryTest() { final int count = 100; for (int i = 0; i < count; i++) { control.set("k" + i, "v" + i); } try (RedisClient jedis = RedisClient.builder() .hostAndPort(hnp) .clientConfig(clientConfig.get()) .cacheConfig(CacheConfig.builder().build()) .build()) { Cache cache = jedis.getCache(); for (int i = 0; i < count; i++) { jedis.get("k" + i); } assertEquals(count, cache.getSize()); List cacheKeys = new ArrayList<>(cache.getCacheEntries()); for (int i = 0; i < count; i++) { CacheKey cacheKey = cacheKeys.get(i).getCacheKey(); assertTrue(cache.delete(cacheKey)); assertFalse(cache.hasCacheKey(cacheKey)); assertEquals(count - i - 1, cache.getSize()); } } } @Test // T.5.3 public void deleteByEntriesTest() { final int count = 100; final int delete = 10; for (int i = 0; i < count; i++) { control.set("k" + i, "v" + i); } try (RedisClient jedis = RedisClient.builder() .hostAndPort(hnp) .clientConfig(clientConfig.get()) .cacheConfig(CacheConfig.builder().build()) .build()) { Cache cache = jedis.getCache(); for (int i = 0; i < count; i++) { jedis.get("k" + i); } assertEquals(count, cache.getSize()); List cacheKeysToDelete = new ArrayList<>(cache.getCacheEntries()).subList(0, delete).stream().map(e -> e.getCacheKey()) .collect(Collectors.toList()); List isDeleted = cache.delete(cacheKeysToDelete); assertThat(isDeleted, hasSize(delete)); isDeleted.forEach(Assertions::assertTrue); assertEquals(count - delete, cache.getSize()); } } @Test public void multiKeyOperation() { control.set("k1", "v1"); control.set("k2", "v2"); try (RedisClient jedis = RedisClient.builder() .hostAndPort(hnp) .clientConfig(clientConfig.get()) .cacheConfig(CacheConfig.builder().build()) .build()) { jedis.mget("k1", "k2"); assertEquals(1, jedis.getCache().getSize()); } } @Test public void maximumSizeExact() { control.set("k1", "v1"); control.set("k2", "v2"); try (RedisClient jedis = RedisClient.builder() .hostAndPort(hnp) .clientConfig(clientConfig.get()) .cacheConfig(CacheConfig.builder().maxSize(1).build()) .build()) { Cache cache = jedis.getCache(); assertEquals(0, cache.getSize()); jedis.get("k1"); assertEquals(1, cache.getSize()); assertEquals(0, cache.getStats().getEvictCount()); jedis.get("k2"); assertEquals(1, cache.getSize()); assertEquals(1, cache.getStats().getEvictCount()); } } @Test public void testInvalidationWithUnifiedJedis() { Cache cache = new TestCache(); Cache mock = Mockito.spy(cache); UnifiedJedis client = new UnifiedJedis(hnp, clientConfig.get(), mock); UnifiedJedis controlClient = new UnifiedJedis(hnp, clientConfig.get()); try { // "foo" is cached client.set("foo", "bar"); client.get("foo"); // read from the server assertEquals("bar", client.get("foo")); // cache hit // Using another connection controlClient.set("foo", "bar2"); assertEquals("bar2", controlClient.get("foo")); //invalidating the cache and read it back from server assertEquals("bar2", client.get("foo")); Mockito.verify(mock, Mockito.times(1)).deleteByRedisKeys(Mockito.anyList()); Mockito.verify(mock, Mockito.times(2)).set(Mockito.any(CacheKey.class), Mockito.any(CacheEntry.class)); } finally { client.close(); controlClient.close(); } } @Test public void differentInstanceOnEachCacheHit() { // fill the cache for maxSize try (RedisClient jedis = RedisClient.builder() .hostAndPort(hnp) .clientConfig(clientConfig.get()) .cacheConfig(CacheConfig.builder().build()) .build()) { Cache cache = jedis.getCache(); jedis.sadd("foo", "a"); jedis.sadd("foo", "b"); Set expected = new HashSet<>(); expected.add("a"); expected.add("b"); Set members1 = jedis.smembers("foo"); Set members2 = jedis.smembers("foo"); Set fromMap = (Set) cache.get(new CacheKey<>(new CommandObjects().smembers("foo"))).getValue(); assertEquals(expected, members1); assertEquals(expected, members2); assertEquals(expected, fromMap); assertTrue(members1 != members2); assertTrue(members1 != fromMap); } } @Test @ConditionalOnEnv(value = TestEnvUtil.ENV_REDIS_ENTERPRISE, enabled = false) public void testSequentialAccess() throws InterruptedException { int threadCount = 10; int iterations = 10000; control.set("foo", "0"); ReentrantLock lock = new ReentrantLock(true); ExecutorService executorService = Executors.newFixedThreadPool(threadCount); CacheConfig cacheConfig = CacheConfig.builder().maxSize(1000).build(); try (RedisClient jedis = RedisClient.builder() .hostAndPort(endpoint.getHostAndPort()) .clientConfig(clientConfig.get()) .cacheConfig(cacheConfig) .build()) { // Submit multiple threads to perform concurrent operations CountDownLatch latch = new CountDownLatch(threadCount); for (int i = 0; i < threadCount; i++) { executorService.submit(() -> { try { for (int j = 0; j < iterations; j++) { lock.lock(); try { // Simulate continious get and update operations and consume invalidation events meanwhile assertEquals(control.get("foo"), jedis.get("foo")); Integer value = new Integer(jedis.get("foo")); assertEquals("OK", jedis.set("foo", (++value).toString())); } finally { lock.unlock(); } } } finally { latch.countDown(); } }); } // wait for all threads to complete latch.await(); } executorService.shutdownNow(); // Verify the final value of "foo" in Redis String finalValue = control.get("foo"); assertEquals(threadCount * iterations, Integer.parseInt(finalValue)); } @Test @ConditionalOnEnv(value = TestEnvUtil.ENV_REDIS_ENTERPRISE, enabled = false) public void testConcurrentAccessWithStats() throws InterruptedException { int threadCount = 10; int iterations = 10000; control.set("foo", "0"); ExecutorService executorService = Executors.newFixedThreadPool(threadCount); // Create the shared mock instance of cache try (RedisClient jedis = RedisClient.builder() .hostAndPort(endpoint.getHostAndPort()) .clientConfig(clientConfig.get()) .cacheConfig(CacheConfig.builder().build()) .build()) { Cache cache = jedis.getCache(); // Submit multiple threads to perform concurrent operations CountDownLatch latch = new CountDownLatch(threadCount); for (int i = 0; i < threadCount; i++) { executorService.submit(() -> { try { for (int j = 0; j < iterations; j++) { // Simulate continious get and update operations and consume invalidation events meanwhile Integer value = new Integer(jedis.get("foo")) + 1; assertEquals("OK", jedis.set("foo", value.toString())); } } finally { latch.countDown(); } }); } // wait for all threads to complete latch.await(); executorService.shutdownNow(); CacheStats stats = cache.getStats(); assertEquals(threadCount * iterations, stats.getMissCount() + stats.getHitCount()); assertEquals(stats.getMissCount(), stats.getLoadCount()); } } @Test @ConditionalOnEnv(value = TestEnvUtil.ENV_REDIS_ENTERPRISE, enabled = false) public void testMaxSize() throws InterruptedException { int threadCount = 10; int iterations = 11000; int maxSize = 1000; ExecutorService executorService = Executors.newFixedThreadPool(threadCount); try (RedisClient jedis = RedisClient.builder() .hostAndPort(endpoint.getHostAndPort()) .clientConfig(clientConfig.get()) .cacheConfig(CacheConfig.builder().maxSize(maxSize).build()) .build()) { Cache testCache = jedis.getCache(); // Submit multiple threads to perform concurrent operations CountDownLatch latch = new CountDownLatch(threadCount); for (int i = 0; i < threadCount; i++) { executorService.submit(() -> { try { for (int j = 0; j < iterations; j++) { // Simulate continious get and update operations and consume invalidation events meanwhile assertEquals("OK", jedis.set("foo" + j, "foo" + j)); jedis.get("foo" + j); } } finally { latch.countDown(); } }); } // wait for all threads to complete latch.await(); executorService.shutdownNow(); CacheStats stats = testCache.getStats(); assertEquals(threadCount * iterations, stats.getMissCount() + stats.getHitCount()); assertEquals(stats.getMissCount(), stats.getLoadCount()); assertEquals(threadCount * iterations, stats.getNonCacheableCount()); assertTrue(maxSize >= testCache.getSize()); } } @Test public void testEvictionPolicy() throws InterruptedException { int maxSize = 100; int expectedEvictions = 20; int touchOffset = 10; // fill the cache for maxSize try (RedisClient jedis = RedisClient.builder() .hostAndPort(endpoint.getHostAndPort()) .clientConfig(clientConfig.get()) .cacheConfig(CacheConfig.builder().maxSize(maxSize).build()) .build()) { Cache cache = jedis.getCache(); for (int i = 0; i < maxSize; i++) { jedis.set("foo" + i, "bar" + i); assertEquals("bar" + i, jedis.get("foo" + i)); } // touch a set of keys to prevent from eviction from index 10 to 29 for (int i = touchOffset; i < touchOffset + expectedEvictions; i++) { assertEquals("bar" + i, jedis.get("foo" + i)); } // add more keys to trigger eviction, adding from 100 to 119 for (int i = maxSize; i < maxSize + expectedEvictions; i++) { jedis.set("foo" + i, "bar" + i); assertEquals("bar" + i, jedis.get("foo" + i)); } // check touched keys not evicted for (int i = touchOffset; i < touchOffset + expectedEvictions; i++) { assertTrue(cache.hasCacheKey(new CacheKey(new CommandObjects().get("foo" + i)))); } // check expected evictions are done till the offset for (int i = 0; i < touchOffset; i++) { assertTrue(!cache.hasCacheKey(new CacheKey(new CommandObjects().get("foo" + i)))); } // check expected evictions are done after the touched keys for (int i = touchOffset + expectedEvictions; i < (2 * expectedEvictions); i++) { assertFalse(cache.hasCacheKey(new CacheKey(new CommandObjects().get("foo" + i)))); } assertEquals(maxSize, cache.getSize()); } } @Test @ConditionalOnEnv(value = TestEnvUtil.ENV_REDIS_ENTERPRISE, enabled = false) public void testEvictionPolicyMultithreaded() throws InterruptedException { int NUMBER_OF_THREADS = 100; int TOTAL_OPERATIONS = 1000000; int NUMBER_OF_DISTINCT_KEYS = 53; int MAX_SIZE = 20; List exceptions = new ArrayList<>(); List tds = new ArrayList<>(); final AtomicInteger ind = new AtomicInteger(); try (RedisClient jedis = RedisClient.builder() .hostAndPort(endpoint.getHostAndPort()) .clientConfig(clientConfig.get()) .cacheConfig(CacheConfig.builder().maxSize(MAX_SIZE).build()) .build()) { Cache cache = jedis.getCache(); for (int i = 0; i < NUMBER_OF_THREADS; i++) { Thread hj = new Thread(new Runnable() { @Override public void run() { for (int i = 0; (i = ind.getAndIncrement()) < TOTAL_OPERATIONS;) { try { final String key = "foo" + i % NUMBER_OF_DISTINCT_KEYS; if (i < NUMBER_OF_DISTINCT_KEYS) { jedis.set(key, key); } jedis.get(key); } catch (Exception e) { exceptions.add(e); throw e; } } } }); tds.add(hj); hj.start(); } for (Thread t : tds) { t.join(); } assertEquals(MAX_SIZE, cache.getSize()); assertEquals(0, exceptions.size()); } } @Test public void testNullValue() throws InterruptedException { int MAX_SIZE = 20; String nonExisting = "non-existing-key-"+ UUID.randomUUID().toString(); control.del(nonExisting); try (RedisClient jedis = RedisClient.builder() .hostAndPort(hnp) .clientConfig(clientConfig.get()) .cacheConfig(CacheConfig.builder().maxSize(MAX_SIZE).build()) .build()) { Cache cache = jedis.getCache(); CacheStats stats = cache.getStats(); String val = jedis.get(nonExisting); assertNull(val); assertEquals(1, cache.getSize()); assertEquals(0, stats.getHitCount()); assertEquals(1, stats.getMissCount()); val = jedis.get(nonExisting); assertNull(val); assertEquals(1, cache.getSize()); assertNull(cache.getCacheEntries().iterator().next().getValue()); assertEquals(1, stats.getHitCount()); assertEquals(1, stats.getMissCount()); control.set(nonExisting, "bar"); await() .atMost(5, TimeUnit.SECONDS) .pollInterval(10, TimeUnit.MILLISECONDS) .untilAsserted(() -> assertEquals("bar", jedis.get(nonExisting))); assertEquals(1, cache.getSize()); assertEquals("bar", cache.getCacheEntries().iterator().next().getValue()); assertEquals(1, stats.getHitCount()); assertEquals(2, stats.getMissCount()); } } @Test public void testCacheFactory() throws InterruptedException { // this checks the instantiation with parameters (int, EvictionPolicy, Cacheable) try (RedisClient jedis = RedisClient.builder() .hostAndPort(hnp) .clientConfig(clientConfig.get()) .cacheConfig(CacheConfig.builder().cacheClass(TestCache.class).build()) .build()) { Cache cache = jedis.getCache(); CacheStats stats = cache.getStats(); String val = jedis.get("foo"); val = jedis.get("foo"); assertNull(val); assertEquals(1, cache.getSize()); assertNull(cache.getCacheEntries().iterator().next().getValue()); assertEquals(1, stats.getHitCount()); assertEquals(1, stats.getMissCount()); } // this checks the instantiation with parameters (int, EvictionPolicy) try (RedisClient jedis = RedisClient.builder() .hostAndPort(hnp) .clientConfig(clientConfig.get()) .cacheConfig(CacheConfig.builder().cacheClass(TestCache.class).cacheable(null).build()) .build()) { Cache cache = jedis.getCache(); CacheStats stats = cache.getStats(); String val = jedis.get("foo"); val = jedis.get("foo"); assertNull(val); assertEquals(1, cache.getSize()); assertNull(cache.getCacheEntries().iterator().next().getValue()); assertEquals(1, stats.getHitCount()); assertEquals(1, stats.getMissCount()); } } } ================================================ FILE: src/test/java/redis/clients/jedis/csc/ClientSideCacheTestBase.java ================================================ package redis.clients.jedis.csc; import java.util.function.Supplier; import io.redis.test.annotations.SinceRedisVersion; import org.apache.commons.pool2.impl.GenericObjectPoolConfig; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Tag; import org.junit.jupiter.api.extension.RegisterExtension; import org.junit.jupiter.api.BeforeAll; import redis.clients.jedis.*; import redis.clients.jedis.util.EnvCondition; import redis.clients.jedis.util.RedisVersionCondition; @SinceRedisVersion(value = "7.4.0", message = "Jedis client-side caching is only supported with Redis 7.4 or later.") @Tag("integration") public abstract class ClientSideCacheTestBase { protected static EndpointConfig endpoint; protected static HostAndPort hnp; protected Jedis control; @RegisterExtension public RedisVersionCondition versionCondition = new RedisVersionCondition( () -> Endpoints.getRedisEndpoint("standalone1")); @RegisterExtension public static EnvCondition envCondition = new EnvCondition(); @BeforeAll public static void prepareEndpoint() { endpoint = Endpoints.getRedisEndpoint("standalone1"); hnp = endpoint.getHostAndPort(); } @BeforeEach public void setUp() throws Exception { control = new Jedis(hnp, endpoint.getClientConfigBuilder().build()); control.flushAll(); } @AfterEach public void tearDown() throws Exception { control.close(); } protected static final Supplier clientConfig = () -> endpoint.getClientConfigBuilder().resp3().build(); protected static final Supplier> singleConnectionPoolConfig = () -> { ConnectionPoolConfig poolConfig = new ConnectionPoolConfig(); poolConfig.setMaxTotal(1); return poolConfig; }; } ================================================ FILE: src/test/java/redis/clients/jedis/csc/RedisClientSideCacheTest.java ================================================ package redis.clients.jedis.csc; import io.redis.test.annotations.SinceRedisVersion; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.extension.RegisterExtension; import org.junit.jupiter.api.Tag; import redis.clients.jedis.Endpoints; import redis.clients.jedis.util.RedisVersionCondition; @SinceRedisVersion(value = "7.4.0", message = "Jedis client-side caching is only supported with Redis 7.4 or later.") @Tag("integration") public class RedisClientSideCacheTest extends RedisClientSideCacheTestBase { @RegisterExtension public static RedisVersionCondition versionCondition = new RedisVersionCondition( () -> Endpoints.getRedisEndpoint("standalone0")); @BeforeAll public static void prepare() { endpoint = Endpoints.getRedisEndpoint("standalone0"); } } ================================================ FILE: src/test/java/redis/clients/jedis/csc/RedisClientSideCacheTestBase.java ================================================ package redis.clients.jedis.csc; import org.junit.jupiter.api.Test; import redis.clients.jedis.EndpointConfig; import redis.clients.jedis.Jedis; import redis.clients.jedis.RedisClient; import redis.clients.jedis.args.ClientType; import redis.clients.jedis.exceptions.JedisConnectionException; import redis.clients.jedis.params.ClientKillParams; import static org.junit.jupiter.api.Assertions.assertEquals; public abstract class RedisClientSideCacheTestBase extends UnifiedJedisClientSideCacheTestBase { protected static EndpointConfig endpoint; @Override protected RedisClient createRegularJedis() { return RedisClient.builder() .hostAndPort(endpoint.getHostAndPort()) .clientConfig(endpoint.getClientConfigBuilder().build()) .build(); } @Override protected RedisClient createCachedJedis(CacheConfig cacheConfig) { return RedisClient.builder() .hostAndPort(endpoint.getHostAndPort()) .clientConfig(endpoint.getClientConfigBuilder().resp3().build()) .cacheConfig(cacheConfig) .build(); } @Test public void clearIfOneDiesTest() { try (RedisClient jedis = createCachedJedis(CacheConfig.builder().build())) { Cache cache = jedis.getCache(); // Create 100 keys for (int i = 0; i < 100; i++) { jedis.set("key" + i, "value" + i); } assertEquals(0, cache.getSize()); // Get 100 keys into the cache for (int i = 0; i < 100; i++) { jedis.get("key" + i); } assertEquals(100, cache.getSize()); try (Jedis killer = new Jedis(endpoint.getHostAndPort(), endpoint.getClientConfigBuilder().build())) { killer.clientKill(ClientKillParams.clientKillParams().type(ClientType.NORMAL).skipMe(ClientKillParams.SkipMe.YES)); } try { jedis.get("foo"); } catch (JedisConnectionException jce) { // expected } assertEquals(0, cache.getSize()); } } } ================================================ FILE: src/test/java/redis/clients/jedis/csc/RedisClusterClientSideCacheTest.java ================================================ package redis.clients.jedis.csc; import io.redis.test.annotations.ConditionalOnEnv; import io.redis.test.annotations.SinceRedisVersion; import java.util.HashSet; import java.util.Set; import java.util.function.Supplier; import org.apache.commons.pool2.impl.GenericObjectPoolConfig; import org.junit.jupiter.api.extension.RegisterExtension; import org.junit.jupiter.api.Tag; import org.junit.jupiter.api.BeforeAll; import redis.clients.jedis.*; import redis.clients.jedis.util.EnvCondition; import redis.clients.jedis.util.RedisVersionCondition; import redis.clients.jedis.util.TestEnvUtil; @SinceRedisVersion(value = "7.4.0", message = "Jedis client-side caching is only supported with Redis 7.4 or later.") @Tag("integration") @ConditionalOnEnv(value = TestEnvUtil.ENV_OSS_SOURCE, enabled = false) public class RedisClusterClientSideCacheTest extends UnifiedJedisClientSideCacheTestBase { protected static EndpointConfig endpoint; private static Set hnp; private static final Supplier clientConfig = () -> endpoint.getClientConfigBuilder().resp3().build(); private static final Supplier> singleConnectionPoolConfig = () -> { ConnectionPoolConfig poolConfig = new ConnectionPoolConfig(); poolConfig.setMaxTotal(1); return poolConfig; }; @RegisterExtension public static EnvCondition envCondition = new EnvCondition(); @RegisterExtension public static RedisVersionCondition versionCondition = new RedisVersionCondition( () -> Endpoints.getRedisEndpoint("cluster-stable")); @BeforeAll public static void prepare() { endpoint = Endpoints.getRedisEndpoint("cluster-stable"); hnp = new HashSet<>(endpoint.getHostsAndPorts()); } @Override protected RedisClusterClient createRegularJedis() { return RedisClusterClient.builder() .nodes(hnp) .clientConfig(clientConfig.get()) .build(); } @Override protected RedisClusterClient createCachedJedis(CacheConfig cacheConfig) { return RedisClusterClient.builder() .nodes(hnp) .clientConfig(clientConfig.get()) .cacheConfig(cacheConfig) .build(); } } ================================================ FILE: src/test/java/redis/clients/jedis/csc/RedisSentinelClientSideCacheTest.java ================================================ package redis.clients.jedis.csc; import io.redis.test.utils.RedisVersion; import java.util.Arrays; import java.util.HashSet; import java.util.Set; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.Tag; import redis.clients.jedis.*; import redis.clients.jedis.util.RedisVersionUtil; import static org.junit.jupiter.api.Assumptions.assumeTrue; @Tag("integration") public class RedisSentinelClientSideCacheTest extends UnifiedJedisClientSideCacheTestBase { private static final String MASTER_NAME = "mymaster"; protected static HostAndPort sentinel1; protected static HostAndPort sentinel2; private static Set sentinels; private static final JedisClientConfig masterClientConfig = DefaultJedisClientConfig.builder() .resp3().password("foobared").build(); private static final JedisClientConfig sentinelClientConfig = DefaultJedisClientConfig.builder() .resp3().build(); @BeforeAll public static void prepareEndpoints() { sentinel1 = Endpoints.getRedisEndpoint("sentinel-standalone2-1").getHostAndPort(); sentinel2 = Endpoints.getRedisEndpoint("sentinel-standalone2-3").getHostAndPort(); sentinels = new HashSet<>(Arrays.asList(sentinel1, sentinel2)); } @Override protected RedisSentinelClient createRegularJedis() { return RedisSentinelClient.builder().masterName(MASTER_NAME).clientConfig(masterClientConfig) .sentinels(sentinels).sentinelClientConfig(sentinelClientConfig).build(); } @Override protected RedisSentinelClient createCachedJedis(CacheConfig cacheConfig) { return RedisSentinelClient.builder().masterName(MASTER_NAME).clientConfig(masterClientConfig) .sentinels(sentinels).sentinelClientConfig(sentinelClientConfig).cacheConfig(cacheConfig) .build(); } @BeforeAll public static void prepare() { try ( RedisSentinelClient sentinelClient = RedisSentinelClient.builder().masterName(MASTER_NAME) .clientConfig(masterClientConfig).sentinels(sentinels) .sentinelClientConfig(sentinelClientConfig).build(); Jedis master = new Jedis(sentinelClient.getCurrentMaster(), masterClientConfig)) { assumeTrue(RedisVersionUtil.getRedisVersion(master).isGreaterThanOrEqualTo(RedisVersion.V7_4), "Jedis Client side caching is only supported with 'Redis 7.4' or later."); } } } ================================================ FILE: src/test/java/redis/clients/jedis/csc/SSLRedisClientSideCacheTest.java ================================================ package redis.clients.jedis.csc; import io.redis.test.utils.RedisVersion; import java.nio.file.Path; import java.util.Collections; import java.util.List; import org.junit.jupiter.api.AfterAll; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.Tag; import redis.clients.jedis.Endpoints; import redis.clients.jedis.Jedis; import redis.clients.jedis.util.RedisVersionUtil; import redis.clients.jedis.util.TlsUtil; import static org.junit.jupiter.api.Assumptions.assumeTrue; @Tag("integration") public class SSLRedisClientSideCacheTest extends RedisClientSideCacheTestBase { private static final String trustStoreName = SSLRedisClientSideCacheTest.class.getSimpleName(); @BeforeAll public static void prepare() { endpoint = Endpoints.getRedisEndpoint("standalone0-tls"); List trustedCertLocation = Collections.singletonList(endpoint.getCertificatesLocation()); Path trustStorePath = TlsUtil.createAndSaveTestTruststore(trustStoreName, trustedCertLocation,"changeit"); TlsUtil.setCustomTrustStore(trustStorePath, "changeit"); try (Jedis jedis = new Jedis(endpoint.getHostAndPort(), endpoint.getClientConfigBuilder().build())) { assumeTrue(RedisVersionUtil.getRedisVersion(jedis).isGreaterThanOrEqualTo(RedisVersion.V7_4), "Jedis Client side caching is only supported with 'Redis 7.4' or later."); } } @AfterAll public static void teardownTrustStore() { TlsUtil.restoreOriginalTrustStore(); } } ================================================ FILE: src/test/java/redis/clients/jedis/csc/TestCache.java ================================================ package redis.clients.jedis.csc; import java.util.HashMap; import java.util.Map; public class TestCache extends DefaultCache { public TestCache() { this(new HashMap()); } public TestCache(Map map) { super(10000, map); } public TestCache(Map map, Cacheable cacheable) { super(10000, map, cacheable, new LRUEviction(10000)); } public TestCache(int maximumSize, EvictionPolicy evictionPolicy ) { super(maximumSize, new HashMap(), DefaultCacheable.INSTANCE, evictionPolicy); } public TestCache(int maximumSize, EvictionPolicy evictionPolicy, Cacheable cacheable ) { super(maximumSize, new HashMap(), cacheable, evictionPolicy); } } ================================================ FILE: src/test/java/redis/clients/jedis/csc/UnifiedJedisClientSideCacheTestBase.java ================================================ package redis.clients.jedis.csc; import static org.awaitility.Awaitility.await; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertNotSame; import static org.junit.jupiter.api.Assertions.assertNull; import static org.junit.jupiter.api.Assertions.assertTrue; import java.util.ArrayList; import java.util.Arrays; import java.util.List; import java.util.concurrent.Executors; import java.util.concurrent.ScheduledExecutorService; import java.util.concurrent.TimeUnit; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import redis.clients.jedis.JedisPubSub; import redis.clients.jedis.UnifiedJedis; public abstract class UnifiedJedisClientSideCacheTestBase { protected UnifiedJedis control; protected abstract UnifiedJedis createRegularJedis(); protected abstract UnifiedJedis createCachedJedis(CacheConfig cacheConfig); @BeforeEach public void setUp() throws Exception { control = createRegularJedis(); control.flushAll(); } @AfterEach public void tearDown() throws Exception { control.close(); } @Test public void simple() { CacheConfig cacheConfig = CacheConfig.builder().maxSize(1000).build(); try (UnifiedJedis jedis = createCachedJedis(cacheConfig)) { control.set("foo", "bar"); assertEquals("bar", jedis.get("foo")); control.del("foo"); await().atMost(5, TimeUnit.SECONDS).pollInterval(50, TimeUnit.MILLISECONDS) .untilAsserted(() -> assertNull(jedis.get("foo"))); } } @Test public void simpleWithSimpleMap() { try (UnifiedJedis jedis = createCachedJedis(CacheConfig.builder().build())) { Cache cache = jedis.getCache(); control.set("foo", "bar"); assertEquals(0, cache.getSize()); assertEquals("bar", jedis.get("foo")); assertEquals(1, cache.getSize()); control.del("foo"); assertEquals(1, cache.getSize()); await().atMost(5, TimeUnit.SECONDS).pollInterval(50, TimeUnit.MILLISECONDS) .untilAsserted(() -> assertNull(jedis.get("foo"))); assertEquals(1, cache.getSize()); } } @Test public void flushAll() { CacheConfig cacheConfig = CacheConfig.builder().maxSize(1000).build(); try (UnifiedJedis jedis = createCachedJedis(cacheConfig)) { control.set("foo", "bar"); assertEquals("bar", jedis.get("foo")); control.flushAll(); await().atMost(5, TimeUnit.SECONDS).pollInterval(50, TimeUnit.MILLISECONDS) .untilAsserted(() -> assertNull(jedis.get("foo"))); } } @Test public void flushAllWithSimpleMap() { try (UnifiedJedis jedis = createCachedJedis(CacheConfig.builder().build())) { Cache cache = jedis.getCache(); control.set("foo", "bar"); assertEquals(0, cache.getSize()); assertEquals("bar", jedis.get("foo")); assertEquals(1, cache.getSize()); control.flushAll(); assertEquals(1, cache.getSize()); await().atMost(5, TimeUnit.SECONDS).pollInterval(50, TimeUnit.MILLISECONDS) .untilAsserted(() -> assertNull(jedis.get("foo"))); assertEquals(1, cache.getSize()); } } @Test public void cacheNotEmptyTest() { try (UnifiedJedis jedis = createCachedJedis(CacheConfig.builder().build())) { Cache cache = jedis.getCache(); control.set("foo", "bar"); assertEquals(0, cache.getSize()); assertEquals("bar", jedis.get("foo")); assertEquals(1, cache.getSize()); } } @Test public void cacheUsedTest() { try (UnifiedJedis jedis = createCachedJedis(CacheConfig.builder().build())) { Cache cache = jedis.getCache(); control.set("foo", "bar"); assertEquals(0, cache.getStats().getMissCount()); assertEquals(0, cache.getStats().getHitCount()); assertEquals("bar", jedis.get("foo")); assertEquals(1, cache.getStats().getMissCount()); assertEquals(0, cache.getStats().getHitCount()); assertEquals("bar", jedis.get("foo")); assertEquals(1, cache.getStats().getMissCount()); assertEquals(1, cache.getStats().getHitCount()); } } @Test public void immutableCacheEntriesTest() { try (UnifiedJedis jedis = createCachedJedis(CacheConfig.builder().build())) { jedis.set("{csc}a", "AA"); jedis.set("{csc}b", "BB"); jedis.set("{csc}c", "CC"); List expected = Arrays.asList("AA", "BB", "CC"); List reply1 = jedis.mget("{csc}a", "{csc}b", "{csc}c"); List reply2 = jedis.mget("{csc}a", "{csc}b", "{csc}c"); assertEquals(expected, reply1); assertEquals(expected, reply2); assertEquals(reply1, reply2); assertNotSame(reply1, reply2); } } @Test public void invalidationTest() { try (UnifiedJedis jedis = createCachedJedis(CacheConfig.builder().build())) { Cache cache = jedis.getCache(); jedis.set("{csc}1", "one"); jedis.set("{csc}2", "two"); jedis.set("{csc}3", "three"); assertEquals(0, cache.getSize()); assertEquals(0, cache.getStats().getInvalidationCount()); List reply1 = jedis.mget("{csc}1", "{csc}2", "{csc}3"); assertEquals(Arrays.asList("one", "two", "three"), reply1); assertEquals(1, cache.getSize()); assertEquals(0, cache.getStats().getInvalidationCount()); jedis.set("{csc}1", "new-one"); List reply2 = jedis.mget("{csc}1", "{csc}2", "{csc}3"); assertEquals(Arrays.asList("new-one", "two", "three"), reply2); assertEquals(1, cache.getSize()); assertEquals(1, cache.getStats().getInvalidationCount()); } } @Test public void getNumEntriesTest() { try (UnifiedJedis jedis = createCachedJedis(CacheConfig.builder().build())) { Cache cache = jedis.getCache(); // Create 100 keys for (int i = 0; i < 100; i++) { jedis.set("key" + i, "value" + i); } assertEquals(0, cache.getSize()); // Get 100 keys into the cache for (int i = 0; i < 100; i++) { jedis.get("key" + i); } assertEquals(100, cache.getSize()); } } @Test public void invalidationOnCacheHitTest() { try (UnifiedJedis jedis = createCachedJedis(CacheConfig.builder().build())) { Cache cache = jedis.getCache(); // Create 100 keys for (int i = 0; i < 100; i++) { jedis.set("key" + i, "value" + i); } assertEquals(0, cache.getSize()); // Get 100 keys into the cache for (int i = 0; i < 100; i++) { jedis.get("key" + i); } assertEquals(100, cache.getSize()); assertEquals(100, cache.getStats().getLoadCount()); assertEquals(0, cache.getStats().getInvalidationCount()); // Change 50 of the 100 keys for (int i = 1; i < 100; i += 2) { jedis.set("key" + i, "val" + i); } assertEquals(100, cache.getStats().getLoadCount()); // invalidation count is anything between 0 and 50 // Get the 100 keys again for (int i = 0; i < 100; i++) { jedis.get("key" + i); } assertEquals(100, cache.getSize()); assertEquals(150, cache.getStats().getLoadCount()); assertEquals(50, cache.getStats().getInvalidationCount()); } } @Test public void simplePubsubWithClientCache() { String test_channel = "test_channel"; String test_message = "test message"; UnifiedJedis publisher = createCachedJedis(CacheConfig.builder().build()); Runnable command = () -> publisher.publish(test_channel, test_message + System.currentTimeMillis()); ScheduledExecutorService executor = Executors.newSingleThreadScheduledExecutor(); executor.scheduleAtFixedRate(command, 0, 100, java.util.concurrent.TimeUnit.MILLISECONDS); List receivedMessages = new ArrayList<>(); try (UnifiedJedis subscriber = createCachedJedis(CacheConfig.builder().build())) { JedisPubSub jedisPubSub = new JedisPubSub() { private int count = 0; @Override public void onMessage(String channel, String message) { receivedMessages.add(message); if (message.startsWith(test_message) && count++ > 1) { this.unsubscribe(test_channel); } } }; subscriber.subscribe(jedisPubSub, test_channel); } executor.shutdown(); publisher.close(); assertTrue(receivedMessages.size() > 1); receivedMessages.forEach(message -> assertTrue(message.startsWith(test_message))); } @Test public void advancedPubsubWithClientCache() { String test_channel = "test_channel"; String test_message = "test message"; String test_key = "test_key"; String test_value = "test_value"; UnifiedJedis publisher = createCachedJedis(CacheConfig.builder().build()); Runnable command = () -> publisher.publish(test_channel, test_message + System.currentTimeMillis()); ScheduledExecutorService executor = Executors.newSingleThreadScheduledExecutor(); executor.scheduleAtFixedRate(command, 0, 50, java.util.concurrent.TimeUnit.MILLISECONDS); int iteration = 0; int totalIteration = 10; while (iteration++ < totalIteration) { List receivedMessages = new ArrayList<>(); try (UnifiedJedis subscriber = createCachedJedis(CacheConfig.builder().build())) { subscriber.set(test_key, test_value); assertEquals(test_value, subscriber.get(test_key)); JedisPubSub jedisPubSub = new JedisPubSub() { private int count = 0; @Override public void onMessage(String channel, String message) { receivedMessages.add(message); if (message.startsWith(test_message) && count++ > 1) { this.unsubscribe(test_channel); } } }; subscriber.subscribe(jedisPubSub, test_channel); subscriber.set(test_key, test_value); assertEquals(test_value, subscriber.get(test_key)); } assertTrue(receivedMessages.size() > 1); receivedMessages.forEach(message -> assertTrue(message.startsWith(test_message))); } executor.shutdown(); publisher.close(); } } ================================================ FILE: src/test/java/redis/clients/jedis/csc/VersionTest.java ================================================ package redis.clients.jedis.csc; import org.junit.jupiter.api.Test; import static org.junit.jupiter.api.Assertions.assertEquals; public class VersionTest { @Test public void compareSameVersions() { RedisVersion a = new RedisVersion("5.2.4"); RedisVersion b = new RedisVersion("5.2.4"); assertEquals(a, b); RedisVersion c = new RedisVersion("5.2.0.0"); RedisVersion d = new RedisVersion("5.2"); assertEquals(a, b); } @Test public void compareDifferentVersions() { RedisVersion a = new RedisVersion("5.2.4"); RedisVersion b = new RedisVersion("5.1.4"); assertEquals(1, a.compareTo(b)); RedisVersion c = new RedisVersion("5.2.4"); RedisVersion d = new RedisVersion("5.2.5"); assertEquals(-1, c.compareTo(d)); } } ================================================ FILE: src/test/java/redis/clients/jedis/examples/BroadcastCommandsToAllClusterNodes.java ================================================ package redis.clients.jedis.examples; import redis.clients.jedis.HostAndPort; import redis.clients.jedis.RedisClusterClient; /** * When using the Open Source Redis Cluster * API, some commands must be executed against all primary nodes. To simplify this task, Jedis * provides an easy way to broadcast commands. * * For example, to update the server configuration of all nodes in the Redis Cluster, we broadcast * the command [CONFIG SET](https://redis.io/commands/config-set/) to all nodes. */ public class BroadcastCommandsToAllClusterNodes { public static void main(String[] args) { HostAndPort clusterNode = new HostAndPort("127.0.0.1", 7000); RedisClusterClient client = RedisClusterClient.create(clusterNode); String reply = client.configSet("maxmemory", "100mb"); // reply is "OK" } } ================================================ FILE: src/test/java/redis/clients/jedis/examples/GeoShapeFieldsUsageInRediSearch.java ================================================ package redis.clients.jedis.examples; import org.locationtech.jts.geom.Coordinate; import org.locationtech.jts.geom.Geometry; import org.locationtech.jts.geom.GeometryFactory; import org.locationtech.jts.geom.Polygon; import org.locationtech.jts.io.ParseException; import org.locationtech.jts.io.WKTReader; import redis.clients.jedis.HostAndPort; import redis.clients.jedis.RedisClient; import redis.clients.jedis.search.FTSearchParams; import redis.clients.jedis.search.SearchResult; import redis.clients.jedis.search.schemafields.GeoShapeField; import static org.junit.jupiter.api.Assertions.assertEquals; /** * As of RediSearch 2.8.4, advanced GEO querying with GEOSHAPE fields is supported. *

* Notes: *

    *
  • As of RediSearch 2.8.4, only POLYGON and POINT objects are supported.
  • *
  • As of RediSearch 2.8.4, only WITHIN and CONTAINS conditions are supported.
  • *
  • As of RedisStack 7.4.0, support for INTERSECTS and DISJOINT conditions are added.
  • *
* * Any object/library producing a * well-known text (WKT) in {@code toString()} method can be used. * * This example uses the JTS library. *
 * {@code
 * 
 *   org.locationtech.jts
 *   jts-core
 *   1.19.0
 * 
 * }
 * 
*/ public class GeoShapeFieldsUsageInRediSearch { public static void main(String[] args) { // We'll create geometry objects with GeometryFactory final GeometryFactory factory = new GeometryFactory(); final String host = "localhost"; final int port = 6379; final HostAndPort address = new HostAndPort(host, port); RedisClient client = RedisClient.create(address); // client.setDefaultSearchDialect(3); // we can set default search dialect for the client (UnifiedJedis) object // to avoid setting dialect in every query. // creating index client.ftCreate("geometry-index", GeoShapeField.of("geometry", GeoShapeField.CoordinateSystem.SPHERICAL) // 'SPHERICAL' is for geographic (lon, lat). // 'FLAT' coordinate system also available for cartesian (X,Y). ); // preparing data final Polygon small = factory.createPolygon( new Coordinate[]{new Coordinate(34.9001, 29.7001), new Coordinate(34.9001, 29.7100), new Coordinate(34.9100, 29.7100), new Coordinate(34.9100, 29.7001), new Coordinate(34.9001, 29.7001)} ); // client.hset("small", RediSearchUtil.toStringMap(Collections.singletonMap("geometry", small))); // setting data // client.hset("small", "geometry", small.toString()); // simplified setting data client.hsetObject("small", "geometry", small); // more simplified setting data final Polygon large = factory.createPolygon( new Coordinate[]{new Coordinate(34.9001, 29.7001), new Coordinate(34.9001, 29.7200), new Coordinate(34.9200, 29.7200), new Coordinate(34.9200, 29.7001), new Coordinate(34.9001, 29.7001)} ); // client.hset("large", RediSearchUtil.toStringMap(Collections.singletonMap("geometry", large))); // setting data // client.hset("large", "geometry", large.toString()); // simplified setting data client.hsetObject("large", "geometry", large); // more simplified setting data // searching final Polygon within = factory.createPolygon( new Coordinate[]{new Coordinate(34.9000, 29.7000), new Coordinate(34.9000, 29.7150), new Coordinate(34.9150, 29.7150), new Coordinate(34.9150, 29.7000), new Coordinate(34.9000, 29.7000)} ); SearchResult res = client.ftSearch("geometry-index", "@geometry:[within $poly]", // query string FTSearchParams.searchParams() .addParam("poly", within) .dialect(3) // DIALECT '3' is required for this query ); assertEquals(1, res.getTotalResults()); assertEquals(1, res.getDocuments().size()); // We can parse geometry objects with WKTReader try { final WKTReader reader = new WKTReader(); Geometry object = reader.read(res.getDocuments().get(0).getString("geometry")); assertEquals(small, object); } catch (ParseException ex) { // WKTReader#read throws ParseException ex.printStackTrace(System.err); } } } ================================================ FILE: src/test/java/redis/clients/jedis/examples/RedisCredentialsProviderUsage.java ================================================ package redis.clients.jedis.examples; import redis.clients.jedis.DefaultJedisClientConfig; import redis.clients.jedis.DefaultRedisCredentials; import redis.clients.jedis.DefaultRedisCredentialsProvider; import redis.clients.jedis.HostAndPort; import redis.clients.jedis.RedisClient; public class RedisCredentialsProviderUsage { public static void main(String[] args) { DefaultRedisCredentials initialCredentials = new DefaultRedisCredentials("", ""); DefaultRedisCredentialsProvider credentialsProvider = new DefaultRedisCredentialsProvider(initialCredentials); final String host = ""; final int port = 6379; final HostAndPort address = new HostAndPort(host, port); final DefaultJedisClientConfig clientConfig = DefaultJedisClientConfig.builder() .credentialsProvider(credentialsProvider).build(); RedisClient jedis = RedisClient.builder() .hostAndPort(address) .clientConfig(clientConfig) .build(); // ... // do operations with jedis // ... // when credentials is required to be updated DefaultRedisCredentials updatedCredentials = new DefaultRedisCredentials("", ""); credentialsProvider.setCredentials(updatedCredentials); // ... // continue doing operations with jedis // ... jedis.close(); } } ================================================ FILE: src/test/java/redis/clients/jedis/examples/RetryableCommandExecution.java ================================================ package redis.clients.jedis.examples; import java.time.Duration; import org.apache.commons.pool2.impl.GenericObjectPoolConfig; import redis.clients.jedis.Connection; import redis.clients.jedis.ConnectionPoolConfig; import redis.clients.jedis.DefaultJedisClientConfig; import redis.clients.jedis.HostAndPort; import redis.clients.jedis.JedisClientConfig; import redis.clients.jedis.UnifiedJedis; import redis.clients.jedis.providers.PooledConnectionProvider; /** * It is possible to retry command executions in case of connection failures in UnifiedJedis class. * * The retry-ability comes through RetryableCommandExecutor class. It is also possible to directly provide * RetryableCommandExecutor as a parameter. * * Note: RetryableCommandExecutor should not be considered for * Open Source Redis Cluster mode because it requires to * handle more than connection failures. These are done in ClusterCommandExecutor. */ public class RetryableCommandExecution { public static void main(String[] args) { // Connection and pool parameters HostAndPort hostAndPort = new HostAndPort("127.0.0.1", 6379); JedisClientConfig clientConfig = DefaultJedisClientConfig.builder().user("myuser").password("mypassword").build(); GenericObjectPoolConfig poolConfig = new ConnectionPoolConfig(); PooledConnectionProvider provider = new PooledConnectionProvider(hostAndPort, clientConfig, poolConfig); // Retry parameters int maxAttempts = 5; Duration maxTotalRetriesDuration = Duration.ofSeconds(2); UnifiedJedis jedis = new UnifiedJedis(provider, maxAttempts, maxTotalRetriesDuration); jedis.set("foo", "bar"); jedis.get("foo"); jedis.close(); } } ================================================ FILE: src/test/java/redis/clients/jedis/exceptions/ExceptionsTest.java ================================================ package redis.clients.jedis.exceptions; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.Test; import redis.clients.jedis.HostAndPort; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertNull; import static org.junit.jupiter.api.Assertions.assertSame; public class ExceptionsTest { private static String MESSAGE; private static Throwable CAUSE; @BeforeAll public static void prepare() { MESSAGE = "This is a test message."; CAUSE = new Throwable("This is a test cause."); } @Test public void invalidURI() { try { throw new InvalidURIException(MESSAGE); } catch (Exception e) { assertSame(InvalidURIException.class, e.getClass()); assertEquals(MESSAGE, e.getMessage()); assertNull(e.getCause()); } try { throw new InvalidURIException(CAUSE); } catch (Exception e) { assertSame(InvalidURIException.class, e.getClass()); assertEquals(CAUSE, e.getCause()); assertEquals(CAUSE.toString(), e.getMessage()); } try { throw new InvalidURIException(MESSAGE, CAUSE); } catch (Exception e) { assertSame(InvalidURIException.class, e.getClass()); assertEquals(MESSAGE, e.getMessage()); assertEquals(CAUSE, e.getCause()); } } @Test public void accessControl() { try { throw new JedisAccessControlException(MESSAGE); } catch (Exception e) { assertSame(JedisAccessControlException.class, e.getClass()); assertEquals(MESSAGE, e.getMessage()); assertNull(e.getCause()); } try { throw new JedisAccessControlException(CAUSE); } catch (Exception e) { assertSame(JedisAccessControlException.class, e.getClass()); assertEquals(CAUSE, e.getCause()); assertEquals(CAUSE.toString(), e.getMessage()); } try { throw new JedisAccessControlException(MESSAGE, CAUSE); } catch (Exception e) { assertSame(JedisAccessControlException.class, e.getClass()); assertEquals(MESSAGE, e.getMessage()); assertEquals(CAUSE, e.getCause()); } } @Test public void askData() { HostAndPort hap = new HostAndPort("", 0); int slot = -1; try { throw new JedisAskDataException(MESSAGE, hap, slot); } catch (Exception e) { assertSame(JedisAskDataException.class, e.getClass()); assertEquals(MESSAGE, e.getMessage()); assertNull(e.getCause()); } try { throw new JedisAskDataException(CAUSE, hap, slot); } catch (Exception e) { assertSame(JedisAskDataException.class, e.getClass()); assertEquals(CAUSE, e.getCause()); assertEquals(CAUSE.toString(), e.getMessage()); } try { throw new JedisAskDataException(MESSAGE, CAUSE, hap, slot); } catch (Exception e) { assertSame(JedisAskDataException.class, e.getClass()); assertEquals(MESSAGE, e.getMessage()); assertEquals(CAUSE, e.getCause()); } } @Test public void busy() { try { throw new JedisBusyException(MESSAGE); } catch (Exception e) { assertSame(JedisBusyException.class, e.getClass()); assertEquals(MESSAGE, e.getMessage()); assertNull(e.getCause()); } try { throw new JedisBusyException(CAUSE); } catch (Exception e) { assertSame(JedisBusyException.class, e.getClass()); assertEquals(CAUSE, e.getCause()); assertEquals(CAUSE.toString(), e.getMessage()); } try { throw new JedisBusyException(MESSAGE, CAUSE); } catch (Exception e) { assertSame(JedisBusyException.class, e.getClass()); assertEquals(MESSAGE, e.getMessage()); assertEquals(CAUSE, e.getCause()); } } @Test public void cluster() { try { throw new JedisClusterException(MESSAGE); } catch (Exception e) { assertSame(JedisClusterException.class, e.getClass()); assertEquals(MESSAGE, e.getMessage()); assertNull(e.getCause()); } try { throw new JedisClusterException(CAUSE); } catch (Exception e) { assertSame(JedisClusterException.class, e.getClass()); assertEquals(CAUSE, e.getCause()); assertEquals(CAUSE.toString(), e.getMessage()); } try { throw new JedisClusterException(MESSAGE, CAUSE); } catch (Exception e) { assertSame(JedisClusterException.class, e.getClass()); assertEquals(MESSAGE, e.getMessage()); assertEquals(CAUSE, e.getCause()); } } @Test public void clusterOperation() { try { throw new JedisClusterOperationException(MESSAGE); } catch (Exception e) { assertSame(JedisClusterOperationException.class, e.getClass()); assertEquals(MESSAGE, e.getMessage()); assertNull(e.getCause()); } try { throw new JedisClusterOperationException(CAUSE); } catch (Exception e) { assertSame(JedisClusterOperationException.class, e.getClass()); assertEquals(CAUSE, e.getCause()); assertEquals(CAUSE.toString(), e.getMessage()); } try { throw new JedisClusterOperationException(MESSAGE, CAUSE); } catch (Exception e) { assertSame(JedisClusterOperationException.class, e.getClass()); assertEquals(MESSAGE, e.getMessage()); assertEquals(CAUSE, e.getCause()); } } @Test public void connection() { try { throw new JedisConnectionException(MESSAGE); } catch (Exception e) { assertSame(JedisConnectionException.class, e.getClass()); assertEquals(MESSAGE, e.getMessage()); assertNull(e.getCause()); } try { throw new JedisConnectionException(CAUSE); } catch (Exception e) { assertSame(JedisConnectionException.class, e.getClass()); assertEquals(CAUSE, e.getCause()); assertEquals(CAUSE.toString(), e.getMessage()); } try { throw new JedisConnectionException(MESSAGE, CAUSE); } catch (Exception e) { assertSame(JedisConnectionException.class, e.getClass()); assertEquals(MESSAGE, e.getMessage()); assertEquals(CAUSE, e.getCause()); } } @Test public void data() { try { throw new JedisDataException(MESSAGE); } catch (Exception e) { assertSame(JedisDataException.class, e.getClass()); assertEquals(MESSAGE, e.getMessage()); assertNull(e.getCause()); } try { throw new JedisDataException(CAUSE); } catch (Exception e) { assertSame(JedisDataException.class, e.getClass()); assertEquals(CAUSE, e.getCause()); assertEquals(CAUSE.toString(), e.getMessage()); } try { throw new JedisDataException(MESSAGE, CAUSE); } catch (Exception e) { assertSame(JedisDataException.class, e.getClass()); assertEquals(MESSAGE, e.getMessage()); assertEquals(CAUSE, e.getCause()); } } @Test public void jedis() { try { throw new JedisException(MESSAGE); } catch (Exception e) { assertSame(JedisException.class, e.getClass()); assertEquals(MESSAGE, e.getMessage()); assertNull(e.getCause()); } try { throw new JedisException(CAUSE); } catch (Exception e) { assertSame(JedisException.class, e.getClass()); assertEquals(CAUSE, e.getCause()); assertEquals(CAUSE.toString(), e.getMessage()); } try { throw new JedisException(MESSAGE, CAUSE); } catch (Exception e) { assertSame(JedisException.class, e.getClass()); assertEquals(MESSAGE, e.getMessage()); assertEquals(CAUSE, e.getCause()); } } @Test public void movedData() { HostAndPort hap = new HostAndPort("", 0); int slot = -1; try { throw new JedisMovedDataException(MESSAGE, hap, slot); } catch (Exception e) { assertSame(JedisMovedDataException.class, e.getClass()); assertEquals(MESSAGE, e.getMessage()); assertNull(e.getCause()); } try { throw new JedisMovedDataException(CAUSE, hap, slot); } catch (Exception e) { assertSame(JedisMovedDataException.class, e.getClass()); assertEquals(CAUSE, e.getCause()); assertEquals(CAUSE.toString(), e.getMessage()); } try { throw new JedisMovedDataException(MESSAGE, CAUSE, hap, slot); } catch (Exception e) { assertSame(JedisMovedDataException.class, e.getClass()); assertEquals(MESSAGE, e.getMessage()); assertEquals(CAUSE, e.getCause()); } } @Test public void noScript() { try { throw new JedisNoScriptException(MESSAGE); } catch (Exception e) { assertSame(JedisNoScriptException.class, e.getClass()); assertEquals(MESSAGE, e.getMessage()); assertNull(e.getCause()); } try { throw new JedisNoScriptException(CAUSE); } catch (Exception e) { assertSame(JedisNoScriptException.class, e.getClass()); assertEquals(CAUSE, e.getCause()); assertEquals(CAUSE.toString(), e.getMessage()); } try { throw new JedisNoScriptException(MESSAGE, CAUSE); } catch (Exception e) { assertSame(JedisNoScriptException.class, e.getClass()); assertEquals(MESSAGE, e.getMessage()); assertEquals(CAUSE, e.getCause()); } } @Test public void redirection() { HostAndPort hap = new HostAndPort("", 0); int slot = -1; try { throw new JedisRedirectionException(MESSAGE, hap, slot); } catch (Exception e) { assertSame(JedisRedirectionException.class, e.getClass()); assertEquals(MESSAGE, e.getMessage()); assertNull(e.getCause()); } try { throw new JedisRedirectionException(CAUSE, hap, slot); } catch (Exception e) { assertSame(JedisRedirectionException.class, e.getClass()); assertEquals(CAUSE, e.getCause()); assertEquals(CAUSE.toString(), e.getMessage()); } try { throw new JedisRedirectionException(MESSAGE, CAUSE, hap, slot); } catch (Exception e) { assertSame(JedisRedirectionException.class, e.getClass()); assertEquals(MESSAGE, e.getMessage()); assertEquals(CAUSE, e.getCause()); } } } ================================================ FILE: src/test/java/redis/clients/jedis/exceptions/FailoverAbortedException.java ================================================ package redis.clients.jedis.exceptions; public class FailoverAbortedException extends RuntimeException { private static final long serialVersionUID = 1925110762858409954L; public FailoverAbortedException(String message) { super(message); } public FailoverAbortedException(Throwable cause) { super(cause); } public FailoverAbortedException(String message, Throwable cause) { super(message, cause); } } ================================================ FILE: src/test/java/redis/clients/jedis/executors/ClusterCommandExecutorTest.java ================================================ package redis.clients.jedis.executors; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertNotNull; import static org.junit.jupiter.api.Assertions.assertThrows; import static org.junit.jupiter.api.Assertions.assertTrue; import static org.junit.jupiter.api.Assertions.fail; import static org.mockito.Mockito.inOrder; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; import java.time.Duration; import java.util.ArrayList; import java.util.HashMap; import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Set; import java.util.concurrent.atomic.AtomicLong; import java.util.function.LongConsumer; import redis.clients.jedis.*; import redis.clients.jedis.util.JedisClusterCRC16; import org.hamcrest.MatcherAssert; import org.hamcrest.Matchers; import org.junit.jupiter.api.Test; import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.MethodSource; import org.mockito.ArgumentMatchers; import org.mockito.InOrder; import org.mockito.Mockito; import org.mockito.invocation.InvocationOnMock; import redis.clients.jedis.exceptions.JedisAskDataException; import redis.clients.jedis.exceptions.JedisClusterOperationException; import redis.clients.jedis.exceptions.JedisConnectionException; import redis.clients.jedis.exceptions.JedisDataException; import redis.clients.jedis.exceptions.JedisMovedDataException; import redis.clients.jedis.providers.ClusterConnectionProvider; import redis.clients.jedis.util.ReflectionTestUtil; public class ClusterCommandExecutorTest { private static final Duration ONE_SECOND = Duration.ofSeconds(1); private static final CommandObject STR_COM_OBJECT = new CommandObject<>( new CommandArguments(Protocol.Command.GET).key("testkey"), BuilderFactory.STRING); // Keyless command object for testing keyless command execution with WRITE flag (FLUSHDB is // keyless and WRITE) private static final CommandObject KEYLESS_WRITE_COM_OBJECT = new CommandObject<>( new CommandArguments(Protocol.Command.FLUSHDB), BuilderFactory.STRING); /** * Helper method to invoke the private executeKeylessCommand method via reflection. */ @SuppressWarnings("unchecked") private static T invokeExecuteKeylessCommand(ClusterCommandExecutor executor, CommandObject commandObject) { return ReflectionTestUtil.invokeMethod(executor, "executeKeylessCommand", new Class[] { CommandObject.class }, commandObject); } @Test public void runSuccessfulExecute() { ClusterConnectionProvider connectionHandler = mock(ClusterConnectionProvider.class); Connection connection = mock(Connection.class); when(connectionHandler.getConnection(ArgumentMatchers.any(CommandArguments.class))) .thenReturn(connection); ClusterCommandExecutor testMe = new ClusterCommandExecutor(connectionHandler, 10, Duration.ZERO, StaticCommandFlagsRegistry.registry()) { @Override public T execute(Connection connection, CommandObject commandObject) { return (T) "foo"; } @Override protected void sleep(long ignored) { throw new RuntimeException("This test should never sleep"); } }; assertEquals("foo", testMe.executeCommand(STR_COM_OBJECT)); } @Test public void runFailOnFirstExecSuccessOnSecondExec() { ClusterConnectionProvider connectionHandler = mock(ClusterConnectionProvider.class); Connection connection = mock(Connection.class); when(connectionHandler.getConnection(ArgumentMatchers.any(CommandArguments.class))) .thenReturn(connection); ClusterCommandExecutor testMe = new ClusterCommandExecutor(connectionHandler, 10, ONE_SECOND, StaticCommandFlagsRegistry.registry()) { boolean isFirstCall = true; @Override public T execute(Connection connection, CommandObject commandObject) { if (isFirstCall) { isFirstCall = false; throw new JedisConnectionException("Borkenz"); } return (T) "foo"; } @Override protected void sleep(long ignored) { throw new RuntimeException("This test should never sleep"); } }; assertEquals("foo", testMe.executeCommand(STR_COM_OBJECT)); } @Test public void runAlwaysFailing() { ClusterConnectionProvider connectionHandler = mock(ClusterConnectionProvider.class); Connection connection = mock(Connection.class); when(connectionHandler.getConnection(ArgumentMatchers.any(CommandArguments.class))) .thenReturn(connection); final LongConsumer sleep = mock(LongConsumer.class); ClusterCommandExecutor testMe = new ClusterCommandExecutor(connectionHandler, 3, ONE_SECOND, StaticCommandFlagsRegistry.registry()) { @Override public T execute(Connection connection, CommandObject commandObject) { throw new JedisConnectionException("Connection failed"); } @Override protected void sleep(long sleepMillis) { sleep.accept(sleepMillis); } }; try { testMe.executeCommand(STR_COM_OBJECT); fail("cluster command did not fail"); } catch (JedisClusterOperationException e) { // expected } InOrder inOrder = inOrder(connectionHandler, sleep); inOrder.verify(connectionHandler, times(2)) .getConnection(ArgumentMatchers.any(CommandArguments.class)); inOrder.verify(sleep).accept(ArgumentMatchers.anyLong()); inOrder.verify(connectionHandler).renewSlotCache(); inOrder.verify(connectionHandler).getConnection(ArgumentMatchers.any(CommandArguments.class)); inOrder.verifyNoMoreInteractions(); } @Test public void runMovedSuccess() { ClusterConnectionProvider connectionHandler = mock(ClusterConnectionProvider.class); Connection connection = mock(Connection.class); final HostAndPort movedTarget = new HostAndPort(null, 0); when(connectionHandler.getConnection(ArgumentMatchers.any(CommandArguments.class))) .thenReturn(connection); when(connectionHandler.getConnection(movedTarget)).thenReturn(connection); ClusterCommandExecutor testMe = new ClusterCommandExecutor(connectionHandler, 10, ONE_SECOND, StaticCommandFlagsRegistry.registry()) { boolean isFirstCall = true; @Override public T execute(Connection connection, CommandObject commandObject) { if (isFirstCall) { isFirstCall = false; // Slot 0 moved throw new JedisMovedDataException("", movedTarget, 0); } return (T) "foo"; } @Override protected void sleep(long ignored) { throw new RuntimeException("This test should never sleep"); } }; assertEquals("foo", testMe.executeCommand(STR_COM_OBJECT)); InOrder inOrder = inOrder(connectionHandler); inOrder.verify(connectionHandler).getConnection(ArgumentMatchers.any(CommandArguments.class)); inOrder.verify(connectionHandler).renewSlotCache(ArgumentMatchers.any()); inOrder.verify(connectionHandler).getConnection(movedTarget); inOrder.verifyNoMoreInteractions(); } @Test public void runAskSuccess() { ClusterConnectionProvider connectionHandler = mock(ClusterConnectionProvider.class); Connection connection = mock(Connection.class); final HostAndPort askTarget = new HostAndPort(null, 0); when(connectionHandler.getConnection(ArgumentMatchers.any(CommandArguments.class))) .thenReturn(connection); when(connectionHandler.getConnection(askTarget)).thenReturn(connection); ClusterCommandExecutor testMe = new ClusterCommandExecutor(connectionHandler, 10, ONE_SECOND, StaticCommandFlagsRegistry.registry()) { boolean isFirstCall = true; @Override public T execute(Connection connection, CommandObject commandObject) { if (isFirstCall) { isFirstCall = false; // Slot 0 moved throw new JedisAskDataException("", askTarget, 0); } return (T) "foo"; } @Override protected void sleep(long ignored) { throw new RuntimeException("This test should never sleep"); } }; assertEquals("foo", testMe.executeCommand(STR_COM_OBJECT)); InOrder inOrder = inOrder(connectionHandler, connection); inOrder.verify(connectionHandler).getConnection(ArgumentMatchers.any(CommandArguments.class)); inOrder.verify(connectionHandler).getConnection(askTarget); // inOrder.verify(connection).asking(); inOrder.verify(connection).close(); // From the finally clause in runWithRetries() inOrder.verifyNoMoreInteractions(); } // requires 'execute(Connection connection, CommandObject commandObject)' separately @Test public void runMovedThenAllNodesFailing() { // Test: // First attempt is a JedisMovedDataException() move, because we asked the wrong node. // All subsequent attempts are JedisConnectionExceptions, because all nodes are now down. // In response to the JedisConnectionExceptions, run() retries random nodes until maxAttempts is // reached. ClusterConnectionProvider connectionHandler = mock(ClusterConnectionProvider.class); final Connection redirecter = mock(Connection.class); when(connectionHandler.getConnection(ArgumentMatchers.any(CommandArguments.class))) .thenReturn(redirecter); final Connection failer = mock(Connection.class); when(connectionHandler.getConnection(ArgumentMatchers.any(HostAndPort.class))) .thenReturn(failer); Mockito.doAnswer((InvocationOnMock invocation) -> { when(connectionHandler.getConnection(ArgumentMatchers.any(CommandArguments.class))) .thenReturn(failer); return null; }).when(connectionHandler).renewSlotCache(); final LongConsumer sleep = mock(LongConsumer.class); final HostAndPort movedTarget = new HostAndPort(null, 0); ClusterCommandExecutor testMe = new ClusterCommandExecutor(connectionHandler, 5, ONE_SECOND, StaticCommandFlagsRegistry.registry()) { @Override public T execute(Connection connection, CommandObject commandObject) { if (redirecter == connection) { // First attempt, report moved throw new JedisMovedDataException("Moved", movedTarget, 0); } if (failer == connection) { // Second attempt in response to the move, report failure throw new JedisConnectionException("Connection failed"); } throw new IllegalStateException("Should have thrown jedis exception"); } @Override protected void sleep(long sleepMillis) { sleep.accept(sleepMillis); } }; try { testMe.executeCommand(STR_COM_OBJECT); fail("cluster command did not fail"); } catch (JedisClusterOperationException e) { // expected } InOrder inOrder = inOrder(connectionHandler, sleep); inOrder.verify(connectionHandler).getConnection(ArgumentMatchers.any(CommandArguments.class)); inOrder.verify(connectionHandler).renewSlotCache(redirecter); inOrder.verify(connectionHandler, times(2)).getConnection(movedTarget); inOrder.verify(sleep).accept(ArgumentMatchers.anyLong()); inOrder.verify(connectionHandler).renewSlotCache(); inOrder.verify(connectionHandler, times(2)) .getConnection(ArgumentMatchers.any(CommandArguments.class)); inOrder.verify(sleep).accept(ArgumentMatchers.anyLong()); inOrder.verify(connectionHandler).renewSlotCache(); inOrder.verifyNoMoreInteractions(); } // requires 'execute(Connection connection, CommandObject commandObject)' separately @Test public void runMasterFailingReplicaRecovering() { // We have two nodes, master and replica, and master has just gone down permanently. // // Test: // 1. We try to contact master => JedisConnectionException // 2. We try to contact master => JedisConnectionException // 3. sleep and renew // 4. We try to contact replica => Success, because it has now failed over final Connection master = mock(Connection.class); when(master.toString()).thenReturn("master"); final Connection replica = mock(Connection.class); when(replica.toString()).thenReturn("replica"); ClusterConnectionProvider connectionHandler = mock(ClusterConnectionProvider.class); when(connectionHandler.getConnection(ArgumentMatchers.any(CommandArguments.class))) .thenReturn(master); Mockito.doAnswer((InvocationOnMock invocation) -> { when(connectionHandler.getConnection(ArgumentMatchers.any(CommandArguments.class))) .thenReturn(replica); return null; }).when(connectionHandler).renewSlotCache(); final AtomicLong totalSleepMs = new AtomicLong(); ClusterCommandExecutor testMe = new ClusterCommandExecutor(connectionHandler, 5, ONE_SECOND, StaticCommandFlagsRegistry.registry()) { @Override public T execute(Connection connection, CommandObject commandObject) { assertNotNull(connection); if (connection.toString().equals("master")) { throw new JedisConnectionException("Master is down"); } assert connection.toString().equals("replica"); return (T) "Success!"; } @Override protected void sleep(long sleepMillis) { // assert sleepMillis > 0; totalSleepMs.addAndGet(sleepMillis); } }; assertEquals("Success!", testMe.executeCommand(STR_COM_OBJECT)); InOrder inOrder = inOrder(connectionHandler); inOrder.verify(connectionHandler, times(2)) .getConnection(ArgumentMatchers.any(CommandArguments.class)); inOrder.verify(connectionHandler).renewSlotCache(); inOrder.verify(connectionHandler).getConnection(ArgumentMatchers.any(CommandArguments.class)); inOrder.verifyNoMoreInteractions(); MatcherAssert.assertThat(totalSleepMs.get(), Matchers.greaterThan(0L)); } @Test public void runRethrowsJedisNoReachableClusterNodeException() { ClusterConnectionProvider connectionHandler = mock(ClusterConnectionProvider.class); when(connectionHandler.getConnection(ArgumentMatchers.any(CommandArguments.class))) .thenThrow(JedisClusterOperationException.class); ClusterCommandExecutor testMe = new ClusterCommandExecutor(connectionHandler, 10, Duration.ZERO, StaticCommandFlagsRegistry.registry()) { @Override public T execute(Connection connection, CommandObject commandObject) { return null; } @Override protected void sleep(long ignored) { throw new RuntimeException("This test should never sleep"); } }; assertThrows(JedisClusterOperationException.class, () -> testMe.executeCommand(STR_COM_OBJECT)); } @Test public void runStopsRetryingAfterTimeout() { ClusterConnectionProvider connectionHandler = mock(ClusterConnectionProvider.class); Connection connection = mock(Connection.class); when(connectionHandler.getConnection(ArgumentMatchers.any(CommandArguments.class))) .thenReturn(connection); // final LongConsumer sleep = mock(LongConsumer.class); final AtomicLong totalSleepMs = new AtomicLong(); ClusterCommandExecutor testMe = new ClusterCommandExecutor(connectionHandler, 3, Duration.ZERO, StaticCommandFlagsRegistry.registry()) { @Override public T execute(Connection connection, CommandObject commandObject) { try { // exceed deadline Thread.sleep(2L); } catch (InterruptedException e) { throw new RuntimeException(e); } throw new JedisConnectionException("Connection failed"); } @Override protected void sleep(long sleepMillis) { // sleep.accept(sleepMillis); totalSleepMs.addAndGet(sleepMillis); } }; try { testMe.executeCommand(STR_COM_OBJECT); fail("cluster command did not fail"); } catch (JedisClusterOperationException e) { // expected } // InOrder inOrder = inOrder(connectionHandler, sleep); InOrder inOrder = inOrder(connectionHandler); inOrder.verify(connectionHandler).getConnection(ArgumentMatchers.any(CommandArguments.class)); inOrder.verifyNoMoreInteractions(); assertEquals(0L, totalSleepMs.get()); } @Test public void runSuccessfulExecuteKeylessCommand() { ClusterConnectionProvider connectionHandler = mock(ClusterConnectionProvider.class); Map connectionMap = new HashMap<>(); ConnectionPool pool = mock(ConnectionPool.class); Connection connection = mock(Connection.class); connectionMap.put("localhost:6379", pool); when(connectionHandler.getPrimaryNodesConnectionMap()).thenReturn(connectionMap); when(pool.getResource()).thenReturn(connection); ClusterCommandExecutor testMe = new ClusterCommandExecutor(connectionHandler, 10, Duration.ZERO, StaticCommandFlagsRegistry.registry()) { @Override public T execute(Connection connection, CommandObject commandObject) { return (T) "OK"; } @Override protected void sleep(long ignored) { throw new RuntimeException("This test should never sleep"); } }; assertEquals("OK", invokeExecuteKeylessCommand(testMe, KEYLESS_WRITE_COM_OBJECT)); } @Test public void runKeylessCommandUsesConnectionMapRoundRobin() { ClusterConnectionProvider connectionHandler = mock(ClusterConnectionProvider.class); Map connectionMap = new HashMap<>(); ConnectionPool pool = mock(ConnectionPool.class); Connection connection = mock(Connection.class); connectionMap.put("localhost:6379", pool); when(connectionHandler.getPrimaryNodesConnectionMap()).thenReturn(connectionMap); when(pool.getResource()).thenReturn(connection); ClusterCommandExecutor testMe = new ClusterCommandExecutor(connectionHandler, 10, Duration.ZERO, StaticCommandFlagsRegistry.registry()) { @Override public T execute(Connection connection, CommandObject commandObject) { return (T) "OK"; } @Override protected void sleep(long ignored) { throw new RuntimeException("This test should never sleep"); } }; invokeExecuteKeylessCommand(testMe, KEYLESS_WRITE_COM_OBJECT); // Verify that getPrimaryNodesConnectionMap() was called for round-robin distribution InOrder inOrder = inOrder(connectionHandler, pool, connection); inOrder.verify(connectionHandler).getPrimaryNodesConnectionMap(); inOrder.verify(pool).getResource(); inOrder.verify(connection).close(); inOrder.verifyNoMoreInteractions(); } @Test public void runKeylessCommandThrowsOnRedirections() { ClusterConnectionProvider connectionHandler = mock(ClusterConnectionProvider.class); Map connectionMap = new HashMap<>(); ConnectionPool pool = mock(ConnectionPool.class); Connection connection1 = mock(Connection.class); final HostAndPort movedTarget = new HostAndPort("127.0.0.1", 6380); final int slot = 12345; connectionMap.put("localhost:6379", pool); when(connectionHandler.getPrimaryNodesConnectionMap()).thenReturn(connectionMap); when(pool.getResource()).thenReturn(connection1); ClusterCommandExecutor testMe = new ClusterCommandExecutor(connectionHandler, 10, ONE_SECOND, StaticCommandFlagsRegistry.registry()) { @Override public T execute(Connection connection, CommandObject commandObject) { // When followRedirections=false, redirections should be thrown as exceptions throw new JedisMovedDataException("MOVED " + slot, movedTarget, slot); } @Override protected void sleep(long ignored) { throw new RuntimeException("This test should never sleep"); } }; // When followRedirections=false, the redirection exception should be thrown JedisMovedDataException exception = assertThrows(JedisMovedDataException.class, () -> invokeExecuteKeylessCommand(testMe, KEYLESS_WRITE_COM_OBJECT)); // Verify exception contains correct information assertEquals(movedTarget, exception.getTargetNode()); assertEquals(slot, exception.getSlot()); // Verify that we only tried once (no retry after redirection) verify(connectionHandler, times(1)).getPrimaryNodesConnectionMap(); verify(pool, times(1)).getResource(); verify(connection1).close(); } @Test public void runKeylessCommandThrowsAskDataExceptionOnAskRedirection() { ClusterConnectionProvider connectionHandler = mock(ClusterConnectionProvider.class); Map connectionMap = new HashMap<>(); ConnectionPool pool = mock(ConnectionPool.class); Connection connection1 = mock(Connection.class); final HostAndPort askTarget = new HostAndPort("127.0.0.1", 6381); final int slot = 9999; connectionMap.put("localhost:6379", pool); when(connectionHandler.getPrimaryNodesConnectionMap()).thenReturn(connectionMap); when(pool.getResource()).thenReturn(connection1); ClusterCommandExecutor testMe = new ClusterCommandExecutor(connectionHandler, 10, ONE_SECOND, StaticCommandFlagsRegistry.registry()) { @Override public T execute(Connection connection, CommandObject commandObject) { // When followRedirections=false, ASK redirections should be thrown as exceptions throw new JedisAskDataException("ASK " + slot, askTarget, slot); } @Override protected void sleep(long ignored) { throw new RuntimeException("This test should never sleep"); } }; // When followRedirections=false, the ASK exception should be thrown JedisAskDataException exception = assertThrows(JedisAskDataException.class, () -> invokeExecuteKeylessCommand(testMe, KEYLESS_WRITE_COM_OBJECT)); // Verify exception contains correct information assertEquals(askTarget, exception.getTargetNode()); assertEquals(slot, exception.getSlot()); // Verify that we only tried once (no retry after redirection) verify(connectionHandler, times(1)).getPrimaryNodesConnectionMap(); verify(pool, times(1)).getResource(); verify(connection1).close(); } @Test public void runKeylessCommandFailsAfterMaxAttempts() { ClusterConnectionProvider connectionHandler = mock(ClusterConnectionProvider.class); Map connectionMap = new HashMap<>(); ConnectionPool pool = mock(ConnectionPool.class); Connection connection1 = mock(Connection.class); Connection connection2 = mock(Connection.class); Connection connection3 = mock(Connection.class); final LongConsumer sleep = mock(LongConsumer.class); connectionMap.put("localhost:6379", pool); when(connectionHandler.getPrimaryNodesConnectionMap()).thenReturn(connectionMap); when(pool.getResource()).thenReturn(connection1, connection2, connection3); ClusterCommandExecutor testMe = new ClusterCommandExecutor(connectionHandler, 3, ONE_SECOND, StaticCommandFlagsRegistry.registry()) { @Override public T execute(Connection connection, CommandObject commandObject) { throw new JedisConnectionException("Connection failed"); } @Override protected void sleep(long sleepMillis) { sleep.accept(sleepMillis); } }; try { invokeExecuteKeylessCommand(testMe, KEYLESS_WRITE_COM_OBJECT); fail("keyless command did not fail"); } catch (JedisClusterOperationException e) { // expected } // Verify that we tried connection map access and performed slot cache renewal // getPrimaryNodesConnectionMap() called 3 times (once for each connection attempt) // getResource() called 3 times, sleep called once, renewSlotCache called once verify(connectionHandler, times(3)).getPrimaryNodesConnectionMap(); verify(pool, times(3)).getResource(); verify(connection1).close(); verify(connection2).close(); verify(connection3).close(); verify(sleep).accept(ArgumentMatchers.anyLong()); verify(connectionHandler).renewSlotCache(); } @Test public void runKeylessCommandFailsWithEmptyConnectionMap() { ClusterConnectionProvider connectionHandler = mock(ClusterConnectionProvider.class); Map emptyConnectionMap = new HashMap<>(); when(connectionHandler.getPrimaryNodesConnectionMap()).thenReturn(emptyConnectionMap); ClusterCommandExecutor testMe = new ClusterCommandExecutor(connectionHandler, 3, ONE_SECOND, StaticCommandFlagsRegistry.registry()) { @Override public T execute(Connection connection, CommandObject commandObject) { return (T) "should_not_reach_here"; } @Override protected void sleep(long ignored) { throw new RuntimeException("This test should never sleep"); } }; try { invokeExecuteKeylessCommand(testMe, KEYLESS_WRITE_COM_OBJECT); fail("keyless command should fail with empty connection map"); } catch (JedisClusterOperationException e) { assertEquals("No cluster nodes available.", e.getMessage()); } // Verify that getPrimaryNodesConnectionMap() was called verify(connectionHandler).getPrimaryNodesConnectionMap(); } @Test public void runKeylessCommandRoundRobinDistribution() { ClusterConnectionProvider connectionHandler = mock(ClusterConnectionProvider.class); Map connectionMap = new HashMap<>(); // Create multiple pools to test round-robin ConnectionPool pool1 = mock(ConnectionPool.class); ConnectionPool pool2 = mock(ConnectionPool.class); ConnectionPool pool3 = mock(ConnectionPool.class); Connection connection1 = mock(Connection.class); Connection connection2 = mock(Connection.class); Connection connection3 = mock(Connection.class); connectionMap.put("localhost:6379", pool1); connectionMap.put("localhost:6380", pool2); connectionMap.put("localhost:6381", pool3); when(connectionHandler.getPrimaryNodesConnectionMap()).thenReturn(connectionMap); when(pool1.getResource()).thenReturn(connection1); when(pool2.getResource()).thenReturn(connection2); when(pool3.getResource()).thenReturn(connection3); // Track which connections are used List usedConnections = new ArrayList<>(); ClusterCommandExecutor testMe = new ClusterCommandExecutor(connectionHandler, 10, Duration.ZERO, StaticCommandFlagsRegistry.registry()) { @Override public T execute(Connection connection, CommandObject commandObject) { usedConnections.add(connection); return (T) "OK"; } @Override protected void sleep(long ignored) { throw new RuntimeException("This test should never sleep"); } }; // Execute multiple keyless commands to verify round-robin invokeExecuteKeylessCommand(testMe, KEYLESS_WRITE_COM_OBJECT); invokeExecuteKeylessCommand(testMe, KEYLESS_WRITE_COM_OBJECT); invokeExecuteKeylessCommand(testMe, KEYLESS_WRITE_COM_OBJECT); invokeExecuteKeylessCommand(testMe, KEYLESS_WRITE_COM_OBJECT); // Should cycle back to first // Verify round-robin behavior - should cycle through all connections assertEquals(4, usedConnections.size()); Set uniqueConnections = new HashSet<>(usedConnections); assertEquals(3, uniqueConnections.size(), "Round-robin should distribute across multiple nodes"); } @Test public void runKeylessCommandCircularCounterNeverOverflows() { ClusterConnectionProvider connectionHandler = mock(ClusterConnectionProvider.class); Map connectionMap = new HashMap<>(); // Create 3 pools to test circular behavior ConnectionPool pool1 = mock(ConnectionPool.class); ConnectionPool pool2 = mock(ConnectionPool.class); ConnectionPool pool3 = mock(ConnectionPool.class); Connection connection1 = mock(Connection.class); Connection connection2 = mock(Connection.class); Connection connection3 = mock(Connection.class); connectionMap.put("node1:6379", pool1); connectionMap.put("node2:6379", pool2); connectionMap.put("node3:6379", pool3); when(connectionHandler.getPrimaryNodesConnectionMap()).thenReturn(connectionMap); when(pool1.getResource()).thenReturn(connection1); when(pool2.getResource()).thenReturn(connection2); when(pool3.getResource()).thenReturn(connection3); ClusterCommandExecutor testMe = new ClusterCommandExecutor(connectionHandler, 10, Duration.ZERO, StaticCommandFlagsRegistry.registry()) { @Override public T execute(Connection connection, CommandObject commandObject) { return (T) "OK"; } @Override protected void sleep(long ignored) { throw new RuntimeException("This test should never sleep"); } }; // Execute many commands to test circular behavior // With our implementation using getAndUpdate(current -> (current + 1) % nodeCount), // the counter never exceeds nodeCount-1, so overflow is impossible for (int i = 0; i < 100; i++) { String result = invokeExecuteKeylessCommand(testMe, KEYLESS_WRITE_COM_OBJECT); assertEquals("OK", result); } // Verify that getPrimaryNodesConnectionMap() was called for each execution verify(connectionHandler, times(100)).getPrimaryNodesConnectionMap(); // The circular counter implementation ensures no overflow can occur // because the counter value is always between 0 and (nodeCount-1) } @Test public void runKeylessCommandEvenDistributionRoundRobin() { ClusterConnectionProvider connectionHandler = mock(ClusterConnectionProvider.class); Map connectionMap = new HashMap<>(); // Create 4 pools to test even distribution ConnectionPool pool1 = mock(ConnectionPool.class); ConnectionPool pool2 = mock(ConnectionPool.class); ConnectionPool pool3 = mock(ConnectionPool.class); ConnectionPool pool4 = mock(ConnectionPool.class); Connection connection1 = mock(Connection.class); Connection connection2 = mock(Connection.class); Connection connection3 = mock(Connection.class); Connection connection4 = mock(Connection.class); // Use ordered map to ensure consistent iteration order for testing connectionMap.put("node1:6379", pool1); connectionMap.put("node2:6379", pool2); connectionMap.put("node3:6379", pool3); connectionMap.put("node4:6379", pool4); when(connectionHandler.getPrimaryNodesConnectionMap()).thenReturn(connectionMap); when(pool1.getResource()).thenReturn(connection1); when(pool2.getResource()).thenReturn(connection2); when(pool3.getResource()).thenReturn(connection3); when(pool4.getResource()).thenReturn(connection4); // Track connection usage count Map connectionUsage = new HashMap<>(); connectionUsage.put(connection1, 0); connectionUsage.put(connection2, 0); connectionUsage.put(connection3, 0); connectionUsage.put(connection4, 0); ClusterCommandExecutor testMe = new ClusterCommandExecutor(connectionHandler, 10, Duration.ZERO, StaticCommandFlagsRegistry.registry()) { @Override public T execute(Connection connection, CommandObject commandObject) { connectionUsage.put(connection, connectionUsage.get(connection) + 1); return (T) "OK"; } @Override protected void sleep(long ignored) { throw new RuntimeException("This test should never sleep"); } }; // Execute commands - should be evenly distributed int totalCommands = 40; // Multiple of 4 for perfect distribution for (int i = 0; i < totalCommands; i++) { invokeExecuteKeylessCommand(testMe, KEYLESS_WRITE_COM_OBJECT); } // Verify even distribution - each node should get exactly 10 commands int expectedPerNode = totalCommands / 4; assertEquals(expectedPerNode, connectionUsage.get(connection1).intValue(), "Node 1 should receive exactly " + expectedPerNode + " commands"); assertEquals(expectedPerNode, connectionUsage.get(connection2).intValue(), "Node 2 should receive exactly " + expectedPerNode + " commands"); assertEquals(expectedPerNode, connectionUsage.get(connection3).intValue(), "Node 3 should receive exactly " + expectedPerNode + " commands"); assertEquals(expectedPerNode, connectionUsage.get(connection4).intValue(), "Node 4 should receive exactly " + expectedPerNode + " commands"); // Verify total commands executed int totalExecuted = connectionUsage.values().stream().mapToInt(Integer::intValue).sum(); assertEquals(totalCommands, totalExecuted, "Total commands executed should match"); // Verify that getPrimaryNodesConnectionMap() was called for each execution verify(connectionHandler, times(totalCommands)).getPrimaryNodesConnectionMap(); } @Test public void runKeylessCommandRoundRobinSequence() { ClusterConnectionProvider connectionHandler = mock(ClusterConnectionProvider.class); Map connectionMap = new HashMap<>(); // Create 3 pools for simpler sequence verification ConnectionPool pool1 = mock(ConnectionPool.class); ConnectionPool pool2 = mock(ConnectionPool.class); ConnectionPool pool3 = mock(ConnectionPool.class); Connection connection1 = mock(Connection.class); Connection connection2 = mock(Connection.class); Connection connection3 = mock(Connection.class); // Use LinkedHashMap to ensure consistent iteration order connectionMap = new java.util.LinkedHashMap<>(); connectionMap.put("node1:6379", pool1); connectionMap.put("node2:6379", pool2); connectionMap.put("node3:6379", pool3); when(connectionHandler.getPrimaryNodesConnectionMap()).thenReturn(connectionMap); when(pool1.getResource()).thenReturn(connection1); when(pool2.getResource()).thenReturn(connection2); when(pool3.getResource()).thenReturn(connection3); // Track the exact sequence of connections used List connectionSequence = new ArrayList<>(); ClusterCommandExecutor testMe = new ClusterCommandExecutor(connectionHandler, 10, Duration.ZERO, StaticCommandFlagsRegistry.registry()) { @Override public T execute(Connection connection, CommandObject commandObject) { if (connection == connection1) { connectionSequence.add("node1"); } else if (connection == connection2) { connectionSequence.add("node2"); } else if (connection == connection3) { connectionSequence.add("node3"); } return (T) "OK"; } @Override protected void sleep(long ignored) { throw new RuntimeException("This test should never sleep"); } }; // Execute 9 commands to see 3 complete cycles for (int i = 0; i < 9; i++) { invokeExecuteKeylessCommand(testMe, KEYLESS_WRITE_COM_OBJECT); } // Verify the round-robin sequence List expectedSequence = new ArrayList<>(); expectedSequence.add("node1"); expectedSequence.add("node2"); expectedSequence.add("node3"); // First cycle expectedSequence.add("node1"); expectedSequence.add("node2"); expectedSequence.add("node3"); // Second cycle expectedSequence.add("node1"); expectedSequence.add("node2"); expectedSequence.add("node3"); // Third cycle assertEquals(expectedSequence, connectionSequence, "Round-robin should follow exact sequence: node1 -> node2 -> node3 -> node1 -> ..."); } @Test public void runKeylessCommandWithReadOnlyCommandUsesAllNodesConnectionMap() { // Create a read-only command object using GET command (which has READONLY flag) CommandObject readOnlyCommandObject = new CommandObject<>( new CommandArguments(Protocol.Command.GET).key("testkey"), BuilderFactory.STRING); ClusterConnectionProvider connectionHandler = mock(ClusterConnectionProvider.class); Map allNodesConnectionMap = new HashMap<>(); ConnectionPool pool = mock(ConnectionPool.class); Connection connection = mock(Connection.class); // Setup connection map with all nodes (including replicas) allNodesConnectionMap.put("primary:6379", pool); allNodesConnectionMap.put("replica:6380", pool); // For read-only commands, getConnectionMap() should be called (all nodes including replicas) when(connectionHandler.getConnectionMap()).thenReturn(allNodesConnectionMap); when(pool.getResource()).thenReturn(connection); ClusterCommandExecutor testMe = new ClusterCommandExecutor(connectionHandler, 10, Duration.ZERO, StaticCommandFlagsRegistry.registry()) { @Override public T execute(Connection connection, CommandObject commandObject) { return (T) "readonly_result"; } @Override protected void sleep(long ignored) { throw new RuntimeException("This test should never sleep"); } }; assertEquals("readonly_result", invokeExecuteKeylessCommand(testMe, readOnlyCommandObject)); // Verify that getConnectionMap() was called (for read-only commands, uses all nodes) // and NOT getPrimaryNodesConnectionMap() verify(connectionHandler).getConnectionMap(); verify(connectionHandler, times(0)).getPrimaryNodesConnectionMap(); verify(pool).getResource(); verify(connection).close(); } @Test public void runKeylessCommandWithWriteCommandUsesPrimaryNodesConnectionMap() { // Create a write command object using SET command (which has WRITE flag, not READONLY) CommandObject writeCommandObject = new CommandObject<>( new CommandArguments(Protocol.Command.SET).key("testkey").add("value"), BuilderFactory.STRING); ClusterConnectionProvider connectionHandler = mock(ClusterConnectionProvider.class); Map primaryNodesConnectionMap = new HashMap<>(); ConnectionPool pool = mock(ConnectionPool.class); Connection connection = mock(Connection.class); // Setup connection map with only primary nodes primaryNodesConnectionMap.put("primary:6379", pool); // For write commands, getPrimaryNodesConnectionMap() should be called when(connectionHandler.getPrimaryNodesConnectionMap()).thenReturn(primaryNodesConnectionMap); when(pool.getResource()).thenReturn(connection); ClusterCommandExecutor testMe = new ClusterCommandExecutor(connectionHandler, 10, Duration.ZERO, StaticCommandFlagsRegistry.registry()) { @Override public T execute(Connection connection, CommandObject commandObject) { return (T) "write_result"; } @Override protected void sleep(long ignored) { throw new RuntimeException("This test should never sleep"); } }; assertEquals("write_result", invokeExecuteKeylessCommand(testMe, writeCommandObject)); // Verify that getPrimaryNodesConnectionMap() was called (for write commands, uses only // primaries) // and NOT getConnectionMap() verify(connectionHandler).getPrimaryNodesConnectionMap(); verify(connectionHandler, times(0)).getConnectionMap(); verify(pool).getResource(); verify(connection).close(); } /** * Provides command objects for commands that use ONE_SUCCEEDED response policy. These commands * should return success if at least one node succeeds. */ static java.util.stream.Stream> oneSucceededPolicyCommands() { return java.util.stream.Stream.of( new CommandObject<>(new CommandArguments(Protocol.Command.SCRIPT).add("KILL"), BuilderFactory.STRING), new CommandObject<>(new CommandArguments(Protocol.Command.FUNCTION).add("KILL"), BuilderFactory.STRING)); } /** * This test verifies the bug: broadcastCommand throws on partial failure ignoring ONE_SUCCEEDED * policy. When any node throws an exception, isErrored is set to true, which causes subsequent * successful replies to be skipped and the method to unconditionally throw * JedisBroadcastException. For ONE_SUCCEEDED response policy (used by SCRIPT KILL, FUNCTION * KILL), the method needs to return success if at least one node succeeded. Currently, a single * node failure causes the entire broadcast to fail even when other nodes succeed. NOTE: This test * FAILS when the bug exists, demonstrating the issue. When the bug is fixed, this test will pass. */ @ParameterizedTest @MethodSource("oneSucceededPolicyCommands") public void broadcastCommandShouldSucceedWithOneSucceededPolicyWhenSomeNodesFail( CommandObject killCommand) { ClusterConnectionProvider connectionHandler = mock(ClusterConnectionProvider.class); Map connectionMap = new java.util.LinkedHashMap<>(); // Create 3 node pools ConnectionPool pool1 = mock(ConnectionPool.class); ConnectionPool pool2 = mock(ConnectionPool.class); ConnectionPool pool3 = mock(ConnectionPool.class); Connection connection1 = mock(Connection.class); Connection connection2 = mock(Connection.class); Connection connection3 = mock(Connection.class); connectionMap.put("node1:6379", pool1); connectionMap.put("node2:6379", pool2); connectionMap.put("node3:6379", pool3); when(connectionHandler.getPrimaryNodesConnectionMap()).thenReturn(connectionMap); when(pool1.getResource()).thenReturn(connection1); when(pool2.getResource()).thenReturn(connection2); when(pool3.getResource()).thenReturn(connection3); List nodeResults = new ArrayList<>(); ClusterCommandExecutor testMe = new ClusterCommandExecutor(connectionHandler, 10, Duration.ZERO, StaticCommandFlagsRegistry.registry()) { int callCount = 0; @Override public T execute(Connection connection, CommandObject commandObject) { callCount++; if (callCount == 1 || callCount == 3) { // First and third nodes fail (e.g., no script/function running) nodeResults.add("FAIL"); throw new JedisDataException("NOTBUSY No scripts in execution right now."); } // Second node succeeds nodeResults.add("OK"); return (T) "OK"; } @Override protected void sleep(long ignored) { throw new RuntimeException("This test should never sleep"); } }; // With ONE_SUCCEEDED policy, the broadcast should succeed because at least one node returned OK // BUG: Currently this throws JedisBroadcastException because isErrored=true ignores the policy String result = testMe.broadcastCommand(killCommand, true); assertEquals("OK", result, "broadcastCommand should return OK when at least one node succeeds " + "with ONE_SUCCEEDED policy"); // Verify that all nodes were queried assertEquals(3, nodeResults.size(), "Should have queried all 3 nodes"); assertTrue(nodeResults.contains("OK"), "At least one node should have succeeded"); } /** * This test verifies that broadcastCommand throws JedisMovedDataException when a node responds * with a MOVED redirection, since broadcastCommand uses followRedirections=false. The exception * should contain the correct target node and slot information. */ @Test public void broadcastCommandThrowsMovedDataExceptionOnRedirection() { ClusterConnectionProvider connectionHandler = mock(ClusterConnectionProvider.class); Map connectionMap = new java.util.LinkedHashMap<>(); ConnectionPool pool1 = mock(ConnectionPool.class); Connection connection1 = mock(Connection.class); final HostAndPort movedTarget = new HostAndPort("127.0.0.1", 6382); final int slot = 7777; connectionMap.put("node1:6379", pool1); when(connectionHandler.getPrimaryNodesConnectionMap()).thenReturn(connectionMap); when(pool1.getResource()).thenReturn(connection1); CommandObject flushdbCommand = new CommandObject<>( new CommandArguments(Protocol.Command.FLUSHDB), BuilderFactory.STRING); ClusterCommandExecutor testMe = new ClusterCommandExecutor(connectionHandler, 10, ONE_SECOND, StaticCommandFlagsRegistry.registry()) { @Override public T execute(Connection connection, CommandObject commandObject) { // Simulate MOVED redirection throw new JedisMovedDataException("MOVED " + slot, movedTarget, slot); } @Override protected void sleep(long ignored) { throw new RuntimeException("This test should never sleep"); } }; // broadcastCommand uses followRedirections=false, so it should throw the exception // (caught by the aggregator as an error for that node) // In this case with only one node, the broadcast will fail try { testMe.broadcastCommand(flushdbCommand, true); fail("broadcastCommand should have thrown an exception when redirection occurs"); } catch (Exception e) { // The exception should be caught by the aggregator; verify the root cause is a redirection // The aggregator collects errors per node } // Verify that we tried to execute the command verify(pool1, times(1)).getResource(); verify(connection1).close(); } /** * This test verifies that broadcastCommand throws JedisAskDataException when a node responds with * an ASK redirection, since broadcastCommand uses followRedirections=false. The exception should * contain the correct target node and slot information. */ @Test public void broadcastCommandThrowsAskDataExceptionOnRedirection() { ClusterConnectionProvider connectionHandler = mock(ClusterConnectionProvider.class); Map connectionMap = new java.util.LinkedHashMap<>(); ConnectionPool pool1 = mock(ConnectionPool.class); Connection connection1 = mock(Connection.class); final HostAndPort askTarget = new HostAndPort("127.0.0.1", 6383); final int slot = 8888; connectionMap.put("node1:6379", pool1); when(connectionHandler.getPrimaryNodesConnectionMap()).thenReturn(connectionMap); when(pool1.getResource()).thenReturn(connection1); CommandObject flushdbCommand = new CommandObject<>( new CommandArguments(Protocol.Command.FLUSHDB), BuilderFactory.STRING); ClusterCommandExecutor testMe = new ClusterCommandExecutor(connectionHandler, 10, ONE_SECOND, StaticCommandFlagsRegistry.registry()) { @Override public T execute(Connection connection, CommandObject commandObject) { // Simulate ASK redirection throw new JedisAskDataException("ASK " + slot, askTarget, slot); } @Override protected void sleep(long ignored) { throw new RuntimeException("This test should never sleep"); } }; // broadcastCommand uses followRedirections=false, so it should throw the exception // (caught by the aggregator as an error for that node) try { testMe.broadcastCommand(flushdbCommand, true); fail("broadcastCommand should have thrown an exception when redirection occurs"); } catch (Exception e) { // The exception should be caught by the aggregator; verify the root cause is a redirection } // Verify that we tried to execute the command verify(pool1, times(1)).getResource(); verify(connection1).close(); } /** * This test verifies the bug: executeMultiShardCommand throws on partial failure ignoring * ONE_SUCCEEDED policy. The same issue as broadcastCommand exists in executeMultiShardCommand: * when any shard throws an exception, isErrored is set to true, which causes subsequent * successful replies to be skipped and the method to unconditionally throw * JedisBroadcastException. For ONE_SUCCEEDED response policy, the method needs to return success * if at least one shard succeeded. NOTE: This test FAILS when the bug exists, demonstrating the * issue. When the bug is fixed, this test will pass. */ @Test public void executeMultiShardCommandShouldSucceedWithOneSucceededPolicyWhenSomeShardsFail() { ClusterConnectionProvider connectionHandler = mock(ClusterConnectionProvider.class); Connection connection = mock(Connection.class); when(connectionHandler.getConnection(ArgumentMatchers.any(CommandArguments.class))) .thenReturn(connection); // Create multiple command objects to simulate multi-shard execution // Using SCRIPT KILL which has ONE_SUCCEEDED policy List> commandObjects = new ArrayList<>(); commandObjects.add(new CommandObject<>( new CommandArguments(Protocol.Command.SCRIPT).add("KILL"), BuilderFactory.STRING)); commandObjects.add(new CommandObject<>( new CommandArguments(Protocol.Command.SCRIPT).add("KILL"), BuilderFactory.STRING)); commandObjects.add(new CommandObject<>( new CommandArguments(Protocol.Command.SCRIPT).add("KILL"), BuilderFactory.STRING)); ClusterCommandExecutor testMe = new ClusterCommandExecutor(connectionHandler, 10, Duration.ZERO, StaticCommandFlagsRegistry.registry()) { int callCount = 0; @Override public T execute(Connection connection, CommandObject commandObject) { callCount++; if (callCount == 1) { // First shard fails throw new JedisDataException("NOTBUSY No scripts in execution right now."); } // Other shards succeed return (T) "OK"; } @Override protected void sleep(long ignored) { throw new RuntimeException("This test should never sleep"); } }; // With ONE_SUCCEEDED policy, the multi-shard command should succeed because at least one // shard returned OK // BUG: Currently this throws JedisBroadcastException because isErrored=true ignores the policy String result = testMe.executeMultiShardCommand(commandObjects); assertEquals("OK", result, "executeMultiShardCommand should return OK when at least one shard " + "succeeds with ONE_SUCCEEDED policy"); } /** * This test verifies the bug: MGET multi-shard silently returns values in wrong order. The issue * is that mgetMultiShard groups keys by hash slot using a HashMap, which doesn't preserve * insertion order. The aggregated result concatenates per-slot lists in arbitrary order, breaking * MGET's contract that returned values correspond positionally to input keys. Callers using * index-based access (e.g., values.get(i) for keys[i]) will silently get wrong values when keys * span multiple slots. This test runs MGET with many different key combinations to find at least * one case where the HashMap iteration order differs from insertion order, proving the bug * exists. NOTE: This test FAILS when the bug exists, demonstrating the issue. When the bug is * fixed (e.g., by using LinkedHashMap to preserve insertion order), this test will pass. */ /** * This test verifies that MGET multi-shard returns values in the correct order matching the input * keys, even when keys span multiple hash slots. The fix groups consecutive keys with the same * slot together, but keeps non-consecutive keys with the same slot in separate commands to * preserve order. For example, keys mapping to slots [A, B, A] result in 3 separate commands, not * 2, ensuring the concatenated results match the input key order. */ @Test public void mgetMultiShardReturnsValuesInCorrectOrderWhenKeysSpanMultipleSlots() { ClusterConnectionProvider connectionHandler = mock(ClusterConnectionProvider.class); Connection connection = mock(Connection.class); when(connectionHandler.getConnection(ArgumentMatchers.any(CommandArguments.class))) .thenReturn(connection); ClusterCommandObjects commandObjects = new ClusterCommandObjects(); // Try many different key combinations including ones where keys hash to different slots for (int i = 0; i < 100; i++) { // Generate unique keys that are likely to hash to different slots String key1 = "testkey_a_" + i; String key2 = "testkey_b_" + i; String key3 = "testkey_c_" + i; int slot1 = JedisClusterCRC16.getSlot(key1); int slot2 = JedisClusterCRC16.getSlot(key2); int slot3 = JedisClusterCRC16.getSlot(key3); // Only test if all keys hash to different slots (the challenging case) if (slot1 == slot2 || slot2 == slot3 || slot1 == slot3) { continue; } // Use the standard MGET multi-shard API List>> mgetCommands = commandObjects.mgetMultiShard(key1, key2, key3); List inputOrder = java.util.Arrays.asList(key1, key2, key3); // Execute MGET with the standard executeMultiShardCommand ClusterCommandExecutor testMe = new ClusterCommandExecutor(connectionHandler, 10, Duration.ZERO, StaticCommandFlagsRegistry.registry()) { @Override @SuppressWarnings("unchecked") public T execute(Connection conn, CommandObject commandObject) { List values = new ArrayList<>(); CommandArguments args = commandObject.getArguments(); boolean isFirst = true; for (redis.clients.jedis.args.Rawable arg : args) { if (isFirst) { isFirst = false; continue; } String key = new String(arg.getRaw()); values.add("value_for_" + key); } return (T) values; } @Override protected void sleep(long ignored) { throw new RuntimeException("This test should never sleep"); } }; // Use the standard executeMultiShardCommand method List result = testMe.executeMultiShardCommand(mgetCommands); // Expected correct order based on input - MGET contract says values must match key positions List expectedCorrectOrder = java.util.Arrays.asList("value_for_" + key1, "value_for_" + key2, "value_for_" + key3); // Verify all values are present assertEquals(3, result.size(), "Should have 3 values"); assertTrue(result.contains("value_for_" + key1)); assertTrue(result.contains("value_for_" + key2)); assertTrue(result.contains("value_for_" + key3)); // THE KEY ASSERTION: Values must be in the same order as input keys // With the fix (grouping consecutive keys only), this should pass for all key combinations assertEquals(expectedCorrectOrder, result, "MGET multi-shard should return values in the same order as input keys. " + "Input keys: " + inputOrder + ", slots: [" + slot1 + ", " + slot2 + ", " + slot3 + "]"); } } /** * This test verifies that MGET multi-shard returns values in the correct order when keys have * interleaved hash slots (e.g., [slotA, slotB, slotA]). This is the critical case that the fix * addresses: without the fix, keys with the same slot would be grouped together, producing 2 * commands instead of 3, which would return values in wrong order. */ @Test public void mgetMultiShardReturnsValuesInCorrectOrderForInterleavedSlots() { ClusterConnectionProvider connectionHandler = mock(ClusterConnectionProvider.class); Connection connection = mock(Connection.class); when(connectionHandler.getConnection(ArgumentMatchers.any(CommandArguments.class))) .thenReturn(connection); String key1 = "key1"; String key2 = "key2"; String key3 = "key3"; // Mock JedisClusterCRC16.getSlot to return interleaved slots: [slotA, slotB, slotA] try (org.mockito.MockedStatic mockedStatic = Mockito .mockStatic(JedisClusterCRC16.class)) { mockedStatic.when(() -> JedisClusterCRC16.getSlot(key1)).thenReturn(100); mockedStatic.when(() -> JedisClusterCRC16.getSlot(key2)).thenReturn(200); mockedStatic.when(() -> JedisClusterCRC16.getSlot(key3)).thenReturn(100); ClusterCommandObjects commandObjects = new ClusterCommandObjects(); // The fix should create 3 separate commands (not 2) to preserve order List>> mgetCommands = commandObjects.mgetMultiShard(key1, key2, key3); assertEquals(3, mgetCommands.size(), "Should have 3 separate commands for interleaved slots [A, B, A], not 2"); // Execute MGET with the standard executeMultiShardCommand ClusterCommandExecutor testMe = new ClusterCommandExecutor(connectionHandler, 10, Duration.ZERO, StaticCommandFlagsRegistry.registry()) { @Override @SuppressWarnings("unchecked") public T execute(Connection conn, CommandObject commandObject) { List values = new ArrayList<>(); CommandArguments args = commandObject.getArguments(); boolean isFirst = true; for (redis.clients.jedis.args.Rawable arg : args) { if (isFirst) { isFirst = false; continue; } String key = new String(arg.getRaw()); values.add("value_for_" + key); } return (T) values; } @Override protected void sleep(long ignored) { throw new RuntimeException("This test should never sleep"); } }; // Use the standard executeMultiShardCommand method List result = testMe.executeMultiShardCommand(mgetCommands); // Expected correct order based on input List expectedCorrectOrder = java.util.Arrays.asList("value_for_" + key1, "value_for_" + key2, "value_for_" + key3); // THE KEY ASSERTION: Values must be in the same order as input keys assertEquals(expectedCorrectOrder, result, "MGET multi-shard should return values in the same order as input keys. " + "Input keys: [" + key1 + ", " + key2 + ", " + key3 + "], slots: [100, 200, 100]"); } } /** * This test verifies that when keys are sorted by hash slot (consecutive keys belong to the same * slot), they are combined into a single command for optimal batching. For example, keys mapping * to slots [A, A, B, B] should result in 2 commands (not 4), with keys grouped as: * command1=[key1, key2], command2=[key3, key4]. */ @Test public void mgetMultiShardCombinesConsecutiveKeysWithSameSlotIntoOneCommand() { ClusterConnectionProvider connectionHandler = mock(ClusterConnectionProvider.class); Connection connection = mock(Connection.class); when(connectionHandler.getConnection(ArgumentMatchers.any(CommandArguments.class))) .thenReturn(connection); String key1 = "key1"; String key2 = "key2"; String key3 = "key3"; String key4 = "key4"; // Mock JedisClusterCRC16.getSlot to return sorted/grouped slots: [A, A, B, B] try (org.mockito.MockedStatic mockedStatic = Mockito .mockStatic(JedisClusterCRC16.class)) { mockedStatic.when(() -> JedisClusterCRC16.getSlot(key1)).thenReturn(100); mockedStatic.when(() -> JedisClusterCRC16.getSlot(key2)).thenReturn(100); mockedStatic.when(() -> JedisClusterCRC16.getSlot(key3)).thenReturn(200); mockedStatic.when(() -> JedisClusterCRC16.getSlot(key4)).thenReturn(200); ClusterCommandObjects commandObjects = new ClusterCommandObjects(); // Consecutive keys with the same slot should be combined into one command List>> mgetCommands = commandObjects.mgetMultiShard(key1, key2, key3, key4); assertEquals(2, mgetCommands.size(), "Should have 2 commands for sorted slots [A, A, B, B], not 4"); // Execute MGET with the standard executeMultiShardCommand ClusterCommandExecutor testMe = new ClusterCommandExecutor(connectionHandler, 10, Duration.ZERO, StaticCommandFlagsRegistry.registry()) { @Override @SuppressWarnings("unchecked") public T execute(Connection conn, CommandObject commandObject) { List values = new ArrayList<>(); CommandArguments args = commandObject.getArguments(); boolean isFirst = true; for (redis.clients.jedis.args.Rawable arg : args) { if (isFirst) { isFirst = false; continue; } String key = new String(arg.getRaw()); values.add("value_for_" + key); } return (T) values; } @Override protected void sleep(long ignored) { throw new RuntimeException("This test should never sleep"); } }; // Use the standard executeMultiShardCommand method List result = testMe.executeMultiShardCommand(mgetCommands); // Expected correct order based on input List expectedCorrectOrder = java.util.Arrays.asList("value_for_" + key1, "value_for_" + key2, "value_for_" + key3, "value_for_" + key4); // Verify values are in the correct order assertEquals(expectedCorrectOrder, result, "MGET multi-shard should return values in the same order as input keys"); } } /** * This test verifies the fix for the race condition in RoundRobinConnectionResolver where the * round-robin counter could cause IndexOutOfBoundsException on topology change. *

* The bug occurred because: 1. Thread A with a 5-node list sets counter to 4 (valid for its list) * 2. Thread B with a 3-node list (after topology change) reads counter value 4 3. Thread B uses 4 * as index into a 3-element list -> IndexOutOfBoundsException *

* The fix applies modulo with the current list size after reading the counter, ensuring the index * is always valid for the current thread's node list. */ @Test public void runKeylessCommandDoesNotThrowIndexOutOfBoundsOnTopologyChange() { ClusterConnectionProvider connectionHandler = mock(ClusterConnectionProvider.class); // Create initial larger connection map (5 nodes) Map largeConnectionMap = new java.util.LinkedHashMap<>(); ConnectionPool pool1 = mock(ConnectionPool.class); ConnectionPool pool2 = mock(ConnectionPool.class); ConnectionPool pool3 = mock(ConnectionPool.class); ConnectionPool pool4 = mock(ConnectionPool.class); ConnectionPool pool5 = mock(ConnectionPool.class); Connection connection1 = mock(Connection.class); Connection connection2 = mock(Connection.class); Connection connection3 = mock(Connection.class); Connection connection4 = mock(Connection.class); Connection connection5 = mock(Connection.class); largeConnectionMap.put("node1:6379", pool1); largeConnectionMap.put("node2:6379", pool2); largeConnectionMap.put("node3:6379", pool3); largeConnectionMap.put("node4:6379", pool4); largeConnectionMap.put("node5:6379", pool5); when(pool1.getResource()).thenReturn(connection1); when(pool2.getResource()).thenReturn(connection2); when(pool3.getResource()).thenReturn(connection3); when(pool4.getResource()).thenReturn(connection4); when(pool5.getResource()).thenReturn(connection5); // Create smaller connection map (2 nodes) - simulates topology change Map smallConnectionMap = new java.util.LinkedHashMap<>(); smallConnectionMap.put("node1:6379", pool1); smallConnectionMap.put("node2:6379", pool2); // Start with large map, then switch to small map when(connectionHandler.getPrimaryNodesConnectionMap()).thenReturn(largeConnectionMap) .thenReturn(largeConnectionMap).thenReturn(largeConnectionMap) .thenReturn(largeConnectionMap) // After 4 calls, switch to smaller topology .thenReturn(smallConnectionMap).thenReturn(smallConnectionMap) .thenReturn(smallConnectionMap).thenReturn(smallConnectionMap) .thenReturn(smallConnectionMap); ClusterCommandExecutor testMe = new ClusterCommandExecutor(connectionHandler, 10, Duration.ZERO, StaticCommandFlagsRegistry.registry()) { @Override public T execute(Connection connection, CommandObject commandObject) { return (T) "OK"; } @Override protected void sleep(long ignored) { throw new RuntimeException("This test should never sleep"); } }; // Execute with large topology (5 nodes) - this advances the round-robin counter for (int i = 0; i < 4; i++) { String result = invokeExecuteKeylessCommand(testMe, KEYLESS_WRITE_COM_OBJECT); assertEquals("OK", result); } // At this point, the internal counter could be at value 4 (pointing to 5th node) // Now topology changes to only 2 nodes // Execute with small topology (2 nodes) - should NOT throw IndexOutOfBoundsException // This is the key assertion: the fix ensures modulo is applied with current list size for (int i = 0; i < 5; i++) { String result = invokeExecuteKeylessCommand(testMe, KEYLESS_WRITE_COM_OBJECT); assertEquals("OK", result, "Should succeed even when counter value exceeds new list size"); } // Verify both maps were used verify(connectionHandler, times(9)).getPrimaryNodesConnectionMap(); } /** * This test verifies the round-robin counter handles integer overflow gracefully. When the * counter reaches Integer.MAX_VALUE, the next increment wraps to Integer.MIN_VALUE. The fix uses * Math.abs to ensure the modulo result is always non-negative. */ @Test public void runKeylessCommandHandlesCounterOverflowGracefully() { ClusterConnectionProvider connectionHandler = mock(ClusterConnectionProvider.class); Map connectionMap = new java.util.LinkedHashMap<>(); ConnectionPool pool = mock(ConnectionPool.class); Connection connection = mock(Connection.class); connectionMap.put("node1:6379", pool); connectionMap.put("node2:6379", pool); connectionMap.put("node3:6379", pool); when(connectionHandler.getPrimaryNodesConnectionMap()).thenReturn(connectionMap); when(pool.getResource()).thenReturn(connection); ClusterCommandExecutor testMe = new ClusterCommandExecutor(connectionHandler, 10, Duration.ZERO, StaticCommandFlagsRegistry.registry()) { @Override public T execute(Connection connection, CommandObject commandObject) { return (T) "OK"; } @Override protected void sleep(long ignored) { throw new RuntimeException("This test should never sleep"); } }; // Get the roundRobinConnectionResolver field and set its counter to near MAX_VALUE ConnectionResolver roundRobinResolver = ReflectionTestUtil.getField(testMe, "roundRobinConnectionResolver"); java.util.concurrent.atomic.AtomicInteger counter = ReflectionTestUtil .getField(roundRobinResolver, "roundRobinCounter"); // Set counter to MAX_VALUE - 1, so next calls will overflow counter.set(Integer.MAX_VALUE - 1); // Execute several commands - should NOT throw any exception even when counter overflows for (int i = 0; i < 5; i++) { String result = invokeExecuteKeylessCommand(testMe, KEYLESS_WRITE_COM_OBJECT); assertEquals("OK", result, "Should handle counter overflow gracefully"); } } } ================================================ FILE: src/test/java/redis/clients/jedis/executors/RetryableCommandExecutorTest.java ================================================ package redis.clients.jedis.executors; import static org.junit.jupiter.api.Assertions.*; import static org.mockito.Mockito.*; import java.time.Duration; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; import org.mockito.Mock; import org.mockito.junit.jupiter.MockitoExtension; import redis.clients.jedis.CommandObject; import redis.clients.jedis.Connection; import redis.clients.jedis.exceptions.JedisConnectionException; import redis.clients.jedis.exceptions.JedisException; import redis.clients.jedis.providers.ConnectionProvider; @ExtendWith(MockitoExtension.class) public class RetryableCommandExecutorTest { @Mock private ConnectionProvider mockProvider; @Mock private Connection mockConnection; @Mock private CommandObject mockCommandObject; @Test public void testConstructorWithNullProvider() { IllegalArgumentException exception = assertThrows( IllegalArgumentException.class, () -> new RetryableCommandExecutor(null, 3, Duration.ofSeconds(1)), "Should throw IllegalArgumentException when provider is null" ); assertTrue(exception.getMessage().contains("provider"), "Exception message should mention 'provider'"); } @Test public void testConstructorWithInvalidMaxAttempts() { // Test with zero IllegalArgumentException exceptionZero = assertThrows( IllegalArgumentException.class, () -> new RetryableCommandExecutor(mockProvider, 0, Duration.ofSeconds(1)), "Should throw IllegalArgumentException when maxAttempts is zero" ); assertTrue(exceptionZero.getMessage().contains("maxAttempts"), "Exception message should mention 'maxAttempts'"); // Test with negative value IllegalArgumentException exceptionNegative = assertThrows( IllegalArgumentException.class, () -> new RetryableCommandExecutor(mockProvider, -1, Duration.ofSeconds(1)), "Should throw IllegalArgumentException when maxAttempts is negative" ); assertTrue(exceptionNegative.getMessage().contains("maxAttempts"), "Exception message should mention 'maxAttempts'"); } @Test public void testValidConstruction() { // Should not throw any exceptions assertDoesNotThrow(() -> new RetryableCommandExecutor(mockProvider, 1, Duration.ofSeconds(1))); assertDoesNotThrow(() -> new RetryableCommandExecutor(mockProvider, 3, Duration.ZERO)); assertDoesNotThrow(() -> new RetryableCommandExecutor(mockProvider, 10, Duration.ofMinutes(5))); } @Test public void testMaxAttemptsIsRespected() throws Exception { // Set up the mock to return a connection but throw an exception when executing when(mockProvider.getConnection(any())).thenReturn(mockConnection); when(mockConnection.executeCommand(any(CommandObject.class))).thenThrow(new JedisConnectionException("Connection failed")); // Create the executor with exactly 3 attempts final int maxAttempts = 3; RetryableCommandExecutor executor = spy(new RetryableCommandExecutor(mockProvider, maxAttempts, Duration.ofSeconds(10))); // Mock the sleep method to avoid actual sleeping doNothing().when(executor).sleep(anyLong()); // Execute the command and expect an exception assertThrows(JedisException.class, () -> executor.executeCommand(mockCommandObject)); // Verify that we tried exactly maxAttempts times verify(mockProvider, times(maxAttempts)).getConnection(any()); verify(mockConnection, times(maxAttempts)).close(); } @Test public void testExecuteCommandWithNoRetries() throws Exception { // Set up the mock to return a connection and have it execute the command successfully when(mockProvider.getConnection(any())).thenReturn(mockConnection); when(mockConnection.executeCommand(mockCommandObject)).thenReturn("success"); // Create the executor with just 1 attempt (no retries) RetryableCommandExecutor executor = new RetryableCommandExecutor(mockProvider, 1, Duration.ofSeconds(1)); // Execute the command String result = executor.executeCommand(mockCommandObject); // Verify the result and that the connection was closed assertEquals("success", result); verify(mockConnection, times(1)).close(); verify(mockProvider, times(1)).getConnection(any()); } @Test public void testMaxAttemptsExceeded() throws Exception { // Set up the mock to return a connection but throw an exception when executing when(mockProvider.getConnection(any())).thenReturn(mockConnection); when(mockConnection.executeCommand(any(CommandObject.class))).thenThrow(new JedisConnectionException("Connection failed")); // Create the executor with 3 attempts RetryableCommandExecutor executor = spy(new RetryableCommandExecutor(mockProvider, 3, Duration.ofSeconds(1))); // Mock the sleep method to avoid actual sleeping doNothing().when(executor).sleep(anyLong()); // Execute the command and expect an exception JedisException exception = assertThrows( JedisException.class, () -> executor.executeCommand(mockCommandObject), "Should throw JedisException when max attempts are exceeded" ); // Verify the exception and that we tried the correct number of times assertEquals("No more attempts left.", exception.getMessage()); assertEquals(1, exception.getSuppressed().length); verify(mockProvider, times(3)).getConnection(any()); verify(mockConnection, times(3)).close(); } } ================================================ FILE: src/test/java/redis/clients/jedis/executors/aggregators/ClusterReplyAggregatorTest.java ================================================ package redis.clients.jedis.executors.aggregators; import org.junit.jupiter.api.Nested; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.TestInstance; import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.MethodSource; import redis.clients.jedis.CommandFlagsRegistry; import redis.clients.jedis.exceptions.UnsupportedAggregationException; import redis.clients.jedis.util.ByteArrayMapMatcher; import redis.clients.jedis.util.JedisByteHashMap; import redis.clients.jedis.util.JedisByteMap; import redis.clients.jedis.util.JedisByteMapMatcher; import redis.clients.jedis.util.KeyValue; import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; import java.util.HashMap; import java.util.HashSet; import java.util.LinkedHashMap; import java.util.LinkedHashSet; import java.util.LinkedList; import java.util.List; import java.util.Map; import java.util.Set; import java.util.stream.Stream; import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.arrayContainingInAnyOrder; import static org.hamcrest.Matchers.contains; import static org.hamcrest.Matchers.containsInAnyOrder; import static org.hamcrest.Matchers.equalTo; import static org.hamcrest.Matchers.instanceOf; import static org.hamcrest.Matchers.sameInstance; import static org.junit.jupiter.api.Assertions.assertArrayEquals; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertNotNull; import static org.junit.jupiter.api.Assertions.assertNull; import static org.junit.jupiter.api.Assertions.assertSame; import static org.junit.jupiter.api.Assertions.assertThrows; import static org.junit.jupiter.api.Assertions.assertTrue; public class ClusterReplyAggregatorTest { // ==================== aggregateAllSucceeded Tests ==================== // Per Redis ALL_SUCCEEDED spec: returns successfully only if there are no error replies. // Error handling is done separately by the caller (MultiNodeResultAggregator.addError()), // so aggregateAllSucceeded simply returns the first reply when aggregating successful responses. @Nested @TestInstance(TestInstance.Lifecycle.PER_CLASS) class AggregateAllSucceededTests { /** * Provides test cases for ALL_SUCCEEDED aggregator. Each Object[] contains {firstValue, * secondValue, thirdValue}. Includes Long, Integer, Double, String, Boolean. */ Stream valuesProvider() { return Stream.of(new Object[] { 42L, 42L, 100L }, // Long new Object[] { 123, 123, 456 }, // Integer new Object[] { 3.14159, 3.14159, 2.71828 }, // Double new Object[] { "OK", "OK", "DIFFERENT" }, // String new Object[] { true, true, false }, // Boolean new Object[] { false, false, true }, // Boolean new Object[] { new byte[] { 1, 2 }, new byte[] { 1, 2 }, new byte[] { 3, 4 } } // byte[] ); } @ParameterizedTest @MethodSource("valuesProvider") void testAggregateAllSucceeded_returnsFirstValue(Object first, Object second, Object third) { @SuppressWarnings("unchecked") ClusterReplyAggregator aggregator = new ClusterReplyAggregator<>( CommandFlagsRegistry.ResponsePolicy.ALL_SUCCEEDED); // first addition aggregator.add(first); assertThat("First value should be result", aggregator.getResult(), equalTo(first)); // add same value again aggregator.add(second); assertThat("Result should remain first value", aggregator.getResult(), equalTo(first)); // add a different value aggregator.add(third); assertThat("Result should still remain first value", aggregator.getResult(), equalTo(first)); } } // ==================== aggregateDefault - List Tests ==================== @Nested @TestInstance(TestInstance.Lifecycle.PER_CLASS) public class AggregateDefaultTests { // ==================== aggregateDefault - Unsupported Types Throw Exception // ==================== @Test public void testAggregateDefault_nonListTypes_throwsUnsupportedAggregationException() { String first = "existing"; ClusterReplyAggregator aggregator = new ClusterReplyAggregator<>( CommandFlagsRegistry.ResponsePolicy.DEFAULT); UnsupportedAggregationException exception = assertThrows( UnsupportedAggregationException.class, () -> aggregator.add(first)); assertTrue(exception.getMessage().contains("DEFAULT policy requires"), "Exception message should describe the policy requirement"); assertTrue(exception.getMessage().contains("String"), "Exception message should mention the unsupported type"); } @Nested @TestInstance(TestInstance.Lifecycle.PER_CLASS) class AggregateDefaultListTests { /** * Provides test cases: {firstList, secondList, expectedResult}. */ Stream listProvider() { return Stream.of( // aggregate non empty lists new Object[] { Arrays.asList("key1", "key2"), Arrays.asList("key3", "key4"), Arrays.asList("key1", "key2", "key3", "key4") }, // aggregate null and non empty list new Object[] { null, Arrays.asList("key1", "key2"), Arrays.asList("key1", "key2") }, // aggregate empty and non empty list new Object[] { Collections.emptyList(), Arrays.asList("key1", "key2"), Arrays.asList("key1", "key2") }, // aggregate empty and non empty list new Object[] { new ArrayList<>(), Arrays.asList("key1", "key2"), Arrays.asList("key1", "key2") }, // aggregate non empty and empty list new Object[] { Arrays.asList("key1", "key2"), new ArrayList<>(), Arrays.asList("key1", "key2") }, // aggregate two empty lists new Object[] { new ArrayList<>(), new ArrayList<>(), new ArrayList<>() }, // both empty → // empty // aggregate two null lists new Object[] { null, null, null }); } @ParameterizedTest @MethodSource("listProvider") void testAggregateDefault_lists(List first, List second, List expected) { ClusterReplyAggregator> aggregator = new ClusterReplyAggregator<>( CommandFlagsRegistry.ResponsePolicy.DEFAULT); aggregator.add(first); aggregator.add(second); List result = aggregator.getResult(); assertThat("Aggregated list should match expected", result, equalTo(expected)); } // ==================== aggregateDefault - List Tests ==================== @Test public void testAggregateDefault_twoByteArrayLists_concatenatesThem() { List first = new ArrayList<>( Arrays.asList(new byte[] { 1, 2 }, new byte[] { 3, 4 })); List second = new ArrayList<>( Arrays.asList(new byte[] { 5, 6 }, new byte[] { 7, 8 })); ClusterReplyAggregator> aggregator = new ClusterReplyAggregator<>( CommandFlagsRegistry.ResponsePolicy.DEFAULT); aggregator.add(first); aggregator.add(second); List result = aggregator.getResult(); assertEquals(4, result.size(), "Should contain all byte arrays from both lists"); assertThat(result, contains(new byte[] { 1, 2 }, new byte[] { 3, 4 }, new byte[] { 5, 6 }, new byte[] { 7, 8 })); } // ==================== aggregateDefault - Different List Implementations ==================== @Test public void testAggregateDefault_linkedListAndArrayList() { List first = new LinkedList<>(Arrays.asList("a", "b")); List second = new ArrayList<>(Arrays.asList("c", "d")); ClusterReplyAggregator> aggregator = new ClusterReplyAggregator<>( CommandFlagsRegistry.ResponsePolicy.DEFAULT); aggregator.add(first); aggregator.add(second); List result = aggregator.getResult(); assertEquals(4, result.size(), "Should concatenate different list implementations"); assertEquals(Arrays.asList("a", "b", "c", "d"), result); } // ==================== aggregateDefault - Mutates Existing ArrayList In Place // ==================== @Test public void testAggregateDefault_singleReplyDoesNotCreateNewList() { List first = null; List second = new ArrayList<>(Arrays.asList("c", "d")); ClusterReplyAggregator> aggregator = new ClusterReplyAggregator<>( CommandFlagsRegistry.ResponsePolicy.DEFAULT); aggregator.add(first); aggregator.add(second); List result = aggregator.getResult(); // If single non null reply, Result should be the same instance assertSame(second, result, "Result should be the same instance as first non null reply"); assertThat(result, contains("c", "d")); assertThat(result, sameInstance(second)); } } // ==================== aggregateDefault - Map Tests ==================== @Nested @TestInstance(TestInstance.Lifecycle.PER_CLASS) class AggregateDefaultMapTests { /** * Provides test cases: {firstMap, secondMap, expectedResult}. */ Stream mapProvider() { Map firstMap = new HashMap<>(); firstMap.put("key1", 1); firstMap.put("key2", 2); Map secondMap = new HashMap<>(); secondMap.put("key3", 3); secondMap.put("key4", 4); Map expectedFirstOnly = new HashMap<>(); expectedFirstOnly.put("key1", 1); expectedFirstOnly.put("key2", 2); Map expectedMergedMap = new HashMap<>(); expectedMergedMap.put("key1", 1); expectedMergedMap.put("key2", 2); expectedMergedMap.put("key3", 3); expectedMergedMap.put("key4", 4); Map overlappingMap = new HashMap<>(); overlappingMap.put("key1", 1); overlappingMap.put("key3", 3); Map expectedOverlappingMap = new HashMap<>(); expectedOverlappingMap.put("key1", 1); expectedOverlappingMap.put("key2", 2); expectedOverlappingMap.put("key3", 3); return Stream.of( // empty + non-empty → non-empty new Object[] { new HashMap(), firstMap, expectedFirstOnly }, // non-empty + empty → non-empty new Object[] { firstMap, new HashMap(), expectedFirstOnly }, // empty + empty → empty new Object[] { new HashMap(), new HashMap(), new HashMap() }, // null + null → null new Object[] { null, null, null }, // null + empty → empty new Object[] { null, new HashMap(), new HashMap() }, // unmodifiableMap + non-empty → non-empty new Object[] { Collections.emptyMap(), firstMap, expectedFirstOnly }, // non-empty + unmodifiableMap → non-empty new Object[] { firstMap, Collections.emptyMap(), expectedFirstOnly }, // maps with different keys new Object[] { firstMap, secondMap, expectedMergedMap }, // maps with overlapping keys, second map takes precedence new Object[] { firstMap, overlappingMap, expectedOverlappingMap } ); } @ParameterizedTest @MethodSource("mapProvider") void testAggregateDefault_maps(Map first, Map second, Map expected) { ClusterReplyAggregator> aggregator = new ClusterReplyAggregator<>( CommandFlagsRegistry.ResponsePolicy.DEFAULT); aggregator.add(first); aggregator.add(second); Map result = aggregator.getResult(); assertThat("Aggregated map should match expected", result, equalTo(expected)); } @Test public void testAggregateDefault_differentMapImplementations_mergesThem() { Map first = new LinkedHashMap<>(); first.put("a", "1"); first.put("b", "2"); Map second = new HashMap<>(); second.put("c", "3"); second.put("d", "4"); ClusterReplyAggregator> aggregator = new ClusterReplyAggregator<>( CommandFlagsRegistry.ResponsePolicy.DEFAULT); aggregator.add(first); aggregator.add(second); Map result = aggregator.getResult(); assertEquals(4, result.size(), "Should merge different map implementations"); assertEquals("1", result.get("a")); assertEquals("2", result.get("b")); assertEquals("3", result.get("c")); assertEquals("4", result.get("d")); assertTrue(result instanceof HashMap, "Result should be a HashMap"); } } // ==================== aggregateDefault - Set Tests ==================== @Nested @TestInstance(TestInstance.Lifecycle.PER_CLASS) class AggregateDefaultSetTests { /** * Provides test cases: {firstSet, secondSet, expectedResult}. */ Stream setProvider() { Set nonEmptySet = new HashSet<>(Arrays.asList("a", "b")); Set expectedSet = new HashSet<>(Arrays.asList("a", "b")); return Stream.of( // empty + non-empty → non-empty new Object[] { new HashSet(), nonEmptySet, expectedSet }, // non-empty + empty → non-empty new Object[] { nonEmptySet, new HashSet(), expectedSet }, // empty + empty → empty new Object[] { new HashSet(), new HashSet(), new HashSet() }, // unmodifiableSet + non-empty → non-empty new Object[] { Collections.emptySet(), nonEmptySet, expectedSet }, // non-empty + unmodifiableSet → non-empty new Object[] { nonEmptySet, Collections.emptySet(), expectedSet }, // sets with overlapping elements, merges without duplicates new Object[] { new HashSet(Arrays.asList("a", "b", "c")), new HashSet(Arrays.asList("b", "c", "d")), new HashSet(Arrays.asList("a", "b", "c", "d")) }, // sets with different elements, merges all elements new Object[] { new HashSet(Arrays.asList("a", "b")), new HashSet(Arrays.asList("c", "d")), new HashSet(Arrays.asList("a", "b", "c", "d")) }, // different set implementations, merges all elements new Object[] { new LinkedHashSet(Arrays.asList("a", "b")), new HashSet(Arrays.asList("c", "d")), new HashSet(Arrays.asList("a", "b", "c", "d")) }); } @ParameterizedTest @MethodSource("setProvider") void testAggregateDefault_sets(Set first, Set second, Set expected) { ClusterReplyAggregator> aggregator = new ClusterReplyAggregator<>( CommandFlagsRegistry.ResponsePolicy.DEFAULT); aggregator.add(first); aggregator.add(second); Set result = aggregator.getResult(); assertThat("Aggregated set should match expected", result, instanceOf(HashSet.class)); assertThat("Aggregated set should match expected", result, containsInAnyOrder(expected.toArray(new String[0]))); } /** * Provides test cases: {firstSet, secondSet, expectedResult}. */ Stream setByteArrayProvider() { Set nonEmptySet1 = new HashSet<>(Arrays.asList("a".getBytes(), "b".getBytes())); Set nonEmptySet2 = new HashSet<>(Arrays.asList("c".getBytes(), "d".getBytes())); Set expectedSet = new HashSet<>( Arrays.asList("a".getBytes(), "b".getBytes(), "c".getBytes(), "d".getBytes())); return Stream.of( // set of byte arrays new Object[] { nonEmptySet1, nonEmptySet2, expectedSet }, // overlapping elements new Object[] { new HashSet<>(Arrays.asList("a".getBytes(), "b".getBytes())), new HashSet<>(Arrays.asList("b".getBytes(), "c".getBytes())), new HashSet<>(Arrays.asList("a".getBytes(), "b".getBytes(), "c".getBytes())) }, // empty + non-empty → non-empty new Object[] { new HashSet(), nonEmptySet1, nonEmptySet1 }, // non-empty + empty → non-empty new Object[] { nonEmptySet1, new HashSet(), nonEmptySet1 }); } @ParameterizedTest @MethodSource("setByteArrayProvider") void testAggregateDefault_sets_byteArrays(Set first, Set second, Set expected) { ClusterReplyAggregator> aggregator = new ClusterReplyAggregator<>( CommandFlagsRegistry.ResponsePolicy.DEFAULT); aggregator.add(first); aggregator.add(second); Set result = aggregator.getResult(); assertThat("Aggregated set should match expected", result, instanceOf(HashSet.class)); assertThat(result.toArray(new byte[0][]), arrayContainingInAnyOrder(expected.toArray(new byte[0][]))); } @Test public void testAggregateDefault_singleSet_returnsSameInstance() { Set first = null; Set second = new HashSet<>(Arrays.asList("c", "d")); ClusterReplyAggregator> aggregator = new ClusterReplyAggregator<>( CommandFlagsRegistry.ResponsePolicy.DEFAULT); aggregator.add(first); aggregator.add(second); Set result = aggregator.getResult(); // ClusterReplyAggregator mutates the first set in place assertThat(result, sameInstance(second)); assertThat(result, contains("c", "d")); } } // ==================== aggregateDefault - JedisByteHashMap Tests ==================== @Nested @TestInstance(TestInstance.Lifecycle.PER_CLASS) class AggregateDefaultJedisByteHashMapTests { /** * Provides test cases: {firstMap, secondMap, expectedResult}. */ Stream jedisByteHashMapProvider() { JedisByteHashMap first = new JedisByteHashMap(); first.put(new byte[] { 'k', '1' }, new byte[] { 'v', '1' }); first.put(new byte[] { 'k', '2' }, new byte[] { 'v', '2' }); JedisByteHashMap second = new JedisByteHashMap(); second.put(new byte[] { 'k', '3' }, new byte[] { 'v', '3' }); second.put(new byte[] { 'k', '4' }, new byte[] { 'v', '4' }); JedisByteHashMap expectedFirstOnly = new JedisByteHashMap(); expectedFirstOnly.put(new byte[] { 'k', '1' }, new byte[] { 'v', '1' }); expectedFirstOnly.put(new byte[] { 'k', '2' }, new byte[] { 'v', '2' }); JedisByteHashMap expectedFirstSecondMerged = new JedisByteHashMap(); expectedFirstSecondMerged.put(new byte[] { 'k', '1' }, new byte[] { 'v', '1' }); expectedFirstSecondMerged.put(new byte[] { 'k', '2' }, new byte[] { 'v', '2' }); expectedFirstSecondMerged.put(new byte[] { 'k', '3' }, new byte[] { 'v', '3' }); expectedFirstSecondMerged.put(new byte[] { 'k', '4' }, new byte[] { 'v', '4' }); JedisByteHashMap overlapFirstKeys = new JedisByteHashMap(); overlapFirstKeys.put(new byte[] { 'k', '1' }, new byte[] { 'v', 'A' }); overlapFirstKeys.put(new byte[] { 'k', '2' }, new byte[] { 'v', '2' }); overlapFirstKeys.put(new byte[] { 'k', '3' }, new byte[] { 'v', '3' }); JedisByteHashMap overlapKeysMerged = new JedisByteHashMap(); overlapKeysMerged.put(new byte[] { 'k', '1' }, new byte[] { 'v', 'A' }); overlapKeysMerged.put(new byte[] { 'k', '2' }, new byte[] { 'v', '2' }); overlapKeysMerged.put(new byte[] { 'k', '3' }, new byte[] { 'v', '3' }); return Stream.of( // empty + non-empty → non-empty new Object[] { new JedisByteHashMap(), first, expectedFirstOnly }, // non-empty + empty → non-empty new Object[] { first, new JedisByteHashMap(), expectedFirstOnly }, // empty + empty → empty new Object[] { new JedisByteHashMap(), new JedisByteHashMap(), new JedisByteHashMap() }, // null + null → null new Object[] { null, null, null }, // null + empty → empty new Object[] { null, new JedisByteHashMap(), new JedisByteHashMap() }, // maps with no overlapping keys new Object[] { first, second, expectedFirstSecondMerged }, // maps with overlapping keys, second map takes precedence new Object[] { first, overlapFirstKeys, overlapKeysMerged }); } @ParameterizedTest @MethodSource("jedisByteHashMapProvider") void testAggregateDefault_jedisByteHashMap(JedisByteHashMap first, JedisByteHashMap second, JedisByteHashMap expected) { ClusterReplyAggregator> aggregator = new ClusterReplyAggregator<>( CommandFlagsRegistry.ResponsePolicy.DEFAULT); aggregator.add(first); aggregator.add(second); Map result = aggregator.getResult(); if (expected == null) { assertNull(result); } else { assertThat(result, instanceOf(JedisByteHashMap.class)); assertThat(result, ByteArrayMapMatcher.contentEquals(expected)); } } @Test public void testAggregateDefault_twoJedisByteHashMapsWithOverlappingKeys_secondMapTakesPrecedence() { JedisByteHashMap first = new JedisByteHashMap(); first.put(new byte[] { 's', 'h', 'a', 'r', 'e', 'd' }, new byte[] { 'f', 'i', 'r', 's', 't' }); first.put(new byte[] { 'u', 'n', 'i', 'q', '1' }, new byte[] { 'v', 'a', 'l', '1' }); JedisByteHashMap second = new JedisByteHashMap(); second.put(new byte[] { 's', 'h', 'a', 'r', 'e', 'd' }, new byte[] { 's', 'e', 'c', 'o', 'n', 'd' }); second.put(new byte[] { 'u', 'n', 'i', 'q', '2' }, new byte[] { 'v', 'a', 'l', '2' }); ClusterReplyAggregator aggregator = new ClusterReplyAggregator<>( CommandFlagsRegistry.ResponsePolicy.DEFAULT); aggregator.add(first); aggregator.add(second); JedisByteHashMap result = aggregator.getResult(); assertEquals(3, result.size(), "Should contain merged entries"); assertArrayEquals(new byte[] { 's', 'e', 'c', 'o', 'n', 'd' }, result.get(new byte[] { 's', 'h', 'a', 'r', 'e', 'd' }), "Second map's value should overwrite first"); assertArrayEquals(new byte[] { 'v', 'a', 'l', '1' }, result.get(new byte[] { 'u', 'n', 'i', 'q', '1' })); assertArrayEquals(new byte[] { 'v', 'a', 'l', '2' }, result.get(new byte[] { 'u', 'n', 'i', 'q', '2' })); } } // ==================== aggregateDefault - JedisByteMap Tests ==================== @Nested @TestInstance(TestInstance.Lifecycle.PER_CLASS) class AggregateDefaultJedisByteMapTests { /** * Provides test cases: {firstMap, secondMap, expectedResult}. */ Stream jedisByteMapProvider() { JedisByteMap first = new JedisByteMap<>(); first.put(new byte[] { 'k', '1' }, "v1"); first.put(new byte[] { 'k', '2' }, "v2"); JedisByteMap second = new JedisByteMap<>(); second.put(new byte[] { 'k', '3' }, "v3"); second.put(new byte[] { 'k', '4' }, "v4"); JedisByteMap expectedFirstOnly = new JedisByteMap<>(); expectedFirstOnly.put(new byte[] { 'k', '1' }, "v1"); expectedFirstOnly.put(new byte[] { 'k', '2' }, "v2"); JedisByteMap expectedFirstSecondMerged = new JedisByteMap<>(); expectedFirstSecondMerged.put(new byte[] { 'k', '1' }, "v1"); expectedFirstSecondMerged.put(new byte[] { 'k', '2' }, "v2"); expectedFirstSecondMerged.put(new byte[] { 'k', '3' }, "v3"); expectedFirstSecondMerged.put(new byte[] { 'k', '4' }, "v4"); JedisByteMap overlapFirstKeys = new JedisByteMap<>(); overlapFirstKeys.put(new byte[] { 'k', '1' }, "vA"); overlapFirstKeys.put(new byte[] { 'k', '2' }, "v2"); overlapFirstKeys.put(new byte[] { 'k', '3' }, "v3"); JedisByteMap overlapKeysMerged = new JedisByteMap<>(); overlapKeysMerged.put(new byte[] { 'k', '1' }, "vA"); overlapKeysMerged.put(new byte[] { 'k', '2' }, "v2"); overlapKeysMerged.put(new byte[] { 'k', '3' }, "v3"); return Stream.of( // empty + non-empty → non-empty new Object[] { new JedisByteMap<>(), first, expectedFirstOnly }, // non-empty + empty → non-empty new Object[] { first, new JedisByteMap<>(), expectedFirstOnly }, // empty + empty → empty new Object[] { new JedisByteMap<>(), new JedisByteMap<>(), new JedisByteMap<>() }, // null + null → null new Object[] { null, null, null }, // null + empty → empty new Object[] { null, new JedisByteMap<>(), new JedisByteMap<>() }, // maps with no overlapping keys new Object[] { first, second, expectedFirstSecondMerged }, // maps with overlapping keys, second map takes precedence new Object[] { first, overlapFirstKeys, overlapKeysMerged }); } @ParameterizedTest @MethodSource("jedisByteMapProvider") void testAggregateDefault_jedisByteHashMap(Map first, Map second, Map expected) { ClusterReplyAggregator> aggregator = new ClusterReplyAggregator<>( CommandFlagsRegistry.ResponsePolicy.DEFAULT); aggregator.add(first); aggregator.add(second); Map result = aggregator.getResult(); if (expected == null) { assertNull(result); } else { assertThat(result, instanceOf(JedisByteMap.class)); assertThat(result, JedisByteMapMatcher.contentEquals(expected)); } } } } // ==================== aggregateMin - KeyValue Tests ==================== @Nested @TestInstance(TestInstance.Lifecycle.PER_CLASS) class AggregateMinTests { @Test public void testAggregateMin_keyValueLongLong_returnsMinOfEachComponent() { KeyValue first = KeyValue.of(10L, 20L); KeyValue second = KeyValue.of(5L, 25L); ClusterReplyAggregator> aggregator = new ClusterReplyAggregator<>( CommandFlagsRegistry.ResponsePolicy.AGG_MIN); aggregator.add(first); aggregator.add(second); KeyValue result = aggregator.getResult(); assertEquals(5L, result.getKey(), "Should return minimum key"); assertEquals(20L, result.getValue(), "Should return minimum value"); } @Test public void testAggregateMin_keyValueLongLong_firstSmaller() { KeyValue first = KeyValue.of(1L, 2L); KeyValue second = KeyValue.of(10L, 20L); ClusterReplyAggregator> aggregator = new ClusterReplyAggregator<>( CommandFlagsRegistry.ResponsePolicy.AGG_MIN); aggregator.add(first); aggregator.add(second); KeyValue result = aggregator.getResult(); assertEquals(1L, result.getKey(), "Should return minimum key from first"); assertEquals(2L, result.getValue(), "Should return minimum value from first"); } @Test public void testAggregateMin_keyValueLongLong_secondSmaller() { KeyValue first = KeyValue.of(10L, 20L); KeyValue second = KeyValue.of(1L, 2L); ClusterReplyAggregator> aggregator = new ClusterReplyAggregator<>( CommandFlagsRegistry.ResponsePolicy.AGG_MIN); aggregator.add(first); aggregator.add(second); KeyValue result = aggregator.getResult(); assertEquals(1L, result.getKey(), "Should return minimum key from second"); assertEquals(2L, result.getValue(), "Should return minimum value from second"); } @Test public void testAggregateMin_keyValueLongLong_equalValues() { KeyValue first = KeyValue.of(5L, 5L); KeyValue second = KeyValue.of(5L, 5L); ClusterReplyAggregator> aggregator = new ClusterReplyAggregator<>( CommandFlagsRegistry.ResponsePolicy.AGG_MIN); aggregator.add(first); aggregator.add(second); KeyValue result = aggregator.getResult(); assertEquals(5L, result.getKey(), "Should return equal key"); assertEquals(5L, result.getValue(), "Should return equal value"); } @Test public void testAggregateMin_keyValueStringString_returnsMinOfEachComponent() { KeyValue first = KeyValue.of("b", "y"); KeyValue second = KeyValue.of("a", "z"); ClusterReplyAggregator> aggregator = new ClusterReplyAggregator<>( CommandFlagsRegistry.ResponsePolicy.AGG_MIN); aggregator.add(first); aggregator.add(second); KeyValue result = aggregator.getResult(); assertEquals("a", result.getKey(), "Should return minimum key"); assertEquals("y", result.getValue(), "Should return minimum value"); } } // ==================== aggregateMax - KeyValue Tests ==================== @Nested @TestInstance(TestInstance.Lifecycle.PER_CLASS) class AggregateMaxTests { @Test public void testAggregateMax_keyValueLongLong_returnsMaxOfEachComponent() { KeyValue first = KeyValue.of(10L, 20L); KeyValue second = KeyValue.of(5L, 25L); ClusterReplyAggregator> aggregator = new ClusterReplyAggregator<>( CommandFlagsRegistry.ResponsePolicy.AGG_MAX); aggregator.add(first); aggregator.add(second); KeyValue result = aggregator.getResult(); assertEquals(10L, result.getKey(), "Should return maximum key"); assertEquals(25L, result.getValue(), "Should return maximum value"); } @Test public void testAggregateMax_keyValueLongLong_firstLarger() { KeyValue first = KeyValue.of(10L, 20L); KeyValue second = KeyValue.of(1L, 2L); ClusterReplyAggregator> aggregator = new ClusterReplyAggregator<>( CommandFlagsRegistry.ResponsePolicy.AGG_MAX); aggregator.add(first); aggregator.add(second); KeyValue result = aggregator.getResult(); assertEquals(10L, result.getKey(), "Should return maximum key from first"); assertEquals(20L, result.getValue(), "Should return maximum value from first"); } @Test public void testAggregateMax_keyValueLongLong_secondLarger() { KeyValue first = KeyValue.of(1L, 2L); KeyValue second = KeyValue.of(10L, 20L); ClusterReplyAggregator> aggregator = new ClusterReplyAggregator<>( CommandFlagsRegistry.ResponsePolicy.AGG_MAX); aggregator.add(first); aggregator.add(second); KeyValue result = aggregator.getResult(); assertEquals(10L, result.getKey(), "Should return maximum key from second"); assertEquals(20L, result.getValue(), "Should return maximum value from second"); } @Test public void testAggregateMax_keyValueLongLong_equalValues() { KeyValue first = KeyValue.of(5L, 5L); KeyValue second = KeyValue.of(5L, 5L); ClusterReplyAggregator> aggregator = new ClusterReplyAggregator<>( CommandFlagsRegistry.ResponsePolicy.AGG_MAX); aggregator.add(first); aggregator.add(second); KeyValue result = aggregator.getResult(); assertEquals(5L, result.getKey(), "Should return equal key"); assertEquals(5L, result.getValue(), "Should return equal value"); } @Test public void testAggregateMax_keyValueStringString_returnsMaxOfEachComponent() { KeyValue first = KeyValue.of("b", "y"); KeyValue second = KeyValue.of("a", "z"); ClusterReplyAggregator> aggregator = new ClusterReplyAggregator<>( CommandFlagsRegistry.ResponsePolicy.AGG_MAX); aggregator.add(first); aggregator.add(second); KeyValue result = aggregator.getResult(); assertEquals("b", result.getKey(), "Should return maximum key"); assertEquals("z", result.getValue(), "Should return maximum value"); } } } ================================================ FILE: src/test/java/redis/clients/jedis/executors/aggregators/MultiNodeResultAggregatorTest.java ================================================ package redis.clients.jedis.executors.aggregators; import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.contains; import static org.junit.jupiter.api.Assertions.*; import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; import java.util.List; import java.util.Map; import org.junit.jupiter.api.Nested; import org.junit.jupiter.api.Test; import redis.clients.jedis.CommandFlagsRegistry.ResponsePolicy; import redis.clients.jedis.HostAndPort; import redis.clients.jedis.exceptions.JedisBroadcastException; import redis.clients.jedis.exceptions.JedisClusterOperationException; public class MultiNodeResultAggregatorTest { private static final HostAndPort NODE_1 = HostAndPort.from("127.0.0.1:7001"); private static final HostAndPort NODE_2 = HostAndPort.from("127.0.0.1:7002"); private static final HostAndPort NODE_3 = HostAndPort.from("127.0.0.1:7003"); private static final HostAndPort UNKNOWN_NODE = HostAndPort.from("unknown:0"); // ==================== Constructor Tests ==================== @Test public void testConstructor_initializesWithResponsePolicy() { MultiNodeResultAggregator aggregator = new MultiNodeResultAggregator<>( ResponsePolicy.ALL_SUCCEEDED); assertEquals(ResponsePolicy.ALL_SUCCEEDED, aggregator.getResponsePolicy(), "Should store the provided response policy"); } @Test public void testConstructor_initializesWithDifferentPolicies() { for (ResponsePolicy policy : ResponsePolicy.values()) { MultiNodeResultAggregator aggregator = new MultiNodeResultAggregator<>(policy); assertEquals(policy, aggregator.getResponsePolicy(), "Should store the response policy: " + policy); } } @Nested class BasicTests { // ==================== getResponsePolicy Tests ==================== @Test public void testGetResponsePolicy_returnsCorrectPolicy() { MultiNodeResultAggregator aggregator = new MultiNodeResultAggregator<>( ResponsePolicy.AGG_SUM); assertEquals(ResponsePolicy.AGG_SUM, aggregator.getResponsePolicy(), "getResponsePolicy should return the policy passed to constructor"); } // ==================== addSuccess(HostAndPort, T) Tests ==================== @Test public void testAddSuccess_withNode_addsToRepliesMap() { MultiNodeResultAggregator aggregator = new MultiNodeResultAggregator<>( ResponsePolicy.ALL_SUCCEEDED); aggregator.addSuccess(NODE_1, "OK"); aggregator.addError(NODE_2, new RuntimeException("error")); JedisBroadcastException ex = assertThrows(JedisBroadcastException.class, () -> aggregator.getResult()); Map replies = ex.getReplies(); assertEquals(2, replies.size(), "Should have entries for both nodes"); assertEquals("OK", replies.get(NODE_1), "Should contain the success reply for NODE_1"); assertTrue(replies.get(NODE_2) instanceof RuntimeException, "Should contain the error for NODE_2"); } @Test public void testAddSuccess_withNullNode_aggregatesResult() { MultiNodeResultAggregator> aggregator = new MultiNodeResultAggregator<>( ResponsePolicy.DEFAULT); aggregator.addSuccess(null, Collections.singletonList("S1")); aggregator.addSuccess(null, Collections.singletonList("S2")); List result = aggregator.getResult(); assertThat(result, contains("S1", "S2")); } @Test public void testAddSuccess_withNullNode_doesNotAddToRepliesMap() { MultiNodeResultAggregator aggregator = new MultiNodeResultAggregator<>( ResponsePolicy.ALL_SUCCEEDED); aggregator.addSuccess(null, "result"); aggregator.addError(NODE_1, new RuntimeException("error")); JedisBroadcastException ex = assertThrows(JedisBroadcastException.class, () -> aggregator.getResult()); Map replies = ex.getReplies(); assertEquals(1, replies.size(), "Should only have entry for NODE_1, not null node"); assertFalse(replies.containsKey(null), "Should not contain null key"); } // ==================== addSuccess(T) Tests ==================== @Test public void testAddSuccess_withoutNode_recordsResult() { MultiNodeResultAggregator> aggregator = new MultiNodeResultAggregator<>( ResponsePolicy.DEFAULT); aggregator.addSuccess(Collections.singletonList("S1")); assertThat(aggregator.getResult(), contains("S1")); } @Test public void testAddSuccess_withoutNode_aggregatesMultipleResults() { MultiNodeResultAggregator aggregator = new MultiNodeResultAggregator<>( ResponsePolicy.AGG_SUM); aggregator.addSuccess(10L); aggregator.addSuccess(20L); aggregator.addSuccess(30L); Long result = aggregator.getResult(); assertEquals(60L, result, "Should aggregate results using AGG_SUM policy"); } // ==================== addError(HostAndPort, Exception) Tests ==================== @Test public void testAddError_withNode_recordsErrorAndNode() { MultiNodeResultAggregator aggregator = new MultiNodeResultAggregator<>( ResponsePolicy.ALL_SUCCEEDED); RuntimeException error = new RuntimeException("Connection failed"); aggregator.addError(NODE_1, error); JedisBroadcastException ex = assertThrows(JedisBroadcastException.class, () -> aggregator.getResult()); Map replies = ex.getReplies(); assertEquals(1, replies.size(), "Should have one error entry"); assertSame(error, replies.get(NODE_1), "Should contain the exception for NODE_1"); } @Test public void testAddError_multipleNodes_recordsAllErrors() { MultiNodeResultAggregator aggregator = new MultiNodeResultAggregator<>( ResponsePolicy.ALL_SUCCEEDED); RuntimeException error1 = new RuntimeException("Error 1"); RuntimeException error2 = new RuntimeException("Error 2"); aggregator.addError(NODE_1, error1); aggregator.addError(NODE_2, error2); JedisBroadcastException ex = assertThrows(JedisBroadcastException.class, () -> aggregator.getResult()); Map replies = ex.getReplies(); assertEquals(2, replies.size(), "Should have two error entries"); assertSame(error1, replies.get(NODE_1)); assertSame(error2, replies.get(NODE_2)); } // ==================== addError(Exception) Tests ==================== @Test public void testAddError_withJedisClusterOperationException_extractsNode() { MultiNodeResultAggregator aggregator = new MultiNodeResultAggregator<>( ResponsePolicy.ALL_SUCCEEDED); JedisClusterOperationException error = new JedisClusterOperationException("Cluster error", NODE_1); aggregator.addError(error); JedisBroadcastException ex = assertThrows(JedisBroadcastException.class, () -> aggregator.getResult()); Map replies = ex.getReplies(); assertEquals(1, replies.size(), "Should have one error entry"); assertTrue(replies.containsKey(NODE_1), "Should extract node from JedisClusterOperationException"); assertSame(error, replies.get(NODE_1), "Should contain the original exception"); } @Test public void testAddError_withJedisClusterOperationException_nullNode_usesUnknown() { MultiNodeResultAggregator aggregator = new MultiNodeResultAggregator<>( ResponsePolicy.ALL_SUCCEEDED); // JedisClusterOperationException without node (null) JedisClusterOperationException error = new JedisClusterOperationException("Cluster error"); aggregator.addError(error); JedisBroadcastException ex = assertThrows(JedisBroadcastException.class, () -> aggregator.getResult()); Map replies = ex.getReplies(); assertEquals(1, replies.size(), "Should have one error entry"); assertTrue(replies.containsKey(UNKNOWN_NODE), "Should use unknown:0 when node is null"); } @Test public void testAddError_withRegularException_usesUnknownNode() { MultiNodeResultAggregator aggregator = new MultiNodeResultAggregator<>( ResponsePolicy.ALL_SUCCEEDED); RuntimeException error = new RuntimeException("Generic error"); aggregator.addError(error); JedisBroadcastException ex = assertThrows(JedisBroadcastException.class, () -> aggregator.getResult()); Map replies = ex.getReplies(); assertEquals(1, replies.size(), "Should have one error entry"); assertTrue(replies.containsKey(UNKNOWN_NODE), "Should use unknown:0 for non-JedisClusterOperationException"); assertSame(error, replies.get(UNKNOWN_NODE)); } // ==================== getResult() - ONE_SUCCEEDED Policy Tests ==================== @Test public void testGetResult_oneSucceeded_allNodesSucceed_returnsAggregatedResult() { MultiNodeResultAggregator aggregator = new MultiNodeResultAggregator<>( ResponsePolicy.ONE_SUCCEEDED); aggregator.addSuccess(NODE_1, "OK"); aggregator.addSuccess(NODE_2, "OK"); aggregator.addSuccess(NODE_3, "OK"); String result = aggregator.getResult(); assertEquals("OK", result, "Should return successful result when all nodes succeed"); } @Test public void testGetResult_oneSucceeded_oneNodeSucceedsOthersFail_returnsSuccess() { MultiNodeResultAggregator aggregator = new MultiNodeResultAggregator<>( ResponsePolicy.ONE_SUCCEEDED); aggregator.addError(NODE_1, new RuntimeException("Error 1")); aggregator.addSuccess(NODE_2, "OK"); aggregator.addError(NODE_3, new RuntimeException("Error 3")); // ONE_SUCCEEDED should return success if at least one node succeeded String result = aggregator.getResult(); assertEquals("OK", result, "Should return success if at least one node succeeded"); } @Test public void testGetResult_oneSucceeded_allNodesFail_throwsException() { MultiNodeResultAggregator aggregator = new MultiNodeResultAggregator<>( ResponsePolicy.ONE_SUCCEEDED); aggregator.addError(NODE_1, new RuntimeException("Error 1")); aggregator.addError(NODE_2, new RuntimeException("Error 2")); aggregator.addError(NODE_3, new RuntimeException("Error 3")); JedisBroadcastException ex = assertThrows(JedisBroadcastException.class, () -> aggregator.getResult(), "Should throw JedisBroadcastException when all nodes fail"); Map replies = ex.getReplies(); assertEquals(3, replies.size(), "Should contain all error replies"); } @Test public void testGetResult_oneSucceeded_mixedResults_returnsFirstSuccess() { MultiNodeResultAggregator aggregator = new MultiNodeResultAggregator<>( ResponsePolicy.ONE_SUCCEEDED); aggregator.addError(NODE_1, new RuntimeException("Error")); aggregator.addSuccess(NODE_2, 42L); aggregator.addSuccess(NODE_3, 100L); Long result = aggregator.getResult(); // ONE_SUCCEEDED returns the first successful result (existing value) assertEquals(42L, result, "Should return the first successful result"); } } // ==================== getResult() - ALL_SUCCEEDED Policy Tests ==================== @Nested class AllSucceededPolicyTests { @Test public void testGetResult_allSucceeded_allNodesSucceed_returnsResult() { MultiNodeResultAggregator aggregator = new MultiNodeResultAggregator<>( ResponsePolicy.ALL_SUCCEEDED); aggregator.addSuccess(NODE_1, "OK"); aggregator.addSuccess(NODE_2, "OK"); String result = aggregator.getResult(); assertEquals("OK", result, "Should return result when all nodes succeed with equal values"); } @Test public void testGetResult_allSucceeded_oneNodeFails_throwsException() { MultiNodeResultAggregator aggregator = new MultiNodeResultAggregator<>( ResponsePolicy.ALL_SUCCEEDED); aggregator.addSuccess(NODE_1, "OK"); aggregator.addError(NODE_2, new RuntimeException("Connection failed")); JedisBroadcastException ex = assertThrows(JedisBroadcastException.class, () -> aggregator.getResult(), "Should throw JedisBroadcastException when any node fails"); assertTrue(ex.getMessage().contains("failed"), "Exception message should indicate broadcast failure"); } @Test public void testGetResult_allSucceeded_allNodesFail_throwsException() { MultiNodeResultAggregator aggregator = new MultiNodeResultAggregator<>( ResponsePolicy.ALL_SUCCEEDED); aggregator.addError(NODE_1, new RuntimeException("Error 1")); aggregator.addError(NODE_2, new RuntimeException("Error 2")); assertThrows(JedisBroadcastException.class, () -> aggregator.getResult(), "Should throw JedisBroadcastException when all nodes fail"); } } // ==================== getResult() - DEFAULT Policy Tests ==================== // Nested class for comprehensive DEFAULT policy testing @Nested class DefaultPolicyTests { @Test public void testGetResult_default_allNodesSucceed_returnsResult() { MultiNodeResultAggregator> aggregator = new MultiNodeResultAggregator<>( ResponsePolicy.DEFAULT); aggregator.addSuccess(NODE_1, new ArrayList<>(Collections.singletonList("a"))); aggregator.addSuccess(NODE_2, new ArrayList<>(Collections.singletonList("b"))); ArrayList result = aggregator.getResult(); assertEquals(new ArrayList<>(Arrays.asList("a", "b")), result, "Should return result when all nodes succeed"); } @Test public void testGetResult_default_oneNodeFails_throwsException() { MultiNodeResultAggregator> aggregator = new MultiNodeResultAggregator<>( ResponsePolicy.DEFAULT); aggregator.addSuccess(NODE_1, new ArrayList<>(Collections.singletonList("a"))); aggregator.addError(NODE_2, new RuntimeException("Error")); assertThrows(JedisBroadcastException.class, () -> aggregator.getResult(), "DEFAULT policy should throw when any node fails"); } } // ==================== getResult() - AGG_SUM Policy Tests ==================== @Nested class AggSumPolicyTests { @Test public void testGetResult_aggSum_sumsLongResults() { MultiNodeResultAggregator aggregator = new MultiNodeResultAggregator<>( ResponsePolicy.AGG_SUM); aggregator.addSuccess(NODE_1, 5L); aggregator.addSuccess(NODE_2, 10L); aggregator.addSuccess(NODE_3, 15L); Long result = aggregator.getResult(); assertEquals(30L, result, "Should sum all Long results"); } @Test public void testGetResult_aggSum_oneNodeFails_throwsException() { MultiNodeResultAggregator aggregator = new MultiNodeResultAggregator<>( ResponsePolicy.AGG_SUM); aggregator.addSuccess(NODE_1, 5L); aggregator.addError(NODE_2, new RuntimeException("Error")); assertThrows(JedisBroadcastException.class, () -> aggregator.getResult(), "AGG_SUM should throw when any node fails"); } } // ==================== getResult() - AGG_MIN Policy Tests ==================== @Nested class AggMinPolicyTests { @Test public void testGetResult_aggMin_returnsMinimumValue() { MultiNodeResultAggregator aggregator = new MultiNodeResultAggregator<>( ResponsePolicy.AGG_MIN); aggregator.addSuccess(NODE_1, 100L); aggregator.addSuccess(NODE_2, 50L); aggregator.addSuccess(NODE_3, 75L); Long result = aggregator.getResult(); assertEquals(50L, result, "Should return minimum Long value"); } @Test public void testGetResult_aggMin_oneNodeFails_throwsException() { MultiNodeResultAggregator aggregator = new MultiNodeResultAggregator<>( ResponsePolicy.AGG_MIN); aggregator.addSuccess(NODE_1, 100L); aggregator.addError(NODE_2, new RuntimeException("Error")); assertThrows(JedisBroadcastException.class, () -> aggregator.getResult(), "AGG_MIN should throw when any node fails"); } } // ==================== getResult() - AGG_MAX Policy Tests ==================== @Nested class AggMaxPolicyTests { @Test public void testGetResult_aggMax_returnsMaximumValue() { MultiNodeResultAggregator aggregator = new MultiNodeResultAggregator<>( ResponsePolicy.AGG_MAX); aggregator.addSuccess(NODE_1, 25L); aggregator.addSuccess(NODE_2, 100L); aggregator.addSuccess(NODE_3, 75L); Long result = aggregator.getResult(); assertEquals(100L, result, "Should return maximum Long value"); } @Test public void testGetResult_aggMax_oneNodeFails_throwsException() { MultiNodeResultAggregator aggregator = new MultiNodeResultAggregator<>( ResponsePolicy.AGG_MAX); aggregator.addSuccess(NODE_1, 100L); aggregator.addError(NODE_2, new RuntimeException("Error")); assertThrows(JedisBroadcastException.class, () -> aggregator.getResult(), "AGG_MAX should throw when any node fails"); } } // ==================== getResult() - AGG_LOGICAL_AND Policy Tests ==================== @Nested class AggLogicalAndPolicyTests { @Test public void testGetResult_aggLogicalAnd_allTrue_returnsTrue() { MultiNodeResultAggregator aggregator = new MultiNodeResultAggregator<>( ResponsePolicy.AGG_LOGICAL_AND); aggregator.addSuccess(NODE_1, 1L); aggregator.addSuccess(NODE_2, 1L); aggregator.addSuccess(NODE_3, 1L); Long result = aggregator.getResult(); assertEquals(1L, result, "Logical AND of all true (1L) should be 1L"); } @Test public void testGetResult_aggLogicalAnd_oneFalse_returnsFalse() { MultiNodeResultAggregator aggregator = new MultiNodeResultAggregator<>( ResponsePolicy.AGG_LOGICAL_AND); aggregator.addSuccess(NODE_1, 1L); aggregator.addSuccess(NODE_2, 0L); aggregator.addSuccess(NODE_3, 1L); Long result = aggregator.getResult(); assertEquals(0L, result, "Logical AND with one false (0L) should be 0L"); } @Test public void testGetResult_aggLogicalAnd_oneNodeFails_throwsException() { MultiNodeResultAggregator aggregator = new MultiNodeResultAggregator<>( ResponsePolicy.AGG_LOGICAL_AND); aggregator.addSuccess(NODE_1, 1L); aggregator.addError(NODE_2, new RuntimeException("Error")); assertThrows(JedisBroadcastException.class, () -> aggregator.getResult(), "AGG_LOGICAL_AND should throw when any node fails"); } } // ==================== getResult() - AGG_LOGICAL_OR Policy Tests ==================== @Nested class AggLogicalOrPolicyTests { @Test public void testGetResult_aggLogicalOr_allFalse_returnsFalse() { MultiNodeResultAggregator aggregator = new MultiNodeResultAggregator<>( ResponsePolicy.AGG_LOGICAL_OR); aggregator.addSuccess(NODE_1, 0L); aggregator.addSuccess(NODE_2, 0L); aggregator.addSuccess(NODE_3, 0L); Long result = aggregator.getResult(); assertEquals(0L, result, "Logical OR of all false (0L) should be 0L"); } @Test public void testGetResult_aggLogicalOr_oneTrue_returnsTrue() { MultiNodeResultAggregator aggregator = new MultiNodeResultAggregator<>( ResponsePolicy.AGG_LOGICAL_OR); aggregator.addSuccess(NODE_1, 0L); aggregator.addSuccess(NODE_2, 1L); aggregator.addSuccess(NODE_3, 0L); Long result = aggregator.getResult(); assertEquals(1L, result, "Logical OR with one true (1L) should be 1L"); } @Test public void testGetResult_aggLogicalOr_oneNodeFails_throwsException() { MultiNodeResultAggregator aggregator = new MultiNodeResultAggregator<>( ResponsePolicy.AGG_LOGICAL_OR); aggregator.addSuccess(NODE_1, 1L); aggregator.addError(NODE_2, new RuntimeException("Error")); assertThrows(JedisBroadcastException.class, () -> aggregator.getResult(), "AGG_LOGICAL_OR should throw when any node fails"); } } // ==================== getResult() - SPECIAL Policy Tests ==================== @Nested class SpecialPolicyTests { @Test public void testGetResult_special_returnsFirstResult() { MultiNodeResultAggregator aggregator = new MultiNodeResultAggregator<>( ResponsePolicy.SPECIAL); aggregator.addSuccess(NODE_1, "first"); aggregator.addSuccess(NODE_2, "second"); String result = aggregator.getResult(); assertEquals("first", result, "SPECIAL policy should return existing/first result"); } @Test public void testGetResult_special_oneNodeFails_throwsException() { MultiNodeResultAggregator aggregator = new MultiNodeResultAggregator<>( ResponsePolicy.SPECIAL); aggregator.addSuccess(NODE_1, "first"); aggregator.addError(NODE_2, new RuntimeException("Error")); assertThrows(JedisBroadcastException.class, () -> aggregator.getResult(), "SPECIAL policy should throw when any node fails"); } } // ==================== Edge Case Tests ==================== @Nested class EdgeCaseTests { @Test public void testGetResult_noResultsAdded_returnsNull() { MultiNodeResultAggregator aggregator = new MultiNodeResultAggregator<>( ResponsePolicy.DEFAULT); String result = aggregator.getResult(); assertNull(result, "Should return null when no results have been added"); } @Test public void testGetResult_singleSuccess_returnsResult() { MultiNodeResultAggregator> aggregator = new MultiNodeResultAggregator<>( ResponsePolicy.DEFAULT); aggregator.addSuccess(NODE_1, Collections.singletonList("S1")); List result = aggregator.getResult(); assertThat(result, contains("S1")); } @Test public void testGetResult_singleError_throwsException() { MultiNodeResultAggregator aggregator = new MultiNodeResultAggregator<>( ResponsePolicy.DEFAULT); aggregator.addError(NODE_1, new RuntimeException("Single error")); JedisBroadcastException ex = assertThrows(JedisBroadcastException.class, () -> aggregator.getResult()); assertEquals(1, ex.getReplies().size(), "Should have one error reply"); } @Test public void testAddSuccess_nullResult_handledCorrectly() { MultiNodeResultAggregator aggregator = new MultiNodeResultAggregator<>( ResponsePolicy.DEFAULT); aggregator.addSuccess(NODE_1, null); String result = aggregator.getResult(); assertNull(result, "Should handle null result correctly"); } @Test public void testAddSuccess_nullResult_followedByRealResult() { MultiNodeResultAggregator> aggregator = new MultiNodeResultAggregator<>( ResponsePolicy.DEFAULT); aggregator.addSuccess(NODE_1, null); aggregator.addSuccess(NODE_2, Collections.singletonList("S1")); assertThat(aggregator.getResult(), contains("S1")); } @Test public void testAddSuccess_realResult_followedByNull() { MultiNodeResultAggregator> aggregator = new MultiNodeResultAggregator<>( ResponsePolicy.DEFAULT); aggregator.addSuccess(NODE_1, Collections.singletonList("S1")); aggregator.addSuccess(NODE_2, null); List result = aggregator.getResult(); assertThat(result, contains("S1")); } @Test public void testOneSucceeded_errorThenSuccess_returnsSuccess() { MultiNodeResultAggregator aggregator = new MultiNodeResultAggregator<>( ResponsePolicy.ONE_SUCCEEDED); // First add error, then success aggregator.addError(NODE_1, new RuntimeException("Error")); aggregator.addSuccess(NODE_2, "success"); String result = aggregator.getResult(); assertEquals("success", result, "ONE_SUCCEEDED should return success even if error came first"); } @Test public void testOneSucceeded_successThenError_returnsSuccess() { MultiNodeResultAggregator aggregator = new MultiNodeResultAggregator<>( ResponsePolicy.ONE_SUCCEEDED); // First add success, then error aggregator.addSuccess(NODE_1, "success"); aggregator.addError(NODE_2, new RuntimeException("Error")); String result = aggregator.getResult(); assertEquals("success", result, "ONE_SUCCEEDED should return success even if error came after"); } @Test public void testBroadcastException_containsCorrectMessage() { MultiNodeResultAggregator aggregator = new MultiNodeResultAggregator<>( ResponsePolicy.ALL_SUCCEEDED); aggregator.addError(NODE_1, new RuntimeException("Connection timeout")); JedisBroadcastException ex = assertThrows(JedisBroadcastException.class, () -> aggregator.getResult()); assertTrue(ex.getMessage().contains("failed"), "JedisBroadcastException should have meaningful message"); } @Test public void testBroadcastException_containsMixedReplies() { MultiNodeResultAggregator aggregator = new MultiNodeResultAggregator<>( ResponsePolicy.ALL_SUCCEEDED); aggregator.addSuccess(NODE_1, "OK"); aggregator.addSuccess(NODE_2, "OK"); aggregator.addError(NODE_3, new RuntimeException("Failed")); JedisBroadcastException ex = assertThrows(JedisBroadcastException.class, () -> aggregator.getResult()); Map replies = ex.getReplies(); assertEquals(3, replies.size(), "Should contain all replies (successes and errors)"); assertEquals("OK", replies.get(NODE_1)); assertEquals("OK", replies.get(NODE_2)); assertTrue(replies.get(NODE_3) instanceof RuntimeException); } @Test public void testAggregation_multipleSuccessesBeforeError() { MultiNodeResultAggregator aggregator = new MultiNodeResultAggregator<>( ResponsePolicy.AGG_SUM); aggregator.addSuccess(NODE_1, 10L); aggregator.addSuccess(NODE_2, 20L); aggregator.addError(NODE_3, new RuntimeException("Error")); // Should throw because AGG_SUM is not ONE_SUCCEEDED assertThrows(JedisBroadcastException.class, () -> aggregator.getResult(), "AGG_SUM should throw on any error, even if successes were aggregated"); } @Test public void testOneSucceeded_aggregatesMultipleSuccesses() { // With ONE_SUCCEEDED, aggregation still happens using the policy's aggregation behavior MultiNodeResultAggregator aggregator = new MultiNodeResultAggregator<>( ResponsePolicy.ONE_SUCCEEDED); aggregator.addSuccess(NODE_1, "first"); aggregator.addError(NODE_2, new RuntimeException("Error")); aggregator.addSuccess(NODE_3, "second"); String result = aggregator.getResult(); // ONE_SUCCEEDED returns existing value (first successful result) assertEquals("first", result, "ONE_SUCCEEDED should return the first successful result"); } @Test public void testMixedAddSuccessMethods() { MultiNodeResultAggregator aggregator = new MultiNodeResultAggregator<>( ResponsePolicy.AGG_SUM); // Mix both addSuccess methods aggregator.addSuccess(NODE_1, 10L); aggregator.addSuccess(20L); // Without node aggregator.addSuccess(NODE_2, 30L); Long result = aggregator.getResult(); assertEquals(60L, result, "Should aggregate results from both addSuccess methods"); } @Test public void testMixedAddErrorMethods() { MultiNodeResultAggregator aggregator = new MultiNodeResultAggregator<>( ResponsePolicy.ALL_SUCCEEDED); aggregator.addError(NODE_1, new RuntimeException("Error 1")); aggregator.addError(new RuntimeException("Error 2")); // Will use unknown:0 aggregator.addError(new JedisClusterOperationException("Error 3", NODE_2)); JedisBroadcastException ex = assertThrows(JedisBroadcastException.class, () -> aggregator.getResult()); Map replies = ex.getReplies(); assertEquals(3, replies.size(), "Should have three error entries"); assertTrue(replies.containsKey(NODE_1)); assertTrue(replies.containsKey(UNKNOWN_NODE)); assertTrue(replies.containsKey(NODE_2)); } } } ================================================ FILE: src/test/java/redis/clients/jedis/failover/FailoverIntegrationTest.java ================================================ package redis.clients.jedis.failover; import eu.rekawek.toxiproxy.Proxy; import eu.rekawek.toxiproxy.ToxiproxyClient; import eu.rekawek.toxiproxy.model.Toxic; import eu.rekawek.toxiproxy.model.ToxicDirection; import io.github.resilience4j.circuitbreaker.CircuitBreaker; import org.junit.jupiter.api.AfterAll; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Tag; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Timeout; import redis.clients.jedis.DefaultJedisClientConfig; import redis.clients.jedis.EndpointConfig; import redis.clients.jedis.Endpoints; import redis.clients.jedis.JedisClientConfig; import redis.clients.jedis.MultiDbClient; import redis.clients.jedis.MultiDbConfig; import redis.clients.jedis.UnifiedJedis; import redis.clients.jedis.exceptions.JedisConnectionException; import redis.clients.jedis.mcf.MultiDbConnectionProvider; import redis.clients.jedis.scenario.RecommendedSettings; import java.io.IOException; import java.util.Arrays; import java.util.List; import java.util.concurrent.ExecutionException; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.Future; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicInteger; import java.util.function.Function; import java.util.regex.Matcher; import java.util.regex.Pattern; import java.util.stream.Collectors; import static org.awaitility.Awaitility.await; import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.instanceOf; import static org.hamcrest.core.IsEqual.equalTo; import static org.junit.jupiter.api.Assertions.assertThrows; @Tag("failover") public class FailoverIntegrationTest { private static EndpointConfig endpoint1; private static EndpointConfig endpoint2; private static final ToxiproxyClient tp = new ToxiproxyClient("localhost", 8474); public static ExecutorService executor; public static Pattern pattern = Pattern.compile("run_id:([a-f0-9]+)"); private static Proxy redisProxy1; private static Proxy redisProxy2; private UnifiedJedis jedis1; private UnifiedJedis jedis2; private static String JEDIS1_ID = ""; private static String JEDIS2_ID = ""; private MultiDbConnectionProvider provider; private MultiDbClient failoverClient; @BeforeAll public static void setupAdminClients() throws IOException { endpoint1 = Endpoints.getRedisEndpoint("redis-failover-1"); endpoint2 = Endpoints.getRedisEndpoint("redis-failover-2"); if (tp.getProxyOrNull("redis-1") != null) { tp.getProxy("redis-1").delete(); } if (tp.getProxyOrNull("redis-2") != null) { tp.getProxy("redis-2").delete(); } executor = Executors.newCachedThreadPool(); redisProxy1 = tp.createProxy("redis-1", "0.0.0.0:29379", "redis-failover-1:9379"); redisProxy2 = tp.createProxy("redis-2", "0.0.0.0:29380", "redis-failover-2:9380"); } @AfterAll public static void cleanupAdminClients() throws IOException { if (redisProxy1 != null) redisProxy1.delete(); if (redisProxy2 != null) redisProxy2.delete(); if (executor != null) executor.shutdown(); } @BeforeEach public void setup() throws IOException { tp.getProxies().forEach(proxy -> { try { proxy.enable(); for (Toxic toxic : proxy.toxics().getAll()) { toxic.remove(); } } catch (IOException e) { throw new RuntimeException(e); } }); jedis1 = new UnifiedJedis(endpoint1.getHostAndPort(), DefaultJedisClientConfig.builder().build()); jedis2 = new UnifiedJedis(endpoint2.getHostAndPort(), DefaultJedisClientConfig.builder().build()); jedis1.flushAll(); jedis2.flushAll(); JEDIS1_ID = getNodeId(jedis1); JEDIS2_ID = getNodeId(jedis2); // Create default provider and client for most tests provider = createProvider(); failoverClient = MultiDbClient.builder().connectionProvider(provider).build(); } @AfterEach public void cleanup() throws IOException { if (failoverClient != null) failoverClient.close(); if (jedis1 != null) jedis1.close(); if (jedis2 != null) jedis2.close(); } /** * Tests the automatic failover behavior when a Redis server becomes unavailable. This test * verifies: *
    *
  1. Initial connection to the first Redis server works correctly
  2. *
  3. Disable access, the first command throws
  4. *
  5. Command failure is propagated to the caller
  6. *
  7. CB transitions to OPEN, failover is initiated and following commands are sent to the next * endpoint
  8. *
  9. Second server is also disabled, all commands fail with JedisConnectionException and error * is propagated to the caller
  10. *
*/ @Test public void testAutomaticFailoverWhenServerBecomesUnavailable() throws Exception { assertThat(getNodeId(failoverClient.info("server")), equalTo(JEDIS1_ID)); await().atMost(1, TimeUnit.SECONDS).pollInterval(50, TimeUnit.MILLISECONDS) .until(() -> provider.getDatabase(endpoint2.getHostAndPort()).isHealthy()); // Disable redisProxy1 redisProxy1.disable(); // Endpoint 1 not available // 1. First call should throw JedisConnectionException and trigger failover // 2. Endpoint 1 CB transitions to OPEN // 3. Subsequent calls should be routed to Endpoint 2 assertThrows(JedisConnectionException.class, () -> failoverClient.info("server")); assertThat(provider.getDatabase(endpoint1.getHostAndPort()).getCircuitBreaker().getState(), equalTo(CircuitBreaker.State.FORCED_OPEN)); // Check that the failoverClient is now using Endpoint 2 assertThat(getNodeId(failoverClient.info("server")), equalTo(JEDIS2_ID)); // Disable also second proxy redisProxy2.disable(); // Endpoint1 and Endpoint2 are NOT available, assertThrows(JedisConnectionException.class, () -> failoverClient.info("server")); assertThat(provider.getDatabase(endpoint2.getHostAndPort()).getCircuitBreaker().getState(), equalTo(CircuitBreaker.State.FORCED_OPEN)); // and since no other nodes are available, it should propagate the errors to the caller // subsequent calls assertThrows(JedisConnectionException.class, () -> failoverClient.info("server")); } @Test public void testManualFailoverNewCommandsAreSentToActiveDatabase() throws InterruptedException { assertThat(getNodeId(failoverClient.info("server")), equalTo(JEDIS1_ID)); await().atMost(1, TimeUnit.SECONDS).pollInterval(50, TimeUnit.MILLISECONDS) .until(() -> provider.getDatabase(endpoint2.getHostAndPort()).isHealthy()); provider.setActiveDatabase(endpoint2.getHostAndPort()); assertThat(getNodeId(failoverClient.info("server")), equalTo(JEDIS2_ID)); } private List getDatabaseConfigs(JedisClientConfig clientConfig, EndpointConfig... endpoints) { int weight = endpoints.length; AtomicInteger weightCounter = new AtomicInteger(weight); return Arrays.stream(endpoints) .map(e -> MultiDbConfig.DatabaseConfig.builder(e.getHostAndPort(), clientConfig) .weight(1.0f / weightCounter.getAndIncrement()).healthCheckEnabled(false).build()) .collect(Collectors.toList()); } @Test @Timeout(5) public void testManualFailoverInflightCommandsCompleteGracefully() throws ExecutionException, InterruptedException { await().atMost(1, TimeUnit.SECONDS).pollInterval(50, TimeUnit.MILLISECONDS) .until(() -> provider.getDatabase(endpoint2.getHostAndPort()).isHealthy()); assertThat(getNodeId(failoverClient.info("server")), equalTo(JEDIS1_ID)); // We will trigger failover while this command is in-flight Future> blpop = executor.submit(() -> failoverClient.blpop(1000, "test-list")); provider.setActiveDatabase(endpoint2.getHostAndPort()); // After the manual failover, commands should be executed against Endpoint 2 assertThat(getNodeId(failoverClient.info("server")), equalTo(JEDIS2_ID)); // Failover was manually triggered, and there were no errors // previous endpoint CB should still be in CLOSED state assertThat(provider.getDatabase(endpoint1.getHostAndPort()).getCircuitBreaker().getState(), equalTo(CircuitBreaker.State.CLOSED)); jedis1.rpush("test-list", "somevalue"); assertThat(blpop.get(), equalTo(Arrays.asList("test-list", "somevalue"))); } /** * Verify that in-flight commands that complete with error during manual failover will propagate * the error to the caller and toggle CB to OPEN state. */ @Test public void testManualFailoverInflightCommandsWithErrorsPropagateError() throws Exception { assertThat(getNodeId(failoverClient.info("server")), equalTo(JEDIS1_ID)); await().atMost(1, TimeUnit.SECONDS).pollInterval(50, TimeUnit.MILLISECONDS) .until(() -> provider.getDatabase(endpoint2.getHostAndPort()).isHealthy()); Future> blpop = executor.submit(() -> failoverClient.blpop(10000, "test-list-1")); // trigger failover manually provider.setActiveDatabase(endpoint2.getHostAndPort()); Future infoCmd = executor.submit(() -> failoverClient.info("server")); // After the manual failover, commands should be executed against Endpoint 2 assertThat(getNodeId(infoCmd.get()), equalTo(JEDIS2_ID)); // Disable redisProxy1 to drop active connections and trigger an error redisProxy1.disable(); // previously submitted command should fail with JedisConnectionException ExecutionException exception = assertThrows(ExecutionException.class, blpop::get); assertThat(exception.getCause(), instanceOf(JedisConnectionException.class)); // Check that the circuit breaker for Endpoint 1 is open after the error assertThat(provider.getDatabase(endpoint1.getHostAndPort()).getCircuitBreaker().getState(), equalTo(CircuitBreaker.State.OPEN)); // Ensure that the active cluster is still Endpoint 2 assertThat(getNodeId(failoverClient.info("server")), equalTo(JEDIS2_ID)); } /** * Tests that the CircuitBreaker counts each command error separately, and not just after all * retries are exhausted. This ensures that the circuit breaker opens based on the actual number * of send commands with failures, and not based on the number of logical operations. */ @Test public void testCircuitBreakerCountsEachConnectionErrorSeparately() throws IOException { MultiDbConfig failoverConfig = new MultiDbConfig.Builder(getDatabaseConfigs( DefaultJedisClientConfig.builder().socketTimeoutMillis(RecommendedSettings.DEFAULT_TIMEOUT_MS) .connectionTimeoutMillis(RecommendedSettings.DEFAULT_TIMEOUT_MS).build(), endpoint1, endpoint2)) .commandRetry(MultiDbConfig.RetryConfig.builder().maxAttempts(2).waitDuration(1).build()) .failureDetector(MultiDbConfig.CircuitBreakerConfig.builder().slidingWindowSize(3) .minNumOfFailures(2).failureRateThreshold(50f) // %50 failure rate .build()) .build(); MultiDbConnectionProvider provider = new MultiDbConnectionProvider(failoverConfig); try (MultiDbClient client = MultiDbClient.builder().connectionProvider(provider).build()) { // Verify initial connection to first endpoint assertThat(getNodeId(client.info("server")), equalTo(JEDIS1_ID)); // Disable first endpoint redisProxy1.disable(); // First command should fail and OPEN the circuit breaker immediately // // If CB is applied after retries: // - It would take 2 commands to OPEN CB (error is propagated for both commands) // - Failover to the next Endpoint happens on the 3rd command // // If CB is applied before retries: // - It should open after just 1 command with retries // - CB is OPEN after the 2nd retry of the first command // - Failover to the next Endpoint happens on the 2nd command // // This test verifies the second case by checking that: // 1. CB opens after the first command (with retries) // 2. The second command is routed to the second endpoint // Command 1 assertThrows(JedisConnectionException.class, () -> client.info("server")); // Circuit breaker should be open after just one command with retries assertThat(provider.getDatabase(endpoint1.getHostAndPort()).getCircuitBreaker().getState(), equalTo(CircuitBreaker.State.FORCED_OPEN)); // Next command should be routed to the second endpoint // Command 2 assertThat(getNodeId(client.info("server")), equalTo(JEDIS2_ID)); // Command 3 assertThat(getNodeId(client.info("server")), equalTo(JEDIS2_ID)); } } /** * Tests that in-flight commands are retried after automatic failover when retry is enabled. */ @Test public void testInflightCommandsAreRetriedAfterFailover() throws Exception { MultiDbConnectionProvider customProvider = createProvider( builder -> builder.retryOnFailover(true)); // Create a custom client with retryOnFailover enabled for this specific test try (MultiDbClient customClient = MultiDbClient.builder().connectionProvider(customProvider) .build()) { assertThat(getNodeId(customClient.info("server")), equalTo(JEDIS1_ID)); Thread.sleep(1000); // We will trigger failover while this command is in-flight Future> blpop = executor.submit(() -> customClient.blpop(10000, "test-list-1")); // Simulate error by sending more than 100 bytes. This causes the connection close, and // CB -> OPEN, failover will be actually triggered by the next command redisProxy1.toxics().limitData("simulate-socket-failure", ToxicDirection.UPSTREAM, 100); assertThrows(JedisConnectionException.class, () -> customClient.set("test-key", generateTestValue(150))); // Actual failover is performed on first command received after CB is OPEN // TODO : Remove second command. Once we Refactor existing code to perform actual failover // immediately when CB state change to OPEN/FORCED_OPENs assertThat(getNodeId(customClient.info("server")), equalTo(JEDIS2_ID)); // Check that the circuit breaker for Endpoint 1 is open assertThat( customProvider.getDatabase(endpoint1.getHostAndPort()).getCircuitBreaker().getState(), equalTo(CircuitBreaker.State.FORCED_OPEN)); // Disable redisProxy1 to enforce connection drop for the in-flight (blpop) command redisProxy1.disable(); // The in-flight command should be retried and succeed after failover customClient.rpush("test-list-1", "somevalue"); assertThat(blpop.get(), equalTo(Arrays.asList("test-list-1", "somevalue"))); } } /** * Tests that in-flight commands are not retried after automatic failover when retry is disabled. */ @Test public void testInflightCommandsAreNotRetriedAfterFailover() throws Exception { // Create a custom provider and client with retry disabled for this specific test MultiDbConnectionProvider customProvider = createProvider( builder -> builder.retryOnFailover(false)); try (MultiDbClient customClient = MultiDbClient.builder().connectionProvider(customProvider) .build()) { assertThat(getNodeId(customClient.info("server")), equalTo(JEDIS1_ID)); Future> blpop = executor.submit(() -> customClient.blpop(500, "test-list-2")); // Simulate error by sending more than 100 bytes. This causes connection close, and triggers // failover redisProxy1.toxics().limitData("simulate-socket-failure", ToxicDirection.UPSTREAM, 100); assertThrows(JedisConnectionException.class, () -> customClient.set("test-key", generateTestValue(150))); // Check that the circuit breaker for Endpoint 1 is open assertThat( customProvider.getDatabase(endpoint1.getHostAndPort()).getCircuitBreaker().getState(), equalTo(CircuitBreaker.State.FORCED_OPEN)); // Disable redisProxy1 to enforce the current blpop command failure redisProxy1.disable(); // The in-flight command should fail since the retry is disabled ExecutionException exception = assertThrows(ExecutionException.class, () -> blpop.get(1, TimeUnit.SECONDS)); assertThat(exception.getCause(), instanceOf(JedisConnectionException.class)); } } private static String getNodeId(UnifiedJedis client) { return getNodeId(client.info("server")); } private static String getNodeId(String info) { Matcher m = pattern.matcher(info); if (m.find()) { return m.group(1); } return null; } /** * Generates a string of a specific byte size. * @param byteSize The desired size in bytes * @return A string of the specified byte size */ private static String generateTestValue(int byteSize) { StringBuilder value = new StringBuilder(byteSize); for (int i = 0; i < byteSize; i++) { value.append('x'); } return value.toString(); } /** * Creates a MultiDbConnectionProvider with standard configuration * @return A configured provider */ private MultiDbConnectionProvider createProvider() { JedisClientConfig clientConfig = DefaultJedisClientConfig.builder() .socketTimeoutMillis(RecommendedSettings.DEFAULT_TIMEOUT_MS) .connectionTimeoutMillis(RecommendedSettings.DEFAULT_TIMEOUT_MS).build(); MultiDbConfig failoverConfig = new MultiDbConfig.Builder( getDatabaseConfigs(clientConfig, endpoint1, endpoint2)) .commandRetry( MultiDbConfig.RetryConfig.builder().maxAttempts(1).waitDuration(1).build()) .failureDetector(MultiDbConfig.CircuitBreakerConfig.builder().slidingWindowSize(3) .minNumOfFailures(1).failureRateThreshold(50f).build()) .build(); return new MultiDbConnectionProvider(failoverConfig); } /** * Creates a MultiDbConnectionProvider with standard configuration * @return A configured provider */ private MultiDbConnectionProvider createProvider( Function configCustomizer) { JedisClientConfig clientConfig = DefaultJedisClientConfig.builder() .socketTimeoutMillis(RecommendedSettings.DEFAULT_TIMEOUT_MS) .connectionTimeoutMillis(RecommendedSettings.DEFAULT_TIMEOUT_MS).build(); MultiDbConfig.Builder builder = new MultiDbConfig.Builder( getDatabaseConfigs(clientConfig, endpoint1, endpoint2)) .commandRetry( MultiDbConfig.RetryConfig.builder().maxAttempts(1).waitDuration(1).build()) .failureDetector(MultiDbConfig.CircuitBreakerConfig.builder().slidingWindowSize(3) .minNumOfFailures(1).failureRateThreshold(50f).build()); if (configCustomizer != null) { builder = configCustomizer.apply(builder); } return new MultiDbConnectionProvider(builder.build()); } } ================================================ FILE: src/test/java/redis/clients/jedis/mcf/ActiveActiveLocalFailoverTest.java ================================================ package redis.clients.jedis.mcf; import io.github.resilience4j.ratelimiter.RateLimiterConfig; import org.hamcrest.Matchers; import org.junit.jupiter.api.AfterAll; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Tag; import org.junit.jupiter.api.Tags; import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.CsvSource; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import eu.rekawek.toxiproxy.Proxy; import eu.rekawek.toxiproxy.ToxiproxyClient; import eu.rekawek.toxiproxy.model.Toxic; import redis.clients.jedis.*; import redis.clients.jedis.MultiDbConfig.CircuitBreakerConfig; import redis.clients.jedis.MultiDbConfig.DatabaseConfig; import redis.clients.jedis.MultiDbConfig.RetryConfig; import redis.clients.jedis.scenario.MultiThreadedFakeApp; import redis.clients.jedis.scenario.RecommendedSettings; import redis.clients.jedis.scenario.FaultInjectionClient.TriggerActionResponse; import redis.clients.jedis.exceptions.JedisConnectionException; import redis.clients.jedis.util.ClientTestUtil; import java.io.IOException; import java.time.Duration; import java.time.Instant; import java.util.HashMap; import java.util.Map; import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.atomic.AtomicLong; import java.util.concurrent.atomic.AtomicReference; import java.util.function.Consumer; import static org.awaitility.Awaitility.await; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertTrue; import static org.junit.jupiter.api.Assertions.assertFalse; import static org.hamcrest.MatcherAssert.assertThat; @Tags({ @Tag("failover"), @Tag("integration") }) public class ActiveActiveLocalFailoverTest { private static final Logger log = LoggerFactory.getLogger(ActiveActiveLocalFailoverTest.class); private static EndpointConfig endpoint1; private static EndpointConfig endpoint2; private static final ToxiproxyClient tp = new ToxiproxyClient("localhost", 8474); public static final int ENDPOINT_PAUSE_TIME_MS = 10000; private static Proxy redisProxy1; private static Proxy redisProxy2; @BeforeAll public static void setupAdminClients() throws IOException { endpoint1 = Endpoints.getRedisEndpoint("redis-failover-1"); endpoint2 = Endpoints.getRedisEndpoint("redis-failover-2"); if (tp.getProxyOrNull("redis-1") != null) { tp.getProxy("redis-1").delete(); } if (tp.getProxyOrNull("redis-2") != null) { tp.getProxy("redis-2").delete(); } redisProxy1 = tp.createProxy("redis-1", "0.0.0.0:29379", "redis-failover-1:9379"); redisProxy2 = tp.createProxy("redis-2", "0.0.0.0:29380", "redis-failover-2:9380"); } @AfterAll public static void cleanupAdminClients() throws IOException { if (redisProxy1 != null) redisProxy1.delete(); if (redisProxy2 != null) redisProxy2.delete(); } @BeforeEach public void setup() throws IOException { tp.getProxies().forEach(proxy -> { try { proxy.enable(); for (Toxic toxic : proxy.toxics().getAll()) { toxic.remove(); } } catch (IOException e) { throw new RuntimeException(e); } }); } @ParameterizedTest @CsvSource({ "true, 0, 2, 4", "true, 0, 2, 6", "true, 0, 2, 7", "true, 0, 2, 8", "true, 0, 2, 9", "true, 0, 2, 16", }) public void testFailover(boolean fastFailover, long minFailoverCompletionDuration, long maxFailoverCompletionDuration, int numberOfThreads) { log.info( "TESTING WITH PARAMETERS: fastFailover: {} numberOfThreads: {} minFailoverCompletionDuration: {} maxFailoverCompletionDuration: {] ", fastFailover, numberOfThreads, minFailoverCompletionDuration, maxFailoverCompletionDuration); JedisClientConfig config = endpoint1.getClientConfigBuilder() .socketTimeoutMillis(RecommendedSettings.DEFAULT_TIMEOUT_MS) .connectionTimeoutMillis(RecommendedSettings.DEFAULT_TIMEOUT_MS).build(); DatabaseConfig db1 = DatabaseConfig.builder(endpoint1.getHostAndPort(), config) .connectionPoolConfig(RecommendedSettings.poolConfig).weight(1.0f).build(); DatabaseConfig db2 = DatabaseConfig.builder(endpoint2.getHostAndPort(), config) .connectionPoolConfig(RecommendedSettings.poolConfig).weight(0.5f).build(); MultiDbConfig.Builder builder = new MultiDbConfig.Builder().database(db1).database(db2) .failureDetector( CircuitBreakerConfig.builder().slidingWindowSize(1).failureRateThreshold(10.0f).build()) .failbackSupported(false).gracePeriod(10000).commandRetry(RetryConfig.builder() .waitDuration(10).maxAttempts(1).exponentialBackoffMultiplier(1).build()); // Use the parameterized fastFailover setting builder.fastFailover(fastFailover); class FailoverReporter implements Consumer { String currentDatabaseName = "not set"; boolean failoverHappened = false; Instant failoverAt = null; boolean failbackHappened = false; Instant failbackAt = null; public String getCurrentDatabaseName() { return currentDatabaseName; } @Override public void accept(DatabaseSwitchEvent e) { this.currentDatabaseName = e.getDatabaseName(); log.info("\n\n===={}=== \nJedis switching to database: {}\n====End of log===\n", e.getReason(), e.getDatabaseName()); if ((e.getReason() == SwitchReason.CIRCUIT_BREAKER || e.getReason() == SwitchReason.HEALTH_CHECK)) { failoverHappened = true; failoverAt = Instant.now(); } if (e.getReason() == SwitchReason.FAILBACK) { failbackHappened = true; failbackAt = Instant.now(); } } } // Ensure endpoints are healthy assertTrue(redisProxy1.isEnabled()); assertTrue(redisProxy2.isEnabled()); ensureEndpointAvailability(endpoint1.getHostAndPort(), config); ensureEndpointAvailability(endpoint2.getHostAndPort(), config); FailoverReporter reporter = new FailoverReporter(); MultiDbClient multiDbClient = MultiDbClient.builder().multiDbConfig(builder.build()) .databaseSwitchListener(reporter).build(); multiDbClient.setActiveDatabase(endpoint1.getHostAndPort()); AtomicLong retryingThreadsCounter = new AtomicLong(0); AtomicLong failedCommandsAfterFailover = new AtomicLong(0); AtomicReference lastFailedCommandAt = new AtomicReference<>(); AtomicReference lastFailedBeforeFailover = new AtomicReference<>(); AtomicBoolean errorOccuredAfterFailover = new AtomicBoolean(false); AtomicBoolean unexpectedErrors = new AtomicBoolean(false); AtomicReference lastException = new AtomicReference(); AtomicLong stopRunningAt = new AtomicLong(); Endpoint db2Endpoint = endpoint2.getHostAndPort(); // Start thread that imitates an application that uses the multiDbClient RateLimiterConfig rateLimiterConfig = RateLimiterConfig.custom().limitForPeriod(100) .limitRefreshPeriod(Duration.ofSeconds(1)).timeoutDuration(Duration.ofSeconds(1)).build(); MultiThreadedFakeApp fakeApp = new MultiThreadedFakeApp(multiDbClient, (UnifiedJedis c) -> { long threadId = Thread.currentThread().getId(); int attempt = 0; int maxTries = 500; int retryingDelay = 5; String currentDbKey = null; while (true) { try { if (System.currentTimeMillis() > stopRunningAt.get()) break; currentDbKey = dbKey(multiDbClient.getActiveDatabaseEndpoint()); Map executionInfo = new HashMap() { { put("threadId", String.valueOf(threadId)); put("database", reporter.getCurrentDatabaseName()); } }; multiDbClient.xadd("execution_log", StreamEntryID.NEW_ENTRY, executionInfo); if (attempt > 0) { log.info("Thread {} recovered after {} ms. Threads still not recovered: {}", threadId, attempt * retryingDelay, retryingThreadsCounter.decrementAndGet()); } break; } catch (JedisConnectionException e) { if (dbKey(db2Endpoint).equals(currentDbKey)) { break; } lastException.set(e); lastFailedBeforeFailover.set(Instant.now()); if (reporter.failoverHappened) { errorOccuredAfterFailover.set(true); long failedCommands = failedCommandsAfterFailover.incrementAndGet(); lastFailedCommandAt.set(Instant.now()); log.warn( "Thread {} failed to execute command after failover. Failed commands after failover: {}", threadId, failedCommands); } if (attempt == 0) { long failedThreads = retryingThreadsCounter.incrementAndGet(); log.warn("Thread {} failed to execute command. Failed threads: {}", threadId, failedThreads); } try { Thread.sleep(retryingDelay); } catch (InterruptedException ie) { throw new RuntimeException(ie); } if (++attempt == maxTries) throw e; } catch (Exception e) { if (dbKey(db2Endpoint).equals(currentDbKey)) { break; } lastException.set(e); unexpectedErrors.set(true); lastFailedBeforeFailover.set(Instant.now()); log.error("UNEXPECTED exception", e); if (reporter.failoverHappened) { errorOccuredAfterFailover.set(true); lastFailedCommandAt.set(Instant.now()); } } } return true; }, numberOfThreads, rateLimiterConfig); fakeApp.setKeepExecutingForSeconds(30); Thread t = new Thread(fakeApp); t.start(); stopRunningAt.set(System.currentTimeMillis() + 30000); log.info("Triggering issue on endpoint1"); try (Jedis jedis = new Jedis(endpoint1.getHostAndPort(), endpoint1.getClientConfigBuilder().build())) { jedis.clientPause(ENDPOINT_PAUSE_TIME_MS); } fakeApp.setAction(new TriggerActionResponse(null) { private long completeAt = System.currentTimeMillis() + 10000; @Override public boolean isCompleted(Duration checkInterval, Duration delayAfter, Duration timeout) { return System.currentTimeMillis() > completeAt; } }); log.info("Waiting for fake app to complete"); try { t.join(); } catch (InterruptedException e) { throw new RuntimeException(e); } log.info("Fake app completed"); MultiDbConnectionProvider provider = ClientTestUtil.getConnectionProvider(multiDbClient); ConnectionPool pool = provider.getDatabase(endpoint1.getHostAndPort()).getConnectionPool(); log.info("First connection pool state: active: {}, idle: {}", pool.getNumActive(), pool.getNumIdle()); log.info("Failover happened at: {}", reporter.failoverAt); log.info("Failback happened at: {}", reporter.failbackAt); assertEquals(0, pool.getNumActive()); assertTrue(fakeApp.capturedExceptions().isEmpty()); assertTrue(reporter.failoverHappened); if (errorOccuredAfterFailover.get()) { log.info("Last failed command at: {}", lastFailedCommandAt.get()); Duration fullFailoverTime = Duration.between(reporter.failoverAt, lastFailedCommandAt.get()); log.info("Full failover time: {} s", fullFailoverTime.getSeconds()); log.info("Last failed command exception: {}", lastException.get()); // assertTrue(reporter.failbackHappened); assertThat(fullFailoverTime.getSeconds(), Matchers.greaterThanOrEqualTo(minFailoverCompletionDuration)); assertThat(fullFailoverTime.getSeconds(), Matchers.lessThanOrEqualTo(maxFailoverCompletionDuration)); } else { log.info("No failed commands after failover!"); } if (lastFailedBeforeFailover.get() != null) { log.info("Last failed command before failover at: {}", lastFailedBeforeFailover.get()); } assertFalse(unexpectedErrors.get()); multiDbClient.close(); } private String dbKey(Endpoint endpoint) { return endpoint.getHost() + ":" + endpoint.getPort(); } private static void ensureEndpointAvailability(HostAndPort endpoint, JedisClientConfig config) { await().atMost(Duration.ofSeconds(ENDPOINT_PAUSE_TIME_MS)).until(() -> { try (Jedis jedis = new Jedis(endpoint, config)) { return "PONG".equals(jedis.ping()); } catch (Exception e) { log.info("Waiting for endpoint {} to become available...", endpoint); return false; } }); } } ================================================ FILE: src/test/java/redis/clients/jedis/mcf/ConnectionInitializationContextTest.java ================================================ package redis.clients.jedis.mcf; import static org.junit.jupiter.api.Assertions.*; import static org.mockito.Mockito.*; import java.util.HashMap; import java.util.Map; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Nested; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; import org.mockito.Mock; import org.mockito.junit.jupiter.MockitoExtension; import redis.clients.jedis.Endpoint; import redis.clients.jedis.HostAndPort; import redis.clients.jedis.mcf.InitializationPolicy.Decision; /** * Unit tests for {@link ConnectionInitializationContext}. */ @ExtendWith(MockitoExtension.class) public class ConnectionInitializationContextTest { @Mock private HealthStatusManager healthStatusManager; @Mock private MultiDbConnectionProvider.Database database1; @Mock private MultiDbConnectionProvider.Database database2; @Mock private MultiDbConnectionProvider.Database database3; private Endpoint endpoint1; private Endpoint endpoint2; private Endpoint endpoint3; private Map databases; @BeforeEach void setUp() { endpoint1 = new HostAndPort("fake", 6379); endpoint2 = new HostAndPort("fake", 6380); endpoint3 = new HostAndPort("fake", 6381); databases = new HashMap<>(); } @Nested @DisplayName("Context Construction Tests") class ContextConstructionTests { @Test @DisplayName("Should count healthy endpoints as available") void shouldCountHealthyAsAvailable() { databases.put(endpoint1, database1); databases.put(endpoint2, database2); when(healthStatusManager.hasHealthCheck(endpoint1)).thenReturn(true); when(healthStatusManager.getHealthStatus(endpoint1)).thenReturn(HealthStatus.HEALTHY); when(healthStatusManager.hasHealthCheck(endpoint2)).thenReturn(true); when(healthStatusManager.getHealthStatus(endpoint2)).thenReturn(HealthStatus.HEALTHY); ConnectionInitializationContext ctx = new ConnectionInitializationContext(databases, healthStatusManager); assertEquals(2, ctx.getAvailableConnections()); assertEquals(0, ctx.getFailedConnections()); assertEquals(0, ctx.getPendingConnections()); } @Test @DisplayName("Should count unhealthy endpoints as failed") void shouldCountUnhealthyAsFailed() { databases.put(endpoint1, database1); databases.put(endpoint2, database2); when(healthStatusManager.hasHealthCheck(endpoint1)).thenReturn(true); when(healthStatusManager.getHealthStatus(endpoint1)).thenReturn(HealthStatus.UNHEALTHY); when(healthStatusManager.hasHealthCheck(endpoint2)).thenReturn(true); when(healthStatusManager.getHealthStatus(endpoint2)).thenReturn(HealthStatus.UNHEALTHY); ConnectionInitializationContext ctx = new ConnectionInitializationContext(databases, healthStatusManager); assertEquals(0, ctx.getAvailableConnections()); assertEquals(2, ctx.getFailedConnections()); assertEquals(0, ctx.getPendingConnections()); } @Test @DisplayName("Should count unknown status as pending") void shouldCountUnknownAsPending() { databases.put(endpoint1, database1); when(healthStatusManager.hasHealthCheck(endpoint1)).thenReturn(true); when(healthStatusManager.getHealthStatus(endpoint1)).thenReturn(HealthStatus.UNKNOWN); ConnectionInitializationContext ctx = new ConnectionInitializationContext(databases, healthStatusManager); assertEquals(0, ctx.getAvailableConnections()); assertEquals(0, ctx.getFailedConnections()); assertEquals(1, ctx.getPendingConnections()); } @Test @DisplayName("Should count endpoints without health check as available") void shouldCountNoHealthCheckAsAvailable() { databases.put(endpoint1, database1); databases.put(endpoint2, database2); when(healthStatusManager.hasHealthCheck(endpoint1)).thenReturn(false); when(healthStatusManager.hasHealthCheck(endpoint2)).thenReturn(false); ConnectionInitializationContext ctx = new ConnectionInitializationContext(databases, healthStatusManager); assertEquals(2, ctx.getAvailableConnections()); assertEquals(0, ctx.getFailedConnections()); assertEquals(0, ctx.getPendingConnections()); } @Test @DisplayName("Should handle mixed health check states") void shouldHandleMixedStates() { databases.put(endpoint1, database1); databases.put(endpoint2, database2); databases.put(endpoint3, database3); when(healthStatusManager.hasHealthCheck(endpoint1)).thenReturn(true); when(healthStatusManager.getHealthStatus(endpoint1)).thenReturn(HealthStatus.HEALTHY); when(healthStatusManager.hasHealthCheck(endpoint2)).thenReturn(true); when(healthStatusManager.getHealthStatus(endpoint2)).thenReturn(HealthStatus.UNHEALTHY); when(healthStatusManager.hasHealthCheck(endpoint3)).thenReturn(true); when(healthStatusManager.getHealthStatus(endpoint3)).thenReturn(HealthStatus.UNKNOWN); ConnectionInitializationContext ctx = new ConnectionInitializationContext(databases, healthStatusManager); assertEquals(1, ctx.getAvailableConnections()); assertEquals(1, ctx.getFailedConnections()); assertEquals(1, ctx.getPendingConnections()); } @Test @DisplayName("Should handle empty database map") void shouldHandleEmptyDatabaseMap() { ConnectionInitializationContext ctx = new ConnectionInitializationContext(databases, healthStatusManager); assertEquals(0, ctx.getAvailableConnections()); assertEquals(0, ctx.getFailedConnections()); assertEquals(0, ctx.getPendingConnections()); } @Test @DisplayName("Should handle mix of health check enabled and disabled endpoints") void shouldHandleMixedHealthCheckConfiguration() { databases.put(endpoint1, database1); databases.put(endpoint2, database2); databases.put(endpoint3, database3); // endpoint1: health check enabled, healthy when(healthStatusManager.hasHealthCheck(endpoint1)).thenReturn(true); when(healthStatusManager.getHealthStatus(endpoint1)).thenReturn(HealthStatus.HEALTHY); // endpoint2: health check disabled - should be counted as available when(healthStatusManager.hasHealthCheck(endpoint2)).thenReturn(false); // endpoint3: health check enabled, pending when(healthStatusManager.hasHealthCheck(endpoint3)).thenReturn(true); when(healthStatusManager.getHealthStatus(endpoint3)).thenReturn(HealthStatus.UNKNOWN); ConnectionInitializationContext ctx = new ConnectionInitializationContext(databases, healthStatusManager); assertEquals(2, ctx.getAvailableConnections()); // endpoint1 + endpoint2 assertEquals(0, ctx.getFailedConnections()); assertEquals(1, ctx.getPendingConnections()); // endpoint3 } } @Nested @DisplayName("ConformsTo Policy Evaluation Tests") class ConformsToPolicyTests { @Test @DisplayName("Should delegate to policy evaluate method") void shouldDelegateToPolicy() { databases.put(endpoint1, database1); when(healthStatusManager.hasHealthCheck(endpoint1)).thenReturn(true); when(healthStatusManager.getHealthStatus(endpoint1)).thenReturn(HealthStatus.HEALTHY); ConnectionInitializationContext ctx = new ConnectionInitializationContext(databases, healthStatusManager); assertEquals(Decision.SUCCESS, ctx.conformsTo(InitializationPolicy.BuiltIn.ONE_AVAILABLE)); assertEquals(Decision.SUCCESS, ctx.conformsTo(InitializationPolicy.BuiltIn.ALL_AVAILABLE)); assertEquals(Decision.SUCCESS, ctx.conformsTo(InitializationPolicy.BuiltIn.MAJORITY_AVAILABLE)); } @Test @DisplayName("Should return CONTINUE for ONE_AVAILABLE when all pending") void shouldReturnContinueWhenAllPending() { databases.put(endpoint1, database1); databases.put(endpoint2, database2); when(healthStatusManager.hasHealthCheck(endpoint1)).thenReturn(true); when(healthStatusManager.getHealthStatus(endpoint1)).thenReturn(HealthStatus.UNKNOWN); when(healthStatusManager.hasHealthCheck(endpoint2)).thenReturn(true); when(healthStatusManager.getHealthStatus(endpoint2)).thenReturn(HealthStatus.UNKNOWN); ConnectionInitializationContext ctx = new ConnectionInitializationContext(databases, healthStatusManager); assertEquals(Decision.CONTINUE, ctx.conformsTo(InitializationPolicy.BuiltIn.ONE_AVAILABLE)); } @Test @DisplayName("Should return FAIL for ALL_AVAILABLE when any failed") void shouldReturnFailWhenAnyFailed() { databases.put(endpoint1, database1); databases.put(endpoint2, database2); when(healthStatusManager.hasHealthCheck(endpoint1)).thenReturn(true); when(healthStatusManager.getHealthStatus(endpoint1)).thenReturn(HealthStatus.HEALTHY); when(healthStatusManager.hasHealthCheck(endpoint2)).thenReturn(true); when(healthStatusManager.getHealthStatus(endpoint2)).thenReturn(HealthStatus.UNHEALTHY); ConnectionInitializationContext ctx = new ConnectionInitializationContext(databases, healthStatusManager); assertEquals(Decision.FAIL, ctx.conformsTo(InitializationPolicy.BuiltIn.ALL_AVAILABLE)); } } @Nested @DisplayName("ToString Tests") class ToStringTests { @Test @DisplayName("Should include counts in toString") void shouldIncludeCountsInToString() { databases.put(endpoint1, database1); databases.put(endpoint2, database2); databases.put(endpoint3, database3); when(healthStatusManager.hasHealthCheck(endpoint1)).thenReturn(true); when(healthStatusManager.getHealthStatus(endpoint1)).thenReturn(HealthStatus.HEALTHY); when(healthStatusManager.hasHealthCheck(endpoint2)).thenReturn(true); when(healthStatusManager.getHealthStatus(endpoint2)).thenReturn(HealthStatus.UNHEALTHY); when(healthStatusManager.hasHealthCheck(endpoint3)).thenReturn(true); when(healthStatusManager.getHealthStatus(endpoint3)).thenReturn(HealthStatus.UNKNOWN); ConnectionInitializationContext ctx = new ConnectionInitializationContext(databases, healthStatusManager); String result = ctx.toString(); assertTrue(result.contains("available=1")); assertTrue(result.contains("failed=1")); assertTrue(result.contains("pending=1")); } } } ================================================ FILE: src/test/java/redis/clients/jedis/mcf/DatabaseEvaluateThresholdsTest.java ================================================ package redis.clients.jedis.mcf; import static org.junit.jupiter.api.Assertions.*; import static org.mockito.ArgumentMatchers.*; import static org.mockito.Mockito.*; import io.github.resilience4j.circuitbreaker.CircuitBreaker; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.CsvSource; import redis.clients.jedis.DefaultJedisClientConfig; import redis.clients.jedis.HostAndPort; import redis.clients.jedis.MultiDbConfig; import redis.clients.jedis.mcf.MultiDbConnectionProvider.Database; /** * Tests for circuit breaker thresholds: both failure-rate threshold and minimum number of failures * must be exceeded to trigger failover. Uses a real CircuitBreaker and real Retry, but mocks the * provider and {@link Database} wiring to avoid network I/O. */ public class DatabaseEvaluateThresholdsTest { private MultiDbConnectionProvider provider; private Database database; private CircuitBreaker circuitBreaker; private CircuitBreaker.Metrics metrics; @BeforeEach public void setup() { provider = mock(MultiDbConnectionProvider.class); database = mock(Database.class); circuitBreaker = mock(CircuitBreaker.class); metrics = mock(CircuitBreaker.Metrics.class); when(database.getCircuitBreaker()).thenReturn(circuitBreaker); when(circuitBreaker.getMetrics()).thenReturn(metrics); when(circuitBreaker.getState()).thenReturn(CircuitBreaker.State.CLOSED); // Configure the mock to call the real evaluateThresholds method doCallRealMethod().when(database).evaluateThresholds(anyBoolean()); } /** * Below minimum failures; even if all calls are failures, failover should NOT trigger. Note: The * isThresholdsExceeded method adds +1 to account for the current failing call, so we set * failures=1 which becomes 2 with +1, still below minFailures=3. */ @Test public void belowMinFailures_doesNotFailover() { when(database.getCircuitBreakerMinNumOfFailures()).thenReturn(3); when(metrics.getNumberOfFailedCalls()).thenReturn(1); // +1 becomes 2, still < 3 when(metrics.getNumberOfSuccessfulCalls()).thenReturn(0); when(database.getCircuitBreakerFailureRateThreshold()).thenReturn(50.0f); when(circuitBreaker.getState()).thenReturn(CircuitBreaker.State.CLOSED); database.evaluateThresholds(false); verify(circuitBreaker, never()).transitionToOpenState(); verify(provider, never()).switchToHealthyDatabase(any(), any()); } /** * Reaching minFailures and exceeding failure rate threshold should trigger circuit breaker to * OPEN state. Note: The isThresholdsExceeded method adds +1 to account for the current failing * call, so we set failures=2 which becomes 3 with +1, reaching minFailures=3. */ @Test public void minFailuresAndRateExceeded_triggersOpenState() { when(database.getCircuitBreakerMinNumOfFailures()).thenReturn(3); when(metrics.getNumberOfFailedCalls()).thenReturn(2); // +1 becomes 3, reaching minFailures when(metrics.getNumberOfSuccessfulCalls()).thenReturn(0); when(database.getCircuitBreakerFailureRateThreshold()).thenReturn(50.0f); when(circuitBreaker.getState()).thenReturn(CircuitBreaker.State.CLOSED); database.evaluateThresholds(false); verify(circuitBreaker, times(1)).transitionToOpenState(); } /** * Even after reaching minFailures, if failure rate is below threshold, do not failover. Note: The * isThresholdsExceeded method adds +1 to account for the current failing call, so we set * failures=2 which becomes 3 with +1, reaching minFailures=3. Rate calculation: (3 failures) / (3 * failures + 3 successes) = 50% < 80% threshold. */ @Test public void rateBelowThreshold_doesNotFailover() { when(database.getCircuitBreakerMinNumOfFailures()).thenReturn(3); when(metrics.getNumberOfSuccessfulCalls()).thenReturn(3); when(metrics.getNumberOfFailedCalls()).thenReturn(2); // +1 becomes 3, rate = 3/(3+3) = 50% when(database.getCircuitBreakerFailureRateThreshold()).thenReturn(80.0f); when(circuitBreaker.getState()).thenReturn(CircuitBreaker.State.CLOSED); database.evaluateThresholds(false); verify(circuitBreaker, never()).transitionToOpenState(); verify(provider, never()).switchToHealthyDatabase(any(), any()); } @Test public void providerBuilder_zeroRate_mapsToHundredAndHugeMinCalls() { MultiDbConfig.Builder cfgBuilder = MultiDbConfig .builder(java.util.Arrays.asList(MultiDbConfig.DatabaseConfig .builder(new HostAndPort("dummy", 6379), DefaultJedisClientConfig.builder().build()) .healthCheckEnabled(false).build())); cfgBuilder.failureDetector(MultiDbConfig.CircuitBreakerConfig.builder() .failureRateThreshold(0.0f).minNumOfFailures(3).slidingWindowSize(10).build()); MultiDbConfig mcc = cfgBuilder.build(); CircuitBreakerThresholdsAdapter adapter = new CircuitBreakerThresholdsAdapter(mcc); assertEquals(100.0f, adapter.getFailureRateThreshold(), 0.0001f); assertEquals(Integer.MAX_VALUE, adapter.getMinimumNumberOfCalls()); } @ParameterizedTest @CsvSource({ // Format: "minFails, rate%, success, fails, lastFailRecorded, expected" // === Basic threshold crossing cases === "0, 1.0, 0, 1, false, true", // +1 = 2 fails, rate=100% >= 1%, min=0 -> trigger "0, 1.0, 0, 1, true, true", // +0 = 1 fails, rate=100% >= 1%, min=0 -> trigger "1, 1.0, 0, 0, false, true", // +1 = 1 fails, rate=100% >= 1%, min=1 -> trigger "1, 1.0, 0, 0, true, false", // +0 = 0 fails, 0 < 1 min -> no trigger "3, 50.0, 0, 2, false, true", // +1 = 3 fails, rate=100% >= 50%, min=3 -> trigger "3, 50.0, 0, 2, true, false", // +0 = 2 fails, 2 < 3 min -> no trigger // === Rate threshold boundary cases === "1, 100.0, 0, 0, false, true", // +1 = 1 fails, rate=100% >= 100%, min=1 -> trigger "1, 100.0, 0, 0, true, false", // +0 = 0 fails, 0 < 1 min -> no trigger "0, 100.0, 99, 1, false, false", // +1 = 2 fails, rate=1.98% < 100% -> no trigger "0, 100.0, 99, 1, true, false", // +0 = 1 fails, rate=1.0% < 100% -> no trigger "0, 1.0, 99, 1, false, true", // +1 = 2 fails, rate=1.98% >= 1%, min=0 -> trigger "0, 1.0, 99, 1, true, true", // +0 = 1 fails, rate=1.0% >= 1%, min=0 -> trigger // === Zero rate threshold (always trigger if min failures met) === "1, 0.0, 0, 0, false, true", // +1 = 1 fails, rate=100% >= 0%, min=1 -> trigger "1, 0.0, 0, 0, true, false", // +0 = 0 fails, 0 < 1 min -> no trigger "1, 0.0, 100, 0, false, true", // +1 = 1 fails, rate=0.99% >= 0%, min=1 -> trigger "1, 0.0, 100, 0, true, false", // +0 = 0 fails, 0 < 1 min -> no trigger // === High minimum failures cases === "3, 50.0, 3, 1, false, false", // +1 = 2 fails, 2 < 3 min -> no trigger "3, 50.0, 3, 1, true, false", // +0 = 1 fails, 1 < 3 min -> no trigger "1000, 1.0, 198, 2, false, false", // +1 = 3 fails, 3 < 1000 min -> no trigger "1000, 1.0, 198, 2, true, false", // +0 = 2 fails, 2 < 1000 min -> no trigger // === Corner cases === "0, 50.0, 0, 0, false, true", // +1 = 1 fails, rate=100% >= 50%, min=0 -> trigger "0, 50.0, 0, 0, true, false", // +0 = 0 fails, no calls -> no trigger "1, 50.0, 1, 1, false, true", // +1 = 2 fails, rate=66.7% >= 50%, min=1 -> trigger "1, 50.0, 1, 1, true, true", // +0 = 1 fails, rate=50% >= 50%, min=1 -> trigger "2, 33.0, 2, 1, false, true", // +1 = 2 fails, rate=50% >= 33%, min=2 -> trigger "2, 33.0, 2, 1, true, false", // +0 = 1 fails, 1 < 2 min -> no trigger "5, 20.0, 20, 4, false, true", // +1 = 5 fails, rate=20% >= 20%, min=5 -> trigger "5, 20.0, 20, 4, true, false", // +0 = 4 fails, 4 < 5 min -> no trigger "3, 75.0, 1, 2, false, true", // +1 = 3 fails, rate=75% >= 75%, min=3 -> trigger "3, 75.0, 1, 2, true, false", // +0 = 2 fails, 2 < 3 min -> no trigger }) public void thresholdMatrix(int minFailures, float ratePercent, int successes, int failures, boolean lastFailRecorded, boolean expectOpenState) { when(database.getCircuitBreakerMinNumOfFailures()).thenReturn(minFailures); when(metrics.getNumberOfSuccessfulCalls()).thenReturn(successes); when(metrics.getNumberOfFailedCalls()).thenReturn(failures); when(database.getCircuitBreakerFailureRateThreshold()).thenReturn(ratePercent); when(circuitBreaker.getState()).thenReturn(CircuitBreaker.State.CLOSED); database.evaluateThresholds(lastFailRecorded); if (expectOpenState) { verify(circuitBreaker, times(1)).transitionToOpenState(); } else { verify(circuitBreaker, never()).transitionToOpenState(); } } } ================================================ FILE: src/test/java/redis/clients/jedis/mcf/DefaultValuesTest.java ================================================ package redis.clients.jedis.mcf; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertNotNull; import java.time.Duration; import org.junit.jupiter.api.Test; import redis.clients.jedis.DefaultJedisClientConfig; import redis.clients.jedis.HostAndPort; import redis.clients.jedis.JedisClientConfig; import redis.clients.jedis.MultiDbConfig; public class DefaultValuesTest { HostAndPort fakeEndpoint = new HostAndPort("fake", 6379); JedisClientConfig config = DefaultJedisClientConfig.builder().build(); @Test void testDefaultValuesInConfig() { MultiDbConfig.DatabaseConfig databaseConfig = MultiDbConfig.DatabaseConfig .builder(fakeEndpoint, config).build(); MultiDbConfig multiConfig = new MultiDbConfig.Builder( new MultiDbConfig.DatabaseConfig[] { databaseConfig }).build(); // check for grace period assertEquals(60000, multiConfig.getGracePeriod()); // check for database config assertEquals(databaseConfig, multiConfig.getDatabaseConfigs()[0]); // check healthchecks enabled assertNotNull(databaseConfig.getHealthCheckStrategySupplier()); // check default healthcheck strategy is PingStrategy assertEquals(PingStrategy.DEFAULT, databaseConfig.getHealthCheckStrategySupplier()); // check number of probes assertEquals(3, databaseConfig.getHealthCheckStrategySupplier().get(fakeEndpoint, config).getNumProbes()); assertEquals(500, databaseConfig.getHealthCheckStrategySupplier().get(fakeEndpoint, config) .getDelayInBetweenProbes()); assertEquals(ProbingPolicy.BuiltIn.ALL_SUCCESS, databaseConfig.getHealthCheckStrategySupplier().get(fakeEndpoint, config).getPolicy()); // check health check interval assertEquals(5000, databaseConfig.getHealthCheckStrategySupplier().get(fakeEndpoint, config).getInterval()); // check lag aware tolerance LagAwareStrategy.Config lagAwareConfig = LagAwareStrategy.Config .builder(fakeEndpoint, config.getCredentialsProvider()).build(); assertEquals(Duration.ofMillis(5000), lagAwareConfig.getAvailabilityLagTolerance()); // check CB failure rate threshold assertEquals(10, multiConfig.getFailureDetector().getFailureRateThreshold()); // check CB sliding window size assertEquals(2, multiConfig.getFailureDetector().getSlidingWindowSize()); // check CB number of failures threshold assertEquals(1000, multiConfig.getFailureDetector().getMinNumOfFailures()); // check failback check interval assertEquals(120000, multiConfig.getFailbackCheckInterval()); // check failover max attempts before give up assertEquals(10, multiConfig.getMaxNumFailoverAttempts()); // check delay between failover attempts assertEquals(12000, multiConfig.getDelayInBetweenFailoverAttempts()); // check initialization policy assertEquals(InitializationPolicy.BuiltIn.MAJORITY_AVAILABLE, multiConfig.getInitializationPolicy()); } } ================================================ FILE: src/test/java/redis/clients/jedis/mcf/FailbackMechanismIntegrationTest.java ================================================ package redis.clients.jedis.mcf; import static org.awaitility.Awaitility.await; import static org.junit.jupiter.api.Assertions.*; import static org.mockito.Mockito.*; import java.time.Duration; import org.awaitility.Durations; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; import org.mockito.MockedConstruction; import org.mockito.junit.jupiter.MockitoExtension; import redis.clients.jedis.Connection; import redis.clients.jedis.DefaultJedisClientConfig; import redis.clients.jedis.HostAndPort; import redis.clients.jedis.JedisClientConfig; import redis.clients.jedis.MultiDbConfig; @ExtendWith(MockitoExtension.class) class FailbackMechanismIntegrationTest { private HostAndPort endpoint1; private HostAndPort endpoint2; private HostAndPort endpoint3; private JedisClientConfig clientConfig; private static Duration FIFTY_MILLISECONDS = Duration.ofMillis(50); @BeforeEach void setUp() { endpoint1 = new HostAndPort("localhost", 6379); endpoint2 = new HostAndPort("localhost", 6380); endpoint3 = new HostAndPort("localhost", 6381); clientConfig = DefaultJedisClientConfig.builder().build(); } private MockedConstruction mockPool() { Connection mockConnection = mock(Connection.class); lenient().when(mockConnection.ping()).thenReturn(true); return mockConstruction(TrackingConnectionPool.class, (mock, context) -> { when(mock.getResource()).thenReturn(mockConnection); doNothing().when(mock).close(); }); } @Test void testFailbackDisabledDoesNotPerformFailback() throws InterruptedException { try (MockedConstruction mockedPool = mockPool()) { // Create databases with different weights MultiDbConfig.DatabaseConfig database1 = MultiDbConfig.DatabaseConfig .builder(endpoint1, clientConfig).weight(1.0f).healthCheckEnabled(false).build(); // Lower // weight MultiDbConfig.DatabaseConfig database2 = MultiDbConfig.DatabaseConfig .builder(endpoint2, clientConfig).weight(2.0f) // Higher weight .healthCheckEnabled(false).build(); MultiDbConfig config = new MultiDbConfig.Builder( new MultiDbConfig.DatabaseConfig[] { database1, database2 }).failbackSupported(false) // Disabled .failbackCheckInterval(100) // Short interval for testing .build(); try (MultiDbConnectionProvider provider = new MultiDbConnectionProvider(config)) { // Initially, database2 should be active (highest weight) assertEquals(provider.getDatabase(endpoint2), provider.getDatabase()); // Make database2 unhealthy to force failover to database1 MultiDbConnectionProviderHelper.onHealthStatusChange(provider, endpoint2, HealthStatus.HEALTHY, HealthStatus.UNHEALTHY); // Should now be on database1 (only healthy option) assertEquals(provider.getDatabase(endpoint1), provider.getDatabase()); // Make database2 healthy again (higher weight - would normally trigger failback) MultiDbConnectionProviderHelper.onHealthStatusChange(provider, endpoint2, HealthStatus.UNHEALTHY, HealthStatus.HEALTHY); // Wait longer than failback interval // Should still be on database1 since failback is disabled await().atMost(Durations.FIVE_HUNDRED_MILLISECONDS).pollInterval(FIFTY_MILLISECONDS) .until(() -> provider.getDatabase(endpoint1) == provider.getDatabase()); } } } @Test void testFailbackToHigherWeightDatabase() throws InterruptedException { try (MockedConstruction mockedPool = mockPool()) { // Create databases with different weights MultiDbConfig.DatabaseConfig database1 = MultiDbConfig.DatabaseConfig .builder(endpoint1, clientConfig).weight(2.0f) // Higher weight .healthCheckEnabled(false).build(); MultiDbConfig.DatabaseConfig database2 = MultiDbConfig.DatabaseConfig .builder(endpoint2, clientConfig).weight(1.0f) // Lower weight .healthCheckEnabled(false).build(); MultiDbConfig config = new MultiDbConfig.Builder( new MultiDbConfig.DatabaseConfig[] { database1, database2 }).failbackSupported(true) .failbackCheckInterval(100) // Short interval for testing .gracePeriod(100).build(); try (MultiDbConnectionProvider provider = new MultiDbConnectionProvider(config)) { // Initially, database1 should be active (highest weight) assertEquals(provider.getDatabase(endpoint1), provider.getDatabase()); // Make database1 unhealthy to force failover to database2 MultiDbConnectionProviderHelper.onHealthStatusChange(provider, endpoint1, HealthStatus.HEALTHY, HealthStatus.UNHEALTHY); // Should now be on database2 (lower weight, but only healthy option) assertEquals(provider.getDatabase(endpoint2), provider.getDatabase()); // Make database1 healthy again MultiDbConnectionProviderHelper.onHealthStatusChange(provider, endpoint1, HealthStatus.UNHEALTHY, HealthStatus.HEALTHY); // Wait for failback check interval + some buffer // Should have failed back to database1 (higher weight) await().atMost(Durations.FIVE_HUNDRED_MILLISECONDS).pollInterval(FIFTY_MILLISECONDS) .until(() -> provider.getDatabase(endpoint1) == provider.getDatabase()); } } } @Test void testNoFailbackToLowerWeightDatabase() throws InterruptedException { try (MockedConstruction mockedPool = mockPool()) { // Create three databases with different weights to properly test no failback to lower weight MultiDbConfig.DatabaseConfig database1 = MultiDbConfig.DatabaseConfig .builder(endpoint1, clientConfig).weight(1.0f) // Lowest weight .healthCheckEnabled(false).build(); MultiDbConfig.DatabaseConfig database2 = MultiDbConfig.DatabaseConfig .builder(endpoint2, clientConfig).weight(2.0f) // Medium weight .healthCheckEnabled(false).build(); MultiDbConfig.DatabaseConfig database3 = MultiDbConfig.DatabaseConfig .builder(endpoint3, clientConfig).weight(3.0f) // Highest weight .healthCheckEnabled(false).build(); MultiDbConfig config = new MultiDbConfig.Builder( new MultiDbConfig.DatabaseConfig[] { database1, database2, database3 }) .failbackSupported(true).failbackCheckInterval(100).build(); try (MultiDbConnectionProvider provider = new MultiDbConnectionProvider(config)) { // Initially, database3 should be active (highest weight) assertEquals(provider.getDatabase(endpoint3), provider.getDatabase()); // Make database3 unhealthy to force failover to database2 (medium weight) MultiDbConnectionProviderHelper.onHealthStatusChange(provider, endpoint3, HealthStatus.HEALTHY, HealthStatus.UNHEALTHY); // Should now be on database2 (highest weight among healthy databases) assertEquals(provider.getDatabase(endpoint2), provider.getDatabase()); // Make database1 (lowest weight) healthy - this should NOT trigger failback // since we don't failback to lower weight databases MultiDbConnectionProviderHelper.onHealthStatusChange(provider, endpoint1, HealthStatus.UNHEALTHY, HealthStatus.HEALTHY); // Wait for failback check interval // Should still be on database2 (no failback to lower weight database1) await().atMost(Durations.FIVE_HUNDRED_MILLISECONDS).pollInterval(FIFTY_MILLISECONDS) .until(() -> provider.getDatabase(endpoint2) == provider.getDatabase()); } } } @Test void testFailbackToHigherWeightDatabaseImmediately() throws InterruptedException { try (MockedConstruction mockedPool = mockPool()) { MultiDbConfig.DatabaseConfig database1 = MultiDbConfig.DatabaseConfig .builder(endpoint1, clientConfig).weight(2.0f).healthCheckEnabled(false).build(); // Higher // weight MultiDbConfig.DatabaseConfig database2 = MultiDbConfig.DatabaseConfig .builder(endpoint2, clientConfig).weight(1.0f).healthCheckEnabled(false).build(); // Lower // weight MultiDbConfig config = new MultiDbConfig.Builder( new MultiDbConfig.DatabaseConfig[] { database1, database2 }).failbackSupported(true) .failbackCheckInterval(100).gracePeriod(50).build(); try (MultiDbConnectionProvider provider = new MultiDbConnectionProvider(config)) { // Initially, database1 should be active (highest weight) assertEquals(provider.getDatabase(endpoint1), provider.getDatabase()); // Make database1 unhealthy to force failover to database2 MultiDbConnectionProviderHelper.onHealthStatusChange(provider, endpoint1, HealthStatus.HEALTHY, HealthStatus.UNHEALTHY); // Should now be on database2 (only healthy option) assertEquals(provider.getDatabase(endpoint2), provider.getDatabase()); // Make database1 healthy again MultiDbConnectionProviderHelper.onHealthStatusChange(provider, endpoint1, HealthStatus.UNHEALTHY, HealthStatus.HEALTHY); // Wait for failback check // Should have failed back to database1 immediately (higher weight, no stability period // required) await().atMost(Durations.TWO_HUNDRED_MILLISECONDS).pollInterval(FIFTY_MILLISECONDS) .until(() -> provider.getDatabase(endpoint1) == provider.getDatabase()); } } } @Test void testUnhealthyDatabaseCancelsFailback() throws InterruptedException { try (MockedConstruction mockedPool = mockPool()) { MultiDbConfig.DatabaseConfig database1 = MultiDbConfig.DatabaseConfig .builder(endpoint1, clientConfig).weight(2.0f).healthCheckEnabled(false).build(); // Higher // weight MultiDbConfig.DatabaseConfig database2 = MultiDbConfig.DatabaseConfig .builder(endpoint2, clientConfig).weight(1.0f).healthCheckEnabled(false).build(); // Lower // weight MultiDbConfig config = new MultiDbConfig.Builder( new MultiDbConfig.DatabaseConfig[] { database1, database2 }).failbackSupported(true) .failbackCheckInterval(200).build(); try (MultiDbConnectionProvider provider = new MultiDbConnectionProvider(config)) { // Initially, database1 should be active (highest weight) assertEquals(provider.getDatabase(endpoint1), provider.getDatabase()); // Make database1 unhealthy to force failover to database2 MultiDbConnectionProviderHelper.onHealthStatusChange(provider, endpoint1, HealthStatus.HEALTHY, HealthStatus.UNHEALTHY); // Should now be on database2 (only healthy option) assertEquals(provider.getDatabase(endpoint2), provider.getDatabase()); // Make database1 healthy again (should trigger failback attempt) MultiDbConnectionProviderHelper.onHealthStatusChange(provider, endpoint1, HealthStatus.UNHEALTHY, HealthStatus.HEALTHY); // Wait a bit Thread.sleep(100); // Make database1 unhealthy again before failback completes MultiDbConnectionProviderHelper.onHealthStatusChange(provider, endpoint1, HealthStatus.HEALTHY, HealthStatus.UNHEALTHY); // Wait past the original failback interval // Should still be on database2 (failback was cancelled due to database1 becoming unhealthy) await().atMost(Durations.TWO_HUNDRED_MILLISECONDS).pollInterval(FIFTY_MILLISECONDS) .until(() -> provider.getDatabase(endpoint2) == provider.getDatabase()); } } } @Test void testMultipleDatabaseFailbackPriority() throws InterruptedException { try (MockedConstruction mockedPool = mockPool()) { MultiDbConfig.DatabaseConfig database1 = MultiDbConfig.DatabaseConfig .builder(endpoint1, clientConfig).weight(1.0f).healthCheckEnabled(false).build(); // Lowest // weight MultiDbConfig.DatabaseConfig database2 = MultiDbConfig.DatabaseConfig .builder(endpoint2, clientConfig).weight(2.0f).healthCheckEnabled(false).build(); // Medium // weight MultiDbConfig.DatabaseConfig database3 = MultiDbConfig.DatabaseConfig .builder(endpoint3, clientConfig).weight(3.0f) // Highest weight .healthCheckEnabled(false).build(); MultiDbConfig config = new MultiDbConfig.Builder( new MultiDbConfig.DatabaseConfig[] { database1, database2, database3 }) .failbackSupported(true).failbackCheckInterval(100).gracePeriod(100).build(); try (MultiDbConnectionProvider provider = new MultiDbConnectionProvider(config)) { // Initially, database3 should be active (highest weight) assertEquals(provider.getDatabase(endpoint3), provider.getDatabase()); // Make database3 unhealthy to force failover to database2 (next highest weight) MultiDbConnectionProviderHelper.onHealthStatusChange(provider, endpoint3, HealthStatus.HEALTHY, HealthStatus.UNHEALTHY); // Should now be on database2 (highest weight among healthy databases) assertEquals(provider.getDatabase(endpoint2), provider.getDatabase()); // Make database3 healthy again MultiDbConnectionProviderHelper.onHealthStatusChange(provider, endpoint3, HealthStatus.UNHEALTHY, HealthStatus.HEALTHY); // Wait for failback // Should fail back to database3 (highest weight) await().atMost(Durations.FIVE_HUNDRED_MILLISECONDS).pollInterval(FIFTY_MILLISECONDS) .until(() -> provider.getDatabase(endpoint3) == provider.getDatabase()); } } } @Test void testGracePeriodDisablesDatabaseOnUnhealthy() throws InterruptedException { try (MockedConstruction mockedPool = mockPool()) { MultiDbConfig.DatabaseConfig database1 = MultiDbConfig.DatabaseConfig .builder(endpoint1, clientConfig).weight(1.0f).healthCheckEnabled(false).build(); // Lower // weight MultiDbConfig.DatabaseConfig database2 = MultiDbConfig.DatabaseConfig .builder(endpoint2, clientConfig).weight(2.0f).healthCheckEnabled(false).build(); // Higher // weight MultiDbConfig config = new MultiDbConfig.Builder( new MultiDbConfig.DatabaseConfig[] { database1, database2 }).failbackSupported(true) .failbackCheckInterval(100).gracePeriod(200) // 200ms grace // period .build(); try (MultiDbConnectionProvider provider = new MultiDbConnectionProvider(config)) { // Initially, database2 should be active (highest weight) assertEquals(provider.getDatabase(endpoint2), provider.getDatabase()); // Now make database2 unhealthy - it should be disabled for grace period MultiDbConnectionProviderHelper.onHealthStatusChange(provider, endpoint2, HealthStatus.HEALTHY, HealthStatus.UNHEALTHY); // Should failover to database1 assertEquals(provider.getDatabase(endpoint1), provider.getDatabase()); // Database2 should be in grace period assertTrue(provider.getDatabase(endpoint2).isInGracePeriod()); } } } @Test void testGracePeriodReEnablesDatabaseAfterPeriod() throws InterruptedException { try (MockedConstruction mockedPool = mockPool()) { MultiDbConfig.DatabaseConfig database1 = MultiDbConfig.DatabaseConfig .builder(endpoint1, clientConfig).weight(1.0f).healthCheckEnabled(false).build(); // Lower // weight MultiDbConfig.DatabaseConfig database2 = MultiDbConfig.DatabaseConfig .builder(endpoint2, clientConfig).weight(2.0f).healthCheckEnabled(false).build(); // Higher // weight MultiDbConfig config = new MultiDbConfig.Builder( new MultiDbConfig.DatabaseConfig[] { database1, database2 }).failbackSupported(true) .failbackCheckInterval(50) // Short interval for testing .gracePeriod(100) // Short grace period for testing .build(); try (MultiDbConnectionProvider provider = new MultiDbConnectionProvider(config)) { // Initially, database2 should be active (highest weight) assertEquals(provider.getDatabase(endpoint2), provider.getDatabase()); // Make database2 unhealthy to start grace period and force failover MultiDbConnectionProviderHelper.onHealthStatusChange(provider, endpoint2, HealthStatus.HEALTHY, HealthStatus.UNHEALTHY); // Should failover to database1 assertEquals(provider.getDatabase(endpoint1), provider.getDatabase()); // Database2 should be in grace period assertTrue(provider.getDatabase(endpoint2).isInGracePeriod()); // Make database2 healthy again while it's still in grace period MultiDbConnectionProviderHelper.onHealthStatusChange(provider, endpoint2, HealthStatus.UNHEALTHY, HealthStatus.HEALTHY); // Should still be on database1 because database2 is in grace period assertEquals(provider.getDatabase(endpoint1), provider.getDatabase()); // Wait for grace period to expire // Database2 should no longer be in grace period await().atMost(Durations.FIVE_HUNDRED_MILLISECONDS).pollInterval(FIFTY_MILLISECONDS) .until(() -> !provider.getDatabase(endpoint2).isInGracePeriod()); // Wait for failback check to run // Should now failback to database2 (higher weight) since grace period has expired await().atMost(Durations.FIVE_HUNDRED_MILLISECONDS).pollInterval(FIFTY_MILLISECONDS) .until(() -> provider.getDatabase(endpoint2) == provider.getDatabase()); } } } } ================================================ FILE: src/test/java/redis/clients/jedis/mcf/FailbackMechanismUnitTest.java ================================================ package redis.clients.jedis.mcf; import static org.junit.jupiter.api.Assertions.*; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; import org.mockito.junit.jupiter.MockitoExtension; import redis.clients.jedis.DefaultJedisClientConfig; import redis.clients.jedis.HostAndPort; import redis.clients.jedis.JedisClientConfig; import redis.clients.jedis.MultiDbConfig; @ExtendWith(MockitoExtension.class) class FailbackMechanismUnitTest { private HostAndPort endpoint1; private JedisClientConfig clientConfig; @BeforeEach void setUp() { endpoint1 = new HostAndPort("dummy", 6379); clientConfig = DefaultJedisClientConfig.builder().build(); } @Test void testFailbackCheckIntervalConfiguration() { // Test default value MultiDbConfig.DatabaseConfig databaseConfig = MultiDbConfig.DatabaseConfig .builder(endpoint1, clientConfig).healthCheckEnabled(false).build(); MultiDbConfig defaultConfig = new MultiDbConfig.Builder( new MultiDbConfig.DatabaseConfig[] { databaseConfig }).build(); assertEquals(120000, defaultConfig.getFailbackCheckInterval()); // Test custom value MultiDbConfig customConfig = new MultiDbConfig.Builder( new MultiDbConfig.DatabaseConfig[] { databaseConfig }).failbackCheckInterval(3000).build(); assertEquals(3000, customConfig.getFailbackCheckInterval()); } @Test void testFailbackSupportedConfiguration() { MultiDbConfig.DatabaseConfig databaseConfig = MultiDbConfig.DatabaseConfig .builder(endpoint1, clientConfig).healthCheckEnabled(false).build(); // Test default (should be true) MultiDbConfig defaultConfig = new MultiDbConfig.Builder( new MultiDbConfig.DatabaseConfig[] { databaseConfig }).build(); assertTrue(defaultConfig.isFailbackSupported()); // Test disabled MultiDbConfig disabledConfig = new MultiDbConfig.Builder( new MultiDbConfig.DatabaseConfig[] { databaseConfig }).failbackSupported(false).build(); assertFalse(disabledConfig.isFailbackSupported()); } @Test void testFailbackCheckIntervalValidation() { MultiDbConfig.DatabaseConfig databaseConfig = MultiDbConfig.DatabaseConfig .builder(endpoint1, clientConfig).healthCheckEnabled(false).build(); // Test zero interval (should be allowed) MultiDbConfig zeroConfig = new MultiDbConfig.Builder( new MultiDbConfig.DatabaseConfig[] { databaseConfig }).failbackCheckInterval(0).build(); assertEquals(0, zeroConfig.getFailbackCheckInterval()); // Test negative interval (should be allowed - implementation decision) MultiDbConfig negativeConfig = new MultiDbConfig.Builder( new MultiDbConfig.DatabaseConfig[] { databaseConfig }).failbackCheckInterval(-1000).build(); assertEquals(-1000, negativeConfig.getFailbackCheckInterval()); } @Test void testBuilderChaining() { MultiDbConfig.DatabaseConfig databaseConfig = MultiDbConfig.DatabaseConfig .builder(endpoint1, clientConfig).healthCheckEnabled(false).build(); // Test that builder methods can be chained MultiDbConfig config = new MultiDbConfig.Builder( new MultiDbConfig.DatabaseConfig[] { databaseConfig }).failbackSupported(true) .failbackCheckInterval(2000).retryOnFailover(true).build(); assertTrue(config.isFailbackSupported()); assertEquals(2000, config.getFailbackCheckInterval()); assertTrue(config.isRetryOnFailover()); } @Test void testGracePeriodConfiguration() { // Test default value MultiDbConfig.DatabaseConfig databaseConfig = MultiDbConfig.DatabaseConfig .builder(endpoint1, clientConfig).healthCheckEnabled(false).build(); MultiDbConfig defaultConfig = new MultiDbConfig.Builder( new MultiDbConfig.DatabaseConfig[] { databaseConfig }).build(); assertEquals(60000, defaultConfig.getGracePeriod()); // Test custom value MultiDbConfig customConfig = new MultiDbConfig.Builder( new MultiDbConfig.DatabaseConfig[] { databaseConfig }).gracePeriod(5000).build(); assertEquals(5000, customConfig.getGracePeriod()); } @Test void testGracePeriodValidation() { MultiDbConfig.DatabaseConfig databaseConfig = MultiDbConfig.DatabaseConfig .builder(endpoint1, clientConfig).healthCheckEnabled(false).build(); // Test zero grace period (should be allowed) MultiDbConfig zeroConfig = new MultiDbConfig.Builder( new MultiDbConfig.DatabaseConfig[] { databaseConfig }).gracePeriod(0).build(); assertEquals(0, zeroConfig.getGracePeriod()); // Test negative grace period (should be allowed - implementation decision) MultiDbConfig negativeConfig = new MultiDbConfig.Builder( new MultiDbConfig.DatabaseConfig[] { databaseConfig }).gracePeriod(-1000).build(); assertEquals(-1000, negativeConfig.getGracePeriod()); } @Test void testGracePeriodBuilderChaining() { MultiDbConfig.DatabaseConfig databaseConfig = MultiDbConfig.DatabaseConfig .builder(endpoint1, clientConfig).healthCheckEnabled(false).build(); // Test that builder methods can be chained MultiDbConfig config = new MultiDbConfig.Builder( new MultiDbConfig.DatabaseConfig[] { databaseConfig }).failbackSupported(true) .failbackCheckInterval(2000).gracePeriod(8000).retryOnFailover(true).build(); assertTrue(config.isFailbackSupported()); assertEquals(2000, config.getFailbackCheckInterval()); assertEquals(8000, config.getGracePeriod()); assertTrue(config.isRetryOnFailover()); } } ================================================ FILE: src/test/java/redis/clients/jedis/mcf/HealthCheckIntegrationTest.java ================================================ package redis.clients.jedis.mcf; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertTrue; import java.util.Arrays; import java.util.List; import java.util.concurrent.CountDownLatch; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicInteger; import java.util.function.Function; import java.util.stream.Collectors; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.Test; import redis.clients.jedis.EndpointConfig; import redis.clients.jedis.Endpoints; import redis.clients.jedis.JedisClientConfig; import redis.clients.jedis.MultiDbClient; import redis.clients.jedis.MultiDbConfig; import redis.clients.jedis.MultiDbConfig.DatabaseConfig; import redis.clients.jedis.MultiDbConfig.StrategySupplier; import redis.clients.jedis.UnifiedJedis; import redis.clients.jedis.mcf.ProbingPolicy.BuiltIn; import redis.clients.jedis.scenario.RecommendedSettings; public class HealthCheckIntegrationTest { private static EndpointConfig endpoint1; private static JedisClientConfig clientConfig; @BeforeAll public static void prepareEndpoints() { endpoint1 = Endpoints.getRedisEndpoint("standalone0"); clientConfig = endpoint1.getClientConfigBuilder() .socketTimeoutMillis(RecommendedSettings.DEFAULT_TIMEOUT_MS) .connectionTimeoutMillis(RecommendedSettings.DEFAULT_TIMEOUT_MS).build(); } @Test public void testDisableHealthCheck() { // No health check strategy supplier means health check is disabled MultiDbConfig multiDbConfig = getMCCF(null); try ( MultiDbClient customClient = MultiDbClient.builder().multiDbConfig(multiDbConfig).build()) { // Verify that the client can connect and execute commands String result = customClient.ping(); assertEquals("PONG", result); } } @Test public void testDefaultStrategySupplier() { // Create a default strategy supplier that creates PingStrategy instances MultiDbConfig.StrategySupplier defaultSupplier = (hostAndPort, jedisClientConfig) -> { return new PingStrategy(hostAndPort, jedisClientConfig); }; MultiDbConfig multiDbConfig = getMCCF(defaultSupplier); try ( MultiDbClient customClient = MultiDbClient.builder().multiDbConfig(multiDbConfig).build()) { // Verify that the client can connect and execute commands String result = customClient.ping(); assertEquals("PONG", result); } } @Test public void testCustomStrategySupplier() { // Create a StrategySupplier that uses the JedisClientConfig when available MultiDbConfig.StrategySupplier strategySupplier = (hostAndPort, jedisClientConfig) -> { return new TestHealthCheckStrategy(HealthCheckStrategy.Config.builder().interval(500) .timeout(500).numProbes(1).policy(BuiltIn.ANY_SUCCESS).build(), (endpoint) -> { // Create connection per health check to avoid resource leak try (UnifiedJedis pinger = new UnifiedJedis(hostAndPort, jedisClientConfig)) { String result = pinger.ping(); return "PONG".equals(result) ? HealthStatus.HEALTHY : HealthStatus.UNHEALTHY; } catch (Exception e) { return HealthStatus.UNHEALTHY; } }); }; MultiDbConfig multiDbConfig = getMCCF(strategySupplier); try ( MultiDbClient customClient = MultiDbClient.builder().multiDbConfig(multiDbConfig).build()) { // Verify that the client can connect and execute commands String result = customClient.ping(); assertEquals("PONG", result); } } private MultiDbConfig getMCCF(MultiDbConfig.StrategySupplier strategySupplier) { Function modifier = builder -> strategySupplier == null ? builder.healthCheckEnabled(false) : builder.healthCheckStrategySupplier(strategySupplier); List databaseConfigs = Arrays.stream(new EndpointConfig[] { endpoint1 }) .map(e -> modifier .apply(MultiDbConfig.DatabaseConfig.builder(e.getHostAndPort(), clientConfig)).build()) .collect(Collectors.toList()); MultiDbConfig multiDbConfig = new MultiDbConfig.Builder(databaseConfigs) .commandRetry(MultiDbConfig.RetryConfig.builder().maxAttempts(1).waitDuration(1).build()) .failureDetector(MultiDbConfig.CircuitBreakerConfig.builder().slidingWindowSize(1) .failureRateThreshold(100).build()) .build(); return multiDbConfig; } // ========== Probe Logic Integration Tests ========== @Test public void testProbingLogic_RealHealthCheckWithProbes() throws InterruptedException { // Create a strategy that fails first few times then succeeds AtomicInteger attemptCount = new AtomicInteger(0); StrategySupplier strategySupplier = (hostAndPort, jedisClientConfig) -> { // Fast interval, short timeout, 3 probes, short delay return new TestHealthCheckStrategy(100, 50, 3, BuiltIn.ANY_SUCCESS, 20, (endpoint) -> { int attempt = attemptCount.incrementAndGet(); if (attempt <= 2) { // First 2 attempts fail throw new RuntimeException("Simulated failure on attempt " + attempt); } // Third attempt succeeds - do actual health check try (UnifiedJedis jedis = new UnifiedJedis(hostAndPort, jedisClientConfig)) { String result = jedis.ping(); return "PONG".equals(result) ? HealthStatus.HEALTHY : HealthStatus.UNHEALTHY; } catch (Exception e) { return HealthStatus.UNHEALTHY; } }); }; CountDownLatch healthyLatch = new CountDownLatch(1); HealthStatusManager manager = new HealthStatusManager(); // Register listener to detect when health becomes HEALTHY manager.registerListener(endpoint1.getHostAndPort(), event -> { if (event.getNewStatus() == HealthStatus.HEALTHY) { healthyLatch.countDown(); } }); // Add health check with strategy HealthCheck healthCheck = manager.add(endpoint1.getHostAndPort(), strategySupplier.get(endpoint1.getHostAndPort(), clientConfig)); try { // Wait for health check to eventually succeed after probes assertTrue(healthyLatch.await(5, TimeUnit.SECONDS), "Health check should succeed after probes"); assertEquals(HealthStatus.HEALTHY, healthCheck.getStatus()); // Verify that multiple attempts were made (should be 3: 2 failures + 1 success) assertTrue(attemptCount.get() >= 3, "Should have made at least 3 probes, but made: " + attemptCount.get()); } finally { manager.remove(endpoint1.getHostAndPort()); } } @Test public void testProbingLogic_ExhaustProbesAndStayUnhealthy() throws InterruptedException { // Create a strategy that always fails AtomicInteger attemptCount = new AtomicInteger(0); StrategySupplier alwaysFailSupplier = (hostAndPort, jedisClientConfig) -> { // Fast interval, short timeout, 3 probes, short delay return new TestHealthCheckStrategy(100, 50, 3, BuiltIn.ANY_SUCCESS, 10, (endpoint) -> { attemptCount.incrementAndGet(); throw new RuntimeException("Always fails"); }); }; CountDownLatch unhealthyLatch = new CountDownLatch(1); HealthStatusManager manager = new HealthStatusManager(); // Register listener to detect when health becomes UNHEALTHY manager.registerListener(endpoint1.getHostAndPort(), event -> { if (event.getNewStatus() == HealthStatus.UNHEALTHY) { unhealthyLatch.countDown(); } }); // Add health check with always-fail strategy HealthCheck healthCheck = manager.add(endpoint1.getHostAndPort(), alwaysFailSupplier.get(endpoint1.getHostAndPort(), clientConfig)); try { // Wait for health check to fail after exhausting probes assertTrue(unhealthyLatch.await(3, TimeUnit.SECONDS), "Health check should become UNHEALTHY after exhausting probes"); assertEquals(HealthStatus.UNHEALTHY, healthCheck.getStatus()); // Verify that all attempts were made (should 3 probes) assertTrue(attemptCount.get() >= 3, "Should have made at least 3 attempts , but made: " + attemptCount.get()); } finally { manager.remove(endpoint1.getHostAndPort()); } } @Test public void testProbingLogic_AllSuccess_EarlyFail_Integration() throws InterruptedException { AtomicInteger attemptCount = new AtomicInteger(0); StrategySupplier supplier = (hostAndPort, jedisClientConfig) -> new TestHealthCheckStrategy(100, 100, 3, BuiltIn.ALL_SUCCESS, 10, e -> { int c = attemptCount.incrementAndGet(); return c == 1 ? HealthStatus.UNHEALTHY : HealthStatus.HEALTHY; }); CountDownLatch unhealthyLatch = new CountDownLatch(1); HealthStatusManager manager = new HealthStatusManager(); manager.registerListener(endpoint1.getHostAndPort(), event -> { if (event.getNewStatus() == HealthStatus.UNHEALTHY) unhealthyLatch.countDown(); }); HealthCheck hc = manager.add(endpoint1.getHostAndPort(), supplier.get(endpoint1.getHostAndPort(), clientConfig)); try { assertTrue(unhealthyLatch.await(2, TimeUnit.SECONDS), "Should become UNHEALTHY after first failure with ALL_SUCCESS"); assertEquals(HealthStatus.UNHEALTHY, hc.getStatus()); assertEquals(1, attemptCount.get()); } finally { manager.remove(endpoint1.getHostAndPort()); } } @Test public void testProbingLogic_Majority_EarlySuccess_Integration() throws InterruptedException { AtomicInteger attemptCount = new AtomicInteger(0); StrategySupplier supplier = (hostAndPort, jedisClientConfig) -> new TestHealthCheckStrategy(100, 100, 5, BuiltIn.MAJORITY_SUCCESS, 10, e -> { int c = attemptCount.incrementAndGet(); return c <= 3 ? HealthStatus.HEALTHY : HealthStatus.UNHEALTHY; }); CountDownLatch healthyLatch = new CountDownLatch(1); HealthStatusManager manager = new HealthStatusManager(); manager.registerListener(endpoint1.getHostAndPort(), event -> { if (event.getNewStatus() == HealthStatus.HEALTHY) healthyLatch.countDown(); }); HealthCheck hc = manager.add(endpoint1.getHostAndPort(), supplier.get(endpoint1.getHostAndPort(), clientConfig)); try { assertTrue(healthyLatch.await(2, TimeUnit.SECONDS), "Should become HEALTHY after reaching majority successes"); assertEquals(HealthStatus.HEALTHY, hc.getStatus()); assertEquals(3, attemptCount.get()); } finally { manager.remove(endpoint1.getHostAndPort()); } } } ================================================ FILE: src/test/java/redis/clients/jedis/mcf/HealthCheckTest.java ================================================ package redis.clients.jedis.mcf; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Timeout; import org.mockito.Mock; import org.mockito.MockitoAnnotations; import redis.clients.jedis.DefaultJedisClientConfig; import redis.clients.jedis.Endpoint; import redis.clients.jedis.HostAndPort; import redis.clients.jedis.JedisClientConfig; import redis.clients.jedis.MultiDbConfig; import redis.clients.jedis.UnifiedJedis; import redis.clients.jedis.mcf.ProbingPolicy.BuiltIn; import java.util.concurrent.CountDownLatch; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicInteger; import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.atomic.AtomicReference; import java.util.function.Consumer; import java.util.function.Function; import static org.junit.jupiter.api.Assertions.*; import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.argThat; import static org.mockito.Mockito.*; public class HealthCheckTest { @Mock private UnifiedJedis mockJedis; @Mock private HealthCheckStrategy mockStrategy; private final HealthCheckStrategy alwaysHealthyStrategy = new TestHealthCheckStrategy(100, 50, 1, BuiltIn.ANY_SUCCESS, 10, e -> HealthStatus.HEALTHY); @Mock private Consumer mockCallback; private HostAndPort testEndpoint; private JedisClientConfig testConfig; @BeforeEach void setUp() { MockitoAnnotations.openMocks(this); testEndpoint = new HostAndPort("dummy", 6379); testConfig = DefaultJedisClientConfig.builder().build(); // Default stubs for mockStrategy used across tests when(mockStrategy.getNumProbes()).thenReturn(1); when(mockStrategy.getDelayInBetweenProbes()).thenReturn(100); when(mockStrategy.getPolicy()).thenReturn(BuiltIn.ANY_SUCCESS); } // ========== HealthCheckCollection Tests ========== @Test void testHealthCheckCollectionAdd() { HealthCheckCollection collection = new HealthCheckCollection(); HealthCheck healthCheck = new HealthCheckImpl(testEndpoint, mockStrategy, mockCallback); HealthCheck previous = collection.add(healthCheck); assertNull(previous); assertEquals(healthCheck, collection.get(testEndpoint)); } @Test void testHealthCheckCollectionRemoveByEndpoint() { HealthCheckCollection collection = new HealthCheckCollection(); HealthCheck healthCheck = new HealthCheckImpl(testEndpoint, mockStrategy, mockCallback); collection.add(healthCheck); HealthCheck removed = collection.remove(testEndpoint); assertEquals(healthCheck, removed); assertNull(collection.get(testEndpoint)); } @Test void testHealthCheckCollectionAddAll() { HealthCheckCollection collection = new HealthCheckCollection(); HealthCheck[] healthChecks = { new HealthCheckImpl(new HostAndPort("host1", 6379), mockStrategy, mockCallback), new HealthCheckImpl(new HostAndPort("host2", 6379), mockStrategy, mockCallback) }; HealthCheck[] previous = collection.addAll(healthChecks); assertNotNull(previous); assertEquals(2, previous.length); assertNull(previous[0]); // No previous health check for host1 assertNull(previous[1]); // No previous health check for host2 assertEquals(healthChecks[0], collection.get(new HostAndPort("host1", 6379))); assertEquals(healthChecks[1], collection.get(new HostAndPort("host2", 6379))); } @Test void testHealthCheckCollectionReplacement() { HealthCheckCollection collection = new HealthCheckCollection(); HealthCheck healthCheck1 = new HealthCheckImpl(testEndpoint, mockStrategy, mockCallback); HealthCheck healthCheck2 = new HealthCheckImpl(testEndpoint, mockStrategy, mockCallback); collection.add(healthCheck1); HealthCheck previous = collection.add(healthCheck2); assertEquals(healthCheck1, previous); assertEquals(healthCheck2, collection.get(testEndpoint)); } @Test void testHealthCheckCollectionRemoveByHealthCheck() { HealthCheckCollection collection = new HealthCheckCollection(); HealthCheck healthCheck = new HealthCheckImpl(testEndpoint, mockStrategy, mockCallback); collection.add(healthCheck); HealthCheck removed = collection.remove(healthCheck); assertEquals(healthCheck, removed); assertNull(collection.get(testEndpoint)); } @Test void testHealthCheckCollectionClose() { HealthCheckCollection collection = new HealthCheckCollection(); // Create mock health checks HealthCheck mockHealthCheck1 = spy( new HealthCheckImpl(testEndpoint, mockStrategy, mockCallback)); collection.add(mockHealthCheck1); // Call close collection.close(); // Verify stop was called on all health checks verify(mockHealthCheck1).stop(); } // ========== HealthCheck Tests ========== @Test void testHealthCheckStatusUpdate() throws InterruptedException { when(mockStrategy.getInterval()).thenReturn(1); when(mockStrategy.getTimeout()).thenReturn(50); when(mockStrategy.doHealthCheck(any(Endpoint.class))).thenReturn(HealthStatus.UNHEALTHY); CountDownLatch latch = new CountDownLatch(1); Consumer callback = event -> { assertEquals(HealthStatus.UNKNOWN, event.getOldStatus()); assertEquals(HealthStatus.UNHEALTHY, event.getNewStatus()); latch.countDown(); }; HealthCheck healthCheck = new HealthCheckImpl(testEndpoint, mockStrategy, callback); healthCheck.start(); assertTrue(latch.await(2, TimeUnit.SECONDS)); healthCheck.stop(); } @Test void testSafeUpdateChecksDoNotTriggerFalseNotifications() { AtomicInteger notificationCount = new AtomicInteger(0); Consumer callback = event -> notificationCount.incrementAndGet(); HealthCheckImpl healthCheck = new HealthCheckImpl(testEndpoint, mockStrategy, callback); // Simulate concurrent health checks with different results healthCheck.safeUpdate(2000, HealthStatus.HEALTHY); // Newer timestamp healthCheck.safeUpdate(1000, HealthStatus.UNHEALTHY); // Older timestamp (should be ignored) // Should only have 1 notification (for the first update), not 2 assertEquals(1, notificationCount.get()); assertEquals(HealthStatus.HEALTHY, healthCheck.getStatus()); } @Test void testSafeUpdateWithConcurrentResults() { AtomicInteger notificationCount = new AtomicInteger(0); Consumer callback = event -> notificationCount.incrementAndGet(); HealthCheckImpl healthCheck = new HealthCheckImpl(testEndpoint, mockStrategy, callback); // Test the exact scenario: newer result first, then older result healthCheck.safeUpdate(2000, HealthStatus.HEALTHY); // Should update and notify assertEquals(1, notificationCount.get()); assertEquals(HealthStatus.HEALTHY, healthCheck.getStatus()); healthCheck.safeUpdate(1000, HealthStatus.UNHEALTHY); // Should NOT update or notify assertEquals(1, notificationCount.get()); // Still 1, no additional notification assertEquals(HealthStatus.HEALTHY, healthCheck.getStatus()); // Status unchanged } @Test void testHealthCheckStop() { when(mockStrategy.getInterval()).thenReturn(1000); when(mockStrategy.getTimeout()).thenReturn(500); HealthCheck healthCheck = new HealthCheckImpl(testEndpoint, mockStrategy, mockCallback); healthCheck.start(); assertDoesNotThrow(healthCheck::stop); } // ========== HealthStatusManager Tests ========== @Test void testHealthStatusManagerRegisterListener() { HealthStatusManager manager = new HealthStatusManager(); HealthStatusListener listener = mock(HealthStatusListener.class); manager.registerListener(listener); // Verify listener is registered by triggering an event manager.add(testEndpoint, alwaysHealthyStrategy); // Give some time for health check to run try { Thread.sleep(100); } catch (InterruptedException e) { } verify(listener, atLeastOnce()).onStatusChange(any(HealthStatusChangeEvent.class)); } @Test void testHealthStatusManagerUnregisterListener() { HealthStatusManager manager = new HealthStatusManager(); HealthStatusListener listener = mock(HealthStatusListener.class); manager.registerListener(listener); manager.unregisterListener(listener); manager.add(testEndpoint, alwaysHealthyStrategy); // Give some time for potential health check try { Thread.sleep(100); } catch (InterruptedException e) { } verify(listener, never()).onStatusChange(any(HealthStatusChangeEvent.class)); } @Test void testHealthStatusManagerEndpointSpecificListener() { HealthStatusManager manager = new HealthStatusManager(); HealthStatusListener listener = mock(HealthStatusListener.class); HostAndPort otherEndpoint = new HostAndPort("other", 6379); manager.registerListener(testEndpoint, listener); manager.add(testEndpoint, alwaysHealthyStrategy); manager.add(otherEndpoint, alwaysHealthyStrategy); // Give some time for health checks try { Thread.sleep(100); } catch (InterruptedException e) { } // Listener should only receive events for testEndpoint verify(listener, atLeastOnce()) .onStatusChange(argThat(event -> event.getEndpoint().equals(testEndpoint))); } @Test void testHealthStatusManagerLifecycle() throws InterruptedException { HealthStatusManager manager = new HealthStatusManager(); // Before adding health check assertEquals(HealthStatus.UNKNOWN, manager.getHealthStatus(testEndpoint)); // Set up event listener to wait for initial health check completion CountDownLatch healthCheckCompleteLatch = new CountDownLatch(1); HealthStatusListener listener = event -> healthCheckCompleteLatch.countDown(); // Register listener before adding health check to capture the initial event manager.registerListener(testEndpoint, listener); HealthCheckStrategy delayedStrategy = new TestHealthCheckStrategy(2000, 1000, 3, BuiltIn.ALL_SUCCESS, 100, e -> HealthStatus.HEALTHY); // Add health check - this will start async health checking manager.add(testEndpoint, delayedStrategy); // Initially should still be UNKNOWN until first check completes assertEquals(HealthStatus.UNKNOWN, manager.getHealthStatus(testEndpoint)); // Wait for initial health check to complete assertTrue(healthCheckCompleteLatch.await(2, TimeUnit.SECONDS), "Initial health check should complete within timeout"); // Now should be HEALTHY after initial check assertEquals(HealthStatus.HEALTHY, manager.getHealthStatus(testEndpoint)); // Clean up and verify removal manager.remove(testEndpoint); assertEquals(HealthStatus.UNKNOWN, manager.getHealthStatus(testEndpoint)); } @Test void testHealthStatusManagerClose() { HealthCheckStrategy closeableStrategy = mock(HealthCheckStrategy.class); when(closeableStrategy.getNumProbes()).thenReturn(1); when(closeableStrategy.getInterval()).thenReturn(1000); when(closeableStrategy.getTimeout()).thenReturn(500); when(closeableStrategy.doHealthCheck(any(Endpoint.class))).thenReturn(HealthStatus.HEALTHY); HealthStatusManager manager = new HealthStatusManager(); // Add health check manager.add(testEndpoint, closeableStrategy); // Close manager manager.close(); // Verify health check is stopped verify(closeableStrategy).close(); } // ========== PingStrategy Tests ========== @Test void testPingStrategyCustomIntervalTimeout() { try (PingStrategy strategy = new PingStrategy(testEndpoint, testConfig, HealthCheckStrategy.Config.builder().interval(2000).timeout(1500).delayInBetweenProbes(50) .numProbes(11).policy(BuiltIn.ANY_SUCCESS).build())) { assertEquals(2000, strategy.getInterval()); assertEquals(1500, strategy.getTimeout()); assertEquals(11, strategy.getNumProbes()); assertEquals(BuiltIn.ANY_SUCCESS, strategy.getPolicy()); assertEquals(50, strategy.getDelayInBetweenProbes()); } } @Test void testPingStrategyDefaultSupplier() { MultiDbConfig.StrategySupplier supplier = PingStrategy.DEFAULT; HealthCheckStrategy strategy = supplier.get(testEndpoint, testConfig); assertInstanceOf(PingStrategy.class, strategy); } // ========== Failover configuration Tests ========== @Test void testNewFieldLocations() { // Test new field locations in DatabaseConfig and MultiDbConfig MultiDbConfig.DatabaseConfig databaseConfig = MultiDbConfig.DatabaseConfig .builder(testEndpoint, testConfig).weight(2.5f).build(); MultiDbConfig multiConfig = new MultiDbConfig.Builder( new MultiDbConfig.DatabaseConfig[] { databaseConfig }).retryOnFailover(true) .failbackSupported(false).build(); assertEquals(2.5f, databaseConfig.getWeight()); assertTrue(multiConfig.isRetryOnFailover()); assertFalse(multiConfig.isFailbackSupported()); } @Test void testDefaultValues() { // Test default values in DatabaseConfig MultiDbConfig.DatabaseConfig databaseConfig = MultiDbConfig.DatabaseConfig .builder(testEndpoint, testConfig).build(); assertEquals(1.0f, databaseConfig.getWeight()); // Default weight assertEquals(PingStrategy.DEFAULT, databaseConfig.getHealthCheckStrategySupplier()); // Default // is null // (no // health // check) // Test default values in MultiDbConfig MultiDbConfig multiConfig = new MultiDbConfig.Builder( new MultiDbConfig.DatabaseConfig[] { databaseConfig }).build(); assertFalse(multiConfig.isRetryOnFailover()); // Default is false assertTrue(multiConfig.isFailbackSupported()); // Default is true } @Test void testDatabaseConfigWithHealthCheckStrategy() { HealthCheckStrategy customStrategy = mock(HealthCheckStrategy.class); MultiDbConfig.StrategySupplier supplier = (hostAndPort, jedisClientConfig) -> customStrategy; MultiDbConfig.DatabaseConfig databaseConfig = MultiDbConfig.DatabaseConfig .builder(testEndpoint, testConfig).healthCheckStrategySupplier(supplier).build(); assertNotNull(databaseConfig.getHealthCheckStrategySupplier()); HealthCheckStrategy result = databaseConfig.getHealthCheckStrategySupplier().get(testEndpoint, testConfig); assertEquals(customStrategy, result); } @Test void testDatabaseConfigWithStrategySupplier() { MultiDbConfig.StrategySupplier customSupplier = (hostAndPort, jedisClientConfig) -> { return mock(HealthCheckStrategy.class); }; MultiDbConfig.DatabaseConfig databaseConfig = MultiDbConfig.DatabaseConfig .builder(testEndpoint, testConfig).healthCheckStrategySupplier(customSupplier).build(); assertEquals(customSupplier, databaseConfig.getHealthCheckStrategySupplier()); } @Test void testDatabaseConfigWithPingStrategy() { MultiDbConfig.StrategySupplier pingSupplier = (hostAndPort, jedisClientConfig) -> { return new PingStrategy(hostAndPort, jedisClientConfig); }; MultiDbConfig.DatabaseConfig databaseConfig = MultiDbConfig.DatabaseConfig .builder(testEndpoint, testConfig).healthCheckStrategySupplier(pingSupplier).build(); MultiDbConfig.StrategySupplier supplier = databaseConfig.getHealthCheckStrategySupplier(); assertNotNull(supplier); assertInstanceOf(PingStrategy.class, supplier.get(testEndpoint, testConfig)); } @Test void testDatabaseConfigWithDefaultHealthCheck() { MultiDbConfig.DatabaseConfig databaseConfig = MultiDbConfig.DatabaseConfig .builder(testEndpoint, testConfig).build(); // Should use default PingStrategy assertNotNull(databaseConfig.getHealthCheckStrategySupplier()); assertEquals(PingStrategy.DEFAULT, databaseConfig.getHealthCheckStrategySupplier()); } @Test void testDatabaseConfigWithDisabledHealthCheck() { MultiDbConfig.DatabaseConfig databaseConfig = MultiDbConfig.DatabaseConfig .builder(testEndpoint, testConfig).healthCheckEnabled(false).build(); assertNull(databaseConfig.getHealthCheckStrategySupplier()); } @Test void testDatabaseConfigHealthCheckEnabledExplicitly() { MultiDbConfig.DatabaseConfig databaseConfig = MultiDbConfig.DatabaseConfig .builder(testEndpoint, testConfig).healthCheckEnabled(true).build(); assertNotNull(databaseConfig.getHealthCheckStrategySupplier()); assertEquals(PingStrategy.DEFAULT, databaseConfig.getHealthCheckStrategySupplier()); } // ========== Integration Tests ========== @Test @Timeout(5) void testHealthCheckRecoversAfterException() throws InterruptedException { // Create a strategy that alternates between exception/UNHEALTHY and HEALTHY AtomicBoolean isHealthy = new AtomicBoolean(true); AtomicInteger exceptionOccurred = new AtomicInteger(0); int exceptionLimit = 2; HealthCheckStrategy alternatingStrategy = new TestHealthCheckStrategy( HealthCheckStrategy.Config.builder().interval(1).timeout(5).numProbes(1).build(), e -> { if (isHealthy.get()) { isHealthy.set(false); if (exceptionOccurred.getAndIncrement() < exceptionLimit) { throw new RuntimeException("Simulated exception"); } else { return HealthStatus.UNHEALTHY; } } else { isHealthy.set(true); return HealthStatus.HEALTHY; } }); // Wait for 2 status changes, // it will start with unhealthy(due to simulated exception) and then switch to healthy CountDownLatch statusChangeLatch = new CountDownLatch(2); HealthStatusListener listener = event -> statusChangeLatch.countDown(); HealthStatusManager manager = new HealthStatusManager(); manager.registerListener(listener); manager.add(testEndpoint, alternatingStrategy); assertTrue(statusChangeLatch.await(1, TimeUnit.SECONDS)); manager.remove(testEndpoint); } @Test @Timeout(5) void testHealthCheckIntegration() throws InterruptedException { // Create a mock strategy that alternates between healthy and unhealthy AtomicReference statusToReturn = new AtomicReference<>(HealthStatus.HEALTHY); HealthCheckStrategy alternatingStrategy = new TestHealthCheckStrategy(100, 50, 1, BuiltIn.ALL_SUCCESS, 10, e -> { HealthStatus current = statusToReturn.get(); statusToReturn .set(current == HealthStatus.HEALTHY ? HealthStatus.UNHEALTHY : HealthStatus.HEALTHY); return current; }); CountDownLatch statusChangeLatch = new CountDownLatch(2); // Wait for 2 status changes HealthStatusListener listener = event -> statusChangeLatch.countDown(); HealthStatusManager manager = new HealthStatusManager(); manager.registerListener(listener); manager.add(testEndpoint, alternatingStrategy); assertTrue(statusChangeLatch.await(3, TimeUnit.SECONDS)); manager.remove(testEndpoint); } @Test void testStrategySupplierPolymorphism() { // Test that the polymorphic design works correctly MultiDbConfig.StrategySupplier supplier = (hostAndPort, jedisClientConfig) -> { if (jedisClientConfig != null) { return new PingStrategy(hostAndPort, jedisClientConfig, HealthCheckStrategy.Config.builder().interval(500).timeout(250).numProbes(1).build()); } else { return new PingStrategy(hostAndPort, DefaultJedisClientConfig.builder().build()); } }; // Test with config HealthCheckStrategy strategyWithConfig = supplier.get(testEndpoint, testConfig); assertNotNull(strategyWithConfig); assertEquals(500, strategyWithConfig.getInterval()); assertEquals(250, strategyWithConfig.getTimeout()); // Test without config HealthCheckStrategy strategyWithoutConfig = supplier.get(testEndpoint, null); assertNotNull(strategyWithoutConfig); assertEquals(5000, strategyWithoutConfig.getInterval()); // Default values assertEquals(1000, strategyWithoutConfig.getTimeout()); } // ========== Retry Logic Unit Tests ========== @Test void testRetryLogic_SuccessOnFirstAttempt() throws InterruptedException { AtomicInteger callCount = new AtomicInteger(0); TestHealthCheckStrategy strategy = new TestHealthCheckStrategy(100, 50, 2, BuiltIn.ANY_SUCCESS, 10, e -> { callCount.incrementAndGet(); return HealthStatus.HEALTHY; // Always succeeds }); CountDownLatch latch = new CountDownLatch(1); Consumer callback = event -> latch.countDown(); HealthCheck healthCheck = new HealthCheckImpl(testEndpoint, strategy, callback); healthCheck.start(); assertTrue(latch.await(2, TimeUnit.SECONDS)); assertEquals(HealthStatus.HEALTHY, healthCheck.getStatus()); // Should only call doHealthCheck once (no retries needed) assertEquals(1, callCount.get()); healthCheck.stop(); } @Test void testRetryLogic_FailThenSucceedOnRetry() throws InterruptedException { AtomicInteger callCount = new AtomicInteger(0); TestHealthCheckStrategy strategy = new TestHealthCheckStrategy(100, 50, 2, BuiltIn.ANY_SUCCESS, 10, e -> { int attempt = callCount.incrementAndGet(); if (attempt == 1) { throw new RuntimeException("First attempt fails"); } return HealthStatus.HEALTHY; // Second attempt succeeds }); CountDownLatch latch = new CountDownLatch(1); Consumer callback = event -> { if (event.getNewStatus() == HealthStatus.HEALTHY) { latch.countDown(); } }; HealthCheck healthCheck = new HealthCheckImpl(testEndpoint, strategy, callback); healthCheck.start(); assertTrue(latch.await(3, TimeUnit.SECONDS)); assertEquals(HealthStatus.HEALTHY, healthCheck.getStatus()); // Should call doHealthCheck twice (first fails, second succeeds) assertEquals(2, callCount.get()); healthCheck.stop(); } @Test void testRetryLogic_ExhaustAllProbesAndFail() throws InterruptedException { AtomicInteger callCount = new AtomicInteger(0); TestHealthCheckStrategy strategy = new TestHealthCheckStrategy(100, 50, 3, BuiltIn.ANY_SUCCESS, 10, e -> { callCount.incrementAndGet(); throw new RuntimeException("Always fails"); }); CountDownLatch latch = new CountDownLatch(1); Consumer callback = event -> { if (event.getNewStatus() == HealthStatus.UNHEALTHY) { latch.countDown(); } }; HealthCheck healthCheck = new HealthCheckImpl(testEndpoint, strategy, callback); healthCheck.start(); assertTrue(latch.await(3, TimeUnit.SECONDS)); assertEquals(HealthStatus.UNHEALTHY, healthCheck.getStatus()); // Should call doHealthCheck 3 times (all probes fail) assertEquals(3, callCount.get()); healthCheck.stop(); } @Test void testRetryLogic_ZeroProbes() throws InterruptedException { AtomicInteger callCount = new AtomicInteger(0); TestHealthCheckStrategy strategy = new TestHealthCheckStrategy(100, 50, 0, BuiltIn.ANY_SUCCESS, 10, e -> { callCount.incrementAndGet(); throw new RuntimeException("Fails"); }); CountDownLatch latch = new CountDownLatch(1); Consumer callback = event -> { if (event.getNewStatus() == HealthStatus.UNHEALTHY) { latch.countDown(); } }; assertThrows(IllegalArgumentException.class, () -> new HealthCheckImpl(testEndpoint, strategy, callback)); } @Test void testRetryLogic_NegativeProbes() throws InterruptedException { AtomicInteger callCount = new AtomicInteger(0); TestHealthCheckStrategy strategy = new TestHealthCheckStrategy(100, 50, -1, BuiltIn.ANY_SUCCESS, 10, e -> { callCount.incrementAndGet(); throw new RuntimeException("Fails"); }); CountDownLatch latch = new CountDownLatch(1); Consumer callback = event -> { if (event.getNewStatus() == HealthStatus.UNHEALTHY) { latch.countDown(); } }; assertThrows(IllegalArgumentException.class, () -> new HealthCheckImpl(testEndpoint, strategy, callback)); } /** *

* - Verifies that the health check probes stop after the first probe when the scheduler thread is * interrupted. *

* - The scheduler thread is the one that calls healthCheck(), which in turn calls * doHealthCheck(). *

* - This test interrupts the scheduler thread while it is waiting on the future from the first * probe. *

* - The health check operation itself is not interrupted. This test does not validate * interruption of the health check operation itself, as that is not the responsibility of the * HealthCheckImpl. */ @Test void testRetryLogic_InterruptionStopsProbes() throws Exception { AtomicInteger callCount = new AtomicInteger(0); CountDownLatch schedulerTaskStarted = new CountDownLatch(1); CountDownLatch statusChanged = new CountDownLatch(1); Thread[] schedulerThread = new Thread[1]; final int OPERATION_TIMEOUT = 1000; final int LESS_THAN_OPERATION_TIMEOUT = 800; final int NUM_PROBES = 3; // Long interval so no second run, generous timeout so we can interrupt while waiting Function healthCheckOperation = e -> { callCount.incrementAndGet(); try { Thread.sleep(LESS_THAN_OPERATION_TIMEOUT); // keep worker busy so scheduler waits on // future.get } catch (InterruptedException ie) { } return HealthStatus.UNHEALTHY; }; // Override getPolicy() to capture the scheduler thread HealthCheckStrategy strategy = new TestHealthCheckStrategy(5000, OPERATION_TIMEOUT, NUM_PROBES, BuiltIn.ANY_SUCCESS, 10, healthCheckOperation) { public ProbingPolicy getPolicy() { schedulerThread[0] = Thread.currentThread(); schedulerTaskStarted.countDown(); return super.getPolicy(); } }; Consumer callback = evt -> statusChanged.countDown(); HealthCheckImpl hc = new HealthCheckImpl(testEndpoint, strategy, callback); hc.start(); // Ensure first probe is in flight (scheduler is blocked on future.get) assertTrue(schedulerTaskStarted.await(1, TimeUnit.SECONDS), "Task should have started"); // Interrupt the scheduler thread that runs HealthCheckImpl.healthCheck() schedulerThread[0].interrupt(); // No status change should be published because healthCheck() returns early without safeUpdate assertFalse(statusChanged.await(hc.getMaxWaitFor(), TimeUnit.MILLISECONDS), "No status change expected"); assertEquals(HealthStatus.UNKNOWN, hc.getStatus()); // Only the first probe should have been attempted int calls = callCount.get(); assertTrue(calls <= 1, "Only one probe should have been attempted: " + calls); hc.stop(); } // ========== ProbingPolicy Unit Tests ========== @Test void testPolicy_AllSuccess_StopsOnFirstFailure() throws Exception { AtomicInteger callCount = new AtomicInteger(0); CountDownLatch unhealthyLatch = new CountDownLatch(1); TestHealthCheckStrategy strategy = new TestHealthCheckStrategy( HealthCheckStrategy.Config.builder().interval(5).timeout(200).numProbes(3) .policy(BuiltIn.ALL_SUCCESS).delayInBetweenProbes(5).build(), e -> { int c = callCount.incrementAndGet(); return c == 1 ? HealthStatus.UNHEALTHY : HealthStatus.HEALTHY; }); HealthCheckImpl hc = new HealthCheckImpl(testEndpoint, strategy, evt -> { if (evt.getNewStatus() == HealthStatus.UNHEALTHY) unhealthyLatch.countDown(); }); hc.start(); assertTrue(unhealthyLatch.await(1, TimeUnit.SECONDS)); assertEquals(HealthStatus.UNHEALTHY, hc.getStatus()); assertEquals(1, callCount.get(), "ALL_SUCCESS should stop after first failure"); hc.stop(); } @Test void testPolicy_Majority_EarlySuccessStopsAtThree() throws Exception { AtomicInteger callCount = new AtomicInteger(0); CountDownLatch healthyLatch = new CountDownLatch(1); TestHealthCheckStrategy strategy = new TestHealthCheckStrategy( HealthCheckStrategy.Config.builder().interval(5000).timeout(200).numProbes(5) .policy(BuiltIn.MAJORITY_SUCCESS).delayInBetweenProbes(5).build(), e -> { int c = callCount.incrementAndGet(); return c <= 3 ? HealthStatus.HEALTHY : HealthStatus.UNHEALTHY; }); HealthCheckImpl hc = new HealthCheckImpl(testEndpoint, strategy, evt -> { if (evt.getNewStatus() == HealthStatus.HEALTHY) healthyLatch.countDown(); }); hc.start(); assertTrue(healthyLatch.await(1, TimeUnit.SECONDS)); assertEquals(HealthStatus.HEALTHY, hc.getStatus()); assertEquals(3, callCount.get(), "MAJORITY early success should stop after 3 successes"); hc.stop(); } @Test void testPolicy_Majority_EarlyFailStopsAtTwo() throws Exception { AtomicInteger callCount = new AtomicInteger(0); CountDownLatch unhealthyLatch = new CountDownLatch(1); TestHealthCheckStrategy strategy = new TestHealthCheckStrategy( HealthCheckStrategy.Config.builder().interval(5000).timeout(200).numProbes(4) .policy(BuiltIn.MAJORITY_SUCCESS).delayInBetweenProbes(5).build(), e -> { int c = callCount.incrementAndGet(); return c <= 2 ? HealthStatus.UNHEALTHY : HealthStatus.HEALTHY; }); HealthCheckImpl hc = new HealthCheckImpl(testEndpoint, strategy, evt -> { if (evt.getNewStatus() == HealthStatus.UNHEALTHY) unhealthyLatch.countDown(); }); hc.start(); assertTrue(unhealthyLatch.await(1, TimeUnit.SECONDS)); assertEquals(HealthStatus.UNHEALTHY, hc.getStatus()); assertEquals(2, callCount.get(), "MAJORITY early fail should stop when majority impossible"); hc.stop(); } } ================================================ FILE: src/test/java/redis/clients/jedis/mcf/InitializationPolicyTest.java ================================================ package redis.clients.jedis.mcf; import static org.junit.jupiter.api.Assertions.*; import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Nested; import org.junit.jupiter.api.Test; import redis.clients.jedis.mcf.InitializationPolicy.Decision; import redis.clients.jedis.mcf.InitializationPolicy.InitializationContext; /** * Unit tests for {@link InitializationPolicy} implementations. *

* Tests verify the decision logic for different combinations of available, failed, and pending * connections for each built-in policy: *

    *
  • {@link InitializationPolicy.BuiltIn#ALL_AVAILABLE}
  • *
  • {@link InitializationPolicy.BuiltIn#MAJORITY_AVAILABLE}
  • *
  • {@link InitializationPolicy.BuiltIn#ONE_AVAILABLE}
  • *
*/ @DisplayName("InitializationPolicy Unit Tests") public class InitializationPolicyTest { /** * Test implementation of InitializationContext for unit testing. */ private static class TestContext implements InitializationContext { private final int available; private final int failed; private final int pending; TestContext(int available, int failed, int pending) { this.available = available; this.failed = failed; this.pending = pending; } @Override public int getAvailableConnections() { return available; } @Override public int getFailedConnections() { return failed; } @Override public int getPendingConnections() { return pending; } } @Nested @DisplayName("ALL_AVAILABLE Policy Tests") class AllAvailablePolicyTests { private final InitializationPolicy policy = InitializationPolicy.BuiltIn.ALL_AVAILABLE; @Test @DisplayName("Should return SUCCESS when all connections are available") void shouldSucceedWhenAllAvailable() { TestContext ctx = new TestContext(3, 0, 0); assertEquals(Decision.SUCCESS, policy.evaluate(ctx)); } @Test @DisplayName("Should return SUCCESS when single connection is available and no others") void shouldSucceedWithSingleConnection() { TestContext ctx = new TestContext(1, 0, 0); assertEquals(Decision.SUCCESS, policy.evaluate(ctx)); } @Test @DisplayName("Should return FAIL when no connections configured (0,0,0)") void shouldFailWithEmptyContext() { TestContext ctx = new TestContext(0, 0, 0); assertEquals(Decision.FAIL, policy.evaluate(ctx)); } @Test @DisplayName("Should return FAIL when any connection fails") void shouldFailWhenAnyConnectionFails() { TestContext ctx = new TestContext(2, 1, 0); assertEquals(Decision.FAIL, policy.evaluate(ctx)); } @Test @DisplayName("Should return FAIL when all connections fail") void shouldFailWhenAllConnectionsFail() { TestContext ctx = new TestContext(0, 3, 0); assertEquals(Decision.FAIL, policy.evaluate(ctx)); } @Test @DisplayName("Should return FAIL when single connection fails") void shouldFailWithSingleFailure() { TestContext ctx = new TestContext(0, 1, 0); assertEquals(Decision.FAIL, policy.evaluate(ctx)); } @Test @DisplayName("Should return FAIL immediately when any failure even with pending") void shouldFailImmediatelyWithAnyFailure() { TestContext ctx = new TestContext(1, 1, 2); assertEquals(Decision.FAIL, policy.evaluate(ctx)); } @Test @DisplayName("Should return CONTINUE when connections are pending") void shouldContinueWhenPending() { TestContext ctx = new TestContext(2, 0, 1); assertEquals(Decision.CONTINUE, policy.evaluate(ctx)); } @Test @DisplayName("Should return CONTINUE when all connections are pending") void shouldContinueWhenAllPending() { TestContext ctx = new TestContext(0, 0, 5); assertEquals(Decision.CONTINUE, policy.evaluate(ctx)); } } @Nested @DisplayName("MAJORITY_AVAILABLE Policy Tests") class MajorityAvailablePolicyTests { private final InitializationPolicy policy = InitializationPolicy.BuiltIn.MAJORITY_AVAILABLE; @Test @DisplayName("Should return SUCCESS when majority is reached (3 of 5)") void shouldSucceedWithMajority() { TestContext ctx = new TestContext(3, 1, 1); assertEquals(Decision.SUCCESS, policy.evaluate(ctx)); } @Test @DisplayName("Should return SUCCESS when all are available") void shouldSucceedWhenAllAvailable() { TestContext ctx = new TestContext(5, 0, 0); assertEquals(Decision.SUCCESS, policy.evaluate(ctx)); } @Test @DisplayName("Should return SUCCESS with exact majority (2 of 3)") void shouldSucceedWithExactMajority() { TestContext ctx = new TestContext(2, 1, 0); assertEquals(Decision.SUCCESS, policy.evaluate(ctx)); } @Test @DisplayName("Should return SUCCESS with single connection out of one") void shouldSucceedWithSingleConnection() { TestContext ctx = new TestContext(1, 0, 0); assertEquals(Decision.SUCCESS, policy.evaluate(ctx)); } @Test @DisplayName("Should return SUCCESS early when majority reached with pending (3 of 5)") void shouldSucceedEarlyWithMajority() { TestContext ctx = new TestContext(3, 0, 2); assertEquals(Decision.SUCCESS, policy.evaluate(ctx)); } @Test @DisplayName("Should return SUCCESS early when majority reached (2 of 3, 1 pending)") void shouldSucceedEarlyWithMajorityOf3() { TestContext ctx = new TestContext(2, 0, 1); assertEquals(Decision.SUCCESS, policy.evaluate(ctx)); } @Test @DisplayName("Should handle even number of connections (3 of 4)") void shouldHandleEvenNumberOfConnections() { TestContext ctx = new TestContext(3, 1, 0); assertEquals(Decision.SUCCESS, policy.evaluate(ctx)); } @Test @DisplayName("Should handle two connections (2 of 2)") void shouldHandleTwoConnections() { TestContext ctx = new TestContext(2, 0, 0); assertEquals(Decision.SUCCESS, policy.evaluate(ctx)); } @Test @DisplayName("Should return SUCCESS with large number of connections (51 of 100)") void shouldSucceedWithLargeNumberOfConnections() { TestContext ctx = new TestContext(51, 49, 0); assertEquals(Decision.SUCCESS, policy.evaluate(ctx)); } @Test @DisplayName("Should return FAIL when no connections configured (0,0,0)") void shouldFailWithEmptyContext() { TestContext ctx = new TestContext(0, 0, 0); assertEquals(Decision.FAIL, policy.evaluate(ctx)); } @Test @DisplayName("Should return FAIL when majority is impossible (2 failed of 3)") void shouldFailWhenMajorityImpossible() { TestContext ctx = new TestContext(0, 2, 1); assertEquals(Decision.FAIL, policy.evaluate(ctx)); } @Test @DisplayName("Should return FAIL when all connections fail") void shouldFailWhenAllFail() { TestContext ctx = new TestContext(0, 5, 0); assertEquals(Decision.FAIL, policy.evaluate(ctx)); } @Test @DisplayName("Should return FAIL when single database fails") void shouldFailWithSingleDatabaseFailed() { TestContext ctx = new TestContext(0, 1, 0); assertEquals(Decision.FAIL, policy.evaluate(ctx)); } @Test @DisplayName("Should return FAIL when majority not reached and no pending (1 of 3)") void shouldFailWhenMajorityNotReachedNoPending() { TestContext ctx = new TestContext(1, 2, 0); assertEquals(Decision.FAIL, policy.evaluate(ctx)); } @Test @DisplayName("Should return FAIL early when majority impossible with pending (1,3,1)") void shouldFailEarlyWhenMajorityImpossibleWithPending() { TestContext ctx = new TestContext(1, 3, 1); assertEquals(Decision.FAIL, policy.evaluate(ctx)); } @Test @DisplayName("Should return FAIL early when majority impossible (3 failed of 5, 2 pending)") void shouldFailEarlyWhenMajorityImpossible() { TestContext ctx = new TestContext(0, 3, 2); assertEquals(Decision.FAIL, policy.evaluate(ctx)); } @Test @DisplayName("Should fail with even split (2 of 4)") void shouldFailWithEvenSplit() { TestContext ctx = new TestContext(2, 2, 0); assertEquals(Decision.FAIL, policy.evaluate(ctx)); } @Test @DisplayName("Should fail with one of two connections") void shouldFailWithOneOfTwo() { TestContext ctx = new TestContext(1, 1, 0); assertEquals(Decision.FAIL, policy.evaluate(ctx)); } @Test @DisplayName("Should return CONTINUE when majority possible but not yet reached") void shouldContinueWhenMajorityPossible() { TestContext ctx = new TestContext(1, 1, 3); assertEquals(Decision.CONTINUE, policy.evaluate(ctx)); } @Test @DisplayName("Should return CONTINUE when all pending") void shouldContinueWhenAllPending() { TestContext ctx = new TestContext(0, 0, 5); assertEquals(Decision.CONTINUE, policy.evaluate(ctx)); } @Test @DisplayName("Should return CONTINUE when below majority with pending (2 of 4)") void shouldContinueBelowMajority() { TestContext ctx = new TestContext(2, 0, 2); assertEquals(Decision.CONTINUE, policy.evaluate(ctx)); } @Test @DisplayName("Should return CONTINUE with one available and one pending (1,0,1)") void shouldContinueWithOneAvailableOnePending() { TestContext ctx = new TestContext(1, 0, 1); assertEquals(Decision.CONTINUE, policy.evaluate(ctx)); } @Test @DisplayName("Should return CONTINUE when majority still possible (1,1,1)") void shouldContinueWhenMajorityStillPossible() { TestContext ctx = new TestContext(1, 1, 1); assertEquals(Decision.CONTINUE, policy.evaluate(ctx)); } } @Nested @DisplayName("ONE_AVAILABLE Policy Tests") class OneAvailablePolicyTests { private final InitializationPolicy policy = InitializationPolicy.BuiltIn.ONE_AVAILABLE; @Test @DisplayName("Should return SUCCESS when one connection is available") void shouldSucceedWithOneAvailable() { TestContext ctx = new TestContext(1, 2, 0); assertEquals(Decision.SUCCESS, policy.evaluate(ctx)); } @Test @DisplayName("Should return SUCCESS when all connections are available") void shouldSucceedWhenAllAvailable() { TestContext ctx = new TestContext(5, 0, 0); assertEquals(Decision.SUCCESS, policy.evaluate(ctx)); } @Test @DisplayName("Should return SUCCESS early with one available and pending") void shouldSucceedEarlyWithOneAvailable() { TestContext ctx = new TestContext(1, 0, 4); assertEquals(Decision.SUCCESS, policy.evaluate(ctx)); } @Test @DisplayName("Should return SUCCESS with multiple available") void shouldSucceedWithMultipleAvailable() { TestContext ctx = new TestContext(3, 2, 0); assertEquals(Decision.SUCCESS, policy.evaluate(ctx)); } @Test @DisplayName("Should return FAIL when no connections configured (0,0,0)") void shouldFailWithEmptyContext() { TestContext ctx = new TestContext(0, 0, 0); assertEquals(Decision.FAIL, policy.evaluate(ctx)); } @Test @DisplayName("Should return FAIL when all connections fail") void shouldFailWhenAllFail() { TestContext ctx = new TestContext(0, 5, 0); assertEquals(Decision.FAIL, policy.evaluate(ctx)); } @Test @DisplayName("Should return FAIL when single connection fails") void shouldFailWithSingleFailure() { TestContext ctx = new TestContext(0, 1, 0); assertEquals(Decision.FAIL, policy.evaluate(ctx)); } @Test @DisplayName("Should return CONTINUE when all connections are pending") void shouldContinueWhenAllPending() { TestContext ctx = new TestContext(0, 0, 5); assertEquals(Decision.CONTINUE, policy.evaluate(ctx)); } @Test @DisplayName("Should return CONTINUE when some failed but some pending") void shouldContinueWithFailedAndPending() { TestContext ctx = new TestContext(0, 2, 3); assertEquals(Decision.CONTINUE, policy.evaluate(ctx)); } } } ================================================ FILE: src/test/java/redis/clients/jedis/mcf/LagAwareStrategyUnitTest.java ================================================ package redis.clients.jedis.mcf; import static org.junit.jupiter.api.Assertions.*; import static org.mockito.ArgumentMatchers.anyBoolean; import static org.mockito.ArgumentMatchers.anyString; import static org.mockito.Mockito.*; import java.time.Duration; import java.util.Arrays; import java.util.Collections; import java.util.function.Supplier; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.mockito.MockedConstruction; import redis.clients.jedis.DefaultRedisCredentials; import redis.clients.jedis.Endpoint; import redis.clients.jedis.RedisCredentials; import redis.clients.jedis.exceptions.JedisException; import redis.clients.jedis.mcf.LagAwareStrategy.Config; public class LagAwareStrategyUnitTest { private Endpoint endpoint; private Supplier creds; @BeforeEach void setup() { endpoint = new Endpoint() { @Override public String getHost() { return "dummy"; } @Override public int getPort() { return 8443; } }; creds = () -> new DefaultRedisCredentials("user", "pwd"); } @Test void healthy_when_bdb_available_and_cached_uid_used_on_next_check() throws Exception { RedisRestAPI.BdbInfo bdbInfo = new RedisRestAPI.BdbInfo("1", Arrays .asList(new RedisRestAPI.EndpointInfo(Arrays.asList("127.0.0.1"), "dummy", 6379, "1:1"))); RedisRestAPI[] reference = new RedisRestAPI[1]; try (MockedConstruction mockedConstructor = mockConstruction(RedisRestAPI.class, (mock, context) -> { when(mock.getBdbs()).thenReturn(Arrays.asList(bdbInfo)); when(mock.checkBdbAvailability("1", true, 5000L)).thenReturn(true); reference[0] = mock; })) { Config lagCheckConfig = Config.builder(endpoint, creds).interval(500).timeout(250) .numProbes(2).build(); try (LagAwareStrategy strategy = new LagAwareStrategy(lagCheckConfig)) { assertEquals(HealthStatus.HEALTHY, strategy.doHealthCheck(endpoint)); RedisRestAPI api = reference[0]; assertEquals(HealthStatus.HEALTHY, strategy.doHealthCheck(endpoint)); verify(api, times(1)).getBdbs(); // Should not call getBdbs again when cached verify(api, times(2)).checkBdbAvailability("1", true, 5000L); } } } @Test void exception_when_no_bdb_returned() throws Exception { RedisRestAPI[] reference = new RedisRestAPI[1]; try (MockedConstruction mockedConstructor = mockConstruction(RedisRestAPI.class, (mock, context) -> { when(mock.getBdbs()).thenReturn(Collections.emptyList()); // No BDBs found reference[0] = mock; })) { Config lagCheckConfig = Config.builder(endpoint, creds).interval(500).timeout(250) .numProbes(1).build(); try (LagAwareStrategy strategy = new LagAwareStrategy(lagCheckConfig)) { assertThrows(JedisException.class, () -> strategy.doHealthCheck(endpoint)); RedisRestAPI api = reference[0]; verify(api, times(1)).getBdbs(); verify(api, never()).checkBdbAvailability(anyString(), anyBoolean()); } } } @Test void exception_and_cache_reset_on_exception_then_recovers_next_time() throws Exception { RedisRestAPI.BdbInfo bdbInfo = new RedisRestAPI.BdbInfo("42", Arrays .asList(new RedisRestAPI.EndpointInfo(Arrays.asList("127.0.0.1"), "dummy", 6379, "1:1"))); RedisRestAPI[] reference = new RedisRestAPI[1]; try (MockedConstruction mockedConstructor = mockConstruction(RedisRestAPI.class, (mock, context) -> { // First call throws exception, second call returns bdbInfo when(mock.getBdbs()).thenThrow(new RuntimeException("boom")) .thenReturn(Arrays.asList(bdbInfo)); when(mock.checkBdbAvailability("42", true, 5000L)).thenReturn(true); reference[0] = mock; })) { Config lagCheckConfig = Config.builder(endpoint, creds).interval(500).timeout(250) .numProbes(1).build(); try (LagAwareStrategy strategy = new LagAwareStrategy(lagCheckConfig)) { RedisRestAPI api = reference[0]; // First call should throw JedisException due to getBdbs() throwing RuntimeException assertThrows(JedisException.class, () -> strategy.doHealthCheck(endpoint)); // Second call should succeed - getBdbs() now returns bdbInfo and availability check passes assertEquals(HealthStatus.HEALTHY, strategy.doHealthCheck(endpoint)); // Verify getBdbs was called twice (once failed, once succeeded) verify(api, times(2)).getBdbs(); // Verify availability check was called only once (on the successful attempt) verify(api, times(1)).checkBdbAvailability("42", true, 5000L); } } } @Test void healthy_when_matching_bdb_found_by_host() throws Exception { RedisRestAPI.BdbInfo matchingBdb = new RedisRestAPI.BdbInfo("matched-bdb-123", Arrays .asList(new RedisRestAPI.EndpointInfo(Arrays.asList("127.0.0.1"), "dummy", 6379, "1:1"))); RedisRestAPI[] reference = new RedisRestAPI[1]; try (MockedConstruction mockedConstructor = mockConstruction(RedisRestAPI.class, (mock, context) -> { when(mock.getBdbs()).thenReturn(Arrays.asList(matchingBdb)); when(mock.checkBdbAvailability("matched-bdb-123", true, 100L)).thenReturn(true); reference[0] = mock; })) { Config lagCheckConfig = Config.builder(endpoint, creds).interval(500).timeout(250) .numProbes(2).extendedCheckEnabled(true).availabilityLagTolerance(Duration.ofMillis(100)) .build(); try (LagAwareStrategy strategy = new LagAwareStrategy(lagCheckConfig)) { assertEquals(HealthStatus.HEALTHY, strategy.doHealthCheck(endpoint)); RedisRestAPI api = reference[0]; verify(api, times(1)).getBdbs(); verify(api, times(1)).checkBdbAvailability("matched-bdb-123", true, 100L); } } } @Test void exception_when_no_matching_host_found() throws Exception { RedisRestAPI.BdbInfo nonMatchingBdb = new RedisRestAPI.BdbInfo("other-bdb-456", Arrays.asList(new RedisRestAPI.EndpointInfo(Arrays.asList("192.168.1.100"), "other-host.example.com", 6379, "2:1"))); RedisRestAPI[] reference = new RedisRestAPI[1]; try (MockedConstruction mockedConstructor = mockConstruction(RedisRestAPI.class, (mock, context) -> { when(mock.getBdbs()).thenReturn(Arrays.asList(nonMatchingBdb)); // BDB that doesn't match // localhost reference[0] = mock; })) { Config lagCheckConfig = Config.builder(endpoint, creds).interval(500).timeout(250) .numProbes(2).build(); try (LagAwareStrategy strategy = new LagAwareStrategy(lagCheckConfig)) { assertThrows(JedisException.class, () -> strategy.doHealthCheck(endpoint)); RedisRestAPI api = reference[0]; verify(api, times(1)).getBdbs(); verify(api, never()).checkBdbAvailability(anyString(), anyBoolean()); // Should not check // availability } } } @Test void config_builder_creates_config_with_default_values() { Config config = Config.builder(endpoint, creds).build(); assertEquals(5000, config.interval); assertEquals(1000, config.timeout); assertEquals(3, config.numProbes); assertEquals(Duration.ofMillis(5000), config.getAvailabilityLagTolerance()); assertEquals(endpoint, config.getRestEndpoint()); assertEquals(creds, config.getCredentialsSupplier()); } @Test void config_builder_creates_config_with_custom_values() { Config config = Config.builder(endpoint, creds).interval(500).timeout(250).numProbes(2) .availabilityLagTolerance(Duration.ofMillis(50)).build(); assertEquals(500, config.interval); assertEquals(250, config.timeout); assertEquals(2, config.numProbes); assertEquals(Duration.ofMillis(50), config.getAvailabilityLagTolerance()); assertEquals(endpoint, config.getRestEndpoint()); assertEquals(creds, config.getCredentialsSupplier()); } @Test void config_builder_allows_fluent_chaining() { // Test that all builder methods return the builder instance for chaining Config config = Config.builder(endpoint, creds).interval(800).timeout(400).numProbes(5) .availabilityLagTolerance(Duration.ofMillis(200)).build(); assertNotNull(config); assertEquals(800, config.interval); assertEquals(400, config.timeout); assertEquals(5, config.numProbes); assertEquals(Duration.ofMillis(200), config.getAvailabilityLagTolerance()); } @Test void config_builder_creates_config_with_extended_check_enabled() { Config config = Config.builder(endpoint, creds).extendedCheckEnabled(true) .availabilityLagTolerance(Duration.ofMillis(150)).build(); assertTrue(config.isExtendedCheckEnabled()); assertEquals(Duration.ofMillis(150), config.getAvailabilityLagTolerance()); } @Test void config_builder_creates_config_with_extended_check_disabled_by_default() { Config config = Config.builder(endpoint, creds).build(); assertTrue(config.isExtendedCheckEnabled()); } @Test void healthy_when_extended_check_enabled_and_lag_check_passes() throws Exception { RedisRestAPI.BdbInfo bdbInfo = new RedisRestAPI.BdbInfo("1", Arrays .asList(new RedisRestAPI.EndpointInfo(Arrays.asList("127.0.0.1"), "dummy", 6379, "1:1"))); RedisRestAPI[] reference = new RedisRestAPI[1]; try (MockedConstruction mockedConstructor = mockConstruction(RedisRestAPI.class, (mock, context) -> { when(mock.getBdbs()).thenReturn(Arrays.asList(bdbInfo)); when(mock.checkBdbAvailability("1", true, 100L)).thenReturn(true); reference[0] = mock; })) { Config config = Config.builder(endpoint, creds).extendedCheckEnabled(true) .availabilityLagTolerance(Duration.ofMillis(100)).build(); try (LagAwareStrategy strategy = new LagAwareStrategy(config)) { assertEquals(HealthStatus.HEALTHY, strategy.doHealthCheck(endpoint)); RedisRestAPI api = reference[0]; verify(api, times(1)).getBdbs(); verify(api, times(1)).checkBdbAvailability("1", true, 100L); verify(api, never()).checkBdbAvailability("1", false); } } } @Test void healthy_when_extended_check_disabled_and_standard_check_passes() throws Exception { RedisRestAPI.BdbInfo bdbInfo = new RedisRestAPI.BdbInfo("1", Arrays .asList(new RedisRestAPI.EndpointInfo(Arrays.asList("127.0.0.1"), "dummy", 6379, "1:1"))); RedisRestAPI[] reference = new RedisRestAPI[1]; try (MockedConstruction mockedConstructor = mockConstruction(RedisRestAPI.class, (mock, context) -> { when(mock.getBdbs()).thenReturn(Arrays.asList(bdbInfo)); when(mock.checkBdbAvailability("1", false)).thenReturn(true); reference[0] = mock; })) { Config config = Config.builder(endpoint, creds).extendedCheckEnabled(false).build(); try (LagAwareStrategy strategy = new LagAwareStrategy(config)) { assertEquals(HealthStatus.HEALTHY, strategy.doHealthCheck(endpoint)); RedisRestAPI api = reference[0]; verify(api, times(1)).getBdbs(); verify(api, times(1)).checkBdbAvailability("1", false); verify(api, never()).checkBdbAvailability(eq("1"), eq(true), any()); } } } @Test void base_config_builder_factory_method_works() { HealthCheckStrategy.Config config = HealthCheckStrategy.Config.builder().interval(2000) .timeout(1500).numProbes(5).build(); assertEquals(2000, config.getInterval()); assertEquals(1500, config.getTimeout()); assertEquals(5, config.getNumProbes()); } @Test void base_config_create_factory_method_uses_defaults() { HealthCheckStrategy.Config config = HealthCheckStrategy.Config.create(); assertEquals(5000, config.getInterval()); assertEquals(1000, config.getTimeout()); assertEquals(3, config.getNumProbes()); } } ================================================ FILE: src/test/java/redis/clients/jedis/mcf/MultiDbCircuitBreakerThresholdsTest.java ================================================ package redis.clients.jedis.mcf; import static org.junit.jupiter.api.Assertions.*; import static org.mockito.ArgumentMatchers.*; import static org.mockito.Mockito.*; import io.github.resilience4j.circuitbreaker.CircuitBreaker; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.CsvSource; import redis.clients.jedis.BuilderFactory; import redis.clients.jedis.CommandArguments; import redis.clients.jedis.CommandObject; import redis.clients.jedis.Connection; import redis.clients.jedis.DefaultJedisClientConfig; import redis.clients.jedis.HostAndPort; import redis.clients.jedis.MultiDbConfig; import redis.clients.jedis.MultiDbConfig.DatabaseConfig; import redis.clients.jedis.Protocol; import redis.clients.jedis.exceptions.JedisConnectionException; import redis.clients.jedis.mcf.MultiDbConnectionProvider.Database; import redis.clients.jedis.util.ReflectionTestUtil; /** * Tests for circuit breaker thresholds: both failure-rate threshold and minimum number of failures * must be exceeded to trigger failover. Uses a real CircuitBreaker and real Retry, but mocks the * provider and database wiring to avoid network I/O. */ public class MultiDbCircuitBreakerThresholdsTest { private MultiDbConnectionProvider realProvider; private MultiDbConnectionProvider spyProvider; private Database database; private MultiDbCommandExecutor executor; private CommandObject dummyCommand; private TrackingConnectionPool poolMock; private final HostAndPort fakeEndpoint = new HostAndPort("fake", 6379); private final HostAndPort fakeEndpoint2 = new HostAndPort("fake2", 6379); private DatabaseConfig[] fakeDatabaseConfigs; @BeforeEach public void setup() throws Exception { DatabaseConfig[] databaseConfigs = new DatabaseConfig[] { DatabaseConfig.builder(fakeEndpoint, DefaultJedisClientConfig.builder().build()) .healthCheckEnabled(false).weight(1.0f).build(), DatabaseConfig.builder(fakeEndpoint2, DefaultJedisClientConfig.builder().build()) .healthCheckEnabled(false).weight(0.5f).build() }; fakeDatabaseConfigs = databaseConfigs; MultiDbConfig.Builder cfgBuilder = MultiDbConfig.builder(databaseConfigs) .failureDetector(MultiDbConfig.CircuitBreakerConfig.builder().failureRateThreshold(50.0f) .minNumOfFailures(3).slidingWindowSize(10).build()) .commandRetry(MultiDbConfig.RetryConfig.builder().maxAttempts(1).build()) .retryOnFailover(false); MultiDbConfig mcc = cfgBuilder.build(); realProvider = new MultiDbConnectionProvider(mcc); spyProvider = spy(realProvider); database = spyProvider.getDatabase(); executor = new MultiDbCommandExecutor(spyProvider); dummyCommand = new CommandObject<>(new CommandArguments(Protocol.Command.PING), BuilderFactory.STRING); // Replace the database's pool with a mock to avoid real network I/O poolMock = mock(TrackingConnectionPool.class); ReflectionTestUtil.setField(database, "connectionPool", poolMock); } /** * Below minimum failures; even if all calls are failures, failover should NOT trigger. */ @Test public void belowMinFailures_doesNotFailover() { // Always failing connections Connection failing = mock(Connection.class); when(failing.executeCommand(org.mockito.Mockito.> any())) .thenThrow(new JedisConnectionException("fail")); doNothing().when(failing).close(); when(poolMock.getResource()).thenReturn(failing); for (int i = 0; i < 2; i++) { assertThrows(JedisConnectionException.class, () -> executor.executeCommand(dummyCommand)); } // Below min failures; CB remains CLOSED assertEquals(CircuitBreaker.State.CLOSED, spyProvider.getDatabaseCircuitBreaker().getState()); } /** * Reaching minFailures and exceeding failure rate threshold should trigger failover. */ @Test public void minFailuresAndRateExceeded_triggersFailover() { // Always failing connections Connection failing = mock(Connection.class); when(failing.executeCommand(org.mockito.Mockito.> any())) .thenThrow(new JedisConnectionException("fail")); doNothing().when(failing).close(); when(poolMock.getResource()).thenReturn(failing); // Reach min failures and exceed rate threshold for (int i = 0; i < 3; i++) { assertThrows(JedisConnectionException.class, () -> executor.executeCommand(dummyCommand)); } // Next call should hit open CB (CallNotPermitted) and trigger failover assertThrows(JedisConnectionException.class, () -> executor.executeCommand(dummyCommand)); verify(spyProvider, atLeastOnce()).switchToHealthyDatabase(eq(SwitchReason.CIRCUIT_BREAKER), any()); assertEquals(CircuitBreaker.State.FORCED_OPEN, spyProvider.getDatabase(fakeEndpoint).getCircuitBreaker().getState()); } /** * Even after reaching minFailures, if failure rate is below threshold, do not failover. */ @Test public void rateBelowThreshold_doesNotFailover() throws Exception { // Use local provider with higher threshold (80%) and no retries MultiDbConfig.Builder cfgBuilder = MultiDbConfig.builder(fakeDatabaseConfigs) .failureDetector(MultiDbConfig.CircuitBreakerConfig.builder().failureRateThreshold(80.0f) .minNumOfFailures(3).slidingWindowSize(10).build()) .commandRetry(MultiDbConfig.RetryConfig.builder().maxAttempts(1).build()) .retryOnFailover(false); MultiDbConnectionProvider rp = new MultiDbConnectionProvider(cfgBuilder.build()); MultiDbConnectionProvider sp = spy(rp); Database c = sp.getDatabase(); try (MultiDbCommandExecutor ex = new MultiDbCommandExecutor(sp)) { CommandObject cmd = new CommandObject<>(new CommandArguments(Protocol.Command.PING), BuilderFactory.STRING); TrackingConnectionPool pool = mock(TrackingConnectionPool.class); ReflectionTestUtil.setField(c, "connectionPool", pool); // 3 successes Connection success = mock(Connection.class); when(success.executeCommand(org.mockito.Mockito.> any())) .thenReturn("PONG"); doNothing().when(success).close(); when(pool.getResource()).thenReturn(success); for (int i = 0; i < 3; i++) { assertEquals("PONG", ex.executeCommand(cmd)); } // 3 failures -> total 6 calls, 50% failure rate; threshold 80% means stay CLOSED Connection failing = mock(Connection.class); when(failing.executeCommand(org.mockito.Mockito.> any())) .thenThrow(new JedisConnectionException("fail")); doNothing().when(failing).close(); when(pool.getResource()).thenReturn(failing); for (int i = 0; i < 3; i++) { assertThrows(JedisConnectionException.class, () -> ex.executeCommand(cmd)); } assertEquals(CircuitBreaker.State.CLOSED, sp.getDatabaseCircuitBreaker().getState()); } } @Test public void providerBuilder_zeroRate_mapsToHundredAndHugeMinCalls() { MultiDbConfig.Builder cfgBuilder = MultiDbConfig.builder(fakeDatabaseConfigs); cfgBuilder.failureDetector(MultiDbConfig.CircuitBreakerConfig.builder() .failureRateThreshold(0.0f).minNumOfFailures(3).slidingWindowSize(10).build()); MultiDbConfig mcc = cfgBuilder.build(); CircuitBreakerThresholdsAdapter adapter = new CircuitBreakerThresholdsAdapter(mcc); assertEquals(100.0f, adapter.getFailureRateThreshold(), 0.0001f); assertEquals(Integer.MAX_VALUE, adapter.getMinimumNumberOfCalls()); } @ParameterizedTest @CsvSource({ // minFailures, ratePercent, successes, failures, expectFailoverOnNext "0, 1.0, 0, 1, true", // "1, 1.0, 0, 1, true", // "3, 50.0, 0, 3, true", // "1, 100.0, 0, 1, true", // "0, 100.0, 99, 1, false", // "0, 1.0, 99, 1, true", // // additional edge cases "1, 0.0, 0, 1, true", // "3, 50.0, 3, 2, false", // "1000, 1.0, 198, 2, false", }) public void thresholdMatrix(int minFailures, float ratePercent, int successes, int failures, boolean expectFailoverOnNext) throws Exception { MultiDbConfig.Builder cfgBuilder = MultiDbConfig.builder(fakeDatabaseConfigs) .failureDetector(MultiDbConfig.CircuitBreakerConfig.builder() .failureRateThreshold(ratePercent).minNumOfFailures(minFailures) .slidingWindowSize(Math.max(10, successes + failures + 2)).build()) .commandRetry(MultiDbConfig.RetryConfig.builder().maxAttempts(1).build()) .retryOnFailover(false); MultiDbConnectionProvider real = new MultiDbConnectionProvider(cfgBuilder.build()); MultiDbConnectionProvider spy = spy(real); Database c = spy.getDatabase(); try (MultiDbCommandExecutor ex = new MultiDbCommandExecutor(spy)) { CommandObject cmd = new CommandObject<>(new CommandArguments(Protocol.Command.PING), BuilderFactory.STRING); TrackingConnectionPool pool = mock(TrackingConnectionPool.class); ReflectionTestUtil.setField(c, "connectionPool", pool); if (successes > 0) { Connection ok = mock(Connection.class); when(ok.executeCommand(org.mockito.Mockito.> any())) .thenReturn("PONG"); doNothing().when(ok).close(); when(pool.getResource()).thenReturn(ok); for (int i = 0; i < successes; i++) { ex.executeCommand(cmd); } } if (failures > 0) { Connection bad = mock(Connection.class); when(bad.executeCommand(org.mockito.Mockito.> any())) .thenThrow(new JedisConnectionException("fail")); doNothing().when(bad).close(); when(pool.getResource()).thenReturn(bad); for (int i = 0; i < failures; i++) { try { ex.executeCommand(cmd); } catch (Exception ignore) { } } } if (expectFailoverOnNext) { assertThrows(Exception.class, () -> ex.executeCommand(cmd)); verify(spy, atLeastOnce()).switchToHealthyDatabase(eq(SwitchReason.CIRCUIT_BREAKER), any()); assertEquals(CircuitBreaker.State.FORCED_OPEN, c.getCircuitBreaker().getState()); } else { CircuitBreaker.State st = c.getCircuitBreaker().getState(); assertTrue(st == CircuitBreaker.State.CLOSED || st == CircuitBreaker.State.HALF_OPEN); } } } } ================================================ FILE: src/test/java/redis/clients/jedis/mcf/MultiDbConnectionProviderDynamicEndpointUnitTest.java ================================================ package redis.clients.jedis.mcf; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.mockito.MockedConstruction; import redis.clients.jedis.Connection; import redis.clients.jedis.DefaultJedisClientConfig; import redis.clients.jedis.Endpoint; import redis.clients.jedis.EndpointConfig; import redis.clients.jedis.HostAndPort; import redis.clients.jedis.Endpoints; import redis.clients.jedis.JedisClientConfig; import redis.clients.jedis.MultiDbClient; import redis.clients.jedis.MultiDbConfig; import redis.clients.jedis.MultiDbConfig.DatabaseConfig; import redis.clients.jedis.exceptions.JedisValidationException; import static org.junit.jupiter.api.Assertions.*; import static org.mockito.Mockito.doNothing; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.mockConstruction; import static org.mockito.Mockito.when; public class MultiDbConnectionProviderDynamicEndpointUnitTest { private MultiDbConnectionProvider provider; private JedisClientConfig clientConfig; private static EndpointConfig endpoint1; private static EndpointConfig endpoint2; private static final float WEIGHT1 = 0.9f; private static final float WEIGHT2 = 0.8f; @BeforeAll static void prepareEndpoints() { endpoint1 = Endpoints.getRedisEndpoint("standalone0"); endpoint2 = Endpoints.getRedisEndpoint("standalone1"); } @BeforeEach void setUp() { clientConfig = DefaultJedisClientConfig.builder().build(); // Create initial provider with endpoint1 DatabaseConfig initialConfig = createDatabaseConfig(endpoint1.getHostAndPort(), 1.0f); MultiDbConfig multiConfig = new MultiDbConfig.Builder(new DatabaseConfig[] { initialConfig }) .build(); provider = new MultiDbConnectionProvider(multiConfig); } // Helper method to create database configurations private DatabaseConfig createDatabaseConfig(HostAndPort hostAndPort, float weight) { // Disable health check for unit tests to avoid real connections return DatabaseConfig.builder(hostAndPort, clientConfig).weight(weight) .healthCheckEnabled(false).build(); } @Test void testAddNewDatabase() { DatabaseConfig newConfig = createDatabaseConfig(endpoint2.getHostAndPort(), 2.0f); // Should not throw exception assertDoesNotThrow(() -> provider.add(newConfig)); // Verify the database was added by checking it can be retrieved assertNotNull(provider.getDatabase(endpoint2.getHostAndPort())); } @Test void testAddDuplicateDatabase() { DatabaseConfig duplicateConfig = createDatabaseConfig(endpoint1.getHostAndPort(), 2.0f); // Should throw validation exception for duplicate endpoint assertThrows(JedisValidationException.class, () -> provider.add(duplicateConfig)); } @Test void testAddNullDatabaseConfig() { // Should throw validation exception for null config assertThrows(JedisValidationException.class, () -> provider.add(null)); } @Test void testRemoveExistingDatabase() { Connection mockConnection = mock(Connection.class); when(mockConnection.ping()).thenReturn(true); try (MockedConstruction mockedPool = mockPool(mockConnection)) { // Create initial provider with endpoint1 DatabaseConfig dbConfig1 = createDatabaseConfig(endpoint1.getHostAndPort(), 1.0f); MultiDbConfig multiConfig = MultiDbConfig.builder(new DatabaseConfig[] { dbConfig1 }).build(); try (MultiDbConnectionProvider providerWithMockedPool = new MultiDbConnectionProvider( multiConfig)) { // Add endpoint2 as second database DatabaseConfig newConfig = createDatabaseConfig(endpoint2.getHostAndPort(), 2.0f); providerWithMockedPool.add(newConfig); // Now remove endpoint1 (original database) assertDoesNotThrow(() -> providerWithMockedPool.remove(endpoint1.getHostAndPort())); // Verify endpoint1 was removed assertNull(providerWithMockedPool.getDatabase(endpoint1.getHostAndPort())); // Verify endpoint2 still exists assertNotNull(providerWithMockedPool.getDatabase(endpoint2.getHostAndPort())); } } } private MockedConstruction mockPool(Connection mockConnection) { return mockConstruction(TrackingConnectionPool.class, (mock, context) -> { when(mock.getResource()).thenReturn(mockConnection); doNothing().when(mock).close(); }); } @Test void testRemoveNonExistentDatabase() { HostAndPort nonExistentEndpoint = new HostAndPort("dummy", 9999); // Should throw validation exception for non-existent endpoint assertThrows(JedisValidationException.class, () -> provider.remove(nonExistentEndpoint)); } @Test void testRemoveLastRemainingDatabase() { // Should throw validation exception when trying to remove the last database assertThrows(JedisValidationException.class, () -> provider.remove(endpoint1.getHostAndPort())); } @Test void testRemoveNullEndpoint() { // Should throw validation exception for null endpoint assertThrows(JedisValidationException.class, () -> provider.remove(null)); } @Test void testAddAndRemoveMultipleDatabases() { // Add endpoint2 as second database DatabaseConfig config2 = createDatabaseConfig(endpoint2.getHostAndPort(), 2.0f); // Create a third endpoint for this test HostAndPort endpoint3 = new HostAndPort("dummy", 6381); DatabaseConfig config3 = createDatabaseConfig(endpoint3, 3.0f); provider.add(config2); provider.add(config3); // Verify all databases exist assertNotNull(provider.getDatabase(endpoint1.getHostAndPort())); assertNotNull(provider.getDatabase(endpoint2.getHostAndPort())); assertNotNull(provider.getDatabase(endpoint3)); // Remove endpoint2 provider.remove(endpoint2.getHostAndPort()); // Verify correct database was removed assertNull(provider.getDatabase(endpoint2.getHostAndPort())); assertNotNull(provider.getDatabase(endpoint1.getHostAndPort())); assertNotNull(provider.getDatabase(endpoint3)); } @Test void testActiveDatabaseHandlingOnAdd() { // The initial database should be active assertNotNull(provider.getDatabase()); // Add endpoint2 with higher weight DatabaseConfig newConfig = createDatabaseConfig(endpoint2.getHostAndPort(), 5.0f); provider.add(newConfig); // Active database should still be valid (implementation may or may not switch) assertNotNull(provider.getDatabase()); } @Test void testActiveDatabaseHandlingOnRemove() { Connection mockConnection = mock(Connection.class); when(mockConnection.ping()).thenReturn(true); try (MockedConstruction mockedPool = mockPool(mockConnection)) { // Create initial provider with endpoint1 DatabaseConfig dbConfig1 = createDatabaseConfig(endpoint1.getHostAndPort(), 1.0f); MultiDbConfig multiConfig = MultiDbConfig.builder(new DatabaseConfig[] { dbConfig1 }).build(); try (MultiDbConnectionProvider providerWithMockedPool = new MultiDbConnectionProvider( multiConfig)) { // Add endpoint2 as second database DatabaseConfig newConfig = createDatabaseConfig(endpoint2.getHostAndPort(), 2.0f); providerWithMockedPool.add(newConfig); // Get current active database Object initialActiveDb = providerWithMockedPool.getDatabase(); assertNotNull(initialActiveDb); // Remove endpoint1 (original database, might be active) providerWithMockedPool.remove(endpoint1.getHostAndPort()); // Should still have an active database assertNotNull(providerWithMockedPool.getDatabase()); } } } private MultiDbClient getClient() { MultiDbClient client = MultiDbClient.builder() .multiDbConfig(MultiDbConfig.builder() .database(createDatabaseConfig(endpoint1.getHostAndPort(), WEIGHT1)) .database(createDatabaseConfig(endpoint2.getHostAndPort(), WEIGHT2)).build()) .build(); return client; } @Test void testGetWeight() { MultiDbClient client = getClient(); // Verify we can get the initial weight set during configuration float weight1 = client.getWeight(endpoint1.getHostAndPort()); float weight2 = client.getWeight(endpoint2.getHostAndPort()); assertEquals(WEIGHT1, weight1); assertEquals(WEIGHT2, weight2); } @Test void testGetWeightWithNonExistingEndpoint() { MultiDbClient client = getClient(); Endpoint nonExistingEndpoint = new HostAndPort("non-existing-host", 6379); assertThrows(JedisValidationException.class, () -> client.getWeight(nonExistingEndpoint)); } @Test void testSetWeightWithNonExistingEndpoint() { MultiDbClient client = getClient(); Endpoint nonExistingEndpoint = new HostAndPort("non-existing-host", 6379); assertThrows(JedisValidationException.class, () -> client.setWeight(nonExistingEndpoint, 1.0f)); } @Test void testSetWeight() { MultiDbClient client = getClient(); Endpoint endpoint = endpoint1.getHostAndPort(); // Verify initial weight assertEquals(WEIGHT1, client.getWeight(endpoint)); // Set a new weight client.setWeight(endpoint, 75.0f); // Verify the weight has changed assertEquals(75.0f, client.getWeight(endpoint)); } @Test void testSetWeightToZero() { MultiDbClient client = getClient(); Endpoint endpoint = endpoint2.getHostAndPort(); // Set weight to zero assertThrows(IllegalArgumentException.class, () -> client.setWeight(endpoint, 0.0f)); } @Test void testSetWeightMultipleTimes() { MultiDbClient client = getClient(); Endpoint endpoint = endpoint1.getHostAndPort(); // Set weight multiple times client.setWeight(endpoint, 25.0f); assertEquals(25.0f, client.getWeight(endpoint)); client.setWeight(endpoint, 80.0f); assertEquals(80.0f, client.getWeight(endpoint)); client.setWeight(endpoint, 1.0f); assertEquals(1.0f, client.getWeight(endpoint)); } @Test void testSetWeightWithNegativeWeight() { MultiDbClient client = getClient(); Endpoint endpoint = endpoint1.getHostAndPort(); // Verify that setting a negative weight is rejected assertThrows(IllegalArgumentException.class, () -> client.setWeight(endpoint, -1.0f)); } } ================================================ FILE: src/test/java/redis/clients/jedis/mcf/MultiDbConnectionProviderFailoverAttemptsConfigTest.java ================================================ package redis.clients.jedis.mcf; import org.awaitility.Durations; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import redis.clients.jedis.DefaultJedisClientConfig; import redis.clients.jedis.HostAndPort; import redis.clients.jedis.JedisClientConfig; import redis.clients.jedis.MultiDbConfig; import redis.clients.jedis.MultiDbConfig.DatabaseConfig; import redis.clients.jedis.mcf.JedisFailoverException.JedisPermanentlyNotAvailableException; import redis.clients.jedis.mcf.JedisFailoverException.JedisTemporarilyNotAvailableException; import redis.clients.jedis.util.ReflectionTestUtil; import java.time.Duration; import java.util.concurrent.atomic.AtomicInteger; import static org.junit.jupiter.api.Assertions.*; import static org.awaitility.Awaitility.await; /** * Tests for how getMaxNumFailoverAttempts and getDelayInBetweenFailoverAttempts impact * MultiDbConnectionProvider behaviour when no healthy databases are available. */ public class MultiDbConnectionProviderFailoverAttemptsConfigTest { private HostAndPort endpoint0 = new HostAndPort("purposefully-incorrect", 0000); private HostAndPort endpoint1 = new HostAndPort("purposefully-incorrect", 0001); private MultiDbConnectionProvider provider; @BeforeEach void setUp() throws Exception { JedisClientConfig clientCfg = DefaultJedisClientConfig.builder().build(); DatabaseConfig[] databaseConfigs = new DatabaseConfig[] { DatabaseConfig.builder(endpoint0, clientCfg).weight(1.0f).healthCheckEnabled(false).build(), DatabaseConfig.builder(endpoint1, clientCfg).weight(0.5f).healthCheckEnabled(false) .build() }; MultiDbConfig.Builder builder = new MultiDbConfig.Builder(databaseConfigs); // Use small values by default for tests unless overridden per-test via reflection setBuilderFailoverConfig(builder, /* maxAttempts */ 10, /* delayMs */ 12000); provider = new MultiDbConnectionProvider(builder.build()); // Disable both databases to force handleNoHealthyDatabase path provider.getDatabase(endpoint0).setDisabled(true); provider.getDatabase(endpoint1).setDisabled(true); } @AfterEach void tearDown() { if (provider != null) provider.close(); } @Test void delayBetweenFailoverAttempts_gatesCounterIncrementsWithinWindow() throws Exception { // Configure: small max (2) with a large non-zero delay window to ensure rapid calls stay within // window setProviderFailoverConfig(/* maxAttempts */ 2, /* delayMs */ 5000); assertEquals(2, getProviderMaxAttempts()); assertEquals(5000, getProviderDelayMs()); assertEquals(0, getProviderAttemptCount()); // First call: should throw temporary and start the freeze window, incrementing attempt count to // 1 assertThrows(JedisTemporarilyNotAvailableException.class, () -> provider.switchToHealthyDatabase(SwitchReason.HEALTH_CHECK, provider.getDatabase())); int afterFirst = getProviderAttemptCount(); assertEquals(1, afterFirst); // Many rapid subsequent calls within the delay window should continue to throw temporary // and should NOT increment the attempt count beyond 1 for (int i = 0; i < 50; i++) { assertThrows(JedisTemporarilyNotAvailableException.class, () -> provider.switchToHealthyDatabase(SwitchReason.HEALTH_CHECK, provider.getDatabase())); assertEquals(1, getProviderAttemptCount()); } } @Test void delayBetweenFailoverAttempts_permanentExceptionAfterAttemptsExhausted() throws Exception { // Configure: small max (2) with a large non-zero delay window to ensure rapid calls stay within // window setProviderFailoverConfig(/* maxAttempts */ 2, /* delayMs */ 20); assertEquals(2, getProviderMaxAttempts()); assertEquals(20, getProviderDelayMs()); assertEquals(0, getProviderAttemptCount()); // First call: should throw temporary and start the freeze window, incrementing attempt count to // 1 assertThrows(JedisTemporarilyNotAvailableException.class, () -> provider.switchToHealthyDatabase(SwitchReason.HEALTH_CHECK, provider.getDatabase())); int afterFirst = getProviderAttemptCount(); assertEquals(1, afterFirst); // Many rapid subsequent calls within the delay window should continue to throw temporary // and should NOT increment the attempt count beyond 1 for (int i = 0; i < 50; i++) { assertThrows(JedisTemporarilyNotAvailableException.class, () -> provider.switchToHealthyDatabase(SwitchReason.HEALTH_CHECK, provider.getDatabase())); assertEquals(1, getProviderAttemptCount()); } await().atMost(Durations.TWO_HUNDRED_MILLISECONDS).pollInterval(Duration.ofMillis(10)) .until(() -> { Exception e = assertThrows(JedisFailoverException.class, () -> provider .switchToHealthyDatabase(SwitchReason.HEALTH_CHECK, provider.getDatabase())); return e instanceof JedisPermanentlyNotAvailableException; }); } @Test void maxNumFailoverAttempts_zeroDelay_leadsToPermanentAfterExceeding() throws Exception { // Configure: maxAttempts = 2, delay = 0 so each call increments the counter immediately setProviderFailoverConfig(/* maxAttempts */ 2, /* delayMs */ 0); assertEquals(2, getProviderMaxAttempts()); assertEquals(0, getProviderDelayMs()); assertEquals(0, getProviderAttemptCount()); // Expect exactly 'maxAttempts' temporary exceptions, then a permanent one assertThrows(JedisTemporarilyNotAvailableException.class, () -> provider.switchToHealthyDatabase(SwitchReason.HEALTH_CHECK, provider.getDatabase())); // attempt // 1 assertThrows(JedisTemporarilyNotAvailableException.class, () -> provider.switchToHealthyDatabase(SwitchReason.HEALTH_CHECK, provider.getDatabase())); // attempt // 2 // Next should exceed max and become permanent assertThrows(JedisPermanentlyNotAvailableException.class, () -> provider.switchToHealthyDatabase(SwitchReason.HEALTH_CHECK, provider.getDatabase())); // attempt // 3 // -> // permanent } // ======== Test helper methods (reflection) ======== private static void setBuilderFailoverConfig(MultiDbConfig.Builder builder, int maxAttempts, int delayMs) throws Exception { ReflectionTestUtil.setField(builder, "maxNumFailoverAttempts", maxAttempts); ReflectionTestUtil.setField(builder, "delayInBetweenFailoverAttempts", delayMs); } private void setProviderFailoverConfig(int maxAttempts, int delayMs) throws Exception { // Access the underlying MultiDbConfig inside provider and adjust fields for this // test Object cfg = ReflectionTestUtil.getField(provider, "multiDbConfig"); ReflectionTestUtil.setField(cfg, "maxNumFailoverAttempts", maxAttempts); ReflectionTestUtil.setField(cfg, "delayInBetweenFailoverAttempts", delayMs); } private int getProviderMaxAttempts() throws Exception { Object cfg = ReflectionTestUtil.getField(provider, "multiDbConfig"); return ReflectionTestUtil.getField(cfg, "maxNumFailoverAttempts"); } private int getProviderDelayMs() throws Exception { Object cfg = ReflectionTestUtil.getField(provider, "multiDbConfig"); return ReflectionTestUtil.getField(cfg, "delayInBetweenFailoverAttempts"); } private int getProviderAttemptCount() throws Exception { AtomicInteger val = ReflectionTestUtil.getField(provider, "failoverAttemptCount"); return val.get(); } } ================================================ FILE: src/test/java/redis/clients/jedis/mcf/MultiDbConnectionProviderHelper.java ================================================ package redis.clients.jedis.mcf; import redis.clients.jedis.Endpoint; public class MultiDbConnectionProviderHelper { public static void onHealthStatusChange(MultiDbConnectionProvider provider, Endpoint endpoint, HealthStatus oldStatus, HealthStatus newStatus) { provider.onHealthStatusChange(new HealthStatusChangeEvent(endpoint, oldStatus, newStatus)); } public static void periodicFailbackCheck(MultiDbConnectionProvider provider) { provider.periodicFailbackCheck(); } public static Endpoint switchToHealthyDatabase(MultiDbConnectionProvider provider, SwitchReason reason, MultiDbConnectionProvider.Database iterateFrom) { return provider.switchToHealthyDatabase(reason, iterateFrom); } public static MultiDbConnectionProvider.Database waitForInitializationPolicy( MultiDbConnectionProvider provider, StatusTracker statusTracker) { return provider.waitForInitializationPolicy(statusTracker); } } ================================================ FILE: src/test/java/redis/clients/jedis/mcf/MultiDbConnectionProviderInitializationTest.java ================================================ package redis.clients.jedis.mcf; import static org.junit.jupiter.api.Assertions.*; import static org.mockito.Mockito.*; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; import org.mockito.MockedConstruction; import org.mockito.junit.jupiter.MockitoExtension; import redis.clients.jedis.Connection; import redis.clients.jedis.ConnectionPool; import redis.clients.jedis.DefaultJedisClientConfig; import redis.clients.jedis.HostAndPort; import redis.clients.jedis.JedisClientConfig; import redis.clients.jedis.MultiDbConfig; import redis.clients.jedis.MultiDbConfig.DatabaseConfig; import redis.clients.jedis.exceptions.JedisValidationException; /** * Tests for MultiDbConnectionProvider initialization edge cases */ @ExtendWith(MockitoExtension.class) public class MultiDbConnectionProviderInitializationTest { private HostAndPort endpoint1; private HostAndPort endpoint2; private HostAndPort endpoint3; private JedisClientConfig clientConfig; @BeforeEach void setUp() { endpoint1 = new HostAndPort("fake", 6379); endpoint2 = new HostAndPort("fake", 6380); endpoint3 = new HostAndPort("fake", 6381); clientConfig = DefaultJedisClientConfig.builder().build(); } private MockedConstruction mockPool() { Connection mockConnection = mock(Connection.class); lenient().when(mockConnection.ping()).thenReturn(true); return mockConstruction(ConnectionPool.class, (mock, context) -> { when(mock.getResource()).thenReturn(mockConnection); doNothing().when(mock).close(); }); } @Test void testInitializationWithMixedHealthCheckConfiguration() { try (MockedConstruction mockedPool = mockPool()) { // Create databases with mixed health check configuration DatabaseConfig db1 = DatabaseConfig.builder(endpoint1, clientConfig).weight(1.0f) .healthCheckEnabled(false) // No health // check .build(); DatabaseConfig db2 = DatabaseConfig.builder(endpoint2, clientConfig).weight(2.0f) .healthCheckStrategySupplier(PingStrategy.DEFAULT) // With // health // check .build(); MultiDbConfig config = new MultiDbConfig.Builder(new DatabaseConfig[] { db1, db2 }) .initializationPolicy(InitializationPolicy.BuiltIn.ONE_AVAILABLE).build(); try (MultiDbConnectionProvider provider = new MultiDbConnectionProvider(config)) { // Should initialize successfully assertNotNull(provider.getDatabase()); // Should select db1 (no health check, assumed healthy) or db2 based on weight // Since db2 has higher weight and health checks, it should be selected if healthy assertTrue(provider.getDatabase() == provider.getDatabase(endpoint1) || provider.getDatabase() == provider.getDatabase(endpoint2)); } } } @Test void testInitializationWithAllHealthChecksDisabled() { try (MockedConstruction mockedPool = mockPool()) { // Create databases with no health checks DatabaseConfig db1 = DatabaseConfig.builder(endpoint1, clientConfig).weight(1.0f) .healthCheckEnabled(false).build(); DatabaseConfig db22 = DatabaseConfig.builder(endpoint2, clientConfig).weight(3.0f) // Higher // weight .healthCheckEnabled(false).build(); MultiDbConfig config = new MultiDbConfig.Builder(new DatabaseConfig[] { db1, db22 }).build(); try (MultiDbConnectionProvider provider = new MultiDbConnectionProvider(config)) { // Should select db22 (highest weight, no health checks) assertEquals(provider.getDatabase(endpoint2), provider.getDatabase()); } } } @Test void testInitializationWithSingleDatabase() { try (MockedConstruction mockedPool = mockPool()) { DatabaseConfig db = DatabaseConfig.builder(endpoint1, clientConfig).weight(1.0f) .healthCheckEnabled(false).build(); MultiDbConfig config = new MultiDbConfig.Builder(new DatabaseConfig[] { db }).build(); try (MultiDbConnectionProvider provider = new MultiDbConnectionProvider(config)) { // Should select the only available db assertEquals(provider.getDatabase(endpoint1), provider.getDatabase()); } } } @Test void testErrorHandlingWithNullConfiguration() { assertThrows(JedisValidationException.class, () -> { new MultiDbConnectionProvider(null); }); } @Test void testErrorHandlingWithEmptyDatabaseArray() { assertThrows(JedisValidationException.class, () -> { new MultiDbConfig.Builder(new DatabaseConfig[0]).build(); }); } @Test void testErrorHandlingWithNullDatabaseConfig() { assertThrows(IllegalArgumentException.class, () -> { new MultiDbConfig.Builder(new DatabaseConfig[] { null }).build(); }); } @Test void testInitializationWithZeroWeights() { assertThrows(IllegalArgumentException.class, () -> { DatabaseConfig.builder(endpoint1, clientConfig).weight(0.0f); }); } @Test void testInitializationWithOneAvailablePolicy() { try (MockedConstruction mockedPool = mockPool()) { DatabaseConfig db1 = DatabaseConfig.builder(endpoint1, clientConfig).weight(1.0f) .healthCheckEnabled(false).build(); DatabaseConfig db2 = DatabaseConfig.builder(endpoint2, clientConfig).weight(2.0f) .healthCheckEnabled(false).build(); MultiDbConfig config = new MultiDbConfig.Builder(new DatabaseConfig[] { db1, db2 }) .initializationPolicy(InitializationPolicy.BuiltIn.ONE_AVAILABLE).build(); try (MultiDbConnectionProvider provider = new MultiDbConnectionProvider(config)) { // Should initialize successfully with ONE_AVAILABLE policy assertNotNull(provider.getDatabase()); } } } @Test void testInitializationWithAllAvailablePolicy() { try (MockedConstruction mockedPool = mockPool()) { DatabaseConfig db1 = DatabaseConfig.builder(endpoint1, clientConfig).weight(1.0f) .healthCheckEnabled(false).build(); DatabaseConfig db2 = DatabaseConfig.builder(endpoint2, clientConfig).weight(2.0f) .healthCheckEnabled(false).build(); MultiDbConfig config = new MultiDbConfig.Builder(new DatabaseConfig[] { db1, db2 }) .initializationPolicy(InitializationPolicy.BuiltIn.ALL_AVAILABLE).build(); try (MultiDbConnectionProvider provider = new MultiDbConnectionProvider(config)) { // Should initialize successfully with ALL_AVAILABLE policy when all health checks // are disabled assertNotNull(provider.getDatabase()); } } } @Test void testInitializationWithMajorityAvailablePolicy() { try (MockedConstruction mockedPool = mockPool()) { DatabaseConfig db1 = DatabaseConfig.builder(endpoint1, clientConfig).weight(1.0f) .healthCheckEnabled(false).build(); DatabaseConfig db2 = DatabaseConfig.builder(endpoint2, clientConfig).weight(2.0f) .healthCheckEnabled(false).build(); DatabaseConfig db3 = DatabaseConfig.builder(endpoint3, clientConfig).weight(3.0f) .healthCheckEnabled(false).build(); MultiDbConfig config = new MultiDbConfig.Builder(new DatabaseConfig[] { db1, db2, db3 }) .initializationPolicy(InitializationPolicy.BuiltIn.MAJORITY_AVAILABLE).build(); try (MultiDbConnectionProvider provider = new MultiDbConnectionProvider(config)) { // Should initialize successfully with MAJORITY_AVAILABLE policy assertNotNull(provider.getDatabase()); // Should select db3 (highest weight) assertEquals(provider.getDatabase(endpoint3), provider.getDatabase()); } } } @Test void testInitializationPolicyNullThrowsException() { DatabaseConfig db = DatabaseConfig.builder(endpoint1, clientConfig).weight(1.0f) .healthCheckEnabled(false).build(); assertThrows(IllegalArgumentException.class, () -> { new MultiDbConfig.Builder(new DatabaseConfig[] { db }).initializationPolicy(null).build(); }); } @Test void testInitializationPolicyIsConfigured() { DatabaseConfig db = DatabaseConfig.builder(endpoint1, clientConfig).weight(1.0f) .healthCheckEnabled(false).build(); MultiDbConfig config = new MultiDbConfig.Builder(new DatabaseConfig[] { db }) .initializationPolicy(InitializationPolicy.BuiltIn.ALL_AVAILABLE).build(); assertEquals(InitializationPolicy.BuiltIn.ALL_AVAILABLE, config.getInitializationPolicy()); } @Test void testInitializationPolicyDefaultValue() { DatabaseConfig db = DatabaseConfig.builder(endpoint1, clientConfig).weight(1.0f) .healthCheckEnabled(false).build(); MultiDbConfig config = new MultiDbConfig.Builder(new DatabaseConfig[] { db }).build(); // Default should be MAJORITY_AVAILABLE assertEquals(InitializationPolicy.BuiltIn.MAJORITY_AVAILABLE, config.getInitializationPolicy()); } } ================================================ FILE: src/test/java/redis/clients/jedis/mcf/MultiDbConnectionProviderTest.java ================================================ package redis.clients.jedis.mcf; import io.github.resilience4j.circuitbreaker.CircuitBreaker; import org.awaitility.Awaitility; import org.awaitility.Durations; import org.junit.jupiter.api.*; import redis.clients.jedis.*; import redis.clients.jedis.MultiDbConfig.DatabaseConfig; import redis.clients.jedis.exceptions.JedisConnectionException; import redis.clients.jedis.exceptions.JedisValidationException; import redis.clients.jedis.mcf.MultiDbConnectionProvider.Database; import redis.clients.jedis.mcf.ProbingPolicy.BuiltIn; import redis.clients.jedis.mcf.JedisFailoverException.JedisPermanentlyNotAvailableException; import redis.clients.jedis.mcf.JedisFailoverException.JedisTemporarilyNotAvailableException; import java.time.Duration; import java.util.Arrays; import java.util.Collections; import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.atomic.AtomicInteger; import static org.awaitility.Awaitility.await; import static org.junit.jupiter.api.Assertions.*; /** * @see MultiDbConnectionProvider */ @Tag("integration") public class MultiDbConnectionProviderTest { private static EndpointConfig endpointStandalone0; private static EndpointConfig endpointStandalone1; private MultiDbConnectionProvider provider; @BeforeAll public static void prepareEndpoints() { endpointStandalone0 = Endpoints.getRedisEndpoint("standalone0"); endpointStandalone1 = Endpoints.getRedisEndpoint("standalone1"); } @BeforeEach public void setUp() { DatabaseConfig[] databaseConfigs = new DatabaseConfig[2]; databaseConfigs[0] = DatabaseConfig.builder(endpointStandalone0.getHostAndPort(), endpointStandalone0.getClientConfigBuilder().build()).weight(0.5f).build(); databaseConfigs[1] = DatabaseConfig.builder(endpointStandalone1.getHostAndPort(), endpointStandalone1.getClientConfigBuilder().build()).weight(0.3f).build(); provider = new MultiDbConnectionProvider(new MultiDbConfig.Builder(databaseConfigs).build()); } @AfterEach public void destroy() { provider.close(); provider = null; } @Test public void testCircuitBreakerForcedTransitions() { CircuitBreaker circuitBreaker = provider.getDatabaseCircuitBreaker(); circuitBreaker.getState(); if (CircuitBreaker.State.FORCED_OPEN.equals(circuitBreaker.getState())) circuitBreaker.transitionToClosedState(); circuitBreaker.transitionToForcedOpenState(); assertEquals(CircuitBreaker.State.FORCED_OPEN, circuitBreaker.getState()); circuitBreaker.transitionToClosedState(); assertEquals(CircuitBreaker.State.CLOSED, circuitBreaker.getState()); } @Test public void testSwitchToHealthyDatabase() throws InterruptedException { waitForDatabaseToGetHealthy(provider.getDatabase(endpointStandalone0.getHostAndPort()), provider.getDatabase(endpointStandalone1.getHostAndPort())); Endpoint e2 = provider.switchToHealthyDatabase(SwitchReason.HEALTH_CHECK, provider.getDatabase()); assertEquals(endpointStandalone1.getHostAndPort(), e2); } @Test public void testCanIterateOnceMore() { Endpoint endpoint0 = endpointStandalone0.getHostAndPort(); waitForDatabaseToGetHealthy(provider.getDatabase(endpoint0), provider.getDatabase(endpointStandalone1.getHostAndPort())); provider.setActiveDatabase(endpoint0); provider.getDatabase().setDisabled(true); provider.switchToHealthyDatabase(SwitchReason.HEALTH_CHECK, provider.getDatabase(endpoint0)); assertFalse(provider.canIterateFrom(provider.getDatabase())); } private void waitForDatabaseToGetHealthy(Database... databases) { Awaitility.await().pollInterval(Durations.ONE_HUNDRED_MILLISECONDS) .atMost(Durations.TWO_SECONDS) .until(() -> Arrays.stream(databases).allMatch(Database::isHealthy)); } @Test public void testDatabaseSwitchListener() { DatabaseConfig[] databaseConfigs = new DatabaseConfig[2]; databaseConfigs[0] = DatabaseConfig .builder(new HostAndPort("purposefully-incorrect", 0000), DefaultJedisClientConfig.builder().build()) .weight(0.5f).healthCheckEnabled(false).build(); databaseConfigs[1] = DatabaseConfig .builder(new HostAndPort("purposefully-incorrect", 0001), DefaultJedisClientConfig.builder().build()) .weight(0.4f).healthCheckEnabled(false).build(); MultiDbConfig.Builder builder = new MultiDbConfig.Builder(databaseConfigs); // Configures a single failed command to trigger an open circuit on the next subsequent failure builder.failureDetector(MultiDbConfig.CircuitBreakerConfig.builder().slidingWindowSize(3) .minNumOfFailures(1).failureRateThreshold(0).build()); AtomicBoolean isValidTest = new AtomicBoolean(false); MultiDbConnectionProvider localProvider = new MultiDbConnectionProvider(builder.build()); localProvider.setDatabaseSwitchListener(a -> { isValidTest.set(true); }); try (MultiDbClient jedis = MultiDbClient.builder().connectionProvider(localProvider).build()) { // This will fail due to unable to connect and open the circuit which will trigger the post // processor try { jedis.get("foo"); } catch (Exception e) { } } assertTrue(isValidTest.get()); } @Test public void testSetActiveDatabaseNull() { assertThrows(JedisValidationException.class, () -> provider.setActiveDatabase(null)); } @Test public void testSetActiveDatabaseByMissingEndpoint() { assertThrows(JedisValidationException.class, () -> provider.setActiveDatabase(new Endpoint() { @Override public String getHost() { return "purposefully-incorrect"; } @Override public int getPort() { return 0000; } })); // Should throw an exception } @Test public void testConnectionPoolConfigApplied() { ConnectionPoolConfig poolConfig = new ConnectionPoolConfig(); poolConfig.setMaxTotal(8); poolConfig.setMaxIdle(4); poolConfig.setMinIdle(1); DatabaseConfig[] databaseConfigs = new DatabaseConfig[2]; databaseConfigs[0] = DatabaseConfig .builder(endpointStandalone0.getHostAndPort(), endpointStandalone0.getClientConfigBuilder().build()) .connectionPoolConfig(poolConfig).build(); databaseConfigs[1] = DatabaseConfig .builder(endpointStandalone1.getHostAndPort(), endpointStandalone1.getClientConfigBuilder().build()) .connectionPoolConfig(poolConfig).build(); try (MultiDbConnectionProvider customProvider = new MultiDbConnectionProvider( new MultiDbConfig.Builder(databaseConfigs).build())) { MultiDbConnectionProvider.Database activeDatabase = customProvider.getDatabase(); ConnectionPool connectionPool = activeDatabase.getConnectionPool(); assertEquals(8, connectionPool.getMaxTotal()); assertEquals(4, connectionPool.getMaxIdle()); assertEquals(1, connectionPool.getMinIdle()); } } @Test @Timeout(5) void testHealthChecksStopAfterProviderClose() throws InterruptedException { AtomicInteger healthCheckCount = new AtomicInteger(0); // Custom strategy that counts health checks HealthCheckStrategy countingStrategy = new redis.clients.jedis.mcf.TestHealthCheckStrategy( redis.clients.jedis.mcf.HealthCheckStrategy.Config.builder().interval(5).timeout(50) .policy(BuiltIn.ANY_SUCCESS).build(), e -> { healthCheckCount.incrementAndGet(); return HealthStatus.HEALTHY; }); // Create new provider with health check strategy (don't use the setUp() provider) DatabaseConfig config = DatabaseConfig .builder(endpointStandalone0.getHostAndPort(), endpointStandalone0.getClientConfigBuilder().build()) .healthCheckStrategy(countingStrategy).build(); MultiDbConnectionProvider testProvider = new MultiDbConnectionProvider( new MultiDbConfig.Builder(Collections.singletonList(config)).build()); try { // Wait for some health checks to occur Awaitility.await().atMost(Durations.ONE_SECOND).until(() -> healthCheckCount.get() > 2); int checksBeforeClose = healthCheckCount.get(); // Close provider testProvider.close(); // Wait longer than health check interval Thread.sleep(100); int checksAfterClose = healthCheckCount.get(); // Health check count should not increase after close assertEquals(checksBeforeClose, checksAfterClose, "Health checks should stop after provider is closed"); } finally { // Ensure cleanup even if test fails testProvider.close(); } } @Test public void userCommand_firstTemporary_thenPermanent_inOrder() { DatabaseConfig[] databaseConfigs = new DatabaseConfig[2]; databaseConfigs[0] = DatabaseConfig.builder(endpointStandalone0.getHostAndPort(), endpointStandalone0.getClientConfigBuilder().build()).weight(0.5f).build(); databaseConfigs[1] = DatabaseConfig.builder(endpointStandalone1.getHostAndPort(), endpointStandalone1.getClientConfigBuilder().build()).weight(0.3f).build(); MultiDbConnectionProvider testProvider = new MultiDbConnectionProvider( new MultiDbConfig.Builder(databaseConfigs).delayInBetweenFailoverAttempts(100) .maxNumFailoverAttempts(2) .commandRetry(MultiDbConfig.RetryConfig.builder().maxAttempts(1).build()).build()); try (MultiDbClient jedis = MultiDbClient.builder().connectionProvider(testProvider).build()) { jedis.get("foo"); // Disable both databases so any attempt to switch results in 'no healthy database' path testProvider.getDatabase(endpointStandalone0.getHostAndPort()).setDisabled(true); testProvider.getDatabase(endpointStandalone1.getHostAndPort()).setDisabled(true); // Simulate user running a command that fails and triggers failover iteration assertThrows(JedisTemporarilyNotAvailableException.class, () -> jedis.get("foo")); // Next immediate attempt should exceed max attempts and become permanent (expected to fail // until feature exists) await().atMost(Durations.ONE_SECOND).pollInterval(Durations.ONE_HUNDRED_MILLISECONDS) .until(() -> (assertThrows(JedisFailoverException.class, () -> jedis.get("foo")) instanceof JedisPermanentlyNotAvailableException)); } } @Test public void userCommand_connectionExceptions_thenMultipleTemporary_thenPermanent_inOrder() { DatabaseConfig[] databaseConfigs = new DatabaseConfig[2]; databaseConfigs[0] = DatabaseConfig .builder(endpointStandalone0.getHostAndPort(), endpointStandalone0.getClientConfigBuilder().build()) .weight(0.5f).healthCheckEnabled(false).build(); databaseConfigs[1] = DatabaseConfig .builder(endpointStandalone1.getHostAndPort(), endpointStandalone1.getClientConfigBuilder().build()) .weight(0.3f).healthCheckEnabled(false).build(); // ATTENTION: these configuration settings are not random and // adjusted to get exact numbers of failures with exact exception types // and open to impact from other defaulted values withing the components in use. MultiDbConnectionProvider testProvider = new MultiDbConnectionProvider( new MultiDbConfig.Builder(databaseConfigs).delayInBetweenFailoverAttempts(100) .maxNumFailoverAttempts(2) .commandRetry(MultiDbConfig.RetryConfig.builder().maxAttempts(1).build()) .failureDetector(MultiDbConfig.CircuitBreakerConfig.builder().slidingWindowSize(5) .failureRateThreshold(60).build()) .build()) { }; try (MultiDbClient jedis = MultiDbClient.builder().connectionProvider(testProvider).build()) { jedis.get("foo"); // disable most weighted database so that it will fail on initial requests testProvider.getDatabase(endpointStandalone0.getHostAndPort()).setDisabled(true); Exception e = assertThrows(JedisConnectionException.class, () -> jedis.get("foo")); assertEquals(JedisConnectionException.class, e.getClass()); e = assertThrows(JedisConnectionException.class, () -> jedis.get("foo")); assertEquals(JedisConnectionException.class, e.getClass()); // then disable the second ones testProvider.getDatabase(endpointStandalone1.getHostAndPort()).setDisabled(true); assertThrows(JedisTemporarilyNotAvailableException.class, () -> jedis.get("foo")); assertThrows(JedisTemporarilyNotAvailableException.class, () -> jedis.get("foo")); // Third get request should exceed max attempts and throw // JedisPermanentlyNotAvailableException await().atMost(Durations.FIVE_HUNDRED_MILLISECONDS).pollInterval(Duration.ofMillis(50)) .until(() -> (assertThrows(JedisFailoverException.class, () -> jedis.get("foo")) instanceof JedisPermanentlyNotAvailableException)); // Fourth get request should continue to throw JedisPermanentlyNotAvailableException assertThrows(JedisPermanentlyNotAvailableException.class, () -> jedis.get("foo")); } } } ================================================ FILE: src/test/java/redis/clients/jedis/mcf/PeriodicFailbackTest.java ================================================ package redis.clients.jedis.mcf; import static org.junit.jupiter.api.Assertions.*; import static org.mockito.Mockito.*; import static redis.clients.jedis.mcf.MultiDbConnectionProviderHelper.onHealthStatusChange; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; import org.mockito.MockedConstruction; import org.mockito.junit.jupiter.MockitoExtension; import redis.clients.jedis.Connection; import redis.clients.jedis.DefaultJedisClientConfig; import redis.clients.jedis.HostAndPort; import redis.clients.jedis.JedisClientConfig; import redis.clients.jedis.MultiDbConfig; @ExtendWith(MockitoExtension.class) class PeriodicFailbackTest { private HostAndPort endpoint1; private HostAndPort endpoint2; private JedisClientConfig databaseConfig; @BeforeEach void setUp() { endpoint1 = new HostAndPort("dummy", 6379); endpoint2 = new HostAndPort("dummy", 6380); databaseConfig = DefaultJedisClientConfig.builder().build(); } private MockedConstruction mockPool() { Connection mockConnection = mock(Connection.class); lenient().when(mockConnection.ping()).thenReturn(true); return mockConstruction(TrackingConnectionPool.class, (mock, context) -> { when(mock.getResource()).thenReturn(mockConnection); doNothing().when(mock).close(); }); } @Test void testPeriodicFailbackCheckWithDisabledDatabase() throws InterruptedException { try (MockedConstruction mockedPool = mockPool()) { MultiDbConfig.DatabaseConfig database1 = MultiDbConfig.DatabaseConfig .builder(endpoint1, databaseConfig).weight(1.0f).healthCheckEnabled(false).build(); MultiDbConfig.DatabaseConfig database2 = MultiDbConfig.DatabaseConfig .builder(endpoint2, databaseConfig).weight(2.0f).healthCheckEnabled(false).build(); MultiDbConfig config = new MultiDbConfig.Builder( new MultiDbConfig.DatabaseConfig[] { database1, database2 }).failbackSupported(true) .failbackCheckInterval(100).build(); try (MultiDbConnectionProvider provider = new MultiDbConnectionProvider(config)) { // Initially, database2 should be active (highest weight: 2.0f vs 1.0f) assertEquals(provider.getDatabase(endpoint2), provider.getDatabase()); // Start grace period for database2 manually provider.getDatabase(endpoint2).setGracePeriod(); provider.getDatabase(endpoint2).setDisabled(true); // Force failover to database1 since database2 is disabled provider.switchToHealthyDatabase(SwitchReason.FORCED, provider.getDatabase(endpoint2)); // Manually trigger periodic check MultiDbConnectionProviderHelper.periodicFailbackCheck(provider); // Should still be on database1 (database2 is in grace period) assertEquals(provider.getDatabase(endpoint1), provider.getDatabase()); } } } @Test void testPeriodicFailbackCheckWithHealthyDatabase() throws InterruptedException { try (MockedConstruction mockedPool = mockPool()) { MultiDbConfig.DatabaseConfig database1 = MultiDbConfig.DatabaseConfig .builder(endpoint1, databaseConfig).weight(1.0f).healthCheckEnabled(false).build(); MultiDbConfig.DatabaseConfig database2 = MultiDbConfig.DatabaseConfig .builder(endpoint2, databaseConfig).weight(2.0f).healthCheckEnabled(false).build(); MultiDbConfig config = new MultiDbConfig.Builder( new MultiDbConfig.DatabaseConfig[] { database1, database2 }).failbackSupported(true) .failbackCheckInterval(50).gracePeriod(100).build(); // Add // grace // period try (MultiDbConnectionProvider provider = new MultiDbConnectionProvider(config)) { // Initially, database2 should be active (highest weight: 2.0f vs 1.0f) assertEquals(provider.getDatabase(endpoint2), provider.getDatabase()); // Make database2 unhealthy to force failover to database1 onHealthStatusChange(provider, endpoint2, HealthStatus.HEALTHY, HealthStatus.UNHEALTHY); // Should now be on database1 (database2 is in grace period) assertEquals(provider.getDatabase(endpoint1), provider.getDatabase()); // Verify database2 is in grace period assertTrue(provider.getDatabase(endpoint2).isInGracePeriod()); // Make database2 healthy again (but it's still in grace period) onHealthStatusChange(provider, endpoint2, HealthStatus.UNHEALTHY, HealthStatus.HEALTHY); // Trigger periodic check immediately - should still be on database1 MultiDbConnectionProviderHelper.periodicFailbackCheck(provider); assertEquals(provider.getDatabase(endpoint1), provider.getDatabase()); // Wait for grace period to expire Thread.sleep(150); // Trigger periodic check after grace period expires MultiDbConnectionProviderHelper.periodicFailbackCheck(provider); // Should have failed back to database2 (higher weight, grace period expired) assertEquals(provider.getDatabase(endpoint2), provider.getDatabase()); } } } @Test void testPeriodicFailbackCheckWithFailbackDisabled() throws InterruptedException { try (MockedConstruction mockedPool = mockPool()) { MultiDbConfig.DatabaseConfig database1 = MultiDbConfig.DatabaseConfig .builder(endpoint1, databaseConfig).weight(1.0f).healthCheckEnabled(false).build(); MultiDbConfig.DatabaseConfig database2 = MultiDbConfig.DatabaseConfig .builder(endpoint2, databaseConfig).weight(2.0f).healthCheckEnabled(false).build(); MultiDbConfig config = new MultiDbConfig.Builder( new MultiDbConfig.DatabaseConfig[] { database1, database2 }).failbackSupported(false) // Disabled .failbackCheckInterval(50).build(); try (MultiDbConnectionProvider provider = new MultiDbConnectionProvider(config)) { // Initially, database2 should be active (highest weight: 2.0f vs 1.0f) assertEquals(provider.getDatabase(endpoint2), provider.getDatabase()); // Make database2 unhealthy to force failover to database1 onHealthStatusChange(provider, endpoint2, HealthStatus.HEALTHY, HealthStatus.UNHEALTHY); // Should now be on database1 assertEquals(provider.getDatabase(endpoint1), provider.getDatabase()); // Make database2 healthy again onHealthStatusChange(provider, endpoint2, HealthStatus.UNHEALTHY, HealthStatus.HEALTHY); // Wait for stability period Thread.sleep(100); // Trigger periodic check MultiDbConnectionProviderHelper.periodicFailbackCheck(provider); // Should still be on database1 (failback disabled) assertEquals(provider.getDatabase(endpoint1), provider.getDatabase()); } } } @Test void testPeriodicFailbackCheckSelectsHighestWeightDatabase() throws InterruptedException { try (MockedConstruction mockedPool = mockPool()) { HostAndPort endpoint3 = new HostAndPort("dummy", 6381); MultiDbConfig.DatabaseConfig database1 = MultiDbConfig.DatabaseConfig .builder(endpoint1, databaseConfig).weight(1.0f).healthCheckEnabled(false).build(); MultiDbConfig.DatabaseConfig database2 = MultiDbConfig.DatabaseConfig .builder(endpoint2, databaseConfig).weight(2.0f).healthCheckEnabled(false).build(); MultiDbConfig.DatabaseConfig database3 = MultiDbConfig.DatabaseConfig .builder(endpoint3, databaseConfig).weight(3.0f) // Highest weight .healthCheckEnabled(false).build(); MultiDbConfig config = new MultiDbConfig.Builder( new MultiDbConfig.DatabaseConfig[] { database1, database2, database3 }) .failbackSupported(true).failbackCheckInterval(50).gracePeriod(100).build(); // Add // grace // period try (MultiDbConnectionProvider provider = new MultiDbConnectionProvider(config)) { // Initially, database3 should be active (highest weight: 3.0f vs 2.0f vs 1.0f) assertEquals(provider.getDatabase(endpoint3), provider.getDatabase()); // Make database3 unhealthy to force failover to database2 (next highest weight) onHealthStatusChange(provider, endpoint3, HealthStatus.HEALTHY, HealthStatus.UNHEALTHY); // Should now be on database2 (weight 2.0f, higher than database1's 1.0f) assertEquals(provider.getDatabase(endpoint2), provider.getDatabase()); // Make database2 unhealthy to force failover to database1 onHealthStatusChange(provider, endpoint2, HealthStatus.HEALTHY, HealthStatus.UNHEALTHY); // Should now be on database1 (only healthy databases left) assertEquals(provider.getDatabase(endpoint1), provider.getDatabase()); // Make database2 and database3 healthy again onHealthStatusChange(provider, endpoint2, HealthStatus.UNHEALTHY, HealthStatus.HEALTHY); onHealthStatusChange(provider, endpoint3, HealthStatus.UNHEALTHY, HealthStatus.HEALTHY); // Wait for grace period to expire Thread.sleep(150); // Trigger periodic check MultiDbConnectionProviderHelper.periodicFailbackCheck(provider); // Should have failed back to database3 (highest weight, grace period expired) assertEquals(provider.getDatabase(endpoint3), provider.getDatabase()); } } } @Test void testSettingHigherWeightCausesFailback() throws InterruptedException { try (MockedConstruction mockedPool = mockPool()) { MultiDbConfig.DatabaseConfig database1 = MultiDbConfig.DatabaseConfig .builder(endpoint1, databaseConfig).weight(1.0f).healthCheckEnabled(false).build(); MultiDbConfig.DatabaseConfig database2 = MultiDbConfig.DatabaseConfig .builder(endpoint2, databaseConfig).weight(2.0f).healthCheckEnabled(false).build(); MultiDbConfig config = new MultiDbConfig.Builder( new MultiDbConfig.DatabaseConfig[] { database1, database2 }).failbackSupported(true) .failbackCheckInterval(50).gracePeriod(100).build(); try (MultiDbConnectionProvider provider = new MultiDbConnectionProvider(config)) { // Initially, database2 should be active (highest weight: 2.0f vs 1.0f) assertEquals(provider.getDatabase(endpoint2), provider.getDatabase()); // Make database2 unhealthy to force failover to database1 onHealthStatusChange(provider, endpoint2, HealthStatus.HEALTHY, HealthStatus.UNHEALTHY); // Should now be on database1 (only healthy option) assertEquals(provider.getDatabase(endpoint1), provider.getDatabase()); // Increase weight of database1 to be higher than database2 provider.getDatabase(endpoint1).setWeight(3.0f); // Make database2 healthy again onHealthStatusChange(provider, endpoint2, HealthStatus.UNHEALTHY, HealthStatus.HEALTHY); // Wait for grace period to expire Thread.sleep(150); // Trigger periodic check MultiDbConnectionProviderHelper.periodicFailbackCheck(provider); // Should still be on database1 (now has higher weight: 3.0f vs 2.0f) assertEquals(provider.getDatabase(endpoint1), provider.getDatabase()); } } } } ================================================ FILE: src/test/java/redis/clients/jedis/mcf/PingStrategyIntegrationTest.java ================================================ package redis.clients.jedis.mcf; import eu.rekawek.toxiproxy.Proxy; import eu.rekawek.toxiproxy.ToxiproxyClient; import eu.rekawek.toxiproxy.model.ToxicDirection; import org.junit.jupiter.api.AfterAll; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Tag; import redis.clients.jedis.DefaultJedisClientConfig; import redis.clients.jedis.EndpointConfig; import redis.clients.jedis.HostAndPort; import redis.clients.jedis.Endpoints; import redis.clients.jedis.JedisClientConfig; import redis.clients.jedis.exceptions.JedisException; import java.io.IOException; import static org.junit.jupiter.api.Assertions.*; @Tag("failover") public class PingStrategyIntegrationTest { private static EndpointConfig endpoint; private static HostAndPort proxyHostAndPort; private static final ToxiproxyClient tp = new ToxiproxyClient("localhost", 8474); private static Proxy redisProxy; @BeforeAll public static void setupProxy() throws IOException { endpoint = Endpoints.getRedisEndpoint("redis-failover-1"); proxyHostAndPort = endpoint.getHostAndPort(); if (tp.getProxyOrNull("redis-health-test") != null) { tp.getProxy("redis-health-test").delete(); } redisProxy = tp.createProxy("redis-health-test", "0.0.0.0:29379", "redis-failover-1:9379"); } @AfterAll public static void cleanupProxy() throws IOException { if (redisProxy != null) { redisProxy.delete(); } } @BeforeEach public void resetProxy() throws IOException { redisProxy.enable(); redisProxy.toxics().getAll().forEach(toxic -> { try { toxic.remove(); } catch (IOException e) { throw new RuntimeException(e); } }); } @Test public void testPingStrategyRecoversAfterDisconnect() throws Exception { JedisClientConfig config = DefaultJedisClientConfig.builder().socketTimeoutMillis(1000) .connectionTimeoutMillis(1000).build(); try (PingStrategy strategy = new PingStrategy(proxyHostAndPort, config, HealthCheckStrategy.Config.create())) { // Initial health check should work HealthStatus initialStatus = strategy.doHealthCheck(proxyHostAndPort); assertEquals(HealthStatus.HEALTHY, initialStatus); // Disable the proxy to simulate network failure redisProxy.disable(); // Health check should now fail - this will expose the bug assertThrows(JedisException.class, () -> strategy.doHealthCheck(proxyHostAndPort)); // Re-enable proxy redisProxy.enable(); // Health check should recover HealthStatus statusAfterEnable = strategy.doHealthCheck(proxyHostAndPort); assertEquals(HealthStatus.HEALTHY, statusAfterEnable); } } @Test public void testPingStrategyWithConnectionTimeout() throws Exception { JedisClientConfig config = DefaultJedisClientConfig.builder().socketTimeoutMillis(100) .connectionTimeoutMillis(100).build(); try (PingStrategy strategy = new PingStrategy(proxyHostAndPort, config, HealthCheckStrategy.Config.builder().interval(1000).timeout(500).numProbes(1).build())) { // Initial health check should work assertEquals(HealthStatus.HEALTHY, strategy.doHealthCheck(proxyHostAndPort)); // Add latency toxic to simulate slow network redisProxy.toxics().latency("slow-connection", ToxicDirection.DOWNSTREAM, 1000); // Health check should timeout and return unhealthy assertThrows(JedisException.class, () -> strategy.doHealthCheck(proxyHostAndPort)); // Remove toxic redisProxy.toxics().get("slow-connection").remove(); // Health check should recover HealthStatus recoveredStatus = strategy.doHealthCheck(proxyHostAndPort); assertEquals(HealthStatus.HEALTHY, recoveredStatus, "Health check should recover from high latency"); } } @Test public void testConnectionDropDuringHealthCheck() throws Exception { JedisClientConfig config = DefaultJedisClientConfig.builder().socketTimeoutMillis(2000).build(); try (PingStrategy strategy = new PingStrategy(proxyHostAndPort, config, HealthCheckStrategy.Config.create())) { // Initial health check assertEquals(HealthStatus.HEALTHY, strategy.doHealthCheck(proxyHostAndPort)); // Simulate connection drop by limiting data transfer redisProxy.toxics().limitData("connection-drop", ToxicDirection.UPSTREAM, 10); // This should fail due to connection issues assertThrows(JedisException.class, () -> strategy.doHealthCheck(proxyHostAndPort)); // Remove toxic redisProxy.toxics().get("connection-drop").remove(); // Health check should recover HealthStatus afterRecovery = strategy.doHealthCheck(proxyHostAndPort); assertEquals(HealthStatus.HEALTHY, afterRecovery); } } } ================================================ FILE: src/test/java/redis/clients/jedis/mcf/RedisRestAPIIT.java ================================================ package redis.clients.jedis.mcf; import static org.junit.jupiter.api.Assertions.*; import static org.junit.jupiter.api.Assumptions.assumeTrue; import java.security.cert.X509Certificate; import java.util.List; import java.util.function.Supplier; import javax.net.ssl.HostnameVerifier; import javax.net.ssl.HttpsURLConnection; import javax.net.ssl.SSLContext; import javax.net.ssl.SSLSocketFactory; import javax.net.ssl.TrustManager; import javax.net.ssl.X509TrustManager; import org.junit.jupiter.api.AfterAll; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.Tag; import org.junit.jupiter.api.Tags; import org.junit.jupiter.api.Test; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import redis.clients.jedis.DefaultRedisCredentials; import redis.clients.jedis.Endpoint; import redis.clients.jedis.EndpointConfig; import redis.clients.jedis.Endpoints; import redis.clients.jedis.RedisCredentials; import redis.clients.jedis.scenario.RestEndpointUtil; @Tags({ @Tag("failover"), @Tag("scenario") }) public class RedisRestAPIIT { public static class SSLBypass { private static SSLSocketFactory originalSSLSocketFactory; private static HostnameVerifier originalHostnameVerifier; public static void disableSSLVerification() { try { // Store original settings BEFORE changing them originalSSLSocketFactory = HttpsURLConnection.getDefaultSSLSocketFactory(); originalHostnameVerifier = HttpsURLConnection.getDefaultHostnameVerifier(); // Create trust-all manager TrustManager[] trustAllCerts = new TrustManager[] { new X509TrustManager() { public X509Certificate[] getAcceptedIssuers() { return null; } public void checkClientTrusted(X509Certificate[] certs, String authType) { } public void checkServerTrusted(X509Certificate[] certs, String authType) { } } }; // Apply bypass SSLContext sc = SSLContext.getInstance("SSL"); sc.init(null, trustAllCerts, new java.security.SecureRandom()); HttpsURLConnection.setDefaultSSLSocketFactory(sc.getSocketFactory()); HttpsURLConnection.setDefaultHostnameVerifier((hostname, session) -> true); } catch (Exception e) { log.error("Failed to disable SSL verification", e); } } public static void restoreSSLVerification() { // Restore original settings if (originalSSLSocketFactory != null) { HttpsURLConnection.setDefaultSSLSocketFactory(originalSSLSocketFactory); } if (originalHostnameVerifier != null) { HttpsURLConnection.setDefaultHostnameVerifier(originalHostnameVerifier); } } } private static EndpointConfig crdb; private static EndpointConfig db1; private static Endpoint restAPIEndpoint; private static Supplier credentialsSupplier; private static final Logger log = LoggerFactory.getLogger(RedisRestAPIIT.class); @BeforeAll public static void beforeClass() { try { crdb = Endpoints.getRedisEndpoint("re-active-active"); db1 = Endpoints.getRedisEndpoint("re-standalone"); restAPIEndpoint = RestEndpointUtil.getRestAPIEndpoint(crdb); credentialsSupplier = () -> new DefaultRedisCredentials("test@redis.com", "test123"); SSLBypass.disableSSLVerification(); } catch (IllegalArgumentException e) { log.warn("Skipping test because no Redis endpoint is configured"); assumeTrue(false); } } @AfterAll public static void teardownTrustStore() { SSLBypass.restoreSSLVerification(); } @Test void testGetBdbs() throws Exception { RedisRestAPI api = new RedisRestAPI(restAPIEndpoint, credentialsSupplier); List bdbs = api.getBdbs(); assertEquals(4, bdbs.size()); assertFalse(bdbs.isEmpty()); // Verify that each BDB has a UID and endpoints for (RedisRestAPI.BdbInfo bdb : bdbs) { assertNotNull(bdb.getUid()); assertNotNull(bdb.getEndpoints()); } } @Test void testCheckAvailability() throws Exception { RedisRestAPI api = new RedisRestAPI(restAPIEndpoint, credentialsSupplier); List bdbs = api.getBdbs(); // Verify availability against CRDB - without extended lag aware checks assertTrue(api.checkBdbAvailability(String.valueOf(crdb.getBdbId()), false)); // Verify availability against CRDB - with lag aware assertTrue(api.checkBdbAvailability(String.valueOf(crdb.getBdbId()), true)); // Verify availability checks against non-CRDB with lag aware assertTrue(api.checkBdbAvailability(String.valueOf(db1.getBdbId()), false)); assertFalse(api.checkBdbAvailability(String.valueOf(db1.getBdbId()), true)); assertFalse(api.checkBdbAvailability("non-existent-bdb", false)); assertFalse(api.checkBdbAvailability("non-existent-bdb", true)); } } ================================================ FILE: src/test/java/redis/clients/jedis/mcf/RedisRestAPIUnitTest.java ================================================ package redis.clients.jedis.mcf; import static org.junit.jupiter.api.Assertions.*; import static org.mockito.ArgumentMatchers.any; import static org.mockito.Mockito.*; import java.io.ByteArrayInputStream; import java.net.HttpURLConnection; import java.util.Arrays; import java.util.List; import java.util.function.Supplier; import org.junit.jupiter.api.Test; import redis.clients.jedis.DefaultRedisCredentials; import redis.clients.jedis.Endpoint; import redis.clients.jedis.RedisCredentials; public class RedisRestAPIUnitTest { static class TestEndpoint implements Endpoint { @Override public String getHost() { return "dummy"; } @Override public int getPort() { return 8443; } } @Test void getBdbs_parsesArrayOfObjects() throws Exception { RedisRestAPI api = spy(new RedisRestAPI(new TestEndpoint(), creds(), 1000)); HttpURLConnection conn = mock(HttpURLConnection.class); doReturn(conn).when(api).createConnection(any(), any(), any()); when(conn.getResponseCode()).thenReturn(200); String body = "[ {\"uid\":\"1\", \"endpoints\":[]}, {\"uid\":\"2\", \"endpoints\":[]} ]"; when(conn.getInputStream()).thenReturn(new ByteArrayInputStream(body.getBytes())); List result = api.getBdbs(); assertEquals(2, result.size()); assertEquals("1", result.get(0).getUid()); assertEquals("2", result.get(1).getUid()); verify(conn, times(1)).disconnect(); } @Test void availability_logsAndReturnsFalseForNon200() throws Exception { RedisRestAPI api = spy(new RedisRestAPI(new TestEndpoint(), creds(), 1000)); HttpURLConnection conn = mock(HttpURLConnection.class); doReturn(conn).when(api).createConnection(any(), any(), any()); when(conn.getResponseCode()).thenReturn(503); String body = "{\"error_code\":\"bdb_unavailable\",\"description\":\"Database is not available\"}"; when(conn.getErrorStream()).thenReturn(new ByteArrayInputStream(body.getBytes())); assertFalse(api.checkBdbAvailability("2", false)); } private static Supplier creds() { return () -> new DefaultRedisCredentials("testUser", "testPwd"); } @Test void availability_200_and_503_paths_cover_lagAware_toggle() throws Exception { RedisRestAPI api = spy(new RedisRestAPI(new TestEndpoint(), creds(), 1000)); HttpURLConnection conn = mock(HttpURLConnection.class); doReturn(conn).when(api).createConnection(any(), any(), any()); // Healthy path (200) when(conn.getResponseCode()).thenReturn(200); assertTrue(api.checkBdbAvailability("123", true)); // Unhealthy path (503) with error body reset(conn); doReturn(conn).when(api).createConnection(any(), any(), any()); when(conn.getResponseCode()).thenReturn(503); when(conn.getErrorStream()) .thenReturn(new ByteArrayInputStream("{\"error_code\":\"bdb_unavailable\"}".getBytes())); assertFalse(api.checkBdbAvailability("123", false)); } @Test void testCheckBdbAvailabilityWithExtendedCheck() throws Exception { RedisRestAPI api = spy( new RedisRestAPI(new TestEndpoint(), () -> new DefaultRedisCredentials("user", "pass"))); HttpURLConnection conn = mock(HttpURLConnection.class); doReturn(conn).when(api).createConnection(any(), any(), any()); when(conn.getResponseCode()).thenReturn(200); assertTrue(api.checkBdbAvailability("123", true, 100L)); // Verify the correct URL was constructed with extended check parameters verify(api).createConnection(eq( "https://dummy:8443/v1/bdbs/123/availability?extend_check=lag&availability_lag_tolerance_ms=100"), eq("GET"), any()); } @Test void testCheckBdbAvailabilityWithExtendedCheckNoTolerance() throws Exception { RedisRestAPI api = spy( new RedisRestAPI(new TestEndpoint(), () -> new DefaultRedisCredentials("user", "pass"))); HttpURLConnection conn = mock(HttpURLConnection.class); doReturn(conn).when(api).createConnection(any(), any(), any()); when(conn.getResponseCode()).thenReturn(200); assertTrue(api.checkBdbAvailability("123", true, null)); // Verify the correct URL was constructed with extended check but no tolerance parameter verify(api).createConnection(eq("https://dummy:8443/v1/bdbs/123/availability?extend_check=lag"), eq("GET"), any()); } @Test void testCheckBdbAvailabilityWithStandardCheck() throws Exception { RedisRestAPI api = spy( new RedisRestAPI(new TestEndpoint(), () -> new DefaultRedisCredentials("user", "pass"))); HttpURLConnection conn = mock(HttpURLConnection.class); doReturn(conn).when(api).createConnection(any(), any(), any()); when(conn.getResponseCode()).thenReturn(200); assertTrue(api.checkBdbAvailability("123", false, null)); // Verify the correct URL was constructed for standard check (no query parameters) verify(api).createConnection(eq("https://dummy:8443/v1/bdbs/123/availability"), eq("GET"), any()); } // ========== Parsing and BDB Matching Tests ========== @Test void parseBdbInfoFromResponse_parses_correctly() { String responseBody = "[\n" + " {\n" + " \"uid\": \"1\",\n" + " \"endpoints\": [\n" + " {\n" + " \"dns_name\": \"redis-db1.example.com\",\n" + " \"addr\": [\"10.0.1.100\"],\n" + " \"port\": 6379,\n" + " \"uid\": \"1:1\"\n" + " }\n" + " ]\n" + " },\n" + " {\n" + " \"uid\": \"2\",\n" + " \"endpoints\": [\n" + " {\n" + " \"dns_name\": \"redis-db2.example.com\",\n" + " \"addr\": [\"10.0.1.101\"],\n" + " \"port\": 6380,\n" + " \"uid\": \"2:1\"\n" + " }\n" + " ]\n" + " }\n" + "]"; List result = RedisRestAPI.parseBdbInfoFromResponse(responseBody); assertEquals(2, result.size()); RedisRestAPI.BdbInfo bdb1 = result.get(0); assertEquals("1", bdb1.getUid()); assertEquals(1, bdb1.getEndpoints().size()); RedisRestAPI.EndpointInfo endpoint1 = bdb1.getEndpoints().get(0); assertEquals("redis-db1.example.com", endpoint1.getDnsName()); assertEquals(Arrays.asList("10.0.1.100"), endpoint1.getAddr()); assertEquals(Integer.valueOf(6379), endpoint1.getPort()); assertEquals("1:1", endpoint1.getUid()); } @Test void findMatchingBdb_matches_dns_name() { RedisRestAPI.BdbInfo bdb1 = new RedisRestAPI.BdbInfo("1", Arrays.asList(new RedisRestAPI.EndpointInfo(Arrays.asList("10.0.1.100"), "redis-db1.example.com", 6379, "1:1"))); RedisRestAPI.BdbInfo bdb2 = new RedisRestAPI.BdbInfo("2", Arrays.asList(new RedisRestAPI.EndpointInfo(Arrays.asList("10.0.1.101"), "redis-db2.example.com", 6380, "2:1"))); List bdbs = Arrays.asList(bdb1, bdb2); RedisRestAPI.BdbInfo result = RedisRestAPI.BdbInfo.findMatchingBdb(bdbs, "redis-db2.example.com"); assertNotNull(result); assertEquals("2", result.getUid()); } @Test void findMatchingBdb_matches_ip_address() { RedisRestAPI.BdbInfo bdb1 = new RedisRestAPI.BdbInfo("1", Arrays.asList(new RedisRestAPI.EndpointInfo(Arrays.asList("10.0.1.100", "192.168.1.100"), "redis-db1.example.com", 6379, "1:1"))); RedisRestAPI.BdbInfo bdb2 = new RedisRestAPI.BdbInfo("2", Arrays.asList(new RedisRestAPI.EndpointInfo(Arrays.asList("10.0.1.101"), "redis-db2.example.com", 6380, "2:1"))); List bdbs = Arrays.asList(bdb1, bdb2); RedisRestAPI.BdbInfo result = RedisRestAPI.BdbInfo.findMatchingBdb(bdbs, "192.168.1.100"); assertNotNull(result); assertEquals("1", result.getUid()); } @Test void findMatchingBdb_returns_null_when_no_match() { RedisRestAPI.BdbInfo bdb1 = new RedisRestAPI.BdbInfo("1", Arrays.asList(new RedisRestAPI.EndpointInfo(Arrays.asList("10.0.1.100"), "redis-db1.example.com", 6379, "1:1"))); List bdbs = Arrays.asList(bdb1); RedisRestAPI.BdbInfo result = RedisRestAPI.BdbInfo.findMatchingBdb(bdbs, "nonexistent.example.com"); assertNull(result); } @Test void findMatchingBdb_handles_multiple_endpoints_per_bdb() { RedisRestAPI.BdbInfo bdb1 = new RedisRestAPI.BdbInfo("1", Arrays.asList( new RedisRestAPI.EndpointInfo(Arrays.asList("10.0.1.100"), "redis-db1-primary.example.com", 6379, "1:1"), new RedisRestAPI.EndpointInfo(Arrays.asList("10.0.1.101"), "redis-db1-replica.example.com", 6380, "1:2"))); List bdbs = Arrays.asList(bdb1); RedisRestAPI.BdbInfo result = RedisRestAPI.BdbInfo.findMatchingBdb(bdbs, "redis-db1-replica.example.com"); assertNotNull(result); assertEquals("1", result.getUid()); } @Test void parseBdbInfoFromResponse_handles_missing_fields_gracefully() { String responseBody = "[\n" + " {\n" + " \"uid\": \"1\"\n" + " },\n" + " {\n" + " \"endpoints\": [\n" + " {\n" + " \"dns_name\": \"redis-db2.example.com\"\n" + " }\n" + " ]\n" + " },\n" + " {\n" + " \"uid\": \"3\",\n" + " \"endpoints\": [\n" + " {\n" + " \"dns_name\": \"redis-db3.example.com\",\n" + " \"addr\": [\"10.0.1.103\"],\n" + " \"port\": 6379\n" + " }\n" + " ]\n" + " }\n" + "]"; List result = RedisRestAPI.parseBdbInfoFromResponse(responseBody); assertEquals(2, result.size()); // Only BDBs with uid are included RedisRestAPI.BdbInfo bdb1 = result.get(0); assertEquals("1", bdb1.getUid()); assertEquals(0, bdb1.getEndpoints().size()); // No endpoints RedisRestAPI.BdbInfo bdb3 = result.get(1); assertEquals("3", bdb3.getUid()); assertEquals(1, bdb3.getEndpoints().size()); RedisRestAPI.EndpointInfo endpoint = bdb3.getEndpoints().get(0); assertEquals("redis-db3.example.com", endpoint.getDnsName()); assertEquals(Arrays.asList("10.0.1.103"), endpoint.getAddr()); assertEquals(Integer.valueOf(6379), endpoint.getPort()); assertNull(endpoint.getUid()); // Missing uid field } @Test void parseBdbInfoFromResponse_handles_empty_response() { String responseBody = "[]"; List result = RedisRestAPI.parseBdbInfoFromResponse(responseBody); assertTrue(result.isEmpty()); } @Test void findMatchingBdb_prefers_dns_name_over_addr() { RedisRestAPI.BdbInfo bdb1 = new RedisRestAPI.BdbInfo("1", Arrays.asList(new RedisRestAPI.EndpointInfo(Arrays.asList("10.0.1.100"), "target-host.example.com", 6379, "1:1"))); RedisRestAPI.BdbInfo bdb2 = new RedisRestAPI.BdbInfo("2", Arrays.asList(new RedisRestAPI.EndpointInfo(Arrays.asList("target-host.example.com"), "other-host.example.com", 6380, "2:1"))); List bdbs = Arrays.asList(bdb1, bdb2); // Should match BDB 1 by dns_name, not BDB 2 by addr RedisRestAPI.BdbInfo result = RedisRestAPI.BdbInfo.findMatchingBdb(bdbs, "target-host.example.com"); assertNotNull(result); assertEquals("1", result.getUid()); } @Test void findMatchingBdb_matches_correct_bdb_with_same_host_different_ports() { // Two BDBs with same DNS name but different ports RedisRestAPI.BdbInfo bdb1 = new RedisRestAPI.BdbInfo("1", Arrays.asList(new RedisRestAPI.EndpointInfo(Arrays.asList("10.0.1.100"), "redis.example.com", 6379, "1:1"))); RedisRestAPI.BdbInfo bdb2 = new RedisRestAPI.BdbInfo("2", Arrays.asList(new RedisRestAPI.EndpointInfo(Arrays.asList("10.0.1.100"), "redis.example.com", 6380, "2:1"))); List bdbs = Arrays.asList(bdb1, bdb2); // Should match first BDB found with the DNS name (current implementation matches by host only) RedisRestAPI.BdbInfo result = RedisRestAPI.BdbInfo.findMatchingBdb(bdbs, "redis.example.com"); assertNotNull(result); assertEquals("1", result.getUid()); // First match wins } @Test void findMatchingBdb_matches_correct_bdb_with_same_ip_different_ports() { // Two BDBs with same IP but different ports and DNS names RedisRestAPI.BdbInfo bdb1 = new RedisRestAPI.BdbInfo("1", Arrays.asList(new RedisRestAPI.EndpointInfo(Arrays.asList("10.0.1.100"), "redis1.example.com", 6379, "1:1"))); RedisRestAPI.BdbInfo bdb2 = new RedisRestAPI.BdbInfo("2", Arrays.asList(new RedisRestAPI.EndpointInfo(Arrays.asList("10.0.1.100"), "redis2.example.com", 6380, "2:1"))); List bdbs = Arrays.asList(bdb1, bdb2); // Should match first BDB found with the IP address (current implementation matches by host // only) RedisRestAPI.BdbInfo result = RedisRestAPI.BdbInfo.findMatchingBdb(bdbs, "10.0.1.100"); assertNotNull(result); assertEquals("1", result.getUid()); // First match wins } } ================================================ FILE: src/test/java/redis/clients/jedis/mcf/StatusTrackerTest.java ================================================ package redis.clients.jedis.mcf; import static org.junit.jupiter.api.Assertions.*; import static org.mockito.Mockito.*; import static org.awaitility.Awaitility.await; import java.time.Duration; import java.util.concurrent.CountDownLatch; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicReference; import org.awaitility.Durations; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.mockito.Mock; import org.mockito.MockitoAnnotations; import redis.clients.jedis.HostAndPort; public class StatusTrackerTest { @Mock private HealthStatusManager mockHealthStatusManager; private StatusTracker statusTracker; private HostAndPort testEndpoint; @BeforeEach void setUp() { MockitoAnnotations.openMocks(this); statusTracker = new StatusTracker(mockHealthStatusManager); testEndpoint = new HostAndPort("dummy", 6379); } @Test void testWaitForHealthStatus_AlreadyDetermined() { // Given: Health status is already HEALTHY when(mockHealthStatusManager.getHealthStatus(testEndpoint)).thenReturn(HealthStatus.HEALTHY); // When: Waiting for health status HealthStatus result = statusTracker.waitForHealthStatus(testEndpoint); // Then: Should return immediately without waiting assertEquals(HealthStatus.HEALTHY, result); verify(mockHealthStatusManager, never()).registerListener(eq(testEndpoint), any(HealthStatusListener.class)); } @Test void testWaitForHealthStatus_EventDriven() throws InterruptedException { // Given: Health status is initially UNKNOWN when(mockHealthStatusManager.getHealthStatus(testEndpoint)).thenReturn(HealthStatus.UNKNOWN) // First // call .thenReturn(HealthStatus.UNKNOWN); // Second call after registering listener when(mockHealthStatusManager.getMaxWaitFor(testEndpoint)).thenReturn(3000L); // Capture the registered listener final HealthStatusListener[] capturedListener = new HealthStatusListener[1]; doAnswer(invocation -> { capturedListener[0] = invocation.getArgument(1); return null; }).when(mockHealthStatusManager).registerListener(eq(testEndpoint), any(HealthStatusListener.class)); // When: Start waiting in a separate thread CountDownLatch testLatch = new CountDownLatch(1); final HealthStatus[] result = new HealthStatus[1]; Thread waitingThread = new Thread(() -> { result[0] = statusTracker.waitForHealthStatus(testEndpoint); testLatch.countDown(); }); waitingThread.start(); await().atMost(Duration.ofMillis(100L)).pollInterval(Duration.ofMillis(5L)).untilAsserted( () -> assertNotNull(capturedListener[0], "Listener should have been registered")); HealthStatusChangeEvent event = new HealthStatusChangeEvent(testEndpoint, HealthStatus.UNKNOWN, HealthStatus.HEALTHY); capturedListener[0].onStatusChange(event); // Then: Should complete and return the new status assertTrue(testLatch.await(1, TimeUnit.SECONDS), "Should complete within timeout"); assertEquals(HealthStatus.HEALTHY, result[0]); // Verify cleanup verify(mockHealthStatusManager).unregisterListener(eq(testEndpoint), eq(capturedListener[0])); } @Test void testWaitForHealthStatus_IgnoresUnknownStatus() throws InterruptedException { // Given: Health status is initially UNKNOWN when(mockHealthStatusManager.getHealthStatus(testEndpoint)).thenReturn(HealthStatus.UNKNOWN); when(mockHealthStatusManager.getMaxWaitFor(testEndpoint)).thenReturn(3000L); // Capture the registered listener final HealthStatusListener[] capturedListener = new HealthStatusListener[1]; doAnswer(invocation -> { capturedListener[0] = invocation.getArgument(1); return null; }).when(mockHealthStatusManager).registerListener(eq(testEndpoint), any(HealthStatusListener.class)); // When: Start waiting in a separate thread CountDownLatch testLatch = new CountDownLatch(1); final HealthStatus[] result = new HealthStatus[1]; Thread waitingThread = new Thread(() -> { result[0] = statusTracker.waitForHealthStatus(testEndpoint); testLatch.countDown(); }); waitingThread.start(); // Give some time for the listener to be registered Thread.sleep(50); // Simulate UNKNOWN status change (should be ignored) assertNotNull(capturedListener[0], "Listener should have been registered"); HealthStatusChangeEvent unknownEvent = new HealthStatusChangeEvent(testEndpoint, HealthStatus.UNKNOWN, HealthStatus.UNKNOWN); capturedListener[0].onStatusChange(unknownEvent); // Should not complete yet assertFalse(testLatch.await(100, TimeUnit.MILLISECONDS), "Should not complete with UNKNOWN status"); // Now send a real status change HealthStatusChangeEvent realEvent = new HealthStatusChangeEvent(testEndpoint, HealthStatus.UNKNOWN, HealthStatus.UNHEALTHY); capturedListener[0].onStatusChange(realEvent); // Then: Should complete now assertTrue(testLatch.await(1, TimeUnit.SECONDS), "Should complete with real status"); assertEquals(HealthStatus.UNHEALTHY, result[0]); } @Test void testWaitForHealthStatus_IgnoresOtherEndpoints() throws InterruptedException { // Given: Health status is initially UNKNOWN when(mockHealthStatusManager.getHealthStatus(testEndpoint)).thenReturn(HealthStatus.UNKNOWN); when(mockHealthStatusManager.getMaxWaitFor(testEndpoint)).thenReturn(3000L); HostAndPort otherEndpoint = new HostAndPort("other", 6379); // Capture the registered listener final HealthStatusListener[] capturedListener = new HealthStatusListener[1]; doAnswer(invocation -> { capturedListener[0] = invocation.getArgument(1); return null; }).when(mockHealthStatusManager).registerListener(eq(testEndpoint), any(HealthStatusListener.class)); // When: Start waiting in a separate thread CountDownLatch testLatch = new CountDownLatch(1); final HealthStatus[] result = new HealthStatus[1]; Thread waitingThread = new Thread(() -> { result[0] = statusTracker.waitForHealthStatus(testEndpoint); testLatch.countDown(); }); waitingThread.start(); // Give some time for the listener to be registered await().atMost(Durations.FIVE_HUNDRED_MILLISECONDS) .pollInterval(Durations.ONE_HUNDRED_MILLISECONDS).untilAsserted(() -> { assertNotNull(capturedListener[0], "Listener should have been registered"); }); // Simulate status change for different endpoint (should be ignored) HealthStatusChangeEvent otherEvent = new HealthStatusChangeEvent(otherEndpoint, HealthStatus.UNKNOWN, HealthStatus.HEALTHY); capturedListener[0].onStatusChange(otherEvent); // Should not complete yet assertFalse(testLatch.await(100, TimeUnit.MILLISECONDS), "Should not complete with other endpoint"); // Now send event for correct endpoint HealthStatusChangeEvent correctEvent = new HealthStatusChangeEvent(testEndpoint, HealthStatus.UNKNOWN, HealthStatus.HEALTHY); capturedListener[0].onStatusChange(correctEvent); // Then: Should complete now assertTrue(testLatch.await(1, TimeUnit.SECONDS), "Should complete with correct endpoint"); assertEquals(HealthStatus.HEALTHY, result[0]); } @Test void testWaitForHealthStatus_InterruptHandling() { // Given: Health status is initially UNKNOWN and will stay that way when(mockHealthStatusManager.getHealthStatus(testEndpoint)).thenReturn(HealthStatus.UNKNOWN); when(mockHealthStatusManager.getMaxWaitFor(any())).thenReturn(3000L); AtomicReference interruptedThreadName = new AtomicReference<>(); AtomicReference thrownException = new AtomicReference<>(); AtomicReference isInterrupted = new AtomicReference<>(); // When: Interrupt thse waiting thread Thread testThread = new Thread(() -> { try { statusTracker.waitForHealthStatus(testEndpoint); fail("Should have thrown JedisConnectionException due to interrupt"); } catch (Exception e) { interruptedThreadName.set(Thread.currentThread().getName()); thrownException.set(e); isInterrupted.set(Thread.currentThread().isInterrupted()); } }); testThread.start(); // Give thread time to start waiting try { Thread.sleep(50); } catch (InterruptedException e) { Thread.currentThread().interrupt(); } // Interrupt the waiting thread testThread.interrupt(); try { testThread.join(1000); } catch (InterruptedException e) { Thread.currentThread().interrupt(); } assertFalse(testThread.isAlive(), "Test thread should have completed"); assertTrue(thrownException.get().getMessage().contains("Interrupted while waiting")); assertTrue(isInterrupted.get(), "Thread should be interrupted"); } @Test void testWaitForHealthStatus_RaceConditionProtection() { // Given: Health status changes between first check and listener registration when(mockHealthStatusManager.getHealthStatus(testEndpoint)).thenReturn(HealthStatus.UNKNOWN) // First // call .thenReturn(HealthStatus.HEALTHY); // Second call after registering listener // When: Waiting for health status HealthStatus result = statusTracker.waitForHealthStatus(testEndpoint); // Then: Should return the status from the second check without waiting assertEquals(HealthStatus.HEALTHY, result); // Verify listener was registered and unregistered verify(mockHealthStatusManager).registerListener(eq(testEndpoint), any(HealthStatusListener.class)); verify(mockHealthStatusManager).unregisterListener(eq(testEndpoint), any(HealthStatusListener.class)); } @Test void testWaitForHealthStatus_ListenerCleanupOnException() { // Given: Health status is initially UNKNOWN when(mockHealthStatusManager.getHealthStatus(testEndpoint)).thenReturn(HealthStatus.UNKNOWN); // Mock registerListener to throw an exception doThrow(new RuntimeException("Registration failed")).when(mockHealthStatusManager) .registerListener(eq(testEndpoint), any(HealthStatusListener.class)); // When: Waiting for health status assertThrows(RuntimeException.class, () -> { statusTracker.waitForHealthStatus(testEndpoint); }); // Then: Should still attempt to unregister (cleanup in finally block) verify(mockHealthStatusManager).registerListener(eq(testEndpoint), any(HealthStatusListener.class)); // Note: unregisterListener might not be called if registerListener fails, // but the finally block should handle this gracefully } } ================================================ FILE: src/test/java/redis/clients/jedis/mcf/TestHealthCheckStrategy.java ================================================ package redis.clients.jedis.mcf; import java.util.function.Function; import redis.clients.jedis.Endpoint; public class TestHealthCheckStrategy implements HealthCheckStrategy { private int interval; private int timeout; private int probes; private int delay; private Function healthCheck; private ProbingPolicy policy; public TestHealthCheckStrategy(int interval, int timeout, int probes, ProbingPolicy policy, int delay, Function healthCheck) { this.interval = interval; this.timeout = timeout; this.probes = probes; this.delay = delay; this.healthCheck = healthCheck; this.policy = policy; } public TestHealthCheckStrategy(HealthCheckStrategy.Config config, Function healthCheck) { this(config.getInterval(), config.getTimeout(), config.getNumProbes(), config.getPolicy(), config.getDelayInBetweenProbes(), healthCheck); } public TestHealthCheckStrategy(Function healthCheck) { this(HealthCheckStrategy.Config.create(), healthCheck); } @Override public int getInterval() { return interval; } @Override public int getTimeout() { return timeout; } @Override public int getNumProbes() { return probes; } @Override public ProbingPolicy getPolicy() { return policy; } @Override public int getDelayInBetweenProbes() { return delay; } @Override public HealthStatus doHealthCheck(Endpoint endpoint) { return healthCheck.apply(endpoint); } }; ================================================ FILE: src/test/java/redis/clients/jedis/misc/AutomaticFailoverTest.java ================================================ package redis.clients.jedis.misc; import java.net.UnknownHostException; import java.util.Arrays; import java.util.Collections; import java.util.List; import java.util.function.Consumer; import java.util.stream.Collectors; import io.redis.test.annotations.ConditionalOnEnv; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Tag; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.RegisterExtension; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import redis.clients.jedis.*; import redis.clients.jedis.MultiDbConfig.DatabaseConfig; import redis.clients.jedis.exceptions.JedisAccessControlException; import redis.clients.jedis.exceptions.JedisConnectionException; import redis.clients.jedis.mcf.DatabaseSwitchEvent; import redis.clients.jedis.mcf.MultiDbConnectionProvider; import redis.clients.jedis.mcf.MultiDbConnectionProviderHelper; import redis.clients.jedis.mcf.SwitchReason; import redis.clients.jedis.util.EnvCondition; import redis.clients.jedis.util.IOUtils; import redis.clients.jedis.util.TestEnvUtil; import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.instanceOf; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertThrows; import static org.junit.jupiter.api.Assertions.assertTrue; @Tag("failover") @Tag("integration") @ConditionalOnEnv(value = TestEnvUtil.ENV_REDIS_ENTERPRISE, enabled = false) public class AutomaticFailoverTest { @RegisterExtension public static EnvCondition envCondition = new EnvCondition(); private static final Logger log = LoggerFactory.getLogger(AutomaticFailoverTest.class); private static HostAndPort hostPortWithFailure; private static EndpointConfig endpointForAuthFailure; private static EndpointConfig workingEndpoint; private final JedisClientConfig clientConfig = DefaultJedisClientConfig.builder().build(); private Jedis jedis2; @BeforeAll public static void prepareEndpoints() { endpointForAuthFailure = Endpoints.getRedisEndpoint("standalone0"); workingEndpoint = Endpoints.getRedisEndpoint("standalone7-with-lfu-policy"); hostPortWithFailure = new HostAndPort(endpointForAuthFailure.getHost(), 6378); } private List getDatabaseConfigs( JedisClientConfig clientConfig, HostAndPort... hostPorts) { return Arrays.stream(hostPorts) .map(hp -> DatabaseConfig.builder(hp, clientConfig).healthCheckEnabled(false).build()) .collect(Collectors.toList()); } @BeforeEach public void setUp() { jedis2 = new Jedis(workingEndpoint.getHostAndPort(), workingEndpoint.getClientConfigBuilder().build()); jedis2.flushAll(); } @AfterEach public void cleanUp() { IOUtils.closeQuietly(jedis2); } @Test public void pipelineWithSwitch() { MultiDbConnectionProvider provider = new MultiDbConnectionProvider( new MultiDbConfig.Builder( getDatabaseConfigs(clientConfig, hostPortWithFailure, workingEndpoint.getHostAndPort())) .build()); try (MultiDbClient client = MultiDbClient.builder().connectionProvider(provider).build()) { AbstractPipeline pipe = client.pipelined(); pipe.set("pstr", "foobar"); pipe.hset("phash", "foo", "bar"); MultiDbConnectionProviderHelper.switchToHealthyDatabase(provider, SwitchReason.HEALTH_CHECK, provider.getDatabase()); pipe.sync(); } assertEquals("foobar", jedis2.get("pstr")); assertEquals("bar", jedis2.hget("phash", "foo")); } @Test public void transactionWithSwitch() { MultiDbConnectionProvider provider = new MultiDbConnectionProvider( new MultiDbConfig.Builder( getDatabaseConfigs(clientConfig, hostPortWithFailure, workingEndpoint.getHostAndPort())) .build()); try (MultiDbClient client = MultiDbClient.builder().connectionProvider(provider).build()) { AbstractTransaction tx = client.multi(); tx.set("tstr", "foobar"); tx.hset("thash", "foo", "bar"); MultiDbConnectionProviderHelper.switchToHealthyDatabase(provider, SwitchReason.HEALTH_CHECK, provider.getDatabase()); assertEquals(Arrays.asList("OK", 1L), tx.exec()); } assertEquals("foobar", jedis2.get("tstr")); assertEquals("bar", jedis2.hget("thash", "foo")); } @Test public void commandFailoverUnresolvableHost() { int slidingWindowMinFails = 2; int slidingWindowSize = 2; HostAndPort unresolvableHostAndPort = new HostAndPort("unresolvable", 6379); MultiDbConfig.Builder builder = new MultiDbConfig.Builder( getDatabaseConfigs(clientConfig, unresolvableHostAndPort, workingEndpoint.getHostAndPort())) .commandRetry(MultiDbConfig.RetryConfig.builder().waitDuration(1).maxAttempts(1).build()) .failureDetector(MultiDbConfig.CircuitBreakerConfig.builder() .slidingWindowSize(slidingWindowSize) .minNumOfFailures(slidingWindowMinFails) .build()); RedisFailoverReporter failoverReporter = new RedisFailoverReporter(); MultiDbConnectionProvider connectionProvider = new MultiDbConnectionProvider( builder.build()); connectionProvider.setDatabaseSwitchListener(failoverReporter); MultiDbClient jedis = MultiDbClient.builder().connectionProvider(connectionProvider).build(); String key = "hash-" + System.nanoTime(); log.info("Starting calls to Redis"); assertFalse(failoverReporter.failedOver); for (int attempt = 0; attempt < slidingWindowMinFails; attempt++) { assertFalse(failoverReporter.failedOver); Throwable thrown = assertThrows(JedisConnectionException.class, () -> jedis.hset(key, "f1", "v1")); assertThat(thrown.getCause(), instanceOf(UnknownHostException.class)); } // already failed over now assertTrue(failoverReporter.failedOver); jedis.hset(key, "f1", "v1"); assertEquals(Collections.singletonMap("f1", "v1"), jedis.hgetAll(key)); jedis.flushAll(); jedis.close(); } @Test public void commandFailover() { int slidingWindowMinFails = 6; int slidingWindowSize = 6; int retryMaxAttempts = 3; MultiDbConfig.Builder builder = new MultiDbConfig.Builder( getDatabaseConfigs(clientConfig, hostPortWithFailure, workingEndpoint.getHostAndPort())) .commandRetry(MultiDbConfig.RetryConfig.builder().maxAttempts(retryMaxAttempts).build()) .failureDetector(MultiDbConfig.CircuitBreakerConfig.builder() .failureRateThreshold(50) .minNumOfFailures(slidingWindowMinFails) .slidingWindowSize(slidingWindowSize) .build()); RedisFailoverReporter failoverReporter = new RedisFailoverReporter(); MultiDbConnectionProvider connectionProvider = new MultiDbConnectionProvider( builder.build()); connectionProvider.setDatabaseSwitchListener(failoverReporter); MultiDbClient jedis = MultiDbClient.builder().connectionProvider(connectionProvider).build(); String key = "hash-" + System.nanoTime(); log.info("Starting calls to Redis"); assertFalse(failoverReporter.failedOver); // First call fails - will be retried 3 times // this will increase the CircuitBreaker failure count to 3 assertThrows(JedisConnectionException.class, () -> jedis.hset(key, "c1", "v1")); // Second call fails - will be retried 3 times // this will increase the CircuitBreaker failure count to 6 // should failover now assertThrows(JedisConnectionException.class, () -> jedis.hset(key, "c2", "v1")); // CB is in OPEN state now, next call should cause failover assertEquals(1L, jedis.hset(key, "c3", "v1")); assertTrue(failoverReporter.failedOver); assertEquals(Collections.singletonMap("c3", "v1"), jedis.hgetAll(key)); jedis.flushAll(); jedis.close(); } @Test public void pipelineFailover() { int slidingWindowSize = 10; MultiDbConfig.Builder builder = new MultiDbConfig.Builder( getDatabaseConfigs(clientConfig, hostPortWithFailure, workingEndpoint.getHostAndPort())) .failureDetector(MultiDbConfig.CircuitBreakerConfig.builder() .slidingWindowSize(slidingWindowSize) .build()) .fallbackExceptionList(Collections.singletonList(JedisConnectionException.class)); RedisFailoverReporter failoverReporter = new RedisFailoverReporter(); MultiDbConnectionProvider cacheProvider = new MultiDbConnectionProvider( builder.build()); cacheProvider.setDatabaseSwitchListener(failoverReporter); MultiDbClient jedis = MultiDbClient.builder().connectionProvider(cacheProvider).build(); String key = "hash-" + System.nanoTime(); log.info("Starting calls to Redis"); assertFalse(failoverReporter.failedOver); AbstractPipeline pipe = jedis.pipelined(); assertFalse(failoverReporter.failedOver); pipe.hset(key, "f1", "v1"); assertFalse(failoverReporter.failedOver); pipe.sync(); assertTrue(failoverReporter.failedOver); assertEquals(Collections.singletonMap("f1", "v1"), jedis.hgetAll(key)); jedis.flushAll(); jedis.close(); } @Test public void failoverFromAuthError() { int slidingWindowSize = 10; MultiDbConfig.Builder builder = new MultiDbConfig.Builder( getDatabaseConfigs(clientConfig, endpointForAuthFailure.getHostAndPort(), workingEndpoint.getHostAndPort())) .failureDetector(MultiDbConfig.CircuitBreakerConfig.builder() .slidingWindowSize(slidingWindowSize) .build()) .fallbackExceptionList(Collections.singletonList(JedisAccessControlException.class)); RedisFailoverReporter failoverReporter = new RedisFailoverReporter(); MultiDbConnectionProvider cacheProvider = new MultiDbConnectionProvider( builder.build()); cacheProvider.setDatabaseSwitchListener(failoverReporter); MultiDbClient jedis = MultiDbClient.builder().connectionProvider(cacheProvider).build(); String key = "hash-" + System.nanoTime(); log.info("Starting calls to Redis"); assertFalse(failoverReporter.failedOver); jedis.hset(key, "f1", "v1"); assertTrue(failoverReporter.failedOver); assertEquals(Collections.singletonMap("f1", "v1"), jedis.hgetAll(key)); jedis.flushAll(); jedis.close(); } static class RedisFailoverReporter implements Consumer { boolean failedOver = false; @Override public void accept(DatabaseSwitchEvent e) { log.info("Jedis fail over to cluster: " + e.getDatabaseName()); failedOver = true; } } } ================================================ FILE: src/test/java/redis/clients/jedis/misc/ClientSetInfoConfigTest.java ================================================ package redis.clients.jedis.misc; import java.util.Arrays; import org.junit.jupiter.api.Test; import redis.clients.jedis.ClientSetInfoConfig; import redis.clients.jedis.DriverInfo; import redis.clients.jedis.exceptions.JedisValidationException; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertNotNull; import static org.junit.jupiter.api.Assertions.assertThrows; import static org.junit.jupiter.api.Assertions.assertTrue; public class ClientSetInfoConfigTest { @Test public void defaultConfig() { ClientSetInfoConfig config = ClientSetInfoConfig.DEFAULT; assertFalse(config.isDisabled()); assertEquals("", config.getUpstreamDrivers()); assertNotNull(config.getDriverInfo()); assertEquals("jedis", config.getDriverInfo().getName()); assertEquals("jedis", config.getDriverInfo().getFormattedName()); } @Test public void disabledConfig() { ClientSetInfoConfig config = ClientSetInfoConfig.DISABLED; assertTrue(config.isDisabled()); assertEquals("", config.getUpstreamDrivers()); } @Test public void constructorWithNullDriverInfoThrows() { assertThrows(JedisValidationException.class, () -> new ClientSetInfoConfig((DriverInfo) null)); } @Test public void withLibNameSuffixFormatsCorrectly() { ClientSetInfoConfig config = ClientSetInfoConfig.withLibNameSuffix("my-suffix"); assertEquals("my-suffix", config.getUpstreamDrivers()); assertEquals("jedis(my-suffix)", config.getDriverInfo().getFormattedName()); } @Test public void withLibNameSuffixThenAddUpstreamDriverPrepends() { // Start with legacy suffix ClientSetInfoConfig config = ClientSetInfoConfig.withLibNameSuffix("my-suffix"); assertEquals("jedis(my-suffix)", config.getDriverInfo().getFormattedName()); // Add upstream driver - should prepend to the suffix DriverInfo driverInfo = DriverInfo.builder(config.getDriverInfo()) .addUpstreamDriver("spring-data-redis", "3.2.0").build(); config = new ClientSetInfoConfig(driverInfo); assertEquals("spring-data-redis_v3.2.0;my-suffix", config.getUpstreamDrivers()); assertEquals("jedis(spring-data-redis_v3.2.0;my-suffix)", config.getDriverInfo().getFormattedName()); } @Test public void defaultNameWithUpstreamDriversKeepsJedisName() { // When using default name, adding upstream drivers should keep "jedis" as the base name DriverInfo driverInfo = DriverInfo.builder().addUpstreamDriver("spring-data-redis", "3.2.0") .build(); assertEquals("jedis", driverInfo.getName()); assertEquals("jedis(spring-data-redis_v3.2.0)", driverInfo.getFormattedName()); } @Test public void chainingDriverInfoFromExistingConfig() { // First library (e.g., spring-data-redis) creates its config DriverInfo firstDriverInfo = DriverInfo.builder() .addUpstreamDriver("spring-data-redis", "3.2.0").build(); ClientSetInfoConfig firstConfig = new ClientSetInfoConfig(firstDriverInfo); assertEquals("jedis(spring-data-redis_v3.2.0)", firstConfig.getDriverInfo().getFormattedName()); // Second library builds on top of the first config DriverInfo secondDriverInfo = DriverInfo.builder(firstConfig.getDriverInfo()) .addUpstreamDriver("upstream-library", "1.0.0").build(); ClientSetInfoConfig secondConfig = new ClientSetInfoConfig(secondDriverInfo); assertEquals("upstream-library_v1.0.0;spring-data-redis_v3.2.0", secondConfig.getUpstreamDrivers()); assertEquals("jedis(upstream-library_v1.0.0;spring-data-redis_v3.2.0)", secondConfig.getDriverInfo().getFormattedName()); } @Test public void withLibNameSuffixErrorForBraces() { Arrays.asList('(', ')', '[', ']', '{', '}') .forEach(brace -> assertThrows(JedisValidationException.class, () -> ClientSetInfoConfig.withLibNameSuffix("" + brace))); } @Test public void builderWithNullDriverInfoThrows() { assertThrows(JedisValidationException.class, () -> DriverInfo.builder(null)); } @Test public void builderNameNullThrows() { assertThrows(JedisValidationException.class, () -> DriverInfo.builder().name(null)); } @Test public void builderCustomName() { DriverInfo driverInfo = DriverInfo.builder().name("my-custom-client").build(); assertEquals("my-custom-client", driverInfo.getName()); assertEquals("my-custom-client", driverInfo.getFormattedName()); assertEquals("", driverInfo.getUpstreamDrivers()); } @Test public void builderCustomNameWithUpstreamDrivers() { DriverInfo driverInfo = DriverInfo.builder().name("my-custom-client") .addUpstreamDriver("spring-data-redis", "3.2.0").build(); assertEquals("my-custom-client", driverInfo.getName()); assertEquals("my-custom-client(spring-data-redis_v3.2.0)", driverInfo.getFormattedName()); assertEquals("spring-data-redis_v3.2.0", driverInfo.getUpstreamDrivers()); } @Test public void builderCopiesExistingDriverInfo() { DriverInfo original = DriverInfo.builder().name("custom-name") .addUpstreamDriver("driver1", "1.0.0").build(); DriverInfo copied = DriverInfo.builder(original).addUpstreamDriver("driver2", "2.0.0").build(); assertEquals("custom-name", copied.getName()); assertEquals("driver2_v2.0.0;driver1_v1.0.0", copied.getUpstreamDrivers()); } @Test public void addUpstreamDriverSingle() { DriverInfo driverInfo = DriverInfo.builder().addUpstreamDriver("spring-data-redis", "3.2.0") .build(); ClientSetInfoConfig config = new ClientSetInfoConfig(driverInfo); assertEquals("spring-data-redis_v3.2.0", config.getUpstreamDrivers()); } @Test public void addUpstreamDriverMultiple() { DriverInfo driverInfo = DriverInfo.builder().addUpstreamDriver("driver1", "1.0.0") .addUpstreamDriver("driver2", "2.0.0").addUpstreamDriver("driver3", "3.0.0").build(); ClientSetInfoConfig config = new ClientSetInfoConfig(driverInfo); assertEquals("driver3_v3.0.0;driver2_v2.0.0;driver1_v1.0.0", config.getUpstreamDrivers()); } @Test public void addUpstreamDriverPrepends() { DriverInfo driverInfo = DriverInfo.builder().addUpstreamDriver("first", "1.0.0").build(); ClientSetInfoConfig config = new ClientSetInfoConfig(driverInfo); assertEquals("first_v1.0.0", config.getUpstreamDrivers()); driverInfo = DriverInfo.builder(config.getDriverInfo()).addUpstreamDriver("second", "2.0.0") .build(); config = new ClientSetInfoConfig(driverInfo); assertEquals("second_v2.0.0;first_v1.0.0", config.getUpstreamDrivers()); driverInfo = DriverInfo.builder(config.getDriverInfo()).addUpstreamDriver("third", "3.0.0") .build(); config = new ClientSetInfoConfig(driverInfo); assertEquals("third_v3.0.0;second_v2.0.0;first_v1.0.0", config.getUpstreamDrivers()); } @Test public void formattedNameWithNoUpstreamDrivers() { DriverInfo driverInfo = DriverInfo.builder().build(); assertEquals("jedis", driverInfo.getFormattedName()); } @Test public void formattedNameWithSingleUpstreamDriver() { DriverInfo driverInfo = DriverInfo.builder().addUpstreamDriver("spring-data-redis", "3.2.0") .build(); assertEquals("jedis(spring-data-redis_v3.2.0)", driverInfo.getFormattedName()); } @Test public void formattedNameWithMultipleUpstreamDrivers() { DriverInfo driverInfo = DriverInfo.builder().addUpstreamDriver("driver1", "1.0.0") .addUpstreamDriver("driver2", "2.0.0").build(); assertEquals("jedis(driver2_v2.0.0;driver1_v1.0.0)", driverInfo.getFormattedName()); } @Test public void toStringReturnsFormattedName() { DriverInfo driverInfo = DriverInfo.builder().addUpstreamDriver("spring-data-redis", "3.2.0") .build(); assertEquals(driverInfo.getFormattedName(), driverInfo.toString()); } @Test public void driverNameValidation() { // Valid names DriverInfo.builder().addUpstreamDriver("spring-data-redis", "1.0.0"); DriverInfo.builder().addUpstreamDriver("lettuce-core", "1.0.0"); DriverInfo.builder().addUpstreamDriver("akka-redis_2.13", "1.0.0"); DriverInfo.builder().addUpstreamDriver("jedis", "1.0.0"); DriverInfo.builder().addUpstreamDriver("redis-client", "1.0.0"); DriverInfo.builder().addUpstreamDriver("my_driver", "1.0.0"); DriverInfo.builder().addUpstreamDriver("driver123", "1.0.0"); DriverInfo.builder().addUpstreamDriver("Spring-Data", "1.0.0"); DriverInfo.builder().addUpstreamDriver("123driver", "1.0.0"); DriverInfo.builder().addUpstreamDriver("driver@name", "1.0.0"); DriverInfo.builder().addUpstreamDriver("driver.name", "1.0.0"); assertThrows(JedisValidationException.class, () -> DriverInfo.builder().addUpstreamDriver("driver name", "1.0.0")); // space } @Test public void driverNameNullOrEmpty() { assertThrows(JedisValidationException.class, () -> DriverInfo.builder().addUpstreamDriver(null, "3.2.0")); assertThrows(JedisValidationException.class, () -> DriverInfo.builder().addUpstreamDriver("", "3.2.0")); } @Test public void driverVersionNullOrEmpty() { assertThrows(JedisValidationException.class, () -> DriverInfo.builder().addUpstreamDriver("spring-data-redis", null)); assertThrows(JedisValidationException.class, () -> DriverInfo.builder().addUpstreamDriver("spring-data-redis", "")); } } ================================================ FILE: src/test/java/redis/clients/jedis/misc/ClusterInitErrorTest.java ================================================ package redis.clients.jedis.misc; import java.util.Collections; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.Test; import redis.clients.jedis.EndpointConfig; import redis.clients.jedis.Endpoints; import redis.clients.jedis.RedisClusterClient; import redis.clients.jedis.exceptions.JedisClusterOperationException; import static org.junit.jupiter.api.Assertions.assertNull; import static org.junit.jupiter.api.Assertions.assertThrows; public class ClusterInitErrorTest { private static final String INIT_NO_ERROR_PROPERTY = "jedis.cluster.initNoError"; @AfterEach public void cleanUp() { System.getProperties().remove(INIT_NO_ERROR_PROPERTY); } @Test public void initError() { assertNull(System.getProperty(INIT_NO_ERROR_PROPERTY)); EndpointConfig endpoint = Endpoints.getRedisEndpoint("standalone0"); assertThrows(JedisClusterOperationException.class, () -> { try (RedisClusterClient cluster = RedisClusterClient.builder() .nodes(Collections.singleton(endpoint.getHostAndPort())) .clientConfig(endpoint.getClientConfigBuilder().build()) .build()) { // Intentionally left empty because the exception is expected } }); } @Test public void initNoError() { System.setProperty(INIT_NO_ERROR_PROPERTY, ""); EndpointConfig endpoint = Endpoints.getRedisEndpoint("standalone0"); try (RedisClusterClient cluster = RedisClusterClient.builder() .nodes(Collections.singleton(endpoint.getHostAndPort())) .clientConfig(endpoint.getClientConfigBuilder().build()) .build()) { assertThrows(JedisClusterOperationException.class, () -> cluster.get("foo")); } } } ================================================ FILE: src/test/java/redis/clients/jedis/misc/ResponsesToStringTest.java ================================================ package redis.clients.jedis.misc; import org.junit.jupiter.api.Test; import redis.clients.jedis.GeoCoordinate; import redis.clients.jedis.resps.GeoRadiusResponse; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertNotEquals; import static org.junit.jupiter.api.Assertions.assertTrue; public class ResponsesToStringTest { @Test public void GeoRadiusResponse() { byte[] member = {0x01, 0x02, 0x03, 0x04}; GeoRadiusResponse response = new GeoRadiusResponse(member); response.setDistance(5); response.setCoordinate(new GeoCoordinate(2, 3)); response.setRawScore(10); GeoRadiusResponse response_copy = new GeoRadiusResponse(member); response_copy.setDistance(5); response_copy.setCoordinate(new GeoCoordinate(2, 3)); response_copy.setRawScore(10); assertTrue(response.equals(response)); assertEquals(response, response_copy); assertNotEquals(response, new Object()); } } ================================================ FILE: src/test/java/redis/clients/jedis/misc/TupleTest.java ================================================ package redis.clients.jedis.misc; import java.util.HashSet; import org.hamcrest.MatcherAssert; import org.hamcrest.Matchers; import org.junit.jupiter.api.Test; import redis.clients.jedis.resps.Tuple; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertTrue; public class TupleTest { @Test public void compareSameObject() { Tuple t1 = new Tuple("foo", 1d); assertTrue(t1.equals(t1)); } @Test public void compareEqual() { Tuple t1 = new Tuple("foo", 1d); Tuple t2 = new Tuple("foo", 1d); assertEquals(0, t1.compareTo(t2)); assertEquals(0, t2.compareTo(t1)); assertTrue(t1.equals(t2)); assertTrue(t2.equals(t1)); } @Test public void compareSameScore() { Tuple t1 = new Tuple("foo", 1d); Tuple t2 = new Tuple("bar", 1d); assertEquals(1, t1.compareTo(t2)); assertEquals(-1, t2.compareTo(t1)); assertFalse(t1.equals(t2)); assertFalse(t2.equals(t1)); } @Test public void compareSameScoreObject() { Double score = 1d; Tuple t1 = new Tuple("foo", score); Tuple t2 = new Tuple("bar", score); assertEquals(1, t1.compareTo(t2)); assertEquals(-1, t2.compareTo(t1)); assertFalse(t1.equals(t2)); assertFalse(t2.equals(t1)); } @Test public void compareNoMatch() { Tuple t1 = new Tuple("foo", 1d); Tuple t2 = new Tuple("bar", 2d); assertEquals(-1, t1.compareTo(t2)); assertEquals(1, t2.compareTo(t1)); assertFalse(t1.equals(t2)); assertFalse(t2.equals(t1)); } @Test public void compareDifferentType() { Tuple t1 = new Tuple("foo", 1d); Object anyObject = new Object(); assertFalse(t1.equals(anyObject)); Object nullObject = null; assertFalse(t1.equals(nullObject)); } @Test public void testToString() { Tuple t1 = new Tuple("key-name", 1d); String toStringResult = t1.toString(); MatcherAssert.assertThat(toStringResult, Matchers.containsString("key-name")); MatcherAssert.assertThat(toStringResult, Matchers.containsString("1")); } @Test public void testSameElement() { Tuple t1 = new Tuple("user1", 10.0); Tuple t2 = new Tuple("user1", 5.0); // Intentionally skipping compareTo. assertFalse(t1.equals(t2)); assertFalse(t2.equals(t1)); HashSet hashSet = new HashSet<>(); hashSet.add(t1); hashSet.add(t2); assertEquals(2, hashSet.size()); } } ================================================ FILE: src/test/java/redis/clients/jedis/mocked/MockedCommandObjectsTestBase.java ================================================ package redis.clients.jedis.mocked; import java.util.List; import java.util.Map; import java.util.Set; import org.json.JSONArray; import org.junit.jupiter.api.extension.ExtendWith; import org.mockito.Mock; import redis.clients.jedis.CommandObject; import redis.clients.jedis.GeoCoordinate; import redis.clients.jedis.StreamEntryID; import redis.clients.jedis.resps.*; import redis.clients.jedis.search.ProfilingInfo; import redis.clients.jedis.search.SearchResult; import redis.clients.jedis.search.aggr.AggregationResult; import redis.clients.jedis.timeseries.*; import redis.clients.jedis.util.KeyValue; import org.mockito.junit.jupiter.MockitoExtension; /** * Provides an exhaustive list of mocked {@link redis.clients.jedis.CommandObject}s for use in unit tests. */ @ExtendWith(MockitoExtension.class) public abstract class MockedCommandObjectsTestBase { /** * Used for JSON related tests. The fields are not used actually, given that tests are mocked. */ @SuppressWarnings("unused") public final static class MyBean { String field1; String field2; } // Below follows a list of mocked CommandObjects, one per type. This is the cleanest way to create // mocks, given that CommandObject is a generic class. Using {@code Mockito.mock(...)} yields too // many warnings related to generics. // To make the code more readable, try to keep the list sorted alphabetically, and without automatic // reformatting. // @formatter:off @Mock protected CommandObject aggregationResultCommandObject; @Mock protected CommandObject booleanCommandObject; @Mock protected CommandObject> classCommandObject; @Mock protected CommandObject doubleCommandObject; @Mock protected CommandObject functionStatsCommandObject; @Mock protected CommandObject> keyValueLongDoubleCommandObject; @Mock protected CommandObject> keyValueLongLongCommandObject; @Mock protected CommandObject>> keyValueStringListStringCommandObject; @Mock protected CommandObject>> keyValueStringListTupleCommandObject; @Mock protected CommandObject> keyValueStringStringCommandObject; @Mock protected CommandObject> keyValueStringTupleCommandObject; @Mock protected CommandObject>> keyValueBytesListTupleCommandObject; @Mock protected CommandObject>> keyValueBytesListBytesCommandObject; @Mock protected CommandObject> keyValueBytesTupleCommandObject; @Mock protected CommandObject> keyValueBytesBytesCommandObject; @Mock protected CommandObject lcsMatchResultCommandObject; @Mock protected CommandObject> listBooleanCommandObject; @Mock protected CommandObject>> listClassCommandObject; @Mock protected CommandObject> listDoubleCommandObject; @Mock protected CommandObject> listGeoCoordinateCommandObject; @Mock protected CommandObject> listGeoRadiusResponseCommandObject; @Mock protected CommandObject> listJsonArrayCommandObject; @Mock protected CommandObject> listLibraryInfoCommandObject; @Mock protected CommandObject>> listListObjectCommandObject; @Mock protected CommandObject>> listListStringCommandObject; @Mock protected CommandObject> listLongCommandObject; @Mock protected CommandObject>>> listEntryStringListStreamEntryCommandObject; @Mock protected CommandObject>> listEntryStringStringCommandObject; @Mock protected CommandObject>> listEntryBytesBytesCommandObject; @Mock protected CommandObject> listMyBeanCommandObject; @Mock protected CommandObject> listObjectCommandObject; @Mock protected CommandObject> listStreamConsumerInfoCommandObject; @Mock protected CommandObject> listStreamConsumersInfoCommandObject; @Mock protected CommandObject> listStreamEntryCommandObject; @Mock protected CommandObject> listStreamEntryIdCommandObject; @Mock protected CommandObject> listStreamGroupInfoCommandObject; @Mock protected CommandObject> listStreamPendingEntryCommandObject; @Mock protected CommandObject> listStringCommandObject; @Mock protected CommandObject> listTsElementCommandObject; @Mock protected CommandObject> listTupleCommandObject; @Mock protected CommandObject> listBytesCommandObject; @Mock protected CommandObject longCommandObject; @Mock protected CommandObject> entryAggregationResultMapStringObjectCommandObject; @Mock protected CommandObject> entryLongBytesCommandObject; @Mock protected CommandObject> entrySearchResultMapStringObjectCommandObject; @Mock protected CommandObject>> entryStreamEntryIdListStreamEntryCommandObject; @Mock protected CommandObject>> entryStreamEntryIdListStreamEntryIdCommandObject; @Mock protected CommandObject>> mapStringListStringCommandObject; @Mock protected CommandObject>> mapStringListStreamEntryCommandObject; @Mock protected CommandObject> mapStringLongCommandObject; @Mock protected CommandObject>> mapStringMapStringDoubleCommandObject; @Mock protected CommandObject> mapStringObjectCommandObject; @Mock protected CommandObject> mapStringStringCommandObject; @Mock protected CommandObject> mapStringTsmGetElementCommandObject; @Mock protected CommandObject> mapStringTsmRangeElementsCommandObject; @Mock protected CommandObject> mapBytesBytesCommandObject; @Mock protected CommandObject myBeanCommandObject; @Mock protected CommandObject objectCommandObject; @Mock protected CommandObject>> scanResultEntryStringStringCommandObject; @Mock protected CommandObject>> scanResultEntryBytesBytesCommandObject; @Mock protected CommandObject> scanResultStringCommandObject; @Mock protected CommandObject> scanResultTupleCommandObject; @Mock protected CommandObject> scanResultBytesCommandObject; @Mock protected CommandObject searchResultCommandObject; @Mock protected CommandObject> setStringCommandObject; @Mock protected CommandObject> setBytesCommandObject; @Mock protected CommandObject streamEntryIdCommandObject; @Mock protected CommandObject streamFullInfoCommandObject; @Mock protected CommandObject streamInfoCommandObject; @Mock protected CommandObject streamPendingSummaryCommandObject; @Mock protected CommandObject stringCommandObject; @Mock protected CommandObject tsElementCommandObject; @Mock protected CommandObject tsInfoCommandObject; @Mock protected CommandObject tupleCommandObject; @Mock protected CommandObject bytesCommandObject; // @formatter:on } ================================================ FILE: src/test/java/redis/clients/jedis/mocked/pipeline/PipeliningBaseBitmapCommandsTest.java ================================================ package redis.clients.jedis.mocked.pipeline; import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.contains; import static org.hamcrest.Matchers.is; import static org.mockito.Mockito.when; import java.util.List; import org.junit.jupiter.api.Test; import redis.clients.jedis.Response; import redis.clients.jedis.args.BitCountOption; import redis.clients.jedis.args.BitOP; import redis.clients.jedis.params.BitPosParams; public class PipeliningBaseBitmapCommandsTest extends PipeliningBaseMockedTestBase { @Test public void testBitcount() { when(commandObjects.bitcount("key")).thenReturn(longCommandObject); Response response = pipeliningBase.bitcount("key"); assertThat(commands, contains(longCommandObject)); assertThat(response, is(predefinedResponse)); } @Test public void testBitcountBinary() { byte[] key = "key".getBytes(); when(commandObjects.bitcount(key)).thenReturn(longCommandObject); Response response = pipeliningBase.bitcount(key); assertThat(commands, contains(longCommandObject)); assertThat(response, is(predefinedResponse)); } @Test public void testBitcountRange() { when(commandObjects.bitcount("key", 0, 10)).thenReturn(longCommandObject); Response response = pipeliningBase.bitcount("key", 0, 10); assertThat(commands, contains(longCommandObject)); assertThat(response, is(predefinedResponse)); } @Test public void testBitcountRangeBinary() { byte[] key = "key".getBytes(); long start = 0L; long end = 10L; when(commandObjects.bitcount(key, start, end)).thenReturn(longCommandObject); Response response = pipeliningBase.bitcount(key, start, end); assertThat(commands, contains(longCommandObject)); assertThat(response, is(predefinedResponse)); } @Test public void testBitcountRangeOption() { BitCountOption option = BitCountOption.BYTE; when(commandObjects.bitcount("key", 0, 10, option)).thenReturn(longCommandObject); Response response = pipeliningBase.bitcount("key", 0, 10, option); assertThat(commands, contains(longCommandObject)); assertThat(response, is(predefinedResponse)); } @Test public void testBitcountRangeOptionBinary() { byte[] key = "key".getBytes(); long start = 0L; long end = 10L; BitCountOption option = BitCountOption.BYTE; when(commandObjects.bitcount(key, start, end, option)).thenReturn(longCommandObject); Response response = pipeliningBase.bitcount(key, start, end, option); assertThat(commands, contains(longCommandObject)); assertThat(response, is(predefinedResponse)); } @Test public void testBitfield() { String[] arguments = { "INCRBY", "mykey", "2", "1" }; when(commandObjects.bitfield("key", arguments)).thenReturn(listLongCommandObject); Response> response = pipeliningBase.bitfield("key", arguments); assertThat(commands, contains(listLongCommandObject)); assertThat(response, is(predefinedResponse)); } @Test public void testBitfieldBinary() { byte[] key = "key".getBytes(); byte[][] arguments = { "INCRBY".getBytes(), "mykey".getBytes(), "2".getBytes(), "1".getBytes() }; when(commandObjects.bitfield(key, arguments)).thenReturn(listLongCommandObject); Response> response = pipeliningBase.bitfield(key, arguments); assertThat(commands, contains(listLongCommandObject)); assertThat(response, is(predefinedResponse)); } @Test public void testBitfieldReadonly() { String[] arguments = { "GET", "u4", "0" }; when(commandObjects.bitfieldReadonly("key", arguments)).thenReturn(listLongCommandObject); Response> response = pipeliningBase.bitfieldReadonly("key", arguments); assertThat(commands, contains(listLongCommandObject)); assertThat(response, is(predefinedResponse)); } @Test public void testBitfieldReadonlyBinary() { byte[] key = "key".getBytes(); byte[][] arguments = { "GET".getBytes(), "u4".getBytes(), "0".getBytes() }; when(commandObjects.bitfieldReadonly(key, arguments)).thenReturn(listLongCommandObject); Response> response = pipeliningBase.bitfieldReadonly(key, arguments); assertThat(commands, contains(listLongCommandObject)); assertThat(response, is(predefinedResponse)); } @Test public void testBitop() { BitOP op = BitOP.AND; when(commandObjects.bitop(op, "destKey", "srckey1", "srckey2")).thenReturn(longCommandObject); Response response = pipeliningBase.bitop(op, "destKey", "srckey1", "srckey2"); assertThat(commands, contains(longCommandObject)); assertThat(response, is(predefinedResponse)); } @Test public void testBitopBinary() { BitOP op = BitOP.AND; byte[] destKey = "destKey".getBytes(); byte[] srcKey1 = "srcKey1".getBytes(); byte[] srcKey2 = "srcKey2".getBytes(); when(commandObjects.bitop(op, destKey, srcKey1, srcKey2)).thenReturn(longCommandObject); Response response = pipeliningBase.bitop(op, destKey, srcKey1, srcKey2); assertThat(commands, contains(longCommandObject)); assertThat(response, is(predefinedResponse)); } @Test public void testBitpos() { when(commandObjects.bitpos("key", true)).thenReturn(longCommandObject); Response response = pipeliningBase.bitpos("key", true); assertThat(commands, contains(longCommandObject)); assertThat(response, is(predefinedResponse)); } @Test public void testBitposBinary() { byte[] key = "key".getBytes(); boolean value = true; when(commandObjects.bitpos(key, value)).thenReturn(longCommandObject); Response response = pipeliningBase.bitpos(key, value); assertThat(commands, contains(longCommandObject)); assertThat(response, is(predefinedResponse)); } @Test public void testBitposParams() { BitPosParams params = new BitPosParams(0, -1); when(commandObjects.bitpos("key", true, params)).thenReturn(longCommandObject); Response response = pipeliningBase.bitpos("key", true, params); assertThat(commands, contains(longCommandObject)); assertThat(response, is(predefinedResponse)); } @Test public void testBitposParamsBinary() { byte[] key = "key".getBytes(); boolean value = true; BitPosParams params = new BitPosParams(0); when(commandObjects.bitpos(key, value, params)).thenReturn(longCommandObject); Response response = pipeliningBase.bitpos(key, value, params); assertThat(commands, contains(longCommandObject)); assertThat(response, is(predefinedResponse)); } @Test public void testGetbit() { when(commandObjects.getbit("key", 100)).thenReturn(booleanCommandObject); Response response = pipeliningBase.getbit("key", 100); assertThat(commands, contains(booleanCommandObject)); assertThat(response, is(predefinedResponse)); } @Test public void testGetbitBinary() { byte[] key = "key".getBytes(); long offset = 10L; when(commandObjects.getbit(key, offset)).thenReturn(booleanCommandObject); Response response = pipeliningBase.getbit(key, offset); assertThat(commands, contains(booleanCommandObject)); assertThat(response, is(predefinedResponse)); } @Test public void testSetbit() { when(commandObjects.setbit("key", 100, true)).thenReturn(booleanCommandObject); Response response = pipeliningBase.setbit("key", 100, true); assertThat(commands, contains(booleanCommandObject)); assertThat(response, is(predefinedResponse)); } @Test public void testSetbitBinary() { byte[] key = "key".getBytes(); long offset = 10L; boolean value = true; when(commandObjects.setbit(key, offset, value)).thenReturn(booleanCommandObject); Response response = pipeliningBase.setbit(key, offset, value); assertThat(commands, contains(booleanCommandObject)); assertThat(response, is(predefinedResponse)); } } ================================================ FILE: src/test/java/redis/clients/jedis/mocked/pipeline/PipeliningBaseBloomFilterCommandsTest.java ================================================ package redis.clients.jedis.mocked.pipeline; import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.contains; import static org.hamcrest.Matchers.is; import static org.mockito.Mockito.when; import java.util.List; import java.util.Map; import org.junit.jupiter.api.Test; import redis.clients.jedis.Response; import redis.clients.jedis.bloom.BFInsertParams; import redis.clients.jedis.bloom.BFReserveParams; public class PipeliningBaseBloomFilterCommandsTest extends PipeliningBaseMockedTestBase { @Test public void testBfAdd() { when(commandObjects.bfAdd("myBloomFilter", "item1")).thenReturn(booleanCommandObject); Response response = pipeliningBase.bfAdd("myBloomFilter", "item1"); assertThat(commands, contains(booleanCommandObject)); assertThat(response, is(predefinedResponse)); } @Test public void testBfCard() { when(commandObjects.bfCard("myBloomFilter")).thenReturn(longCommandObject); Response response = pipeliningBase.bfCard("myBloomFilter"); assertThat(commands, contains(longCommandObject)); assertThat(response, is(predefinedResponse)); } @Test public void testBfExists() { when(commandObjects.bfExists("myBloomFilter", "item1")).thenReturn(booleanCommandObject); Response response = pipeliningBase.bfExists("myBloomFilter", "item1"); assertThat(commands, contains(booleanCommandObject)); assertThat(response, is(predefinedResponse)); } @Test public void testBfInfo() { when(commandObjects.bfInfo("myBloomFilter")).thenReturn(mapStringObjectCommandObject); Response> response = pipeliningBase.bfInfo("myBloomFilter"); assertThat(commands, contains(mapStringObjectCommandObject)); assertThat(response, is(predefinedResponse)); } @Test public void testBfInsert() { when(commandObjects.bfInsert("myBloomFilter", "item1", "item2")).thenReturn(listBooleanCommandObject); Response> response = pipeliningBase.bfInsert("myBloomFilter", "item1", "item2"); assertThat(commands, contains(listBooleanCommandObject)); assertThat(response, is(predefinedResponse)); } @Test public void testBfInsertWithParams() { BFInsertParams insertParams = new BFInsertParams().capacity(10000L).error(0.01); when(commandObjects.bfInsert("myBloomFilter", insertParams, "item1", "item2")).thenReturn(listBooleanCommandObject); Response> response = pipeliningBase.bfInsert("myBloomFilter", insertParams, "item1", "item2"); assertThat(commands, contains(listBooleanCommandObject)); assertThat(response, is(predefinedResponse)); } @Test public void testBfLoadChunk() { byte[] data = { 1, 2, 3, 4 }; when(commandObjects.bfLoadChunk("myBloomFilter", 0L, data)).thenReturn(stringCommandObject); Response response = pipeliningBase.bfLoadChunk("myBloomFilter", 0L, data); assertThat(commands, contains(stringCommandObject)); assertThat(response, is(predefinedResponse)); } @Test public void testBfMAdd() { when(commandObjects.bfMAdd("myBloomFilter", "item1", "item2")).thenReturn(listBooleanCommandObject); Response> response = pipeliningBase.bfMAdd("myBloomFilter", "item1", "item2"); assertThat(commands, contains(listBooleanCommandObject)); assertThat(response, is(predefinedResponse)); } @Test public void testBfMExists() { when(commandObjects.bfMExists("myBloomFilter", "item1", "item2")).thenReturn(listBooleanCommandObject); Response> response = pipeliningBase.bfMExists("myBloomFilter", "item1", "item2"); assertThat(commands, contains(listBooleanCommandObject)); assertThat(response, is(predefinedResponse)); } @Test public void testBfReserve() { double errorRate = 0.01; long capacity = 10000L; when(commandObjects.bfReserve("myBloomFilter", errorRate, capacity)).thenReturn(stringCommandObject); Response response = pipeliningBase.bfReserve("myBloomFilter", errorRate, capacity); assertThat(commands, contains(stringCommandObject)); assertThat(response, is(predefinedResponse)); } @Test public void testBfReserveWithParams() { double errorRate = 0.01; long capacity = 10000L; BFReserveParams reserveParams = new BFReserveParams().expansion(2); when(commandObjects.bfReserve("myBloomFilter", errorRate, capacity, reserveParams)).thenReturn(stringCommandObject); Response response = pipeliningBase.bfReserve("myBloomFilter", errorRate, capacity, reserveParams); assertThat(commands, contains(stringCommandObject)); assertThat(response, is(predefinedResponse)); } @Test public void testBfScanDump() { when(commandObjects.bfScanDump("myBloomFilter", 0L)).thenReturn(entryLongBytesCommandObject); Response> response = pipeliningBase.bfScanDump("myBloomFilter", 0L); assertThat(commands, contains(entryLongBytesCommandObject)); assertThat(response, is(predefinedResponse)); } } ================================================ FILE: src/test/java/redis/clients/jedis/mocked/pipeline/PipeliningBaseCountMinSketchCommandsTest.java ================================================ package redis.clients.jedis.mocked.pipeline; import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.contains; import static org.hamcrest.Matchers.is; import static org.mockito.Mockito.when; import java.util.HashMap; import java.util.List; import java.util.Map; import org.junit.jupiter.api.Test; import redis.clients.jedis.Response; public class PipeliningBaseCountMinSketchCommandsTest extends PipeliningBaseMockedTestBase { @Test public void testCmsIncrBy() { Map itemIncrements = new HashMap<>(); itemIncrements.put("item1", 1L); itemIncrements.put("item2", 2L); when(commandObjects.cmsIncrBy("myCountMinSketch", itemIncrements)).thenReturn(listLongCommandObject); Response> response = pipeliningBase.cmsIncrBy("myCountMinSketch", itemIncrements); assertThat(commands, contains(listLongCommandObject)); assertThat(response, is(predefinedResponse)); } @Test public void testCmsInfo() { when(commandObjects.cmsInfo("myCountMinSketch")).thenReturn(mapStringObjectCommandObject); Response> response = pipeliningBase.cmsInfo("myCountMinSketch"); assertThat(commands, contains(mapStringObjectCommandObject)); assertThat(response, is(predefinedResponse)); } @Test public void testCmsInitByDim() { when(commandObjects.cmsInitByDim("myCountMinSketch", 1000L, 5L)).thenReturn(stringCommandObject); Response response = pipeliningBase.cmsInitByDim("myCountMinSketch", 1000L, 5L); assertThat(commands, contains(stringCommandObject)); assertThat(response, is(predefinedResponse)); } @Test public void testCmsInitByProb() { double error = 0.01; double probability = 0.99; when(commandObjects.cmsInitByProb("myCountMinSketch", error, probability)).thenReturn(stringCommandObject); Response response = pipeliningBase.cmsInitByProb("myCountMinSketch", error, probability); assertThat(commands, contains(stringCommandObject)); assertThat(response, is(predefinedResponse)); } @Test public void testCmsMerge() { when(commandObjects.cmsMerge("mergedCountMinSketch", "cms1", "cms2")).thenReturn(stringCommandObject); Response response = pipeliningBase.cmsMerge("mergedCountMinSketch", "cms1", "cms2"); assertThat(commands, contains(stringCommandObject)); assertThat(response, is(predefinedResponse)); } @Test public void testCmsMergeWithWeights() { Map keysAndWeights = new HashMap<>(); keysAndWeights.put("cms1", 1L); keysAndWeights.put("cms2", 2L); when(commandObjects.cmsMerge("mergedCountMinSketch", keysAndWeights)).thenReturn(stringCommandObject); Response response = pipeliningBase.cmsMerge("mergedCountMinSketch", keysAndWeights); assertThat(commands, contains(stringCommandObject)); assertThat(response, is(predefinedResponse)); } @Test public void testCmsQuery() { when(commandObjects.cmsQuery("myCountMinSketch", "item1", "item2")).thenReturn(listLongCommandObject); Response> response = pipeliningBase.cmsQuery("myCountMinSketch", "item1", "item2"); assertThat(commands, contains(listLongCommandObject)); assertThat(response, is(predefinedResponse)); } } ================================================ FILE: src/test/java/redis/clients/jedis/mocked/pipeline/PipeliningBaseCuckooFilterCommandsTest.java ================================================ package redis.clients.jedis.mocked.pipeline; import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.contains; import static org.hamcrest.Matchers.is; import static org.mockito.Mockito.when; import java.util.List; import java.util.Map; import org.junit.jupiter.api.Test; import redis.clients.jedis.Response; import redis.clients.jedis.bloom.CFInsertParams; import redis.clients.jedis.bloom.CFReserveParams; public class PipeliningBaseCuckooFilterCommandsTest extends PipeliningBaseMockedTestBase { @Test public void testCfAdd() { when(commandObjects.cfAdd("myCuckooFilter", "item1")).thenReturn(booleanCommandObject); Response response = pipeliningBase.cfAdd("myCuckooFilter", "item1"); assertThat(commands, contains(booleanCommandObject)); assertThat(response, is(predefinedResponse)); } @Test public void testCfAddNx() { when(commandObjects.cfAddNx("myCuckooFilter", "item1")).thenReturn(booleanCommandObject); Response response = pipeliningBase.cfAddNx("myCuckooFilter", "item1"); assertThat(commands, contains(booleanCommandObject)); assertThat(response, is(predefinedResponse)); } @Test public void testCfCount() { when(commandObjects.cfCount("myCuckooFilter", "item1")).thenReturn(longCommandObject); Response response = pipeliningBase.cfCount("myCuckooFilter", "item1"); assertThat(commands, contains(longCommandObject)); assertThat(response, is(predefinedResponse)); } @Test public void testCfDel() { when(commandObjects.cfDel("myCuckooFilter", "item1")).thenReturn(booleanCommandObject); Response response = pipeliningBase.cfDel("myCuckooFilter", "item1"); assertThat(commands, contains(booleanCommandObject)); assertThat(response, is(predefinedResponse)); } @Test public void testCfExists() { when(commandObjects.cfExists("myCuckooFilter", "item1")).thenReturn(booleanCommandObject); Response response = pipeliningBase.cfExists("myCuckooFilter", "item1"); assertThat(commands, contains(booleanCommandObject)); assertThat(response, is(predefinedResponse)); } @Test public void testCfInfo() { when(commandObjects.cfInfo("myCuckooFilter")).thenReturn(mapStringObjectCommandObject); Response> response = pipeliningBase.cfInfo("myCuckooFilter"); assertThat(commands, contains(mapStringObjectCommandObject)); assertThat(response, is(predefinedResponse)); } @Test public void testCfInsert() { when(commandObjects.cfInsert("myCuckooFilter", "item1", "item2")).thenReturn(listBooleanCommandObject); Response> response = pipeliningBase.cfInsert("myCuckooFilter", "item1", "item2"); assertThat(commands, contains(listBooleanCommandObject)); assertThat(response, is(predefinedResponse)); } @Test public void testCfInsertWithParams() { CFInsertParams insertParams = new CFInsertParams().capacity(10000L).noCreate(); when(commandObjects.cfInsert("myCuckooFilter", insertParams, "item1", "item2")).thenReturn(listBooleanCommandObject); Response> response = pipeliningBase.cfInsert("myCuckooFilter", insertParams, "item1", "item2"); assertThat(commands, contains(listBooleanCommandObject)); assertThat(response, is(predefinedResponse)); } @Test public void testCfInsertNx() { when(commandObjects.cfInsertNx("myCuckooFilter", "item1", "item2")).thenReturn(listBooleanCommandObject); Response> response = pipeliningBase.cfInsertNx("myCuckooFilter", "item1", "item2"); assertThat(commands, contains(listBooleanCommandObject)); assertThat(response, is(predefinedResponse)); } @Test public void testCfInsertNxWithParams() { CFInsertParams insertParams = new CFInsertParams().capacity(10000L).noCreate(); when(commandObjects.cfInsertNx("myCuckooFilter", insertParams, "item1", "item2")).thenReturn(listBooleanCommandObject); Response> response = pipeliningBase.cfInsertNx("myCuckooFilter", insertParams, "item1", "item2"); assertThat(commands, contains(listBooleanCommandObject)); assertThat(response, is(predefinedResponse)); } @Test public void testCfLoadChunk() { byte[] data = { 1, 2, 3, 4 }; when(commandObjects.cfLoadChunk("myCuckooFilter", 0L, data)).thenReturn(stringCommandObject); Response response = pipeliningBase.cfLoadChunk("myCuckooFilter", 0L, data); assertThat(commands, contains(stringCommandObject)); assertThat(response, is(predefinedResponse)); } @Test public void testCfMExists() { when(commandObjects.cfMExists("myCuckooFilter", "item1", "item2", "item3")).thenReturn(listBooleanCommandObject); Response> response = pipeliningBase.cfMExists("myCuckooFilter", "item1", "item2", "item3"); assertThat(commands, contains(listBooleanCommandObject)); assertThat(response, is(predefinedResponse)); } @Test public void testCfReserve() { when(commandObjects.cfReserve("myCuckooFilter", 10000L)).thenReturn(stringCommandObject); Response response = pipeliningBase.cfReserve("myCuckooFilter", 10000L); assertThat(commands, contains(stringCommandObject)); assertThat(response, is(predefinedResponse)); } @Test public void testCfReserveWithParams() { CFReserveParams reserveParams = new CFReserveParams().bucketSize(2).maxIterations(500).expansion(2); when(commandObjects.cfReserve("myCuckooFilter", 10000L, reserveParams)).thenReturn(stringCommandObject); Response response = pipeliningBase.cfReserve("myCuckooFilter", 10000L, reserveParams); assertThat(commands, contains(stringCommandObject)); assertThat(response, is(predefinedResponse)); } @Test public void testCfScanDump() { when(commandObjects.cfScanDump("myCuckooFilter", 0L)).thenReturn(entryLongBytesCommandObject); Response> response = pipeliningBase.cfScanDump("myCuckooFilter", 0L); assertThat(commands, contains(entryLongBytesCommandObject)); assertThat(response, is(predefinedResponse)); } } ================================================ FILE: src/test/java/redis/clients/jedis/mocked/pipeline/PipeliningBaseGenericCommandsTest.java ================================================ package redis.clients.jedis.mocked.pipeline; import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.contains; import static org.hamcrest.Matchers.is; import static org.mockito.Mockito.when; import java.util.List; import java.util.Set; import org.junit.jupiter.api.Test; import redis.clients.jedis.Response; import redis.clients.jedis.args.ExpiryOption; import redis.clients.jedis.params.MigrateParams; import redis.clients.jedis.params.RestoreParams; import redis.clients.jedis.params.ScanParams; import redis.clients.jedis.params.SortingParams; import redis.clients.jedis.resps.ScanResult; import redis.clients.jedis.util.CompareCondition; import redis.clients.jedis.util.KeyValue; public class PipeliningBaseGenericCommandsTest extends PipeliningBaseMockedTestBase { @Test public void testCopy() { when(commandObjects.copy("srcKey", "dstKey", true)).thenReturn(booleanCommandObject); Response response = pipeliningBase.copy("srcKey", "dstKey", true); assertThat(commands, contains(booleanCommandObject)); assertThat(response, is(predefinedResponse)); } @Test public void testCopyBinary() { byte[] srcKey = "sourceKey".getBytes(); byte[] dstKey = "destinationKey".getBytes(); boolean replace = true; when(commandObjects.copy(srcKey, dstKey, replace)).thenReturn(booleanCommandObject); Response response = pipeliningBase.copy(srcKey, dstKey, replace); assertThat(commands, contains(booleanCommandObject)); assertThat(response, is(predefinedResponse)); } @Test public void testDel() { when(commandObjects.del("key")).thenReturn(longCommandObject); Response response = pipeliningBase.del("key"); assertThat(commands, contains(longCommandObject)); assertThat(response, is(predefinedResponse)); } @Test public void testDelBinary() { byte[] key = "key".getBytes(); when(commandObjects.del(key)).thenReturn(longCommandObject); Response response = pipeliningBase.del(key); assertThat(commands, contains(longCommandObject)); assertThat(response, is(predefinedResponse)); } @Test public void testDelMultipleKeys() { String[] keys = { "key1", "key2", "key3" }; when(commandObjects.del(keys)).thenReturn(longCommandObject); Response response = pipeliningBase.del(keys); assertThat(commands, contains(longCommandObject)); assertThat(response, is(predefinedResponse)); } @Test public void testDelMultipleKeysBinary() { byte[] key1 = "key1".getBytes(); byte[] key2 = "key2".getBytes(); when(commandObjects.del(key1, key2)).thenReturn(longCommandObject); Response response = pipeliningBase.del(key1, key2); assertThat(commands, contains(longCommandObject)); assertThat(response, is(predefinedResponse)); } @Test public void testDelexBinary() { byte[] key = "key".getBytes(); CompareCondition condition = CompareCondition.valueEq("value"); when(commandObjects.delex(key, condition)).thenReturn(longCommandObject); Response response = pipeliningBase.delex(key, condition); assertThat(commands, contains(longCommandObject)); assertThat(response, is(predefinedResponse)); } @Test public void testDelex() { String key = "key"; CompareCondition condition = CompareCondition.valueEq("value"); when(commandObjects.delex(key, condition)).thenReturn(longCommandObject); Response response = pipeliningBase.delex(key, condition); assertThat(commands, contains(longCommandObject)); assertThat(response, is(predefinedResponse)); } @Test public void testDigestKeyBinary() { byte[] key = "key".getBytes(); when(commandObjects.digestKey(key)).thenReturn(bytesCommandObject); Response response = pipeliningBase.digestKey(key); assertThat(commands, contains(bytesCommandObject)); assertThat(response, is(predefinedResponse)); } @Test public void testDigestKey() { String key = "key"; when(commandObjects.digestKey(key)).thenReturn(stringCommandObject); Response response = pipeliningBase.digestKey(key); assertThat(commands, contains(stringCommandObject)); assertThat(response, is(predefinedResponse)); } @Test public void testDump() { when(commandObjects.dump("key")).thenReturn(bytesCommandObject); Response response = pipeliningBase.dump("key"); assertThat(commands, contains(bytesCommandObject)); assertThat(response, is(predefinedResponse)); } @Test public void testDumpBinary() { byte[] key = "key".getBytes(); when(commandObjects.dump(key)).thenReturn(bytesCommandObject); Response response = pipeliningBase.dump(key); assertThat(commands, contains(bytesCommandObject)); assertThat(response, is(predefinedResponse)); } @Test public void testExists() { when(commandObjects.exists("key")).thenReturn(booleanCommandObject); Response result = pipeliningBase.exists("key"); assertThat(commands, contains(booleanCommandObject)); assertThat(result, is(predefinedResponse)); } @Test public void testExistsBinary() { byte[] key = "key".getBytes(); when(commandObjects.exists(key)).thenReturn(booleanCommandObject); Response response = pipeliningBase.exists(key); assertThat(commands, contains(booleanCommandObject)); assertThat(response, is(predefinedResponse)); } @Test public void testExistsMultipleKeys() { when(commandObjects.exists("key1", "key2", "key3")).thenReturn(longCommandObject); Response response = pipeliningBase.exists("key1", "key2", "key3"); assertThat(commands, contains(longCommandObject)); assertThat(response, is(predefinedResponse)); } @Test public void testExistsMultipleKeysBinary() { byte[] key1 = "key1".getBytes(); byte[] key2 = "key2".getBytes(); when(commandObjects.exists(key1, key2)).thenReturn(longCommandObject); Response response = pipeliningBase.exists(key1, key2); assertThat(commands, contains(longCommandObject)); assertThat(response, is(predefinedResponse)); } @Test public void testExpire() { when(commandObjects.expire("key", 60)).thenReturn(longCommandObject); Response response = pipeliningBase.expire("key", 60); assertThat(commands, contains(longCommandObject)); assertThat(response, is(predefinedResponse)); } @Test public void testExpireBinary() { byte[] key = "key".getBytes(); long seconds = 60L; when(commandObjects.expire(key, seconds)).thenReturn(longCommandObject); Response response = pipeliningBase.expire(key, seconds); assertThat(commands, contains(longCommandObject)); assertThat(response, is(predefinedResponse)); } @Test public void testExpireWithExpiryOption() { when(commandObjects.expire("key", 60, ExpiryOption.NX)).thenReturn(longCommandObject); Response response = pipeliningBase.expire("key", 60, ExpiryOption.NX); assertThat(commands, contains(longCommandObject)); assertThat(response, is(predefinedResponse)); } @Test public void testExpireWithExpiryOptionBinary() { byte[] key = "key".getBytes(); long seconds = 60L; ExpiryOption expiryOption = ExpiryOption.NX; when(commandObjects.expire(key, seconds, expiryOption)).thenReturn(longCommandObject); Response response = pipeliningBase.expire(key, seconds, expiryOption); assertThat(commands, contains(longCommandObject)); assertThat(response, is(predefinedResponse)); } @Test public void testExpireAt() { int unixTime = 1609459200; when(commandObjects.expireAt("key", unixTime)).thenReturn(longCommandObject); Response response = pipeliningBase.expireAt("key", unixTime); assertThat(commands, contains(longCommandObject)); assertThat(response, is(predefinedResponse)); } @Test public void testExpireAtBinary() { byte[] key = "key".getBytes(); long unixTime = 1625097600L; when(commandObjects.expireAt(key, unixTime)).thenReturn(longCommandObject); Response response = pipeliningBase.expireAt(key, unixTime); assertThat(commands, contains(longCommandObject)); assertThat(response, is(predefinedResponse)); } @Test public void testExpireAtWithExpiryOption() { int unixTime = 1609459200; when(commandObjects.expireAt("key", unixTime, ExpiryOption.NX)).thenReturn(longCommandObject); Response response = pipeliningBase.expireAt("key", unixTime, ExpiryOption.NX); assertThat(commands, contains(longCommandObject)); assertThat(response, is(predefinedResponse)); } @Test public void testExpireAtWithExpiryOptionBinary() { byte[] key = "key".getBytes(); long unixTime = 1625097600L; ExpiryOption expiryOption = ExpiryOption.NX; when(commandObjects.expireAt(key, unixTime, expiryOption)).thenReturn(longCommandObject); Response response = pipeliningBase.expireAt(key, unixTime, expiryOption); assertThat(commands, contains(longCommandObject)); assertThat(response, is(predefinedResponse)); } @Test public void testExpireTime() { when(commandObjects.expireTime("key")).thenReturn(longCommandObject); Response response = pipeliningBase.expireTime("key"); assertThat(commands, contains(longCommandObject)); assertThat(response, is(predefinedResponse)); } @Test public void testExpireTimeBinary() { byte[] key = "key".getBytes(); when(commandObjects.expireTime(key)).thenReturn(longCommandObject); Response response = pipeliningBase.expireTime(key); assertThat(commands, contains(longCommandObject)); assertThat(response, is(predefinedResponse)); } @Test public void testKeys() { when(commandObjects.keys("pattern")).thenReturn(setStringCommandObject); Response> response = pipeliningBase.keys("pattern"); assertThat(commands, contains(setStringCommandObject)); assertThat(response, is(predefinedResponse)); } @Test public void testKeysBinary() { byte[] pattern = "*".getBytes(); when(commandObjects.keys(pattern)).thenReturn(setBytesCommandObject); Response> response = pipeliningBase.keys(pattern); assertThat(commands, contains(setBytesCommandObject)); assertThat(response, is(predefinedResponse)); } @Test public void testMigrate() { when(commandObjects.migrate("host", 6379, "key", 5000)).thenReturn(stringCommandObject); Response response = pipeliningBase.migrate("host", 6379, "key", 5000); assertThat(commands, contains(stringCommandObject)); assertThat(response, is(predefinedResponse)); } @Test public void testMigrateBinary() { String host = "localhost"; int port = 6379; byte[] key = "key".getBytes(); int timeout = 1000; when(commandObjects.migrate(host, port, key, timeout)).thenReturn(stringCommandObject); Response response = pipeliningBase.migrate(host, port, key, timeout); assertThat(commands, contains(stringCommandObject)); assertThat(response, is(predefinedResponse)); } @Test public void testMigrateMultipleKeys() { MigrateParams params = new MigrateParams(); String[] keys = { "key1", "key2" }; when(commandObjects.migrate("host", 6379, 5000, params, keys)).thenReturn(stringCommandObject); Response response = pipeliningBase.migrate("host", 6379, 5000, params, keys); assertThat(commands, contains(stringCommandObject)); assertThat(response, is(predefinedResponse)); } @Test public void testMigrateMultipleKeysBinary() { String host = "localhost"; int port = 6379; int timeout = 1000; MigrateParams params = MigrateParams.migrateParams().copy().replace(); byte[] key1 = "key1".getBytes(); byte[] key2 = "key2".getBytes(); when(commandObjects.migrate(host, port, timeout, params, key1, key2)).thenReturn(stringCommandObject); Response response = pipeliningBase.migrate(host, port, timeout, params, key1, key2); assertThat(commands, contains(stringCommandObject)); assertThat(response, is(predefinedResponse)); } @Test public void testObjectEncoding() { when(commandObjects.objectEncoding("key")).thenReturn(stringCommandObject); Response response = pipeliningBase.objectEncoding("key"); assertThat(commands, contains(stringCommandObject)); assertThat(response, is(predefinedResponse)); } @Test public void testObjectEncodingBinary() { byte[] key = "key".getBytes(); when(commandObjects.objectEncoding(key)).thenReturn(bytesCommandObject); Response response = pipeliningBase.objectEncoding(key); assertThat(commands, contains(bytesCommandObject)); assertThat(response, is(predefinedResponse)); } @Test public void testObjectFreq() { when(commandObjects.objectFreq("key")).thenReturn(longCommandObject); Response response = pipeliningBase.objectFreq("key"); assertThat(commands, contains(longCommandObject)); assertThat(response, is(predefinedResponse)); } @Test public void testObjectFreqBinary() { byte[] key = "key".getBytes(); when(commandObjects.objectFreq(key)).thenReturn(longCommandObject); Response response = pipeliningBase.objectFreq(key); assertThat(commands, contains(longCommandObject)); assertThat(response, is(predefinedResponse)); } @Test public void testObjectIdletime() { when(commandObjects.objectIdletime("key")).thenReturn(longCommandObject); Response response = pipeliningBase.objectIdletime("key"); assertThat(commands, contains(longCommandObject)); assertThat(response, is(predefinedResponse)); } @Test public void testObjectIdletimeBinary() { byte[] key = "key".getBytes(); when(commandObjects.objectIdletime(key)).thenReturn(longCommandObject); Response response = pipeliningBase.objectIdletime(key); assertThat(commands, contains(longCommandObject)); assertThat(response, is(predefinedResponse)); } @Test public void testObjectRefcount() { when(commandObjects.objectRefcount("key")).thenReturn(longCommandObject); Response response = pipeliningBase.objectRefcount("key"); assertThat(commands, contains(longCommandObject)); assertThat(response, is(predefinedResponse)); } @Test public void testObjectRefcountBinary() { byte[] key = "key".getBytes(); when(commandObjects.objectRefcount(key)).thenReturn(longCommandObject); Response response = pipeliningBase.objectRefcount(key); assertThat(commands, contains(longCommandObject)); assertThat(response, is(predefinedResponse)); } @Test public void testPersist() { when(commandObjects.persist("key")).thenReturn(longCommandObject); Response response = pipeliningBase.persist("key"); assertThat(commands, contains(longCommandObject)); assertThat(response, is(predefinedResponse)); } @Test public void testPersistBinary() { byte[] key = "key".getBytes(); when(commandObjects.persist(key)).thenReturn(longCommandObject); Response response = pipeliningBase.persist(key); assertThat(commands, contains(longCommandObject)); assertThat(response, is(predefinedResponse)); } @Test public void testPexpire() { when(commandObjects.pexpire("key", 100000)).thenReturn(longCommandObject); Response response = pipeliningBase.pexpire("key", 100000); assertThat(commands, contains(longCommandObject)); assertThat(response, is(predefinedResponse)); } @Test public void testPexpireBinary() { byte[] key = "key".getBytes(); long milliseconds = 60000L; when(commandObjects.pexpire(key, milliseconds)).thenReturn(longCommandObject); Response response = pipeliningBase.pexpire(key, milliseconds); assertThat(commands, contains(longCommandObject)); assertThat(response, is(predefinedResponse)); } @Test public void testPexpireWithExpiryOption() { when(commandObjects.pexpire("key", 100000, ExpiryOption.NX)).thenReturn(longCommandObject); Response response = pipeliningBase.pexpire("key", 100000, ExpiryOption.NX); assertThat(commands, contains(longCommandObject)); assertThat(response, is(predefinedResponse)); } @Test public void testPexpireWithExpiryOptionBinary() { byte[] key = "key".getBytes(); long milliseconds = 60000L; ExpiryOption expiryOption = ExpiryOption.NX; when(commandObjects.pexpire(key, milliseconds, expiryOption)).thenReturn(longCommandObject); Response response = pipeliningBase.pexpire(key, milliseconds, expiryOption); assertThat(commands, contains(longCommandObject)); assertThat(response, is(predefinedResponse)); } @Test public void testPexpireAt() { long millisecondsTimestamp = 1609459200000L; when(commandObjects.pexpireAt("key", millisecondsTimestamp)).thenReturn(longCommandObject); Response response = pipeliningBase.pexpireAt("key", millisecondsTimestamp); assertThat(commands, contains(longCommandObject)); assertThat(response, is(predefinedResponse)); } @Test public void testPexpireAtBinary() { byte[] key = "key".getBytes(); long millisecondsTimestamp = 1625097600000L; when(commandObjects.pexpireAt(key, millisecondsTimestamp)).thenReturn(longCommandObject); Response response = pipeliningBase.pexpireAt(key, millisecondsTimestamp); assertThat(commands, contains(longCommandObject)); assertThat(response, is(predefinedResponse)); } @Test public void testPexpireAtWithExpiryOption() { long millisecondsTimestamp = 1609459200000L; when(commandObjects.pexpireAt("key", millisecondsTimestamp, ExpiryOption.NX)).thenReturn(longCommandObject); Response response = pipeliningBase.pexpireAt("key", millisecondsTimestamp, ExpiryOption.NX); assertThat(commands, contains(longCommandObject)); assertThat(response, is(predefinedResponse)); } @Test public void testPexpireAtWithExpiryOptionBinary() { byte[] key = "key".getBytes(); long millisecondsTimestamp = 1625097600000L; ExpiryOption expiryOption = ExpiryOption.NX; when(commandObjects.pexpireAt(key, millisecondsTimestamp, expiryOption)).thenReturn(longCommandObject); Response response = pipeliningBase.pexpireAt(key, millisecondsTimestamp, expiryOption); assertThat(commands, contains(longCommandObject)); assertThat(response, is(predefinedResponse)); } @Test public void testPexpireTime() { when(commandObjects.pexpireTime("key")).thenReturn(longCommandObject); Response response = pipeliningBase.pexpireTime("key"); assertThat(commands, contains(longCommandObject)); assertThat(response, is(predefinedResponse)); } @Test public void testPexpireTimeBinary() { byte[] key = "key".getBytes(); when(commandObjects.pexpireTime(key)).thenReturn(longCommandObject); Response response = pipeliningBase.pexpireTime(key); assertThat(commands, contains(longCommandObject)); assertThat(response, is(predefinedResponse)); } @Test public void testPttl() { when(commandObjects.pttl("key")).thenReturn(longCommandObject); Response response = pipeliningBase.pttl("key"); assertThat(commands, contains(longCommandObject)); assertThat(response, is(predefinedResponse)); } @Test public void testPttlBinary() { byte[] key = "key".getBytes(); when(commandObjects.pttl(key)).thenReturn(longCommandObject); Response response = pipeliningBase.pttl(key); assertThat(commands, contains(longCommandObject)); assertThat(response, is(predefinedResponse)); } @Test public void testRandomKey() { when(commandObjects.randomKey()).thenReturn(stringCommandObject); Response response = pipeliningBase.randomKey(); assertThat(commands, contains(stringCommandObject)); assertThat(response, is(predefinedResponse)); } @Test public void testRandomBinaryKey() { when(commandObjects.randomBinaryKey()).thenReturn(bytesCommandObject); Response response = pipeliningBase.randomBinaryKey(); assertThat(commands, contains(bytesCommandObject)); assertThat(response, is(predefinedResponse)); } @Test public void testRename() { when(commandObjects.rename("oldkey", "newkey")).thenReturn(stringCommandObject); Response response = pipeliningBase.rename("oldkey", "newkey"); assertThat(commands, contains(stringCommandObject)); assertThat(response, is(predefinedResponse)); } @Test public void testRenameBinary() { byte[] oldkey = "oldKey".getBytes(); byte[] newkey = "newKey".getBytes(); when(commandObjects.rename(oldkey, newkey)).thenReturn(stringCommandObject); Response response = pipeliningBase.rename(oldkey, newkey); assertThat(commands, contains(stringCommandObject)); assertThat(response, is(predefinedResponse)); } @Test public void testRenamenx() { when(commandObjects.renamenx("oldkey", "newkey")).thenReturn(longCommandObject); Response response = pipeliningBase.renamenx("oldkey", "newkey"); assertThat(commands, contains(longCommandObject)); assertThat(response, is(predefinedResponse)); } @Test public void testRenamenxBinary() { byte[] oldkey = "oldKey".getBytes(); byte[] newkey = "newKey".getBytes(); when(commandObjects.renamenx(oldkey, newkey)).thenReturn(longCommandObject); Response response = pipeliningBase.renamenx(oldkey, newkey); assertThat(commands, contains(longCommandObject)); assertThat(response, is(predefinedResponse)); } @Test public void testRestore() { byte[] serializedValue = new byte[]{ 1, 2, 3 }; long ttl = 1000L; when(commandObjects.restore("key", ttl, serializedValue)).thenReturn(stringCommandObject); Response response = pipeliningBase.restore("key", ttl, serializedValue); assertThat(commands, contains(stringCommandObject)); assertThat(response, is(predefinedResponse)); } @Test public void testRestoreBinary() { byte[] key = "key".getBytes(); long ttl = 0L; byte[] serializedValue = "serialized".getBytes(); when(commandObjects.restore(key, ttl, serializedValue)).thenReturn(stringCommandObject); Response response = pipeliningBase.restore(key, ttl, serializedValue); assertThat(commands, contains(stringCommandObject)); assertThat(response, is(predefinedResponse)); } @Test public void testRestoreWithParams() { byte[] serializedValue = new byte[]{ 1, 2, 3 }; long ttl = 1000L; RestoreParams params = new RestoreParams(); when(commandObjects.restore("key", ttl, serializedValue, params)).thenReturn(stringCommandObject); Response response = pipeliningBase.restore("key", ttl, serializedValue, params); assertThat(commands, contains(stringCommandObject)); assertThat(response, is(predefinedResponse)); } @Test public void testRestoreWithParamsBinary() { byte[] key = "key".getBytes(); long ttl = 0L; byte[] serializedValue = "serialized".getBytes(); RestoreParams params = RestoreParams.restoreParams().replace(); when(commandObjects.restore(key, ttl, serializedValue, params)).thenReturn(stringCommandObject); Response response = pipeliningBase.restore(key, ttl, serializedValue, params); assertThat(commands, contains(stringCommandObject)); assertThat(response, is(predefinedResponse)); } @Test public void testScan() { when(commandObjects.scan("0")).thenReturn(scanResultStringCommandObject); Response> response = pipeliningBase.scan("0"); assertThat(commands, contains(scanResultStringCommandObject)); assertThat(response, is(predefinedResponse)); } @Test public void testScanBinary() { byte[] cursor = "0".getBytes(); when(commandObjects.scan(cursor)).thenReturn(scanResultBytesCommandObject); Response> response = pipeliningBase.scan(cursor); assertThat(commands, contains(scanResultBytesCommandObject)); assertThat(response, is(predefinedResponse)); } @Test public void testScanWithParams() { ScanParams scanParams = new ScanParams(); when(commandObjects.scan("0", scanParams)).thenReturn(scanResultStringCommandObject); Response> response = pipeliningBase.scan("0", scanParams); assertThat(commands, contains(scanResultStringCommandObject)); assertThat(response, is(predefinedResponse)); } @Test public void testScanWithParamsBinary() { byte[] cursor = "0".getBytes(); ScanParams params = new ScanParams().match("*").count(10); when(commandObjects.scan(cursor, params)).thenReturn(scanResultBytesCommandObject); Response> response = pipeliningBase.scan(cursor, params); assertThat(commands, contains(scanResultBytesCommandObject)); assertThat(response, is(predefinedResponse)); } @Test public void testScanWithType() { ScanParams scanParams = new ScanParams(); when(commandObjects.scan("0", scanParams, "type")).thenReturn(scanResultStringCommandObject); Response> response = pipeliningBase.scan("0", scanParams, "type"); assertThat(commands, contains(scanResultStringCommandObject)); assertThat(response, is(predefinedResponse)); } @Test public void testScanWithTypeBinary() { byte[] cursor = "0".getBytes(); ScanParams params = new ScanParams().match("*").count(10); byte[] type = "string".getBytes(); when(commandObjects.scan(cursor, params, type)).thenReturn(scanResultBytesCommandObject); Response> response = pipeliningBase.scan(cursor, params, type); assertThat(commands, contains(scanResultBytesCommandObject)); assertThat(response, is(predefinedResponse)); } @Test public void testSort() { when(commandObjects.sort("key")).thenReturn(listStringCommandObject); Response> response = pipeliningBase.sort("key"); assertThat(commands, contains(listStringCommandObject)); assertThat(response, is(predefinedResponse)); } @Test public void testSortBinary() { byte[] key = "key".getBytes(); when(commandObjects.sort(key)).thenReturn(listBytesCommandObject); Response> response = pipeliningBase.sort(key); assertThat(commands, contains(listBytesCommandObject)); assertThat(response, is(predefinedResponse)); } @Test public void testSortWithParams() { SortingParams sortingParams = new SortingParams(); when(commandObjects.sort("key", sortingParams)).thenReturn(listStringCommandObject); Response> response = pipeliningBase.sort("key", sortingParams); assertThat(commands, contains(listStringCommandObject)); assertThat(response, is(predefinedResponse)); } @Test public void testSortWithParamsBinary() { byte[] key = "key".getBytes(); SortingParams sortingParams = new SortingParams().alpha().limit(0, 10); when(commandObjects.sort(key, sortingParams)).thenReturn(listBytesCommandObject); Response> response = pipeliningBase.sort(key, sortingParams); assertThat(commands, contains(listBytesCommandObject)); assertThat(response, is(predefinedResponse)); } @Test public void testSortStore() { when(commandObjects.sort("key", "dstKey")).thenReturn(longCommandObject); Response response = pipeliningBase.sort("key", "dstKey"); assertThat(commands, contains(longCommandObject)); assertThat(response, is(predefinedResponse)); } @Test public void testSortStoreBinary() { byte[] key = "key".getBytes(); byte[] dstkey = "dstkey".getBytes(); when(commandObjects.sort(key, dstkey)).thenReturn(longCommandObject); Response response = pipeliningBase.sort(key, dstkey); assertThat(commands, contains(longCommandObject)); assertThat(response, is(predefinedResponse)); } @Test public void testSortStoreWithParams() { SortingParams sortingParams = new SortingParams(); when(commandObjects.sort("key", sortingParams, "dstKey")).thenReturn(longCommandObject); Response response = pipeliningBase.sort("key", sortingParams, "dstKey"); assertThat(commands, contains(longCommandObject)); assertThat(response, is(predefinedResponse)); } @Test public void testSortStoreWithParamsBinary() { byte[] key = "key".getBytes(); byte[] dstkey = "dstkey".getBytes(); SortingParams sortingParams = new SortingParams().alpha().limit(0, 10); when(commandObjects.sort(key, sortingParams, dstkey)).thenReturn(longCommandObject); Response response = pipeliningBase.sort(key, sortingParams, dstkey); assertThat(commands, contains(longCommandObject)); assertThat(response, is(predefinedResponse)); } @Test public void testSortReadonly() { SortingParams sortingParams = new SortingParams(); when(commandObjects.sortReadonly("key", sortingParams)).thenReturn(listStringCommandObject); Response> response = pipeliningBase.sortReadonly("key", sortingParams); assertThat(commands, contains(listStringCommandObject)); assertThat(response, is(predefinedResponse)); } @Test public void testSortReadonlyBinary() { byte[] key = "key".getBytes(); SortingParams sortingParams = new SortingParams().alpha().limit(0, 10); when(commandObjects.sortReadonly(key, sortingParams)).thenReturn(listBytesCommandObject); Response> response = pipeliningBase.sortReadonly(key, sortingParams); assertThat(commands, contains(listBytesCommandObject)); assertThat(response, is(predefinedResponse)); } @Test public void testTouch() { when(commandObjects.touch("key")).thenReturn(longCommandObject); Response response = pipeliningBase.touch("key"); assertThat(commands, contains(longCommandObject)); assertThat(response, is(predefinedResponse)); } @Test public void testTouchBinary() { byte[] key = "key".getBytes(); when(commandObjects.touch(key)).thenReturn(longCommandObject); Response response = pipeliningBase.touch(key); assertThat(commands, contains(longCommandObject)); assertThat(response, is(predefinedResponse)); } @Test public void testTouchMultipleKeys() { String[] keys = { "key1", "key2", "key3" }; when(commandObjects.touch(keys)).thenReturn(longCommandObject); Response response = pipeliningBase.touch(keys); assertThat(commands, contains(longCommandObject)); assertThat(response, is(predefinedResponse)); } @Test public void testTouchMultipleKeysBinary() { byte[] key1 = "key1".getBytes(); byte[] key2 = "key2".getBytes(); when(commandObjects.touch(key1, key2)).thenReturn(longCommandObject); Response response = pipeliningBase.touch(key1, key2); assertThat(commands, contains(longCommandObject)); assertThat(response, is(predefinedResponse)); } @Test public void testTtl() { when(commandObjects.ttl("key")).thenReturn(longCommandObject); Response response = pipeliningBase.ttl("key"); assertThat(commands, contains(longCommandObject)); assertThat(response, is(predefinedResponse)); } @Test public void testTtlBinary() { byte[] key = "key".getBytes(); when(commandObjects.ttl(key)).thenReturn(longCommandObject); Response response = pipeliningBase.ttl(key); assertThat(commands, contains(longCommandObject)); assertThat(response, is(predefinedResponse)); } @Test public void testType() { when(commandObjects.type("key")).thenReturn(stringCommandObject); Response response = pipeliningBase.type("key"); assertThat(commands, contains(stringCommandObject)); assertThat(response, is(predefinedResponse)); } @Test public void testTypeBinary() { byte[] key = "key".getBytes(); when(commandObjects.type(key)).thenReturn(stringCommandObject); Response response = pipeliningBase.type(key); assertThat(commands, contains(stringCommandObject)); assertThat(response, is(predefinedResponse)); } @Test public void testUnlink() { when(commandObjects.unlink("key")).thenReturn(longCommandObject); Response response = pipeliningBase.unlink("key"); assertThat(commands, contains(longCommandObject)); assertThat(response, is(predefinedResponse)); } @Test public void testUnlinkBinary() { byte[] key = "key".getBytes(); when(commandObjects.unlink(key)).thenReturn(longCommandObject); Response response = pipeliningBase.unlink(key); assertThat(commands, contains(longCommandObject)); assertThat(response, is(predefinedResponse)); } @Test public void testUnlinkMultipleKeys() { String[] keys = { "key1", "key2", "key3" }; when(commandObjects.unlink(keys)).thenReturn(longCommandObject); Response response = pipeliningBase.unlink(keys); assertThat(commands, contains(longCommandObject)); assertThat(response, is(predefinedResponse)); } @Test public void testUnlinkMultipleKeysBinary() { byte[] key1 = "key1".getBytes(); byte[] key2 = "key2".getBytes(); when(commandObjects.unlink(key1, key2)).thenReturn(longCommandObject); Response response = pipeliningBase.unlink(key1, key2); assertThat(commands, contains(longCommandObject)); assertThat(response, is(predefinedResponse)); } @Test public void testWaitReplicas() { int replicas = 2; long timeout = 1000L; when(commandObjects.waitReplicas("key", replicas, timeout)).thenReturn(longCommandObject); Response response = pipeliningBase.waitReplicas("key", replicas, timeout); assertThat(commands, contains(longCommandObject)); assertThat(response, is(predefinedResponse)); } @Test public void testWaitReplicasBinary() { byte[] sampleKey = "sampleKey".getBytes(); int replicas = 1; long timeout = 1000; when(commandObjects.waitReplicas(sampleKey, replicas, timeout)).thenReturn(longCommandObject); Response response = pipeliningBase.waitReplicas(sampleKey, replicas, timeout); assertThat(commands, contains(longCommandObject)); assertThat(response, is(predefinedResponse)); } @Test public void testWaitAOF() { long numLocal = 1L; long numReplicas = 1L; long timeout = 1000L; when(commandObjects.waitAOF("key", numLocal, numReplicas, timeout)).thenReturn(keyValueLongLongCommandObject); Response> response = pipeliningBase.waitAOF("key", numLocal, numReplicas, timeout); assertThat(commands, contains(keyValueLongLongCommandObject)); assertThat(response, is(predefinedResponse)); } @Test public void testWaitAOFBinary() { byte[] sampleKey = "sampleKey".getBytes(); long numLocal = 1; long numReplicas = 1; long timeout = 1000; when(commandObjects.waitAOF(sampleKey, numLocal, numReplicas, timeout)).thenReturn(keyValueLongLongCommandObject); Response> response = pipeliningBase.waitAOF(sampleKey, numLocal, numReplicas, timeout); assertThat(commands, contains(keyValueLongLongCommandObject)); assertThat(response, is(predefinedResponse)); } } ================================================ FILE: src/test/java/redis/clients/jedis/mocked/pipeline/PipeliningBaseGeospatialCommandsTest.java ================================================ package redis.clients.jedis.mocked.pipeline; import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.contains; import static org.hamcrest.Matchers.is; import static org.mockito.Mockito.when; import java.util.HashMap; import java.util.List; import java.util.Map; import org.junit.jupiter.api.Test; import redis.clients.jedis.GeoCoordinate; import redis.clients.jedis.Response; import redis.clients.jedis.args.GeoUnit; import redis.clients.jedis.params.GeoAddParams; import redis.clients.jedis.params.GeoRadiusParam; import redis.clients.jedis.params.GeoRadiusStoreParam; import redis.clients.jedis.params.GeoSearchParam; import redis.clients.jedis.resps.GeoRadiusResponse; public class PipeliningBaseGeospatialCommandsTest extends PipeliningBaseMockedTestBase { @Test public void testGeoadd() { when(commandObjects.geoadd("key", 13.361389, 38.115556, "member")).thenReturn(longCommandObject); Response response = pipeliningBase.geoadd("key", 13.361389, 38.115556, "member"); assertThat(commands, contains(longCommandObject)); assertThat(response, is(predefinedResponse)); } @Test public void testGeoaddBinary() { byte[] key = "location".getBytes(); double longitude = 13.361389; double latitude = 38.115556; byte[] member = "Sicily".getBytes(); when(commandObjects.geoadd(key, longitude, latitude, member)).thenReturn(longCommandObject); Response response = pipeliningBase.geoadd(key, longitude, latitude, member); assertThat(commands, contains(longCommandObject)); assertThat(response, is(predefinedResponse)); } @Test public void testGeoaddMap() { Map memberCoordinateMap = new HashMap<>(); memberCoordinateMap.put("member", new GeoCoordinate(13.361389, 38.115556)); when(commandObjects.geoadd("key", memberCoordinateMap)).thenReturn(longCommandObject); Response response = pipeliningBase.geoadd("key", memberCoordinateMap); assertThat(commands, contains(longCommandObject)); assertThat(response, is(predefinedResponse)); } @Test public void testGeoaddMapBinary() { byte[] key = "location".getBytes(); Map memberCoordinateMap = new HashMap<>(); memberCoordinateMap.put("Palermo".getBytes(), new GeoCoordinate(13.361389, 38.115556)); when(commandObjects.geoadd(key, memberCoordinateMap)).thenReturn(longCommandObject); Response response = pipeliningBase.geoadd(key, memberCoordinateMap); assertThat(commands, contains(longCommandObject)); assertThat(response, is(predefinedResponse)); } @Test public void testGeoaddMapWithParams() { GeoAddParams params = new GeoAddParams(); Map memberCoordinateMap = new HashMap<>(); memberCoordinateMap.put("member", new GeoCoordinate(13.361389, 38.115556)); when(commandObjects.geoadd("key", params, memberCoordinateMap)).thenReturn(longCommandObject); Response response = pipeliningBase.geoadd("key", params, memberCoordinateMap); assertThat(commands, contains(longCommandObject)); assertThat(response, is(predefinedResponse)); } @Test public void testGeoaddMapWithParamsBinary() { byte[] key = "location".getBytes(); GeoAddParams params = GeoAddParams.geoAddParams(); Map memberCoordinateMap = new HashMap<>(); memberCoordinateMap.put("Palermo".getBytes(), new GeoCoordinate(13.361389, 38.115556)); when(commandObjects.geoadd(key, params, memberCoordinateMap)).thenReturn(longCommandObject); Response response = pipeliningBase.geoadd(key, params, memberCoordinateMap); assertThat(commands, contains(longCommandObject)); assertThat(response, is(predefinedResponse)); } @Test public void testGeodist() { when(commandObjects.geodist("key", "member1", "member2")).thenReturn(doubleCommandObject); Response response = pipeliningBase.geodist("key", "member1", "member2"); assertThat(commands, contains(doubleCommandObject)); assertThat(response, is(predefinedResponse)); } @Test public void testGeodistBinary() { byte[] key = "location".getBytes(); byte[] member1 = "Palermo".getBytes(); byte[] member2 = "Catania".getBytes(); when(commandObjects.geodist(key, member1, member2)).thenReturn(doubleCommandObject); Response response = pipeliningBase.geodist(key, member1, member2); assertThat(commands, contains(doubleCommandObject)); assertThat(response, is(predefinedResponse)); } @Test public void testGeodistWithUnit() { GeoUnit unit = GeoUnit.KM; when(commandObjects.geodist("key", "member1", "member2", unit)).thenReturn(doubleCommandObject); Response response = pipeliningBase.geodist("key", "member1", "member2", unit); assertThat(commands, contains(doubleCommandObject)); assertThat(response, is(predefinedResponse)); } @Test public void testGeodistWithUnitBinary() { byte[] key = "location".getBytes(); byte[] member1 = "Palermo".getBytes(); byte[] member2 = "Catania".getBytes(); GeoUnit unit = GeoUnit.KM; when(commandObjects.geodist(key, member1, member2, unit)).thenReturn(doubleCommandObject); Response response = pipeliningBase.geodist(key, member1, member2, unit); assertThat(commands, contains(doubleCommandObject)); assertThat(response, is(predefinedResponse)); } @Test public void testGeohash() { when(commandObjects.geohash("key", "member1", "member2")).thenReturn(listStringCommandObject); Response> response = pipeliningBase.geohash("key", "member1", "member2"); assertThat(commands, contains(listStringCommandObject)); assertThat(response, is(predefinedResponse)); } @Test public void testGeohashBinary() { byte[] key = "location".getBytes(); byte[] member = "Palermo".getBytes(); when(commandObjects.geohash(key, member)).thenReturn(listBytesCommandObject); Response> response = pipeliningBase.geohash(key, member); assertThat(commands, contains(listBytesCommandObject)); assertThat(response, is(predefinedResponse)); } @Test public void testGeopos() { when(commandObjects.geopos("key", "member1", "member2")).thenReturn(listGeoCoordinateCommandObject); Response> response = pipeliningBase.geopos("key", "member1", "member2"); assertThat(commands, contains(listGeoCoordinateCommandObject)); assertThat(response, is(predefinedResponse)); } @Test public void testGeoposBinary() { byte[] key = "location".getBytes(); byte[] member = "Palermo".getBytes(); when(commandObjects.geopos(key, member)).thenReturn(listGeoCoordinateCommandObject); Response> response = pipeliningBase.geopos(key, member); assertThat(commands, contains(listGeoCoordinateCommandObject)); assertThat(response, is(predefinedResponse)); } @Test public void testGeoradius() { when(commandObjects.georadius("key", 15.0, 37.0, 100.0, GeoUnit.KM)) .thenReturn(listGeoRadiusResponseCommandObject); Response> response = pipeliningBase.georadius("key", 15.0, 37.0, 100.0, GeoUnit.KM); assertThat(commands, contains(listGeoRadiusResponseCommandObject)); assertThat(response, is(predefinedResponse)); } @Test public void testGeoradiusBinary() { byte[] key = "location".getBytes(); double longitude = 13.361389; double latitude = 38.115556; double radius = 100; GeoUnit unit = GeoUnit.KM; when(commandObjects.georadius(key, longitude, latitude, radius, unit)).thenReturn(listGeoRadiusResponseCommandObject); Response> response = pipeliningBase.georadius(key, longitude, latitude, radius, unit); assertThat(commands, contains(listGeoRadiusResponseCommandObject)); assertThat(response, is(predefinedResponse)); } @Test public void testGeoradiusReadonly() { when(commandObjects.georadiusReadonly("key", 15.0, 37.0, 100.0, GeoUnit.KM)) .thenReturn(listGeoRadiusResponseCommandObject); Response> response = pipeliningBase.georadiusReadonly("key", 15.0, 37.0, 100.0, GeoUnit.KM); assertThat(commands, contains(listGeoRadiusResponseCommandObject)); assertThat(response, is(predefinedResponse)); } @Test public void testGeoradiusReadonlyBinary() { byte[] key = "location".getBytes(); double longitude = 13.361389; double latitude = 38.115556; double radius = 100; GeoUnit unit = GeoUnit.KM; when(commandObjects.georadiusReadonly(key, longitude, latitude, radius, unit)).thenReturn(listGeoRadiusResponseCommandObject); Response> response = pipeliningBase.georadiusReadonly(key, longitude, latitude, radius, unit); assertThat(commands, contains(listGeoRadiusResponseCommandObject)); assertThat(response, is(predefinedResponse)); } @Test public void testGeoradiusWithParam() { GeoRadiusParam param = GeoRadiusParam.geoRadiusParam(); when(commandObjects.georadius("key", 15.0, 37.0, 100.0, GeoUnit.KM, param)) .thenReturn(listGeoRadiusResponseCommandObject); Response> response = pipeliningBase.georadius("key", 15.0, 37.0, 100.0, GeoUnit.KM, param); assertThat(commands, contains(listGeoRadiusResponseCommandObject)); assertThat(response, is(predefinedResponse)); } @Test public void testGeoradiusWithParamBinary() { byte[] key = "location".getBytes(); double longitude = 13.361389; double latitude = 38.115556; double radius = 100; GeoUnit unit = GeoUnit.KM; GeoRadiusParam param = GeoRadiusParam.geoRadiusParam(); when(commandObjects.georadius(key, longitude, latitude, radius, unit, param)).thenReturn(listGeoRadiusResponseCommandObject); Response> response = pipeliningBase.georadius(key, longitude, latitude, radius, unit, param); assertThat(commands, contains(listGeoRadiusResponseCommandObject)); assertThat(response, is(predefinedResponse)); } @Test public void testGeoradiusReadonlyWithParam() { GeoRadiusParam param = GeoRadiusParam.geoRadiusParam(); when(commandObjects.georadiusReadonly("key", 15.0, 37.0, 100.0, GeoUnit.KM, param)) .thenReturn(listGeoRadiusResponseCommandObject); Response> response = pipeliningBase.georadiusReadonly("key", 15.0, 37.0, 100.0, GeoUnit.KM, param); assertThat(commands, contains(listGeoRadiusResponseCommandObject)); assertThat(response, is(predefinedResponse)); } @Test public void testGeoradiusReadonlyWithParamBinary() { byte[] key = "location".getBytes(); double longitude = 13.361389; double latitude = 38.115556; double radius = 100; GeoUnit unit = GeoUnit.KM; GeoRadiusParam param = GeoRadiusParam.geoRadiusParam(); when(commandObjects.georadiusReadonly(key, longitude, latitude, radius, unit, param)).thenReturn(listGeoRadiusResponseCommandObject); Response> response = pipeliningBase.georadiusReadonly(key, longitude, latitude, radius, unit, param); assertThat(commands, contains(listGeoRadiusResponseCommandObject)); assertThat(response, is(predefinedResponse)); } @Test public void testGeoradiusByMember() { when(commandObjects.georadiusByMember("key", "member", 100.0, GeoUnit.KM)) .thenReturn(listGeoRadiusResponseCommandObject); Response> response = pipeliningBase.georadiusByMember("key", "member", 100.0, GeoUnit.KM); assertThat(commands, contains(listGeoRadiusResponseCommandObject)); assertThat(response, is(predefinedResponse)); } @Test public void testGeoradiusByMemberBinary() { byte[] key = "location".getBytes(); byte[] member = "Palermo".getBytes(); double radius = 100; GeoUnit unit = GeoUnit.KM; when(commandObjects.georadiusByMember(key, member, radius, unit)).thenReturn(listGeoRadiusResponseCommandObject); Response> response = pipeliningBase.georadiusByMember(key, member, radius, unit); assertThat(commands, contains(listGeoRadiusResponseCommandObject)); assertThat(response, is(predefinedResponse)); } @Test public void testGeoradiusByMemberReadonly() { when(commandObjects.georadiusByMemberReadonly("key", "member", 100.0, GeoUnit.KM)) .thenReturn(listGeoRadiusResponseCommandObject); Response> response = pipeliningBase.georadiusByMemberReadonly("key", "member", 100.0, GeoUnit.KM); assertThat(commands, contains(listGeoRadiusResponseCommandObject)); assertThat(response, is(predefinedResponse)); } @Test public void testGeoradiusByMemberReadonlyBinary() { byte[] key = "location".getBytes(); byte[] member = "Palermo".getBytes(); double radius = 100; GeoUnit unit = GeoUnit.KM; when(commandObjects.georadiusByMemberReadonly(key, member, radius, unit)).thenReturn(listGeoRadiusResponseCommandObject); Response> response = pipeliningBase.georadiusByMemberReadonly(key, member, radius, unit); assertThat(commands, contains(listGeoRadiusResponseCommandObject)); assertThat(response, is(predefinedResponse)); } @Test public void testGeoradiusByMemberWithParam() { GeoRadiusParam param = GeoRadiusParam.geoRadiusParam(); when(commandObjects.georadiusByMember("key", "member", 100.0, GeoUnit.KM, param)) .thenReturn(listGeoRadiusResponseCommandObject); Response> response = pipeliningBase .georadiusByMember("key", "member", 100.0, GeoUnit.KM, param); assertThat(commands, contains(listGeoRadiusResponseCommandObject)); assertThat(response, is(predefinedResponse)); } @Test public void testGeoradiusByMemberWithParamBinary() { byte[] key = "location".getBytes(); byte[] member = "Palermo".getBytes(); double radius = 100; GeoUnit unit = GeoUnit.KM; GeoRadiusParam param = GeoRadiusParam.geoRadiusParam(); when(commandObjects.georadiusByMember(key, member, radius, unit, param)).thenReturn(listGeoRadiusResponseCommandObject); Response> response = pipeliningBase.georadiusByMember(key, member, radius, unit, param); assertThat(commands, contains(listGeoRadiusResponseCommandObject)); assertThat(response, is(predefinedResponse)); } @Test public void testGeoradiusByMemberReadonlyWithParam() { GeoRadiusParam param = GeoRadiusParam.geoRadiusParam(); when(commandObjects.georadiusByMemberReadonly("key", "member", 100.0, GeoUnit.KM, param)) .thenReturn(listGeoRadiusResponseCommandObject); Response> response = pipeliningBase .georadiusByMemberReadonly("key", "member", 100.0, GeoUnit.KM, param); assertThat(commands, contains(listGeoRadiusResponseCommandObject)); assertThat(response, is(predefinedResponse)); } @Test public void testGeoradiusByMemberReadonlyWithParamBinary() { byte[] key = "location".getBytes(); byte[] member = "Palermo".getBytes(); double radius = 100; GeoUnit unit = GeoUnit.KM; GeoRadiusParam param = GeoRadiusParam.geoRadiusParam(); when(commandObjects.georadiusByMemberReadonly(key, member, radius, unit, param)).thenReturn(listGeoRadiusResponseCommandObject); Response> response = pipeliningBase.georadiusByMemberReadonly(key, member, radius, unit, param); assertThat(commands, contains(listGeoRadiusResponseCommandObject)); assertThat(response, is(predefinedResponse)); } @Test public void testGeoradiusStore() { GeoRadiusParam param = GeoRadiusParam.geoRadiusParam(); GeoRadiusStoreParam storeParam = GeoRadiusStoreParam.geoRadiusStoreParam().store("storeKey"); when(commandObjects.georadiusStore("key", 15.0, 37.0, 100.0, GeoUnit.KM, param, storeParam)) .thenReturn(longCommandObject); Response response = pipeliningBase .georadiusStore("key", 15.0, 37.0, 100.0, GeoUnit.KM, param, storeParam); assertThat(commands, contains(longCommandObject)); assertThat(response, is(predefinedResponse)); } @Test public void testGeoradiusStoreBinary() { byte[] key = "location".getBytes(); double longitude = 13.361389; double latitude = 38.115556; double radius = 100; GeoUnit unit = GeoUnit.KM; GeoRadiusParam param = GeoRadiusParam.geoRadiusParam(); GeoRadiusStoreParam storeParam = GeoRadiusStoreParam.geoRadiusStoreParam().store("storeKey"); when(commandObjects.georadiusStore(key, longitude, latitude, radius, unit, param, storeParam)).thenReturn(longCommandObject); Response response = pipeliningBase.georadiusStore(key, longitude, latitude, radius, unit, param, storeParam); assertThat(commands, contains(longCommandObject)); assertThat(response, is(predefinedResponse)); } @Test public void testGeoradiusByMemberStore() { GeoRadiusParam param = GeoRadiusParam.geoRadiusParam(); GeoRadiusStoreParam storeParam = GeoRadiusStoreParam.geoRadiusStoreParam().store("storeKey"); when(commandObjects.georadiusByMemberStore("key", "member", 100.0, GeoUnit.KM, param, storeParam)) .thenReturn(longCommandObject); Response response = pipeliningBase .georadiusByMemberStore("key", "member", 100.0, GeoUnit.KM, param, storeParam); assertThat(commands, contains(longCommandObject)); assertThat(response, is(predefinedResponse)); } @Test public void testGeoradiusByMemberStoreBinary() { byte[] key = "location".getBytes(); byte[] member = "Palermo".getBytes(); double radius = 100; GeoUnit unit = GeoUnit.KM; GeoRadiusParam param = GeoRadiusParam.geoRadiusParam(); GeoRadiusStoreParam storeParam = GeoRadiusStoreParam.geoRadiusStoreParam().store("storeKey"); when(commandObjects.georadiusByMemberStore(key, member, radius, unit, param, storeParam)).thenReturn(longCommandObject); Response response = pipeliningBase.georadiusByMemberStore(key, member, radius, unit, param, storeParam); assertThat(commands, contains(longCommandObject)); assertThat(response, is(predefinedResponse)); } @Test public void testGeosearchByMemberRadius() { when(commandObjects.geosearch("key", "member", 100.0, GeoUnit.KM)) .thenReturn(listGeoRadiusResponseCommandObject); Response> response = pipeliningBase .geosearch("key", "member", 100.0, GeoUnit.KM); assertThat(commands, contains(listGeoRadiusResponseCommandObject)); assertThat(response, is(predefinedResponse)); } @Test public void testGeosearchByMemberRadiusBinary() { byte[] key = "location".getBytes(); byte[] member = "Palermo".getBytes(); double radius = 100; GeoUnit unit = GeoUnit.KM; when(commandObjects.geosearch(key, member, radius, unit)).thenReturn(listGeoRadiusResponseCommandObject); Response> response = pipeliningBase.geosearch(key, member, radius, unit); assertThat(commands, contains(listGeoRadiusResponseCommandObject)); assertThat(response, is(predefinedResponse)); } @Test public void testGeosearchByCoordRadius() { GeoCoordinate coord = new GeoCoordinate(15.0, 37.0); when(commandObjects.geosearch("key", coord, 100.0, GeoUnit.KM)) .thenReturn(listGeoRadiusResponseCommandObject); Response> response = pipeliningBase.geosearch("key", coord, 100.0, GeoUnit.KM); assertThat(commands, contains(listGeoRadiusResponseCommandObject)); assertThat(response, is(predefinedResponse)); } @Test public void testGeosearchByCoordRadiusBinary() { byte[] key = "location".getBytes(); GeoCoordinate coord = new GeoCoordinate(13.361389, 38.115556); double radius = 100; GeoUnit unit = GeoUnit.KM; when(commandObjects.geosearch(key, coord, radius, unit)).thenReturn(listGeoRadiusResponseCommandObject); Response> response = pipeliningBase.geosearch(key, coord, radius, unit); assertThat(commands, contains(listGeoRadiusResponseCommandObject)); assertThat(response, is(predefinedResponse)); } @Test public void testGeosearchByMemberBox() { when(commandObjects.geosearch("key", "member", 50.0, 50.0, GeoUnit.KM)) .thenReturn(listGeoRadiusResponseCommandObject); Response> response = pipeliningBase .geosearch("key", "member", 50.0, 50.0, GeoUnit.KM); assertThat(commands, contains(listGeoRadiusResponseCommandObject)); assertThat(response, is(predefinedResponse)); } @Test public void testGeosearchByMemberBoxBinary() { byte[] key = "location".getBytes(); byte[] member = "Palermo".getBytes(); double width = 200; double height = 100; GeoUnit unit = GeoUnit.KM; when(commandObjects.geosearch(key, member, width, height, unit)).thenReturn(listGeoRadiusResponseCommandObject); Response> response = pipeliningBase.geosearch(key, member, width, height, unit); assertThat(commands, contains(listGeoRadiusResponseCommandObject)); assertThat(response, is(predefinedResponse)); } @Test public void testGeosearchByCoordBox() { GeoCoordinate coord = new GeoCoordinate(15.0, 37.0); when(commandObjects.geosearch("key", coord, 50.0, 50.0, GeoUnit.KM)) .thenReturn(listGeoRadiusResponseCommandObject); Response> response = pipeliningBase .geosearch("key", coord, 50.0, 50.0, GeoUnit.KM); assertThat(commands, contains(listGeoRadiusResponseCommandObject)); assertThat(response, is(predefinedResponse)); } @Test public void testGeosearchByCoordBoxBinary() { byte[] key = "location".getBytes(); GeoCoordinate coord = new GeoCoordinate(13.361389, 38.115556); double width = 200; double height = 100; GeoUnit unit = GeoUnit.KM; when(commandObjects.geosearch(key, coord, width, height, unit)).thenReturn(listGeoRadiusResponseCommandObject); Response> response = pipeliningBase.geosearch(key, coord, width, height, unit); assertThat(commands, contains(listGeoRadiusResponseCommandObject)); assertThat(response, is(predefinedResponse)); } @Test public void testGeosearchWithParams() { GeoSearchParam params = new GeoSearchParam(); when(commandObjects.geosearch("key", params)).thenReturn(listGeoRadiusResponseCommandObject); Response> response = pipeliningBase.geosearch("key", params); assertThat(commands, contains(listGeoRadiusResponseCommandObject)); assertThat(response, is(predefinedResponse)); } @Test public void testGeosearchWithParamsBinary() { byte[] key = "location".getBytes(); GeoSearchParam params = GeoSearchParam.geoSearchParam().byRadius(100, GeoUnit.KM); when(commandObjects.geosearch(key, params)).thenReturn(listGeoRadiusResponseCommandObject); Response> response = pipeliningBase.geosearch(key, params); assertThat(commands, contains(listGeoRadiusResponseCommandObject)); assertThat(response, is(predefinedResponse)); } @Test public void testGeosearchStoreByMemberRadius() { when(commandObjects.geosearchStore("dest", "src", "member", 100.0, GeoUnit.KM)) .thenReturn(longCommandObject); Response response = pipeliningBase .geosearchStore("dest", "src", "member", 100.0, GeoUnit.KM); assertThat(commands, contains(longCommandObject)); assertThat(response, is(predefinedResponse)); } @Test public void testGeosearchStoreByMemberRadiusBinary() { byte[] dest = "destination".getBytes(); byte[] src = "location".getBytes(); byte[] member = "Palermo".getBytes(); double radius = 100; GeoUnit unit = GeoUnit.KM; when(commandObjects.geosearchStore(dest, src, member, radius, unit)).thenReturn(longCommandObject); Response response = pipeliningBase.geosearchStore(dest, src, member, radius, unit); assertThat(commands, contains(longCommandObject)); assertThat(response, is(predefinedResponse)); } @Test public void testGeosearchStoreByCoordRadius() { GeoCoordinate coord = new GeoCoordinate(15.0, 37.0); when(commandObjects.geosearchStore("dest", "src", coord, 100.0, GeoUnit.KM)) .thenReturn(longCommandObject); Response response = pipeliningBase.geosearchStore("dest", "src", coord, 100.0, GeoUnit.KM); assertThat(commands, contains(longCommandObject)); assertThat(response, is(predefinedResponse)); } @Test public void testGeosearchStoreByCoordRadiusBinary() { byte[] dest = "destination".getBytes(); byte[] src = "location".getBytes(); GeoCoordinate coord = new GeoCoordinate(13.361389, 38.115556); double radius = 100; GeoUnit unit = GeoUnit.KM; when(commandObjects.geosearchStore(dest, src, coord, radius, unit)).thenReturn(longCommandObject); Response response = pipeliningBase.geosearchStore(dest, src, coord, radius, unit); assertThat(commands, contains(longCommandObject)); assertThat(response, is(predefinedResponse)); } @Test public void testGeosearchStoreByMemberBox() { when(commandObjects.geosearchStore("dest", "src", "member", 50.0, 50.0, GeoUnit.KM)) .thenReturn(longCommandObject); Response response = pipeliningBase .geosearchStore("dest", "src", "member", 50.0, 50.0, GeoUnit.KM); assertThat(commands, contains(longCommandObject)); assertThat(response, is(predefinedResponse)); } @Test public void testGeosearchStoreByMemberBoxBinary() { byte[] dest = "destination".getBytes(); byte[] src = "location".getBytes(); byte[] member = "Palermo".getBytes(); double width = 200; double height = 100; GeoUnit unit = GeoUnit.KM; when(commandObjects.geosearchStore(dest, src, member, width, height, unit)).thenReturn(longCommandObject); Response response = pipeliningBase.geosearchStore(dest, src, member, width, height, unit); assertThat(commands, contains(longCommandObject)); assertThat(response, is(predefinedResponse)); } @Test public void testGeosearchStoreByCoordBox() { GeoCoordinate coord = new GeoCoordinate(15.0, 37.0); when(commandObjects.geosearchStore("dest", "src", coord, 50.0, 50.0, GeoUnit.KM)) .thenReturn(longCommandObject); Response response = pipeliningBase .geosearchStore("dest", "src", coord, 50.0, 50.0, GeoUnit.KM); assertThat(commands, contains(longCommandObject)); assertThat(response, is(predefinedResponse)); } @Test public void testGeosearchStoreByCoordBoxBinary() { byte[] dest = "destination".getBytes(); byte[] src = "location".getBytes(); GeoCoordinate coord = new GeoCoordinate(13.361389, 38.115556); double width = 200; double height = 100; GeoUnit unit = GeoUnit.KM; when(commandObjects.geosearchStore(dest, src, coord, width, height, unit)).thenReturn(longCommandObject); Response response = pipeliningBase.geosearchStore(dest, src, coord, width, height, unit); assertThat(commands, contains(longCommandObject)); assertThat(response, is(predefinedResponse)); } @Test public void testGeosearchStoreWithParams() { GeoSearchParam params = new GeoSearchParam(); when(commandObjects.geosearchStore("dest", "src", params)).thenReturn(longCommandObject); Response response = pipeliningBase.geosearchStore("dest", "src", params); assertThat(commands, contains(longCommandObject)); assertThat(response, is(predefinedResponse)); } @Test public void testGeosearchStoreWithParamsBinary() { byte[] dest = "destination".getBytes(); byte[] src = "location".getBytes(); GeoSearchParam params = GeoSearchParam.geoSearchParam().byRadius(100, GeoUnit.KM); when(commandObjects.geosearchStore(dest, src, params)).thenReturn(longCommandObject); Response response = pipeliningBase.geosearchStore(dest, src, params); assertThat(commands, contains(longCommandObject)); assertThat(response, is(predefinedResponse)); } @Test public void testGeosearchStoreStoreDist() { GeoSearchParam params = new GeoSearchParam(); when(commandObjects.geosearchStoreStoreDist("dest", "src", params)).thenReturn(longCommandObject); Response response = pipeliningBase.geosearchStoreStoreDist("dest", "src", params); assertThat(commands, contains(longCommandObject)); assertThat(response, is(predefinedResponse)); } @Test public void testGeosearchStoreStoreDistBinary() { byte[] dest = "destination".getBytes(); byte[] src = "location".getBytes(); GeoSearchParam params = GeoSearchParam.geoSearchParam().byRadius(100, GeoUnit.KM); when(commandObjects.geosearchStoreStoreDist(dest, src, params)).thenReturn(longCommandObject); Response response = pipeliningBase.geosearchStoreStoreDist(dest, src, params); assertThat(commands, contains(longCommandObject)); assertThat(response, is(predefinedResponse)); } } ================================================ FILE: src/test/java/redis/clients/jedis/mocked/pipeline/PipeliningBaseHashCommandsTest.java ================================================ package redis.clients.jedis.mocked.pipeline; import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.contains; import static org.hamcrest.Matchers.in; import static org.hamcrest.Matchers.is; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Set; import org.junit.jupiter.api.Test; import redis.clients.jedis.Response; import redis.clients.jedis.args.ExpiryOption; import redis.clients.jedis.params.ScanParams; import redis.clients.jedis.resps.ScanResult; public class PipeliningBaseHashCommandsTest extends PipeliningBaseMockedTestBase { private final byte[] bfoo = { 0x01, 0x02, 0x03, 0x04 }; private final byte[] bbar1 = { 0x05, 0x06, 0x07, 0x08, 0x0A }; private final byte[] bbar2 = { 0x05, 0x06, 0x07, 0x08, 0x0B }; private final byte[] bbar3 = { 0x05, 0x06, 0x07, 0x08, 0x0C }; @Test public void testHdel() { when(commandObjects.hdel("key", "field1", "field2")).thenReturn(longCommandObject); Response response = pipeliningBase.hdel("key", "field1", "field2"); assertThat(commands, contains(longCommandObject)); assertThat(response, is(predefinedResponse)); } @Test public void testHdelBinary() { byte[] key = "hash".getBytes(); byte[] field1 = "field1".getBytes(); byte[] field2 = "field2".getBytes(); when(commandObjects.hdel(key, field1, field2)).thenReturn(longCommandObject); Response response = pipeliningBase.hdel(key, field1, field2); assertThat(commands, contains(longCommandObject)); assertThat(response, is(predefinedResponse)); } @Test public void testHexists() { when(commandObjects.hexists("key", "field")).thenReturn(booleanCommandObject); Response response = pipeliningBase.hexists("key", "field"); assertThat(commands, contains(booleanCommandObject)); assertThat(response, is(predefinedResponse)); } @Test public void testHexistsBinary() { byte[] key = "hash".getBytes(); byte[] field = "field1".getBytes(); when(commandObjects.hexists(key, field)).thenReturn(booleanCommandObject); Response response = pipeliningBase.hexists(key, field); assertThat(commands, contains(booleanCommandObject)); assertThat(response, is(predefinedResponse)); } @Test public void testHget() { when(commandObjects.hget("key", "field")).thenReturn(stringCommandObject); Response response = pipeliningBase.hget("key", "field"); assertThat(commands, contains(stringCommandObject)); assertThat(response, is(predefinedResponse)); } @Test public void testHgetBinary() { byte[] key = "hash".getBytes(); byte[] field = "field1".getBytes(); when(commandObjects.hget(key, field)).thenReturn(bytesCommandObject); Response response = pipeliningBase.hget(key, field); assertThat(commands, contains(bytesCommandObject)); assertThat(response, is(predefinedResponse)); } @Test public void testHgetAll() { when(commandObjects.hgetAll("key")).thenReturn(mapStringStringCommandObject); Response> response = pipeliningBase.hgetAll("key"); assertThat(commands, contains(mapStringStringCommandObject)); assertThat(response, is(predefinedResponse)); } @Test public void testHgetAllBinary() { byte[] key = "hash".getBytes(); when(commandObjects.hgetAll(key)).thenReturn(mapBytesBytesCommandObject); Response> response = pipeliningBase.hgetAll(key); assertThat(commands, contains(mapBytesBytesCommandObject)); assertThat(response, is(predefinedResponse)); } @Test public void testHincrBy() { when(commandObjects.hincrBy("key", "field", 1L)).thenReturn(longCommandObject); Response response = pipeliningBase.hincrBy("key", "field", 1L); assertThat(commands, contains(longCommandObject)); assertThat(response, is(predefinedResponse)); } @Test public void testHincrByBinary() { byte[] key = "hash".getBytes(); byte[] field = "field1".getBytes(); long increment = 2L; when(commandObjects.hincrBy(key, field, increment)).thenReturn(longCommandObject); Response response = pipeliningBase.hincrBy(key, field, increment); assertThat(commands, contains(longCommandObject)); assertThat(response, is(predefinedResponse)); } @Test public void testHincrByFloat() { when(commandObjects.hincrByFloat("key", "field", 1.0)).thenReturn(doubleCommandObject); Response response = pipeliningBase.hincrByFloat("key", "field", 1.0); assertThat(commands, contains(doubleCommandObject)); assertThat(response, is(predefinedResponse)); } @Test public void testHincrByFloatBinary() { byte[] key = "hash".getBytes(); byte[] field = "field1".getBytes(); double increment = 2.5; when(commandObjects.hincrByFloat(key, field, increment)).thenReturn(doubleCommandObject); Response response = pipeliningBase.hincrByFloat(key, field, increment); assertThat(commands, contains(doubleCommandObject)); assertThat(response, is(predefinedResponse)); } @Test public void testHkeys() { when(commandObjects.hkeys("key")).thenReturn(setStringCommandObject); Response> response = pipeliningBase.hkeys("key"); assertThat(commands, contains(setStringCommandObject)); assertThat(response, is(predefinedResponse)); } @Test public void testHkeysBinary() { byte[] key = "hash".getBytes(); when(commandObjects.hkeys(key)).thenReturn(setBytesCommandObject); Response> response = pipeliningBase.hkeys(key); assertThat(commands, contains(setBytesCommandObject)); assertThat(response, is(predefinedResponse)); } @Test public void testHlen() { when(commandObjects.hlen("key")).thenReturn(longCommandObject); Response response = pipeliningBase.hlen("key"); assertThat(commands, contains(longCommandObject)); assertThat(response, is(predefinedResponse)); } @Test public void testHlenBinary() { byte[] key = "hash".getBytes(); when(commandObjects.hlen(key)).thenReturn(longCommandObject); Response response = pipeliningBase.hlen(key); assertThat(commands, contains(longCommandObject)); assertThat(response, is(predefinedResponse)); } @Test public void testHmget() { when(commandObjects.hmget("key", "field1", "field2")).thenReturn(listStringCommandObject); Response> response = pipeliningBase.hmget("key", "field1", "field2"); assertThat(commands, contains(listStringCommandObject)); assertThat(response, is(predefinedResponse)); } @Test public void testHmgetBinary() { byte[] key = "hash".getBytes(); byte[] field1 = "field1".getBytes(); byte[] field2 = "field2".getBytes(); when(commandObjects.hmget(key, field1, field2)).thenReturn(listBytesCommandObject); Response> response = pipeliningBase.hmget(key, field1, field2); assertThat(commands, contains(listBytesCommandObject)); assertThat(response, is(predefinedResponse)); } @Test public void testHmset() { Map hash = new HashMap<>(); hash.put("field1", "value1"); hash.put("field2", "value2"); when(commandObjects.hmset("key", hash)).thenReturn(stringCommandObject); Response response = pipeliningBase.hmset("key", hash); assertThat(commands, contains(stringCommandObject)); assertThat(response, is(predefinedResponse)); } @Test public void testHmsetBinary() { byte[] key = "hash".getBytes(); Map hash = new HashMap<>(); hash.put("field1".getBytes(), "value1".getBytes()); when(commandObjects.hmset(key, hash)).thenReturn(stringCommandObject); Response response = pipeliningBase.hmset(key, hash); assertThat(commands, contains(stringCommandObject)); assertThat(response, is(predefinedResponse)); } @Test public void testHrandfield() { when(commandObjects.hrandfield("key")).thenReturn(stringCommandObject); Response response = pipeliningBase.hrandfield("key"); assertThat(commands, contains(stringCommandObject)); assertThat(response, is(predefinedResponse)); } @Test public void testHrandfieldBinary() { byte[] key = "hash".getBytes(); when(commandObjects.hrandfield(key)).thenReturn(bytesCommandObject); Response response = pipeliningBase.hrandfield(key); assertThat(commands, contains(bytesCommandObject)); assertThat(response, is(predefinedResponse)); } @Test public void testHrandfieldCount() { long count = 2; when(commandObjects.hrandfield("key", count)).thenReturn(listStringCommandObject); Response> response = pipeliningBase.hrandfield("key", count); assertThat(commands, contains(listStringCommandObject)); assertThat(response, is(predefinedResponse)); } @Test public void testHrandfieldCountBinary() { byte[] key = "hash".getBytes(); long count = 2; when(commandObjects.hrandfield(key, count)).thenReturn(listBytesCommandObject); Response> response = pipeliningBase.hrandfield(key, count); assertThat(commands, contains(listBytesCommandObject)); assertThat(response, is(predefinedResponse)); } @Test public void testHrandfieldWithValues() { long count = 2; when(commandObjects.hrandfieldWithValues("key", count)).thenReturn(listEntryStringStringCommandObject); Response>> response = pipeliningBase.hrandfieldWithValues("key", count); assertThat(commands, contains(listEntryStringStringCommandObject)); assertThat(response, is(predefinedResponse)); } @Test public void testHrandfieldWithValuesBinary() { byte[] key = "hash".getBytes(); long count = 2; when(commandObjects.hrandfieldWithValues(key, count)).thenReturn(listEntryBytesBytesCommandObject); Response>> response = pipeliningBase.hrandfieldWithValues(key, count); assertThat(commands, contains(listEntryBytesBytesCommandObject)); assertThat(response, is(predefinedResponse)); } @Test public void testHscan() { String cursor = "0"; ScanParams params = new ScanParams(); when(commandObjects.hscan("key", cursor, params)).thenReturn(scanResultEntryStringStringCommandObject); Response>> response = pipeliningBase.hscan("key", cursor, params); assertThat(commands, contains(scanResultEntryStringStringCommandObject)); assertThat(response, is(predefinedResponse)); } @Test public void testHscanBinary() { byte[] key = "hash".getBytes(); byte[] cursor = "0".getBytes(); ScanParams params = new ScanParams().match("*").count(10); when(commandObjects.hscan(key, cursor, params)).thenReturn(scanResultEntryBytesBytesCommandObject); Response>> response = pipeliningBase.hscan(key, cursor, params); assertThat(commands, contains(scanResultEntryBytesBytesCommandObject)); assertThat(response, is(predefinedResponse)); } @Test public void testHscanNoValues() { String cursor = "0"; ScanParams params = new ScanParams(); when(commandObjects.hscanNoValues("key", cursor, params)).thenReturn(scanResultStringCommandObject); Response> response = pipeliningBase.hscanNoValues("key", cursor, params); assertThat(commands, contains(scanResultStringCommandObject)); assertThat(response, is(predefinedResponse)); } @Test public void testHscanNoValuesBinary() { byte[] key = "hash".getBytes(); byte[] cursor = "0".getBytes(); ScanParams params = new ScanParams().match("*").count(10); when(commandObjects.hscanNoValues(key, cursor, params)).thenReturn(scanResultBytesCommandObject); Response> response = pipeliningBase.hscanNoValues(key, cursor, params); assertThat(commands, contains(scanResultBytesCommandObject)); assertThat(response, is(predefinedResponse)); } @Test public void testHset() { when(commandObjects.hset("key", "field", "value")).thenReturn(longCommandObject); Response response = pipeliningBase.hset("key", "field", "value"); assertThat(commands, contains(longCommandObject)); assertThat(response, is(predefinedResponse)); } @Test public void testHsetBinary() { byte[] key = "hash".getBytes(); byte[] field = "field1".getBytes(); byte[] value = "value1".getBytes(); when(commandObjects.hset(key, field, value)).thenReturn(longCommandObject); Response response = pipeliningBase.hset(key, field, value); assertThat(commands, contains(longCommandObject)); assertThat(response, is(predefinedResponse)); } @Test public void testHsetMap() { Map hash = new HashMap<>(); hash.put("field1", "value1"); hash.put("field2", "value2"); when(commandObjects.hset("key", hash)).thenReturn(longCommandObject); Response response = pipeliningBase.hset("key", hash); assertThat(commands, contains(longCommandObject)); assertThat(response, is(predefinedResponse)); } @Test public void testHsetMapBinary() { byte[] key = "hash".getBytes(); Map hash = new HashMap<>(); hash.put("field1".getBytes(), "value1".getBytes()); when(commandObjects.hset(key, hash)).thenReturn(longCommandObject); Response response = pipeliningBase.hset(key, hash); assertThat(commands, contains(longCommandObject)); assertThat(response, is(predefinedResponse)); } @Test public void testHsetnx() { when(commandObjects.hsetnx("key", "field", "value")).thenReturn(longCommandObject); Response response = pipeliningBase.hsetnx("key", "field", "value"); assertThat(commands, contains(longCommandObject)); assertThat(response, is(predefinedResponse)); } @Test public void testHsetnxBinary() { byte[] key = "hash".getBytes(); byte[] field = "field1".getBytes(); byte[] value = "value1".getBytes(); when(commandObjects.hsetnx(key, field, value)).thenReturn(longCommandObject); Response response = pipeliningBase.hsetnx(key, field, value); assertThat(commands, contains(longCommandObject)); assertThat(response, is(predefinedResponse)); } @Test public void testHstrlen() { when(commandObjects.hstrlen("key", "field")).thenReturn(longCommandObject); Response response = pipeliningBase.hstrlen("key", "field"); assertThat(commands, contains(longCommandObject)); assertThat(response, is(predefinedResponse)); } @Test public void testHstrlenBinary() { byte[] key = "hash".getBytes(); byte[] field = "field1".getBytes(); when(commandObjects.hstrlen(key, field)).thenReturn(longCommandObject); Response response = pipeliningBase.hstrlen(key, field); assertThat(commands, contains(longCommandObject)); assertThat(response, is(predefinedResponse)); } @Test public void testHvals() { when(commandObjects.hvals("key")).thenReturn(listStringCommandObject); Response> response = pipeliningBase.hvals("key"); assertThat(commands, contains(listStringCommandObject)); assertThat(response, is(predefinedResponse)); } @Test public void testHvalsBinary() { byte[] key = "hash".getBytes(); when(commandObjects.hvals(key)).thenReturn(listBytesCommandObject); Response> response = pipeliningBase.hvals(key); assertThat(commands, contains(listBytesCommandObject)); assertThat(response, is(predefinedResponse)); } @Test public void hexpire() { String key = "hash"; long seconds = 100; String[] fields = { "one", "two", "three" }; when(commandObjects.hexpire(key, seconds, fields)).thenReturn(listLongCommandObject); assertThat(pipeliningBase.hexpire(key, seconds, fields), is(predefinedResponse)); assertThat(listLongCommandObject, in(commands)); } @Test public void hexpireCondition() { String key = "hash"; long seconds = 100; ExpiryOption condition = mock(ExpiryOption.class); String[] fields = { "one", "two", "three" }; when(commandObjects.hexpire(key, seconds, condition, fields)).thenReturn(listLongCommandObject); assertThat(pipeliningBase.hexpire(key, seconds, condition, fields), is(predefinedResponse)); assertThat(listLongCommandObject, in(commands)); } @Test public void hpexpire() { String key = "hash"; long milliseconds = 10000; String[] fields = { "one", "two", "three" }; when(commandObjects.hpexpire(key, milliseconds, fields)).thenReturn(listLongCommandObject); assertThat(pipeliningBase.hpexpire(key, milliseconds, fields), is(predefinedResponse)); assertThat(listLongCommandObject, in(commands)); } @Test public void hpexpireCondition() { String key = "hash"; long milliseconds = 10000; ExpiryOption condition = mock(ExpiryOption.class); String[] fields = { "one", "two", "three" }; when(commandObjects.hpexpire(key, milliseconds, condition, fields)).thenReturn(listLongCommandObject); assertThat(pipeliningBase.hpexpire(key, milliseconds, condition, fields), is(predefinedResponse)); assertThat(listLongCommandObject, in(commands)); } @Test public void hexpireAt() { String key = "hash"; long seconds = 100; String[] fields = { "one", "two", "three" }; when(commandObjects.hexpireAt(key, seconds, fields)).thenReturn(listLongCommandObject); assertThat(pipeliningBase.hexpireAt(key, seconds, fields), is(predefinedResponse)); assertThat(listLongCommandObject, in(commands)); } @Test public void hexpireAtCondition() { String key = "hash"; long seconds = 100; ExpiryOption condition = mock(ExpiryOption.class); String[] fields = { "one", "two", "three" }; when(commandObjects.hexpireAt(key, seconds, condition, fields)).thenReturn(listLongCommandObject); assertThat(pipeliningBase.hexpireAt(key, seconds, condition, fields), is(predefinedResponse)); assertThat(listLongCommandObject, in(commands)); } @Test public void hpexpireAt() { String key = "hash"; long milliseconds = 10000; String[] fields = { "one", "two", "three" }; when(commandObjects.hpexpireAt(key, milliseconds, fields)).thenReturn(listLongCommandObject); assertThat(pipeliningBase.hpexpireAt(key, milliseconds, fields), is(predefinedResponse)); assertThat(listLongCommandObject, in(commands)); } @Test public void hpexpireAtCondition() { String key = "hash"; long milliseconds = 10000; ExpiryOption condition = mock(ExpiryOption.class); String[] fields = { "one", "two", "three" }; when(commandObjects.hpexpireAt(key, milliseconds, condition, fields)).thenReturn(listLongCommandObject); assertThat(pipeliningBase.hpexpireAt(key, milliseconds, condition, fields), is(predefinedResponse)); assertThat(listLongCommandObject, in(commands)); } @Test public void hexpireTime() { String key = "hash"; String[] fields = { "one", "two", "three" }; when(commandObjects.hexpireTime(key, fields)).thenReturn(listLongCommandObject); assertThat(pipeliningBase.hexpireTime(key, fields), is(predefinedResponse)); assertThat(listLongCommandObject, in(commands)); } @Test public void hpexpireTime() { String key = "hash"; String[] fields = { "one", "two", "three" }; when(commandObjects.hpexpireTime(key, fields)).thenReturn(listLongCommandObject); assertThat(pipeliningBase.hpexpireTime(key, fields), is(predefinedResponse)); assertThat(listLongCommandObject, in(commands)); } @Test public void httl() { String key = "hash"; String[] fields = { "one", "two", "three" }; when(commandObjects.httl(key, fields)).thenReturn(listLongCommandObject); assertThat(pipeliningBase.httl(key, fields), is(predefinedResponse)); assertThat(listLongCommandObject, in(commands)); } @Test public void hpttl() { String key = "hash"; String[] fields = { "one", "two", "three" }; when(commandObjects.hpttl(key, fields)).thenReturn(listLongCommandObject); assertThat(pipeliningBase.hpttl(key, fields), is(predefinedResponse)); assertThat(listLongCommandObject, in(commands)); } @Test public void hpersist() { String key = "hash"; String[] fields = { "one", "two", "three" }; when(commandObjects.hpersist(key, fields)).thenReturn(listLongCommandObject); assertThat(pipeliningBase.hpersist(key, fields), is(predefinedResponse)); assertThat(listLongCommandObject, in(commands)); } @Test public void hexpireBinary() { byte[] key = bfoo; long seconds = 100; byte[][] fields = { bbar1, bbar2, bbar3 }; when(commandObjects.hexpire(key, seconds, fields)).thenReturn(listLongCommandObject); assertThat(pipeliningBase.hexpire(key, seconds, fields), is(predefinedResponse)); assertThat(listLongCommandObject, in(commands)); } @Test public void hexpireConditionBinary() { byte[] key = bfoo; long seconds = 100; ExpiryOption condition = mock(ExpiryOption.class); byte[][] fields = { bbar1, bbar2, bbar3 }; when(commandObjects.hexpire(key, seconds, condition, fields)).thenReturn(listLongCommandObject); assertThat(pipeliningBase.hexpire(key, seconds, condition, fields), is(predefinedResponse)); assertThat(listLongCommandObject, in(commands)); } @Test public void hpexpireBinary() { byte[] key = bfoo; long milliseconds = 10000; byte[][] fields = { bbar1, bbar2, bbar3 }; when(commandObjects.hpexpire(key, milliseconds, fields)).thenReturn(listLongCommandObject); assertThat(pipeliningBase.hpexpire(key, milliseconds, fields), is(predefinedResponse)); assertThat(listLongCommandObject, in(commands)); } @Test public void hpexpireConditionBinary() { byte[] key = bfoo; long milliseconds = 10000; ExpiryOption condition = mock(ExpiryOption.class); byte[][] fields = { bbar1, bbar2, bbar3 }; when(commandObjects.hpexpire(key, milliseconds, condition, fields)).thenReturn(listLongCommandObject); assertThat(pipeliningBase.hpexpire(key, milliseconds, condition, fields), is(predefinedResponse)); assertThat(listLongCommandObject, in(commands)); } @Test public void hexpireAtBinary() { byte[] key = bfoo; long seconds = 100; byte[][] fields = { bbar1, bbar2, bbar3 }; when(commandObjects.hexpireAt(key, seconds, fields)).thenReturn(listLongCommandObject); assertThat(pipeliningBase.hexpireAt(key, seconds, fields), is(predefinedResponse)); assertThat(listLongCommandObject, in(commands)); } @Test public void hexpireAtConditionBinary() { byte[] key = bfoo; long seconds = 100; ExpiryOption condition = mock(ExpiryOption.class); byte[][] fields = { bbar1, bbar2, bbar3 }; when(commandObjects.hexpireAt(key, seconds, condition, fields)).thenReturn(listLongCommandObject); assertThat(pipeliningBase.hexpireAt(key, seconds, condition, fields), is(predefinedResponse)); assertThat(listLongCommandObject, in(commands)); } @Test public void hpexpireAtBinary() { byte[] key = bfoo; long milliseconds = 10000; byte[][] fields = { bbar1, bbar2, bbar3 }; when(commandObjects.hpexpireAt(key, milliseconds, fields)).thenReturn(listLongCommandObject); assertThat(pipeliningBase.hpexpireAt(key, milliseconds, fields), is(predefinedResponse)); assertThat(listLongCommandObject, in(commands)); } @Test public void hpexpireAtConditionBinary() { byte[] key = bfoo; long milliseconds = 10000; ExpiryOption condition = mock(ExpiryOption.class); byte[][] fields = { bbar1, bbar2, bbar3 }; when(commandObjects.hpexpireAt(key, milliseconds, condition, fields)).thenReturn(listLongCommandObject); assertThat(pipeliningBase.hpexpireAt(key, milliseconds, condition, fields), is(predefinedResponse)); assertThat(listLongCommandObject, in(commands)); } @Test public void hexpireTimeBinary() { byte[] key = bfoo; byte[][] fields = { bbar1, bbar2, bbar3 }; when(commandObjects.hexpireTime(key, fields)).thenReturn(listLongCommandObject); assertThat(pipeliningBase.hexpireTime(key, fields), is(predefinedResponse)); assertThat(listLongCommandObject, in(commands)); } @Test public void hpexpireTimeBinary() { byte[] key = bfoo; byte[][] fields = { bbar1, bbar2, bbar3 }; when(commandObjects.hpexpireTime(key, fields)).thenReturn(listLongCommandObject); assertThat(pipeliningBase.hpexpireTime(key, fields), is(predefinedResponse)); assertThat(listLongCommandObject, in(commands)); } @Test public void httlBinary() { byte[] key = bfoo; byte[][] fields = { bbar1, bbar2, bbar3 }; when(commandObjects.httl(key, fields)).thenReturn(listLongCommandObject); assertThat(pipeliningBase.httl(key, fields), is(predefinedResponse)); assertThat(listLongCommandObject, in(commands)); } @Test public void hpttlBinary() { byte[] key = bfoo; byte[][] fields = { bbar1, bbar2, bbar3 }; when(commandObjects.hpttl(key, fields)).thenReturn(listLongCommandObject); assertThat(pipeliningBase.hpttl(key, fields), is(predefinedResponse)); assertThat(listLongCommandObject, in(commands)); } @Test public void hpersistBinary() { byte[] key = bfoo; byte[][] fields = { bbar1, bbar2, bbar3 }; when(commandObjects.hpersist(key, fields)).thenReturn(listLongCommandObject); assertThat(pipeliningBase.hpersist(key, fields), is(predefinedResponse)); assertThat(listLongCommandObject, in(commands)); } } ================================================ FILE: src/test/java/redis/clients/jedis/mocked/pipeline/PipeliningBaseHyperloglogCommandsTest.java ================================================ package redis.clients.jedis.mocked.pipeline; import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.contains; import static org.hamcrest.Matchers.is; import static org.mockito.Mockito.when; import org.junit.jupiter.api.Test; import redis.clients.jedis.Response; public class PipeliningBaseHyperloglogCommandsTest extends PipeliningBaseMockedTestBase { @Test public void testPfadd() { when(commandObjects.pfadd("key", "element1", "element2")).thenReturn(longCommandObject); Response response = pipeliningBase.pfadd("key", "element1", "element2"); assertThat(commands, contains(longCommandObject)); assertThat(response, is(predefinedResponse)); } @Test public void testPfaddBinary() { byte[] key = "hll".getBytes(); byte[] element1 = "element1".getBytes(); byte[] element2 = "element2".getBytes(); when(commandObjects.pfadd(key, element1, element2)).thenReturn(longCommandObject); Response response = pipeliningBase.pfadd(key, element1, element2); assertThat(commands, contains(longCommandObject)); assertThat(response, is(predefinedResponse)); } @Test public void testPfcount() { when(commandObjects.pfcount("key")).thenReturn(longCommandObject); Response response = pipeliningBase.pfcount("key"); assertThat(commands, contains(longCommandObject)); assertThat(response, is(predefinedResponse)); } @Test public void testPfcountBinary() { byte[] key = "hll".getBytes(); when(commandObjects.pfcount(key)).thenReturn(longCommandObject); Response response = pipeliningBase.pfcount(key); assertThat(commands, contains(longCommandObject)); assertThat(response, is(predefinedResponse)); } @Test public void testPfcountMultipleKeys() { when(commandObjects.pfcount("key1", "key2")).thenReturn(longCommandObject); Response response = pipeliningBase.pfcount("key1", "key2"); assertThat(commands, contains(longCommandObject)); assertThat(response, is(predefinedResponse)); } @Test public void testPfcountMultipleKeysBinary() { byte[] key1 = "hll1".getBytes(); byte[] key2 = "hll2".getBytes(); when(commandObjects.pfcount(key1, key2)).thenReturn(longCommandObject); Response response = pipeliningBase.pfcount(key1, key2); assertThat(commands, contains(longCommandObject)); assertThat(response, is(predefinedResponse)); } @Test public void testPfmerge() { when(commandObjects.pfmerge("destkey", "sourcekey1", "sourcekey2")).thenReturn(stringCommandObject); Response response = pipeliningBase.pfmerge("destkey", "sourcekey1", "sourcekey2"); assertThat(commands, contains(stringCommandObject)); assertThat(response, is(predefinedResponse)); } @Test public void testPfmergeBinary() { byte[] destkey = "hll_dest".getBytes(); byte[] sourcekey1 = "hll1".getBytes(); byte[] sourcekey2 = "hll2".getBytes(); when(commandObjects.pfmerge(destkey, sourcekey1, sourcekey2)).thenReturn(stringCommandObject); Response response = pipeliningBase.pfmerge(destkey, sourcekey1, sourcekey2); assertThat(commands, contains(stringCommandObject)); assertThat(response, is(predefinedResponse)); } } ================================================ FILE: src/test/java/redis/clients/jedis/mocked/pipeline/PipeliningBaseJsonCommandsTest.java ================================================ package redis.clients.jedis.mocked.pipeline; import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.contains; import static org.hamcrest.Matchers.is; import static org.mockito.Mockito.doNothing; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; import java.util.List; import com.google.gson.JsonObject; import org.json.JSONArray; import org.junit.jupiter.api.Test; import redis.clients.jedis.Response; import redis.clients.jedis.json.JsonObjectMapper; import redis.clients.jedis.json.JsonSetParams; import redis.clients.jedis.json.Path; import redis.clients.jedis.json.Path2; public class PipeliningBaseJsonCommandsTest extends PipeliningBaseMockedTestBase { @Test public void testJsonArrAppendWithPath() { Path path = new Path("$.array"); Object[] objects = { "one", "two", "three" }; when(commandObjects.jsonArrAppend("myJson", path, objects)).thenReturn(longCommandObject); Response response = pipeliningBase.jsonArrAppend("myJson", path, objects); assertThat(commands, contains(longCommandObject)); assertThat(response, is(predefinedResponse)); } @Test public void testJsonArrAppendWithPath2() { Path2 path = Path2.of("$.array"); Object[] objects = { "one", "two", "three" }; when(commandObjects.jsonArrAppend("myJson", path, objects)).thenReturn(listLongCommandObject); Response> response = pipeliningBase.jsonArrAppend("myJson", path, objects); assertThat(commands, contains(listLongCommandObject)); assertThat(response, is(predefinedResponse)); } @Test public void testJsonArrAppendWithPath2WithEscape() { Path2 path = Path2.of("$.array"); Object[] objects = { "one", "two", "three" }; when(commandObjects.jsonArrAppendWithEscape("myJson", path, objects)).thenReturn(listLongCommandObject); Response> response = pipeliningBase.jsonArrAppendWithEscape("myJson", path, objects); assertThat(commands, contains(listLongCommandObject)); assertThat(response, is(predefinedResponse)); } @Test public void testJsonArrIndexWithPath() { Path path = new Path("$.array"); Object scalar = "two"; when(commandObjects.jsonArrIndex("myJson", path, scalar)).thenReturn(longCommandObject); Response response = pipeliningBase.jsonArrIndex("myJson", path, scalar); assertThat(commands, contains(longCommandObject)); assertThat(response, is(predefinedResponse)); } @Test public void testJsonArrIndexWithPath2() { Path2 path = Path2.of("$.array"); Object scalar = "two"; when(commandObjects.jsonArrIndex("myJson", path, scalar)).thenReturn(listLongCommandObject); Response> response = pipeliningBase.jsonArrIndex("myJson", path, scalar); assertThat(commands, contains(listLongCommandObject)); assertThat(response, is(predefinedResponse)); } @Test public void testJsonArrIndexWithPath2WithEscape() { Path2 path = Path2.of("$.array"); Object scalar = "two"; when(commandObjects.jsonArrIndexWithEscape("myJson", path, scalar)).thenReturn(listLongCommandObject); Response> response = pipeliningBase.jsonArrIndexWithEscape("myJson", path, scalar); assertThat(commands, contains(listLongCommandObject)); assertThat(response, is(predefinedResponse)); } @Test public void testJsonArrInsertWithPath() { Path path = new Path("$.array"); Object[] pojos = { "one", "two", "three" }; when(commandObjects.jsonArrInsert("myJson", path, 1, pojos)).thenReturn(longCommandObject); Response response = pipeliningBase.jsonArrInsert("myJson", path, 1, pojos); assertThat(commands, contains(longCommandObject)); assertThat(response, is(predefinedResponse)); } @Test public void testJsonArrInsertWithPath2() { Path2 path = Path2.of("$.array"); Object[] objects = { "one", "two", "three" }; when(commandObjects.jsonArrInsert("myJson", path, 1, objects)).thenReturn(listLongCommandObject); Response> response = pipeliningBase.jsonArrInsert("myJson", path, 1, objects); assertThat(commands, contains(listLongCommandObject)); assertThat(response, is(predefinedResponse)); } @Test public void testJsonArrInsertWithPath2WithEscape() { Path2 path = Path2.of("$.array"); Object[] objects = { "one", "two", "three" }; when(commandObjects.jsonArrInsertWithEscape("myJson", path, 1, objects)).thenReturn(listLongCommandObject); Response> response = pipeliningBase.jsonArrInsertWithEscape("myJson", path, 1, objects); assertThat(commands, contains(listLongCommandObject)); assertThat(response, is(predefinedResponse)); } @Test public void testJsonArrLen() { when(commandObjects.jsonArrLen("myJson")).thenReturn(longCommandObject); Response response = pipeliningBase.jsonArrLen("myJson"); assertThat(commands, contains(longCommandObject)); assertThat(response, is(predefinedResponse)); } @Test public void testJsonArrLenWithPath() { Path path = new Path("$.array"); when(commandObjects.jsonArrLen("myJson", path)).thenReturn(longCommandObject); Response response = pipeliningBase.jsonArrLen("myJson", path); assertThat(commands, contains(longCommandObject)); assertThat(response, is(predefinedResponse)); } @Test public void testJsonArrLenWithPath2() { Path2 path = Path2.of("$.array"); when(commandObjects.jsonArrLen("myJson", path)).thenReturn(listLongCommandObject); Response> response = pipeliningBase.jsonArrLen("myJson", path); assertThat(commands, contains(listLongCommandObject)); assertThat(response, is(predefinedResponse)); } @Test public void testJsonArrPop() { when(commandObjects.jsonArrPop("myJson")).thenReturn(objectCommandObject); Response response = pipeliningBase.jsonArrPop("myJson"); assertThat(commands, contains(objectCommandObject)); assertThat(response, is(predefinedResponse)); } @Test public void testJsonArrPopWithPath() { Path path = new Path("$.array"); when(commandObjects.jsonArrPop("myJson", path)).thenReturn(objectCommandObject); Response response = pipeliningBase.jsonArrPop("myJson", path); assertThat(commands, contains(objectCommandObject)); assertThat(response, is(predefinedResponse)); } @Test public void testJsonArrPopWithPathAndIndex() { Path path = new Path("$.array"); when(commandObjects.jsonArrPop("myJson", path, 1)).thenReturn(objectCommandObject); Response response = pipeliningBase.jsonArrPop("myJson", path, 1); assertThat(commands, contains(objectCommandObject)); assertThat(response, is(predefinedResponse)); } @Test public void testJsonArrPopWithClassAndPath() { Path path = new Path("$.array"); when(commandObjects.jsonArrPop("myJson", MyBean.class, path)).thenReturn(myBeanCommandObject); Response response = pipeliningBase.jsonArrPop("myJson", MyBean.class, path); assertThat(commands, contains(myBeanCommandObject)); assertThat(response, is(predefinedResponse)); } @Test public void testJsonArrPopWithClassPathAndIndex() { Path path = new Path("$.array"); when(commandObjects.jsonArrPop("myJson", MyBean.class, path, 1)).thenReturn(myBeanCommandObject); Response response = pipeliningBase.jsonArrPop("myJson", MyBean.class, path, 1); assertThat(commands, contains(myBeanCommandObject)); assertThat(response, is(predefinedResponse)); } @Test public void testJsonArrPopWithPath2() { Path2 path = Path2.of("$.array"); when(commandObjects.jsonArrPop("myJson", path)).thenReturn(listObjectCommandObject); Response> response = pipeliningBase.jsonArrPop("myJson", path); assertThat(commands, contains(listObjectCommandObject)); assertThat(response, is(predefinedResponse)); } @Test public void testJsonArrPopWithPath2AndIndex() { Path2 path = Path2.of("$.array"); when(commandObjects.jsonArrPop("myJson", path, 1)).thenReturn(listObjectCommandObject); Response> response = pipeliningBase.jsonArrPop("myJson", path, 1); assertThat(commands, contains(listObjectCommandObject)); assertThat(response, is(predefinedResponse)); } @Test public void testJsonArrPopWithClass() { when(commandObjects.jsonArrPop("myJson", MyBean.class)).thenReturn(myBeanCommandObject); Response response = pipeliningBase.jsonArrPop("myJson", MyBean.class); assertThat(commands, contains(myBeanCommandObject)); assertThat(response, is(predefinedResponse)); } @Test public void testJsonArrTrimWithPath() { Path path = new Path("$.array"); when(commandObjects.jsonArrTrim("myJson", path, 1, 2)).thenReturn(longCommandObject); Response response = pipeliningBase.jsonArrTrim("myJson", path, 1, 2); assertThat(commands, contains(longCommandObject)); assertThat(response, is(predefinedResponse)); } @Test public void testJsonArrTrimWithPath2() { Path2 path = Path2.of("$.array"); when(commandObjects.jsonArrTrim("myJson", path, 1, 2)).thenReturn(listLongCommandObject); Response> response = pipeliningBase.jsonArrTrim("myJson", path, 1, 2); assertThat(commands, contains(listLongCommandObject)); assertThat(response, is(predefinedResponse)); } @Test public void testJsonClear() { when(commandObjects.jsonClear("myJson")).thenReturn(longCommandObject); Response response = pipeliningBase.jsonClear("myJson"); assertThat(commands, contains(longCommandObject)); assertThat(response, is(predefinedResponse)); } @Test public void testJsonClearWithPath() { Path path = new Path("$.field"); when(commandObjects.jsonClear("myJson", path)).thenReturn(longCommandObject); Response response = pipeliningBase.jsonClear("myJson", path); assertThat(commands, contains(longCommandObject)); assertThat(response, is(predefinedResponse)); } @Test public void testJsonClearWithPath2() { Path2 path = Path2.of("$.field"); when(commandObjects.jsonClear("myJson", path)).thenReturn(longCommandObject); Response response = pipeliningBase.jsonClear("myJson", path); assertThat(commands, contains(longCommandObject)); assertThat(response, is(predefinedResponse)); } @Test public void testJsonDel() { when(commandObjects.jsonDel("myJson")).thenReturn(longCommandObject); Response response = pipeliningBase.jsonDel("myJson"); assertThat(commands, contains(longCommandObject)); assertThat(response, is(predefinedResponse)); } @Test public void testJsonDelWithPath() { Path path = new Path("$.field"); when(commandObjects.jsonDel("myJson", path)).thenReturn(longCommandObject); Response response = pipeliningBase.jsonDel("myJson", path); assertThat(commands, contains(longCommandObject)); assertThat(response, is(predefinedResponse)); } @Test public void testJsonDelWithPath2() { Path2 path = Path2.of("$.field"); when(commandObjects.jsonDel("myJson", path)).thenReturn(longCommandObject); Response response = pipeliningBase.jsonDel("myJson", path); assertThat(commands, contains(longCommandObject)); assertThat(response, is(predefinedResponse)); } @Test public void testJsonGet() { when(commandObjects.jsonGet("myJson")).thenReturn(objectCommandObject); Response response = pipeliningBase.jsonGet("myJson"); assertThat(commands, contains(objectCommandObject)); assertThat(response, is(predefinedResponse)); } @Test public void testJsonGetWithClass() { when(commandObjects.jsonGet("myJson", MyBean.class)).thenReturn(myBeanCommandObject); Response response = pipeliningBase.jsonGet("myJson", MyBean.class); assertThat(commands, contains(myBeanCommandObject)); assertThat(response, is(predefinedResponse)); } @Test public void testJsonGetWithPath() { Path[] paths = { new Path("$.field1"), new Path("$.field2") }; when(commandObjects.jsonGet("myJson", paths)).thenReturn(objectCommandObject); Response response = pipeliningBase.jsonGet("myJson", paths); assertThat(commands, contains(objectCommandObject)); assertThat(response, is(predefinedResponse)); } @Test public void testJsonGetWithPath2() { Path2[] paths = { Path2.of("$.field1"), Path2.of("$.field2") }; when(commandObjects.jsonGet("myJson", paths)).thenReturn(objectCommandObject); Response response = pipeliningBase.jsonGet("myJson", paths); assertThat(commands, contains(objectCommandObject)); assertThat(response, is(predefinedResponse)); } @Test public void testJsonGetWithClassAndPath() { Path[] paths = { new Path("$.field1"), new Path("$.field2") }; when(commandObjects.jsonGet("myJson", MyBean.class, paths)).thenReturn(myBeanCommandObject); Response response = pipeliningBase.jsonGet("myJson", MyBean.class, paths); assertThat(commands, contains(myBeanCommandObject)); assertThat(response, is(predefinedResponse)); } @Test public void testJsonMergeWithPath() { Path path = new Path("$.field"); Object object = new JsonObject(); when(commandObjects.jsonMerge("myJson", path, object)).thenReturn(stringCommandObject); Response response = pipeliningBase.jsonMerge("myJson", path, object); assertThat(commands, contains(stringCommandObject)); assertThat(response, is(predefinedResponse)); } @Test public void testJsonMergeWithPath2() { Path2 path = Path2.of("$.field"); Object object = new JsonObject(); when(commandObjects.jsonMerge("myJson", path, object)).thenReturn(stringCommandObject); Response response = pipeliningBase.jsonMerge("myJson", path, object); assertThat(commands, contains(stringCommandObject)); assertThat(response, is(predefinedResponse)); } @Test public void testJsonMGetWithPathAndClass() { Path path = new Path("$.field"); when(commandObjects.jsonMGet(path, MyBean.class, "key1", "key2")).thenReturn(listMyBeanCommandObject); Response> response = pipeliningBase.jsonMGet(path, MyBean.class, "key1", "key2"); assertThat(commands, contains(listMyBeanCommandObject)); assertThat(response, is(predefinedResponse)); } @Test public void testJsonMGetWithPath2() { Path2 path = Path2.of("$.field"); when(commandObjects.jsonMGet(path, "key1", "key2")).thenReturn(listJsonArrayCommandObject); Response> response = pipeliningBase.jsonMGet(path, "key1", "key2"); assertThat(commands, contains(listJsonArrayCommandObject)); assertThat(response, is(predefinedResponse)); } @Test public void testJsonNumIncrByWithPath() { Path path = new Path("$.number"); when(commandObjects.jsonNumIncrBy("myJson", path, 42.0)).thenReturn(doubleCommandObject); Response response = pipeliningBase.jsonNumIncrBy("myJson", path, 42.0); assertThat(commands, contains(doubleCommandObject)); assertThat(response, is(predefinedResponse)); } @Test public void testJsonNumIncrByWithPath2() { Path2 path = Path2.of("$.number"); when(commandObjects.jsonNumIncrBy("myJson", path, 42.0)).thenReturn(objectCommandObject); Response response = pipeliningBase.jsonNumIncrBy("myJson", path, 42.0); assertThat(commands, contains(objectCommandObject)); assertThat(response, is(predefinedResponse)); } @Test public void testJsonSetWithPath() { Path path = Path.of("$.field"); Object object = new JsonObject(); when(commandObjects.jsonSet("myJson", path, object)).thenReturn(stringCommandObject); Response response = pipeliningBase.jsonSet("myJson", path, object); assertThat(commands, contains(stringCommandObject)); assertThat(response, is(predefinedResponse)); } @Test public void testJsonSetWithPathAndParams() { Path path = new Path("$.field"); Object object = new JsonObject(); JsonSetParams params = new JsonSetParams().nx(); when(commandObjects.jsonSet("myJson", path, object, params)).thenReturn(stringCommandObject); Response response = pipeliningBase.jsonSet("myJson", path, object, params); assertThat(commands, contains(stringCommandObject)); assertThat(response, is(predefinedResponse)); } @Test public void testJsonSetWithPath2() { Path2 path = Path2.of("$.field"); Object object = new JsonObject(); when(commandObjects.jsonSet("myJson", path, object)).thenReturn(stringCommandObject); Response response = pipeliningBase.jsonSet("myJson", path, object); assertThat(commands, contains(stringCommandObject)); assertThat(response, is(predefinedResponse)); } @Test public void testJsonSetWithPath2WithEscape() { Path2 path = Path2.of("$.field"); Object object = new JsonObject(); when(commandObjects.jsonSetWithEscape("myJson", path, object)).thenReturn(stringCommandObject); Response response = pipeliningBase.jsonSetWithEscape("myJson", path, object); assertThat(commands, contains(stringCommandObject)); assertThat(response, is(predefinedResponse)); } @Test public void testJsonSetWithPath2AndParams() { Path2 path = Path2.of("$.field"); Object object = new JsonObject(); JsonSetParams params = new JsonSetParams().nx(); when(commandObjects.jsonSet("myJson", path, object, params)).thenReturn(stringCommandObject); Response response = pipeliningBase.jsonSet("myJson", path, object, params); assertThat(commands, contains(stringCommandObject)); assertThat(response, is(predefinedResponse)); } @Test public void testJsonSetWithPath2EscapeAndParams() { Path2 path = Path2.of("$.field"); Object object = new JsonObject(); JsonSetParams params = new JsonSetParams().nx(); when(commandObjects.jsonSetWithEscape("myJson", path, object, params)).thenReturn(stringCommandObject); Response response = pipeliningBase.jsonSetWithEscape("myJson", path, object, params); assertThat(commands, contains(stringCommandObject)); assertThat(response, is(predefinedResponse)); } @Test public void testJsonStrAppend() { when(commandObjects.jsonStrAppend("myJson", "append")).thenReturn(longCommandObject); Response response = pipeliningBase.jsonStrAppend("myJson", "append"); assertThat(commands, contains(longCommandObject)); assertThat(response, is(predefinedResponse)); } @Test public void testJsonStrAppendWithPath() { Path path = new Path("$.field"); when(commandObjects.jsonStrAppend("myJson", path, "append")).thenReturn(longCommandObject); Response response = pipeliningBase.jsonStrAppend("myJson", path, "append"); assertThat(commands, contains(longCommandObject)); assertThat(response, is(predefinedResponse)); } @Test public void testJsonStrAppendWithPath2() { Path2 path = Path2.of("$.field"); when(commandObjects.jsonStrAppend("myJson", path, "append")).thenReturn(listLongCommandObject); Response> response = pipeliningBase.jsonStrAppend("myJson", path, "append"); assertThat(commands, contains(listLongCommandObject)); assertThat(response, is(predefinedResponse)); } @Test public void testJsonStrLen() { when(commandObjects.jsonStrLen("myJson")).thenReturn(longCommandObject); Response response = pipeliningBase.jsonStrLen("myJson"); assertThat(commands, contains(longCommandObject)); assertThat(response, is(predefinedResponse)); } @Test public void testJsonStrLenWithPath() { Path path = new Path("$.field"); when(commandObjects.jsonStrLen("myJson", path)).thenReturn(longCommandObject); Response response = pipeliningBase.jsonStrLen("myJson", path); assertThat(commands, contains(longCommandObject)); assertThat(response, is(predefinedResponse)); } @Test public void testJsonStrLenWithPath2() { Path2 path = Path2.of("$.field"); when(commandObjects.jsonStrLen("myJson", path)).thenReturn(listLongCommandObject); Response> response = pipeliningBase.jsonStrLen("myJson", path); assertThat(commands, contains(listLongCommandObject)); assertThat(response, is(predefinedResponse)); } @Test public void testJsonToggleWithPath() { Path path = new Path("$.field"); when(commandObjects.jsonToggle("myJson", path)).thenReturn(stringCommandObject); Response response = pipeliningBase.jsonToggle("myJson", path); assertThat(commands, contains(stringCommandObject)); assertThat(response, is(predefinedResponse)); } @Test public void testJsonToggleWithPath2() { Path2 path = Path2.of("$.field"); when(commandObjects.jsonToggle("myJson", path)).thenReturn(listBooleanCommandObject); Response> response = pipeliningBase.jsonToggle("myJson", path); assertThat(commands, contains(listBooleanCommandObject)); assertThat(response, is(predefinedResponse)); } @Test public void testJsonType() { when(commandObjects.jsonType("myJson")).thenReturn(classCommandObject); Response> response = pipeliningBase.jsonType("myJson"); assertThat(commands, contains(classCommandObject)); assertThat(response, is(predefinedResponse)); } @Test public void testJsonTypeWithPath() { Path path = new Path("$.field"); when(commandObjects.jsonType("myJson", path)).thenReturn(classCommandObject); Response> response = pipeliningBase.jsonType("myJson", path); assertThat(commands, contains(classCommandObject)); assertThat(response, is(predefinedResponse)); } @Test public void testJsonTypeWithPath2() { Path2 path = Path2.of("$.field"); when(commandObjects.jsonType("myJson", path)).thenReturn(listClassCommandObject); Response>> response = pipeliningBase.jsonType("myJson", path); assertThat(commands, contains(listClassCommandObject)); assertThat(response, is(predefinedResponse)); } @Test public void testSetJsonObjectMapper() { JsonObjectMapper jsonObjectMapper = mock(JsonObjectMapper.class); doNothing().when(commandObjects).setJsonObjectMapper(jsonObjectMapper); pipeliningBase.setJsonObjectMapper(jsonObjectMapper); verify(commandObjects).setJsonObjectMapper(jsonObjectMapper); } } ================================================ FILE: src/test/java/redis/clients/jedis/mocked/pipeline/PipeliningBaseListCommandsTest.java ================================================ package redis.clients.jedis.mocked.pipeline; import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.contains; import static org.hamcrest.Matchers.is; import static org.mockito.Mockito.when; import java.util.List; import org.junit.jupiter.api.Test; import redis.clients.jedis.Response; import redis.clients.jedis.args.ListDirection; import redis.clients.jedis.args.ListPosition; import redis.clients.jedis.params.LPosParams; import redis.clients.jedis.util.KeyValue; public class PipeliningBaseListCommandsTest extends PipeliningBaseMockedTestBase { @Test public void testBlmove() { ListDirection from = ListDirection.LEFT; ListDirection to = ListDirection.RIGHT; double timeout = 1.0; when(commandObjects.blmove("srcKey", "dstKey", from, to, timeout)).thenReturn(stringCommandObject); Response response = pipeliningBase.blmove("srcKey", "dstKey", from, to, timeout); assertThat(commands, contains(stringCommandObject)); assertThat(response, is(predefinedResponse)); } @Test public void testBlmoveBinary() { byte[] srcKey = "srcKey".getBytes(); byte[] dstKey = "dstKey".getBytes(); ListDirection from = ListDirection.LEFT; ListDirection to = ListDirection.RIGHT; double timeout = 10.5; when(commandObjects.blmove(srcKey, dstKey, from, to, timeout)).thenReturn(bytesCommandObject); Response response = pipeliningBase.blmove(srcKey, dstKey, from, to, timeout); assertThat(commands, contains(bytesCommandObject)); assertThat(response, is(predefinedResponse)); } @Test public void testBlmpop() { double timeout = 1.0; ListDirection direction = ListDirection.LEFT; when(commandObjects.blmpop(timeout, direction, "key1", "key2")).thenReturn(keyValueStringListStringCommandObject); Response>> response = pipeliningBase.blmpop(timeout, direction, "key1", "key2"); assertThat(commands, contains(keyValueStringListStringCommandObject)); assertThat(response, is(predefinedResponse)); } @Test public void testBlmpopBinary() { double timeout = 10.5; ListDirection direction = ListDirection.LEFT; byte[] key1 = "key1".getBytes(); byte[] key2 = "key2".getBytes(); when(commandObjects.blmpop(timeout, direction, key1, key2)).thenReturn(keyValueBytesListBytesCommandObject); Response>> response = pipeliningBase.blmpop(timeout, direction, key1, key2); assertThat(commands, contains(keyValueBytesListBytesCommandObject)); assertThat(response, is(predefinedResponse)); } @Test public void testBlmpopCount() { double timeout = 1.0; ListDirection direction = ListDirection.LEFT; int count = 2; when(commandObjects.blmpop(timeout, direction, count, "key1", "key2")).thenReturn(keyValueStringListStringCommandObject); Response>> response = pipeliningBase.blmpop(timeout, direction, count, "key1", "key2"); assertThat(commands, contains(keyValueStringListStringCommandObject)); assertThat(response, is(predefinedResponse)); } @Test public void testBlmpopCountBinary() { double timeout = 10.5; ListDirection direction = ListDirection.LEFT; int count = 2; byte[] key1 = "key1".getBytes(); byte[] key2 = "key2".getBytes(); when(commandObjects.blmpop(timeout, direction, count, key1, key2)).thenReturn(keyValueBytesListBytesCommandObject); Response>> response = pipeliningBase.blmpop(timeout, direction, count, key1, key2); assertThat(commands, contains(keyValueBytesListBytesCommandObject)); assertThat(response, is(predefinedResponse)); } @Test public void testBlpop() { when(commandObjects.blpop(30, "key")).thenReturn(listStringCommandObject); Response> response = pipeliningBase.blpop(30, "key"); assertThat(commands, contains(listStringCommandObject)); assertThat(response, is(predefinedResponse)); } @Test public void testBlpopBinary() { int timeout = 10; byte[] key1 = "key1".getBytes(); byte[] key2 = "key2".getBytes(); when(commandObjects.blpop(timeout, key1, key2)).thenReturn(listBytesCommandObject); Response> response = pipeliningBase.blpop(timeout, key1, key2); assertThat(commands, contains(listBytesCommandObject)); assertThat(response, is(predefinedResponse)); } @Test public void testBlpopDoubleTimeout() { when(commandObjects.blpop(30.0, "key")).thenReturn(keyValueStringStringCommandObject); Response> response = pipeliningBase.blpop(30.0, "key"); assertThat(commands, contains(keyValueStringStringCommandObject)); assertThat(response, is(predefinedResponse)); } @Test public void testBlpopDoubleTimeoutBinary() { double timeout = 10.5; byte[] key1 = "key1".getBytes(); byte[] key2 = "key2".getBytes(); when(commandObjects.blpop(timeout, key1, key2)).thenReturn(keyValueBytesBytesCommandObject); Response> response = pipeliningBase.blpop(timeout, key1, key2); assertThat(commands, contains(keyValueBytesBytesCommandObject)); assertThat(response, is(predefinedResponse)); } @Test public void testBlpopMultipleKeys() { when(commandObjects.blpop(30, "key1", "key2")).thenReturn(listStringCommandObject); Response> response = pipeliningBase.blpop(30, "key1", "key2"); assertThat(commands, contains(listStringCommandObject)); assertThat(response, is(predefinedResponse)); } @Test public void testBlpopMultipleKeysDoubleTimeout() { when(commandObjects.blpop(30.0, "key1", "key2")).thenReturn(keyValueStringStringCommandObject); Response> response = pipeliningBase.blpop(30.0, "key1", "key2"); assertThat(commands, contains(keyValueStringStringCommandObject)); assertThat(response, is(predefinedResponse)); } @Test public void testBrpop() { when(commandObjects.brpop(30, "key")).thenReturn(listStringCommandObject); Response> response = pipeliningBase.brpop(30, "key"); assertThat(commands, contains(listStringCommandObject)); assertThat(response, is(predefinedResponse)); } @Test public void testBrpopBinary() { int timeout = 10; byte[] key1 = "key1".getBytes(); byte[] key2 = "key2".getBytes(); when(commandObjects.brpop(timeout, key1, key2)).thenReturn(listBytesCommandObject); Response> response = pipeliningBase.brpop(timeout, key1, key2); assertThat(commands, contains(listBytesCommandObject)); assertThat(response, is(predefinedResponse)); } @Test public void testBrpopDoubleTimeout() { when(commandObjects.brpop(30.0, "key")).thenReturn(keyValueStringStringCommandObject); Response> response = pipeliningBase.brpop(30.0, "key"); assertThat(commands, contains(keyValueStringStringCommandObject)); assertThat(response, is(predefinedResponse)); } @Test public void testBrpopDoubleTimeoutBinary() { double timeout = 10.5; byte[] key1 = "key1".getBytes(); byte[] key2 = "key2".getBytes(); when(commandObjects.brpop(timeout, key1, key2)).thenReturn(keyValueBytesBytesCommandObject); Response> response = pipeliningBase.brpop(timeout, key1, key2); assertThat(commands, contains(keyValueBytesBytesCommandObject)); assertThat(response, is(predefinedResponse)); } @Test public void testBrpopMultipleKeys() { when(commandObjects.brpop(30, "key1", "key2")).thenReturn(listStringCommandObject); Response> response = pipeliningBase.brpop(30, "key1", "key2"); assertThat(commands, contains(listStringCommandObject)); assertThat(response, is(predefinedResponse)); } @Test public void testBrpopMultipleKeysDoubleTimeout() { when(commandObjects.brpop(30.0, "key1", "key2")).thenReturn(keyValueStringStringCommandObject); Response> response = pipeliningBase.brpop(30.0, "key1", "key2"); assertThat(commands, contains(keyValueStringStringCommandObject)); assertThat(response, is(predefinedResponse)); } @Test public void testBrpoplpush() { when(commandObjects.brpoplpush("source", "destination", 30)).thenReturn(stringCommandObject); Response response = pipeliningBase.brpoplpush("source", "destination", 30); assertThat(commands, contains(stringCommandObject)); assertThat(response, is(predefinedResponse)); } @Test public void testBrpoplpushBinary() { byte[] source = "source".getBytes(); byte[] destination = "destination".getBytes(); int timeout = 10; when(commandObjects.brpoplpush(source, destination, timeout)).thenReturn(bytesCommandObject); Response response = pipeliningBase.brpoplpush(source, destination, timeout); assertThat(commands, contains(bytesCommandObject)); assertThat(response, is(predefinedResponse)); } @Test public void testLindex() { when(commandObjects.lindex("key", 1)).thenReturn(stringCommandObject); Response response = pipeliningBase.lindex("key", 1); assertThat(commands, contains(stringCommandObject)); assertThat(response, is(predefinedResponse)); } @Test public void testLindexBinary() { byte[] key = "key".getBytes(); long index = 0; when(commandObjects.lindex(key, index)).thenReturn(bytesCommandObject); Response response = pipeliningBase.lindex(key, index); assertThat(commands, contains(bytesCommandObject)); assertThat(response, is(predefinedResponse)); } @Test public void testLinsert() { ListPosition where = ListPosition.BEFORE; when(commandObjects.linsert("key", where, "pivot", "value")).thenReturn(longCommandObject); Response response = pipeliningBase.linsert("key", where, "pivot", "value"); assertThat(commands, contains(longCommandObject)); assertThat(response, is(predefinedResponse)); } @Test public void testLinsertBinary() { byte[] key = "key".getBytes(); ListPosition where = ListPosition.BEFORE; byte[] pivot = "pivot".getBytes(); byte[] value = "value".getBytes(); when(commandObjects.linsert(key, where, pivot, value)).thenReturn(longCommandObject); Response response = pipeliningBase.linsert(key, where, pivot, value); assertThat(commands, contains(longCommandObject)); assertThat(response, is(predefinedResponse)); } @Test public void testLlen() { when(commandObjects.llen("key")).thenReturn(longCommandObject); Response response = pipeliningBase.llen("key"); assertThat(commands, contains(longCommandObject)); assertThat(response, is(predefinedResponse)); } @Test public void testLlenBinary() { byte[] key = "key".getBytes(); when(commandObjects.llen(key)).thenReturn(longCommandObject); Response response = pipeliningBase.llen(key); assertThat(commands, contains(longCommandObject)); assertThat(response, is(predefinedResponse)); } @Test public void testLmove() { ListDirection from = ListDirection.LEFT; ListDirection to = ListDirection.RIGHT; when(commandObjects.lmove("srcKey", "dstKey", from, to)).thenReturn(stringCommandObject); Response response = pipeliningBase.lmove("srcKey", "dstKey", from, to); assertThat(commands, contains(stringCommandObject)); assertThat(response, is(predefinedResponse)); } @Test public void testLmoveBinary() { byte[] srcKey = "srcKey".getBytes(); byte[] dstKey = "dstKey".getBytes(); ListDirection from = ListDirection.LEFT; ListDirection to = ListDirection.RIGHT; when(commandObjects.lmove(srcKey, dstKey, from, to)).thenReturn(bytesCommandObject); Response response = pipeliningBase.lmove(srcKey, dstKey, from, to); assertThat(commands, contains(bytesCommandObject)); assertThat(response, is(predefinedResponse)); } @Test public void testLmpop() { ListDirection direction = ListDirection.LEFT; when(commandObjects.lmpop(direction, "key1", "key2")).thenReturn(keyValueStringListStringCommandObject); Response>> response = pipeliningBase.lmpop(direction, "key1", "key2"); assertThat(commands, contains(keyValueStringListStringCommandObject)); assertThat(response, is(predefinedResponse)); } @Test public void testLmpopBinary() { ListDirection direction = ListDirection.LEFT; byte[] key1 = "key1".getBytes(); byte[] key2 = "key2".getBytes(); when(commandObjects.lmpop(direction, key1, key2)).thenReturn(keyValueBytesListBytesCommandObject); Response>> response = pipeliningBase.lmpop(direction, key1, key2); assertThat(commands, contains(keyValueBytesListBytesCommandObject)); assertThat(response, is(predefinedResponse)); } @Test public void testLmpopCount() { ListDirection direction = ListDirection.LEFT; int count = 2; when(commandObjects.lmpop(direction, count, "key1", "key2")).thenReturn(keyValueStringListStringCommandObject); Response>> response = pipeliningBase.lmpop(direction, count, "key1", "key2"); assertThat(commands, contains(keyValueStringListStringCommandObject)); assertThat(response, is(predefinedResponse)); } @Test public void testLmpopCountBinary() { ListDirection direction = ListDirection.LEFT; int count = 2; byte[] key1 = "key1".getBytes(); byte[] key2 = "key2".getBytes(); when(commandObjects.lmpop(direction, count, key1, key2)).thenReturn(keyValueBytesListBytesCommandObject); Response>> response = pipeliningBase.lmpop(direction, count, key1, key2); assertThat(commands, contains(keyValueBytesListBytesCommandObject)); assertThat(response, is(predefinedResponse)); } @Test public void testLpop() { when(commandObjects.lpop("key")).thenReturn(stringCommandObject); Response response = pipeliningBase.lpop("key"); assertThat(commands, contains(stringCommandObject)); assertThat(response, is(predefinedResponse)); } @Test public void testLpopBinary() { byte[] key = "key".getBytes(); when(commandObjects.lpop(key)).thenReturn(bytesCommandObject); Response response = pipeliningBase.lpop(key); assertThat(commands, contains(bytesCommandObject)); assertThat(response, is(predefinedResponse)); } @Test public void testLpopCount() { when(commandObjects.lpop("key", 2)).thenReturn(listStringCommandObject); Response> response = pipeliningBase.lpop("key", 2); assertThat(commands, contains(listStringCommandObject)); assertThat(response, is(predefinedResponse)); } @Test public void testLpopCountBinary() { byte[] key = "key".getBytes(); int count = 2; when(commandObjects.lpop(key, count)).thenReturn(listBytesCommandObject); Response> response = pipeliningBase.lpop(key, count); assertThat(commands, contains(listBytesCommandObject)); assertThat(response, is(predefinedResponse)); } @Test public void testLpos() { when(commandObjects.lpos("key", "element")).thenReturn(longCommandObject); Response response = pipeliningBase.lpos("key", "element"); assertThat(commands, contains(longCommandObject)); assertThat(response, is(predefinedResponse)); } @Test public void testLposBinary() { byte[] key = "key".getBytes(); byte[] element = "element".getBytes(); when(commandObjects.lpos(key, element)).thenReturn(longCommandObject); Response response = pipeliningBase.lpos(key, element); assertThat(commands, contains(longCommandObject)); assertThat(response, is(predefinedResponse)); } @Test public void testLposWithParams() { LPosParams params = new LPosParams(); when(commandObjects.lpos("key", "element", params)).thenReturn(longCommandObject); Response response = pipeliningBase.lpos("key", "element", params); assertThat(commands, contains(longCommandObject)); assertThat(response, is(predefinedResponse)); } @Test public void testLposWithParamsBinary() { byte[] key = "key".getBytes(); byte[] element = "element".getBytes(); LPosParams params = new LPosParams().rank(1); when(commandObjects.lpos(key, element, params)).thenReturn(longCommandObject); Response response = pipeliningBase.lpos(key, element, params); assertThat(commands, contains(longCommandObject)); assertThat(response, is(predefinedResponse)); } @Test public void testLposWithParamsCount() { LPosParams params = new LPosParams(); when(commandObjects.lpos("key", "element", params, 3)).thenReturn(listLongCommandObject); Response> response = pipeliningBase.lpos("key", "element", params, 3); assertThat(commands, contains(listLongCommandObject)); assertThat(response, is(predefinedResponse)); } @Test public void testLposWithParamsCountBinary() { byte[] key = "key".getBytes(); byte[] element = "element".getBytes(); LPosParams params = new LPosParams().rank(1); long count = 2; when(commandObjects.lpos(key, element, params, count)).thenReturn(listLongCommandObject); Response> response = pipeliningBase.lpos(key, element, params, count); assertThat(commands, contains(listLongCommandObject)); assertThat(response, is(predefinedResponse)); } @Test public void testLpush() { when(commandObjects.lpush("key", "value1", "value2")).thenReturn(longCommandObject); Response response = pipeliningBase.lpush("key", "value1", "value2"); assertThat(commands, contains(longCommandObject)); assertThat(response, is(predefinedResponse)); } @Test public void testLpushBinary() { byte[] key = "key".getBytes(); byte[] arg1 = "value1".getBytes(); byte[] arg2 = "value2".getBytes(); when(commandObjects.lpush(key, arg1, arg2)).thenReturn(longCommandObject); Response response = pipeliningBase.lpush(key, arg1, arg2); assertThat(commands, contains(longCommandObject)); assertThat(response, is(predefinedResponse)); } @Test public void testLpushx() { when(commandObjects.lpushx("key", "value1", "value2")).thenReturn(longCommandObject); Response response = pipeliningBase.lpushx("key", "value1", "value2"); assertThat(commands, contains(longCommandObject)); assertThat(response, is(predefinedResponse)); } @Test public void testLpushxBinary() { byte[] key = "key".getBytes(); byte[] arg = "value".getBytes(); when(commandObjects.lpushx(key, arg)).thenReturn(longCommandObject); Response response = pipeliningBase.lpushx(key, arg); assertThat(commands, contains(longCommandObject)); assertThat(response, is(predefinedResponse)); } @Test public void testLrange() { when(commandObjects.lrange("key", 0, -1)).thenReturn(listStringCommandObject); Response> response = pipeliningBase.lrange("key", 0, -1); assertThat(commands, contains(listStringCommandObject)); assertThat(response, is(predefinedResponse)); } @Test public void testLrangeBinary() { byte[] key = "key".getBytes(); long start = 0; long stop = -1; when(commandObjects.lrange(key, start, stop)).thenReturn(listBytesCommandObject); Response> response = pipeliningBase.lrange(key, start, stop); assertThat(commands, contains(listBytesCommandObject)); assertThat(response, is(predefinedResponse)); } @Test public void testLrem() { when(commandObjects.lrem("key", 2, "value")).thenReturn(longCommandObject); Response response = pipeliningBase.lrem("key", 2, "value"); assertThat(commands, contains(longCommandObject)); assertThat(response, is(predefinedResponse)); } @Test public void testLremBinary() { byte[] key = "key".getBytes(); long count = 1; byte[] value = "value".getBytes(); when(commandObjects.lrem(key, count, value)).thenReturn(longCommandObject); Response response = pipeliningBase.lrem(key, count, value); assertThat(commands, contains(longCommandObject)); assertThat(response, is(predefinedResponse)); } @Test public void testLset() { when(commandObjects.lset("key", 1, "value")).thenReturn(stringCommandObject); Response response = pipeliningBase.lset("key", 1, "value"); assertThat(commands, contains(stringCommandObject)); assertThat(response, is(predefinedResponse)); } @Test public void testLsetBinary() { byte[] key = "key".getBytes(); long index = 0; byte[] value = "value".getBytes(); when(commandObjects.lset(key, index, value)).thenReturn(stringCommandObject); Response response = pipeliningBase.lset(key, index, value); assertThat(commands, contains(stringCommandObject)); assertThat(response, is(predefinedResponse)); } @Test public void testLtrim() { when(commandObjects.ltrim("key", 1, -1)).thenReturn(stringCommandObject); Response response = pipeliningBase.ltrim("key", 1, -1); assertThat(commands, contains(stringCommandObject)); assertThat(response, is(predefinedResponse)); } @Test public void testLtrimBinary() { byte[] key = "key".getBytes(); long start = 1; long stop = -1; when(commandObjects.ltrim(key, start, stop)).thenReturn(stringCommandObject); Response response = pipeliningBase.ltrim(key, start, stop); assertThat(commands, contains(stringCommandObject)); assertThat(response, is(predefinedResponse)); } @Test public void testRpop() { when(commandObjects.rpop("key")).thenReturn(stringCommandObject); Response response = pipeliningBase.rpop("key"); assertThat(commands, contains(stringCommandObject)); assertThat(response, is(predefinedResponse)); } @Test public void testRpopBinary() { byte[] key = "key".getBytes(); when(commandObjects.rpop(key)).thenReturn(bytesCommandObject); Response response = pipeliningBase.rpop(key); assertThat(commands, contains(bytesCommandObject)); assertThat(response, is(predefinedResponse)); } @Test public void testRpopCount() { when(commandObjects.rpop("key", 2)).thenReturn(listStringCommandObject); Response> response = pipeliningBase.rpop("key", 2); assertThat(commands, contains(listStringCommandObject)); assertThat(response, is(predefinedResponse)); } @Test public void testRpopCountBinary() { byte[] key = "key".getBytes(); int count = 2; when(commandObjects.rpop(key, count)).thenReturn(listBytesCommandObject); Response> response = pipeliningBase.rpop(key, count); assertThat(commands, contains(listBytesCommandObject)); assertThat(response, is(predefinedResponse)); } @Test public void testRpoplpush() { when(commandObjects.rpoplpush("srcKey", "dstKey")).thenReturn(stringCommandObject); Response response = pipeliningBase.rpoplpush("srcKey", "dstKey"); assertThat(commands, contains(stringCommandObject)); assertThat(response, is(predefinedResponse)); } @Test public void testRpoplpushBinary() { byte[] srckey = "srckey".getBytes(); byte[] dstkey = "dstkey".getBytes(); when(commandObjects.rpoplpush(srckey, dstkey)).thenReturn(bytesCommandObject); Response response = pipeliningBase.rpoplpush(srckey, dstkey); assertThat(commands, contains(bytesCommandObject)); assertThat(response, is(predefinedResponse)); } @Test public void testRpush() { when(commandObjects.rpush("key", "value1", "value2")).thenReturn(longCommandObject); Response response = pipeliningBase.rpush("key", "value1", "value2"); assertThat(commands, contains(longCommandObject)); assertThat(response, is(predefinedResponse)); } @Test public void testRpushBinary() { byte[] key = "key".getBytes(); byte[] arg1 = "value1".getBytes(); byte[] arg2 = "value2".getBytes(); when(commandObjects.rpush(key, arg1, arg2)).thenReturn(longCommandObject); Response response = pipeliningBase.rpush(key, arg1, arg2); assertThat(commands, contains(longCommandObject)); assertThat(response, is(predefinedResponse)); } @Test public void testRpushx() { when(commandObjects.rpushx("key", "value1", "value2")).thenReturn(longCommandObject); Response response = pipeliningBase.rpushx("key", "value1", "value2"); assertThat(commands, contains(longCommandObject)); assertThat(response, is(predefinedResponse)); } @Test public void testRpushxBinary() { byte[] key = "key".getBytes(); byte[] arg = "value".getBytes(); when(commandObjects.rpushx(key, arg)).thenReturn(longCommandObject); Response response = pipeliningBase.rpushx(key, arg); assertThat(commands, contains(longCommandObject)); assertThat(response, is(predefinedResponse)); } } ================================================ FILE: src/test/java/redis/clients/jedis/mocked/pipeline/PipeliningBaseMiscellaneousTest.java ================================================ package redis.clients.jedis.mocked.pipeline; import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.contains; import static org.hamcrest.Matchers.hasSize; import static org.hamcrest.Matchers.is; import static org.mockito.Mockito.when; import java.util.ArrayList; import java.util.List; import java.util.stream.Collectors; import org.junit.jupiter.api.Test; import redis.clients.jedis.BuilderFactory; import redis.clients.jedis.CommandArguments; import redis.clients.jedis.CommandObject; import redis.clients.jedis.Protocol; import redis.clients.jedis.Response; import redis.clients.jedis.args.Rawable; import redis.clients.jedis.commands.ProtocolCommand; /** * {@link redis.clients.jedis.PipeliningBase} tests that don't really fall into any category of commands. */ public class PipeliningBaseMiscellaneousTest extends PipeliningBaseMockedTestBase { @Test public void testSendCommandWithStringArgs() { ProtocolCommand cmd = Protocol.Command.GET; String arg1 = "key1"; String arg2 = "key2"; Response response = pipeliningBase.sendCommand(cmd, arg1, arg2); assertThat(commands, hasSize(1)); List arguments = new ArrayList<>(); commands.get(0).getArguments().forEach(arguments::add); assertThat(arguments.stream().map(Rawable::getRaw).collect(Collectors.toList()), contains( Protocol.Command.GET.getRaw(), arg1.getBytes(), arg2.getBytes() )); assertThat(response, is(predefinedResponse)); } @Test public void testSendCommandWithByteArgs() { ProtocolCommand cmd = Protocol.Command.SET; byte[] arg1 = "key1".getBytes(); byte[] arg2 = "value1".getBytes(); Response response = pipeliningBase.sendCommand(cmd, arg1, arg2); assertThat(commands, hasSize(1)); List arguments = new ArrayList<>(); commands.get(0).getArguments().forEach(arguments::add); assertThat(arguments.stream().map(Rawable::getRaw).collect(Collectors.toList()), contains( Protocol.Command.SET.getRaw(), arg1, arg2 )); assertThat(response, is(predefinedResponse)); } @Test public void testExecuteCommand() { CommandArguments commandArguments = new CommandArguments(Protocol.Command.GET).key("key1"); CommandObject commandObject = new CommandObject<>(commandArguments, BuilderFactory.STRING); Response response = pipeliningBase.executeCommand(commandObject); assertThat(commands, contains(commandObject)); assertThat(response, is(predefinedResponse)); } @Test public void testMultipleCommands() { when(commandObjects.exists("key1")).thenReturn(booleanCommandObject); when(commandObjects.exists("key2")).thenReturn(booleanCommandObject); Response result1 = pipeliningBase.exists("key1"); Response result2 = pipeliningBase.exists("key2"); assertThat(commands, contains( booleanCommandObject, booleanCommandObject )); assertThat(result1, is(predefinedResponse)); assertThat(result2, is(predefinedResponse)); } } ================================================ FILE: src/test/java/redis/clients/jedis/mocked/pipeline/PipeliningBaseMockedTestBase.java ================================================ package redis.clients.jedis.mocked.pipeline; import java.util.ArrayList; import java.util.List; import org.junit.jupiter.api.BeforeEach; import org.mockito.Mock; import redis.clients.jedis.CommandObject; import redis.clients.jedis.CommandObjects; import redis.clients.jedis.PipeliningBase; import redis.clients.jedis.Response; import redis.clients.jedis.mocked.MockedCommandObjectsTestBase; /** * Base class for unit tests for {@link PipeliningBase}, using Mockito. Given that {@link PipeliningBase} * is, essentially, only requesting commands from a {@link CommandObjects} instance and sending them * to its subclasses, and given that it has many methods, using mocks is the most convenient and * reliable way to completely test it. */ public abstract class PipeliningBaseMockedTestBase extends MockedCommandObjectsTestBase { /** * A concrete implementation of {@link PipeliningBase} that collects all commands * in a list (so that asserts can be run on the content of the list), and always returns a * predefined response (so that the response can be asserted). */ private static class TestPipeliningBase extends PipeliningBase { private final Response predefinedResponse; private final List> commands; public TestPipeliningBase(CommandObjects commandObjects, Response predefinedResponse, List> commands) { super(commandObjects); this.predefinedResponse = predefinedResponse; this.commands = commands; } @Override @SuppressWarnings("unchecked") protected Response appendCommand(CommandObject commandObject) { // Collect the command in the list. commands.add(commandObject); // Return a well known response, that can be asserted in the test cases. return (Response) predefinedResponse; } } /** * {@link PipeliningBase} under-test. Given that it is an abstract class, an in-place implementation * is used, that collects commands in a list. */ protected PipeliningBase pipeliningBase; /** * Accumulates commands sent by the {@link PipeliningBase} under-test to its subclass. */ protected final List> commands = new ArrayList<>(); /** * {@link CommandObjects} instance used by the {@link PipeliningBase} under-test. Depending on * the test case, it is trained to return one of the mock {@link CommandObject} instances below. */ @Mock protected CommandObjects commandObjects; /** * Mock {@link Response} that is returned by {@link PipeliningBase} from each method. */ @Mock protected Response predefinedResponse; @BeforeEach public void setUp() { pipeliningBase = new TestPipeliningBase(commandObjects, predefinedResponse, commands); } } ================================================ FILE: src/test/java/redis/clients/jedis/mocked/pipeline/PipeliningBaseScriptingAndFunctionsCommandsTest.java ================================================ package redis.clients.jedis.mocked.pipeline; import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.contains; import static org.hamcrest.Matchers.is; import static org.mockito.Mockito.when; import java.util.Collections; import java.util.List; import org.junit.jupiter.api.Test; import redis.clients.jedis.Response; import redis.clients.jedis.args.FlushMode; import redis.clients.jedis.args.FunctionRestorePolicy; import redis.clients.jedis.resps.FunctionStats; import redis.clients.jedis.resps.LibraryInfo; public class PipeliningBaseScriptingAndFunctionsCommandsTest extends PipeliningBaseMockedTestBase { @Test public void testEval() { String script = "return 'Hello, world!'"; when(commandObjects.eval(script)).thenReturn(objectCommandObject); Response response = pipeliningBase.eval(script); assertThat(commands, contains(objectCommandObject)); assertThat(response, is(predefinedResponse)); } @Test public void testEvalBinary() { byte[] script = "return 'Hello, world!'".getBytes(); when(commandObjects.eval(script)).thenReturn(objectCommandObject); Response response = pipeliningBase.eval(script); assertThat(commands, contains(objectCommandObject)); assertThat(response, is(predefinedResponse)); } @Test public void testEvalWithParams() { String script = "return KEYS[1] .. ARGV[1]"; int keyCount = 1; when(commandObjects.eval(script, keyCount, "key", "arg")).thenReturn(objectCommandObject); Response response = pipeliningBase.eval(script, keyCount, "key", "arg"); assertThat(commands, contains(objectCommandObject)); assertThat(response, is(predefinedResponse)); } @Test public void testEvalWithParamsBinary() { byte[] script = "return KEYS[1]".getBytes(); int keyCount = 1; byte[] param1 = "key1".getBytes(); when(commandObjects.eval(script, keyCount, param1)).thenReturn(objectCommandObject); Response response = pipeliningBase.eval(script, keyCount, param1); assertThat(commands, contains(objectCommandObject)); assertThat(response, is(predefinedResponse)); } @Test public void testEvalWithLists() { String script = "return KEYS[1] .. ARGV[1]"; List keys = Collections.singletonList("key"); List args = Collections.singletonList("arg"); when(commandObjects.eval(script, keys, args)).thenReturn(objectCommandObject); Response response = pipeliningBase.eval(script, keys, args); assertThat(commands, contains(objectCommandObject)); assertThat(response, is(predefinedResponse)); } @Test public void testEvalWithListsBinary() { byte[] script = "return {KEYS[1], ARGV[1]}".getBytes(); List keys = Collections.singletonList("key1".getBytes()); List args = Collections.singletonList("arg1".getBytes()); when(commandObjects.eval(script, keys, args)).thenReturn(objectCommandObject); Response response = pipeliningBase.eval(script, keys, args); assertThat(commands, contains(objectCommandObject)); assertThat(response, is(predefinedResponse)); } @Test public void testEvalWithSampleKey() { String script = "return 'Hello, world!'"; when(commandObjects.eval(script, "key")).thenReturn(objectCommandObject); Response response = pipeliningBase.eval(script, "key"); assertThat(commands, contains(objectCommandObject)); assertThat(response, is(predefinedResponse)); } @Test public void testEvalWithSampleKeyBinary() { byte[] script = "return 'Hello, world!'".getBytes(); byte[] sampleKey = "sampleKey".getBytes(); when(commandObjects.eval(script, sampleKey)).thenReturn(objectCommandObject); Response response = pipeliningBase.eval(script, sampleKey); assertThat(commands, contains(objectCommandObject)); assertThat(response, is(predefinedResponse)); } @Test public void testEvalReadonly() { String script = "return KEYS[1] .. ARGV[1]"; List keys = Collections.singletonList("key"); List args = Collections.singletonList("arg"); when(commandObjects.evalReadonly(script, keys, args)).thenReturn(objectCommandObject); Response response = pipeliningBase.evalReadonly(script, keys, args); assertThat(commands, contains(objectCommandObject)); assertThat(response, is(predefinedResponse)); } @Test public void testEvalReadonlyBinary() { byte[] script = "return {KEYS[1], ARGV[1]}".getBytes(); List keys = Collections.singletonList("key1".getBytes()); List args = Collections.singletonList("arg1".getBytes()); when(commandObjects.evalReadonly(script, keys, args)).thenReturn(objectCommandObject); Response response = pipeliningBase.evalReadonly(script, keys, args); assertThat(commands, contains(objectCommandObject)); assertThat(response, is(predefinedResponse)); } @Test public void testEvalsha() { String sha1 = "somehash"; when(commandObjects.evalsha(sha1)).thenReturn(objectCommandObject); Response response = pipeliningBase.evalsha(sha1); assertThat(commands, contains(objectCommandObject)); assertThat(response, is(predefinedResponse)); } @Test public void testEvalshaBinary() { byte[] sha1 = "abcdef1234567890".getBytes(); when(commandObjects.evalsha(sha1)).thenReturn(objectCommandObject); Response response = pipeliningBase.evalsha(sha1); assertThat(commands, contains(objectCommandObject)); assertThat(response, is(predefinedResponse)); } @Test public void testEvalshaWithParams() { String sha1 = "somehash"; int keyCount = 1; when(commandObjects.evalsha(sha1, keyCount, "key", "arg")).thenReturn(objectCommandObject); Response response = pipeliningBase.evalsha(sha1, keyCount, "key", "arg"); assertThat(commands, contains(objectCommandObject)); assertThat(response, is(predefinedResponse)); } @Test public void testEvalshaWithParamsBinary() { byte[] sha1 = "abcdef1234567890".getBytes(); int keyCount = 1; byte[] param1 = "key1".getBytes(); when(commandObjects.evalsha(sha1, keyCount, param1)).thenReturn(objectCommandObject); Response response = pipeliningBase.evalsha(sha1, keyCount, param1); assertThat(commands, contains(objectCommandObject)); assertThat(response, is(predefinedResponse)); } @Test public void testEvalshaWithLists() { String sha1 = "somehash"; List keys = Collections.singletonList("key"); List args = Collections.singletonList("arg"); when(commandObjects.evalsha(sha1, keys, args)).thenReturn(objectCommandObject); Response response = pipeliningBase.evalsha(sha1, keys, args); assertThat(commands, contains(objectCommandObject)); assertThat(response, is(predefinedResponse)); } @Test public void testEvalshaWithListsBinary() { byte[] sha1 = "abcdef1234567890".getBytes(); List keys = Collections.singletonList("key1".getBytes()); List args = Collections.singletonList("arg1".getBytes()); when(commandObjects.evalsha(sha1, keys, args)).thenReturn(objectCommandObject); Response response = pipeliningBase.evalsha(sha1, keys, args); assertThat(commands, contains(objectCommandObject)); assertThat(response, is(predefinedResponse)); } @Test public void testEvalshaWithSampleKey() { String sha1 = "somehash"; when(commandObjects.evalsha(sha1, "key")).thenReturn(objectCommandObject); Response response = pipeliningBase.evalsha(sha1, "key"); assertThat(commands, contains(objectCommandObject)); assertThat(response, is(predefinedResponse)); } @Test public void testEvalshaWithSampleKeyBinary() { byte[] sha1 = "abcdef1234567890".getBytes(); byte[] sampleKey = "sampleKey".getBytes(); when(commandObjects.evalsha(sha1, sampleKey)).thenReturn(objectCommandObject); Response response = pipeliningBase.evalsha(sha1, sampleKey); assertThat(commands, contains(objectCommandObject)); assertThat(response, is(predefinedResponse)); } @Test public void testEvalshaReadonly() { String sha1 = "somehash"; List keys = Collections.singletonList("key"); List args = Collections.singletonList("arg"); when(commandObjects.evalshaReadonly(sha1, keys, args)).thenReturn(objectCommandObject); Response response = pipeliningBase.evalshaReadonly(sha1, keys, args); assertThat(commands, contains(objectCommandObject)); assertThat(response, is(predefinedResponse)); } @Test public void testEvalshaReadonlyBinary() { byte[] sha1 = "abcdef1234567890".getBytes(); List keys = Collections.singletonList("key1".getBytes()); List args = Collections.singletonList("arg1".getBytes()); when(commandObjects.evalshaReadonly(sha1, keys, args)).thenReturn(objectCommandObject); Response response = pipeliningBase.evalshaReadonly(sha1, keys, args); assertThat(commands, contains(objectCommandObject)); assertThat(response, is(predefinedResponse)); } @Test public void testFcall() { String name = "functionName"; List keys = Collections.singletonList("key"); List args = Collections.singletonList("arg"); when(commandObjects.fcall(name, keys, args)).thenReturn(objectCommandObject); Response response = pipeliningBase.fcall(name, keys, args); assertThat(commands, contains(objectCommandObject)); assertThat(response, is(predefinedResponse)); } @Test public void testFcallBinary() { byte[] name = "functionName".getBytes(); List keys = Collections.singletonList("key".getBytes()); List args = Collections.singletonList("arg".getBytes()); when(commandObjects.fcall(name, keys, args)).thenReturn(objectCommandObject); Response response = pipeliningBase.fcall(name, keys, args); assertThat(commands, contains(objectCommandObject)); assertThat(response, is(predefinedResponse)); } @Test public void testFcallReadonly() { String name = "functionName"; List keys = Collections.singletonList("key"); List args = Collections.singletonList("arg"); when(commandObjects.fcallReadonly(name, keys, args)).thenReturn(objectCommandObject); Response response = pipeliningBase.fcallReadonly(name, keys, args); assertThat(commands, contains(objectCommandObject)); assertThat(response, is(predefinedResponse)); } @Test public void testFcallReadonlyBinary() { byte[] name = "functionName".getBytes(); List keys = Collections.singletonList("key".getBytes()); List args = Collections.singletonList("arg".getBytes()); when(commandObjects.fcallReadonly(name, keys, args)).thenReturn(objectCommandObject); Response response = pipeliningBase.fcallReadonly(name, keys, args); assertThat(commands, contains(objectCommandObject)); assertThat(response, is(predefinedResponse)); } @Test public void testFunctionDelete() { String libraryName = "libraryName"; when(commandObjects.functionDelete(libraryName)).thenReturn(stringCommandObject); Response response = pipeliningBase.functionDelete(libraryName); assertThat(commands, contains(stringCommandObject)); assertThat(response, is(predefinedResponse)); } @Test public void testFunctionDeleteBinary() { byte[] libraryName = "libraryName".getBytes(); when(commandObjects.functionDelete(libraryName)).thenReturn(stringCommandObject); Response response = pipeliningBase.functionDelete(libraryName); assertThat(commands, contains(stringCommandObject)); assertThat(response, is(predefinedResponse)); } @Test public void testFunctionDump() { when(commandObjects.functionDump()).thenReturn(bytesCommandObject); Response response = pipeliningBase.functionDump(); assertThat(commands, contains(bytesCommandObject)); assertThat(response, is(predefinedResponse)); } @Test public void testFunctionFlush() { when(commandObjects.functionFlush()).thenReturn(stringCommandObject); Response response = pipeliningBase.functionFlush(); assertThat(commands, contains(stringCommandObject)); assertThat(response, is(predefinedResponse)); } @Test public void testFunctionFlushWithMode() { FlushMode mode = FlushMode.SYNC; when(commandObjects.functionFlush(mode)).thenReturn(stringCommandObject); Response response = pipeliningBase.functionFlush(mode); assertThat(commands, contains(stringCommandObject)); assertThat(response, is(predefinedResponse)); } @Test public void testFunctionKill() { when(commandObjects.functionKill()).thenReturn(stringCommandObject); Response response = pipeliningBase.functionKill(); assertThat(commands, contains(stringCommandObject)); assertThat(response, is(predefinedResponse)); } @Test public void testFunctionList() { when(commandObjects.functionList()).thenReturn(listLibraryInfoCommandObject); Response> response = pipeliningBase.functionList(); assertThat(commands, contains(listLibraryInfoCommandObject)); assertThat(response, is(predefinedResponse)); } @Test public void testFunctionListBinary() { when(commandObjects.functionListBinary()).thenReturn(listObjectCommandObject); Response> response = pipeliningBase.functionListBinary(); assertThat(commands, contains(listObjectCommandObject)); assertThat(response, is(predefinedResponse)); } @Test public void testFunctionListWithPattern() { String libraryNamePattern = "lib*"; when(commandObjects.functionList(libraryNamePattern)).thenReturn(listLibraryInfoCommandObject); Response> response = pipeliningBase.functionList(libraryNamePattern); assertThat(commands, contains(listLibraryInfoCommandObject)); assertThat(response, is(predefinedResponse)); } @Test public void testFunctionListWithPatternBinary() { byte[] libraryNamePattern = "lib*".getBytes(); when(commandObjects.functionList(libraryNamePattern)).thenReturn(listObjectCommandObject); Response> response = pipeliningBase.functionList(libraryNamePattern); assertThat(commands, contains(listObjectCommandObject)); assertThat(response, is(predefinedResponse)); } @Test public void testFunctionListWithCode() { when(commandObjects.functionListWithCode()).thenReturn(listLibraryInfoCommandObject); Response> response = pipeliningBase.functionListWithCode(); assertThat(commands, contains(listLibraryInfoCommandObject)); assertThat(response, is(predefinedResponse)); } @Test public void testFunctionListWithCodeBinary() { when(commandObjects.functionListWithCodeBinary()).thenReturn(listObjectCommandObject); Response> response = pipeliningBase.functionListWithCodeBinary(); assertThat(commands, contains(listObjectCommandObject)); assertThat(response, is(predefinedResponse)); } @Test public void testFunctionListWithCodeAndPattern() { String libraryNamePattern = "lib*"; when(commandObjects.functionListWithCode(libraryNamePattern)).thenReturn(listLibraryInfoCommandObject); Response> response = pipeliningBase.functionListWithCode(libraryNamePattern); assertThat(commands, contains(listLibraryInfoCommandObject)); assertThat(response, is(predefinedResponse)); } @Test public void testFunctionListWithCodeAndPatternBinary() { byte[] libraryNamePattern = "lib*".getBytes(); when(commandObjects.functionListWithCode(libraryNamePattern)).thenReturn(listObjectCommandObject); Response> response = pipeliningBase.functionListWithCode(libraryNamePattern); assertThat(commands, contains(listObjectCommandObject)); assertThat(response, is(predefinedResponse)); } @Test public void testFunctionLoad() { String functionCode = "return 'Hello, world!'"; when(commandObjects.functionLoad(functionCode)).thenReturn(stringCommandObject); Response response = pipeliningBase.functionLoad(functionCode); assertThat(commands, contains(stringCommandObject)); assertThat(response, is(predefinedResponse)); } @Test public void testFunctionLoadBinary() { byte[] functionCode = "return 'Hello, world!'".getBytes(); when(commandObjects.functionLoad(functionCode)).thenReturn(stringCommandObject); Response response = pipeliningBase.functionLoad(functionCode); assertThat(commands, contains(stringCommandObject)); assertThat(response, is(predefinedResponse)); } @Test public void testFunctionLoadReplace() { String functionCode = "return 'Hello, world!'"; when(commandObjects.functionLoadReplace(functionCode)).thenReturn(stringCommandObject); Response response = pipeliningBase.functionLoadReplace(functionCode); assertThat(commands, contains(stringCommandObject)); assertThat(response, is(predefinedResponse)); } @Test public void testFunctionLoadReplaceBinary() { byte[] functionCode = "return 'Hello, world!'".getBytes(); when(commandObjects.functionLoadReplace(functionCode)).thenReturn(stringCommandObject); Response response = pipeliningBase.functionLoadReplace(functionCode); assertThat(commands, contains(stringCommandObject)); assertThat(response, is(predefinedResponse)); } @Test public void testFunctionRestore() { byte[] serializedValue = "serialized".getBytes(); when(commandObjects.functionRestore(serializedValue)).thenReturn(stringCommandObject); Response response = pipeliningBase.functionRestore(serializedValue); assertThat(commands, contains(stringCommandObject)); assertThat(response, is(predefinedResponse)); } @Test public void testFunctionRestoreWithPolicy() { byte[] serializedValue = "serialized".getBytes(); FunctionRestorePolicy policy = FunctionRestorePolicy.FLUSH; when(commandObjects.functionRestore(serializedValue, policy)).thenReturn(stringCommandObject); Response response = pipeliningBase.functionRestore(serializedValue, policy); assertThat(commands, contains(stringCommandObject)); assertThat(response, is(predefinedResponse)); } @Test public void testFunctionStats() { when(commandObjects.functionStats()).thenReturn(functionStatsCommandObject); Response response = pipeliningBase.functionStats(); assertThat(commands, contains(functionStatsCommandObject)); assertThat(response, is(predefinedResponse)); } @Test public void testFunctionStatsBinary() { when(commandObjects.functionStatsBinary()).thenReturn(objectCommandObject); Response response = pipeliningBase.functionStatsBinary(); assertThat(commands, contains(objectCommandObject)); assertThat(response, is(predefinedResponse)); } @Test public void testScriptExistsWithKeyAndSha1s() { String[] sha1 = { "somehash1", "somehash2" }; when(commandObjects.scriptExists("key", sha1)).thenReturn(listBooleanCommandObject); Response> response = pipeliningBase.scriptExists("key", sha1); assertThat(commands, contains(listBooleanCommandObject)); assertThat(response, is(predefinedResponse)); } @Test public void testScriptExistsWithKeyAndSha1sBinary() { byte[] sampleKey = "sampleKey".getBytes(); byte[] sha1 = "abcdef1234567890".getBytes(); when(commandObjects.scriptExists(sampleKey, sha1)).thenReturn(listBooleanCommandObject); Response> response = pipeliningBase.scriptExists(sampleKey, sha1); assertThat(commands, contains(listBooleanCommandObject)); assertThat(response, is(predefinedResponse)); } @Test public void testScriptFlush() { when(commandObjects.scriptFlush("key")).thenReturn(stringCommandObject); Response response = pipeliningBase.scriptFlush("key"); assertThat(commands, contains(stringCommandObject)); assertThat(response, is(predefinedResponse)); } @Test public void testScriptFlushBinary() { byte[] sampleKey = "sampleKey".getBytes(); when(commandObjects.scriptFlush(sampleKey)).thenReturn(stringCommandObject); Response response = pipeliningBase.scriptFlush(sampleKey); assertThat(commands, contains(stringCommandObject)); assertThat(response, is(predefinedResponse)); } @Test public void testScriptFlushWithMode() { FlushMode flushMode = FlushMode.SYNC; when(commandObjects.scriptFlush("key", flushMode)).thenReturn(stringCommandObject); Response response = pipeliningBase.scriptFlush("key", flushMode); assertThat(commands, contains(stringCommandObject)); assertThat(response, is(predefinedResponse)); } @Test public void testScriptFlushWithModeBinary() { byte[] sampleKey = "sampleKey".getBytes(); FlushMode flushMode = FlushMode.SYNC; when(commandObjects.scriptFlush(sampleKey, flushMode)).thenReturn(stringCommandObject); Response response = pipeliningBase.scriptFlush(sampleKey, flushMode); assertThat(commands, contains(stringCommandObject)); assertThat(response, is(predefinedResponse)); } @Test public void testScriptKill() { when(commandObjects.scriptKill("key")).thenReturn(stringCommandObject); Response response = pipeliningBase.scriptKill("key"); assertThat(commands, contains(stringCommandObject)); assertThat(response, is(predefinedResponse)); } @Test public void testScriptKillBinary() { byte[] sampleKey = "sampleKey".getBytes(); when(commandObjects.scriptKill(sampleKey)).thenReturn(stringCommandObject); Response response = pipeliningBase.scriptKill(sampleKey); assertThat(commands, contains(stringCommandObject)); assertThat(response, is(predefinedResponse)); } @Test public void testScriptLoad() { String script = "return 'Hello, world!'"; when(commandObjects.scriptLoad(script, "key")).thenReturn(stringCommandObject); Response response = pipeliningBase.scriptLoad(script, "key"); assertThat(commands, contains(stringCommandObject)); assertThat(response, is(predefinedResponse)); } @Test public void testScriptLoadBinary() { byte[] script = "return 'Hello, world!'".getBytes(); byte[] sampleKey = "sampleKey".getBytes(); when(commandObjects.scriptLoad(script, sampleKey)).thenReturn(bytesCommandObject); Response response = pipeliningBase.scriptLoad(script, sampleKey); assertThat(commands, contains(bytesCommandObject)); assertThat(response, is(predefinedResponse)); } } ================================================ FILE: src/test/java/redis/clients/jedis/mocked/pipeline/PipeliningBaseSearchAndQueryCommandsTest.java ================================================ package redis.clients.jedis.mocked.pipeline; import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.contains; import static org.hamcrest.Matchers.is; import static org.mockito.Mockito.when; import java.util.Collections; import java.util.List; import java.util.Map; import java.util.Set; import org.junit.jupiter.api.Test; import redis.clients.jedis.Response; import redis.clients.jedis.resps.Tuple; import redis.clients.jedis.search.FTCreateParams; import redis.clients.jedis.search.FTSearchParams; import redis.clients.jedis.search.FTSpellCheckParams; import redis.clients.jedis.search.IndexOptions; import redis.clients.jedis.search.Query; import redis.clients.jedis.search.Schema; import redis.clients.jedis.search.SearchResult; import redis.clients.jedis.search.aggr.AggregationBuilder; import redis.clients.jedis.search.aggr.AggregationResult; import redis.clients.jedis.search.schemafields.SchemaField; import redis.clients.jedis.search.schemafields.TextField; public class PipeliningBaseSearchAndQueryCommandsTest extends PipeliningBaseMockedTestBase { @Test public void testFtAggregate() { AggregationBuilder aggr = new AggregationBuilder().groupBy("@field"); when(commandObjects.ftAggregate("myIndex", aggr)).thenReturn(aggregationResultCommandObject); Response response = pipeliningBase.ftAggregate("myIndex", aggr); assertThat(commands, contains(aggregationResultCommandObject)); assertThat(response, is(predefinedResponse)); } @Test public void testFtAliasAdd() { when(commandObjects.ftAliasAdd("myAlias", "myIndex")).thenReturn(stringCommandObject); Response response = pipeliningBase.ftAliasAdd("myAlias", "myIndex"); assertThat(commands, contains(stringCommandObject)); assertThat(response, is(predefinedResponse)); } @Test public void testFtAliasDel() { when(commandObjects.ftAliasDel("myAlias")).thenReturn(stringCommandObject); Response response = pipeliningBase.ftAliasDel("myAlias"); assertThat(commands, contains(stringCommandObject)); assertThat(response, is(predefinedResponse)); } @Test public void testFtAliasUpdate() { when(commandObjects.ftAliasUpdate("myAlias", "myIndex")).thenReturn(stringCommandObject); Response response = pipeliningBase.ftAliasUpdate("myAlias", "myIndex"); assertThat(commands, contains(stringCommandObject)); assertThat(response, is(predefinedResponse)); } @Test public void testFtAlterWithSchema() { Schema schema = new Schema().addField(new Schema.Field("newField", Schema.FieldType.TEXT)); when(commandObjects.ftAlter("myIndex", schema)).thenReturn(stringCommandObject); Response response = pipeliningBase.ftAlter("myIndex", schema); assertThat(commands, contains(stringCommandObject)); assertThat(response, is(predefinedResponse)); } @Test public void testFtAlterWithSchemaFields() { Iterable schemaFields = Collections.singletonList(new TextField("newField")); when(commandObjects.ftAlter("myIndex", schemaFields)).thenReturn(stringCommandObject); Response response = pipeliningBase.ftAlter("myIndex", schemaFields); assertThat(commands, contains(stringCommandObject)); assertThat(response, is(predefinedResponse)); } @Test public void testFtConfigGet() { when(commandObjects.ftConfigGet("TIMEOUT")).thenReturn(mapStringObjectCommandObject); Response> response = pipeliningBase.ftConfigGet("TIMEOUT"); assertThat(commands, contains(mapStringObjectCommandObject)); assertThat(response, is(predefinedResponse)); } @Test public void testFtConfigGetWithIndexName() { when(commandObjects.ftConfigGet("myIndex", "TIMEOUT")).thenReturn(mapStringObjectCommandObject); Response> response = pipeliningBase.ftConfigGet("myIndex", "TIMEOUT"); assertThat(commands, contains(mapStringObjectCommandObject)); assertThat(response, is(predefinedResponse)); } @Test public void testFtConfigSet() { when(commandObjects.ftConfigSet("TIMEOUT", "100")).thenReturn(stringCommandObject); Response response = pipeliningBase.ftConfigSet("TIMEOUT", "100"); assertThat(commands, contains(stringCommandObject)); assertThat(response, is(predefinedResponse)); } @Test public void testFtConfigSetWithIndexName() { when(commandObjects.ftConfigSet("myIndex", "TIMEOUT", "100")).thenReturn(stringCommandObject); Response response = pipeliningBase.ftConfigSet("myIndex", "TIMEOUT", "100"); assertThat(commands, contains(stringCommandObject)); assertThat(response, is(predefinedResponse)); } @Test public void testFtCreateWithOptionsAndSchema() { IndexOptions indexOptions = IndexOptions.defaultOptions(); Schema schema = new Schema().addField(new Schema.Field("myField", Schema.FieldType.TEXT)); when(commandObjects.ftCreate("myIndex", indexOptions, schema)).thenReturn(stringCommandObject); Response response = pipeliningBase.ftCreate("myIndex", indexOptions, schema); assertThat(commands, contains(stringCommandObject)); assertThat(response, is(predefinedResponse)); } @Test public void testFtCreateWithCreateParamsAndSchemaFields() { FTCreateParams createParams = FTCreateParams.createParams(); Iterable schemaFields = Collections.singletonList(new TextField("myField")); when(commandObjects.ftCreate("myIndex", createParams, schemaFields)).thenReturn(stringCommandObject); Response response = pipeliningBase.ftCreate("myIndex", createParams, schemaFields); assertThat(commands, contains(stringCommandObject)); assertThat(response, is(predefinedResponse)); } @Test public void testFtDictAdd() { String[] terms = { "term1", "term2" }; when(commandObjects.ftDictAdd("myDict", terms)).thenReturn(longCommandObject); Response response = pipeliningBase.ftDictAdd("myDict", terms); assertThat(commands, contains(longCommandObject)); assertThat(response, is(predefinedResponse)); } @Test public void testFtDictAddBySampleKey() { String[] terms = { "term1", "term2" }; when(commandObjects.ftDictAddBySampleKey("myIndex", "myDict", terms)).thenReturn(longCommandObject); Response response = pipeliningBase.ftDictAddBySampleKey("myIndex", "myDict", terms); assertThat(commands, contains(longCommandObject)); assertThat(response, is(predefinedResponse)); } @Test public void testFtDictDel() { String[] terms = { "term1", "term2" }; when(commandObjects.ftDictDel("myDict", terms)).thenReturn(longCommandObject); Response response = pipeliningBase.ftDictDel("myDict", terms); assertThat(commands, contains(longCommandObject)); assertThat(response, is(predefinedResponse)); } @Test public void testFtDictDelBySampleKey() { String[] terms = { "term1", "term2" }; when(commandObjects.ftDictDelBySampleKey("myIndex", "myDict", terms)).thenReturn(longCommandObject); Response response = pipeliningBase.ftDictDelBySampleKey("myIndex", "myDict", terms); assertThat(commands, contains(longCommandObject)); assertThat(response, is(predefinedResponse)); } @Test public void testFtDictDump() { when(commandObjects.ftDictDump("myDict")).thenReturn(setStringCommandObject); Response> response = pipeliningBase.ftDictDump("myDict"); assertThat(commands, contains(setStringCommandObject)); assertThat(response, is(predefinedResponse)); } @Test public void testFtDictDumpBySampleKey() { when(commandObjects.ftDictDumpBySampleKey("myIndex", "myDict")).thenReturn(setStringCommandObject); Response> response = pipeliningBase.ftDictDumpBySampleKey("myIndex", "myDict"); assertThat(commands, contains(setStringCommandObject)); assertThat(response, is(predefinedResponse)); } @Test public void testFtDropIndex() { when(commandObjects.ftDropIndex("myIndex")).thenReturn(stringCommandObject); Response response = pipeliningBase.ftDropIndex("myIndex"); assertThat(commands, contains(stringCommandObject)); assertThat(response, is(predefinedResponse)); } @Test public void testFtDropIndexDD() { when(commandObjects.ftDropIndexDD("myIndex")).thenReturn(stringCommandObject); Response response = pipeliningBase.ftDropIndexDD("myIndex"); assertThat(commands, contains(stringCommandObject)); assertThat(response, is(predefinedResponse)); } @Test public void testFtExplain() { Query query = new Query("hello world"); when(commandObjects.ftExplain("myIndex", query)).thenReturn(stringCommandObject); Response response = pipeliningBase.ftExplain("myIndex", query); assertThat(commands, contains(stringCommandObject)); assertThat(response, is(predefinedResponse)); } @Test public void testFtExplainCLI() { Query query = new Query("hello world"); when(commandObjects.ftExplainCLI("myIndex", query)).thenReturn(listStringCommandObject); Response> response = pipeliningBase.ftExplainCLI("myIndex", query); assertThat(commands, contains(listStringCommandObject)); assertThat(response, is(predefinedResponse)); } @Test public void testFtInfo() { when(commandObjects.ftInfo("myIndex")).thenReturn(mapStringObjectCommandObject); Response> response = pipeliningBase.ftInfo("myIndex"); assertThat(commands, contains(mapStringObjectCommandObject)); assertThat(response, is(predefinedResponse)); } @Test public void testFtSearch() { String query = "hello world"; when(commandObjects.ftSearch("myIndex", query)).thenReturn(searchResultCommandObject); Response response = pipeliningBase.ftSearch("myIndex", query); assertThat(commands, contains(searchResultCommandObject)); assertThat(response, is(predefinedResponse)); } @Test public void testFtSearchWithParams() { String query = "hello world"; FTSearchParams searchParams = FTSearchParams.searchParams().limit(0, 10); when(commandObjects.ftSearch("myIndex", query, searchParams)).thenReturn(searchResultCommandObject); Response response = pipeliningBase.ftSearch("myIndex", query, searchParams); assertThat(commands, contains(searchResultCommandObject)); assertThat(response, is(predefinedResponse)); } @Test public void testFtSearchWithQueryObject() { Query query = new Query("hello world").limit(0, 10); when(commandObjects.ftSearch("myIndex", query)).thenReturn(searchResultCommandObject); Response response = pipeliningBase.ftSearch("myIndex", query); assertThat(commands, contains(searchResultCommandObject)); assertThat(response, is(predefinedResponse)); } @Test public void testFtSearchWithQueryObjectBinary() { byte[] indexName = "myIndex".getBytes(); Query query = new Query("hello world").limit(0, 10); when(commandObjects.ftSearch(indexName, query)).thenReturn(searchResultCommandObject); Response response = pipeliningBase.ftSearch(indexName, query); assertThat(commands, contains(searchResultCommandObject)); assertThat(response, is(predefinedResponse)); } @Test public void testFtSpellCheck() { String query = "hello world"; when(commandObjects.ftSpellCheck("myIndex", query)).thenReturn(mapStringMapStringDoubleCommandObject); Response>> response = pipeliningBase.ftSpellCheck("myIndex", query); assertThat(commands, contains(mapStringMapStringDoubleCommandObject)); assertThat(response, is(predefinedResponse)); } @Test public void testFtSpellCheckWithParams() { String query = "hello world"; FTSpellCheckParams spellCheckParams = new FTSpellCheckParams().distance(1); when(commandObjects.ftSpellCheck("myIndex", query, spellCheckParams)).thenReturn(mapStringMapStringDoubleCommandObject); Response>> response = pipeliningBase.ftSpellCheck("myIndex", query, spellCheckParams); assertThat(commands, contains(mapStringMapStringDoubleCommandObject)); assertThat(response, is(predefinedResponse)); } @Test public void testFtSynDump() { when(commandObjects.ftSynDump("myIndex")).thenReturn(mapStringListStringCommandObject); Response>> response = pipeliningBase.ftSynDump("myIndex"); assertThat(commands, contains(mapStringListStringCommandObject)); assertThat(response, is(predefinedResponse)); } @Test public void testFtSynUpdate() { String synonymGroupId = "group1"; String[] terms = { "term1", "term2" }; when(commandObjects.ftSynUpdate("myIndex", synonymGroupId, terms)).thenReturn(stringCommandObject); Response response = pipeliningBase.ftSynUpdate("myIndex", synonymGroupId, terms); assertThat(commands, contains(stringCommandObject)); assertThat(response, is(predefinedResponse)); } @Test public void testFtTagVals() { when(commandObjects.ftTagVals("myIndex", "myField")).thenReturn(setStringCommandObject); Response> response = pipeliningBase.ftTagVals("myIndex", "myField"); assertThat(commands, contains(setStringCommandObject)); assertThat(response, is(predefinedResponse)); } @Test public void testFtSugAdd() { when(commandObjects.ftSugAdd("mySug", "hello", 1.0)).thenReturn(longCommandObject); Response response = pipeliningBase.ftSugAdd("mySug", "hello", 1.0); assertThat(commands, contains(longCommandObject)); assertThat(response, is(predefinedResponse)); } @Test public void testFtSugAddIncr() { when(commandObjects.ftSugAddIncr("mySug", "hello", 1.0)).thenReturn(longCommandObject); Response response = pipeliningBase.ftSugAddIncr("mySug", "hello", 1.0); assertThat(commands, contains(longCommandObject)); assertThat(response, is(predefinedResponse)); } @Test public void testFtSugDel() { when(commandObjects.ftSugDel("mySug", "hello")).thenReturn(booleanCommandObject); Response response = pipeliningBase.ftSugDel("mySug", "hello"); assertThat(commands, contains(booleanCommandObject)); assertThat(response, is(predefinedResponse)); } @Test public void testFtSugGet() { when(commandObjects.ftSugGet("mySug", "he")).thenReturn(listStringCommandObject); Response> response = pipeliningBase.ftSugGet("mySug", "he"); assertThat(commands, contains(listStringCommandObject)); assertThat(response, is(predefinedResponse)); } @Test public void testFtSugGetWithFuzzyAndMax() { when(commandObjects.ftSugGet("mySug", "he", true, 10)).thenReturn(listStringCommandObject); Response> response = pipeliningBase.ftSugGet("mySug", "he", true, 10); assertThat(commands, contains(listStringCommandObject)); assertThat(response, is(predefinedResponse)); } @Test public void testFtSugGetWithScores() { when(commandObjects.ftSugGetWithScores("mySug", "he")).thenReturn(listTupleCommandObject); Response> response = pipeliningBase.ftSugGetWithScores("mySug", "he"); assertThat(commands, contains(listTupleCommandObject)); assertThat(response, is(predefinedResponse)); } @Test public void testFtSugGetWithScoresFuzzyMax() { when(commandObjects.ftSugGetWithScores("mySug", "he", true, 10)).thenReturn(listTupleCommandObject); Response> response = pipeliningBase.ftSugGetWithScores("mySug", "he", true, 10); assertThat(commands, contains(listTupleCommandObject)); assertThat(response, is(predefinedResponse)); } @Test public void testFtSugLen() { when(commandObjects.ftSugLen("mySug")).thenReturn(longCommandObject); Response response = pipeliningBase.ftSugLen("mySug"); assertThat(commands, contains(longCommandObject)); assertThat(response, is(predefinedResponse)); } } ================================================ FILE: src/test/java/redis/clients/jedis/mocked/pipeline/PipeliningBaseServerManagementCommandsTest.java ================================================ package redis.clients.jedis.mocked.pipeline; import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.contains; import static org.hamcrest.Matchers.is; import static org.mockito.Mockito.when; import org.junit.jupiter.api.Test; import redis.clients.jedis.Response; public class PipeliningBaseServerManagementCommandsTest extends PipeliningBaseMockedTestBase { @Test public void testMemoryUsage() { when(commandObjects.memoryUsage("key")).thenReturn(longCommandObject); Response response = pipeliningBase.memoryUsage("key"); assertThat(commands, contains(longCommandObject)); assertThat(response, is(predefinedResponse)); } @Test public void testMemoryUsageBinary() { byte[] key = "key".getBytes(); when(commandObjects.memoryUsage(key)).thenReturn(longCommandObject); Response response = pipeliningBase.memoryUsage(key); assertThat(commands, contains(longCommandObject)); assertThat(response, is(predefinedResponse)); } @Test public void testMemoryUsageWithSamples() { when(commandObjects.memoryUsage("key", 10)).thenReturn(longCommandObject); Response response = pipeliningBase.memoryUsage("key", 10); assertThat(commands, contains(longCommandObject)); assertThat(response, is(predefinedResponse)); } @Test public void testMemoryUsageWithSamplesBinary() { byte[] key = "key".getBytes(); int samples = 5; when(commandObjects.memoryUsage(key, samples)).thenReturn(longCommandObject); Response response = pipeliningBase.memoryUsage(key, samples); assertThat(commands, contains(longCommandObject)); assertThat(response, is(predefinedResponse)); } } ================================================ FILE: src/test/java/redis/clients/jedis/mocked/pipeline/PipeliningBaseSetCommandsTest.java ================================================ package redis.clients.jedis.mocked.pipeline; import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.contains; import static org.hamcrest.Matchers.is; import static org.mockito.Mockito.when; import java.util.List; import java.util.Set; import org.junit.jupiter.api.Test; import redis.clients.jedis.Response; import redis.clients.jedis.params.ScanParams; import redis.clients.jedis.resps.ScanResult; public class PipeliningBaseSetCommandsTest extends PipeliningBaseMockedTestBase { @Test public void testSadd() { when(commandObjects.sadd("key", "member1", "member2")).thenReturn(longCommandObject); Response response = pipeliningBase.sadd("key", "member1", "member2"); assertThat(commands, contains(longCommandObject)); assertThat(response, is(predefinedResponse)); } @Test public void testSaddBinary() { byte[] key = "key".getBytes(); byte[] member1 = "member1".getBytes(); byte[] member2 = "member2".getBytes(); when(commandObjects.sadd(key, member1, member2)).thenReturn(longCommandObject); Response response = pipeliningBase.sadd(key, member1, member2); assertThat(commands, contains(longCommandObject)); assertThat(response, is(predefinedResponse)); } @Test public void testScard() { when(commandObjects.scard("key")).thenReturn(longCommandObject); Response response = pipeliningBase.scard("key"); assertThat(commands, contains(longCommandObject)); assertThat(response, is(predefinedResponse)); } @Test public void testScardBinary() { byte[] key = "key".getBytes(); when(commandObjects.scard(key)).thenReturn(longCommandObject); Response response = pipeliningBase.scard(key); assertThat(commands, contains(longCommandObject)); assertThat(response, is(predefinedResponse)); } @Test public void testSdiff() { when(commandObjects.sdiff("key1", "key2")).thenReturn(setStringCommandObject); Response> response = pipeliningBase.sdiff("key1", "key2"); assertThat(commands, contains(setStringCommandObject)); assertThat(response, is(predefinedResponse)); } @Test public void testSdiffBinary() { byte[][] keys = { "key1".getBytes(), "key2".getBytes(), "key3".getBytes() }; when(commandObjects.sdiff(keys)).thenReturn(setBytesCommandObject); Response> response = pipeliningBase.sdiff(keys); assertThat(commands, contains(setBytesCommandObject)); assertThat(response, is(predefinedResponse)); } @Test public void testSdiffstore() { when(commandObjects.sdiffstore("dstKey", "key1", "key2")).thenReturn(longCommandObject); Response response = pipeliningBase.sdiffstore("dstKey", "key1", "key2"); assertThat(commands, contains(longCommandObject)); assertThat(response, is(predefinedResponse)); } @Test public void testSdiffstoreBinary() { byte[] dstkey = "destination".getBytes(); byte[][] keys = { "key1".getBytes(), "key2".getBytes() }; when(commandObjects.sdiffstore(dstkey, keys)).thenReturn(longCommandObject); Response response = pipeliningBase.sdiffstore(dstkey, keys); assertThat(commands, contains(longCommandObject)); assertThat(response, is(predefinedResponse)); } @Test public void testSinter() { when(commandObjects.sinter("key1", "key2")).thenReturn(setStringCommandObject); Response> response = pipeliningBase.sinter("key1", "key2"); assertThat(commands, contains(setStringCommandObject)); assertThat(response, is(predefinedResponse)); } @Test public void testSinterBinary() { byte[][] keys = { "key1".getBytes(), "key2".getBytes() }; when(commandObjects.sinter(keys)).thenReturn(setBytesCommandObject); Response> response = pipeliningBase.sinter(keys); assertThat(commands, contains(setBytesCommandObject)); assertThat(response, is(predefinedResponse)); } @Test public void testSintercard() { when(commandObjects.sintercard("key1", "key2")).thenReturn(longCommandObject); Response response = pipeliningBase.sintercard("key1", "key2"); assertThat(commands, contains(longCommandObject)); assertThat(response, is(predefinedResponse)); } @Test public void testSintercardBinary() { byte[][] keys = { "key1".getBytes(), "key2".getBytes() }; when(commandObjects.sintercard(keys)).thenReturn(longCommandObject); Response response = pipeliningBase.sintercard(keys); assertThat(commands, contains(longCommandObject)); assertThat(response, is(predefinedResponse)); } @Test public void testSintercardWithLimit() { int limit = 1; when(commandObjects.sintercard(limit, "key1", "key2")).thenReturn(longCommandObject); Response response = pipeliningBase.sintercard(limit, "key1", "key2"); assertThat(commands, contains(longCommandObject)); assertThat(response, is(predefinedResponse)); } @Test public void testSintercardWithLimitBinary() { int limit = 2; byte[][] keys = { "key1".getBytes(), "key2".getBytes() }; when(commandObjects.sintercard(limit, keys)).thenReturn(longCommandObject); Response response = pipeliningBase.sintercard(limit, keys); assertThat(commands, contains(longCommandObject)); assertThat(response, is(predefinedResponse)); } @Test public void testSinterstore() { when(commandObjects.sinterstore("dstKey", "key1", "key2")).thenReturn(longCommandObject); Response response = pipeliningBase.sinterstore("dstKey", "key1", "key2"); assertThat(commands, contains(longCommandObject)); assertThat(response, is(predefinedResponse)); } @Test public void testSinterstoreBinary() { byte[] dstkey = "destination".getBytes(); byte[][] keys = { "key1".getBytes(), "key2".getBytes() }; when(commandObjects.sinterstore(dstkey, keys)).thenReturn(longCommandObject); Response response = pipeliningBase.sinterstore(dstkey, keys); assertThat(commands, contains(longCommandObject)); assertThat(response, is(predefinedResponse)); } @Test public void testSismember() { when(commandObjects.sismember("key", "member")).thenReturn(booleanCommandObject); Response response = pipeliningBase.sismember("key", "member"); assertThat(commands, contains(booleanCommandObject)); assertThat(response, is(predefinedResponse)); } @Test public void testSismemberBinary() { byte[] key = "key".getBytes(); byte[] member = "member".getBytes(); when(commandObjects.sismember(key, member)).thenReturn(booleanCommandObject); Response response = pipeliningBase.sismember(key, member); assertThat(commands, contains(booleanCommandObject)); assertThat(response, is(predefinedResponse)); } @Test public void testSmembers() { when(commandObjects.smembers("key")).thenReturn(setStringCommandObject); Response> response = pipeliningBase.smembers("key"); assertThat(commands, contains(setStringCommandObject)); assertThat(response, is(predefinedResponse)); } @Test public void testSmembersBinary() { byte[] key = "key".getBytes(); when(commandObjects.smembers(key)).thenReturn(setBytesCommandObject); Response> response = pipeliningBase.smembers(key); assertThat(commands, contains(setBytesCommandObject)); assertThat(response, is(predefinedResponse)); } @Test public void testSmismember() { when(commandObjects.smismember("key", "member1", "member2")).thenReturn(listBooleanCommandObject); Response> response = pipeliningBase.smismember("key", "member1", "member2"); assertThat(commands, contains(listBooleanCommandObject)); assertThat(response, is(predefinedResponse)); } @Test public void testSmismemberBinary() { byte[] key = "key".getBytes(); byte[] member1 = "member1".getBytes(); byte[] member2 = "member2".getBytes(); when(commandObjects.smismember(key, member1, member2)).thenReturn(listBooleanCommandObject); Response> response = pipeliningBase.smismember(key, member1, member2); assertThat(commands, contains(listBooleanCommandObject)); assertThat(response, is(predefinedResponse)); } @Test public void testSmove() { when(commandObjects.smove("srcKey", "dstKey", "member")).thenReturn(longCommandObject); Response response = pipeliningBase.smove("srcKey", "dstKey", "member"); assertThat(commands, contains(longCommandObject)); assertThat(response, is(predefinedResponse)); } @Test public void testSmoveBinary() { byte[] srckey = "source".getBytes(); byte[] dstkey = "destination".getBytes(); byte[] member = "member".getBytes(); when(commandObjects.smove(srckey, dstkey, member)).thenReturn(longCommandObject); Response response = pipeliningBase.smove(srckey, dstkey, member); assertThat(commands, contains(longCommandObject)); assertThat(response, is(predefinedResponse)); } @Test public void testSpop() { when(commandObjects.spop("key")).thenReturn(stringCommandObject); Response response = pipeliningBase.spop("key"); assertThat(commands, contains(stringCommandObject)); assertThat(response, is(predefinedResponse)); } @Test public void testSpopBinary() { byte[] key = "key".getBytes(); when(commandObjects.spop(key)).thenReturn(bytesCommandObject); Response response = pipeliningBase.spop(key); assertThat(commands, contains(bytesCommandObject)); assertThat(response, is(predefinedResponse)); } @Test public void testSpopCount() { long count = 2; when(commandObjects.spop("key", count)).thenReturn(setStringCommandObject); Response> response = pipeliningBase.spop("key", count); assertThat(commands, contains(setStringCommandObject)); assertThat(response, is(predefinedResponse)); } @Test public void testSpopCountBinary() { byte[] key = "key".getBytes(); long count = 2; when(commandObjects.spop(key, count)).thenReturn(setBytesCommandObject); Response> response = pipeliningBase.spop(key, count); assertThat(commands, contains(setBytesCommandObject)); assertThat(response, is(predefinedResponse)); } @Test public void testSrandmember() { when(commandObjects.srandmember("key")).thenReturn(stringCommandObject); Response response = pipeliningBase.srandmember("key"); assertThat(commands, contains(stringCommandObject)); assertThat(response, is(predefinedResponse)); } @Test public void testSrandmemberBinary() { byte[] key = "key".getBytes(); when(commandObjects.srandmember(key)).thenReturn(bytesCommandObject); Response response = pipeliningBase.srandmember(key); assertThat(commands, contains(bytesCommandObject)); assertThat(response, is(predefinedResponse)); } @Test public void testSrandmemberCount() { int count = 2; when(commandObjects.srandmember("key", count)).thenReturn(listStringCommandObject); Response> response = pipeliningBase.srandmember("key", count); assertThat(commands, contains(listStringCommandObject)); assertThat(response, is(predefinedResponse)); } @Test public void testSrandmemberCountBinary() { byte[] key = "key".getBytes(); int count = 2; when(commandObjects.srandmember(key, count)).thenReturn(listBytesCommandObject); Response> response = pipeliningBase.srandmember(key, count); assertThat(commands, contains(listBytesCommandObject)); assertThat(response, is(predefinedResponse)); } @Test public void testSrem() { when(commandObjects.srem("key", "member1", "member2")).thenReturn(longCommandObject); Response response = pipeliningBase.srem("key", "member1", "member2"); assertThat(commands, contains(longCommandObject)); assertThat(response, is(predefinedResponse)); } @Test public void testSremBinary() { byte[] key = "key".getBytes(); byte[] member1 = "member1".getBytes(); byte[] member2 = "member2".getBytes(); when(commandObjects.srem(key, member1, member2)).thenReturn(longCommandObject); Response response = pipeliningBase.srem(key, member1, member2); assertThat(commands, contains(longCommandObject)); assertThat(response, is(predefinedResponse)); } @Test public void testSscan() { String cursor = "0"; ScanParams params = new ScanParams(); when(commandObjects.sscan("key", cursor, params)).thenReturn(scanResultStringCommandObject); Response> response = pipeliningBase.sscan("key", cursor, params); assertThat(commands, contains(scanResultStringCommandObject)); assertThat(response, is(predefinedResponse)); } @Test public void testSscanBinary() { byte[] key = "key".getBytes(); byte[] cursor = "0".getBytes(); ScanParams params = new ScanParams().match("pattern*").count(10); when(commandObjects.sscan(key, cursor, params)).thenReturn(scanResultBytesCommandObject); Response> response = pipeliningBase.sscan(key, cursor, params); assertThat(commands, contains(scanResultBytesCommandObject)); assertThat(response, is(predefinedResponse)); } @Test public void testSunion() { when(commandObjects.sunion("key1", "key2")).thenReturn(setStringCommandObject); Response> response = pipeliningBase.sunion("key1", "key2"); assertThat(commands, contains(setStringCommandObject)); assertThat(response, is(predefinedResponse)); } @Test public void testSunionBinary() { byte[][] keys = { "key1".getBytes(), "key2".getBytes() }; when(commandObjects.sunion(keys)).thenReturn(setBytesCommandObject); Response> response = pipeliningBase.sunion(keys); assertThat(commands, contains(setBytesCommandObject)); assertThat(response, is(predefinedResponse)); } @Test public void testSunionstore() { when(commandObjects.sunionstore("dstKey", "key1", "key2")).thenReturn(longCommandObject); Response response = pipeliningBase.sunionstore("dstKey", "key1", "key2"); assertThat(commands, contains(longCommandObject)); assertThat(response, is(predefinedResponse)); } @Test public void testSunionstoreBinary() { byte[] dstkey = "destination".getBytes(); byte[][] keys = { "key1".getBytes(), "key2".getBytes() }; when(commandObjects.sunionstore(dstkey, keys)).thenReturn(longCommandObject); Response response = pipeliningBase.sunionstore(dstkey, keys); assertThat(commands, contains(longCommandObject)); assertThat(response, is(predefinedResponse)); } } ================================================ FILE: src/test/java/redis/clients/jedis/mocked/pipeline/PipeliningBaseSortedSetCommandsTest.java ================================================ package redis.clients.jedis.mocked.pipeline; import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.contains; import static org.hamcrest.Matchers.is; import static org.mockito.Mockito.when; import java.util.HashMap; import java.util.List; import java.util.Map; import org.junit.jupiter.api.Test; import redis.clients.jedis.Response; import redis.clients.jedis.args.SortedSetOption; import redis.clients.jedis.params.ScanParams; import redis.clients.jedis.params.ZAddParams; import redis.clients.jedis.params.ZIncrByParams; import redis.clients.jedis.params.ZParams; import redis.clients.jedis.params.ZRangeParams; import redis.clients.jedis.resps.ScanResult; import redis.clients.jedis.resps.Tuple; import redis.clients.jedis.util.KeyValue; public class PipeliningBaseSortedSetCommandsTest extends PipeliningBaseMockedTestBase { @Test public void testBzmpop() { SortedSetOption option = SortedSetOption.MAX; when(commandObjects.bzmpop(1.0, option, "key1", "key2")).thenReturn(keyValueStringListTupleCommandObject); Response>> response = pipeliningBase.bzmpop(1.0, option, "key1", "key2"); assertThat(commands, contains(keyValueStringListTupleCommandObject)); assertThat(response, is(predefinedResponse)); } @Test public void testBzmpopBinary() { double timeout = 1.0; SortedSetOption option = SortedSetOption.MAX; byte[][] keys = { "zset1".getBytes(), "zset2".getBytes() }; when(commandObjects.bzmpop(timeout, option, keys)).thenReturn(keyValueBytesListTupleCommandObject); Response>> response = pipeliningBase.bzmpop(timeout, option, keys); assertThat(commands, contains(keyValueBytesListTupleCommandObject)); assertThat(response, is(predefinedResponse)); } @Test public void testBzmpopWithCount() { SortedSetOption option = SortedSetOption.MAX; int count = 2; when(commandObjects.bzmpop(1.0, option, count, "key1", "key2")).thenReturn(keyValueStringListTupleCommandObject); Response>> response = pipeliningBase.bzmpop(1.0, option, count, "key1", "key2"); assertThat(commands, contains(keyValueStringListTupleCommandObject)); assertThat(response, is(predefinedResponse)); } @Test public void testBzmpopWithCountBinary() { double timeout = 1.0; SortedSetOption option = SortedSetOption.MAX; int count = 2; byte[][] keys = { "zset1".getBytes(), "zset2".getBytes() }; when(commandObjects.bzmpop(timeout, option, count, keys)).thenReturn(keyValueBytesListTupleCommandObject); Response>> response = pipeliningBase.bzmpop(timeout, option, count, keys); assertThat(commands, contains(keyValueBytesListTupleCommandObject)); assertThat(response, is(predefinedResponse)); } @Test public void testBzpopmax() { when(commandObjects.bzpopmax(1.0, "key1", "key2")).thenReturn(keyValueStringTupleCommandObject); Response> response = pipeliningBase.bzpopmax(1.0, "key1", "key2"); assertThat(commands, contains(keyValueStringTupleCommandObject)); assertThat(response, is(predefinedResponse)); } @Test public void testBzpopmaxBinary() { double timeout = 1.0; byte[][] keys = { "zset1".getBytes(), "zset2".getBytes() }; when(commandObjects.bzpopmax(timeout, keys)).thenReturn(keyValueBytesTupleCommandObject); Response> response = pipeliningBase.bzpopmax(timeout, keys); assertThat(commands, contains(keyValueBytesTupleCommandObject)); assertThat(response, is(predefinedResponse)); } @Test public void testBzpopmin() { when(commandObjects.bzpopmin(1.0, "key1", "key2")).thenReturn(keyValueStringTupleCommandObject); Response> response = pipeliningBase.bzpopmin(1.0, "key1", "key2"); assertThat(commands, contains(keyValueStringTupleCommandObject)); assertThat(response, is(predefinedResponse)); } @Test public void testBzpopminBinary() { double timeout = 1.0; byte[][] keys = { "zset1".getBytes(), "zset2".getBytes() }; when(commandObjects.bzpopmin(timeout, keys)).thenReturn(keyValueBytesTupleCommandObject); Response> response = pipeliningBase.bzpopmin(timeout, keys); assertThat(commands, contains(keyValueBytesTupleCommandObject)); assertThat(response, is(predefinedResponse)); } @Test public void testZadd() { when(commandObjects.zadd("key", 1.0, "member")).thenReturn(longCommandObject); Response response = pipeliningBase.zadd("key", 1.0, "member"); assertThat(commands, contains(longCommandObject)); assertThat(response, is(predefinedResponse)); } @Test public void testZaddBinary() { byte[] key = "zset".getBytes(); double score = 1.0; byte[] member = "member".getBytes(); when(commandObjects.zadd(key, score, member)).thenReturn(longCommandObject); Response response = pipeliningBase.zadd(key, score, member); assertThat(commands, contains(longCommandObject)); assertThat(response, is(predefinedResponse)); } @Test public void testZaddWithParams() { ZAddParams params = new ZAddParams(); when(commandObjects.zadd("key", 1.0, "member", params)).thenReturn(longCommandObject); Response response = pipeliningBase.zadd("key", 1.0, "member", params); assertThat(commands, contains(longCommandObject)); assertThat(response, is(predefinedResponse)); } @Test public void testZaddWithParamsBinary() { byte[] key = "zset".getBytes(); double score = 1.0; byte[] member = "member".getBytes(); ZAddParams params = ZAddParams.zAddParams().nx(); when(commandObjects.zadd(key, score, member, params)).thenReturn(longCommandObject); Response response = pipeliningBase.zadd(key, score, member, params); assertThat(commands, contains(longCommandObject)); assertThat(response, is(predefinedResponse)); } @Test public void testZaddMultiple() { Map scoreMembers = new HashMap<>(); scoreMembers.put("member1", 1.0); scoreMembers.put("member2", 2.0); when(commandObjects.zadd("key", scoreMembers)).thenReturn(longCommandObject); Response response = pipeliningBase.zadd("key", scoreMembers); assertThat(commands, contains(longCommandObject)); assertThat(response, is(predefinedResponse)); } @Test public void testZaddMultipleBinary() { byte[] key = "zset".getBytes(); Map scoreMembers = new HashMap<>(); scoreMembers.put("member1".getBytes(), 1.0); scoreMembers.put("member2".getBytes(), 2.0); when(commandObjects.zadd(key, scoreMembers)).thenReturn(longCommandObject); Response response = pipeliningBase.zadd(key, scoreMembers); assertThat(commands, contains(longCommandObject)); assertThat(response, is(predefinedResponse)); } @Test public void testZaddMultipleWithParams() { Map scoreMembers = new HashMap<>(); scoreMembers.put("member1", 1.0); scoreMembers.put("member2", 2.0); ZAddParams params = new ZAddParams(); when(commandObjects.zadd("key", scoreMembers, params)).thenReturn(longCommandObject); Response response = pipeliningBase.zadd("key", scoreMembers, params); assertThat(commands, contains(longCommandObject)); assertThat(response, is(predefinedResponse)); } @Test public void testZaddMultipleWithParamsBinary() { byte[] key = "zset".getBytes(); Map scoreMembers = new HashMap<>(); scoreMembers.put("member1".getBytes(), 1.0); scoreMembers.put("member2".getBytes(), 2.0); ZAddParams params = ZAddParams.zAddParams().nx(); when(commandObjects.zadd(key, scoreMembers, params)).thenReturn(longCommandObject); Response response = pipeliningBase.zadd(key, scoreMembers, params); assertThat(commands, contains(longCommandObject)); assertThat(response, is(predefinedResponse)); } @Test public void testZaddIncr() { ZAddParams params = new ZAddParams(); when(commandObjects.zaddIncr("key", 1.0, "member", params)).thenReturn(doubleCommandObject); Response response = pipeliningBase.zaddIncr("key", 1.0, "member", params); assertThat(commands, contains(doubleCommandObject)); assertThat(response, is(predefinedResponse)); } @Test public void testZaddIncrBinary() { byte[] key = "zset".getBytes(); double score = 1.0; byte[] member = "member".getBytes(); ZAddParams params = ZAddParams.zAddParams().xx(); when(commandObjects.zaddIncr(key, score, member, params)).thenReturn(doubleCommandObject); Response response = pipeliningBase.zaddIncr(key, score, member, params); assertThat(commands, contains(doubleCommandObject)); assertThat(response, is(predefinedResponse)); } @Test public void testZcard() { when(commandObjects.zcard("key")).thenReturn(longCommandObject); Response response = pipeliningBase.zcard("key"); assertThat(commands, contains(longCommandObject)); assertThat(response, is(predefinedResponse)); } @Test public void testZcardBinary() { byte[] key = "zset".getBytes(); when(commandObjects.zcard(key)).thenReturn(longCommandObject); Response response = pipeliningBase.zcard(key); assertThat(commands, contains(longCommandObject)); assertThat(response, is(predefinedResponse)); } @Test public void testZcount() { when(commandObjects.zcount("key", "1", "2")).thenReturn(longCommandObject); Response response = pipeliningBase.zcount("key", "1", "2"); assertThat(commands, contains(longCommandObject)); assertThat(response, is(predefinedResponse)); } @Test public void testZcountBinary() { byte[] key = "zset".getBytes(); byte[] min = "min".getBytes(); byte[] max = "max".getBytes(); when(commandObjects.zcount(key, min, max)).thenReturn(longCommandObject); Response response = pipeliningBase.zcount(key, min, max); assertThat(commands, contains(longCommandObject)); assertThat(response, is(predefinedResponse)); } @Test public void testZcountDouble() { when(commandObjects.zcount("key", 1.0, 2.0)).thenReturn(longCommandObject); Response response = pipeliningBase.zcount("key", 1.0, 2.0); assertThat(commands, contains(longCommandObject)); assertThat(response, is(predefinedResponse)); } @Test public void testZcountDoubleBinary() { byte[] key = "zset".getBytes(); double min = 1.0; double max = 2.0; when(commandObjects.zcount(key, min, max)).thenReturn(longCommandObject); Response response = pipeliningBase.zcount(key, min, max); assertThat(commands, contains(longCommandObject)); assertThat(response, is(predefinedResponse)); } @Test public void testZdiff() { when(commandObjects.zdiff("key1", "key2")).thenReturn(listStringCommandObject); Response> response = pipeliningBase.zdiff("key1", "key2"); assertThat(commands, contains(listStringCommandObject)); assertThat(response, is(predefinedResponse)); } @Test public void testZdiffBinary() { byte[][] keys = { "zset1".getBytes(), "zset2".getBytes() }; when(commandObjects.zdiff(keys)).thenReturn(listBytesCommandObject); Response> response = pipeliningBase.zdiff(keys); assertThat(commands, contains(listBytesCommandObject)); assertThat(response, is(predefinedResponse)); } @Test public void testZdiffWithScores() { when(commandObjects.zdiffWithScores("key1", "key2")).thenReturn(listTupleCommandObject); Response> response = pipeliningBase.zdiffWithScores("key1", "key2"); assertThat(commands, contains(listTupleCommandObject)); assertThat(response, is(predefinedResponse)); } @Test public void testZdiffWithScoresBinary() { byte[][] keys = { "zset1".getBytes(), "zset2".getBytes() }; when(commandObjects.zdiffWithScores(keys)).thenReturn(listTupleCommandObject); Response> response = pipeliningBase.zdiffWithScores(keys); assertThat(commands, contains(listTupleCommandObject)); assertThat(response, is(predefinedResponse)); } @Test public void testZdiffStore() { when(commandObjects.zdiffStore("dstKey", "key1", "key2")).thenReturn(longCommandObject); Response response = pipeliningBase.zdiffStore("dstKey", "key1", "key2"); assertThat(commands, contains(longCommandObject)); assertThat(response, is(predefinedResponse)); } @Test public void testZdiffStoreBinary() { byte[] dstkey = "destZset".getBytes(); byte[][] keys = { "zset1".getBytes(), "zset2".getBytes() }; when(commandObjects.zdiffStore(dstkey, keys)).thenReturn(longCommandObject); Response response = pipeliningBase.zdiffStore(dstkey, keys); assertThat(commands, contains(longCommandObject)); assertThat(response, is(predefinedResponse)); } @Test public void testZdiffstore() { when(commandObjects.zdiffstore("dstKey", "key1", "key2")).thenReturn(longCommandObject); Response response = pipeliningBase.zdiffstore("dstKey", "key1", "key2"); assertThat(commands, contains(longCommandObject)); assertThat(response, is(predefinedResponse)); } @Test public void testZdiffstoreBinary() { byte[] dstkey = "destZset".getBytes(); byte[][] keys = { "zset1".getBytes(), "zset2".getBytes() }; when(commandObjects.zdiffstore(dstkey, keys)).thenReturn(longCommandObject); Response response = pipeliningBase.zdiffstore(dstkey, keys); assertThat(commands, contains(longCommandObject)); assertThat(response, is(predefinedResponse)); } @Test public void testZincrby() { when(commandObjects.zincrby("key", 1.0, "member")).thenReturn(doubleCommandObject); Response response = pipeliningBase.zincrby("key", 1.0, "member"); assertThat(commands, contains(doubleCommandObject)); assertThat(response, is(predefinedResponse)); } @Test public void testZincrbyBinary() { byte[] key = "zset".getBytes(); double increment = 2.0; byte[] member = "member".getBytes(); when(commandObjects.zincrby(key, increment, member)).thenReturn(doubleCommandObject); Response response = pipeliningBase.zincrby(key, increment, member); assertThat(commands, contains(doubleCommandObject)); assertThat(response, is(predefinedResponse)); } @Test public void testZincrbyWithParams() { ZIncrByParams params = new ZIncrByParams(); when(commandObjects.zincrby("key", 1.0, "member", params)).thenReturn(doubleCommandObject); Response response = pipeliningBase.zincrby("key", 1.0, "member", params); assertThat(commands, contains(doubleCommandObject)); assertThat(response, is(predefinedResponse)); } @Test public void testZincrbyWithParamsBinary() { byte[] key = "zset".getBytes(); double increment = 2.0; byte[] member = "member".getBytes(); ZIncrByParams params = ZIncrByParams.zIncrByParams().xx(); when(commandObjects.zincrby(key, increment, member, params)).thenReturn(doubleCommandObject); Response response = pipeliningBase.zincrby(key, increment, member, params); assertThat(commands, contains(doubleCommandObject)); assertThat(response, is(predefinedResponse)); } @Test public void testZinter() { ZParams params = new ZParams(); when(commandObjects.zinter(params, "key1", "key2")).thenReturn(listStringCommandObject); Response> response = pipeliningBase.zinter(params, "key1", "key2"); assertThat(commands, contains(listStringCommandObject)); assertThat(response, is(predefinedResponse)); } @Test public void testZinterBinary() { ZParams params = new ZParams(); byte[][] keys = { "zset1".getBytes(), "zset2".getBytes() }; when(commandObjects.zinter(params, keys)).thenReturn(listBytesCommandObject); Response> response = pipeliningBase.zinter(params, keys); assertThat(commands, contains(listBytesCommandObject)); assertThat(response, is(predefinedResponse)); } @Test public void testZinterWithScores() { ZParams params = new ZParams(); when(commandObjects.zinterWithScores(params, "key1", "key2")).thenReturn(listTupleCommandObject); Response> response = pipeliningBase.zinterWithScores(params, "key1", "key2"); assertThat(commands, contains(listTupleCommandObject)); assertThat(response, is(predefinedResponse)); } @Test public void testZinterWithScoresBinary() { ZParams params = new ZParams(); byte[][] keys = { "zset1".getBytes(), "zset2".getBytes() }; when(commandObjects.zinterWithScores(params, keys)).thenReturn(listTupleCommandObject); Response> response = pipeliningBase.zinterWithScores(params, keys); assertThat(commands, contains(listTupleCommandObject)); assertThat(response, is(predefinedResponse)); } @Test public void testZintercard() { when(commandObjects.zintercard("key1", "key2")).thenReturn(longCommandObject); Response response = pipeliningBase.zintercard("key1", "key2"); assertThat(commands, contains(longCommandObject)); assertThat(response, is(predefinedResponse)); } @Test public void testZintercardBinary() { byte[][] keys = { "zset1".getBytes(), "zset2".getBytes() }; when(commandObjects.zintercard(keys)).thenReturn(longCommandObject); Response response = pipeliningBase.zintercard(keys); assertThat(commands, contains(longCommandObject)); assertThat(response, is(predefinedResponse)); } @Test public void testZintercardWithLimit() { long limit = 2; when(commandObjects.zintercard(limit, "key1", "key2")).thenReturn(longCommandObject); Response response = pipeliningBase.zintercard(limit, "key1", "key2"); assertThat(commands, contains(longCommandObject)); assertThat(response, is(predefinedResponse)); } @Test public void testZintercardWithLimitBinary() { long limit = 2; byte[][] keys = { "zset1".getBytes(), "zset2".getBytes() }; when(commandObjects.zintercard(limit, keys)).thenReturn(longCommandObject); Response response = pipeliningBase.zintercard(limit, keys); assertThat(commands, contains(longCommandObject)); assertThat(response, is(predefinedResponse)); } @Test public void testZinterstore() { when(commandObjects.zinterstore("dstKey", "set1", "set2")).thenReturn(longCommandObject); Response response = pipeliningBase.zinterstore("dstKey", "set1", "set2"); assertThat(commands, contains(longCommandObject)); assertThat(response, is(predefinedResponse)); } @Test public void testZinterstoreBinary() { byte[] dstkey = "destZset".getBytes(); byte[][] sets = { "zset1".getBytes(), "zset2".getBytes() }; when(commandObjects.zinterstore(dstkey, sets)).thenReturn(longCommandObject); Response response = pipeliningBase.zinterstore(dstkey, sets); assertThat(commands, contains(longCommandObject)); assertThat(response, is(predefinedResponse)); } @Test public void testZinterstoreWithParams() { ZParams params = new ZParams(); when(commandObjects.zinterstore("dstKey", params, "set1", "set2")).thenReturn(longCommandObject); Response response = pipeliningBase.zinterstore("dstKey", params, "set1", "set2"); assertThat(commands, contains(longCommandObject)); assertThat(response, is(predefinedResponse)); } @Test public void testZinterstoreWithParamsBinary() { byte[] dstkey = "destZset".getBytes(); ZParams params = new ZParams(); byte[][] sets = { "zset1".getBytes(), "zset2".getBytes() }; when(commandObjects.zinterstore(dstkey, params, sets)).thenReturn(longCommandObject); Response response = pipeliningBase.zinterstore(dstkey, params, sets); assertThat(commands, contains(longCommandObject)); assertThat(response, is(predefinedResponse)); } @Test public void testZlexcount() { when(commandObjects.zlexcount("key", "[a", "[z")).thenReturn(longCommandObject); Response response = pipeliningBase.zlexcount("key", "[a", "[z"); assertThat(commands, contains(longCommandObject)); assertThat(response, is(predefinedResponse)); } @Test public void testZlexcountBinary() { byte[] key = "zset".getBytes(); byte[] min = "[a".getBytes(); byte[] max = "[z".getBytes(); when(commandObjects.zlexcount(key, min, max)).thenReturn(longCommandObject); Response response = pipeliningBase.zlexcount(key, min, max); assertThat(commands, contains(longCommandObject)); assertThat(response, is(predefinedResponse)); } @Test public void testZmpop() { SortedSetOption option = SortedSetOption.MAX; when(commandObjects.zmpop(option, "key1", "key2")).thenReturn(keyValueStringListTupleCommandObject); Response>> response = pipeliningBase.zmpop(option, "key1", "key2"); assertThat(commands, contains(keyValueStringListTupleCommandObject)); assertThat(response, is(predefinedResponse)); } @Test public void testZmpopBinary() { SortedSetOption option = SortedSetOption.MAX; byte[][] keys = { "zset1".getBytes(), "zset2".getBytes() }; when(commandObjects.zmpop(option, keys)).thenReturn(keyValueBytesListTupleCommandObject); Response>> response = pipeliningBase.zmpop(option, keys); assertThat(commands, contains(keyValueBytesListTupleCommandObject)); assertThat(response, is(predefinedResponse)); } @Test public void testZmpopWithCount() { SortedSetOption option = SortedSetOption.MAX; int count = 2; when(commandObjects.zmpop(option, count, "key1", "key2")).thenReturn(keyValueStringListTupleCommandObject); Response>> response = pipeliningBase.zmpop(option, count, "key1", "key2"); assertThat(commands, contains(keyValueStringListTupleCommandObject)); assertThat(response, is(predefinedResponse)); } @Test public void testZmpopWithCountBinary() { SortedSetOption option = SortedSetOption.MAX; int count = 2; byte[][] keys = { "zset1".getBytes(), "zset2".getBytes() }; when(commandObjects.zmpop(option, count, keys)).thenReturn(keyValueBytesListTupleCommandObject); Response>> response = pipeliningBase.zmpop(option, count, keys); assertThat(commands, contains(keyValueBytesListTupleCommandObject)); assertThat(response, is(predefinedResponse)); } @Test public void testZmscore() { when(commandObjects.zmscore("key", "member1", "member2")).thenReturn(listDoubleCommandObject); Response> response = pipeliningBase.zmscore("key", "member1", "member2"); assertThat(commands, contains(listDoubleCommandObject)); assertThat(response, is(predefinedResponse)); } @Test public void testZmscoreBinary() { byte[] key = "zset".getBytes(); byte[][] members = { "member1".getBytes(), "member2".getBytes() }; when(commandObjects.zmscore(key, members)).thenReturn(listDoubleCommandObject); Response> response = pipeliningBase.zmscore(key, members); assertThat(commands, contains(listDoubleCommandObject)); assertThat(response, is(predefinedResponse)); } @Test public void testZpopmax() { when(commandObjects.zpopmax("key")).thenReturn(tupleCommandObject); Response response = pipeliningBase.zpopmax("key"); assertThat(commands, contains(tupleCommandObject)); assertThat(response, is(predefinedResponse)); } @Test public void testZpopmaxBinary() { byte[] key = "zset".getBytes(); when(commandObjects.zpopmax(key)).thenReturn(tupleCommandObject); Response response = pipeliningBase.zpopmax(key); assertThat(commands, contains(tupleCommandObject)); assertThat(response, is(predefinedResponse)); } @Test public void testZpopmaxCount() { int count = 2; when(commandObjects.zpopmax("key", count)).thenReturn(listTupleCommandObject); Response> response = pipeliningBase.zpopmax("key", count); assertThat(commands, contains(listTupleCommandObject)); assertThat(response, is(predefinedResponse)); } @Test public void testZpopmaxCountBinary() { byte[] key = "zset".getBytes(); int count = 2; when(commandObjects.zpopmax(key, count)).thenReturn(listTupleCommandObject); Response> response = pipeliningBase.zpopmax(key, count); assertThat(commands, contains(listTupleCommandObject)); assertThat(response, is(predefinedResponse)); } @Test public void testZpopmin() { when(commandObjects.zpopmin("key")).thenReturn(tupleCommandObject); Response response = pipeliningBase.zpopmin("key"); assertThat(commands, contains(tupleCommandObject)); assertThat(response, is(predefinedResponse)); } @Test public void testZpopminBinary() { byte[] key = "zset".getBytes(); when(commandObjects.zpopmin(key)).thenReturn(tupleCommandObject); Response response = pipeliningBase.zpopmin(key); assertThat(commands, contains(tupleCommandObject)); assertThat(response, is(predefinedResponse)); } @Test public void testZpopminCount() { int count = 2; when(commandObjects.zpopmin("key", count)).thenReturn(listTupleCommandObject); Response> response = pipeliningBase.zpopmin("key", count); assertThat(commands, contains(listTupleCommandObject)); assertThat(response, is(predefinedResponse)); } @Test public void testZpopminCountBinary() { byte[] key = "zset".getBytes(); int count = 2; when(commandObjects.zpopmin(key, count)).thenReturn(listTupleCommandObject); Response> response = pipeliningBase.zpopmin(key, count); assertThat(commands, contains(listTupleCommandObject)); assertThat(response, is(predefinedResponse)); } @Test public void testZrandmember() { when(commandObjects.zrandmember("key")).thenReturn(stringCommandObject); Response response = pipeliningBase.zrandmember("key"); assertThat(commands, contains(stringCommandObject)); assertThat(response, is(predefinedResponse)); } @Test public void testZrandmemberBinary() { byte[] key = "zset".getBytes(); when(commandObjects.zrandmember(key)).thenReturn(bytesCommandObject); Response response = pipeliningBase.zrandmember(key); assertThat(commands, contains(bytesCommandObject)); assertThat(response, is(predefinedResponse)); } @Test public void testZrandmemberCount() { long count = 2; when(commandObjects.zrandmember("key", count)).thenReturn(listStringCommandObject); Response> response = pipeliningBase.zrandmember("key", count); assertThat(commands, contains(listStringCommandObject)); assertThat(response, is(predefinedResponse)); } @Test public void testZrandmemberCountBinary() { byte[] key = "zset".getBytes(); long count = 2; when(commandObjects.zrandmember(key, count)).thenReturn(listBytesCommandObject); Response> response = pipeliningBase.zrandmember(key, count); assertThat(commands, contains(listBytesCommandObject)); assertThat(response, is(predefinedResponse)); } @Test public void testZrandmemberWithScores() { long count = 2; when(commandObjects.zrandmemberWithScores("key", count)).thenReturn(listTupleCommandObject); Response> response = pipeliningBase.zrandmemberWithScores("key", count); assertThat(commands, contains(listTupleCommandObject)); assertThat(response, is(predefinedResponse)); } @Test public void testZrandmemberWithScoresBinary() { byte[] key = "zset".getBytes(); long count = 2; when(commandObjects.zrandmemberWithScores(key, count)).thenReturn(listTupleCommandObject); Response> response = pipeliningBase.zrandmemberWithScores(key, count); assertThat(commands, contains(listTupleCommandObject)); assertThat(response, is(predefinedResponse)); } @Test public void testZrange() { when(commandObjects.zrange("key", 0, -1)).thenReturn(listStringCommandObject); Response> response = pipeliningBase.zrange("key", 0, -1); assertThat(commands, contains(listStringCommandObject)); assertThat(response, is(predefinedResponse)); } @Test public void testZrangeBinary() { byte[] key = "zset".getBytes(); long start = 0; long stop = 1; when(commandObjects.zrange(key, start, stop)).thenReturn(listBytesCommandObject); Response> response = pipeliningBase.zrange(key, start, stop); assertThat(commands, contains(listBytesCommandObject)); assertThat(response, is(predefinedResponse)); } @Test public void testZrangeWithScores() { when(commandObjects.zrangeWithScores("key", 0, -1)).thenReturn(listTupleCommandObject); Response> response = pipeliningBase.zrangeWithScores("key", 0, -1); assertThat(commands, contains(listTupleCommandObject)); assertThat(response, is(predefinedResponse)); } @Test public void testZrangeWithScoresBinary() { byte[] key = "zset".getBytes(); long start = 0; long stop = 1; when(commandObjects.zrangeWithScores(key, start, stop)).thenReturn(listTupleCommandObject); Response> response = pipeliningBase.zrangeWithScores(key, start, stop); assertThat(commands, contains(listTupleCommandObject)); assertThat(response, is(predefinedResponse)); } @Test public void testZrangeWithZRangeParams() { ZRangeParams zRangeParams = new ZRangeParams(1, 2); when(commandObjects.zrange("key", zRangeParams)).thenReturn(listStringCommandObject); Response> response = pipeliningBase.zrange("key", zRangeParams); assertThat(commands, contains(listStringCommandObject)); assertThat(response, is(predefinedResponse)); } @Test public void testZrangeWithZRangeParamsBinary() { byte[] key = "zset".getBytes(); ZRangeParams zRangeParams = ZRangeParams.zrangeParams(0, 1); when(commandObjects.zrange(key, zRangeParams)).thenReturn(listBytesCommandObject); Response> response = pipeliningBase.zrange(key, zRangeParams); assertThat(commands, contains(listBytesCommandObject)); assertThat(response, is(predefinedResponse)); } @Test public void testZrangeWithScoresWithZRangeParams() { ZRangeParams zRangeParams = new ZRangeParams(1, 2); when(commandObjects.zrangeWithScores("key", zRangeParams)).thenReturn(listTupleCommandObject); Response> response = pipeliningBase.zrangeWithScores("key", zRangeParams); assertThat(commands, contains(listTupleCommandObject)); assertThat(response, is(predefinedResponse)); } @Test public void testZrangeWithScoresWithZRangeParamsBinary() { byte[] key = "zset".getBytes(); ZRangeParams zRangeParams = ZRangeParams.zrangeParams(0, 1); when(commandObjects.zrangeWithScores(key, zRangeParams)).thenReturn(listTupleCommandObject); Response> response = pipeliningBase.zrangeWithScores(key, zRangeParams); assertThat(commands, contains(listTupleCommandObject)); assertThat(response, is(predefinedResponse)); } @Test public void testZrangeByLex() { when(commandObjects.zrangeByLex("key", "[a", "[z")).thenReturn(listStringCommandObject); Response> response = pipeliningBase.zrangeByLex("key", "[a", "[z"); assertThat(commands, contains(listStringCommandObject)); assertThat(response, is(predefinedResponse)); } @Test public void testZrangeByLexBinary() { byte[] key = "zset".getBytes(); byte[] min = "[a".getBytes(); byte[] max = "[z".getBytes(); when(commandObjects.zrangeByLex(key, min, max)).thenReturn(listBytesCommandObject); Response> response = pipeliningBase.zrangeByLex(key, min, max); assertThat(commands, contains(listBytesCommandObject)); assertThat(response, is(predefinedResponse)); } @Test public void testZrangeByLexWithOffsetCount() { when(commandObjects.zrangeByLex("key", "[a", "[z", 0, 10)).thenReturn(listStringCommandObject); Response> response = pipeliningBase.zrangeByLex("key", "[a", "[z", 0, 10); assertThat(commands, contains(listStringCommandObject)); assertThat(response, is(predefinedResponse)); } @Test public void testZrangeByLexWithOffsetCountBinary() { byte[] key = "zset".getBytes(); byte[] min = "[a".getBytes(); byte[] max = "[z".getBytes(); int offset = 0; int count = 10; when(commandObjects.zrangeByLex(key, min, max, offset, count)).thenReturn(listBytesCommandObject); Response> response = pipeliningBase.zrangeByLex(key, min, max, offset, count); assertThat(commands, contains(listBytesCommandObject)); assertThat(response, is(predefinedResponse)); } @Test public void testZrangeByScore() { when(commandObjects.zrangeByScore("key", "1", "2")).thenReturn(listStringCommandObject); Response> response = pipeliningBase.zrangeByScore("key", "1", "2"); assertThat(commands, contains(listStringCommandObject)); assertThat(response, is(predefinedResponse)); } @Test public void testZrangeByScoreBinary() { byte[] key = "zset".getBytes(); byte[] min = "1".getBytes(); byte[] max = "2".getBytes(); when(commandObjects.zrangeByScore(key, min, max)).thenReturn(listBytesCommandObject); Response> response = pipeliningBase.zrangeByScore(key, min, max); assertThat(commands, contains(listBytesCommandObject)); assertThat(response, is(predefinedResponse)); } @Test public void testZrangeByScoreDouble() { when(commandObjects.zrangeByScore("key", 1.0, 2.0)).thenReturn(listStringCommandObject); Response> response = pipeliningBase.zrangeByScore("key", 1.0, 2.0); assertThat(commands, contains(listStringCommandObject)); assertThat(response, is(predefinedResponse)); } @Test public void testZrangeByScoreDoubleBinary() { byte[] key = "zset".getBytes(); double min = 1.0; double max = 2.0; when(commandObjects.zrangeByScore(key, min, max)).thenReturn(listBytesCommandObject); Response> response = pipeliningBase.zrangeByScore(key, min, max); assertThat(commands, contains(listBytesCommandObject)); assertThat(response, is(predefinedResponse)); } @Test public void testZrangeByScoreWithOffsetCount() { when(commandObjects.zrangeByScore("key", "1", "2", 0, 1)).thenReturn(listStringCommandObject); Response> response = pipeliningBase.zrangeByScore("key", "1", "2", 0, 1); assertThat(commands, contains(listStringCommandObject)); assertThat(response, is(predefinedResponse)); } @Test public void testZrangeByScoreWithOffsetCountBinary() { byte[] key = "zset".getBytes(); byte[] min = "1".getBytes(); byte[] max = "2".getBytes(); int offset = 0; int count = 2; when(commandObjects.zrangeByScore(key, min, max, offset, count)).thenReturn(listBytesCommandObject); Response> response = pipeliningBase.zrangeByScore(key, min, max, offset, count); assertThat(commands, contains(listBytesCommandObject)); assertThat(response, is(predefinedResponse)); } @Test public void testZrangeByScoreDoubleWithOffsetCount() { when(commandObjects.zrangeByScore("key", 1.0, 2.0, 0, 1)).thenReturn(listStringCommandObject); Response> response = pipeliningBase.zrangeByScore("key", 1.0, 2.0, 0, 1); assertThat(commands, contains(listStringCommandObject)); assertThat(response, is(predefinedResponse)); } @Test public void testZrangeByScoreDoubleWithOffsetCountBinary() { byte[] key = "zset".getBytes(); double min = 1.0; double max = 2.0; int offset = 0; int count = 2; when(commandObjects.zrangeByScore(key, min, max, offset, count)).thenReturn(listBytesCommandObject); Response> response = pipeliningBase.zrangeByScore(key, min, max, offset, count); assertThat(commands, contains(listBytesCommandObject)); assertThat(response, is(predefinedResponse)); } @Test public void testZrangeByScoreWithScores() { when(commandObjects.zrangeByScoreWithScores("key", "1", "2")).thenReturn(listTupleCommandObject); Response> response = pipeliningBase.zrangeByScoreWithScores("key", "1", "2"); assertThat(commands, contains(listTupleCommandObject)); assertThat(response, is(predefinedResponse)); } @Test public void testZrangeByScoreWithScoresBinary() { byte[] key = "zset".getBytes(); byte[] min = "1".getBytes(); byte[] max = "2".getBytes(); when(commandObjects.zrangeByScoreWithScores(key, min, max)).thenReturn(listTupleCommandObject); Response> response = pipeliningBase.zrangeByScoreWithScores(key, min, max); assertThat(commands, contains(listTupleCommandObject)); assertThat(response, is(predefinedResponse)); } @Test public void testZrangeByScoreWithScoresDouble() { when(commandObjects.zrangeByScoreWithScores("key", 1.0, 2.0)).thenReturn(listTupleCommandObject); Response> response = pipeliningBase.zrangeByScoreWithScores("key", 1.0, 2.0); assertThat(commands, contains(listTupleCommandObject)); assertThat(response, is(predefinedResponse)); } @Test public void testZrangeByScoreWithScoresDoubleBinary() { byte[] key = "zset".getBytes(); double min = 1.0; double max = 2.0; when(commandObjects.zrangeByScoreWithScores(key, min, max)).thenReturn(listTupleCommandObject); Response> response = pipeliningBase.zrangeByScoreWithScores(key, min, max); assertThat(commands, contains(listTupleCommandObject)); assertThat(response, is(predefinedResponse)); } @Test public void testZrangeByScoreWithScoresWithOffsetCount() { when(commandObjects.zrangeByScoreWithScores("key", "1", "2", 0, 1)).thenReturn(listTupleCommandObject); Response> response = pipeliningBase.zrangeByScoreWithScores("key", "1", "2", 0, 1); assertThat(commands, contains(listTupleCommandObject)); assertThat(response, is(predefinedResponse)); } @Test public void testZrangeByScoreWithScoresWithOffsetCountBinary() { byte[] key = "zset".getBytes(); byte[] min = "1".getBytes(); byte[] max = "2".getBytes(); int offset = 0; int count = 2; when(commandObjects.zrangeByScoreWithScores(key, min, max, offset, count)).thenReturn(listTupleCommandObject); Response> response = pipeliningBase.zrangeByScoreWithScores(key, min, max, offset, count); assertThat(commands, contains(listTupleCommandObject)); assertThat(response, is(predefinedResponse)); } @Test public void testZrangeByScoreWithScoresDoubleWithOffsetCount() { when(commandObjects.zrangeByScoreWithScores("key", 1.0, 2.0, 0, 1)).thenReturn(listTupleCommandObject); Response> response = pipeliningBase.zrangeByScoreWithScores("key", 1.0, 2.0, 0, 1); assertThat(commands, contains(listTupleCommandObject)); assertThat(response, is(predefinedResponse)); } @Test public void testZrangeByScoreWithScoresDoubleWithOffsetCountBinary() { byte[] key = "zset".getBytes(); double min = 1.0; double max = 2.0; int offset = 0; int count = 2; when(commandObjects.zrangeByScoreWithScores(key, min, max, offset, count)).thenReturn(listTupleCommandObject); Response> response = pipeliningBase.zrangeByScoreWithScores(key, min, max, offset, count); assertThat(commands, contains(listTupleCommandObject)); assertThat(response, is(predefinedResponse)); } @Test public void testZrangestore() { ZRangeParams zRangeParams = new ZRangeParams(1, 2); when(commandObjects.zrangestore("dest", "src", zRangeParams)).thenReturn(longCommandObject); Response response = pipeliningBase.zrangestore("dest", "src", zRangeParams); assertThat(commands, contains(longCommandObject)); assertThat(response, is(predefinedResponse)); } @Test public void testZrangestoreBinary() { byte[] dest = "destZset".getBytes(); byte[] src = "srcZset".getBytes(); ZRangeParams zRangeParams = ZRangeParams.zrangeParams(0, 1); when(commandObjects.zrangestore(dest, src, zRangeParams)).thenReturn(longCommandObject); Response response = pipeliningBase.zrangestore(dest, src, zRangeParams); assertThat(commands, contains(longCommandObject)); assertThat(response, is(predefinedResponse)); } @Test public void testZrank() { when(commandObjects.zrank("key", "member")).thenReturn(longCommandObject); Response response = pipeliningBase.zrank("key", "member"); assertThat(commands, contains(longCommandObject)); assertThat(response, is(predefinedResponse)); } @Test public void testZrankBinary() { byte[] key = "zset".getBytes(); byte[] member = "member".getBytes(); when(commandObjects.zrank(key, member)).thenReturn(longCommandObject); Response response = pipeliningBase.zrank(key, member); assertThat(commands, contains(longCommandObject)); assertThat(response, is(predefinedResponse)); } @Test public void testZrankWithScore() { when(commandObjects.zrankWithScore("key", "member")).thenReturn(keyValueLongDoubleCommandObject); Response> response = pipeliningBase.zrankWithScore("key", "member"); assertThat(commands, contains(keyValueLongDoubleCommandObject)); assertThat(response, is(predefinedResponse)); } @Test public void testZrankWithScoreBinary() { byte[] key = "zset".getBytes(); byte[] member = "member".getBytes(); when(commandObjects.zrankWithScore(key, member)).thenReturn(keyValueLongDoubleCommandObject); Response> response = pipeliningBase.zrankWithScore(key, member); assertThat(commands, contains(keyValueLongDoubleCommandObject)); assertThat(response, is(predefinedResponse)); } @Test public void testZrem() { when(commandObjects.zrem("key", "member1", "member2")).thenReturn(longCommandObject); Response response = pipeliningBase.zrem("key", "member1", "member2"); assertThat(commands, contains(longCommandObject)); assertThat(response, is(predefinedResponse)); } @Test public void testZremBinary() { byte[] key = "zset".getBytes(); byte[][] members = { "member1".getBytes(), "member2".getBytes() }; when(commandObjects.zrem(key, members)).thenReturn(longCommandObject); Response response = pipeliningBase.zrem(key, members); assertThat(commands, contains(longCommandObject)); assertThat(response, is(predefinedResponse)); } @Test public void testZremrangeByLex() { when(commandObjects.zremrangeByLex("key", "[a", "[z")).thenReturn(longCommandObject); Response response = pipeliningBase.zremrangeByLex("key", "[a", "[z"); assertThat(commands, contains(longCommandObject)); assertThat(response, is(predefinedResponse)); } @Test public void testZremrangeByLexBinary() { byte[] key = "zset".getBytes(); byte[] min = "[a".getBytes(); byte[] max = "[z".getBytes(); when(commandObjects.zremrangeByLex(key, min, max)).thenReturn(longCommandObject); Response response = pipeliningBase.zremrangeByLex(key, min, max); assertThat(commands, contains(longCommandObject)); assertThat(response, is(predefinedResponse)); } @Test public void testZremrangeByRank() { when(commandObjects.zremrangeByRank("key", 0, 1)).thenReturn(longCommandObject); Response response = pipeliningBase.zremrangeByRank("key", 0, 1); assertThat(commands, contains(longCommandObject)); assertThat(response, is(predefinedResponse)); } @Test public void testZremrangeByRankBinary() { byte[] key = "zset".getBytes(); long start = 0; long stop = 1; when(commandObjects.zremrangeByRank(key, start, stop)).thenReturn(longCommandObject); Response response = pipeliningBase.zremrangeByRank(key, start, stop); assertThat(commands, contains(longCommandObject)); assertThat(response, is(predefinedResponse)); } @Test public void testZremrangeByScore() { when(commandObjects.zremrangeByScore("key", "1", "2")).thenReturn(longCommandObject); Response response = pipeliningBase.zremrangeByScore("key", "1", "2"); assertThat(commands, contains(longCommandObject)); assertThat(response, is(predefinedResponse)); } @Test public void testZremrangeByScoreBinary() { byte[] key = "zset".getBytes(); byte[] min = "1".getBytes(); byte[] max = "2".getBytes(); when(commandObjects.zremrangeByScore(key, min, max)).thenReturn(longCommandObject); Response response = pipeliningBase.zremrangeByScore(key, min, max); assertThat(commands, contains(longCommandObject)); assertThat(response, is(predefinedResponse)); } @Test public void testZremrangeByScoreDouble() { when(commandObjects.zremrangeByScore("key", 1.0, 2.0)).thenReturn(longCommandObject); Response response = pipeliningBase.zremrangeByScore("key", 1.0, 2.0); assertThat(commands, contains(longCommandObject)); assertThat(response, is(predefinedResponse)); } @Test public void testZremrangeByScoreDoubleBinary() { byte[] key = "zset".getBytes(); double min = 1.0; double max = 2.0; when(commandObjects.zremrangeByScore(key, min, max)).thenReturn(longCommandObject); Response response = pipeliningBase.zremrangeByScore(key, min, max); assertThat(commands, contains(longCommandObject)); assertThat(response, is(predefinedResponse)); } @Test public void testZrevrange() { when(commandObjects.zrevrange("key", 0, -1)).thenReturn(listStringCommandObject); Response> response = pipeliningBase.zrevrange("key", 0, -1); assertThat(commands, contains(listStringCommandObject)); assertThat(response, is(predefinedResponse)); } @Test public void testZrevrangeBinary() { byte[] key = "zset".getBytes(); long start = 0; long stop = 1; when(commandObjects.zrevrange(key, start, stop)).thenReturn(listBytesCommandObject); Response> response = pipeliningBase.zrevrange(key, start, stop); assertThat(commands, contains(listBytesCommandObject)); assertThat(response, is(predefinedResponse)); } @Test public void testZrevrangeWithScores() { when(commandObjects.zrevrangeWithScores("key", 0, -1)).thenReturn(listTupleCommandObject); Response> response = pipeliningBase.zrevrangeWithScores("key", 0, -1); assertThat(commands, contains(listTupleCommandObject)); assertThat(response, is(predefinedResponse)); } @Test public void testZrevrangeWithScoresBinary() { byte[] key = "zset".getBytes(); long start = 0; long stop = 1; when(commandObjects.zrevrangeWithScores(key, start, stop)).thenReturn(listTupleCommandObject); Response> response = pipeliningBase.zrevrangeWithScores(key, start, stop); assertThat(commands, contains(listTupleCommandObject)); assertThat(response, is(predefinedResponse)); } @Test public void testZrevrangeByLex() { when(commandObjects.zrevrangeByLex("key", "[z", "[a")).thenReturn(listStringCommandObject); Response> response = pipeliningBase.zrevrangeByLex("key", "[z", "[a"); assertThat(commands, contains(listStringCommandObject)); assertThat(response, is(predefinedResponse)); } @Test public void testZrevrangeByLexBinary() { byte[] key = "zset".getBytes(); byte[] max = "[z".getBytes(); byte[] min = "[a".getBytes(); when(commandObjects.zrevrangeByLex(key, max, min)).thenReturn(listBytesCommandObject); Response> response = pipeliningBase.zrevrangeByLex(key, max, min); assertThat(commands, contains(listBytesCommandObject)); assertThat(response, is(predefinedResponse)); } @Test public void testZrevrangeByLexWithLimit() { when(commandObjects.zrevrangeByLex("key", "[z", "[a", 0, 10)).thenReturn(listStringCommandObject); Response> response = pipeliningBase.zrevrangeByLex("key", "[z", "[a", 0, 10); assertThat(commands, contains(listStringCommandObject)); assertThat(response, is(predefinedResponse)); } @Test public void testZrevrangeByLexWithLimitBinary() { byte[] key = "zset".getBytes(); byte[] max = "[z".getBytes(); byte[] min = "[a".getBytes(); int offset = 0; int count = 10; when(commandObjects.zrevrangeByLex(key, max, min, offset, count)).thenReturn(listBytesCommandObject); Response> response = pipeliningBase.zrevrangeByLex(key, max, min, offset, count); assertThat(commands, contains(listBytesCommandObject)); assertThat(response, is(predefinedResponse)); } @Test public void testZrevrangeByScore() { when(commandObjects.zrevrangeByScore("key", "2", "1")).thenReturn(listStringCommandObject); Response> response = pipeliningBase.zrevrangeByScore("key", "2", "1"); assertThat(commands, contains(listStringCommandObject)); assertThat(response, is(predefinedResponse)); } @Test public void testZrevrangeByScoreBinary() { byte[] key = "zset".getBytes(); byte[] max = "2".getBytes(); byte[] min = "1".getBytes(); when(commandObjects.zrevrangeByScore(key, max, min)).thenReturn(listBytesCommandObject); Response> response = pipeliningBase.zrevrangeByScore(key, max, min); assertThat(commands, contains(listBytesCommandObject)); assertThat(response, is(predefinedResponse)); } @Test public void testZrevrangeByScoreDouble() { when(commandObjects.zrevrangeByScore("key", 2.0, 1.0)).thenReturn(listStringCommandObject); Response> response = pipeliningBase.zrevrangeByScore("key", 2.0, 1.0); assertThat(commands, contains(listStringCommandObject)); assertThat(response, is(predefinedResponse)); } @Test public void testZrevrangeByScoreDoubleBinary() { byte[] key = "zset".getBytes(); double max = 2.0; double min = 1.0; when(commandObjects.zrevrangeByScore(key, max, min)).thenReturn(listBytesCommandObject); Response> response = pipeliningBase.zrevrangeByScore(key, max, min); assertThat(commands, contains(listBytesCommandObject)); assertThat(response, is(predefinedResponse)); } @Test public void testZrevrangeByScoreWithLimit() { when(commandObjects.zrevrangeByScore("key", "2", "1", 0, 1)).thenReturn(listStringCommandObject); Response> response = pipeliningBase.zrevrangeByScore("key", "2", "1", 0, 1); assertThat(commands, contains(listStringCommandObject)); assertThat(response, is(predefinedResponse)); } @Test public void testZrevrangeByScoreWithLimitBinary() { byte[] key = "zset".getBytes(); byte[] max = "2".getBytes(); byte[] min = "1".getBytes(); int offset = 0; int count = 2; when(commandObjects.zrevrangeByScore(key, max, min, offset, count)).thenReturn(listBytesCommandObject); Response> response = pipeliningBase.zrevrangeByScore(key, max, min, offset, count); assertThat(commands, contains(listBytesCommandObject)); assertThat(response, is(predefinedResponse)); } @Test public void testZrevrangeByScoreDoubleWithLimit() { when(commandObjects.zrevrangeByScore("key", 2.0, 1.0, 0, 1)).thenReturn(listStringCommandObject); Response> response = pipeliningBase.zrevrangeByScore("key", 2.0, 1.0, 0, 1); assertThat(commands, contains(listStringCommandObject)); assertThat(response, is(predefinedResponse)); } @Test public void testZrevrangeByScoreDoubleWithLimitBinary() { byte[] key = "zset".getBytes(); double max = 2.0; double min = 1.0; int offset = 0; int count = 2; when(commandObjects.zrevrangeByScore(key, max, min, offset, count)).thenReturn(listBytesCommandObject); Response> response = pipeliningBase.zrevrangeByScore(key, max, min, offset, count); assertThat(commands, contains(listBytesCommandObject)); assertThat(response, is(predefinedResponse)); } @Test public void testZrevrangeByScoreWithScores() { when(commandObjects.zrevrangeByScoreWithScores("key", "2", "1")).thenReturn(listTupleCommandObject); Response> response = pipeliningBase.zrevrangeByScoreWithScores("key", "2", "1"); assertThat(commands, contains(listTupleCommandObject)); assertThat(response, is(predefinedResponse)); } @Test public void testZrevrangeByScoreWithScoresBinary() { byte[] key = "zset".getBytes(); byte[] max = "2".getBytes(); byte[] min = "1".getBytes(); when(commandObjects.zrevrangeByScoreWithScores(key, max, min)).thenReturn(listTupleCommandObject); Response> response = pipeliningBase.zrevrangeByScoreWithScores(key, max, min); assertThat(commands, contains(listTupleCommandObject)); assertThat(response, is(predefinedResponse)); } @Test public void testZrevrangeByScoreWithScoresDouble() { when(commandObjects.zrevrangeByScoreWithScores("key", 2.0, 1.0)).thenReturn(listTupleCommandObject); Response> response = pipeliningBase.zrevrangeByScoreWithScores("key", 2.0, 1.0); assertThat(commands, contains(listTupleCommandObject)); assertThat(response, is(predefinedResponse)); } @Test public void testZrevrangeByScoreWithScoresDoubleBinary() { byte[] key = "zset".getBytes(); double max = 2.0; double min = 1.0; when(commandObjects.zrevrangeByScoreWithScores(key, max, min)).thenReturn(listTupleCommandObject); Response> response = pipeliningBase.zrevrangeByScoreWithScores(key, max, min); assertThat(commands, contains(listTupleCommandObject)); assertThat(response, is(predefinedResponse)); } @Test public void testZrevrangeByScoreWithScoresWithLimit() { when(commandObjects.zrevrangeByScoreWithScores("key", "2", "1", 0, 1)).thenReturn(listTupleCommandObject); Response> response = pipeliningBase.zrevrangeByScoreWithScores("key", "2", "1", 0, 1); assertThat(commands, contains(listTupleCommandObject)); assertThat(response, is(predefinedResponse)); } @Test public void testZrevrangeByScoreWithScoresWithLimitBinary() { byte[] key = "zset".getBytes(); byte[] max = "2".getBytes(); byte[] min = "1".getBytes(); int offset = 0; int count = 2; when(commandObjects.zrevrangeByScoreWithScores(key, max, min, offset, count)).thenReturn(listTupleCommandObject); Response> response = pipeliningBase.zrevrangeByScoreWithScores(key, max, min, offset, count); assertThat(commands, contains(listTupleCommandObject)); assertThat(response, is(predefinedResponse)); } @Test public void testZrevrangeByScoreWithScoresDoubleWithLimit() { when(commandObjects.zrevrangeByScoreWithScores("key", 2.0, 1.0, 0, 1)).thenReturn(listTupleCommandObject); Response> response = pipeliningBase.zrevrangeByScoreWithScores("key", 2.0, 1.0, 0, 1); assertThat(commands, contains(listTupleCommandObject)); assertThat(response, is(predefinedResponse)); } @Test public void testZrevrangeByScoreWithScoresDoubleWithLimitBinary() { byte[] key = "zset".getBytes(); double max = 2.0; double min = 1.0; int offset = 0; int count = 2; when(commandObjects.zrevrangeByScoreWithScores(key, max, min, offset, count)).thenReturn(listTupleCommandObject); Response> response = pipeliningBase.zrevrangeByScoreWithScores(key, max, min, offset, count); assertThat(commands, contains(listTupleCommandObject)); assertThat(response, is(predefinedResponse)); } @Test public void testZrevrank() { when(commandObjects.zrevrank("key", "member")).thenReturn(longCommandObject); Response response = pipeliningBase.zrevrank("key", "member"); assertThat(commands, contains(longCommandObject)); assertThat(response, is(predefinedResponse)); } @Test public void testZrevrankBinary() { byte[] key = "zset".getBytes(); byte[] member = "member".getBytes(); when(commandObjects.zrevrank(key, member)).thenReturn(longCommandObject); Response response = pipeliningBase.zrevrank(key, member); assertThat(commands, contains(longCommandObject)); assertThat(response, is(predefinedResponse)); } @Test public void testZrevrankWithScore() { when(commandObjects.zrevrankWithScore("key", "member")).thenReturn(keyValueLongDoubleCommandObject); Response> response = pipeliningBase.zrevrankWithScore("key", "member"); assertThat(commands, contains(keyValueLongDoubleCommandObject)); assertThat(response, is(predefinedResponse)); } @Test public void testZrevrankWithScoreBinary() { byte[] key = "zset".getBytes(); byte[] member = "member".getBytes(); when(commandObjects.zrevrankWithScore(key, member)).thenReturn(keyValueLongDoubleCommandObject); Response> response = pipeliningBase.zrevrankWithScore(key, member); assertThat(commands, contains(keyValueLongDoubleCommandObject)); assertThat(response, is(predefinedResponse)); } @Test public void testZscan() { ScanParams params = new ScanParams(); when(commandObjects.zscan("key", "0", params)).thenReturn(scanResultTupleCommandObject); Response> response = pipeliningBase.zscan("key", "0", params); assertThat(commands, contains(scanResultTupleCommandObject)); assertThat(response, is(predefinedResponse)); } @Test public void testZscanBinary() { byte[] key = "zset".getBytes(); byte[] cursor = "0".getBytes(); ScanParams params = new ScanParams(); when(commandObjects.zscan(key, cursor, params)).thenReturn(scanResultTupleCommandObject); Response> response = pipeliningBase.zscan(key, cursor, params); assertThat(commands, contains(scanResultTupleCommandObject)); assertThat(response, is(predefinedResponse)); } @Test public void testZscore() { when(commandObjects.zscore("key", "member")).thenReturn(doubleCommandObject); Response response = pipeliningBase.zscore("key", "member"); assertThat(commands, contains(doubleCommandObject)); assertThat(response, is(predefinedResponse)); } @Test public void testZscoreBinary() { byte[] key = "zset".getBytes(); byte[] member = "member".getBytes(); when(commandObjects.zscore(key, member)).thenReturn(doubleCommandObject); Response response = pipeliningBase.zscore(key, member); assertThat(commands, contains(doubleCommandObject)); assertThat(response, is(predefinedResponse)); } @Test public void testZunion() { ZParams params = new ZParams(); when(commandObjects.zunion(params, "key1", "key2")).thenReturn(listStringCommandObject); Response> response = pipeliningBase.zunion(params, "key1", "key2"); assertThat(commands, contains(listStringCommandObject)); assertThat(response, is(predefinedResponse)); } @Test public void testZunionBinary() { ZParams params = new ZParams(); byte[][] keys = { "zset1".getBytes(), "zset2".getBytes() }; when(commandObjects.zunion(params, keys)).thenReturn(listBytesCommandObject); Response> response = pipeliningBase.zunion(params, keys); assertThat(commands, contains(listBytesCommandObject)); assertThat(response, is(predefinedResponse)); } @Test public void testZunionWithScores() { ZParams params = new ZParams(); when(commandObjects.zunionWithScores(params, "key1", "key2")).thenReturn(listTupleCommandObject); Response> response = pipeliningBase.zunionWithScores(params, "key1", "key2"); assertThat(commands, contains(listTupleCommandObject)); assertThat(response, is(predefinedResponse)); } @Test public void testZunionWithScoresBinary() { ZParams params = new ZParams(); byte[][] keys = { "zset1".getBytes(), "zset2".getBytes() }; when(commandObjects.zunionWithScores(params, keys)).thenReturn(listTupleCommandObject); Response> response = pipeliningBase.zunionWithScores(params, keys); assertThat(commands, contains(listTupleCommandObject)); assertThat(response, is(predefinedResponse)); } @Test public void testZunionstore() { when(commandObjects.zunionstore("dstKey", "set1", "set2")).thenReturn(longCommandObject); Response response = pipeliningBase.zunionstore("dstKey", "set1", "set2"); assertThat(commands, contains(longCommandObject)); assertThat(response, is(predefinedResponse)); } @Test public void testZunionstoreBinary() { byte[] dstkey = "destZset".getBytes(); byte[][] sets = { "zset1".getBytes(), "zset2".getBytes() }; when(commandObjects.zunionstore(dstkey, sets)).thenReturn(longCommandObject); Response response = pipeliningBase.zunionstore(dstkey, sets); assertThat(commands, contains(longCommandObject)); assertThat(response, is(predefinedResponse)); } @Test public void testZunionstoreWithParams() { ZParams params = new ZParams(); when(commandObjects.zunionstore("dstKey", params, "set1", "set2")).thenReturn(longCommandObject); Response response = pipeliningBase.zunionstore("dstKey", params, "set1", "set2"); assertThat(commands, contains(longCommandObject)); assertThat(response, is(predefinedResponse)); } @Test public void testZunionstoreWithParamsBinary() { byte[] dstkey = "destZset".getBytes(); ZParams params = new ZParams(); byte[][] sets = { "zset1".getBytes(), "zset2".getBytes() }; when(commandObjects.zunionstore(dstkey, params, sets)).thenReturn(longCommandObject); Response response = pipeliningBase.zunionstore(dstkey, params, sets); assertThat(commands, contains(longCommandObject)); assertThat(response, is(predefinedResponse)); } } ================================================ FILE: src/test/java/redis/clients/jedis/mocked/pipeline/PipeliningBaseStreamCommandsTest.java ================================================ package redis.clients.jedis.mocked.pipeline; import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.contains; import static org.hamcrest.Matchers.is; import static org.mockito.Mockito.when; import java.util.AbstractMap; import java.util.HashMap; import java.util.List; import java.util.Map; import org.junit.jupiter.api.Test; import redis.clients.jedis.Response; import redis.clients.jedis.StreamEntryID; import redis.clients.jedis.params.XAddParams; import redis.clients.jedis.params.XAutoClaimParams; import redis.clients.jedis.params.XClaimParams; import redis.clients.jedis.params.XPendingParams; import redis.clients.jedis.params.XReadGroupParams; import redis.clients.jedis.params.XReadParams; import redis.clients.jedis.params.XTrimParams; import redis.clients.jedis.resps.StreamConsumerInfo; import redis.clients.jedis.resps.StreamConsumersInfo; import redis.clients.jedis.resps.StreamEntry; import redis.clients.jedis.resps.StreamFullInfo; import redis.clients.jedis.resps.StreamGroupInfo; import redis.clients.jedis.resps.StreamInfo; import redis.clients.jedis.resps.StreamPendingEntry; import redis.clients.jedis.resps.StreamPendingSummary; public class PipeliningBaseStreamCommandsTest extends PipeliningBaseMockedTestBase { @Test public void testXack() { StreamEntryID[] ids = { new StreamEntryID("1526999352406-0"), new StreamEntryID("1526999352406-1") }; when(commandObjects.xack("key", "group", ids)).thenReturn(longCommandObject); Response response = pipeliningBase.xack("key", "group", ids); assertThat(commands, contains(longCommandObject)); assertThat(response, is(predefinedResponse)); } @Test public void testXackBinary() { byte[] key = "stream".getBytes(); byte[] group = "group".getBytes(); byte[] id1 = "id1".getBytes(); byte[] id2 = "id2".getBytes(); when(commandObjects.xack(key, group, id1, id2)).thenReturn(longCommandObject); Response response = pipeliningBase.xack(key, group, id1, id2); assertThat(commands, contains(longCommandObject)); assertThat(response, is(predefinedResponse)); } @Test public void testXadd() { StreamEntryID id = new StreamEntryID(); Map hash = new HashMap<>(); hash.put("field1", "value1"); when(commandObjects.xadd("key", id, hash)).thenReturn(streamEntryIdCommandObject); Response response = pipeliningBase.xadd("key", id, hash); assertThat(commands, contains(streamEntryIdCommandObject)); assertThat(response, is(predefinedResponse)); } @Test public void testXaddBinary() { byte[] key = "stream".getBytes(); XAddParams params = new XAddParams(); Map hash = new HashMap<>(); hash.put("field1".getBytes(), "value1".getBytes()); when(commandObjects.xadd(key, params, hash)).thenReturn(bytesCommandObject); Response response = pipeliningBase.xadd(key, params, hash); assertThat(commands, contains(bytesCommandObject)); assertThat(response, is(predefinedResponse)); } @Test public void testXaddWithParams() { XAddParams params = new XAddParams(); Map hash = new HashMap<>(); hash.put("field1", "value1"); when(commandObjects.xadd("key", params, hash)).thenReturn(streamEntryIdCommandObject); Response response = pipeliningBase.xadd("key", params, hash); assertThat(commands, contains(streamEntryIdCommandObject)); assertThat(response, is(predefinedResponse)); } @Test public void testXautoclaim() { StreamEntryID start = new StreamEntryID("0-0"); XAutoClaimParams params = new XAutoClaimParams(); when(commandObjects.xautoclaim("key", "group", "consumerName", 10000L, start, params)) .thenReturn(entryStreamEntryIdListStreamEntryCommandObject); Response>> response = pipeliningBase .xautoclaim("key", "group", "consumerName", 10000L, start, params); assertThat(commands, contains(entryStreamEntryIdListStreamEntryCommandObject)); assertThat(response, is(predefinedResponse)); } @Test public void testXautoclaimBinary() { byte[] key = "stream".getBytes(); byte[] groupName = "group".getBytes(); byte[] consumerName = "consumer".getBytes(); long minIdleTime = 10000L; byte[] start = "startId".getBytes(); XAutoClaimParams params = new XAutoClaimParams(); when(commandObjects.xautoclaim(key, groupName, consumerName, minIdleTime, start, params)).thenReturn(listObjectCommandObject); Response> response = pipeliningBase.xautoclaim(key, groupName, consumerName, minIdleTime, start, params); assertThat(commands, contains(listObjectCommandObject)); assertThat(response, is(predefinedResponse)); } @Test public void testXautoclaimJustId() { StreamEntryID start = new StreamEntryID("0-0"); XAutoClaimParams params = new XAutoClaimParams(); when(commandObjects.xautoclaimJustId("key", "group", "consumerName", 10000L, start, params)) .thenReturn(entryStreamEntryIdListStreamEntryIdCommandObject); Response>> response = pipeliningBase .xautoclaimJustId("key", "group", "consumerName", 10000L, start, params); assertThat(commands, contains(entryStreamEntryIdListStreamEntryIdCommandObject)); assertThat(response, is(predefinedResponse)); } @Test public void testXautoclaimJustIdBinary() { byte[] key = "stream".getBytes(); byte[] groupName = "group".getBytes(); byte[] consumerName = "consumer".getBytes(); long minIdleTime = 10000L; byte[] start = "startId".getBytes(); XAutoClaimParams params = new XAutoClaimParams(); when(commandObjects.xautoclaimJustId(key, groupName, consumerName, minIdleTime, start, params)).thenReturn(listObjectCommandObject); Response> response = pipeliningBase.xautoclaimJustId(key, groupName, consumerName, minIdleTime, start, params); assertThat(commands, contains(listObjectCommandObject)); assertThat(response, is(predefinedResponse)); } @Test public void testXclaim() { StreamEntryID[] ids = { new StreamEntryID("1526999352406-0"), new StreamEntryID("1526999352406-1") }; XClaimParams params = new XClaimParams().idle(10000L); when(commandObjects.xclaim("key", "group", "consumerName", 10000L, params, ids)) .thenReturn(listStreamEntryCommandObject); Response> response = pipeliningBase .xclaim("key", "group", "consumerName", 10000L, params, ids); assertThat(commands, contains(listStreamEntryCommandObject)); assertThat(response, is(predefinedResponse)); } @Test public void testXclaimBinary() { byte[] key = "stream".getBytes(); byte[] group = "group".getBytes(); byte[] consumerName = "consumer".getBytes(); long minIdleTime = 10000L; XClaimParams params = new XClaimParams(); byte[] id1 = "id1".getBytes(); byte[] id2 = "id2".getBytes(); when(commandObjects.xclaim(key, group, consumerName, minIdleTime, params, id1, id2)).thenReturn(listBytesCommandObject); Response> response = pipeliningBase.xclaim(key, group, consumerName, minIdleTime, params, id1, id2); assertThat(commands, contains(listBytesCommandObject)); assertThat(response, is(predefinedResponse)); } @Test public void testXclaimJustId() { StreamEntryID[] ids = { new StreamEntryID("1526999352406-0"), new StreamEntryID("1526999352406-1") }; XClaimParams params = new XClaimParams().idle(10000L); when(commandObjects.xclaimJustId("key", "group", "consumerName", 10000L, params, ids)) .thenReturn(listStreamEntryIdCommandObject); Response> response = pipeliningBase .xclaimJustId("key", "group", "consumerName", 10000L, params, ids); assertThat(commands, contains(listStreamEntryIdCommandObject)); assertThat(response, is(predefinedResponse)); } @Test public void testXclaimJustIdBinary() { byte[] key = "stream".getBytes(); byte[] group = "group".getBytes(); byte[] consumerName = "consumer".getBytes(); long minIdleTime = 10000L; XClaimParams params = new XClaimParams(); byte[] id1 = "id1".getBytes(); byte[] id2 = "id2".getBytes(); when(commandObjects.xclaimJustId(key, group, consumerName, minIdleTime, params, id1, id2)).thenReturn(listBytesCommandObject); Response> response = pipeliningBase.xclaimJustId(key, group, consumerName, minIdleTime, params, id1, id2); assertThat(commands, contains(listBytesCommandObject)); assertThat(response, is(predefinedResponse)); } @Test public void testXdel() { StreamEntryID[] ids = { new StreamEntryID("1526999352406-0"), new StreamEntryID("1526999352406-1") }; when(commandObjects.xdel("key", ids)).thenReturn(longCommandObject); Response response = pipeliningBase.xdel("key", ids); assertThat(commands, contains(longCommandObject)); assertThat(response, is(predefinedResponse)); } @Test public void testXdelBinary() { byte[] key = "stream".getBytes(); byte[] id1 = "id1".getBytes(); byte[] id2 = "id2".getBytes(); when(commandObjects.xdel(key, id1, id2)).thenReturn(longCommandObject); Response response = pipeliningBase.xdel(key, id1, id2); assertThat(commands, contains(longCommandObject)); assertThat(response, is(predefinedResponse)); } @Test public void testXgroupCreate() { StreamEntryID id = new StreamEntryID("0-0"); when(commandObjects.xgroupCreate("key", "groupName", id, true)).thenReturn(stringCommandObject); Response response = pipeliningBase.xgroupCreate("key", "groupName", id, true); assertThat(commands, contains(stringCommandObject)); assertThat(response, is(predefinedResponse)); } @Test public void testXgroupCreateBinary() { byte[] key = "stream".getBytes(); byte[] groupName = "group".getBytes(); byte[] id = "id".getBytes(); boolean makeStream = true; when(commandObjects.xgroupCreate(key, groupName, id, makeStream)).thenReturn(stringCommandObject); Response response = pipeliningBase.xgroupCreate(key, groupName, id, makeStream); assertThat(commands, contains(stringCommandObject)); assertThat(response, is(predefinedResponse)); } @Test public void testXgroupCreateConsumer() { when(commandObjects.xgroupCreateConsumer("key", "groupName", "consumerName")) .thenReturn(booleanCommandObject); Response response = pipeliningBase.xgroupCreateConsumer("key", "groupName", "consumerName"); assertThat(commands, contains(booleanCommandObject)); assertThat(response, is(predefinedResponse)); } @Test public void testXgroupCreateConsumerBinary() { byte[] key = "stream".getBytes(); byte[] groupName = "group".getBytes(); byte[] consumerName = "consumer".getBytes(); when(commandObjects.xgroupCreateConsumer(key, groupName, consumerName)).thenReturn(booleanCommandObject); Response response = pipeliningBase.xgroupCreateConsumer(key, groupName, consumerName); assertThat(commands, contains(booleanCommandObject)); assertThat(response, is(predefinedResponse)); } @Test public void testXgroupDelConsumer() { when(commandObjects.xgroupDelConsumer("key", "groupName", "consumerName")) .thenReturn(longCommandObject); Response response = pipeliningBase.xgroupDelConsumer("key", "groupName", "consumerName"); assertThat(commands, contains(longCommandObject)); assertThat(response, is(predefinedResponse)); } @Test public void testXgroupDelConsumerBinary() { byte[] key = "stream".getBytes(); byte[] groupName = "group".getBytes(); byte[] consumerName = "consumer".getBytes(); when(commandObjects.xgroupDelConsumer(key, groupName, consumerName)).thenReturn(longCommandObject); Response response = pipeliningBase.xgroupDelConsumer(key, groupName, consumerName); assertThat(commands, contains(longCommandObject)); assertThat(response, is(predefinedResponse)); } @Test public void testXgroupDestroy() { when(commandObjects.xgroupDestroy("key", "groupName")).thenReturn(longCommandObject); Response response = pipeliningBase.xgroupDestroy("key", "groupName"); assertThat(commands, contains(longCommandObject)); assertThat(response, is(predefinedResponse)); } @Test public void testXgroupDestroyBinary() { byte[] key = "stream".getBytes(); byte[] groupName = "group".getBytes(); when(commandObjects.xgroupDestroy(key, groupName)).thenReturn(longCommandObject); Response response = pipeliningBase.xgroupDestroy(key, groupName); assertThat(commands, contains(longCommandObject)); assertThat(response, is(predefinedResponse)); } @Test public void testXgroupSetID() { StreamEntryID id = new StreamEntryID("0-0"); when(commandObjects.xgroupSetID("key", "groupName", id)).thenReturn(stringCommandObject); Response response = pipeliningBase.xgroupSetID("key", "groupName", id); assertThat(commands, contains(stringCommandObject)); assertThat(response, is(predefinedResponse)); } @Test public void testXgroupSetIDBinary() { byte[] key = "stream".getBytes(); byte[] groupName = "group".getBytes(); byte[] id = "id".getBytes(); when(commandObjects.xgroupSetID(key, groupName, id)).thenReturn(stringCommandObject); Response response = pipeliningBase.xgroupSetID(key, groupName, id); assertThat(commands, contains(stringCommandObject)); assertThat(response, is(predefinedResponse)); } @Test public void testXinfoConsumers() { when(commandObjects.xinfoConsumers("key", "group")).thenReturn(listStreamConsumersInfoCommandObject); Response> response = pipeliningBase.xinfoConsumers("key", "group"); assertThat(commands, contains(listStreamConsumersInfoCommandObject)); assertThat(response, is(predefinedResponse)); } @Test public void testXinfoConsumersBinary() { byte[] key = "stream".getBytes(); byte[] group = "group".getBytes(); when(commandObjects.xinfoConsumers(key, group)).thenReturn(listObjectCommandObject); Response> response = pipeliningBase.xinfoConsumers(key, group); assertThat(commands, contains(listObjectCommandObject)); assertThat(response, is(predefinedResponse)); } @Test public void testXinfoConsumers2() { when(commandObjects.xinfoConsumers2("key", "group")).thenReturn(listStreamConsumerInfoCommandObject); Response> response = pipeliningBase.xinfoConsumers2("key", "group"); assertThat(commands, contains(listStreamConsumerInfoCommandObject)); assertThat(response, is(predefinedResponse)); } @Test public void testXinfoGroups() { when(commandObjects.xinfoGroups("key")).thenReturn(listStreamGroupInfoCommandObject); Response> response = pipeliningBase.xinfoGroups("key"); assertThat(commands, contains(listStreamGroupInfoCommandObject)); assertThat(response, is(predefinedResponse)); } @Test public void testXinfoGroupsBinary() { byte[] key = "stream".getBytes(); when(commandObjects.xinfoGroups(key)).thenReturn(listObjectCommandObject); Response> response = pipeliningBase.xinfoGroups(key); assertThat(commands, contains(listObjectCommandObject)); assertThat(response, is(predefinedResponse)); } @Test public void testXinfoStream() { when(commandObjects.xinfoStream("key")).thenReturn(streamInfoCommandObject); Response response = pipeliningBase.xinfoStream("key"); assertThat(commands, contains(streamInfoCommandObject)); assertThat(response, is(predefinedResponse)); } @Test public void testXinfoStreamBinary() { byte[] key = "stream".getBytes(); when(commandObjects.xinfoStream(key)).thenReturn(objectCommandObject); Response response = pipeliningBase.xinfoStream(key); assertThat(commands, contains(objectCommandObject)); assertThat(response, is(predefinedResponse)); } @Test public void testXinfoStreamFull() { when(commandObjects.xinfoStreamFull("key")).thenReturn(streamFullInfoCommandObject); Response response = pipeliningBase.xinfoStreamFull("key"); assertThat(commands, contains(streamFullInfoCommandObject)); assertThat(response, is(predefinedResponse)); } @Test public void testXinfoStreamFullBinary() { byte[] key = "stream".getBytes(); when(commandObjects.xinfoStreamFull(key)).thenReturn(objectCommandObject); Response response = pipeliningBase.xinfoStreamFull(key); assertThat(commands, contains(objectCommandObject)); assertThat(response, is(predefinedResponse)); } @Test public void testXinfoStreamFullWithCount() { int count = 10; when(commandObjects.xinfoStreamFull("key", count)).thenReturn(streamFullInfoCommandObject); Response response = pipeliningBase.xinfoStreamFull("key", count); assertThat(commands, contains(streamFullInfoCommandObject)); assertThat(response, is(predefinedResponse)); } @Test public void testXinfoStreamFullWithCountBinary() { byte[] key = "stream".getBytes(); int count = 10; when(commandObjects.xinfoStreamFull(key, count)).thenReturn(objectCommandObject); Response response = pipeliningBase.xinfoStreamFull(key, count); assertThat(commands, contains(objectCommandObject)); assertThat(response, is(predefinedResponse)); } @Test public void testXlen() { when(commandObjects.xlen("key")).thenReturn(longCommandObject); Response response = pipeliningBase.xlen("key"); assertThat(commands, contains(longCommandObject)); assertThat(response, is(predefinedResponse)); } @Test public void testXlenBinary() { byte[] key = "stream".getBytes(); when(commandObjects.xlen(key)).thenReturn(longCommandObject); Response response = pipeliningBase.xlen(key); assertThat(commands, contains(longCommandObject)); assertThat(response, is(predefinedResponse)); } @Test public void testXpending() { when(commandObjects.xpending("key", "groupName")).thenReturn(streamPendingSummaryCommandObject); Response response = pipeliningBase.xpending("key", "groupName"); assertThat(commands, contains(streamPendingSummaryCommandObject)); assertThat(response, is(predefinedResponse)); } @Test public void testXpendingBinary() { byte[] key = "stream".getBytes(); byte[] groupName = "group".getBytes(); when(commandObjects.xpending(key, groupName)).thenReturn(objectCommandObject); Response response = pipeliningBase.xpending(key, groupName); assertThat(commands, contains(objectCommandObject)); assertThat(response, is(predefinedResponse)); } @Test public void testXpendingWithParams() { XPendingParams params = new XPendingParams(); when(commandObjects.xpending("key", "groupName", params)).thenReturn(listStreamPendingEntryCommandObject); Response> response = pipeliningBase.xpending("key", "groupName", params); assertThat(commands, contains(listStreamPendingEntryCommandObject)); assertThat(response, is(predefinedResponse)); } @Test public void testXpendingWithParamsBinary() { byte[] key = "stream".getBytes(); byte[] groupName = "group".getBytes(); XPendingParams params = new XPendingParams().count(10); when(commandObjects.xpending(key, groupName, params)).thenReturn(listObjectCommandObject); Response> response = pipeliningBase.xpending(key, groupName, params); assertThat(commands, contains(listObjectCommandObject)); assertThat(response, is(predefinedResponse)); } @Test public void testXrange() { String start = "-"; String end = "+"; when(commandObjects.xrange("key", start, end)).thenReturn(listStreamEntryCommandObject); Response> response = pipeliningBase.xrange("key", start, end); assertThat(commands, contains(listStreamEntryCommandObject)); assertThat(response, is(predefinedResponse)); } @Test public void testXrangeBinary() { byte[] key = "stream".getBytes(); byte[] start = "startId".getBytes(); byte[] end = "endId".getBytes(); when(commandObjects.xrange(key, start, end)).thenReturn(listObjectCommandObject); Response> response = pipeliningBase.xrange(key, start, end); assertThat(commands, contains(listObjectCommandObject)); assertThat(response, is(predefinedResponse)); } @Test public void testXrangeWithCount() { String start = "-"; String end = "+"; int count = 10; when(commandObjects.xrange("key", start, end, count)).thenReturn(listStreamEntryCommandObject); Response> response = pipeliningBase.xrange("key", start, end, count); assertThat(commands, contains(listStreamEntryCommandObject)); assertThat(response, is(predefinedResponse)); } @Test public void testXrangeWithCountBinary() { byte[] key = "stream".getBytes(); byte[] start = "startId".getBytes(); byte[] end = "endId".getBytes(); int count = 10; when(commandObjects.xrange(key, start, end, count)).thenReturn(listObjectCommandObject); Response> response = pipeliningBase.xrange(key, start, end, count); assertThat(commands, contains(listObjectCommandObject)); assertThat(response, is(predefinedResponse)); } @Test public void testXrangeIds() { StreamEntryID start = new StreamEntryID("0-0"); StreamEntryID end = new StreamEntryID("9999999999999-0"); when(commandObjects.xrange("key", start, end)).thenReturn(listStreamEntryCommandObject); Response> response = pipeliningBase.xrange("key", start, end); assertThat(commands, contains(listStreamEntryCommandObject)); assertThat(response, is(predefinedResponse)); } @Test public void testXrangeIdsWithCount() { StreamEntryID start = new StreamEntryID("0-0"); StreamEntryID end = new StreamEntryID("9999999999999-0"); int count = 10; when(commandObjects.xrange("key", start, end, count)).thenReturn(listStreamEntryCommandObject); Response> response = pipeliningBase.xrange("key", start, end, count); assertThat(commands, contains(listStreamEntryCommandObject)); assertThat(response, is(predefinedResponse)); } @Test public void testXread() { XReadParams xReadParams = new XReadParams(); Map streams = new HashMap<>(); streams.put("key1", new StreamEntryID("0-0")); streams.put("key2", new StreamEntryID("0-0")); when(commandObjects.xread(xReadParams, streams)).thenReturn(listEntryStringListStreamEntryCommandObject); Response>>> response = pipeliningBase.xread(xReadParams, streams); assertThat(commands, contains(listEntryStringListStreamEntryCommandObject)); assertThat(response, is(predefinedResponse)); } @Test public void testXreadBinary() { XReadParams xReadParams = new XReadParams(); Map.Entry stream1 = new AbstractMap.SimpleImmutableEntry<>("stream1".getBytes(), "id1".getBytes()); when(commandObjects.xread(xReadParams, stream1)).thenReturn(listObjectCommandObject); Response> response = pipeliningBase.xread(xReadParams, stream1); assertThat(commands, contains(listObjectCommandObject)); assertThat(response, is(predefinedResponse)); } @Test public void testXreadGroup() { XReadGroupParams xReadGroupParams = new XReadGroupParams(); Map streams = new HashMap<>(); streams.put("stream1", new StreamEntryID("0-0")); when(commandObjects.xreadGroup("groupName", "consumer", xReadGroupParams, streams)) .thenReturn(listEntryStringListStreamEntryCommandObject); Response>>> response = pipeliningBase .xreadGroup("groupName", "consumer", xReadGroupParams, streams); assertThat(commands, contains(listEntryStringListStreamEntryCommandObject)); assertThat(response, is(predefinedResponse)); } @Test public void testXreadGroupBinary() { byte[] groupName = "group".getBytes(); byte[] consumer = "consumer".getBytes(); XReadGroupParams xReadGroupParams = new XReadGroupParams(); Map.Entry stream1 = new AbstractMap.SimpleImmutableEntry<>("stream1".getBytes(), "id1".getBytes()); when(commandObjects.xreadGroup(groupName, consumer, xReadGroupParams, stream1)).thenReturn(listObjectCommandObject); Response> response = pipeliningBase.xreadGroup(groupName, consumer, xReadGroupParams, stream1); assertThat(commands, contains(listObjectCommandObject)); assertThat(response, is(predefinedResponse)); } @Test public void testXrevrange() { String end = "+"; String start = "-"; when(commandObjects.xrevrange("key", end, start)).thenReturn(listStreamEntryCommandObject); Response> response = pipeliningBase.xrevrange("key", end, start); assertThat(commands, contains(listStreamEntryCommandObject)); assertThat(response, is(predefinedResponse)); } @Test public void testXrevrangeBinary() { byte[] key = "stream".getBytes(); byte[] end = "endId".getBytes(); byte[] start = "startId".getBytes(); when(commandObjects.xrevrange(key, end, start)).thenReturn(listObjectCommandObject); Response> response = pipeliningBase.xrevrange(key, end, start); assertThat(commands, contains(listObjectCommandObject)); assertThat(response, is(predefinedResponse)); } @Test public void testXrevrangeWithCount() { String end = "+"; String start = "-"; int count = 10; when(commandObjects.xrevrange("key", end, start, count)).thenReturn(listStreamEntryCommandObject); Response> response = pipeliningBase.xrevrange("key", end, start, count); assertThat(commands, contains(listStreamEntryCommandObject)); assertThat(response, is(predefinedResponse)); } @Test public void testXrevrangeWithCountBinary() { byte[] key = "stream".getBytes(); byte[] end = "endId".getBytes(); byte[] start = "startId".getBytes(); int count = 10; when(commandObjects.xrevrange(key, end, start, count)).thenReturn(listObjectCommandObject); Response> response = pipeliningBase.xrevrange(key, end, start, count); assertThat(commands, contains(listObjectCommandObject)); assertThat(response, is(predefinedResponse)); } @Test public void testXrevrangeIds() { StreamEntryID end = new StreamEntryID("9999999999999-0"); StreamEntryID start = new StreamEntryID("0-0"); when(commandObjects.xrevrange("key", end, start)).thenReturn(listStreamEntryCommandObject); Response> response = pipeliningBase.xrevrange("key", end, start); assertThat(commands, contains(listStreamEntryCommandObject)); assertThat(response, is(predefinedResponse)); } @Test public void testXrevrangeIdsWithCount() { StreamEntryID end = new StreamEntryID("9999999999999-0"); StreamEntryID start = new StreamEntryID("0-0"); int count = 10; when(commandObjects.xrevrange("key", end, start, count)).thenReturn(listStreamEntryCommandObject); Response> response = pipeliningBase.xrevrange("key", end, start, count); assertThat(commands, contains(listStreamEntryCommandObject)); assertThat(response, is(predefinedResponse)); } @Test public void testXtrim() { when(commandObjects.xtrim("key", 1000L, true)).thenReturn(longCommandObject); Response response = pipeliningBase.xtrim("key", 1000L, true); assertThat(commands, contains(longCommandObject)); assertThat(response, is(predefinedResponse)); } @Test public void testXtrimBinary() { byte[] key = "stream".getBytes(); long maxLen = 1000L; boolean approximateLength = true; when(commandObjects.xtrim(key, maxLen, approximateLength)).thenReturn(longCommandObject); Response response = pipeliningBase.xtrim(key, maxLen, approximateLength); assertThat(commands, contains(longCommandObject)); assertThat(response, is(predefinedResponse)); } @Test public void testXtrimWithParams() { XTrimParams params = new XTrimParams().maxLen(1000L); when(commandObjects.xtrim("key", params)).thenReturn(longCommandObject); Response response = pipeliningBase.xtrim("key", params); assertThat(commands, contains(longCommandObject)); assertThat(response, is(predefinedResponse)); } @Test public void testXtrimWithParamsBinary() { byte[] key = "stream".getBytes(); XTrimParams params = new XTrimParams().maxLen(1000L); when(commandObjects.xtrim(key, params)).thenReturn(longCommandObject); Response response = pipeliningBase.xtrim(key, params); assertThat(commands, contains(longCommandObject)); assertThat(response, is(predefinedResponse)); } } ================================================ FILE: src/test/java/redis/clients/jedis/mocked/pipeline/PipeliningBaseStringCommandsTest.java ================================================ package redis.clients.jedis.mocked.pipeline; import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.contains; import static org.hamcrest.Matchers.is; import static org.mockito.Mockito.when; import java.util.List; import org.junit.jupiter.api.Test; import redis.clients.jedis.Response; import redis.clients.jedis.params.GetExParams; import redis.clients.jedis.params.LCSParams; import redis.clients.jedis.params.SetParams; import redis.clients.jedis.params.MSetExParams; import redis.clients.jedis.resps.LCSMatchResult; public class PipeliningBaseStringCommandsTest extends PipeliningBaseMockedTestBase { @Test public void testAppend() { when(commandObjects.append("key", "value")).thenReturn(longCommandObject); Response response = pipeliningBase.append("key", "value"); assertThat(commands, contains(longCommandObject)); assertThat(response, is(predefinedResponse)); } @Test public void testAppendBinary() { byte[] key = "key".getBytes(); byte[] value = "value".getBytes(); when(commandObjects.append(key, value)).thenReturn(longCommandObject); Response response = pipeliningBase.append(key, value); assertThat(commands, contains(longCommandObject)); assertThat(response, is(predefinedResponse)); } @Test public void testDecr() { when(commandObjects.decr("key")).thenReturn(longCommandObject); Response response = pipeliningBase.decr("key"); assertThat(commands, contains(longCommandObject)); assertThat(response, is(predefinedResponse)); } @Test public void testDecrBinary() { byte[] key = "key".getBytes(); when(commandObjects.decr(key)).thenReturn(longCommandObject); Response response = pipeliningBase.decr(key); assertThat(commands, contains(longCommandObject)); assertThat(response, is(predefinedResponse)); } @Test public void testDecrBy() { when(commandObjects.decrBy("key", 10L)).thenReturn(longCommandObject); Response response = pipeliningBase.decrBy("key", 10L); assertThat(commands, contains(longCommandObject)); assertThat(response, is(predefinedResponse)); } @Test public void testDecrByBinary() { byte[] key = "key".getBytes(); long decrement = 2L; when(commandObjects.decrBy(key, decrement)).thenReturn(longCommandObject); Response response = pipeliningBase.decrBy(key, decrement); assertThat(commands, contains(longCommandObject)); assertThat(response, is(predefinedResponse)); } @Test public void testGet() { when(commandObjects.get("key")).thenReturn(stringCommandObject); Response response = pipeliningBase.get("key"); assertThat(commands, contains(stringCommandObject)); assertThat(response, is(predefinedResponse)); } @Test public void testGetBinary() { byte[] key = "key".getBytes(); when(commandObjects.get(key)).thenReturn(bytesCommandObject); Response response = pipeliningBase.get(key); assertThat(commands, contains(bytesCommandObject)); assertThat(response, is(predefinedResponse)); } @Test public void testGetDel() { when(commandObjects.getDel("key")).thenReturn(stringCommandObject); Response response = pipeliningBase.getDel("key"); assertThat(commands, contains(stringCommandObject)); assertThat(response, is(predefinedResponse)); } @Test public void testGetDelBinary() { byte[] key = "key".getBytes(); when(commandObjects.getDel(key)).thenReturn(bytesCommandObject); Response response = pipeliningBase.getDel(key); assertThat(commands, contains(bytesCommandObject)); assertThat(response, is(predefinedResponse)); } @Test public void testGetEx() { GetExParams params = new GetExParams(); when(commandObjects.getEx("key", params)).thenReturn(stringCommandObject); Response response = pipeliningBase.getEx("key", params); assertThat(commands, contains(stringCommandObject)); assertThat(response, is(predefinedResponse)); } @Test public void testGetExBinary() { byte[] key = "key".getBytes(); GetExParams params = new GetExParams().ex(10); when(commandObjects.getEx(key, params)).thenReturn(bytesCommandObject); Response response = pipeliningBase.getEx(key, params); assertThat(commands, contains(bytesCommandObject)); assertThat(response, is(predefinedResponse)); } @Test public void testGetrange() { when(commandObjects.getrange("key", 0, 100)).thenReturn(stringCommandObject); Response response = pipeliningBase.getrange("key", 0, 100); assertThat(commands, contains(stringCommandObject)); assertThat(response, is(predefinedResponse)); } @Test public void testGetrangeBinary() { byte[] key = "key".getBytes(); long startOffset = 0L; long endOffset = 10L; when(commandObjects.getrange(key, startOffset, endOffset)).thenReturn(bytesCommandObject); Response response = pipeliningBase.getrange(key, startOffset, endOffset); assertThat(commands, contains(bytesCommandObject)); assertThat(response, is(predefinedResponse)); } @Test public void testGetSet() { when(commandObjects.getSet("key", "value")).thenReturn(stringCommandObject); Response response = pipeliningBase.getSet("key", "value"); assertThat(commands, contains(stringCommandObject)); assertThat(response, is(predefinedResponse)); } @Test public void testGetSetBinary() { byte[] key = "key".getBytes(); byte[] value = "value".getBytes(); when(commandObjects.getSet(key, value)).thenReturn(bytesCommandObject); Response response = pipeliningBase.getSet(key, value); assertThat(commands, contains(bytesCommandObject)); assertThat(response, is(predefinedResponse)); } @Test public void testIncr() { when(commandObjects.incr("key")).thenReturn(longCommandObject); Response response = pipeliningBase.incr("key"); assertThat(commands, contains(longCommandObject)); assertThat(response, is(predefinedResponse)); } @Test public void testIncrBinary() { byte[] key = "key".getBytes(); when(commandObjects.incr(key)).thenReturn(longCommandObject); Response response = pipeliningBase.incr(key); assertThat(commands, contains(longCommandObject)); assertThat(response, is(predefinedResponse)); } @Test public void testIncrBy() { when(commandObjects.incrBy("key", 10L)).thenReturn(longCommandObject); Response response = pipeliningBase.incrBy("key", 10L); assertThat(commands, contains(longCommandObject)); assertThat(response, is(predefinedResponse)); } @Test public void testIncrByBinary() { byte[] key = "key".getBytes(); long increment = 2L; when(commandObjects.incrBy(key, increment)).thenReturn(longCommandObject); Response response = pipeliningBase.incrBy(key, increment); assertThat(commands, contains(longCommandObject)); assertThat(response, is(predefinedResponse)); } @Test public void testIncrByFloat() { when(commandObjects.incrByFloat("key", 1.5)).thenReturn(doubleCommandObject); Response response = pipeliningBase.incrByFloat("key", 1.5); assertThat(commands, contains(doubleCommandObject)); assertThat(response, is(predefinedResponse)); } @Test public void testIncrByFloatBinary() { byte[] key = "key".getBytes(); double increment = 2.5; when(commandObjects.incrByFloat(key, increment)).thenReturn(doubleCommandObject); Response response = pipeliningBase.incrByFloat(key, increment); assertThat(commands, contains(doubleCommandObject)); assertThat(response, is(predefinedResponse)); } @Test public void testLcs() { LCSParams params = new LCSParams(); when(commandObjects.lcs("keyA", "keyB", params)).thenReturn(lcsMatchResultCommandObject); Response response = pipeliningBase.lcs("keyA", "keyB", params); assertThat(commands, contains(lcsMatchResultCommandObject)); assertThat(response, is(predefinedResponse)); } @Test public void testLcsBinary() { byte[] keyA = "keyA".getBytes(); byte[] keyB = "keyB".getBytes(); LCSParams params = new LCSParams().withMatchLen(); when(commandObjects.lcs(keyA, keyB, params)).thenReturn(lcsMatchResultCommandObject); Response response = pipeliningBase.lcs(keyA, keyB, params); assertThat(commands, contains(lcsMatchResultCommandObject)); assertThat(response, is(predefinedResponse)); } @Test public void testMget() { String[] keys = { "key1", "key2", "key3" }; when(commandObjects.mget(keys)).thenReturn(listStringCommandObject); Response> response = pipeliningBase.mget(keys); assertThat(commands, contains(listStringCommandObject)); assertThat(response, is(predefinedResponse)); } @Test public void testMgetBinary() { byte[] key1 = "key1".getBytes(); byte[] key2 = "key2".getBytes(); when(commandObjects.mget(key1, key2)).thenReturn(listBytesCommandObject); Response> response = pipeliningBase.mget(key1, key2); assertThat(commands, contains(listBytesCommandObject)); assertThat(response, is(predefinedResponse)); } @Test public void testMset() { String[] keysvalues = { "key1", "value1", "key2", "value2" }; when(commandObjects.mset(keysvalues)).thenReturn(stringCommandObject); Response response = pipeliningBase.mset(keysvalues); assertThat(commands, contains(stringCommandObject)); assertThat(response, is(predefinedResponse)); } @Test public void testMsetBinary() { byte[] key1 = "key1".getBytes(); byte[] value1 = "value1".getBytes(); byte[] key2 = "key2".getBytes(); byte[] value2 = "value2".getBytes(); when(commandObjects.mset(key1, value1, key2, value2)).thenReturn(stringCommandObject); Response response = pipeliningBase.mset(key1, value1, key2, value2); assertThat(commands, contains(stringCommandObject)); assertThat(response, is(predefinedResponse)); } @Test public void testMsetnx() { String[] keysvalues = { "key1", "value1", "key2", "value2" }; when(commandObjects.msetnx(keysvalues)).thenReturn(longCommandObject); Response response = pipeliningBase.msetnx(keysvalues); assertThat(commands, contains(longCommandObject)); assertThat(response, is(predefinedResponse)); } @Test public void testMsetnxBinary() { byte[] key1 = "key1".getBytes(); byte[] value1 = "value1".getBytes(); byte[] key2 = "key2".getBytes(); byte[] value2 = "value2".getBytes(); when(commandObjects.msetnx(key1, value1, key2, value2)).thenReturn(longCommandObject); Response response = pipeliningBase.msetnx(key1, value1, key2, value2); assertThat(commands, contains(longCommandObject)); assertThat(response, is(predefinedResponse)); } @Test public void testMsetex() { MSetExParams params = new MSetExParams().nx().ex(3); String[] keysvalues = {"k1","v1","k2","v2"}; when(commandObjects.msetex(params, keysvalues)).thenReturn(booleanCommandObject); Response response = pipeliningBase.msetex(params, keysvalues); assertThat(commands, contains(booleanCommandObject)); assertThat(response, is(predefinedResponse)); } @Test public void testMsetexBinary() { MSetExParams params = new MSetExParams().xx().keepTtl(); byte[] k1 = "k1".getBytes(); byte[] v1 = "v1".getBytes(); byte[] k2 = "k2".getBytes(); byte[] v2 = "v2".getBytes(); when(commandObjects.msetex(params, k1, v1, k2, v2)).thenReturn(booleanCommandObject); Response response = pipeliningBase.msetex(params, k1, v1, k2, v2); assertThat(commands, contains(booleanCommandObject)); assertThat(response, is(predefinedResponse)); } @Test public void testPsetex() { when(commandObjects.psetex("key", 100000, "value")).thenReturn(stringCommandObject); Response response = pipeliningBase.psetex("key", 100000, "value"); assertThat(commands, contains(stringCommandObject)); assertThat(response, is(predefinedResponse)); } @Test public void testPsetexBinary() { byte[] key = "key".getBytes(); long milliseconds = 5000L; byte[] value = "value".getBytes(); when(commandObjects.psetex(key, milliseconds, value)).thenReturn(stringCommandObject); Response response = pipeliningBase.psetex(key, milliseconds, value); assertThat(commands, contains(stringCommandObject)); assertThat(response, is(predefinedResponse)); } @Test public void testSet() { when(commandObjects.set("key", "value")).thenReturn(stringCommandObject); Response response = pipeliningBase.set("key", "value"); assertThat(commands, contains(stringCommandObject)); assertThat(response, is(predefinedResponse)); } @Test public void testSetBinary() { byte[] key = "key".getBytes(); byte[] value = "value".getBytes(); when(commandObjects.set(key, value)).thenReturn(stringCommandObject); Response response = pipeliningBase.set(key, value); assertThat(commands, contains(stringCommandObject)); assertThat(response, is(predefinedResponse)); } @Test public void testSetWithParams() { SetParams params = new SetParams(); when(commandObjects.set("key", "value", params)).thenReturn(stringCommandObject); Response response = pipeliningBase.set("key", "value", params); assertThat(commands, contains(stringCommandObject)); assertThat(response, is(predefinedResponse)); } @Test public void testSetWithParamsBinary() { byte[] key = "key".getBytes(); byte[] value = "value".getBytes(); SetParams params = new SetParams().nx().ex(10); when(commandObjects.set(key, value, params)).thenReturn(stringCommandObject); Response response = pipeliningBase.set(key, value, params); assertThat(commands, contains(stringCommandObject)); assertThat(response, is(predefinedResponse)); } @Test public void testSetGet() { when(commandObjects.setGet("key", "value")).thenReturn(stringCommandObject); Response response = pipeliningBase.setGet("key", "value"); assertThat(commands, contains(stringCommandObject)); assertThat(response, is(predefinedResponse)); } @Test public void testSetGetBinary() { byte[] key = "key".getBytes(); byte[] value = "value".getBytes(); when(commandObjects.setGet(key, value)).thenReturn(bytesCommandObject); Response response = pipeliningBase.setGet(key, value); assertThat(commands, contains(bytesCommandObject)); assertThat(response, is(predefinedResponse)); } @Test public void testSetGetWithParams() { SetParams setParams = new SetParams(); when(commandObjects.setGet("key", "value", setParams)).thenReturn(stringCommandObject); Response response = pipeliningBase.setGet("key", "value", setParams); assertThat(commands, contains(stringCommandObject)); assertThat(response, is(predefinedResponse)); } @Test public void testSetGetWithParamsBinary() { byte[] key = "key".getBytes(); byte[] value = "value".getBytes(); SetParams params = new SetParams().nx().ex(10); when(commandObjects.setGet(key, value, params)).thenReturn(bytesCommandObject); Response response = pipeliningBase.setGet(key, value, params); assertThat(commands, contains(bytesCommandObject)); assertThat(response, is(predefinedResponse)); } @Test public void testSetex() { when(commandObjects.setex("key", 60, "value")).thenReturn(stringCommandObject); Response response = pipeliningBase.setex("key", 60, "value"); assertThat(commands, contains(stringCommandObject)); assertThat(response, is(predefinedResponse)); } @Test public void testSetexBinary() { byte[] key = "key".getBytes(); long seconds = 60L; byte[] value = "value".getBytes(); when(commandObjects.setex(key, seconds, value)).thenReturn(stringCommandObject); Response response = pipeliningBase.setex(key, seconds, value); assertThat(commands, contains(stringCommandObject)); assertThat(response, is(predefinedResponse)); } @Test public void testSetnx() { when(commandObjects.setnx("key", "value")).thenReturn(longCommandObject); Response response = pipeliningBase.setnx("key", "value"); assertThat(commands, contains(longCommandObject)); assertThat(response, is(predefinedResponse)); } @Test public void testSetnxBinary() { byte[] key = "key".getBytes(); byte[] value = "value".getBytes(); when(commandObjects.setnx(key, value)).thenReturn(longCommandObject); Response response = pipeliningBase.setnx(key, value); assertThat(commands, contains(longCommandObject)); assertThat(response, is(predefinedResponse)); } @Test public void testSetrange() { when(commandObjects.setrange("key", 100, "value")).thenReturn(longCommandObject); Response response = pipeliningBase.setrange("key", 100, "value"); assertThat(commands, contains(longCommandObject)); assertThat(response, is(predefinedResponse)); } @Test public void testSetrangeBinary() { byte[] key = "key".getBytes(); long offset = 10L; byte[] value = "value".getBytes(); when(commandObjects.setrange(key, offset, value)).thenReturn(longCommandObject); Response response = pipeliningBase.setrange(key, offset, value); assertThat(commands, contains(longCommandObject)); assertThat(response, is(predefinedResponse)); } @Test public void testStrlen() { when(commandObjects.strlen("key")).thenReturn(longCommandObject); Response response = pipeliningBase.strlen("key"); assertThat(commands, contains(longCommandObject)); assertThat(response, is(predefinedResponse)); } @Test public void testStrlenBinary() { byte[] key = "key".getBytes(); when(commandObjects.strlen(key)).thenReturn(longCommandObject); Response response = pipeliningBase.strlen(key); assertThat(commands, contains(longCommandObject)); assertThat(response, is(predefinedResponse)); } @Test public void testSubstr() { when(commandObjects.substr("key", 0, 10)).thenReturn(stringCommandObject); Response response = pipeliningBase.substr("key", 0, 10); assertThat(commands, contains(stringCommandObject)); assertThat(response, is(predefinedResponse)); } @Test public void testSubstrBinary() { byte[] key = "key".getBytes(); int start = 0; int end = 5; when(commandObjects.substr(key, start, end)).thenReturn(bytesCommandObject); Response response = pipeliningBase.substr(key, start, end); assertThat(commands, contains(bytesCommandObject)); assertThat(response, is(predefinedResponse)); } } ================================================ FILE: src/test/java/redis/clients/jedis/mocked/pipeline/PipeliningBaseTDigestCommandsTest.java ================================================ package redis.clients.jedis.mocked.pipeline; import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.contains; import static org.hamcrest.Matchers.is; import static org.mockito.Mockito.when; import java.util.List; import java.util.Map; import org.junit.jupiter.api.Test; import redis.clients.jedis.Response; import redis.clients.jedis.bloom.TDigestMergeParams; public class PipeliningBaseTDigestCommandsTest extends PipeliningBaseMockedTestBase { @Test public void testTdigestAdd() { when(commandObjects.tdigestAdd("myTDigest", 1.0, 2.0, 3.0)).thenReturn(stringCommandObject); Response response = pipeliningBase.tdigestAdd("myTDigest", 1.0, 2.0, 3.0); assertThat(commands, contains(stringCommandObject)); assertThat(response, is(predefinedResponse)); } @Test public void testTdigestByRank() { when(commandObjects.tdigestByRank("myTDigest", 1, 2)).thenReturn(listDoubleCommandObject); Response> response = pipeliningBase.tdigestByRank("myTDigest", 1, 2); assertThat(commands, contains(listDoubleCommandObject)); assertThat(response, is(predefinedResponse)); } @Test public void testTdigestByRevRank() { when(commandObjects.tdigestByRevRank("myTDigest", 1, 2)).thenReturn(listDoubleCommandObject); Response> response = pipeliningBase.tdigestByRevRank("myTDigest", 1, 2); assertThat(commands, contains(listDoubleCommandObject)); assertThat(response, is(predefinedResponse)); } @Test public void testTdigestCDF() { when(commandObjects.tdigestCDF("myTDigest", 1.0, 2.0)).thenReturn(listDoubleCommandObject); Response> response = pipeliningBase.tdigestCDF("myTDigest", 1.0, 2.0); assertThat(commands, contains(listDoubleCommandObject)); assertThat(response, is(predefinedResponse)); } @Test public void testTdigestCreate() { when(commandObjects.tdigestCreate("myTDigest")).thenReturn(stringCommandObject); Response response = pipeliningBase.tdigestCreate("myTDigest"); assertThat(commands, contains(stringCommandObject)); assertThat(response, is(predefinedResponse)); } @Test public void testTdigestCreateWithCompression() { when(commandObjects.tdigestCreate("myTDigest", 100)).thenReturn(stringCommandObject); Response response = pipeliningBase.tdigestCreate("myTDigest", 100); assertThat(commands, contains(stringCommandObject)); assertThat(response, is(predefinedResponse)); } @Test public void testTdigestInfo() { when(commandObjects.tdigestInfo("myTDigest")).thenReturn(mapStringObjectCommandObject); Response> response = pipeliningBase.tdigestInfo("myTDigest"); assertThat(commands, contains(mapStringObjectCommandObject)); assertThat(response, is(predefinedResponse)); } @Test public void testTdigestMax() { when(commandObjects.tdigestMax("myTDigest")).thenReturn(doubleCommandObject); Response response = pipeliningBase.tdigestMax("myTDigest"); assertThat(commands, contains(doubleCommandObject)); assertThat(response, is(predefinedResponse)); } @Test public void testTdigestMerge() { when(commandObjects.tdigestMerge("destinationTDigest", "sourceTDigest1", "sourceTDigest2")) .thenReturn(stringCommandObject); Response response = pipeliningBase.tdigestMerge("destinationTDigest", "sourceTDigest1", "sourceTDigest2"); assertThat(commands, contains(stringCommandObject)); assertThat(response, is(predefinedResponse)); } @Test public void testTdigestMergeWithParams() { TDigestMergeParams mergeParams = new TDigestMergeParams().compression(100); when(commandObjects.tdigestMerge(mergeParams, "destinationTDigest", "sourceTDigest1", "sourceTDigest2")) .thenReturn(stringCommandObject); Response response = pipeliningBase.tdigestMerge(mergeParams, "destinationTDigest", "sourceTDigest1", "sourceTDigest2"); assertThat(commands, contains(stringCommandObject)); assertThat(response, is(predefinedResponse)); } @Test public void testTdigestMin() { when(commandObjects.tdigestMin("myTDigest")).thenReturn(doubleCommandObject); Response response = pipeliningBase.tdigestMin("myTDigest"); assertThat(commands, contains(doubleCommandObject)); assertThat(response, is(predefinedResponse)); } @Test public void testTdigestQuantile() { when(commandObjects.tdigestQuantile("myTDigest", 0.5, 0.9)).thenReturn(listDoubleCommandObject); Response> response = pipeliningBase.tdigestQuantile("myTDigest", 0.5, 0.9); assertThat(commands, contains(listDoubleCommandObject)); assertThat(response, is(predefinedResponse)); } @Test public void testTdigestRank() { when(commandObjects.tdigestRank("myTDigest", 1.0, 2.0)).thenReturn(listLongCommandObject); Response> response = pipeliningBase.tdigestRank("myTDigest", 1.0, 2.0); assertThat(commands, contains(listLongCommandObject)); assertThat(response, is(predefinedResponse)); } @Test public void testTdigestReset() { when(commandObjects.tdigestReset("myTDigest")).thenReturn(stringCommandObject); Response response = pipeliningBase.tdigestReset("myTDigest"); assertThat(commands, contains(stringCommandObject)); assertThat(response, is(predefinedResponse)); } @Test public void testTdigestRevRank() { when(commandObjects.tdigestRevRank("myTDigest", 1.0, 2.0)).thenReturn(listLongCommandObject); Response> response = pipeliningBase.tdigestRevRank("myTDigest", 1.0, 2.0); assertThat(commands, contains(listLongCommandObject)); assertThat(response, is(predefinedResponse)); } @Test public void testTdigestTrimmedMean() { when(commandObjects.tdigestTrimmedMean("myTDigest", 0.1, 0.9)).thenReturn(doubleCommandObject); Response response = pipeliningBase.tdigestTrimmedMean("myTDigest", 0.1, 0.9); assertThat(commands, contains(doubleCommandObject)); assertThat(response, is(predefinedResponse)); } } ================================================ FILE: src/test/java/redis/clients/jedis/mocked/pipeline/PipeliningBaseTimeSeriesCommandsTest.java ================================================ package redis.clients.jedis.mocked.pipeline; import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.contains; import static org.hamcrest.Matchers.is; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; import java.util.AbstractMap; import java.util.List; import java.util.Map; import org.junit.jupiter.api.Test; import redis.clients.jedis.Response; import redis.clients.jedis.timeseries.*; public class PipeliningBaseTimeSeriesCommandsTest extends PipeliningBaseMockedTestBase { @Test public void testTsAdd() { when(commandObjects.tsAdd("myTimeSeries", 42.0)).thenReturn(longCommandObject); Response response = pipeliningBase.tsAdd("myTimeSeries", 42.0); assertThat(commands, contains(longCommandObject)); assertThat(response, is(predefinedResponse)); } @Test public void testTsAddWithTimestamp() { when(commandObjects.tsAdd("myTimeSeries", 1000L, 42.0)).thenReturn(longCommandObject); Response response = pipeliningBase.tsAdd("myTimeSeries", 1000L, 42.0); assertThat(commands, contains(longCommandObject)); assertThat(response, is(predefinedResponse)); } @Test public void testTsAddWithTimestampAndParams() { TSCreateParams createParams = TSCreateParams.createParams(); when(commandObjects.tsAdd("myTimeSeries", 1000L, 42.0, createParams)).thenReturn(longCommandObject); Response response = pipeliningBase.tsAdd("myTimeSeries", 1000L, 42.0, createParams); assertThat(commands, contains(longCommandObject)); assertThat(response, is(predefinedResponse)); } @Test public void testTsAddWithParams() { TSAddParams addParams = mock(TSAddParams.class); when(commandObjects.tsAdd("myTimeSeries", 1000L, 42.0, addParams)).thenReturn(longCommandObject); Response response = pipeliningBase.tsAdd("myTimeSeries", 1000L, 42.0, addParams); assertThat(commands, contains(longCommandObject)); assertThat(response, is(predefinedResponse)); } @Test public void testTsAlter() { TSAlterParams alterParams = TSAlterParams.alterParams(); when(commandObjects.tsAlter("myTimeSeries", alterParams)).thenReturn(stringCommandObject); Response response = pipeliningBase.tsAlter("myTimeSeries", alterParams); assertThat(commands, contains(stringCommandObject)); assertThat(response, is(predefinedResponse)); } @Test public void testTsCreate() { when(commandObjects.tsCreate("myTimeSeries")).thenReturn(stringCommandObject); Response response = pipeliningBase.tsCreate("myTimeSeries"); assertThat(commands, contains(stringCommandObject)); assertThat(response, is(predefinedResponse)); } @Test public void testTsCreateWithParams() { TSCreateParams createParams = TSCreateParams.createParams(); when(commandObjects.tsCreate("myTimeSeries", createParams)).thenReturn(stringCommandObject); Response response = pipeliningBase.tsCreate("myTimeSeries", createParams); assertThat(commands, contains(stringCommandObject)); assertThat(response, is(predefinedResponse)); } @Test public void testTsCreateRule() { AggregationType aggregationType = AggregationType.AVG; long timeBucket = 60; when(commandObjects.tsCreateRule("sourceTimeSeries", "destTimeSeries", aggregationType, timeBucket)).thenReturn(stringCommandObject); Response response = pipeliningBase.tsCreateRule("sourceTimeSeries", "destTimeSeries", aggregationType, timeBucket); assertThat(commands, contains(stringCommandObject)); assertThat(response, is(predefinedResponse)); } @Test public void testTsCreateRuleWithAlignTimestamp() { AggregationType aggregationType = AggregationType.AVG; long bucketDuration = 60; long alignTimestamp = 0; when(commandObjects.tsCreateRule("sourceTimeSeries", "destTimeSeries", aggregationType, bucketDuration, alignTimestamp)).thenReturn(stringCommandObject); Response response = pipeliningBase.tsCreateRule("sourceTimeSeries", "destTimeSeries", aggregationType, bucketDuration, alignTimestamp); assertThat(commands, contains(stringCommandObject)); assertThat(response, is(predefinedResponse)); } @Test public void testTsDecrBy() { when(commandObjects.tsDecrBy("myTimeSeries", 1.0)).thenReturn(longCommandObject); Response response = pipeliningBase.tsDecrBy("myTimeSeries", 1.0); assertThat(commands, contains(longCommandObject)); assertThat(response, is(predefinedResponse)); } @Test public void testTsDecrByWithTimestamp() { when(commandObjects.tsDecrBy("myTimeSeries", 1.0, 1000L)).thenReturn(longCommandObject); Response response = pipeliningBase.tsDecrBy("myTimeSeries", 1.0, 1000L); assertThat(commands, contains(longCommandObject)); assertThat(response, is(predefinedResponse)); } @Test public void testTsDecrByWithParams() { TSDecrByParams decrByParams = mock(TSDecrByParams.class); when(commandObjects.tsDecrBy("myTimeSeries", 1.0, decrByParams)).thenReturn(longCommandObject); Response response = pipeliningBase.tsDecrBy("myTimeSeries", 1.0, decrByParams); assertThat(commands, contains(longCommandObject)); assertThat(response, is(predefinedResponse)); } @Test public void testTsDel() { when(commandObjects.tsDel("myTimeSeries", 1000L, 2000L)).thenReturn(longCommandObject); Response response = pipeliningBase.tsDel("myTimeSeries", 1000L, 2000L); assertThat(commands, contains(longCommandObject)); assertThat(response, is(predefinedResponse)); } @Test public void testTsDeleteRule() { when(commandObjects.tsDeleteRule("sourceTimeSeries", "destTimeSeries")).thenReturn(stringCommandObject); Response response = pipeliningBase.tsDeleteRule("sourceTimeSeries", "destTimeSeries"); assertThat(commands, contains(stringCommandObject)); assertThat(response, is(predefinedResponse)); } @Test public void testTsGet() { when(commandObjects.tsGet("myTimeSeries")).thenReturn(tsElementCommandObject); Response response = pipeliningBase.tsGet("myTimeSeries"); assertThat(commands, contains(tsElementCommandObject)); assertThat(response, is(predefinedResponse)); } @Test public void testTsGetWithParams() { TSGetParams getParams = TSGetParams.getParams(); when(commandObjects.tsGet("myTimeSeries", getParams)).thenReturn(tsElementCommandObject); Response response = pipeliningBase.tsGet("myTimeSeries", getParams); assertThat(commands, contains(tsElementCommandObject)); assertThat(response, is(predefinedResponse)); } @Test public void testTsIncrBy() { when(commandObjects.tsIncrBy("myTimeSeries", 1.0)).thenReturn(longCommandObject); Response response = pipeliningBase.tsIncrBy("myTimeSeries", 1.0); assertThat(commands, contains(longCommandObject)); assertThat(response, is(predefinedResponse)); } @Test public void testTsIncrByWithTimestamp() { when(commandObjects.tsIncrBy("myTimeSeries", 1.0, 1000L)).thenReturn(longCommandObject); Response response = pipeliningBase.tsIncrBy("myTimeSeries", 1.0, 1000L); assertThat(commands, contains(longCommandObject)); assertThat(response, is(predefinedResponse)); } @Test public void testTsIncrByWithParams() { TSIncrByParams incrByParams = mock(TSIncrByParams.class); when(commandObjects.tsIncrBy("myTimeSeries", 1.0, incrByParams)).thenReturn(longCommandObject); Response response = pipeliningBase.tsIncrBy("myTimeSeries", 1.0, incrByParams); assertThat(commands, contains(longCommandObject)); assertThat(response, is(predefinedResponse)); } @Test public void testTsInfo() { when(commandObjects.tsInfo("myTimeSeries")).thenReturn(tsInfoCommandObject); Response response = pipeliningBase.tsInfo("myTimeSeries"); assertThat(commands, contains(tsInfoCommandObject)); assertThat(response, is(predefinedResponse)); } @Test public void testTsInfoDebug() { when(commandObjects.tsInfoDebug("myTimeSeries")).thenReturn(tsInfoCommandObject); Response response = pipeliningBase.tsInfoDebug("myTimeSeries"); assertThat(commands, contains(tsInfoCommandObject)); assertThat(response, is(predefinedResponse)); } @Test public void testTsMAdd() { Map.Entry entry1 = new AbstractMap.SimpleEntry<>("ts1", new TSElement(1000L, 1.0)); Map.Entry entry2 = new AbstractMap.SimpleEntry<>("ts2", new TSElement(2000L, 2.0)); when(commandObjects.tsMAdd(entry1, entry2)).thenReturn(listLongCommandObject); Response> response = pipeliningBase.tsMAdd(entry1, entry2); assertThat(commands, contains(listLongCommandObject)); assertThat(response, is(predefinedResponse)); } @Test public void testTsMGet() { TSMGetParams multiGetParams = TSMGetParams.multiGetParams(); String[] filters = { "sensor_id=123" }; when(commandObjects.tsMGet(multiGetParams, filters)).thenReturn(mapStringTsmGetElementCommandObject); Response> response = pipeliningBase.tsMGet(multiGetParams, filters); assertThat(commands, contains(mapStringTsmGetElementCommandObject)); assertThat(response, is(predefinedResponse)); } @Test public void testTsMRange() { String[] filters = { "sensor_id=123" }; when(commandObjects.tsMRange(1000L, 2000L, filters)).thenReturn(mapStringTsmRangeElementsCommandObject); Response> response = pipeliningBase.tsMRange(1000L, 2000L, filters); assertThat(commands, contains(mapStringTsmRangeElementsCommandObject)); assertThat(response, is(predefinedResponse)); } @Test public void testTsMRangeWithParams() { TSMRangeParams multiRangeParams = TSMRangeParams.multiRangeParams(); when(commandObjects.tsMRange(multiRangeParams)).thenReturn(mapStringTsmRangeElementsCommandObject); Response> response = pipeliningBase.tsMRange(multiRangeParams); assertThat(commands, contains(mapStringTsmRangeElementsCommandObject)); assertThat(response, is(predefinedResponse)); } @Test public void testTsMRevRange() { String[] filters = { "sensor_id=123" }; when(commandObjects.tsMRevRange(1000L, 2000L, filters)).thenReturn(mapStringTsmRangeElementsCommandObject); Response> response = pipeliningBase.tsMRevRange(1000L, 2000L, filters); assertThat(commands, contains(mapStringTsmRangeElementsCommandObject)); assertThat(response, is(predefinedResponse)); } @Test public void testTsMRevRangeWithParams() { TSMRangeParams multiRangeParams = TSMRangeParams.multiRangeParams(); when(commandObjects.tsMRevRange(multiRangeParams)).thenReturn(mapStringTsmRangeElementsCommandObject); Response> response = pipeliningBase.tsMRevRange(multiRangeParams); assertThat(commands, contains(mapStringTsmRangeElementsCommandObject)); assertThat(response, is(predefinedResponse)); } @Test public void testTsQueryIndex() { String[] filters = { "sensor_id=123" }; when(commandObjects.tsQueryIndex(filters)).thenReturn(listStringCommandObject); Response> response = pipeliningBase.tsQueryIndex(filters); assertThat(commands, contains(listStringCommandObject)); assertThat(response, is(predefinedResponse)); } @Test public void testTsRange() { when(commandObjects.tsRange("myTimeSeries", 1000L, 2000L)).thenReturn(listTsElementCommandObject); Response> response = pipeliningBase.tsRange("myTimeSeries", 1000L, 2000L); assertThat(commands, contains(listTsElementCommandObject)); assertThat(response, is(predefinedResponse)); } @Test public void testTsRangeWithParams() { TSRangeParams rangeParams = TSRangeParams.rangeParams(); when(commandObjects.tsRange("myTimeSeries", rangeParams)).thenReturn(listTsElementCommandObject); Response> response = pipeliningBase.tsRange("myTimeSeries", rangeParams); assertThat(commands, contains(listTsElementCommandObject)); assertThat(response, is(predefinedResponse)); } @Test public void testTsRevRange() { when(commandObjects.tsRevRange("myTimeSeries", 1000L, 2000L)).thenReturn(listTsElementCommandObject); Response> response = pipeliningBase.tsRevRange("myTimeSeries", 1000L, 2000L); assertThat(commands, contains(listTsElementCommandObject)); assertThat(response, is(predefinedResponse)); } @Test public void testTsRevRangeWithParams() { TSRangeParams rangeParams = TSRangeParams.rangeParams(); when(commandObjects.tsRevRange("myTimeSeries", rangeParams)).thenReturn(listTsElementCommandObject); Response> response = pipeliningBase.tsRevRange("myTimeSeries", rangeParams); assertThat(commands, contains(listTsElementCommandObject)); assertThat(response, is(predefinedResponse)); } } ================================================ FILE: src/test/java/redis/clients/jedis/mocked/pipeline/PipeliningBaseTopKCommandsTest.java ================================================ package redis.clients.jedis.mocked.pipeline; import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.contains; import static org.hamcrest.Matchers.is; import static org.mockito.Mockito.when; import java.util.HashMap; import java.util.List; import java.util.Map; import org.junit.jupiter.api.Test; import redis.clients.jedis.Response; public class PipeliningBaseTopKCommandsTest extends PipeliningBaseMockedTestBase { @Test public void testTopkAdd() { when(commandObjects.topkAdd("myTopK", "item1", "item2")).thenReturn(listStringCommandObject); Response> response = pipeliningBase.topkAdd("myTopK", "item1", "item2"); assertThat(commands, contains(listStringCommandObject)); assertThat(response, is(predefinedResponse)); } @Test public void testTopkIncrBy() { Map itemIncrements = new HashMap<>(); itemIncrements.put("item1", 1L); itemIncrements.put("item2", 2L); when(commandObjects.topkIncrBy("myTopK", itemIncrements)).thenReturn(listStringCommandObject); Response> response = pipeliningBase.topkIncrBy("myTopK", itemIncrements); assertThat(commands, contains(listStringCommandObject)); assertThat(response, is(predefinedResponse)); } @Test public void testTopkInfo() { when(commandObjects.topkInfo("myTopK")).thenReturn(mapStringObjectCommandObject); Response> response = pipeliningBase.topkInfo("myTopK"); assertThat(commands, contains(mapStringObjectCommandObject)); assertThat(response, is(predefinedResponse)); } @Test public void testTopkList() { when(commandObjects.topkList("myTopK")).thenReturn(listStringCommandObject); Response> response = pipeliningBase.topkList("myTopK"); assertThat(commands, contains(listStringCommandObject)); assertThat(response, is(predefinedResponse)); } @Test public void testTopkListWithCount() { when(commandObjects.topkListWithCount("myTopK")).thenReturn(mapStringLongCommandObject); Response> response = pipeliningBase.topkListWithCount("myTopK"); assertThat(commands, contains(mapStringLongCommandObject)); assertThat(response, is(predefinedResponse)); } @Test public void testTopkQuery() { when(commandObjects.topkQuery("myTopK", "item1", "item2")).thenReturn(listBooleanCommandObject); Response> response = pipeliningBase.topkQuery("myTopK", "item1", "item2"); assertThat(commands, contains(listBooleanCommandObject)); assertThat(response, is(predefinedResponse)); } @Test public void testTopkReserve() { when(commandObjects.topkReserve("myTopK", 3L)).thenReturn(stringCommandObject); Response response = pipeliningBase.topkReserve("myTopK", 3L); assertThat(commands, contains(stringCommandObject)); assertThat(response, is(predefinedResponse)); } @Test public void testTopkReserveWithParams() { long width = 50L; long depth = 5L; double decay = 0.9; when(commandObjects.topkReserve("myTopK", 3L, width, depth, decay)).thenReturn(stringCommandObject); Response response = pipeliningBase.topkReserve("myTopK", 3L, width, depth, decay); assertThat(commands, contains(stringCommandObject)); assertThat(response, is(predefinedResponse)); } } ================================================ FILE: src/test/java/redis/clients/jedis/mocked/unified/UnifiedJedisBitmapCommandsTest.java ================================================ package redis.clients.jedis.mocked.unified; import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.sameInstance; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; import java.util.Arrays; import java.util.Collections; import java.util.List; import org.junit.jupiter.api.Test; import redis.clients.jedis.args.BitCountOption; import redis.clients.jedis.args.BitOP; import redis.clients.jedis.params.BitPosParams; public class UnifiedJedisBitmapCommandsTest extends UnifiedJedisMockedTestBase { @Test public void testBitcount() { String key = "key"; long expectedCount = 4L; when(commandObjects.bitcount(key)).thenReturn(longCommandObject); when(commandExecutor.executeCommand(longCommandObject)).thenReturn(expectedCount); long result = jedis.bitcount(key); assertThat(result, sameInstance(expectedCount)); verify(commandExecutor).executeCommand(longCommandObject); verify(commandObjects).bitcount(key); } @Test public void testBitcountBinary() { byte[] key = "key".getBytes(); long expectedCount = 4L; when(commandObjects.bitcount(key)).thenReturn(longCommandObject); when(commandExecutor.executeCommand(longCommandObject)).thenReturn(expectedCount); long result = jedis.bitcount(key); assertThat(result, sameInstance(expectedCount)); verify(commandExecutor).executeCommand(longCommandObject); verify(commandObjects).bitcount(key); } @Test public void testBitcountRange() { String key = "key"; long start = 1L; long end = 2L; long expectedCount = 2L; when(commandObjects.bitcount(key, start, end)).thenReturn(longCommandObject); when(commandExecutor.executeCommand(longCommandObject)).thenReturn(expectedCount); long result = jedis.bitcount(key, start, end); assertThat(result, sameInstance(expectedCount)); verify(commandExecutor).executeCommand(longCommandObject); verify(commandObjects).bitcount(key, start, end); } @Test public void testBitcountRangeBinary() { byte[] key = "key".getBytes(); long start = 1L; long end = 2L; long expectedCount = 2L; when(commandObjects.bitcount(key, start, end)).thenReturn(longCommandObject); when(commandExecutor.executeCommand(longCommandObject)).thenReturn(expectedCount); long result = jedis.bitcount(key, start, end); assertThat(result, sameInstance(expectedCount)); verify(commandExecutor).executeCommand(longCommandObject); verify(commandObjects).bitcount(key, start, end); } @Test public void testBitcountRangeOption() { String key = "key"; long start = 1L; long end = 2L; BitCountOption option = BitCountOption.BYTE; long expectedCount = 2L; when(commandObjects.bitcount(key, start, end, option)).thenReturn(longCommandObject); when(commandExecutor.executeCommand(longCommandObject)).thenReturn(expectedCount); long result = jedis.bitcount(key, start, end, option); assertThat(result, sameInstance(expectedCount)); verify(commandExecutor).executeCommand(longCommandObject); verify(commandObjects).bitcount(key, start, end, option); } @Test public void testBitcountRangeOptionBinary() { byte[] key = "key".getBytes(); long start = 1L; long end = 2L; BitCountOption option = BitCountOption.BYTE; long expectedCount = 2L; when(commandObjects.bitcount(key, start, end, option)).thenReturn(longCommandObject); when(commandExecutor.executeCommand(longCommandObject)).thenReturn(expectedCount); long result = jedis.bitcount(key, start, end, option); assertThat(result, sameInstance(expectedCount)); verify(commandExecutor).executeCommand(longCommandObject); verify(commandObjects).bitcount(key, start, end, option); } @Test public void testBitfield() { String key = "key"; String[] arguments = { "INCRBY", "mykey", "1", "1000" }; List expectedResults = Arrays.asList(1000L, 2000L); when(commandObjects.bitfield(key, arguments)).thenReturn(listLongCommandObject); when(commandExecutor.executeCommand(listLongCommandObject)).thenReturn(expectedResults); List results = jedis.bitfield(key, arguments); assertThat(results, sameInstance(expectedResults)); verify(commandExecutor).executeCommand(listLongCommandObject); verify(commandObjects).bitfield(key, arguments); } @Test public void testBitfieldBinary() { byte[] key = "key".getBytes(); byte[][] arguments = { "INCRBY".getBytes(), "mykey".getBytes(), "1".getBytes(), "1000".getBytes() }; List expectedResults = Arrays.asList(1000L, 2000L); when(commandObjects.bitfield(key, arguments)).thenReturn(listLongCommandObject); when(commandExecutor.executeCommand(listLongCommandObject)).thenReturn(expectedResults); List results = jedis.bitfield(key, arguments); assertThat(results, sameInstance(expectedResults)); verify(commandExecutor).executeCommand(listLongCommandObject); verify(commandObjects).bitfield(key, arguments); } @Test public void testBitfieldReadonly() { String key = "key"; String[] arguments = { "GET", "u4", "0" }; List expectedResults = Collections.singletonList(15L); when(commandObjects.bitfieldReadonly(key, arguments)).thenReturn(listLongCommandObject); when(commandExecutor.executeCommand(listLongCommandObject)).thenReturn(expectedResults); List results = jedis.bitfieldReadonly(key, arguments); assertThat(results, sameInstance(expectedResults)); verify(commandExecutor).executeCommand(listLongCommandObject); verify(commandObjects).bitfieldReadonly(key, arguments); } @Test public void testBitfieldReadonlyBinary() { byte[] key = "key".getBytes(); byte[][] arguments = { "GET".getBytes(), "u4".getBytes(), "0".getBytes() }; List expectedResults = Collections.singletonList(15L); when(commandObjects.bitfieldReadonly(key, arguments)).thenReturn(listLongCommandObject); when(commandExecutor.executeCommand(listLongCommandObject)).thenReturn(expectedResults); List results = jedis.bitfieldReadonly(key, arguments); assertThat(results, sameInstance(expectedResults)); verify(commandExecutor).executeCommand(listLongCommandObject); verify(commandObjects).bitfieldReadonly(key, arguments); } @Test public void testBitop() { BitOP op = BitOP.OR; String destKey = "destKey"; String[] srcKeys = { "srcKey1", "srcKey2" }; long expectedResponse = 3L; when(commandObjects.bitop(op, destKey, srcKeys)).thenReturn(longCommandObject); when(commandExecutor.executeCommand(longCommandObject)).thenReturn(expectedResponse); long result = jedis.bitop(op, destKey, srcKeys); assertThat(result, sameInstance(expectedResponse)); verify(commandExecutor).executeCommand(longCommandObject); verify(commandObjects).bitop(op, destKey, srcKeys); } @Test public void testBitopBinary() { BitOP op = BitOP.XOR; byte[] destKey = "destKey".getBytes(); byte[][] srcKeys = { "srcKey1".getBytes(), "srcKey2".getBytes() }; long expectedResponse = 4L; when(commandObjects.bitop(op, destKey, srcKeys)).thenReturn(longCommandObject); when(commandExecutor.executeCommand(longCommandObject)).thenReturn(expectedResponse); long result = jedis.bitop(op, destKey, srcKeys); assertThat(result, sameInstance(expectedResponse)); verify(commandExecutor).executeCommand(longCommandObject); verify(commandObjects).bitop(op, destKey, srcKeys); } @Test public void testBitpos() { String key = "key"; boolean value = true; // Looking for the first bit set to 1 long expectedPosition = 2L; // Assuming the first bit set to 1 is at position 2 when(commandObjects.bitpos(key, value)).thenReturn(longCommandObject); when(commandExecutor.executeCommand(longCommandObject)).thenReturn(expectedPosition); long result = jedis.bitpos(key, value); assertThat(result, sameInstance(expectedPosition)); verify(commandExecutor).executeCommand(longCommandObject); verify(commandObjects).bitpos(key, value); } @Test public void testBitposBinary() { byte[] key = "key".getBytes(); boolean value = true; // Looking for the first bit set to 1 long expectedPosition = 2L; // Assuming the first bit set to 1 is at position 2 when(commandObjects.bitpos(key, value)).thenReturn(longCommandObject); when(commandExecutor.executeCommand(longCommandObject)).thenReturn(expectedPosition); long result = jedis.bitpos(key, value); assertThat(result, sameInstance(expectedPosition)); verify(commandExecutor).executeCommand(longCommandObject); verify(commandObjects).bitpos(key, value); } @Test public void testBitposParams() { String key = "key"; boolean value = false; // Looking for the first bit set to 0 BitPosParams params = new BitPosParams(1); // Starting the search from byte offset 1 long expectedPosition = 8L; // Assuming the first bit set to 0 from offset 1 is at position 8 when(commandObjects.bitpos(key, value, params)).thenReturn(longCommandObject); when(commandExecutor.executeCommand(longCommandObject)).thenReturn(expectedPosition); long result = jedis.bitpos(key, value, params); assertThat(result, sameInstance(expectedPosition)); verify(commandExecutor).executeCommand(longCommandObject); verify(commandObjects).bitpos(key, value, params); } @Test public void testBitposParamsBinary() { byte[] key = "key".getBytes(); boolean value = false; // Looking for the first bit set to 0 BitPosParams params = new BitPosParams(1); // Starting the search from byte offset 1 long expectedPosition = 8L; // Assuming the first bit set to 0 from offset 1 is at position 8 when(commandObjects.bitpos(key, value, params)).thenReturn(longCommandObject); when(commandExecutor.executeCommand(longCommandObject)).thenReturn(expectedPosition); long result = jedis.bitpos(key, value, params); assertThat(result, sameInstance(expectedPosition)); verify(commandExecutor).executeCommand(longCommandObject); verify(commandObjects).bitpos(key, value, params); } @Test public void testGetbit() { String key = "key"; long offset = 10L; boolean expectedResponse = true; when(commandObjects.getbit(key, offset)).thenReturn(booleanCommandObject); when(commandExecutor.executeCommand(booleanCommandObject)).thenReturn(expectedResponse); boolean result = jedis.getbit(key, offset); assertThat(result, sameInstance(expectedResponse)); verify(commandExecutor).executeCommand(booleanCommandObject); verify(commandObjects).getbit(key, offset); } @Test public void testGetbitBinary() { byte[] key = "key".getBytes(); long offset = 10L; boolean expectedResponse = true; when(commandObjects.getbit(key, offset)).thenReturn(booleanCommandObject); when(commandExecutor.executeCommand(booleanCommandObject)).thenReturn(expectedResponse); boolean result = jedis.getbit(key, offset); assertThat(result, sameInstance(expectedResponse)); verify(commandExecutor).executeCommand(booleanCommandObject); verify(commandObjects).getbit(key, offset); } @Test public void testSetbit() { String key = "key"; long offset = 10L; boolean value = true; boolean expectedResponse = true; when(commandObjects.setbit(key, offset, value)).thenReturn(booleanCommandObject); when(commandExecutor.executeCommand(booleanCommandObject)).thenReturn(expectedResponse); boolean result = jedis.setbit(key, offset, value); assertThat(result, sameInstance(expectedResponse)); verify(commandExecutor).executeCommand(booleanCommandObject); verify(commandObjects).setbit(key, offset, value); } @Test public void testSetbitBinary() { byte[] key = "key".getBytes(); long offset = 10L; boolean value = true; boolean expectedResponse = true; when(commandObjects.setbit(key, offset, value)).thenReturn(booleanCommandObject); when(commandExecutor.executeCommand(booleanCommandObject)).thenReturn(expectedResponse); boolean result = jedis.setbit(key, offset, value); assertThat(result, sameInstance(expectedResponse)); verify(commandExecutor).executeCommand(booleanCommandObject); verify(commandObjects).setbit(key, offset, value); } } ================================================ FILE: src/test/java/redis/clients/jedis/mocked/unified/UnifiedJedisBloomFilterCommandsTest.java ================================================ package redis.clients.jedis.mocked.unified; import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.sameInstance; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; import java.util.AbstractMap; import java.util.Arrays; import java.util.HashMap; import java.util.List; import java.util.Map; import org.junit.jupiter.api.Test; import redis.clients.jedis.bloom.BFInsertParams; import redis.clients.jedis.bloom.BFReserveParams; public class UnifiedJedisBloomFilterCommandsTest extends UnifiedJedisMockedTestBase { @Test public void testBfAdd() { String key = "testBloom"; String item = "item1"; boolean expectedResponse = true; when(commandObjects.bfAdd(key, item)).thenReturn(booleanCommandObject); when(commandExecutor.executeCommand(booleanCommandObject)).thenReturn(expectedResponse); boolean result = jedis.bfAdd(key, item); assertThat(result, sameInstance(expectedResponse)); verify(commandExecutor).executeCommand(booleanCommandObject); verify(commandObjects).bfAdd(key, item); } @Test public void testBfCard() { String key = "testBloom"; long expectedResponse = 42L; when(commandObjects.bfCard(key)).thenReturn(longCommandObject); when(commandExecutor.executeCommand(longCommandObject)).thenReturn(expectedResponse); long result = jedis.bfCard(key); assertThat(result, sameInstance(expectedResponse)); verify(commandExecutor).executeCommand(longCommandObject); verify(commandObjects).bfCard(key); } @Test public void testBfExists() { String key = "testBloom"; String item = "item1"; boolean expectedResponse = true; when(commandObjects.bfExists(key, item)).thenReturn(booleanCommandObject); when(commandExecutor.executeCommand(booleanCommandObject)).thenReturn(expectedResponse); boolean result = jedis.bfExists(key, item); assertThat(result, sameInstance(expectedResponse)); verify(commandExecutor).executeCommand(booleanCommandObject); verify(commandObjects).bfExists(key, item); } @Test public void testBfInfo() { String key = "testBloom"; Map expectedResponse = new HashMap<>(); expectedResponse.put("size", 42L); expectedResponse.put("numberOfFilters", 3L); expectedResponse.put("insertedItems", 1000L); when(commandObjects.bfInfo(key)).thenReturn(mapStringObjectCommandObject); when(commandExecutor.executeCommand(mapStringObjectCommandObject)).thenReturn(expectedResponse); Map result = jedis.bfInfo(key); assertThat(result, sameInstance(expectedResponse)); verify(commandExecutor).executeCommand(mapStringObjectCommandObject); verify(commandObjects).bfInfo(key); } @Test public void testBfInsert() { String key = "testBloom"; String[] items = { "item1", "item2" }; List expectedResponse = Arrays.asList(true, false); when(commandObjects.bfInsert(key, items)).thenReturn(listBooleanCommandObject); when(commandExecutor.executeCommand(listBooleanCommandObject)).thenReturn(expectedResponse); List result = jedis.bfInsert(key, items); assertThat(result, sameInstance(expectedResponse)); verify(commandExecutor).executeCommand(listBooleanCommandObject); verify(commandObjects).bfInsert(key, items); } @Test public void testBfInsertWithParams() { String key = "testBloom"; BFInsertParams insertParams = new BFInsertParams().noCreate(); String[] items = { "item1", "item2" }; List expectedResponse = Arrays.asList(true, false); when(commandObjects.bfInsert(key, insertParams, items)).thenReturn(listBooleanCommandObject); when(commandExecutor.executeCommand(listBooleanCommandObject)).thenReturn(expectedResponse); List result = jedis.bfInsert(key, insertParams, items); assertThat(result, sameInstance(expectedResponse)); verify(commandExecutor).executeCommand(listBooleanCommandObject); verify(commandObjects).bfInsert(key, insertParams, items); } @Test public void testBfLoadChunk() { String key = "testBloom"; long iterator = 1L; byte[] data = new byte[]{ 1, 2, 3 }; String expectedResponse = "OK"; when(commandObjects.bfLoadChunk(key, iterator, data)).thenReturn(stringCommandObject); when(commandExecutor.executeCommand(stringCommandObject)).thenReturn(expectedResponse); String result = jedis.bfLoadChunk(key, iterator, data); assertThat(result, sameInstance(expectedResponse)); verify(commandExecutor).executeCommand(stringCommandObject); verify(commandObjects).bfLoadChunk(key, iterator, data); } @Test public void testBfMAdd() { String key = "testBloom"; String[] items = { "item1", "item2", "item3" }; List expectedResponse = Arrays.asList(true, false, true); when(commandObjects.bfMAdd(key, items)).thenReturn(listBooleanCommandObject); when(commandExecutor.executeCommand(listBooleanCommandObject)).thenReturn(expectedResponse); List result = jedis.bfMAdd(key, items); assertThat(result, sameInstance(expectedResponse)); verify(commandExecutor).executeCommand(listBooleanCommandObject); verify(commandObjects).bfMAdd(key, items); } @Test public void testBfMExists() { String key = "testBloom"; String[] items = { "item1", "item2" }; List expectedResponse = Arrays.asList(true, false); when(commandObjects.bfMExists(key, items)).thenReturn(listBooleanCommandObject); when(commandExecutor.executeCommand(listBooleanCommandObject)).thenReturn(expectedResponse); List result = jedis.bfMExists(key, items); assertThat(result, sameInstance(expectedResponse)); verify(commandExecutor).executeCommand(listBooleanCommandObject); verify(commandObjects).bfMExists(key, items); } @Test public void testBfReserve() { String key = "testBloom"; double errorRate = 0.01; long capacity = 10000L; String expectedResponse = "OK"; when(commandObjects.bfReserve(key, errorRate, capacity)).thenReturn(stringCommandObject); when(commandExecutor.executeCommand(stringCommandObject)).thenReturn(expectedResponse); String result = jedis.bfReserve(key, errorRate, capacity); assertThat(result, sameInstance(expectedResponse)); verify(commandExecutor).executeCommand(stringCommandObject); verify(commandObjects).bfReserve(key, errorRate, capacity); } @Test public void testBfReserveWithParams() { String key = "testBloom"; double errorRate = 0.01; long capacity = 10000L; BFReserveParams reserveParams = new BFReserveParams().expansion(2); String expectedResponse = "OK"; when(commandObjects.bfReserve(key, errorRate, capacity, reserveParams)).thenReturn(stringCommandObject); when(commandExecutor.executeCommand(stringCommandObject)).thenReturn(expectedResponse); String result = jedis.bfReserve(key, errorRate, capacity, reserveParams); assertThat(result, sameInstance(expectedResponse)); verify(commandExecutor).executeCommand(stringCommandObject); verify(commandObjects).bfReserve(key, errorRate, capacity, reserveParams); } @Test public void testBfScanDump() { String key = "testBloom"; long iterator = 0L; Map.Entry expectedResponse = new AbstractMap.SimpleEntry<>(1L, new byte[]{ 1, 2, 3 }); when(commandObjects.bfScanDump(key, iterator)).thenReturn(entryLongBytesCommandObject); when(commandExecutor.executeCommand(entryLongBytesCommandObject)).thenReturn(expectedResponse); Map.Entry result = jedis.bfScanDump(key, iterator); assertThat(result, sameInstance(expectedResponse)); verify(commandExecutor).executeCommand(entryLongBytesCommandObject); verify(commandObjects).bfScanDump(key, iterator); } } ================================================ FILE: src/test/java/redis/clients/jedis/mocked/unified/UnifiedJedisConnectionManagementCommandsTest.java ================================================ package redis.clients.jedis.mocked.unified; import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.equalTo; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; import org.junit.jupiter.api.Test; public class UnifiedJedisConnectionManagementCommandsTest extends UnifiedJedisMockedTestBase { @Test public void testPing() { when(commandObjects.ping()).thenReturn(stringCommandObject); when(commandExecutor.executeCommand(stringCommandObject)).thenReturn("foo"); String result = jedis.ping(); assertThat(result, equalTo("foo")); verify(commandObjects).ping(); verify(commandExecutor).executeCommand(stringCommandObject); } } ================================================ FILE: src/test/java/redis/clients/jedis/mocked/unified/UnifiedJedisCountMinSketchCommandsTest.java ================================================ package redis.clients.jedis.mocked.unified; import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.sameInstance; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; import java.util.Arrays; import java.util.HashMap; import java.util.List; import java.util.Map; import org.junit.jupiter.api.Test; public class UnifiedJedisCountMinSketchCommandsTest extends UnifiedJedisMockedTestBase { @Test public void testCmsIncrBy() { String key = "testCMS"; Map itemIncrements = new HashMap<>(); itemIncrements.put("item1", 1L); itemIncrements.put("item2", 2L); List expectedResponse = Arrays.asList(1L, 2L); when(commandObjects.cmsIncrBy(key, itemIncrements)).thenReturn(listLongCommandObject); when(commandExecutor.executeCommand(listLongCommandObject)).thenReturn(expectedResponse); List result = jedis.cmsIncrBy(key, itemIncrements); assertThat(result, sameInstance(expectedResponse)); verify(commandExecutor).executeCommand(listLongCommandObject); verify(commandObjects).cmsIncrBy(key, itemIncrements); } @Test public void testCmsInfo() { String key = "testCMS"; Map expectedResponse = new HashMap<>(); expectedResponse.put("width", 1000L); expectedResponse.put("depth", 5L); expectedResponse.put("count", 42L); when(commandObjects.cmsInfo(key)).thenReturn(mapStringObjectCommandObject); when(commandExecutor.executeCommand(mapStringObjectCommandObject)).thenReturn(expectedResponse); Map result = jedis.cmsInfo(key); assertThat(result, sameInstance(expectedResponse)); verify(commandExecutor).executeCommand(mapStringObjectCommandObject); verify(commandObjects).cmsInfo(key); } @Test public void testCmsInitByDim() { String key = "testCMS"; long width = 1000L; long depth = 5L; String expectedResponse = "OK"; when(commandObjects.cmsInitByDim(key, width, depth)).thenReturn(stringCommandObject); when(commandExecutor.executeCommand(stringCommandObject)).thenReturn(expectedResponse); String result = jedis.cmsInitByDim(key, width, depth); assertThat(result, sameInstance(expectedResponse)); verify(commandExecutor).executeCommand(stringCommandObject); verify(commandObjects).cmsInitByDim(key, width, depth); } @Test public void testCmsInitByProb() { String key = "testCMS"; double error = 0.01; double probability = 0.99; String expectedResponse = "OK"; when(commandObjects.cmsInitByProb(key, error, probability)).thenReturn(stringCommandObject); when(commandExecutor.executeCommand(stringCommandObject)).thenReturn(expectedResponse); String result = jedis.cmsInitByProb(key, error, probability); assertThat(result, sameInstance(expectedResponse)); verify(commandExecutor).executeCommand(stringCommandObject); verify(commandObjects).cmsInitByProb(key, error, probability); } @Test public void testCmsMerge() { String destKey = "destCMS"; String[] keys = { "cms1", "cms2" }; String expectedResponse = "OK"; when(commandObjects.cmsMerge(destKey, keys)).thenReturn(stringCommandObject); when(commandExecutor.executeCommand(stringCommandObject)).thenReturn(expectedResponse); String result = jedis.cmsMerge(destKey, keys); assertThat(result, sameInstance(expectedResponse)); verify(commandExecutor).executeCommand(stringCommandObject); verify(commandObjects).cmsMerge(destKey, keys); } @Test public void testCmsMergeWithWeights() { String destKey = "destCMS"; Map keysAndWeights = new HashMap<>(); keysAndWeights.put("cms1", 1L); keysAndWeights.put("cms2", 2L); String expectedResponse = "OK"; when(commandObjects.cmsMerge(destKey, keysAndWeights)).thenReturn(stringCommandObject); when(commandExecutor.executeCommand(stringCommandObject)).thenReturn(expectedResponse); String result = jedis.cmsMerge(destKey, keysAndWeights); assertThat(result, sameInstance(expectedResponse)); verify(commandExecutor).executeCommand(stringCommandObject); verify(commandObjects).cmsMerge(destKey, keysAndWeights); } @Test public void testCmsQuery() { String key = "testCMS"; String[] items = { "item1", "item2" }; List expectedResponse = Arrays.asList(42L, 27L); when(commandObjects.cmsQuery(key, items)).thenReturn(listLongCommandObject); when(commandExecutor.executeCommand(listLongCommandObject)).thenReturn(expectedResponse); List result = jedis.cmsQuery(key, items); assertThat(result, sameInstance(expectedResponse)); verify(commandExecutor).executeCommand(listLongCommandObject); verify(commandObjects).cmsQuery(key, items); } } ================================================ FILE: src/test/java/redis/clients/jedis/mocked/unified/UnifiedJedisCuckooFilterCommandsTest.java ================================================ package redis.clients.jedis.mocked.unified; import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.sameInstance; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; import java.util.AbstractMap; import java.util.Arrays; import java.util.HashMap; import java.util.List; import java.util.Map; import org.junit.jupiter.api.Test; import redis.clients.jedis.bloom.CFInsertParams; import redis.clients.jedis.bloom.CFReserveParams; public class UnifiedJedisCuckooFilterCommandsTest extends UnifiedJedisMockedTestBase { @Test public void testCfAdd() { String key = "testCuckooFilter"; String item = "item1"; boolean expectedResponse = true; when(commandObjects.cfAdd(key, item)).thenReturn(booleanCommandObject); when(commandExecutor.executeCommand(booleanCommandObject)).thenReturn(expectedResponse); boolean result = jedis.cfAdd(key, item); assertThat(result, sameInstance(expectedResponse)); verify(commandExecutor).executeCommand(booleanCommandObject); verify(commandObjects).cfAdd(key, item); } @Test public void testCfAddNx() { String key = "testCuckooFilter"; String item = "item1"; boolean expectedResponse = true; when(commandObjects.cfAddNx(key, item)).thenReturn(booleanCommandObject); when(commandExecutor.executeCommand(booleanCommandObject)).thenReturn(expectedResponse); boolean result = jedis.cfAddNx(key, item); assertThat(result, sameInstance(expectedResponse)); verify(commandExecutor).executeCommand(booleanCommandObject); verify(commandObjects).cfAddNx(key, item); } @Test public void testCfCount() { String key = "testCuckooFilter"; String item = "item1"; long expectedResponse = 42L; when(commandObjects.cfCount(key, item)).thenReturn(longCommandObject); when(commandExecutor.executeCommand(longCommandObject)).thenReturn(expectedResponse); long result = jedis.cfCount(key, item); assertThat(result, sameInstance(expectedResponse)); verify(commandExecutor).executeCommand(longCommandObject); verify(commandObjects).cfCount(key, item); } @Test public void testCfDel() { String key = "testCuckooFilter"; String item = "item1"; boolean expectedResponse = true; when(commandObjects.cfDel(key, item)).thenReturn(booleanCommandObject); when(commandExecutor.executeCommand(booleanCommandObject)).thenReturn(expectedResponse); boolean result = jedis.cfDel(key, item); assertThat(result, sameInstance(expectedResponse)); verify(commandExecutor).executeCommand(booleanCommandObject); verify(commandObjects).cfDel(key, item); } @Test public void testCfExists() { String key = "testCuckooFilter"; String item = "item1"; boolean expectedResponse = true; when(commandObjects.cfExists(key, item)).thenReturn(booleanCommandObject); when(commandExecutor.executeCommand(booleanCommandObject)).thenReturn(expectedResponse); boolean result = jedis.cfExists(key, item); assertThat(result, sameInstance(expectedResponse)); verify(commandExecutor).executeCommand(booleanCommandObject); verify(commandObjects).cfExists(key, item); } @Test public void testCfInfo() { String key = "testCuckooFilter"; Map expectedResponse = new HashMap<>(); expectedResponse.put("size", 42L); expectedResponse.put("bucketSize", 2L); expectedResponse.put("maxIterations", 500L); when(commandObjects.cfInfo(key)).thenReturn(mapStringObjectCommandObject); when(commandExecutor.executeCommand(mapStringObjectCommandObject)).thenReturn(expectedResponse); Map result = jedis.cfInfo(key); assertThat(result, sameInstance(expectedResponse)); verify(commandExecutor).executeCommand(mapStringObjectCommandObject); verify(commandObjects).cfInfo(key); } @Test public void testCfInsert() { String key = "testCuckooFilter"; String[] items = { "item1", "item2" }; List expectedResponse = Arrays.asList(true, false); when(commandObjects.cfInsert(key, items)).thenReturn(listBooleanCommandObject); when(commandExecutor.executeCommand(listBooleanCommandObject)).thenReturn(expectedResponse); List result = jedis.cfInsert(key, items); assertThat(result, sameInstance(expectedResponse)); verify(commandExecutor).executeCommand(listBooleanCommandObject); verify(commandObjects).cfInsert(key, items); } @Test public void testCfInsertWithParams() { String key = "testCuckooFilter"; CFInsertParams insertParams = new CFInsertParams().noCreate(); String[] items = { "item1", "item2" }; List expectedResponse = Arrays.asList(true, false); when(commandObjects.cfInsert(key, insertParams, items)).thenReturn(listBooleanCommandObject); when(commandExecutor.executeCommand(listBooleanCommandObject)).thenReturn(expectedResponse); List result = jedis.cfInsert(key, insertParams, items); assertThat(result, sameInstance(expectedResponse)); verify(commandExecutor).executeCommand(listBooleanCommandObject); verify(commandObjects).cfInsert(key, insertParams, items); } @Test public void testCfInsertNx() { String key = "testCuckooFilter"; String[] items = { "item1", "item2" }; List expectedResponse = Arrays.asList(true, false); when(commandObjects.cfInsertNx(key, items)).thenReturn(listBooleanCommandObject); when(commandExecutor.executeCommand(listBooleanCommandObject)).thenReturn(expectedResponse); List result = jedis.cfInsertNx(key, items); assertThat(result, sameInstance(expectedResponse)); verify(commandExecutor).executeCommand(listBooleanCommandObject); verify(commandObjects).cfInsertNx(key, items); } @Test public void testCfInsertNxWithParams() { String key = "testCuckooFilter"; CFInsertParams insertParams = new CFInsertParams().noCreate(); String[] items = { "item1", "item2" }; List expectedResponse = Arrays.asList(true, false); when(commandObjects.cfInsertNx(key, insertParams, items)).thenReturn(listBooleanCommandObject); when(commandExecutor.executeCommand(listBooleanCommandObject)).thenReturn(expectedResponse); List result = jedis.cfInsertNx(key, insertParams, items); assertThat(result, sameInstance(expectedResponse)); verify(commandExecutor).executeCommand(listBooleanCommandObject); verify(commandObjects).cfInsertNx(key, insertParams, items); } @Test public void testCfLoadChunk() { String key = "testCuckooFilter"; long iterator = 1L; byte[] data = new byte[]{ 1, 2, 3 }; String expectedResponse = "OK"; when(commandObjects.cfLoadChunk(key, iterator, data)).thenReturn(stringCommandObject); when(commandExecutor.executeCommand(stringCommandObject)).thenReturn(expectedResponse); String result = jedis.cfLoadChunk(key, iterator, data); assertThat(result, sameInstance(expectedResponse)); verify(commandExecutor).executeCommand(stringCommandObject); verify(commandObjects).cfLoadChunk(key, iterator, data); } @Test public void testCfMExists() { String key = "testCuckooFilter"; String[] items = { "item1", "item2", "item3" }; List expectedResponse = Arrays.asList(true, false, true); when(commandObjects.cfMExists(key, items)).thenReturn(listBooleanCommandObject); when(commandExecutor.executeCommand(listBooleanCommandObject)).thenReturn(expectedResponse); List result = jedis.cfMExists(key, items); assertThat(result, sameInstance(expectedResponse)); verify(commandExecutor).executeCommand(listBooleanCommandObject); verify(commandObjects).cfMExists(key, items); } @Test public void testCfReserve() { String key = "testCuckooFilter"; long capacity = 10000L; String expectedResponse = "OK"; when(commandObjects.cfReserve(key, capacity)).thenReturn(stringCommandObject); when(commandExecutor.executeCommand(stringCommandObject)).thenReturn(expectedResponse); String result = jedis.cfReserve(key, capacity); assertThat(result, sameInstance(expectedResponse)); verify(commandExecutor).executeCommand(stringCommandObject); verify(commandObjects).cfReserve(key, capacity); } @Test public void testCfReserveWithParams() { String key = "testCuckooFilter"; long capacity = 10000L; CFReserveParams reserveParams = new CFReserveParams().expansion(2); String expectedResponse = "OK"; when(commandObjects.cfReserve(key, capacity, reserveParams)).thenReturn(stringCommandObject); when(commandExecutor.executeCommand(stringCommandObject)).thenReturn(expectedResponse); String result = jedis.cfReserve(key, capacity, reserveParams); assertThat(result, sameInstance(expectedResponse)); verify(commandExecutor).executeCommand(stringCommandObject); verify(commandObjects).cfReserve(key, capacity, reserveParams); } @Test public void testCfScanDump() { String key = "testCuckooFilter"; long iterator = 0L; Map.Entry expectedResponse = new AbstractMap.SimpleEntry<>(1L, new byte[]{ 1, 2, 3 }); when(commandObjects.cfScanDump(key, iterator)).thenReturn(entryLongBytesCommandObject); when(commandExecutor.executeCommand(entryLongBytesCommandObject)).thenReturn(expectedResponse); Map.Entry result = jedis.cfScanDump(key, iterator); assertThat(result, sameInstance(expectedResponse)); verify(commandExecutor).executeCommand(entryLongBytesCommandObject); verify(commandObjects).cfScanDump(key, iterator); } } ================================================ FILE: src/test/java/redis/clients/jedis/mocked/unified/UnifiedJedisGenericCommandsTest.java ================================================ package redis.clients.jedis.mocked.unified; import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.contains; import static org.hamcrest.Matchers.equalTo; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertTrue; import static org.mockito.ArgumentMatchers.any; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; import java.util.Arrays; import java.util.Collections; import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Set; import org.junit.jupiter.api.Test; import org.mockito.invocation.InvocationOnMock; import org.mockito.stubbing.Answer; import redis.clients.jedis.CommandArguments; import redis.clients.jedis.Connection; import redis.clients.jedis.ScanIteration; import redis.clients.jedis.args.ExpiryOption; import redis.clients.jedis.params.MigrateParams; import redis.clients.jedis.params.RestoreParams; import redis.clients.jedis.params.ScanParams; import redis.clients.jedis.params.SortingParams; import redis.clients.jedis.resps.ScanResult; import redis.clients.jedis.util.KeyValue; public class UnifiedJedisGenericCommandsTest extends UnifiedJedisMockedTestBase { @Test public void testCopy() { String srcKey = "sourceKey"; String dstKey = "destinationKey"; boolean replace = true; when(commandObjects.copy(srcKey, dstKey, replace)).thenReturn(booleanCommandObject); when(commandExecutor.executeCommand(booleanCommandObject)).thenReturn(true); boolean result = jedis.copy(srcKey, dstKey, replace); assertTrue(result); verify(commandExecutor).executeCommand(booleanCommandObject); verify(commandObjects).copy(srcKey, dstKey, replace); } @Test public void testCopyBinary() { byte[] srcKey = new byte[]{ 1, 2, 3 }; byte[] dstKey = new byte[]{ 4, 5, 6 }; boolean replace = false; when(commandObjects.copy(srcKey, dstKey, replace)).thenReturn(booleanCommandObject); when(commandExecutor.executeCommand(booleanCommandObject)).thenReturn(true); boolean result = jedis.copy(srcKey, dstKey, replace); assertTrue(result); verify(commandExecutor).executeCommand(booleanCommandObject); verify(commandObjects).copy(srcKey, dstKey, replace); } @Test public void testDel() { String key = "key1"; when(commandObjects.del(key)).thenReturn(longCommandObject); when(commandExecutor.executeCommand(longCommandObject)).thenReturn(1L); long result = jedis.del(key); assertThat(result, equalTo(1L)); verify(commandExecutor).executeCommand(longCommandObject); verify(commandObjects).del(key); } @Test public void testDelBinary() { byte[] key = new byte[]{ 1, 2, 3 }; when(commandObjects.del(key)).thenReturn(longCommandObject); when(commandExecutor.executeCommand(longCommandObject)).thenReturn(1L); long result = jedis.del(key); assertThat(result, equalTo(1L)); verify(commandExecutor).executeCommand(longCommandObject); verify(commandObjects).del(key); } @Test public void testDelMultipleKeys() { String[] keys = { "key1", "key2", "key3" }; when(commandObjects.del(keys)).thenReturn(longCommandObject); when(commandExecutor.executeCommand(longCommandObject)).thenReturn(3L); long result = jedis.del(keys); assertThat(result, equalTo(3L)); verify(commandExecutor).executeCommand(longCommandObject); verify(commandObjects).del(keys); } @Test public void testDelMultipleKeysBinary() { byte[][] keys = { new byte[]{ 1, 2, 3 }, new byte[]{ 4, 5, 6 } }; when(commandObjects.del(keys)).thenReturn(longCommandObject); when(commandExecutor.executeCommand(longCommandObject)).thenReturn(2L); long result = jedis.del(keys); assertThat(result, equalTo(2L)); verify(commandExecutor).executeCommand(longCommandObject); verify(commandObjects).del(keys); } @Test public void testDump() { String key = "key1"; when(commandObjects.dump(key)).thenReturn(bytesCommandObject); when(commandExecutor.executeCommand(bytesCommandObject)).thenReturn(new byte[]{ 1, 2, 3 }); byte[] result = jedis.dump(key); assertThat(result, equalTo(new byte[]{ 1, 2, 3 })); verify(commandExecutor).executeCommand(bytesCommandObject); verify(commandObjects).dump(key); } @Test public void testDumpBinary() { byte[] key = new byte[]{ 1, 2, 3 }; when(commandObjects.dump(key)).thenReturn(bytesCommandObject); when(commandExecutor.executeCommand(bytesCommandObject)).thenReturn(new byte[]{ 4, 5, 6 }); byte[] result = jedis.dump(key); assertThat(result, equalTo(new byte[]{ 4, 5, 6 })); verify(commandExecutor).executeCommand(bytesCommandObject); verify(commandObjects).dump(key); } @Test public void testExists() { String key = "mykey"; when(commandObjects.exists(key)).thenReturn(booleanCommandObject); when(commandExecutor.executeCommand(booleanCommandObject)).thenReturn(true); boolean result = jedis.exists(key); assertTrue(result); verify(commandExecutor).executeCommand(booleanCommandObject); verify(commandObjects).exists(key); } @Test public void testExistsBinary() { byte[] key = new byte[]{ 1, 2, 3 }; when(commandObjects.exists(key)).thenReturn(booleanCommandObject); when(commandExecutor.executeCommand(booleanCommandObject)).thenReturn(true); boolean result = jedis.exists(key); assertTrue(result); verify(commandExecutor).executeCommand(booleanCommandObject); verify(commandObjects).exists(key); } @Test public void testExistsMultipleKeys() { String[] keys = { "key1", "key2", "key3" }; when(commandObjects.exists(keys)).thenReturn(longCommandObject); when(commandExecutor.executeCommand(longCommandObject)).thenReturn(3L); long result = jedis.exists(keys); assertThat(result, equalTo(3L)); verify(commandExecutor).executeCommand(longCommandObject); verify(commandObjects).exists(keys); } @Test public void testExistsMultipleKeysBinary() { byte[][] keys = { new byte[]{ 1, 2, 3 }, new byte[]{ 4, 5, 6 } }; when(commandObjects.exists(keys)).thenReturn(longCommandObject); when(commandExecutor.executeCommand(longCommandObject)).thenReturn(2L); long result = jedis.exists(keys); assertThat(result, equalTo(2L)); verify(commandExecutor).executeCommand(longCommandObject); verify(commandObjects).exists(keys); } @Test public void testExpire() { String key = "key1"; long seconds = 60L; when(commandObjects.expire(key, seconds)).thenReturn(longCommandObject); when(commandExecutor.executeCommand(longCommandObject)).thenReturn(1L); long result = jedis.expire(key, seconds); assertThat(result, equalTo(1L)); verify(commandExecutor).executeCommand(longCommandObject); verify(commandObjects).expire(key, seconds); } @Test public void testExpireBinary() { byte[] key = new byte[]{ 1, 2, 3 }; long seconds = 60L; when(commandObjects.expire(key, seconds)).thenReturn(longCommandObject); when(commandExecutor.executeCommand(longCommandObject)).thenReturn(1L); long result = jedis.expire(key, seconds); assertThat(result, equalTo(1L)); verify(commandExecutor).executeCommand(longCommandObject); verify(commandObjects).expire(key, seconds); } @Test public void testExpireWithExpiryOption() { String key = "key1"; long seconds = 60L; ExpiryOption expiryOption = ExpiryOption.NX; when(commandObjects.expire(key, seconds, expiryOption)).thenReturn(longCommandObject); when(commandExecutor.executeCommand(longCommandObject)).thenReturn(1L); long result = jedis.expire(key, seconds, expiryOption); assertThat(result, equalTo(1L)); verify(commandExecutor).executeCommand(longCommandObject); verify(commandObjects).expire(key, seconds, expiryOption); } @Test public void testExpireWithExpiryOptionBinary() { byte[] key = new byte[]{ 1, 2, 3 }; long seconds = 60L; ExpiryOption expiryOption = ExpiryOption.NX; when(commandObjects.expire(key, seconds, expiryOption)).thenReturn(longCommandObject); when(commandExecutor.executeCommand(longCommandObject)).thenReturn(1L); long result = jedis.expire(key, seconds, expiryOption); assertThat(result, equalTo(1L)); verify(commandExecutor).executeCommand(longCommandObject); verify(commandObjects).expire(key, seconds, expiryOption); } @Test public void testExpireAt() { String key = "key1"; long unixTime = 1633072800L; when(commandObjects.expireAt(key, unixTime)).thenReturn(longCommandObject); when(commandExecutor.executeCommand(longCommandObject)).thenReturn(1L); long result = jedis.expireAt(key, unixTime); assertThat(result, equalTo(1L)); verify(commandExecutor).executeCommand(longCommandObject); verify(commandObjects).expireAt(key, unixTime); } @Test public void testExpireAtBinary() { byte[] key = new byte[]{ 1, 2, 3 }; long unixTime = 1633072800L; when(commandObjects.expireAt(key, unixTime)).thenReturn(longCommandObject); when(commandExecutor.executeCommand(longCommandObject)).thenReturn(1L); long result = jedis.expireAt(key, unixTime); assertThat(result, equalTo(1L)); verify(commandExecutor).executeCommand(longCommandObject); verify(commandObjects).expireAt(key, unixTime); } @Test public void testExpireAtWithExpiryOption() { String key = "key1"; long unixTime = 1633072800L; ExpiryOption expiryOption = ExpiryOption.NX; when(commandObjects.expireAt(key, unixTime, expiryOption)).thenReturn(longCommandObject); when(commandExecutor.executeCommand(longCommandObject)).thenReturn(1L); long result = jedis.expireAt(key, unixTime, expiryOption); assertThat(result, equalTo(1L)); verify(commandExecutor).executeCommand(longCommandObject); verify(commandObjects).expireAt(key, unixTime, expiryOption); } @Test public void testExpireAtWithExpiryOptionBinary() { byte[] key = new byte[]{ 1, 2, 3 }; long unixTime = 1633072800L; ExpiryOption expiryOption = ExpiryOption.NX; when(commandObjects.expireAt(key, unixTime, expiryOption)).thenReturn(longCommandObject); when(commandExecutor.executeCommand(longCommandObject)).thenReturn(1L); long result = jedis.expireAt(key, unixTime, expiryOption); assertThat(result, equalTo(1L)); verify(commandExecutor).executeCommand(longCommandObject); verify(commandObjects).expireAt(key, unixTime, expiryOption); } @Test public void testExpireTime() { String key = "key1"; when(commandObjects.expireTime(key)).thenReturn(longCommandObject); when(commandExecutor.executeCommand(longCommandObject)).thenReturn(1234567890L); long result = jedis.expireTime(key); assertThat(result, equalTo(1234567890L)); verify(commandExecutor).executeCommand(longCommandObject); verify(commandObjects).expireTime(key); } @Test public void testExpireTimeBinary() { byte[] key = new byte[]{ 1, 2, 3 }; when(commandObjects.expireTime(key)).thenReturn(longCommandObject); when(commandExecutor.executeCommand(longCommandObject)).thenReturn(1234567890L); long result = jedis.expireTime(key); assertThat(result, equalTo(1234567890L)); verify(commandExecutor).executeCommand(longCommandObject); verify(commandObjects).expireTime(key); } @Test public void testKeys() { String pattern = "*"; Set expectedKeys = new HashSet<>(Arrays.asList("key1", "key2", "key3")); when(commandObjects.keys(pattern)).thenReturn(setStringCommandObject); when(commandExecutor.executeCommand(setStringCommandObject)).thenReturn(expectedKeys); Set result = jedis.keys(pattern); assertThat(result, equalTo(expectedKeys)); verify(commandExecutor).executeCommand(setStringCommandObject); verify(commandObjects).keys(pattern); } @Test public void testKeysBinary() { byte[] pattern = "key*".getBytes(); Set expectedKeys = new HashSet<>(Arrays.asList("key1".getBytes(), "key2".getBytes())); when(commandObjects.keys(pattern)).thenReturn(setBytesCommandObject); when(commandExecutor.executeCommand(setBytesCommandObject)).thenReturn(expectedKeys); Set result = jedis.keys(pattern); assertThat(result, equalTo(expectedKeys)); verify(commandExecutor).executeCommand(setBytesCommandObject); verify(commandObjects).keys(pattern); } @Test public void testMigrate() { String host = "destinationHost"; int port = 6379; String key = "myKey"; int timeout = 5000; String expectedResponse = "OK"; when(commandObjects.migrate(host, port, key, timeout)).thenReturn(stringCommandObject); when(commandExecutor.executeCommand(stringCommandObject)).thenReturn(expectedResponse); String result = jedis.migrate(host, port, key, timeout); assertThat(result, equalTo(expectedResponse)); verify(commandExecutor).executeCommand(stringCommandObject); verify(commandObjects).migrate(host, port, key, timeout); } @Test public void testMigrateBinary() { String host = "destinationHost"; int port = 6379; byte[] key = "myKey".getBytes(); int timeout = 5000; String expectedResponse = "OK"; when(commandObjects.migrate(host, port, key, timeout)).thenReturn(stringCommandObject); when(commandExecutor.executeCommand(stringCommandObject)).thenReturn(expectedResponse); String result = jedis.migrate(host, port, key, timeout); assertThat(result, equalTo(expectedResponse)); verify(commandExecutor).executeCommand(stringCommandObject); verify(commandObjects).migrate(host, port, key, timeout); } @Test public void testMigrateMultipleKeys() { String host = "destinationHost"; int port = 6379; int timeout = 5000; MigrateParams params = new MigrateParams(); String[] keys = { "key1", "key2" }; String expectedResponse = "OK"; when(commandObjects.migrate(host, port, timeout, params, keys)).thenReturn(stringCommandObject); when(commandExecutor.executeCommand(stringCommandObject)).thenReturn(expectedResponse); String result = jedis.migrate(host, port, timeout, params, keys); assertThat(result, equalTo(expectedResponse)); verify(commandExecutor).executeCommand(stringCommandObject); verify(commandObjects).migrate(host, port, timeout, params, keys); } @Test public void testMigrateMultipleKeysBinary() { String host = "destinationHost"; int port = 6379; int timeout = 5000; MigrateParams params = new MigrateParams(); byte[][] keys = { "key1".getBytes(), "key2".getBytes() }; String expectedResponse = "OK"; when(commandObjects.migrate(host, port, timeout, params, keys)).thenReturn(stringCommandObject); when(commandExecutor.executeCommand(stringCommandObject)).thenReturn(expectedResponse); String result = jedis.migrate(host, port, timeout, params, keys); assertThat(result, equalTo(expectedResponse)); verify(commandExecutor).executeCommand(stringCommandObject); verify(commandObjects).migrate(host, port, timeout, params, keys); } @Test public void testObjectEncoding() { String key = "myKey"; String expectedEncoding = "ziplist"; when(commandObjects.objectEncoding(key)).thenReturn(stringCommandObject); when(commandExecutor.executeCommand(stringCommandObject)).thenReturn(expectedEncoding); String result = jedis.objectEncoding(key); assertThat(result, equalTo(expectedEncoding)); verify(commandExecutor).executeCommand(stringCommandObject); verify(commandObjects).objectEncoding(key); } @Test public void testObjectEncodingBinary() { byte[] key = "myKey".getBytes(); byte[] expectedEncoding = "ziplist".getBytes(); when(commandObjects.objectEncoding(key)).thenReturn(bytesCommandObject); when(commandExecutor.executeCommand(bytesCommandObject)).thenReturn(expectedEncoding); byte[] result = jedis.objectEncoding(key); assertThat(result, equalTo(expectedEncoding)); verify(commandExecutor).executeCommand(bytesCommandObject); verify(commandObjects).objectEncoding(key); } @Test public void testObjectFreq() { String key = "myKey"; Long expectedFreq = 10L; when(commandObjects.objectFreq(key)).thenReturn(longCommandObject); when(commandExecutor.executeCommand(longCommandObject)).thenReturn(expectedFreq); Long result = jedis.objectFreq(key); assertThat(result, equalTo(expectedFreq)); verify(commandExecutor).executeCommand(longCommandObject); verify(commandObjects).objectFreq(key); } @Test public void testObjectFreqBinary() { byte[] key = "myKey".getBytes(); Long expectedFreq = 10L; when(commandObjects.objectFreq(key)).thenReturn(longCommandObject); when(commandExecutor.executeCommand(longCommandObject)).thenReturn(expectedFreq); Long result = jedis.objectFreq(key); assertThat(result, equalTo(expectedFreq)); verify(commandExecutor).executeCommand(longCommandObject); verify(commandObjects).objectFreq(key); } @Test public void testObjectIdletime() { String key = "myKey"; Long expectedIdletime = 3600L; when(commandObjects.objectIdletime(key)).thenReturn(longCommandObject); when(commandExecutor.executeCommand(longCommandObject)).thenReturn(expectedIdletime); Long result = jedis.objectIdletime(key); assertThat(result, equalTo(expectedIdletime)); verify(commandExecutor).executeCommand(longCommandObject); verify(commandObjects).objectIdletime(key); } @Test public void testObjectIdletimeBinary() { byte[] key = "myKey".getBytes(); Long expectedIdletime = 3600L; when(commandObjects.objectIdletime(key)).thenReturn(longCommandObject); when(commandExecutor.executeCommand(longCommandObject)).thenReturn(expectedIdletime); Long result = jedis.objectIdletime(key); assertThat(result, equalTo(expectedIdletime)); verify(commandExecutor).executeCommand(longCommandObject); verify(commandObjects).objectIdletime(key); } @Test public void testObjectRefcount() { String key = "myKey"; Long expectedRefcount = 42L; when(commandObjects.objectRefcount(key)).thenReturn(longCommandObject); when(commandExecutor.executeCommand(longCommandObject)).thenReturn(expectedRefcount); Long result = jedis.objectRefcount(key); assertThat(result, equalTo(expectedRefcount)); verify(commandExecutor).executeCommand(longCommandObject); verify(commandObjects).objectRefcount(key); } @Test public void testObjectRefcountBinary() { byte[] key = "myKey".getBytes(); Long expectedRefcount = 42L; when(commandObjects.objectRefcount(key)).thenReturn(longCommandObject); when(commandExecutor.executeCommand(longCommandObject)).thenReturn(expectedRefcount); Long result = jedis.objectRefcount(key); assertThat(result, equalTo(expectedRefcount)); verify(commandExecutor).executeCommand(longCommandObject); verify(commandObjects).objectRefcount(key); } @Test public void testPersist() { String key = "key1"; when(commandObjects.persist(key)).thenReturn(longCommandObject); when(commandExecutor.executeCommand(longCommandObject)).thenReturn(1L); long result = jedis.persist(key); assertThat(result, equalTo(1L)); verify(commandExecutor).executeCommand(longCommandObject); verify(commandObjects).persist(key); } @Test public void testPersistBinary() { byte[] key = new byte[]{ 1, 2, 3 }; when(commandObjects.persist(key)).thenReturn(longCommandObject); when(commandExecutor.executeCommand(longCommandObject)).thenReturn(1L); long result = jedis.persist(key); assertThat(result, equalTo(1L)); verify(commandExecutor).executeCommand(longCommandObject); verify(commandObjects).persist(key); } @Test public void testPexpire() { String key = "key1"; long milliseconds = 1000L; when(commandObjects.pexpire(key, milliseconds)).thenReturn(longCommandObject); when(commandExecutor.executeCommand(longCommandObject)).thenReturn(1L); long result = jedis.pexpire(key, milliseconds); assertThat(result, equalTo(1L)); verify(commandExecutor).executeCommand(longCommandObject); verify(commandObjects).pexpire(key, milliseconds); } @Test public void testPexpireBinary() { byte[] key = new byte[]{ 1, 2, 3 }; long milliseconds = 1000L; when(commandObjects.pexpire(key, milliseconds)).thenReturn(longCommandObject); when(commandExecutor.executeCommand(longCommandObject)).thenReturn(1L); long result = jedis.pexpire(key, milliseconds); assertThat(result, equalTo(1L)); verify(commandExecutor).executeCommand(longCommandObject); verify(commandObjects).pexpire(key, milliseconds); } @Test public void testPexpireWithExpiryOption() { String key = "key1"; long milliseconds = 1000L; ExpiryOption expiryOption = ExpiryOption.NX; when(commandObjects.pexpire(key, milliseconds, expiryOption)).thenReturn(longCommandObject); when(commandExecutor.executeCommand(longCommandObject)).thenReturn(1L); long result = jedis.pexpire(key, milliseconds, expiryOption); assertThat(result, equalTo(1L)); verify(commandExecutor).executeCommand(longCommandObject); verify(commandObjects).pexpire(key, milliseconds, expiryOption); } @Test public void testPexpireWithExpiryOptionBinary() { byte[] key = new byte[]{ 1, 2, 3 }; long milliseconds = 1000L; ExpiryOption expiryOption = ExpiryOption.NX; when(commandObjects.pexpire(key, milliseconds, expiryOption)).thenReturn(longCommandObject); when(commandExecutor.executeCommand(longCommandObject)).thenReturn(1L); long result = jedis.pexpire(key, milliseconds, expiryOption); assertThat(result, equalTo(1L)); verify(commandExecutor).executeCommand(longCommandObject); verify(commandObjects).pexpire(key, milliseconds, expiryOption); } @Test public void testPexpireAt() { String key = "key1"; long millisecondsTimestamp = 1633072800123L; when(commandObjects.pexpireAt(key, millisecondsTimestamp)).thenReturn(longCommandObject); when(commandExecutor.executeCommand(longCommandObject)).thenReturn(1L); long result = jedis.pexpireAt(key, millisecondsTimestamp); assertThat(result, equalTo(1L)); verify(commandExecutor).executeCommand(longCommandObject); verify(commandObjects).pexpireAt(key, millisecondsTimestamp); } @Test public void testPexpireAtBinary() { byte[] key = new byte[]{ 1, 2, 3 }; long millisecondsTimestamp = 1633072800123L; when(commandObjects.pexpireAt(key, millisecondsTimestamp)).thenReturn(longCommandObject); when(commandExecutor.executeCommand(longCommandObject)).thenReturn(1L); long result = jedis.pexpireAt(key, millisecondsTimestamp); assertThat(result, equalTo(1L)); verify(commandExecutor).executeCommand(longCommandObject); verify(commandObjects).pexpireAt(key, millisecondsTimestamp); } @Test public void testPexpireAtWithExpiryOption() { String key = "key1"; long millisecondsTimestamp = 1633072800123L; ExpiryOption expiryOption = ExpiryOption.NX; when(commandObjects.pexpireAt(key, millisecondsTimestamp, expiryOption)).thenReturn(longCommandObject); when(commandExecutor.executeCommand(longCommandObject)).thenReturn(1L); long result = jedis.pexpireAt(key, millisecondsTimestamp, expiryOption); assertThat(result, equalTo(1L)); verify(commandExecutor).executeCommand(longCommandObject); verify(commandObjects).pexpireAt(key, millisecondsTimestamp, expiryOption); } @Test public void testPexpireAtWithExpiryOptionBinary() { byte[] key = new byte[]{ 1, 2, 3 }; long millisecondsTimestamp = 1633072800123L; ExpiryOption expiryOption = ExpiryOption.NX; when(commandObjects.pexpireAt(key, millisecondsTimestamp, expiryOption)).thenReturn(longCommandObject); when(commandExecutor.executeCommand(longCommandObject)).thenReturn(1L); long result = jedis.pexpireAt(key, millisecondsTimestamp, expiryOption); assertThat(result, equalTo(1L)); verify(commandExecutor).executeCommand(longCommandObject); verify(commandObjects).pexpireAt(key, millisecondsTimestamp, expiryOption); } @Test public void testPexpireTime() { String key = "key1"; when(commandObjects.pexpireTime(key)).thenReturn(longCommandObject); when(commandExecutor.executeCommand(longCommandObject)).thenReturn(1234567890123L); long result = jedis.pexpireTime(key); assertThat(result, equalTo(1234567890123L)); verify(commandExecutor).executeCommand(longCommandObject); verify(commandObjects).pexpireTime(key); } @Test public void testPexpireTimeBinary() { byte[] key = new byte[]{ 1, 2, 3 }; when(commandObjects.pexpireTime(key)).thenReturn(longCommandObject); when(commandExecutor.executeCommand(longCommandObject)).thenReturn(1234567890123L); long result = jedis.pexpireTime(key); assertThat(result, equalTo(1234567890123L)); verify(commandExecutor).executeCommand(longCommandObject); verify(commandObjects).pexpireTime(key); } @Test public void testPttl() { String key = "key1"; when(commandObjects.pttl(key)).thenReturn(longCommandObject); when(commandExecutor.executeCommand(longCommandObject)).thenReturn(120000L); long result = jedis.pttl(key); assertThat(result, equalTo(120000L)); verify(commandExecutor).executeCommand(longCommandObject); verify(commandObjects).pttl(key); } @Test public void testPttlBinary() { byte[] key = new byte[]{ 1, 2, 3 }; when(commandObjects.pttl(key)).thenReturn(longCommandObject); when(commandExecutor.executeCommand(longCommandObject)).thenReturn(120000L); long result = jedis.pttl(key); assertThat(result, equalTo(120000L)); verify(commandExecutor).executeCommand(longCommandObject); verify(commandObjects).pttl(key); } @Test public void testRandomKey() { String expectedKey = "randomKey"; when(commandObjects.randomKey()).thenReturn(stringCommandObject); when(commandExecutor.executeCommand(stringCommandObject)).thenReturn(expectedKey); String result = jedis.randomKey(); assertThat(result, equalTo(expectedKey)); verify(commandExecutor).executeCommand(stringCommandObject); verify(commandObjects).randomKey(); } @Test public void testRandomBinaryKey() { byte[] expectedKey = "randomKey".getBytes(); when(commandObjects.randomBinaryKey()).thenReturn(bytesCommandObject); when(commandExecutor.executeCommand(bytesCommandObject)).thenReturn(expectedKey); byte[] result = jedis.randomBinaryKey(); assertThat(result, equalTo(expectedKey)); verify(commandExecutor).executeCommand(bytesCommandObject); verify(commandObjects).randomBinaryKey(); } @Test public void testRename() { String oldkey = "oldKey"; String newkey = "newKey"; String expectedStatus = "OK"; when(commandObjects.rename(oldkey, newkey)).thenReturn(stringCommandObject); when(commandExecutor.executeCommand(stringCommandObject)).thenReturn(expectedStatus); String result = jedis.rename(oldkey, newkey); assertEquals(expectedStatus, result); verify(commandExecutor).executeCommand(stringCommandObject); verify(commandObjects).rename(oldkey, newkey); } @Test public void testRenameBinary() { byte[] oldkey = new byte[]{ 1, 2, 3 }; byte[] newkey = new byte[]{ 4, 5, 6 }; String expectedStatus = "OK"; when(commandObjects.rename(oldkey, newkey)).thenReturn(stringCommandObject); when(commandExecutor.executeCommand(stringCommandObject)).thenReturn(expectedStatus); String result = jedis.rename(oldkey, newkey); assertEquals(expectedStatus, result); verify(commandExecutor).executeCommand(stringCommandObject); verify(commandObjects).rename(oldkey, newkey); } @Test public void testRenamenx() { String oldkey = "oldKey"; String newkey = "newKey"; long expected = 1L; when(commandObjects.renamenx(oldkey, newkey)).thenReturn(longCommandObject); when(commandExecutor.executeCommand(longCommandObject)).thenReturn(expected); long result = jedis.renamenx(oldkey, newkey); assertEquals(expected, result); verify(commandExecutor).executeCommand(longCommandObject); verify(commandObjects).renamenx(oldkey, newkey); } @Test public void testRenamenxBinary() { byte[] oldkey = new byte[]{ 1, 2, 3 }; byte[] newkey = new byte[]{ 4, 5, 6 }; long expected = 1L; when(commandObjects.renamenx(oldkey, newkey)).thenReturn(longCommandObject); when(commandExecutor.executeCommand(longCommandObject)).thenReturn(expected); long result = jedis.renamenx(oldkey, newkey); assertThat(result, equalTo(expected)); verify(commandExecutor).executeCommand(longCommandObject); verify(commandObjects).renamenx(oldkey, newkey); } @Test public void testRestore() { String key = "key1"; long ttl = 0L; byte[] serializedValue = new byte[]{ 1, 2, 3 }; when(commandObjects.restore(key, ttl, serializedValue)).thenReturn(stringCommandObject); when(commandExecutor.executeCommand(stringCommandObject)).thenReturn("OK"); String result = jedis.restore(key, ttl, serializedValue); assertThat(result, equalTo("OK")); verify(commandExecutor).executeCommand(stringCommandObject); verify(commandObjects).restore(key, ttl, serializedValue); } @Test public void testRestoreBinary() { byte[] key = new byte[]{ 1, 2, 3 }; long ttl = 1000L; byte[] serializedValue = new byte[]{ 4, 5, 6 }; when(commandObjects.restore(key, ttl, serializedValue)).thenReturn(stringCommandObject); when(commandExecutor.executeCommand(stringCommandObject)).thenReturn("OK"); String result = jedis.restore(key, ttl, serializedValue); assertThat(result, equalTo("OK")); verify(commandExecutor).executeCommand(stringCommandObject); verify(commandObjects).restore(key, ttl, serializedValue); } @Test public void testRestoreWithParams() { String key = "key1"; long ttl = 0L; byte[] serializedValue = new byte[]{ 1, 2, 3 }; RestoreParams params = new RestoreParams(); when(commandObjects.restore(key, ttl, serializedValue, params)).thenReturn(stringCommandObject); when(commandExecutor.executeCommand(stringCommandObject)).thenReturn("OK"); String result = jedis.restore(key, ttl, serializedValue, params); assertThat(result, equalTo("OK")); verify(commandExecutor).executeCommand(stringCommandObject); verify(commandObjects).restore(key, ttl, serializedValue, params); } @Test public void testRestoreWithParamsBinary() { byte[] key = new byte[]{ 1, 2, 3 }; long ttl = 1000L; byte[] serializedValue = new byte[]{ 4, 5, 6 }; RestoreParams params = new RestoreParams(); when(commandObjects.restore(key, ttl, serializedValue, params)).thenReturn(stringCommandObject); when(commandExecutor.executeCommand(stringCommandObject)).thenReturn("OK"); String result = jedis.restore(key, ttl, serializedValue, params); assertThat(result, equalTo("OK")); verify(commandExecutor).executeCommand(stringCommandObject); verify(commandObjects).restore(key, ttl, serializedValue, params); } @Test public void testScan() { String cursor = "0"; ScanResult expectedScanResult = new ScanResult<>(cursor, Arrays.asList("key1", "key2")); when(commandObjects.scan(cursor)).thenReturn(scanResultStringCommandObject); when(commandExecutor.executeCommand(scanResultStringCommandObject)).thenReturn(expectedScanResult); ScanResult result = jedis.scan(cursor); assertThat(result, equalTo(expectedScanResult)); verify(commandExecutor).executeCommand(scanResultStringCommandObject); verify(commandObjects).scan(cursor); } @Test public void testScanBinary() { byte[] cursor = "0".getBytes(); ScanResult expectedScanResult = new ScanResult<>(cursor, Arrays.asList("key1".getBytes(), "key2".getBytes())); when(commandObjects.scan(cursor)).thenReturn(scanResultBytesCommandObject); when(commandExecutor.executeCommand(scanResultBytesCommandObject)).thenReturn(expectedScanResult); ScanResult result = jedis.scan(cursor); assertThat(result, equalTo(expectedScanResult)); verify(commandExecutor).executeCommand(scanResultBytesCommandObject); verify(commandObjects).scan(cursor); } @Test public void testScanWithParams() { String cursor = "0"; ScanParams params = new ScanParams().match("*").count(10); ScanResult expectedScanResult = new ScanResult<>(cursor, Arrays.asList("key1", "key2")); when(commandObjects.scan(cursor, params)).thenReturn(scanResultStringCommandObject); when(commandExecutor.executeCommand(scanResultStringCommandObject)).thenReturn(expectedScanResult); ScanResult result = jedis.scan(cursor, params); assertThat(result, equalTo(expectedScanResult)); verify(commandExecutor).executeCommand(scanResultStringCommandObject); verify(commandObjects).scan(cursor, params); } @Test public void testScanWithParamsBinary() { byte[] cursor = "0".getBytes(); ScanParams params = new ScanParams().match("*".getBytes()).count(10); ScanResult expectedScanResult = new ScanResult<>(cursor, Arrays.asList("key1".getBytes(), "key2".getBytes())); when(commandObjects.scan(cursor, params)).thenReturn(scanResultBytesCommandObject); when(commandExecutor.executeCommand(scanResultBytesCommandObject)).thenReturn(expectedScanResult); ScanResult result = jedis.scan(cursor, params); assertThat(result, equalTo(expectedScanResult)); verify(commandExecutor).executeCommand(scanResultBytesCommandObject); verify(commandObjects).scan(cursor, params); } @Test public void testScanWithType() { String cursor = "0"; ScanParams params = new ScanParams().match("*").count(10); String type = "hash"; ScanResult expectedScanResult = new ScanResult<>(cursor, Arrays.asList("key1", "key2")); when(commandObjects.scan(cursor, params, type)).thenReturn(scanResultStringCommandObject); when(commandExecutor.executeCommand(scanResultStringCommandObject)).thenReturn(expectedScanResult); ScanResult result = jedis.scan(cursor, params, type); assertThat(result, equalTo(expectedScanResult)); verify(commandExecutor).executeCommand(scanResultStringCommandObject); verify(commandObjects).scan(cursor, params, type); } @Test public void testScanWithTypeBinary() { byte[] cursor = "0".getBytes(); ScanParams params = new ScanParams().match("*".getBytes()).count(10); byte[] type = "string".getBytes(); ScanResult expectedScanResult = new ScanResult<>(cursor, Arrays.asList("key1".getBytes(), "key2".getBytes())); when(commandObjects.scan(cursor, params, type)).thenReturn(scanResultBytesCommandObject); when(commandExecutor.executeCommand(scanResultBytesCommandObject)).thenReturn(expectedScanResult); ScanResult result = jedis.scan(cursor, params, type); assertThat(result, equalTo(expectedScanResult)); verify(commandExecutor).executeCommand(scanResultBytesCommandObject); verify(commandObjects).scan(cursor, params, type); } @Test public void testScanIteration() { String cursor = "0"; String key1 = "key1"; String key2 = "key2"; Connection connection = mock(Connection.class); when(connection.executeCommand(any(CommandArguments.class))) .thenReturn(Arrays.asList(cursor.getBytes(), Arrays.asList(key1.getBytes(), key2.getBytes()))); when(connectionProvider.getConnectionMap()).thenAnswer(new Answer>() { @Override public Map answer(InvocationOnMock invocationOnMock) { return Collections.singletonMap("c", connection); } }); ScanIteration result = jedis.scanIteration(10, "prefix:*"); ScanResult batch = result.nextBatch(); assertThat(batch.getCursor(), equalTo(cursor)); assertThat(batch.getResult(), contains(key1, key2)); verify(connectionProvider).getConnectionMap(); } @Test public void testScanIterationWithType() { String cursor = "0"; String key1 = "key1"; String key2 = "key2"; Connection connection = mock(Connection.class); when(connection.executeCommand(any(CommandArguments.class))) .thenReturn(Arrays.asList(cursor.getBytes(), Arrays.asList(key1.getBytes(), key2.getBytes()))); when(connectionProvider.getConnectionMap()).thenAnswer(new Answer>() { @Override public Map answer(InvocationOnMock invocationOnMock) { return Collections.singletonMap("c", connection); } }); ScanIteration result = jedis.scanIteration(10, "prefix:*", "zset"); ScanResult batch = result.nextBatch(); assertThat(batch.getCursor(), equalTo(cursor)); assertThat(batch.getResult(), contains(key1, key2)); verify(connectionProvider).getConnectionMap(); } @Test public void testSort() { String key = "key1"; List expected = Arrays.asList("one", "two", "three"); when(commandObjects.sort(key)).thenReturn(listStringCommandObject); when(commandExecutor.executeCommand(listStringCommandObject)).thenReturn(expected); List result = jedis.sort(key); assertThat(result, equalTo(expected)); verify(commandExecutor).executeCommand(listStringCommandObject); verify(commandObjects).sort(key); } @Test public void testSortBinary() { byte[] key = new byte[]{ 1, 2, 3 }; List expected = Arrays.asList(new byte[]{ 4 }, new byte[]{ 5 }, new byte[]{ 6 }); when(commandObjects.sort(key)).thenReturn(listBytesCommandObject); when(commandExecutor.executeCommand(listBytesCommandObject)).thenReturn(expected); List result = jedis.sort(key); assertThat(result, equalTo(expected)); verify(commandExecutor).executeCommand(listBytesCommandObject); verify(commandObjects).sort(key); } @Test public void testSortWithParams() { String key = "key1"; SortingParams sortingParams = new SortingParams().asc(); List expected = Arrays.asList("one", "three", "two"); when(commandObjects.sort(key, sortingParams)).thenReturn(listStringCommandObject); when(commandExecutor.executeCommand(listStringCommandObject)).thenReturn(expected); List result = jedis.sort(key, sortingParams); assertThat(result, equalTo(expected)); verify(commandExecutor).executeCommand(listStringCommandObject); verify(commandObjects).sort(key, sortingParams); } @Test public void testSortWithParamsBinary() { byte[] key = new byte[]{ 1, 2, 3 }; SortingParams sortingParams = new SortingParams().asc(); List expected = Arrays.asList(new byte[]{ 4 }, new byte[]{ 6 }, new byte[]{ 5 }); when(commandObjects.sort(key, sortingParams)).thenReturn(listBytesCommandObject); when(commandExecutor.executeCommand(listBytesCommandObject)).thenReturn(expected); List result = jedis.sort(key, sortingParams); assertThat(result, equalTo(expected)); verify(commandExecutor).executeCommand(listBytesCommandObject); verify(commandObjects).sort(key, sortingParams); } @Test public void testSortStore() { String key = "key1"; String dstkey = "resultKey"; when(commandObjects.sort(key, dstkey)).thenReturn(longCommandObject); when(commandExecutor.executeCommand(longCommandObject)).thenReturn(3L); long result = jedis.sort(key, dstkey); assertThat(result, equalTo(3L)); verify(commandExecutor).executeCommand(longCommandObject); verify(commandObjects).sort(key, dstkey); } @Test public void testSortStoreBinary() { byte[] key = new byte[]{ 1, 2, 3 }; byte[] dstkey = new byte[]{ 7, 8, 9 }; when(commandObjects.sort(key, dstkey)).thenReturn(longCommandObject); when(commandExecutor.executeCommand(longCommandObject)).thenReturn(3L); long result = jedis.sort(key, dstkey); assertThat(result, equalTo(3L)); verify(commandExecutor).executeCommand(longCommandObject); verify(commandObjects).sort(key, dstkey); } @Test public void testSortStoreWithParams() { String key = "key1"; SortingParams sortingParams = new SortingParams().asc(); String dstkey = "resultKey"; when(commandObjects.sort(key, sortingParams, dstkey)).thenReturn(longCommandObject); when(commandExecutor.executeCommand(longCommandObject)).thenReturn(3L); long result = jedis.sort(key, sortingParams, dstkey); assertThat(result, equalTo(3L)); verify(commandExecutor).executeCommand(longCommandObject); verify(commandObjects).sort(key, sortingParams, dstkey); } @Test public void testSortStoreWithParamsBinary() { byte[] key = new byte[]{ 1, 2, 3 }; SortingParams sortingParams = new SortingParams().asc(); byte[] dstkey = new byte[]{ 7, 8, 9 }; when(commandObjects.sort(key, sortingParams, dstkey)).thenReturn(longCommandObject); when(commandExecutor.executeCommand(longCommandObject)).thenReturn(3L); long result = jedis.sort(key, sortingParams, dstkey); assertThat(result, equalTo(3L)); verify(commandExecutor).executeCommand(longCommandObject); verify(commandObjects).sort(key, sortingParams, dstkey); } @Test public void testSortReadonly() { String key = "key1"; SortingParams sortingParams = new SortingParams().asc(); List expected = Arrays.asList("one", "three", "two"); when(commandObjects.sortReadonly(key, sortingParams)).thenReturn(listStringCommandObject); when(commandExecutor.executeCommand(listStringCommandObject)).thenReturn(expected); List result = jedis.sortReadonly(key, sortingParams); assertThat(result, equalTo(expected)); verify(commandExecutor).executeCommand(listStringCommandObject); verify(commandObjects).sortReadonly(key, sortingParams); } @Test public void testSortReadonlyBinary() { byte[] key = new byte[]{ 1, 2, 3 }; SortingParams sortingParams = new SortingParams().asc(); List expected = Arrays.asList(new byte[]{ 4 }, new byte[]{ 6 }, new byte[]{ 5 }); when(commandObjects.sortReadonly(key, sortingParams)).thenReturn(listBytesCommandObject); when(commandExecutor.executeCommand(listBytesCommandObject)).thenReturn(expected); List result = jedis.sortReadonly(key, sortingParams); assertThat(result, equalTo(expected)); verify(commandExecutor).executeCommand(listBytesCommandObject); verify(commandObjects).sortReadonly(key, sortingParams); } @Test public void testTouch() { String key = "key1"; when(commandObjects.touch(key)).thenReturn(longCommandObject); when(commandExecutor.executeCommand(longCommandObject)).thenReturn(1L); long result = jedis.touch(key); assertThat(result, equalTo(1L)); verify(commandExecutor).executeCommand(longCommandObject); verify(commandObjects).touch(key); } @Test public void testTouchBinary() { byte[] key = new byte[]{ 1, 2, 3 }; when(commandObjects.touch(key)).thenReturn(longCommandObject); when(commandExecutor.executeCommand(longCommandObject)).thenReturn(1L); long result = jedis.touch(key); assertThat(result, equalTo(1L)); verify(commandExecutor).executeCommand(longCommandObject); verify(commandObjects).touch(key); } @Test public void testTouchMultipleKeys() { String[] keys = { "key1", "key2", "key3" }; when(commandObjects.touch(keys)).thenReturn(longCommandObject); when(commandExecutor.executeCommand(longCommandObject)).thenReturn(3L); long result = jedis.touch(keys); assertThat(result, equalTo(3L)); verify(commandExecutor).executeCommand(longCommandObject); verify(commandObjects).touch(keys); } @Test public void testTouchMultipleKeysBinary() { byte[][] keys = { new byte[]{ 1, 2, 3 }, new byte[]{ 4, 5, 6 } }; when(commandObjects.touch(keys)).thenReturn(longCommandObject); when(commandExecutor.executeCommand(longCommandObject)).thenReturn(2L); long result = jedis.touch(keys); assertThat(result, equalTo(2L)); verify(commandExecutor).executeCommand(longCommandObject); verify(commandObjects).touch(keys); } @Test public void testTtl() { String key = "key1"; when(commandObjects.ttl(key)).thenReturn(longCommandObject); when(commandExecutor.executeCommand(longCommandObject)).thenReturn(120L); long result = jedis.ttl(key); assertThat(result, equalTo(120L)); verify(commandExecutor).executeCommand(longCommandObject); verify(commandObjects).ttl(key); } @Test public void testTtlBinary() { byte[] key = new byte[]{ 1, 2, 3 }; when(commandObjects.ttl(key)).thenReturn(longCommandObject); when(commandExecutor.executeCommand(longCommandObject)).thenReturn(120L); long result = jedis.ttl(key); assertThat(result, equalTo(120L)); verify(commandExecutor).executeCommand(longCommandObject); verify(commandObjects).ttl(key); } @Test public void testType() { String key = "key1"; when(commandObjects.type(key)).thenReturn(stringCommandObject); when(commandExecutor.executeCommand(stringCommandObject)).thenReturn("string"); String result = jedis.type(key); assertThat(result, equalTo("string")); verify(commandExecutor).executeCommand(stringCommandObject); verify(commandObjects).type(key); } @Test public void testTypeBinary() { byte[] key = new byte[]{ 1, 2, 3 }; when(commandObjects.type(key)).thenReturn(stringCommandObject); when(commandExecutor.executeCommand(stringCommandObject)).thenReturn("string"); String result = jedis.type(key); assertThat(result, equalTo("string")); verify(commandExecutor).executeCommand(stringCommandObject); verify(commandObjects).type(key); } @Test public void testUnlink() { String key = "key1"; when(commandObjects.unlink(key)).thenReturn(longCommandObject); when(commandExecutor.executeCommand(longCommandObject)).thenReturn(1L); long result = jedis.unlink(key); assertThat(result, equalTo(1L)); verify(commandExecutor).executeCommand(longCommandObject); verify(commandObjects).unlink(key); } @Test public void testUnlinkBinary() { byte[] key = new byte[]{ 1, 2, 3 }; when(commandObjects.unlink(key)).thenReturn(longCommandObject); when(commandExecutor.executeCommand(longCommandObject)).thenReturn(1L); long result = jedis.unlink(key); assertThat(result, equalTo(1L)); verify(commandExecutor).executeCommand(longCommandObject); verify(commandObjects).unlink(key); } @Test public void testUnlinkMultipleKeys() { String[] keys = { "key1", "key2", "key3" }; when(commandObjects.unlink(keys)).thenReturn(longCommandObject); when(commandExecutor.executeCommand(longCommandObject)).thenReturn(3L); long result = jedis.unlink(keys); assertThat(result, equalTo(3L)); verify(commandExecutor).executeCommand(longCommandObject); verify(commandObjects).unlink(keys); } @Test public void testUnlinkMultipleKeysBinary() { byte[][] keys = { new byte[]{ 1, 2, 3 }, new byte[]{ 4, 5, 6 } }; when(commandObjects.unlink(keys)).thenReturn(longCommandObject); when(commandExecutor.executeCommand(longCommandObject)).thenReturn(2L); long result = jedis.unlink(keys); assertThat(result, equalTo(2L)); verify(commandExecutor).executeCommand(longCommandObject); verify(commandObjects).unlink(keys); } @Test public void testWaitReplicas() { String sampleKey = "myKey"; int replicas = 2; long timeout = 10000L; long expectedReplicaCount = 2L; when(commandObjects.waitReplicas(sampleKey, replicas, timeout)).thenReturn(longCommandObject); when(commandExecutor.executeCommand(longCommandObject)).thenReturn(expectedReplicaCount); long result = jedis.waitReplicas(sampleKey, replicas, timeout); assertThat(result, equalTo(expectedReplicaCount)); verify(commandExecutor).executeCommand(longCommandObject); verify(commandObjects).waitReplicas(sampleKey, replicas, timeout); } @Test public void testWaitReplicasBinary() { byte[] sampleKey = "myKey".getBytes(); int replicas = 2; long timeout = 10000L; long expectedReplicaCount = 2L; when(commandObjects.waitReplicas(sampleKey, replicas, timeout)).thenReturn(longCommandObject); when(commandExecutor.executeCommand(longCommandObject)).thenReturn(expectedReplicaCount); long result = jedis.waitReplicas(sampleKey, replicas, timeout); assertThat(result, equalTo(expectedReplicaCount)); verify(commandExecutor).executeCommand(longCommandObject); verify(commandObjects).waitReplicas(sampleKey, replicas, timeout); } @Test public void testWaitAOF() { String sampleKey = "myKey"; long numLocal = 1L; long numReplicas = 2L; long timeout = 10000L; KeyValue expectedResponse = new KeyValue<>(numLocal, numReplicas); when(commandObjects.waitAOF(sampleKey, numLocal, numReplicas, timeout)).thenReturn(keyValueLongLongCommandObject); when(commandExecutor.executeCommand(keyValueLongLongCommandObject)).thenReturn(expectedResponse); KeyValue result = jedis.waitAOF(sampleKey, numLocal, numReplicas, timeout); assertThat(result, equalTo(expectedResponse)); verify(commandExecutor).executeCommand(keyValueLongLongCommandObject); verify(commandObjects).waitAOF(sampleKey, numLocal, numReplicas, timeout); } @Test public void testWaitAOFBinary() { byte[] sampleKey = "myKey".getBytes(); long numLocal = 1L; long numReplicas = 2L; long timeout = 10000L; KeyValue expectedResponse = new KeyValue<>(numLocal, numReplicas); when(commandObjects.waitAOF(sampleKey, numLocal, numReplicas, timeout)).thenReturn(keyValueLongLongCommandObject); when(commandExecutor.executeCommand(keyValueLongLongCommandObject)).thenReturn(expectedResponse); KeyValue result = jedis.waitAOF(sampleKey, numLocal, numReplicas, timeout); assertThat(result, equalTo(expectedResponse)); verify(commandExecutor).executeCommand(keyValueLongLongCommandObject); verify(commandObjects).waitAOF(sampleKey, numLocal, numReplicas, timeout); } } ================================================ FILE: src/test/java/redis/clients/jedis/mocked/unified/UnifiedJedisGeospatialCommandsTest.java ================================================ package redis.clients.jedis.mocked.unified; import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.equalTo; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; import java.util.ArrayList; import java.util.Arrays; import java.util.HashMap; import java.util.List; import java.util.Map; import org.junit.jupiter.api.Test; import redis.clients.jedis.GeoCoordinate; import redis.clients.jedis.args.GeoUnit; import redis.clients.jedis.params.GeoAddParams; import redis.clients.jedis.params.GeoRadiusParam; import redis.clients.jedis.params.GeoRadiusStoreParam; import redis.clients.jedis.params.GeoSearchParam; import redis.clients.jedis.resps.GeoRadiusResponse; public class UnifiedJedisGeospatialCommandsTest extends UnifiedJedisMockedTestBase { @Test public void testGeoadd() { String key = "cities"; double longitude = 13.361389; double latitude = 38.115556; String member = "Palermo"; long expectedAdded = 1L; when(commandObjects.geoadd(key, longitude, latitude, member)).thenReturn(longCommandObject); when(commandExecutor.executeCommand(longCommandObject)).thenReturn(expectedAdded); long result = jedis.geoadd(key, longitude, latitude, member); assertThat(result, equalTo(expectedAdded)); verify(commandExecutor).executeCommand(longCommandObject); verify(commandObjects).geoadd(key, longitude, latitude, member); } @Test public void testGeoaddBinary() { byte[] key = "cities".getBytes(); double longitude = 13.361389; double latitude = 38.115556; byte[] member = "Palermo".getBytes(); long expectedAdded = 1L; when(commandObjects.geoadd(key, longitude, latitude, member)).thenReturn(longCommandObject); when(commandExecutor.executeCommand(longCommandObject)).thenReturn(expectedAdded); long result = jedis.geoadd(key, longitude, latitude, member); assertThat(result, equalTo(expectedAdded)); verify(commandExecutor).executeCommand(longCommandObject); verify(commandObjects).geoadd(key, longitude, latitude, member); } @Test public void testGeoaddMap() { String key = "cities"; Map memberCoordinateMap = new HashMap<>(); memberCoordinateMap.put("Palermo", new GeoCoordinate(13.361389, 38.115556)); memberCoordinateMap.put("Catania", new GeoCoordinate(15.087269, 37.502669)); long expectedAdded = 2L; when(commandObjects.geoadd(key, memberCoordinateMap)).thenReturn(longCommandObject); when(commandExecutor.executeCommand(longCommandObject)).thenReturn(expectedAdded); long result = jedis.geoadd(key, memberCoordinateMap); assertThat(result, equalTo(expectedAdded)); verify(commandExecutor).executeCommand(longCommandObject); verify(commandObjects).geoadd(key, memberCoordinateMap); } @Test public void testGeoaddMapBinary() { byte[] key = "cities".getBytes(); Map memberCoordinateMap = new HashMap<>(); memberCoordinateMap.put("Palermo".getBytes(), new GeoCoordinate(13.361389, 38.115556)); memberCoordinateMap.put("Catania".getBytes(), new GeoCoordinate(15.087269, 37.502669)); long expectedAdded = 2L; when(commandObjects.geoadd(key, memberCoordinateMap)).thenReturn(longCommandObject); when(commandExecutor.executeCommand(longCommandObject)).thenReturn(expectedAdded); long result = jedis.geoadd(key, memberCoordinateMap); assertThat(result, equalTo(expectedAdded)); verify(commandExecutor).executeCommand(longCommandObject); verify(commandObjects).geoadd(key, memberCoordinateMap); } @Test public void testGeoaddMapWithParams() { String key = "cities"; GeoAddParams params = GeoAddParams.geoAddParams().nx(); Map memberCoordinateMap = new HashMap<>(); memberCoordinateMap.put("Palermo", new GeoCoordinate(13.361389, 38.115556)); long expectedAdded = 1L; when(commandObjects.geoadd(key, params, memberCoordinateMap)).thenReturn(longCommandObject); when(commandExecutor.executeCommand(longCommandObject)).thenReturn(expectedAdded); long result = jedis.geoadd(key, params, memberCoordinateMap); assertThat(result, equalTo(expectedAdded)); verify(commandExecutor).executeCommand(longCommandObject); verify(commandObjects).geoadd(key, params, memberCoordinateMap); } @Test public void testGeoaddMapWithParamsBinary() { byte[] key = "cities".getBytes(); GeoAddParams params = GeoAddParams.geoAddParams().nx(); Map memberCoordinateMap = new HashMap<>(); memberCoordinateMap.put("Palermo".getBytes(), new GeoCoordinate(13.361389, 38.115556)); long expectedAdded = 1L; when(commandObjects.geoadd(key, params, memberCoordinateMap)).thenReturn(longCommandObject); when(commandExecutor.executeCommand(longCommandObject)).thenReturn(expectedAdded); long result = jedis.geoadd(key, params, memberCoordinateMap); assertThat(result, equalTo(expectedAdded)); verify(commandExecutor).executeCommand(longCommandObject); verify(commandObjects).geoadd(key, params, memberCoordinateMap); } @Test public void testGeodist() { String key = "cities"; String member1 = "Palermo"; String member2 = "Catania"; Double expectedDistance = 166274.15156960033; when(commandObjects.geodist(key, member1, member2)).thenReturn(doubleCommandObject); when(commandExecutor.executeCommand(doubleCommandObject)).thenReturn(expectedDistance); Double result = jedis.geodist(key, member1, member2); assertThat(result, equalTo(expectedDistance)); verify(commandExecutor).executeCommand(doubleCommandObject); verify(commandObjects).geodist(key, member1, member2); } @Test public void testGeodistBinary() { byte[] key = "cities".getBytes(); byte[] member1 = "Palermo".getBytes(); byte[] member2 = "Catania".getBytes(); Double expectedDistance = 166274.15156960033; when(commandObjects.geodist(key, member1, member2)).thenReturn(doubleCommandObject); when(commandExecutor.executeCommand(doubleCommandObject)).thenReturn(expectedDistance); Double result = jedis.geodist(key, member1, member2); assertThat(result, equalTo(expectedDistance)); verify(commandExecutor).executeCommand(doubleCommandObject); verify(commandObjects).geodist(key, member1, member2); } @Test public void testGeodistWithUnit() { String key = "cities"; String member1 = "Palermo"; String member2 = "Catania"; GeoUnit unit = GeoUnit.KM; Double expectedDistance = 166.274; when(commandObjects.geodist(key, member1, member2, unit)).thenReturn(doubleCommandObject); when(commandExecutor.executeCommand(doubleCommandObject)).thenReturn(expectedDistance); Double result = jedis.geodist(key, member1, member2, unit); assertThat(result, equalTo(expectedDistance)); verify(commandExecutor).executeCommand(doubleCommandObject); verify(commandObjects).geodist(key, member1, member2, unit); } @Test public void testGeodistWithUnitBinary() { byte[] key = "cities".getBytes(); byte[] member1 = "Palermo".getBytes(); byte[] member2 = "Catania".getBytes(); GeoUnit unit = GeoUnit.KM; Double expectedDistance = 166.274; when(commandObjects.geodist(key, member1, member2, unit)).thenReturn(doubleCommandObject); when(commandExecutor.executeCommand(doubleCommandObject)).thenReturn(expectedDistance); Double result = jedis.geodist(key, member1, member2, unit); assertThat(result, equalTo(expectedDistance)); verify(commandExecutor).executeCommand(doubleCommandObject); verify(commandObjects).geodist(key, member1, member2, unit); } @Test public void testGeohash() { String key = "cities"; String[] members = { "Palermo", "Catania" }; List expectedHashes = Arrays.asList("sqc8b49rny0", "sqdtr74hyu0"); when(commandObjects.geohash(key, members)).thenReturn(listStringCommandObject); when(commandExecutor.executeCommand(listStringCommandObject)).thenReturn(expectedHashes); List result = jedis.geohash(key, members); assertThat(result, equalTo(expectedHashes)); verify(commandExecutor).executeCommand(listStringCommandObject); verify(commandObjects).geohash(key, members); } @Test public void testGeohashBinary() { byte[] key = "cities".getBytes(); byte[][] members = { "Palermo".getBytes(), "Catania".getBytes() }; List expectedHashes = Arrays.asList("sqc8b49rny0".getBytes(), "sqdtr74hyu0".getBytes()); when(commandObjects.geohash(key, members)).thenReturn(listBytesCommandObject); when(commandExecutor.executeCommand(listBytesCommandObject)).thenReturn(expectedHashes); List result = jedis.geohash(key, members); assertThat(result, equalTo(expectedHashes)); verify(commandExecutor).executeCommand(listBytesCommandObject); verify(commandObjects).geohash(key, members); } @Test public void testGeopos() { String key = "cities"; String[] members = { "Palermo", "Catania" }; List expectedPositions = Arrays.asList( new GeoCoordinate(13.361389, 38.115556), new GeoCoordinate(15.087269, 37.502669) ); when(commandObjects.geopos(key, members)).thenReturn(listGeoCoordinateCommandObject); when(commandExecutor.executeCommand(listGeoCoordinateCommandObject)).thenReturn(expectedPositions); List result = jedis.geopos(key, members); assertThat(result, equalTo(expectedPositions)); verify(commandExecutor).executeCommand(listGeoCoordinateCommandObject); verify(commandObjects).geopos(key, members); } @Test public void testGeoposBinary() { byte[] key = "cities".getBytes(); byte[][] members = { "Palermo".getBytes(), "Catania".getBytes() }; List expectedPositions = Arrays.asList( new GeoCoordinate(13.361389, 38.115556), new GeoCoordinate(15.087269, 37.502669) ); when(commandObjects.geopos(key, members)).thenReturn(listGeoCoordinateCommandObject); when(commandExecutor.executeCommand(listGeoCoordinateCommandObject)).thenReturn(expectedPositions); List result = jedis.geopos(key, members); assertThat(result, equalTo(expectedPositions)); verify(commandExecutor).executeCommand(listGeoCoordinateCommandObject); verify(commandObjects).geopos(key, members); } @Test public void testGeoradius() { String key = "cities"; double longitude = 15.087269; double latitude = 37.502669; double radius = 100; GeoUnit unit = GeoUnit.KM; List expectedResponses = new ArrayList<>(); expectedResponses.add(new GeoRadiusResponse("Palermo".getBytes())); when(commandObjects.georadius(key, longitude, latitude, radius, unit)).thenReturn(listGeoRadiusResponseCommandObject); when(commandExecutor.executeCommand(listGeoRadiusResponseCommandObject)).thenReturn(expectedResponses); List result = jedis.georadius(key, longitude, latitude, radius, unit); assertThat(result, equalTo(expectedResponses)); verify(commandExecutor).executeCommand(listGeoRadiusResponseCommandObject); verify(commandObjects).georadius(key, longitude, latitude, radius, unit); } @Test public void testGeoradiusBinary() { byte[] key = "cities".getBytes(); double longitude = 15.087269; double latitude = 37.502669; double radius = 100; GeoUnit unit = GeoUnit.KM; List expectedResponses = new ArrayList<>(); expectedResponses.add(new GeoRadiusResponse("Palermo".getBytes())); when(commandObjects.georadius(key, longitude, latitude, radius, unit)).thenReturn(listGeoRadiusResponseCommandObject); when(commandExecutor.executeCommand(listGeoRadiusResponseCommandObject)).thenReturn(expectedResponses); List result = jedis.georadius(key, longitude, latitude, radius, unit); assertThat(result, equalTo(expectedResponses)); verify(commandExecutor).executeCommand(listGeoRadiusResponseCommandObject); verify(commandObjects).georadius(key, longitude, latitude, radius, unit); } @Test public void testGeoradiusReadonly() { String key = "cities"; double longitude = 15.087269; double latitude = 37.502669; double radius = 100; GeoUnit unit = GeoUnit.KM; List expectedResponses = new ArrayList<>(); expectedResponses.add(new GeoRadiusResponse("Palermo".getBytes())); when(commandObjects.georadiusReadonly(key, longitude, latitude, radius, unit)).thenReturn(listGeoRadiusResponseCommandObject); when(commandExecutor.executeCommand(listGeoRadiusResponseCommandObject)).thenReturn(expectedResponses); List result = jedis.georadiusReadonly(key, longitude, latitude, radius, unit); assertThat(result, equalTo(expectedResponses)); verify(commandExecutor).executeCommand(listGeoRadiusResponseCommandObject); verify(commandObjects).georadiusReadonly(key, longitude, latitude, radius, unit); } @Test public void testGeoradiusReadonlyBinary() { byte[] key = "cities".getBytes(); double longitude = 15.087269; double latitude = 37.502669; double radius = 100; GeoUnit unit = GeoUnit.KM; List expectedResponses = new ArrayList<>(); expectedResponses.add(new GeoRadiusResponse("Palermo".getBytes())); when(commandObjects.georadiusReadonly(key, longitude, latitude, radius, unit)).thenReturn(listGeoRadiusResponseCommandObject); when(commandExecutor.executeCommand(listGeoRadiusResponseCommandObject)).thenReturn(expectedResponses); List result = jedis.georadiusReadonly(key, longitude, latitude, radius, unit); assertThat(result, equalTo(expectedResponses)); verify(commandExecutor).executeCommand(listGeoRadiusResponseCommandObject); verify(commandObjects).georadiusReadonly(key, longitude, latitude, radius, unit); } @Test public void testGeoradiusWithParam() { String key = "cities"; double longitude = 15.087269; double latitude = 37.502669; double radius = 100; GeoUnit unit = GeoUnit.KM; GeoRadiusParam param = GeoRadiusParam.geoRadiusParam().withDist(); List expectedResponses = new ArrayList<>(); expectedResponses.add(new GeoRadiusResponse("Palermo".getBytes())); when(commandObjects.georadius(key, longitude, latitude, radius, unit, param)).thenReturn(listGeoRadiusResponseCommandObject); when(commandExecutor.executeCommand(listGeoRadiusResponseCommandObject)).thenReturn(expectedResponses); List result = jedis.georadius(key, longitude, latitude, radius, unit, param); assertThat(result, equalTo(expectedResponses)); verify(commandExecutor).executeCommand(listGeoRadiusResponseCommandObject); verify(commandObjects).georadius(key, longitude, latitude, radius, unit, param); } @Test public void testGeoradiusWithParamBinary() { byte[] key = "cities".getBytes(); double longitude = 15.087269; double latitude = 37.502669; double radius = 100; GeoUnit unit = GeoUnit.KM; GeoRadiusParam param = GeoRadiusParam.geoRadiusParam().withDist(); List expectedResponses = new ArrayList<>(); expectedResponses.add(new GeoRadiusResponse("Palermo".getBytes())); when(commandObjects.georadius(key, longitude, latitude, radius, unit, param)).thenReturn(listGeoRadiusResponseCommandObject); when(commandExecutor.executeCommand(listGeoRadiusResponseCommandObject)).thenReturn(expectedResponses); List result = jedis.georadius(key, longitude, latitude, radius, unit, param); assertThat(result, equalTo(expectedResponses)); verify(commandExecutor).executeCommand(listGeoRadiusResponseCommandObject); verify(commandObjects).georadius(key, longitude, latitude, radius, unit, param); } @Test public void testGeoradiusReadonlyWithParam() { String key = "cities"; double longitude = 15.087269; double latitude = 37.502669; double radius = 100; GeoUnit unit = GeoUnit.KM; GeoRadiusParam param = GeoRadiusParam.geoRadiusParam().withDist(); List expectedResponses = new ArrayList<>(); expectedResponses.add(new GeoRadiusResponse("Palermo".getBytes())); when(commandObjects.georadiusReadonly(key, longitude, latitude, radius, unit, param)).thenReturn(listGeoRadiusResponseCommandObject); when(commandExecutor.executeCommand(listGeoRadiusResponseCommandObject)).thenReturn(expectedResponses); List result = jedis.georadiusReadonly(key, longitude, latitude, radius, unit, param); assertThat(result, equalTo(expectedResponses)); verify(commandExecutor).executeCommand(listGeoRadiusResponseCommandObject); verify(commandObjects).georadiusReadonly(key, longitude, latitude, radius, unit, param); } @Test public void testGeoradiusReadonlyWithParamBinary() { byte[] key = "cities".getBytes(); double longitude = 15.087269; double latitude = 37.502669; double radius = 100; GeoUnit unit = GeoUnit.KM; GeoRadiusParam param = GeoRadiusParam.geoRadiusParam().withDist(); List expectedResponses = new ArrayList<>(); expectedResponses.add(new GeoRadiusResponse("Palermo".getBytes())); when(commandObjects.georadiusReadonly(key, longitude, latitude, radius, unit, param)).thenReturn(listGeoRadiusResponseCommandObject); when(commandExecutor.executeCommand(listGeoRadiusResponseCommandObject)).thenReturn(expectedResponses); List result = jedis.georadiusReadonly(key, longitude, latitude, radius, unit, param); assertThat(result, equalTo(expectedResponses)); verify(commandExecutor).executeCommand(listGeoRadiusResponseCommandObject); verify(commandObjects).georadiusReadonly(key, longitude, latitude, radius, unit, param); } @Test public void testGeoradiusByMember() { String key = "cities"; String member = "Catania"; double radius = 100; GeoUnit unit = GeoUnit.KM; List expectedResponses = new ArrayList<>(); expectedResponses.add(new GeoRadiusResponse("Palermo".getBytes())); when(commandObjects.georadiusByMember(key, member, radius, unit)).thenReturn(listGeoRadiusResponseCommandObject); when(commandExecutor.executeCommand(listGeoRadiusResponseCommandObject)).thenReturn(expectedResponses); List result = jedis.georadiusByMember(key, member, radius, unit); assertThat(result, equalTo(expectedResponses)); verify(commandExecutor).executeCommand(listGeoRadiusResponseCommandObject); verify(commandObjects).georadiusByMember(key, member, radius, unit); } @Test public void testGeoradiusByMemberBinary() { byte[] key = "cities".getBytes(); byte[] member = "Catania".getBytes(); double radius = 100; GeoUnit unit = GeoUnit.KM; List expectedResponses = new ArrayList<>(); expectedResponses.add(new GeoRadiusResponse("Palermo".getBytes())); when(commandObjects.georadiusByMember(key, member, radius, unit)).thenReturn(listGeoRadiusResponseCommandObject); when(commandExecutor.executeCommand(listGeoRadiusResponseCommandObject)).thenReturn(expectedResponses); List result = jedis.georadiusByMember(key, member, radius, unit); assertThat(result, equalTo(expectedResponses)); verify(commandExecutor).executeCommand(listGeoRadiusResponseCommandObject); verify(commandObjects).georadiusByMember(key, member, radius, unit); } @Test public void testGeoradiusByMemberReadonly() { String key = "cities"; String member = "Catania"; double radius = 100; GeoUnit unit = GeoUnit.KM; List expectedResponses = new ArrayList<>(); expectedResponses.add(new GeoRadiusResponse("Palermo".getBytes())); when(commandObjects.georadiusByMemberReadonly(key, member, radius, unit)).thenReturn(listGeoRadiusResponseCommandObject); when(commandExecutor.executeCommand(listGeoRadiusResponseCommandObject)).thenReturn(expectedResponses); List result = jedis.georadiusByMemberReadonly(key, member, radius, unit); assertThat(result, equalTo(expectedResponses)); verify(commandExecutor).executeCommand(listGeoRadiusResponseCommandObject); verify(commandObjects).georadiusByMemberReadonly(key, member, radius, unit); } @Test public void testGeoradiusByMemberReadonlyBinary() { byte[] key = "cities".getBytes(); byte[] member = "Catania".getBytes(); double radius = 100; GeoUnit unit = GeoUnit.KM; List expectedResponses = new ArrayList<>(); expectedResponses.add(new GeoRadiusResponse("Palermo".getBytes())); when(commandObjects.georadiusByMemberReadonly(key, member, radius, unit)).thenReturn(listGeoRadiusResponseCommandObject); when(commandExecutor.executeCommand(listGeoRadiusResponseCommandObject)).thenReturn(expectedResponses); List result = jedis.georadiusByMemberReadonly(key, member, radius, unit); assertThat(result, equalTo(expectedResponses)); verify(commandExecutor).executeCommand(listGeoRadiusResponseCommandObject); verify(commandObjects).georadiusByMemberReadonly(key, member, radius, unit); } @Test public void testGeoradiusByMemberWithParam() { String key = "cities"; String member = "Catania"; double radius = 100; GeoUnit unit = GeoUnit.KM; GeoRadiusParam param = GeoRadiusParam.geoRadiusParam().withDist(); List expectedResponses = new ArrayList<>(); expectedResponses.add(new GeoRadiusResponse("Palermo".getBytes())); when(commandObjects.georadiusByMember(key, member, radius, unit, param)).thenReturn(listGeoRadiusResponseCommandObject); when(commandExecutor.executeCommand(listGeoRadiusResponseCommandObject)).thenReturn(expectedResponses); List result = jedis.georadiusByMember(key, member, radius, unit, param); assertThat(result, equalTo(expectedResponses)); verify(commandExecutor).executeCommand(listGeoRadiusResponseCommandObject); verify(commandObjects).georadiusByMember(key, member, radius, unit, param); } @Test public void testGeoradiusByMemberWithParamBinary() { byte[] key = "cities".getBytes(); byte[] member = "Catania".getBytes(); double radius = 100; GeoUnit unit = GeoUnit.KM; GeoRadiusParam param = GeoRadiusParam.geoRadiusParam().withDist(); List expectedResponses = new ArrayList<>(); expectedResponses.add(new GeoRadiusResponse("Palermo".getBytes())); when(commandObjects.georadiusByMember(key, member, radius, unit, param)).thenReturn(listGeoRadiusResponseCommandObject); when(commandExecutor.executeCommand(listGeoRadiusResponseCommandObject)).thenReturn(expectedResponses); List result = jedis.georadiusByMember(key, member, radius, unit, param); assertThat(result, equalTo(expectedResponses)); verify(commandExecutor).executeCommand(listGeoRadiusResponseCommandObject); verify(commandObjects).georadiusByMember(key, member, radius, unit, param); } @Test public void testGeoradiusByMemberReadonlyWithParam() { String key = "cities"; String member = "Catania"; double radius = 100; GeoUnit unit = GeoUnit.KM; GeoRadiusParam param = GeoRadiusParam.geoRadiusParam().withDist(); List expectedResponses = new ArrayList<>(); expectedResponses.add(new GeoRadiusResponse("Palermo".getBytes())); when(commandObjects.georadiusByMemberReadonly(key, member, radius, unit, param)).thenReturn(listGeoRadiusResponseCommandObject); when(commandExecutor.executeCommand(listGeoRadiusResponseCommandObject)).thenReturn(expectedResponses); List result = jedis.georadiusByMemberReadonly(key, member, radius, unit, param); assertThat(result, equalTo(expectedResponses)); verify(commandExecutor).executeCommand(listGeoRadiusResponseCommandObject); verify(commandObjects).georadiusByMemberReadonly(key, member, radius, unit, param); } @Test public void testGeoradiusByMemberReadonlyWithParamBinary() { byte[] key = "cities".getBytes(); byte[] member = "Catania".getBytes(); double radius = 100; GeoUnit unit = GeoUnit.KM; GeoRadiusParam param = GeoRadiusParam.geoRadiusParam().withDist(); List expectedResponses = new ArrayList<>(); expectedResponses.add(new GeoRadiusResponse("Palermo".getBytes())); when(commandObjects.georadiusByMemberReadonly(key, member, radius, unit, param)).thenReturn(listGeoRadiusResponseCommandObject); when(commandExecutor.executeCommand(listGeoRadiusResponseCommandObject)).thenReturn(expectedResponses); List result = jedis.georadiusByMemberReadonly(key, member, radius, unit, param); assertThat(result, equalTo(expectedResponses)); verify(commandExecutor).executeCommand(listGeoRadiusResponseCommandObject); verify(commandObjects).georadiusByMemberReadonly(key, member, radius, unit, param); } @Test public void testGeoradiusStore() { String key = "cities"; double longitude = 15.087269; double latitude = 37.502669; double radius = 100; GeoUnit unit = GeoUnit.KM; GeoRadiusParam param = GeoRadiusParam.geoRadiusParam().withDist(); GeoRadiusStoreParam storeParam = GeoRadiusStoreParam.geoRadiusStoreParam(); long expectedStored = 2L; when(commandObjects.georadiusStore(key, longitude, latitude, radius, unit, param, storeParam)).thenReturn(longCommandObject); when(commandExecutor.executeCommand(longCommandObject)).thenReturn(expectedStored); long result = jedis.georadiusStore(key, longitude, latitude, radius, unit, param, storeParam); assertThat(result, equalTo(expectedStored)); verify(commandExecutor).executeCommand(longCommandObject); verify(commandObjects).georadiusStore(key, longitude, latitude, radius, unit, param, storeParam); } @Test public void testGeoradiusStoreBinary() { byte[] key = "cities".getBytes(); double longitude = 15.087269; double latitude = 37.502669; double radius = 100; GeoUnit unit = GeoUnit.KM; GeoRadiusParam param = GeoRadiusParam.geoRadiusParam().withDist(); GeoRadiusStoreParam storeParam = GeoRadiusStoreParam.geoRadiusStoreParam(); long expectedStored = 2L; when(commandObjects.georadiusStore(key, longitude, latitude, radius, unit, param, storeParam)).thenReturn(longCommandObject); when(commandExecutor.executeCommand(longCommandObject)).thenReturn(expectedStored); long result = jedis.georadiusStore(key, longitude, latitude, radius, unit, param, storeParam); assertThat(result, equalTo(expectedStored)); verify(commandExecutor).executeCommand(longCommandObject); verify(commandObjects).georadiusStore(key, longitude, latitude, radius, unit, param, storeParam); } @Test public void testGeoradiusByMemberStore() { String key = "cities"; String member = "Catania"; double radius = 100; GeoUnit unit = GeoUnit.KM; GeoRadiusParam param = GeoRadiusParam.geoRadiusParam().withDist(); GeoRadiusStoreParam storeParam = GeoRadiusStoreParam.geoRadiusStoreParam(); long expectedStored = 2L; when(commandObjects.georadiusByMemberStore(key, member, radius, unit, param, storeParam)).thenReturn(longCommandObject); when(commandExecutor.executeCommand(longCommandObject)).thenReturn(expectedStored); long result = jedis.georadiusByMemberStore(key, member, radius, unit, param, storeParam); assertThat(result, equalTo(expectedStored)); verify(commandExecutor).executeCommand(longCommandObject); verify(commandObjects).georadiusByMemberStore(key, member, radius, unit, param, storeParam); } @Test public void testGeoradiusByMemberStoreBinary() { byte[] key = "cities".getBytes(); byte[] member = "Catania".getBytes(); double radius = 100; GeoUnit unit = GeoUnit.KM; GeoRadiusParam param = GeoRadiusParam.geoRadiusParam().withDist(); GeoRadiusStoreParam storeParam = GeoRadiusStoreParam.geoRadiusStoreParam(); long expectedStored = 2L; when(commandObjects.georadiusByMemberStore(key, member, radius, unit, param, storeParam)).thenReturn(longCommandObject); when(commandExecutor.executeCommand(longCommandObject)).thenReturn(expectedStored); long result = jedis.georadiusByMemberStore(key, member, radius, unit, param, storeParam); assertThat(result, equalTo(expectedStored)); verify(commandExecutor).executeCommand(longCommandObject); verify(commandObjects).georadiusByMemberStore(key, member, radius, unit, param, storeParam); } @Test public void testGeosearchByMemberRadius() { String key = "cities"; String member = "Catania"; double radius = 100; GeoUnit unit = GeoUnit.KM; List expectedResponses = new ArrayList<>(); expectedResponses.add(new GeoRadiusResponse("Palermo".getBytes())); when(commandObjects.geosearch(key, member, radius, unit)).thenReturn(listGeoRadiusResponseCommandObject); when(commandExecutor.executeCommand(listGeoRadiusResponseCommandObject)).thenReturn(expectedResponses); List result = jedis.geosearch(key, member, radius, unit); assertThat(result, equalTo(expectedResponses)); verify(commandExecutor).executeCommand(listGeoRadiusResponseCommandObject); verify(commandObjects).geosearch(key, member, radius, unit); } @Test public void testGeosearchByMemberRadiusBinary() { byte[] key = "cities".getBytes(); byte[] member = "Catania".getBytes(); double radius = 100; GeoUnit unit = GeoUnit.KM; List expectedResponses = new ArrayList<>(); expectedResponses.add(new GeoRadiusResponse("Palermo".getBytes())); when(commandObjects.geosearch(key, member, radius, unit)).thenReturn(listGeoRadiusResponseCommandObject); when(commandExecutor.executeCommand(listGeoRadiusResponseCommandObject)).thenReturn(expectedResponses); List result = jedis.geosearch(key, member, radius, unit); assertThat(result, equalTo(expectedResponses)); verify(commandExecutor).executeCommand(listGeoRadiusResponseCommandObject); verify(commandObjects).geosearch(key, member, radius, unit); } @Test public void testGeosearchKeyCoordRadius() { String key = "cities"; GeoCoordinate coord = new GeoCoordinate(15.087269, 37.502669); double radius = 100; GeoUnit unit = GeoUnit.KM; List expectedResponses = new ArrayList<>(); expectedResponses.add(new GeoRadiusResponse("Palermo".getBytes())); when(commandObjects.geosearch(key, coord, radius, unit)).thenReturn(listGeoRadiusResponseCommandObject); when(commandExecutor.executeCommand(listGeoRadiusResponseCommandObject)).thenReturn(expectedResponses); List result = jedis.geosearch(key, coord, radius, unit); assertThat(result, equalTo(expectedResponses)); verify(commandExecutor).executeCommand(listGeoRadiusResponseCommandObject); verify(commandObjects).geosearch(key, coord, radius, unit); } @Test public void testGeosearchKeyCoordRadiusBinary() { byte[] key = "cities".getBytes(); GeoCoordinate coord = new GeoCoordinate(15.087269, 37.502669); double radius = 100; GeoUnit unit = GeoUnit.KM; List expectedResponses = new ArrayList<>(); expectedResponses.add(new GeoRadiusResponse("Palermo".getBytes())); when(commandObjects.geosearch(key, coord, radius, unit)).thenReturn(listGeoRadiusResponseCommandObject); when(commandExecutor.executeCommand(listGeoRadiusResponseCommandObject)).thenReturn(expectedResponses); List result = jedis.geosearch(key, coord, radius, unit); assertThat(result, equalTo(expectedResponses)); verify(commandExecutor).executeCommand(listGeoRadiusResponseCommandObject); verify(commandObjects).geosearch(key, coord, radius, unit); } @Test public void testGeosearchByMemberBox() { String key = "cities"; String member = "Catania"; double width = 100; double height = 200; GeoUnit unit = GeoUnit.KM; List expectedResponses = new ArrayList<>(); expectedResponses.add(new GeoRadiusResponse("Palermo".getBytes())); when(commandObjects.geosearch(key, member, width, height, unit)).thenReturn(listGeoRadiusResponseCommandObject); when(commandExecutor.executeCommand(listGeoRadiusResponseCommandObject)).thenReturn(expectedResponses); List result = jedis.geosearch(key, member, width, height, unit); assertThat(result, equalTo(expectedResponses)); verify(commandExecutor).executeCommand(listGeoRadiusResponseCommandObject); verify(commandObjects).geosearch(key, member, width, height, unit); } @Test public void testGeosearchByMemberBoxBinary() { byte[] key = "cities".getBytes(); byte[] member = "Catania".getBytes(); double width = 150; double height = 75; GeoUnit unit = GeoUnit.KM; List expectedResponses = new ArrayList<>(); expectedResponses.add(new GeoRadiusResponse("Palermo".getBytes())); when(commandObjects.geosearch(key, member, width, height, unit)).thenReturn(listGeoRadiusResponseCommandObject); when(commandExecutor.executeCommand(listGeoRadiusResponseCommandObject)).thenReturn(expectedResponses); List result = jedis.geosearch(key, member, width, height, unit); assertThat(result, equalTo(expectedResponses)); verify(commandExecutor).executeCommand(listGeoRadiusResponseCommandObject); verify(commandObjects).geosearch(key, member, width, height, unit); } @Test public void testGeosearchByCoordBox() { String key = "cities"; GeoCoordinate coord = new GeoCoordinate(15.087269, 37.502669); double width = 100; double height = 200; GeoUnit unit = GeoUnit.KM; List expectedResponses = new ArrayList<>(); expectedResponses.add(new GeoRadiusResponse("Palermo".getBytes())); when(commandObjects.geosearch(key, coord, width, height, unit)).thenReturn(listGeoRadiusResponseCommandObject); when(commandExecutor.executeCommand(listGeoRadiusResponseCommandObject)).thenReturn(expectedResponses); List result = jedis.geosearch(key, coord, width, height, unit); assertThat(result, equalTo(expectedResponses)); verify(commandExecutor).executeCommand(listGeoRadiusResponseCommandObject); verify(commandObjects).geosearch(key, coord, width, height, unit); } @Test public void testGeosearchByCoordBoxBinary() { byte[] key = "cities".getBytes(); GeoCoordinate coord = new GeoCoordinate(15.087269, 37.502669); double width = 150; double height = 75; GeoUnit unit = GeoUnit.KM; List expectedResponses = new ArrayList<>(); expectedResponses.add(new GeoRadiusResponse("Palermo".getBytes())); when(commandObjects.geosearch(key, coord, width, height, unit)).thenReturn(listGeoRadiusResponseCommandObject); when(commandExecutor.executeCommand(listGeoRadiusResponseCommandObject)).thenReturn(expectedResponses); List result = jedis.geosearch(key, coord, width, height, unit); assertThat(result, equalTo(expectedResponses)); verify(commandExecutor).executeCommand(listGeoRadiusResponseCommandObject); verify(commandObjects).geosearch(key, coord, width, height, unit); } @Test public void testGeosearchWithParams() { String key = "cities"; GeoSearchParam params = GeoSearchParam.geoSearchParam().byRadius(100, GeoUnit.KM).withCoord().withDist(); List expectedResponses = new ArrayList<>(); expectedResponses.add(new GeoRadiusResponse("Palermo".getBytes())); when(commandObjects.geosearch(key, params)).thenReturn(listGeoRadiusResponseCommandObject); when(commandExecutor.executeCommand(listGeoRadiusResponseCommandObject)).thenReturn(expectedResponses); List result = jedis.geosearch(key, params); assertThat(result, equalTo(expectedResponses)); verify(commandExecutor).executeCommand(listGeoRadiusResponseCommandObject); verify(commandObjects).geosearch(key, params); } @Test public void testGeosearchWithParamsBinary() { byte[] key = "cities".getBytes(); GeoSearchParam params = GeoSearchParam.geoSearchParam().byRadius(100, GeoUnit.KM).withCoord().withDist(); List expectedResponses = new ArrayList<>(); expectedResponses.add(new GeoRadiusResponse("Palermo".getBytes())); when(commandObjects.geosearch(key, params)).thenReturn(listGeoRadiusResponseCommandObject); when(commandExecutor.executeCommand(listGeoRadiusResponseCommandObject)).thenReturn(expectedResponses); List result = jedis.geosearch(key, params); assertThat(result, equalTo(expectedResponses)); verify(commandExecutor).executeCommand(listGeoRadiusResponseCommandObject); verify(commandObjects).geosearch(key, params); } @Test public void testGeosearchStoreByMemberRadius() { String dest = "cities_store"; String src = "cities"; String member = "Catania"; double radius = 100; GeoUnit unit = GeoUnit.KM; long expectedStored = 2L; when(commandObjects.geosearchStore(dest, src, member, radius, unit)).thenReturn(longCommandObject); when(commandExecutor.executeCommand(longCommandObject)).thenReturn(expectedStored); long result = jedis.geosearchStore(dest, src, member, radius, unit); assertThat(result, equalTo(expectedStored)); verify(commandExecutor).executeCommand(longCommandObject); verify(commandObjects).geosearchStore(dest, src, member, radius, unit); } @Test public void testGeosearchStoreByMemberRadiusBinary() { byte[] dest = "cities_store".getBytes(); byte[] src = "cities".getBytes(); byte[] member = "Catania".getBytes(); double radius = 100; GeoUnit unit = GeoUnit.KM; long expectedStored = 3L; when(commandObjects.geosearchStore(dest, src, member, radius, unit)).thenReturn(longCommandObject); when(commandExecutor.executeCommand(longCommandObject)).thenReturn(expectedStored); long result = jedis.geosearchStore(dest, src, member, radius, unit); assertThat(result, equalTo(expectedStored)); verify(commandExecutor).executeCommand(longCommandObject); verify(commandObjects).geosearchStore(dest, src, member, radius, unit); } @Test public void testGeosearchStoreByCoordRadius() { String dest = "cities_store"; String src = "cities"; GeoCoordinate coord = new GeoCoordinate(15.087269, 37.502669); double radius = 100; GeoUnit unit = GeoUnit.KM; long expectedStored = 2L; when(commandObjects.geosearchStore(dest, src, coord, radius, unit)).thenReturn(longCommandObject); when(commandExecutor.executeCommand(longCommandObject)).thenReturn(expectedStored); long result = jedis.geosearchStore(dest, src, coord, radius, unit); assertThat(result, equalTo(expectedStored)); verify(commandExecutor).executeCommand(longCommandObject); verify(commandObjects).geosearchStore(dest, src, coord, radius, unit); } @Test public void testGeosearchStoreByCoordRadiusBinary() { byte[] dest = "cities_store".getBytes(); byte[] src = "cities".getBytes(); GeoCoordinate coord = new GeoCoordinate(15.087269, 37.502669); double radius = 100; GeoUnit unit = GeoUnit.KM; long expectedStored = 3L; when(commandObjects.geosearchStore(dest, src, coord, radius, unit)).thenReturn(longCommandObject); when(commandExecutor.executeCommand(longCommandObject)).thenReturn(expectedStored); long result = jedis.geosearchStore(dest, src, coord, radius, unit); assertThat(result, equalTo(expectedStored)); verify(commandExecutor).executeCommand(longCommandObject); verify(commandObjects).geosearchStore(dest, src, coord, radius, unit); } @Test public void testGeosearchStoreByMemberBox() { String dest = "cities_store"; String src = "cities"; String member = "Catania"; double width = 150; double height = 75; GeoUnit unit = GeoUnit.KM; long expectedStored = 3L; when(commandObjects.geosearchStore(dest, src, member, width, height, unit)).thenReturn(longCommandObject); when(commandExecutor.executeCommand(longCommandObject)).thenReturn(expectedStored); long result = jedis.geosearchStore(dest, src, member, width, height, unit); assertThat(result, equalTo(expectedStored)); verify(commandExecutor).executeCommand(longCommandObject); verify(commandObjects).geosearchStore(dest, src, member, width, height, unit); } @Test public void testGeosearchStoreByMemberBoxBinary() { byte[] dest = "cities_store".getBytes(); byte[] src = "cities".getBytes(); byte[] member = "Catania".getBytes(); double width = 150; double height = 75; GeoUnit unit = GeoUnit.KM; long expectedStored = 3L; when(commandObjects.geosearchStore(dest, src, member, width, height, unit)).thenReturn(longCommandObject); when(commandExecutor.executeCommand(longCommandObject)).thenReturn(expectedStored); long result = jedis.geosearchStore(dest, src, member, width, height, unit); assertThat(result, equalTo(expectedStored)); verify(commandExecutor).executeCommand(longCommandObject); verify(commandObjects).geosearchStore(dest, src, member, width, height, unit); } @Test public void testGeosearchStoreByCoordBox() { String dest = "cities_store"; String src = "cities"; GeoCoordinate coord = new GeoCoordinate(15.087269, 37.502669); double width = 150; double height = 75; GeoUnit unit = GeoUnit.KM; long expectedStored = 3L; when(commandObjects.geosearchStore(dest, src, coord, width, height, unit)).thenReturn(longCommandObject); when(commandExecutor.executeCommand(longCommandObject)).thenReturn(expectedStored); long result = jedis.geosearchStore(dest, src, coord, width, height, unit); assertThat(result, equalTo(expectedStored)); verify(commandExecutor).executeCommand(longCommandObject); verify(commandObjects).geosearchStore(dest, src, coord, width, height, unit); } @Test public void testGeosearchStoreByCoordBoxBinary() { byte[] dest = "cities_store".getBytes(); byte[] src = "cities".getBytes(); GeoCoordinate coord = new GeoCoordinate(15.087269, 37.502669); double width = 150; double height = 75; GeoUnit unit = GeoUnit.KM; long expectedStored = 3L; when(commandObjects.geosearchStore(dest, src, coord, width, height, unit)).thenReturn(longCommandObject); when(commandExecutor.executeCommand(longCommandObject)).thenReturn(expectedStored); long result = jedis.geosearchStore(dest, src, coord, width, height, unit); assertThat(result, equalTo(expectedStored)); verify(commandExecutor).executeCommand(longCommandObject); verify(commandObjects).geosearchStore(dest, src, coord, width, height, unit); } @Test public void testGeosearchStoreWithParams() { String dest = "cities_store"; String src = "cities"; GeoSearchParam params = GeoSearchParam.geoSearchParam().byRadius(100, GeoUnit.KM).withCoord().withDist(); long expectedStored = 3L; when(commandObjects.geosearchStore(dest, src, params)).thenReturn(longCommandObject); when(commandExecutor.executeCommand(longCommandObject)).thenReturn(expectedStored); long result = jedis.geosearchStore(dest, src, params); assertThat(result, equalTo(expectedStored)); verify(commandExecutor).executeCommand(longCommandObject); verify(commandObjects).geosearchStore(dest, src, params); } @Test public void testGeosearchStoreWithParamsBinary() { byte[] dest = "cities_store".getBytes(); byte[] src = "cities".getBytes(); GeoSearchParam params = GeoSearchParam.geoSearchParam().byRadius(100, GeoUnit.KM).withCoord().withDist(); long expectedStored = 3L; when(commandObjects.geosearchStore(dest, src, params)).thenReturn(longCommandObject); when(commandExecutor.executeCommand(longCommandObject)).thenReturn(expectedStored); long result = jedis.geosearchStore(dest, src, params); assertThat(result, equalTo(expectedStored)); verify(commandExecutor).executeCommand(longCommandObject); verify(commandObjects).geosearchStore(dest, src, params); } @Test public void testGeosearchStoreStoreDist() { String dest = "cities_store"; String src = "cities"; GeoSearchParam params = GeoSearchParam.geoSearchParam().byRadius(100, GeoUnit.KM).withCoord().withDist(); long expectedStoredDist = 3L; when(commandObjects.geosearchStoreStoreDist(dest, src, params)).thenReturn(longCommandObject); when(commandExecutor.executeCommand(longCommandObject)).thenReturn(expectedStoredDist); long result = jedis.geosearchStoreStoreDist(dest, src, params); assertThat(result, equalTo(expectedStoredDist)); verify(commandExecutor).executeCommand(longCommandObject); verify(commandObjects).geosearchStoreStoreDist(dest, src, params); } @Test public void testGeosearchStoreStoreDistBinary() { byte[] dest = "cities_store".getBytes(); byte[] src = "cities".getBytes(); GeoSearchParam params = GeoSearchParam.geoSearchParam().byRadius(100, GeoUnit.KM).withCoord().withDist(); long expectedStoredDist = 3L; when(commandObjects.geosearchStoreStoreDist(dest, src, params)).thenReturn(longCommandObject); when(commandExecutor.executeCommand(longCommandObject)).thenReturn(expectedStoredDist); long result = jedis.geosearchStoreStoreDist(dest, src, params); assertThat(result, equalTo(expectedStoredDist)); verify(commandExecutor).executeCommand(longCommandObject); verify(commandObjects).geosearchStoreStoreDist(dest, src, params); } } ================================================ FILE: src/test/java/redis/clients/jedis/mocked/unified/UnifiedJedisHashCommandsTest.java ================================================ package redis.clients.jedis.mocked.unified; import static java.util.Arrays.asList; import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.equalTo; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; import java.util.AbstractMap; import java.util.ArrayList; import java.util.Arrays; import java.util.HashMap; import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Set; import org.junit.jupiter.api.Test; import redis.clients.jedis.args.ExpiryOption; import redis.clients.jedis.params.ScanParams; import redis.clients.jedis.resps.ScanResult; public class UnifiedJedisHashCommandsTest extends UnifiedJedisMockedTestBase { private final byte[] bfoo = { 0x01, 0x02, 0x03, 0x04 }; private final byte[] bbar1 = { 0x05, 0x06, 0x07, 0x08, 0x0A }; private final byte[] bbar2 = { 0x05, 0x06, 0x07, 0x08, 0x0B }; private final byte[] bbar3 = { 0x05, 0x06, 0x07, 0x08, 0x0C }; @Test public void testHdel() { String key = "hashKey"; String[] fields = { "field1", "field2" }; long expected = 2L; when(commandObjects.hdel(key, fields)).thenReturn(longCommandObject); when(commandExecutor.executeCommand(longCommandObject)).thenReturn(expected); long result = jedis.hdel(key, fields); assertThat(result, equalTo(expected)); verify(commandExecutor).executeCommand(longCommandObject); verify(commandObjects).hdel(key, fields); } @Test public void testHdelBinary() { byte[] key = "hashKey".getBytes(); byte[][] fields = { "field1".getBytes(), "field2".getBytes() }; long expected = 2L; // Assuming both fields were deleted when(commandObjects.hdel(key, fields)).thenReturn(longCommandObject); when(commandExecutor.executeCommand(longCommandObject)).thenReturn(expected); long result = jedis.hdel(key, fields); assertThat(result, equalTo(expected)); verify(commandExecutor).executeCommand(longCommandObject); verify(commandObjects).hdel(key, fields); } @Test public void testHexists() { String key = "hashKey"; String field = "field1"; boolean expected = true; when(commandObjects.hexists(key, field)).thenReturn(booleanCommandObject); when(commandExecutor.executeCommand(booleanCommandObject)).thenReturn(expected); boolean result = jedis.hexists(key, field); assertThat(result, equalTo(expected)); verify(commandExecutor).executeCommand(booleanCommandObject); verify(commandObjects).hexists(key, field); } @Test public void testHexistsBinary() { byte[] key = "hashKey".getBytes(); byte[] field = "field1".getBytes(); boolean expected = true; when(commandObjects.hexists(key, field)).thenReturn(booleanCommandObject); when(commandExecutor.executeCommand(booleanCommandObject)).thenReturn(expected); boolean result = jedis.hexists(key, field); assertThat(result, equalTo(expected)); verify(commandExecutor).executeCommand(booleanCommandObject); verify(commandObjects).hexists(key, field); } @Test public void testHget() { String key = "hashKey"; String field = "field1"; String expectedValue = "value1"; when(commandObjects.hget(key, field)).thenReturn(stringCommandObject); when(commandExecutor.executeCommand(stringCommandObject)).thenReturn(expectedValue); String result = jedis.hget(key, field); assertThat(result, equalTo(expectedValue)); verify(commandExecutor).executeCommand(stringCommandObject); verify(commandObjects).hget(key, field); } @Test public void testHgetBinary() { byte[] key = "hashKey".getBytes(); byte[] field = "field1".getBytes(); byte[] expectedValue = "value1".getBytes(); when(commandObjects.hget(key, field)).thenReturn(bytesCommandObject); when(commandExecutor.executeCommand(bytesCommandObject)).thenReturn(expectedValue); byte[] result = jedis.hget(key, field); assertThat(result, equalTo(expectedValue)); verify(commandExecutor).executeCommand(bytesCommandObject); verify(commandObjects).hget(key, field); } @Test public void testHgetAll() { String key = "hashKey"; Map expectedMap = new HashMap<>(); expectedMap.put("field1", "value1"); expectedMap.put("field2", "value2"); when(commandObjects.hgetAll(key)).thenReturn(mapStringStringCommandObject); when(commandExecutor.executeCommand(mapStringStringCommandObject)).thenReturn(expectedMap); Map result = jedis.hgetAll(key); assertThat(result, equalTo(expectedMap)); verify(commandExecutor).executeCommand(mapStringStringCommandObject); verify(commandObjects).hgetAll(key); } @Test public void testHgetAllBinary() { byte[] key = "hashKey".getBytes(); Map expectedMap = new HashMap<>(); expectedMap.put("field1".getBytes(), "value1".getBytes()); expectedMap.put("field2".getBytes(), "value2".getBytes()); when(commandObjects.hgetAll(key)).thenReturn(mapBytesBytesCommandObject); when(commandExecutor.executeCommand(mapBytesBytesCommandObject)).thenReturn(expectedMap); Map result = jedis.hgetAll(key); assertThat(result, equalTo(expectedMap)); verify(commandExecutor).executeCommand(mapBytesBytesCommandObject); verify(commandObjects).hgetAll(key); } @Test public void testHincrBy() { String key = "hashKey"; String field = "field1"; long increment = 2L; long expectedValue = 5L; // Assuming the original value was 3 when(commandObjects.hincrBy(key, field, increment)).thenReturn(longCommandObject); when(commandExecutor.executeCommand(longCommandObject)).thenReturn(expectedValue); long result = jedis.hincrBy(key, field, increment); assertThat(result, equalTo(expectedValue)); verify(commandExecutor).executeCommand(longCommandObject); verify(commandObjects).hincrBy(key, field, increment); } @Test public void testHincrByBinary() { byte[] key = "hashKey".getBytes(); byte[] field = "field1".getBytes(); long increment = 2L; long expectedValue = 5L; // Assuming the original value was 3 when(commandObjects.hincrBy(key, field, increment)).thenReturn(longCommandObject); when(commandExecutor.executeCommand(longCommandObject)).thenReturn(expectedValue); long result = jedis.hincrBy(key, field, increment); assertThat(result, equalTo(expectedValue)); verify(commandExecutor).executeCommand(longCommandObject); verify(commandObjects).hincrBy(key, field, increment); } @Test public void testHincrByFloat() { String key = "hashKey"; String field = "field1"; double increment = 1.5; double expectedValue = 4.5; // Assuming the original value was 3.0 when(commandObjects.hincrByFloat(key, field, increment)).thenReturn(doubleCommandObject); when(commandExecutor.executeCommand(doubleCommandObject)).thenReturn(expectedValue); double result = jedis.hincrByFloat(key, field, increment); assertThat(result, equalTo(expectedValue)); verify(commandExecutor).executeCommand(doubleCommandObject); verify(commandObjects).hincrByFloat(key, field, increment); } @Test public void testHincrByFloatBinary() { byte[] key = "hashKey".getBytes(); byte[] field = "field1".getBytes(); double increment = 1.5; double expectedValue = 4.5; // Assuming the original value was 3.0 when(commandObjects.hincrByFloat(key, field, increment)).thenReturn(doubleCommandObject); when(commandExecutor.executeCommand(doubleCommandObject)).thenReturn(expectedValue); double result = jedis.hincrByFloat(key, field, increment); assertThat(result, equalTo(expectedValue)); verify(commandExecutor).executeCommand(doubleCommandObject); verify(commandObjects).hincrByFloat(key, field, increment); } @Test public void testHkeys() { String key = "hashKey"; Set expectedKeys = new HashSet<>(Arrays.asList("field1", "field2", "field3")); when(commandObjects.hkeys(key)).thenReturn(setStringCommandObject); when(commandExecutor.executeCommand(setStringCommandObject)).thenReturn(expectedKeys); Set result = jedis.hkeys(key); assertThat(result, equalTo(expectedKeys)); verify(commandExecutor).executeCommand(setStringCommandObject); verify(commandObjects).hkeys(key); } @Test public void testHkeysBinary() { byte[] key = "hashKey".getBytes(); Set expectedKeys = new HashSet<>(Arrays.asList("field1".getBytes(), "field2".getBytes(), "field3".getBytes())); when(commandObjects.hkeys(key)).thenReturn(setBytesCommandObject); when(commandExecutor.executeCommand(setBytesCommandObject)).thenReturn(expectedKeys); Set result = jedis.hkeys(key); assertThat(result, equalTo(expectedKeys)); verify(commandExecutor).executeCommand(setBytesCommandObject); verify(commandObjects).hkeys(key); } @Test public void testHlen() { String key = "hashKey"; long expected = 3L; // Assuming there are 3 fields in the hash when(commandObjects.hlen(key)).thenReturn(longCommandObject); when(commandExecutor.executeCommand(longCommandObject)).thenReturn(expected); long result = jedis.hlen(key); assertThat(result, equalTo(expected)); verify(commandExecutor).executeCommand(longCommandObject); verify(commandObjects).hlen(key); } @Test public void testHlenBinary() { byte[] key = "hashKey".getBytes(); long expected = 3L; // Assuming there are 3 fields in the hash when(commandObjects.hlen(key)).thenReturn(longCommandObject); when(commandExecutor.executeCommand(longCommandObject)).thenReturn(expected); long result = jedis.hlen(key); assertThat(result, equalTo(expected)); verify(commandExecutor).executeCommand(longCommandObject); verify(commandObjects).hlen(key); } @Test public void testHmget() { String key = "hashKey"; String[] fields = { "field1", "field2" }; List expectedValues = Arrays.asList("value1", "value2"); when(commandObjects.hmget(key, fields)).thenReturn(listStringCommandObject); when(commandExecutor.executeCommand(listStringCommandObject)).thenReturn(expectedValues); List result = jedis.hmget(key, fields); assertThat(result, equalTo(expectedValues)); verify(commandExecutor).executeCommand(listStringCommandObject); verify(commandObjects).hmget(key, fields); } @Test public void testHmgetBinary() { byte[] key = "hashKey".getBytes(); byte[][] fields = { "field1".getBytes(), "field2".getBytes() }; List expectedValues = Arrays.asList("value1".getBytes(), "value2".getBytes()); when(commandObjects.hmget(key, fields)).thenReturn(listBytesCommandObject); when(commandExecutor.executeCommand(listBytesCommandObject)).thenReturn(expectedValues); List result = jedis.hmget(key, fields); assertThat(result, equalTo(expectedValues)); verify(commandExecutor).executeCommand(listBytesCommandObject); verify(commandObjects).hmget(key, fields); } @Test public void testHmset() { String key = "hashKey"; Map hash = new HashMap<>(); hash.put("field1", "value1"); hash.put("field2", "value2"); String expectedStatus = "OK"; when(commandObjects.hmset(key, hash)).thenReturn(stringCommandObject); when(commandExecutor.executeCommand(stringCommandObject)).thenReturn(expectedStatus); String result = jedis.hmset(key, hash); assertThat(result, equalTo(expectedStatus)); verify(commandExecutor).executeCommand(stringCommandObject); verify(commandObjects).hmset(key, hash); } @Test public void testHmsetBinary() { byte[] key = "hashKey".getBytes(); Map hash = new HashMap<>(); hash.put("field1".getBytes(), "value1".getBytes()); hash.put("field2".getBytes(), "value2".getBytes()); String expectedStatus = "OK"; when(commandObjects.hmset(key, hash)).thenReturn(stringCommandObject); when(commandExecutor.executeCommand(stringCommandObject)).thenReturn(expectedStatus); String result = jedis.hmset(key, hash); assertThat(result, equalTo(expectedStatus)); verify(commandExecutor).executeCommand(stringCommandObject); verify(commandObjects).hmset(key, hash); } @Test public void testHrandfield() { String key = "hashKey"; String expectedField = "field1"; when(commandObjects.hrandfield(key)).thenReturn(stringCommandObject); when(commandExecutor.executeCommand(stringCommandObject)).thenReturn(expectedField); String result = jedis.hrandfield(key); assertThat(result, equalTo(expectedField)); verify(commandExecutor).executeCommand(stringCommandObject); verify(commandObjects).hrandfield(key); } @Test public void testHrandfieldBinary() { byte[] key = "hashKey".getBytes(); byte[] expectedField = "field1".getBytes(); when(commandObjects.hrandfield(key)).thenReturn(bytesCommandObject); when(commandExecutor.executeCommand(bytesCommandObject)).thenReturn(expectedField); byte[] result = jedis.hrandfield(key); assertThat(result, equalTo(expectedField)); verify(commandExecutor).executeCommand(bytesCommandObject); verify(commandObjects).hrandfield(key); } @Test public void testHrandfieldCount() { String key = "hashKey"; long count = 2; List expectedFields = Arrays.asList("field1", "field2"); when(commandObjects.hrandfield(key, count)).thenReturn(listStringCommandObject); when(commandExecutor.executeCommand(listStringCommandObject)).thenReturn(expectedFields); List result = jedis.hrandfield(key, count); assertThat(result, equalTo(expectedFields)); verify(commandExecutor).executeCommand(listStringCommandObject); verify(commandObjects).hrandfield(key, count); } @Test public void testHrandfieldCountBinary() { byte[] key = "hashKey".getBytes(); long count = 2; List expectedFields = Arrays.asList("field1".getBytes(), "field2".getBytes()); when(commandObjects.hrandfield(key, count)).thenReturn(listBytesCommandObject); when(commandExecutor.executeCommand(listBytesCommandObject)).thenReturn(expectedFields); List result = jedis.hrandfield(key, count); assertThat(result, equalTo(expectedFields)); verify(commandExecutor).executeCommand(listBytesCommandObject); verify(commandObjects).hrandfield(key, count); } @Test public void testHrandfieldWithValues() { String key = "hashKey"; long count = 2; List> expectedEntries = new ArrayList<>(); expectedEntries.add(new AbstractMap.SimpleEntry<>("field1", "value1")); expectedEntries.add(new AbstractMap.SimpleEntry<>("field2", "value2")); when(commandObjects.hrandfieldWithValues(key, count)).thenReturn(listEntryStringStringCommandObject); when(commandExecutor.executeCommand(listEntryStringStringCommandObject)).thenReturn(expectedEntries); List> result = jedis.hrandfieldWithValues(key, count); assertThat(result, equalTo(expectedEntries)); verify(commandExecutor).executeCommand(listEntryStringStringCommandObject); verify(commandObjects).hrandfieldWithValues(key, count); } @Test public void testHrandfieldWithValuesBinary() { byte[] key = "hashKey".getBytes(); long count = 2; List> expectedEntries = new ArrayList<>(); expectedEntries.add(new AbstractMap.SimpleEntry<>("field1".getBytes(), "value1".getBytes())); expectedEntries.add(new AbstractMap.SimpleEntry<>("field2".getBytes(), "value2".getBytes())); when(commandObjects.hrandfieldWithValues(key, count)).thenReturn(listEntryBytesBytesCommandObject); when(commandExecutor.executeCommand(listEntryBytesBytesCommandObject)).thenReturn(expectedEntries); List> result = jedis.hrandfieldWithValues(key, count); assertThat(result, equalTo(expectedEntries)); verify(commandExecutor).executeCommand(listEntryBytesBytesCommandObject); verify(commandObjects).hrandfieldWithValues(key, count); } @Test public void testHscan() { String key = "hashKey"; String cursor = "0"; ScanParams params = new ScanParams().match("*").count(10); List> scanResultData = new ArrayList<>(); scanResultData.add(new AbstractMap.SimpleEntry<>("field1", "value1")); scanResultData.add(new AbstractMap.SimpleEntry<>("field2", "value2")); ScanResult> expectedScanResult = new ScanResult<>(cursor, scanResultData); when(commandObjects.hscan(key, cursor, params)).thenReturn(scanResultEntryStringStringCommandObject); when(commandExecutor.executeCommand(scanResultEntryStringStringCommandObject)).thenReturn(expectedScanResult); ScanResult> result = jedis.hscan(key, cursor, params); assertThat(result, equalTo(expectedScanResult)); verify(commandExecutor).executeCommand(scanResultEntryStringStringCommandObject); verify(commandObjects).hscan(key, cursor, params); } @Test public void testHscanBinary() { byte[] key = "hashKey".getBytes(); byte[] cursor = ScanParams.SCAN_POINTER_START_BINARY; ScanParams params = new ScanParams().match("*".getBytes()).count(10); List> scanResultData = new ArrayList<>(); scanResultData.add(new AbstractMap.SimpleEntry<>("field1".getBytes(), "value1".getBytes())); scanResultData.add(new AbstractMap.SimpleEntry<>("field2".getBytes(), "value2".getBytes())); ScanResult> expectedScanResult = new ScanResult<>(cursor, scanResultData); when(commandObjects.hscan(key, cursor, params)).thenReturn(scanResultEntryBytesBytesCommandObject); when(commandExecutor.executeCommand(scanResultEntryBytesBytesCommandObject)).thenReturn(expectedScanResult); ScanResult> result = jedis.hscan(key, cursor, params); assertThat(result, equalTo(expectedScanResult)); verify(commandExecutor).executeCommand(scanResultEntryBytesBytesCommandObject); verify(commandObjects).hscan(key, cursor, params); } @Test public void testHscanNoValues() { String key = "hashKey"; String cursor = "0"; ScanParams params = new ScanParams().match("*").count(10); List scanResultData = Arrays.asList("field1", "field2"); ScanResult expectedScanResult = new ScanResult<>(cursor, scanResultData); when(commandObjects.hscanNoValues(key, cursor, params)).thenReturn(scanResultStringCommandObject); when(commandExecutor.executeCommand(scanResultStringCommandObject)).thenReturn(expectedScanResult); ScanResult result = jedis.hscanNoValues(key, cursor, params); assertThat(result, equalTo(expectedScanResult)); verify(commandExecutor).executeCommand(scanResultStringCommandObject); verify(commandObjects).hscanNoValues(key, cursor, params); } @Test public void testHscanNoValuesBinary() { byte[] key = "hashKey".getBytes(); byte[] cursor = ScanParams.SCAN_POINTER_START_BINARY; ScanParams params = new ScanParams().match("*".getBytes()).count(10); List scanResultData = Arrays.asList("field1".getBytes(), "field2".getBytes()); ScanResult expectedScanResult = new ScanResult<>(cursor, scanResultData); when(commandObjects.hscanNoValues(key, cursor, params)).thenReturn(scanResultBytesCommandObject); when(commandExecutor.executeCommand(scanResultBytesCommandObject)).thenReturn(expectedScanResult); ScanResult result = jedis.hscanNoValues(key, cursor, params); assertThat(result, equalTo(expectedScanResult)); verify(commandExecutor).executeCommand(scanResultBytesCommandObject); verify(commandObjects).hscanNoValues(key, cursor, params); } @Test public void testHset() { String key = "hashKey"; String field = "field1"; String value = "value1"; long expected = 1L; // Assuming the field was newly set when(commandObjects.hset(key, field, value)).thenReturn(longCommandObject); when(commandExecutor.executeCommand(longCommandObject)).thenReturn(expected); long result = jedis.hset(key, field, value); assertThat(result, equalTo(expected)); verify(commandExecutor).executeCommand(longCommandObject); verify(commandObjects).hset(key, field, value); } @Test public void testHsetBinary() { byte[] key = "hashKey".getBytes(); byte[] field = "field1".getBytes(); byte[] value = "value1".getBytes(); long expected = 1L; // Assuming the field was newly set when(commandObjects.hset(key, field, value)).thenReturn(longCommandObject); when(commandExecutor.executeCommand(longCommandObject)).thenReturn(expected); long result = jedis.hset(key, field, value); assertThat(result, equalTo(expected)); verify(commandExecutor).executeCommand(longCommandObject); verify(commandObjects).hset(key, field, value); } @Test public void testHsetMap() { String key = "hashKey"; Map hash = new HashMap<>(); hash.put("field1", "value1"); hash.put("field2", "value2"); long expected = 2L; // Assuming both fields were newly set when(commandObjects.hset(key, hash)).thenReturn(longCommandObject); when(commandExecutor.executeCommand(longCommandObject)).thenReturn(expected); long result = jedis.hset(key, hash); assertThat(result, equalTo(expected)); verify(commandExecutor).executeCommand(longCommandObject); verify(commandObjects).hset(key, hash); } @Test public void testHsetMapBinary() { byte[] key = "hashKey".getBytes(); Map hash = new HashMap<>(); hash.put("field1".getBytes(), "value1".getBytes()); hash.put("field2".getBytes(), "value2".getBytes()); long expected = 2L; // Assuming both fields were newly set when(commandObjects.hset(key, hash)).thenReturn(longCommandObject); when(commandExecutor.executeCommand(longCommandObject)).thenReturn(expected); long result = jedis.hset(key, hash); assertThat(result, equalTo(expected)); verify(commandExecutor).executeCommand(longCommandObject); verify(commandObjects).hset(key, hash); } @Test public void testHsetObject() { String key = "myHash"; String field = "myField"; Object value = "myValue"; long expectedResponse = 1L; when(commandObjects.hsetObject(key, field, value)).thenReturn(longCommandObject); when(commandExecutor.executeCommand(longCommandObject)).thenReturn(expectedResponse); long result = jedis.hsetObject(key, field, value); assertThat(result, equalTo(expectedResponse)); verify(commandExecutor).executeCommand(longCommandObject); verify(commandObjects).hsetObject(key, field, value); } @Test public void testHsetObjectMap() { String key = "myHash"; Map hash = new HashMap<>(); hash.put("field1", "value1"); hash.put("field2", "value2"); long expectedResponse = 2L; when(commandObjects.hsetObject(key, hash)).thenReturn(longCommandObject); when(commandExecutor.executeCommand(longCommandObject)).thenReturn(expectedResponse); long result = jedis.hsetObject(key, hash); assertThat(result, equalTo(expectedResponse)); verify(commandExecutor).executeCommand(longCommandObject); verify(commandObjects).hsetObject(key, hash); } @Test public void testHsetnx() { String key = "hashKey"; String field = "field1"; String value = "value1"; long expected = 1L; // Assuming the field was newly set when(commandObjects.hsetnx(key, field, value)).thenReturn(longCommandObject); when(commandExecutor.executeCommand(longCommandObject)).thenReturn(expected); long result = jedis.hsetnx(key, field, value); assertThat(result, equalTo(expected)); verify(commandExecutor).executeCommand(longCommandObject); verify(commandObjects).hsetnx(key, field, value); } @Test public void testHsetnxBinary() { byte[] key = "hashKey".getBytes(); byte[] field = "field1".getBytes(); byte[] value = "value1".getBytes(); long expected = 1L; // Assuming the field was newly set when(commandObjects.hsetnx(key, field, value)).thenReturn(longCommandObject); when(commandExecutor.executeCommand(longCommandObject)).thenReturn(expected); long result = jedis.hsetnx(key, field, value); assertThat(result, equalTo(expected)); verify(commandExecutor).executeCommand(longCommandObject); verify(commandObjects).hsetnx(key, field, value); } @Test public void testHstrlen() { String key = "hashKey"; String field = "field1"; long expectedLength = 6L; // Assuming the value of the field is "value1" when(commandObjects.hstrlen(key, field)).thenReturn(longCommandObject); when(commandExecutor.executeCommand(longCommandObject)).thenReturn(expectedLength); long result = jedis.hstrlen(key, field); assertThat(result, equalTo(expectedLength)); verify(commandExecutor).executeCommand(longCommandObject); verify(commandObjects).hstrlen(key, field); } @Test public void testHstrlenBinary() { byte[] key = "hashKey".getBytes(); byte[] field = "field1".getBytes(); long expectedLength = 6L; // Assuming the value of the field is "value1".getBytes() when(commandObjects.hstrlen(key, field)).thenReturn(longCommandObject); when(commandExecutor.executeCommand(longCommandObject)).thenReturn(expectedLength); long result = jedis.hstrlen(key, field); assertThat(result, equalTo(expectedLength)); verify(commandExecutor).executeCommand(longCommandObject); verify(commandObjects).hstrlen(key, field); } @Test public void testHvals() { String key = "hashKey"; List expectedValues = Arrays.asList("value1", "value2", "value3"); when(commandObjects.hvals(key)).thenReturn(listStringCommandObject); when(commandExecutor.executeCommand(listStringCommandObject)).thenReturn(expectedValues); List result = jedis.hvals(key); assertThat(result, equalTo(expectedValues)); verify(commandExecutor).executeCommand(listStringCommandObject); verify(commandObjects).hvals(key); } @Test public void testHvalsBinary() { byte[] key = "hashKey".getBytes(); List expectedValues = Arrays.asList("value1".getBytes(), "value2".getBytes(), "value3".getBytes()); when(commandObjects.hvals(key)).thenReturn(listBytesCommandObject); when(commandExecutor.executeCommand(listBytesCommandObject)).thenReturn(expectedValues); List result = jedis.hvals(key); assertThat(result, equalTo(expectedValues)); verify(commandExecutor).executeCommand(listBytesCommandObject); verify(commandObjects).hvals(key); } @Test public void hexpire() { String key = "hash"; long seconds = 100; String[] fields = { "one", "two", "three" }; List expected = asList( 1L, 2L, 3L ); when(commandObjects.hexpire(key, seconds, fields)).thenReturn(listLongCommandObject); when(commandExecutor.executeCommand(listLongCommandObject)).thenReturn(expected); assertThat(jedis.hexpire(key, seconds, fields), equalTo(expected)); verify(commandExecutor).executeCommand(listLongCommandObject); verify(commandObjects).hexpire(key, seconds, fields); } @Test public void hexpireCondition() { String key = "hash"; long seconds = 100; ExpiryOption condition = mock(ExpiryOption.class); String[] fields = { "one", "two", "three" }; List expected = asList( 1L, 2L, 3L ); when(commandObjects.hexpire(key, seconds, condition, fields)).thenReturn(listLongCommandObject); when(commandExecutor.executeCommand(listLongCommandObject)).thenReturn(expected); assertThat(jedis.hexpire(key, seconds, condition, fields), equalTo(expected)); verify(commandExecutor).executeCommand(listLongCommandObject); verify(commandObjects).hexpire(key, seconds, condition, fields); } @Test public void hpexpire() { String key = "hash"; long milliseconds = 10000; String[] fields = { "one", "two", "three" }; List expected = asList( 100L, 200L, 300L ); when(commandObjects.hpexpire(key, milliseconds, fields)).thenReturn(listLongCommandObject); when(commandExecutor.executeCommand(listLongCommandObject)).thenReturn(expected); assertThat(jedis.hpexpire(key, milliseconds, fields), equalTo(expected)); verify(commandExecutor).executeCommand(listLongCommandObject); verify(commandObjects).hpexpire(key, milliseconds, fields); } @Test public void hpexpireCondition() { String key = "hash"; long milliseconds = 10000; ExpiryOption condition = mock(ExpiryOption.class); String[] fields = { "one", "two", "three" }; List expected = asList( 100L, 200L, 300L ); when(commandObjects.hpexpire(key, milliseconds, condition, fields)).thenReturn(listLongCommandObject); when(commandExecutor.executeCommand(listLongCommandObject)).thenReturn(expected); assertThat(jedis.hpexpire(key, milliseconds, condition, fields), equalTo(expected)); verify(commandExecutor).executeCommand(listLongCommandObject); verify(commandObjects).hpexpire(key, milliseconds, condition, fields); } @Test public void hexpireAt() { String key = "hash"; long seconds = 100; String[] fields = { "one", "two", "three" }; List expected = asList( 1L, 2L, 3L ); when(commandObjects.hexpireAt(key, seconds, fields)).thenReturn(listLongCommandObject); when(commandExecutor.executeCommand(listLongCommandObject)).thenReturn(expected); assertThat(jedis.hexpireAt(key, seconds, fields), equalTo(expected)); verify(commandExecutor).executeCommand(listLongCommandObject); verify(commandObjects).hexpireAt(key, seconds, fields); } @Test public void hexpireAtCondition() { String key = "hash"; long seconds = 100; ExpiryOption condition = mock(ExpiryOption.class); String[] fields = { "one", "two", "three" }; List expected = asList( 1L, 2L, 3L ); when(commandObjects.hexpireAt(key, seconds, condition, fields)).thenReturn(listLongCommandObject); when(commandExecutor.executeCommand(listLongCommandObject)).thenReturn(expected); assertThat(jedis.hexpireAt(key, seconds, condition, fields), equalTo(expected)); verify(commandExecutor).executeCommand(listLongCommandObject); verify(commandObjects).hexpireAt(key, seconds, condition, fields); } @Test public void hpexpireAt() { String key = "hash"; long milliseconds = 10000; String[] fields = { "one", "two", "three" }; List expected = asList( 1L, 2L, 3L ); when(commandObjects.hpexpireAt(key, milliseconds, fields)).thenReturn(listLongCommandObject); when(commandExecutor.executeCommand(listLongCommandObject)).thenReturn(expected); assertThat(jedis.hpexpireAt(key, milliseconds, fields), equalTo(expected)); verify(commandExecutor).executeCommand(listLongCommandObject); verify(commandObjects).hpexpireAt(key, milliseconds, fields); } @Test public void hpexpireAtCondition() { String key = "hash"; long milliseconds = 100; ExpiryOption condition = mock(ExpiryOption.class); String[] fields = { "one", "two", "three" }; List expected = asList( 1L, 2L, 3L ); when(commandObjects.hpexpireAt(key, milliseconds, condition, fields)).thenReturn(listLongCommandObject); when(commandExecutor.executeCommand(listLongCommandObject)).thenReturn(expected); assertThat(jedis.hpexpireAt(key, milliseconds, condition, fields), equalTo(expected)); verify(commandExecutor).executeCommand(listLongCommandObject); verify(commandObjects).hpexpireAt(key, milliseconds, condition, fields); } @Test public void hexpireTime() { String key = "hash"; String[] fields = { "one", "two", "three" }; List expected = asList( 10L, 20L, 30L ); when(commandObjects.hexpireTime(key, fields)).thenReturn(listLongCommandObject); when(commandExecutor.executeCommand(listLongCommandObject)).thenReturn(expected); assertThat(jedis.hexpireTime(key, fields), equalTo(expected)); verify(commandExecutor).executeCommand(listLongCommandObject); verify(commandObjects).hexpireTime(key, fields); } @Test public void hpexpireTime() { String key = "hash"; String[] fields = { "one", "two", "three" }; List expected = asList( 1000L, 2000L, 3000L ); when(commandObjects.hpexpireTime(key, fields)).thenReturn(listLongCommandObject); when(commandExecutor.executeCommand(listLongCommandObject)).thenReturn(expected); assertThat(jedis.hpexpireTime(key, fields), equalTo(expected)); verify(commandExecutor).executeCommand(listLongCommandObject); verify(commandObjects).hpexpireTime(key, fields); } @Test public void httl() { String key = "hash"; String[] fields = { "one", "two", "three" }; List expected = asList( 10L, 20L, 30L ); when(commandObjects.httl(key, fields)).thenReturn(listLongCommandObject); when(commandExecutor.executeCommand(listLongCommandObject)).thenReturn(expected); assertThat(jedis.httl(key, fields), equalTo(expected)); verify(commandExecutor).executeCommand(listLongCommandObject); verify(commandObjects).httl(key, fields); } @Test public void hpttl() { String key = "hash"; String[] fields = { "one", "two", "three" }; List expected = asList( 1000L, 2000L, 3000L ); when(commandObjects.hpttl(key, fields)).thenReturn(listLongCommandObject); when(commandExecutor.executeCommand(listLongCommandObject)).thenReturn(expected); assertThat(jedis.hpttl(key, fields), equalTo(expected)); verify(commandExecutor).executeCommand(listLongCommandObject); verify(commandObjects).hpttl(key, fields); } @Test public void hpersist() { String key = "hash"; String[] fields = { "one", "two", "three" }; List expected = asList( 1L, 2L, 3L ); when(commandObjects.hpersist(key, fields)).thenReturn(listLongCommandObject); when(commandExecutor.executeCommand(listLongCommandObject)).thenReturn(expected); assertThat(jedis.hpersist(key, fields), equalTo(expected)); verify(commandExecutor).executeCommand(listLongCommandObject); verify(commandObjects).hpersist(key, fields); } @Test public void hexpireBinary() { byte[] key = bfoo; long seconds = 100; byte[][] fields = { bbar1, bbar2, bbar3 }; List expected = asList( 1L, 2L, 3L ); when(commandObjects.hexpire(key, seconds, fields)).thenReturn(listLongCommandObject); when(commandExecutor.executeCommand(listLongCommandObject)).thenReturn(expected); assertThat(jedis.hexpire(key, seconds, fields), equalTo(expected)); verify(commandExecutor).executeCommand(listLongCommandObject); verify(commandObjects).hexpire(key, seconds, fields); } @Test public void hexpireConditionBinary() { byte[] key = bfoo; long seconds = 100; ExpiryOption condition = mock(ExpiryOption.class); byte[][] fields = { bbar1, bbar2, bbar3 }; List expected = asList( 1L, 2L, 3L ); when(commandObjects.hexpire(key, seconds, condition, fields)).thenReturn(listLongCommandObject); when(commandExecutor.executeCommand(listLongCommandObject)).thenReturn(expected); assertThat(jedis.hexpire(key, seconds, condition, fields), equalTo(expected)); verify(commandExecutor).executeCommand(listLongCommandObject); verify(commandObjects).hexpire(key, seconds, condition, fields); } @Test public void hpexpireBinary() { byte[] key = bfoo; long milliseconds = 10000; byte[][] fields = { bbar1, bbar2, bbar3 }; List expected = asList( 100L, 200L, 300L ); when(commandObjects.hpexpire(key, milliseconds, fields)).thenReturn(listLongCommandObject); when(commandExecutor.executeCommand(listLongCommandObject)).thenReturn(expected); assertThat(jedis.hpexpire(key, milliseconds, fields), equalTo(expected)); verify(commandExecutor).executeCommand(listLongCommandObject); verify(commandObjects).hpexpire(key, milliseconds, fields); } @Test public void hpexpireConditionBinary() { byte[] key = bfoo; long milliseconds = 10000; ExpiryOption condition = mock(ExpiryOption.class); byte[][] fields = { bbar1, bbar2, bbar3 }; List expected = asList( 100L, 200L, 300L ); when(commandObjects.hpexpire(key, milliseconds, condition, fields)).thenReturn(listLongCommandObject); when(commandExecutor.executeCommand(listLongCommandObject)).thenReturn(expected); assertThat(jedis.hpexpire(key, milliseconds, condition, fields), equalTo(expected)); verify(commandExecutor).executeCommand(listLongCommandObject); verify(commandObjects).hpexpire(key, milliseconds, condition, fields); } @Test public void hexpireAtBinary() { byte[] key = bfoo; long seconds = 100; byte[][] fields = { bbar1, bbar2, bbar3 }; List expected = asList( 1L, 2L, 3L ); when(commandObjects.hexpireAt(key, seconds, fields)).thenReturn(listLongCommandObject); when(commandExecutor.executeCommand(listLongCommandObject)).thenReturn(expected); assertThat(jedis.hexpireAt(key, seconds, fields), equalTo(expected)); verify(commandExecutor).executeCommand(listLongCommandObject); verify(commandObjects).hexpireAt(key, seconds, fields); } @Test public void hexpireAtConditionBinary() { byte[] key = bfoo; long seconds = 100; ExpiryOption condition = mock(ExpiryOption.class); byte[][] fields = { bbar1, bbar2, bbar3 }; List expected = asList( 1L, 2L, 3L ); when(commandObjects.hexpireAt(key, seconds, condition, fields)).thenReturn(listLongCommandObject); when(commandExecutor.executeCommand(listLongCommandObject)).thenReturn(expected); assertThat(jedis.hexpireAt(key, seconds, condition, fields), equalTo(expected)); verify(commandExecutor).executeCommand(listLongCommandObject); verify(commandObjects).hexpireAt(key, seconds, condition, fields); } @Test public void hpexpireAtBinary() { byte[] key = bfoo; long milliseconds = 10000; byte[][] fields = { bbar1, bbar2, bbar3 }; List expected = asList( 1L, 2L, 3L ); when(commandObjects.hpexpireAt(key, milliseconds, fields)).thenReturn(listLongCommandObject); when(commandExecutor.executeCommand(listLongCommandObject)).thenReturn(expected); assertThat(jedis.hpexpireAt(key, milliseconds, fields), equalTo(expected)); verify(commandExecutor).executeCommand(listLongCommandObject); verify(commandObjects).hpexpireAt(key, milliseconds, fields); } @Test public void hpexpireAtConditionBinary() { byte[] key = bfoo; long milliseconds = 100; ExpiryOption condition = mock(ExpiryOption.class); byte[][] fields = { bbar1, bbar2, bbar3 }; List expected = asList( 1L, 2L, 3L ); when(commandObjects.hpexpireAt(key, milliseconds, condition, fields)).thenReturn(listLongCommandObject); when(commandExecutor.executeCommand(listLongCommandObject)).thenReturn(expected); assertThat(jedis.hpexpireAt(key, milliseconds, condition, fields), equalTo(expected)); verify(commandExecutor).executeCommand(listLongCommandObject); verify(commandObjects).hpexpireAt(key, milliseconds, condition, fields); } @Test public void hexpireTimeBinary() { byte[] key = bfoo; byte[][] fields = { bbar1, bbar2, bbar3 }; List expected = asList( 10L, 20L, 30L ); when(commandObjects.hexpireTime(key, fields)).thenReturn(listLongCommandObject); when(commandExecutor.executeCommand(listLongCommandObject)).thenReturn(expected); assertThat(jedis.hexpireTime(key, fields), equalTo(expected)); verify(commandExecutor).executeCommand(listLongCommandObject); verify(commandObjects).hexpireTime(key, fields); } @Test public void hpexpireTimeBinary() { byte[] key = bfoo; byte[][] fields = { bbar1, bbar2, bbar3 }; List expected = asList( 1000L, 2000L, 3000L ); when(commandObjects.hpexpireTime(key, fields)).thenReturn(listLongCommandObject); when(commandExecutor.executeCommand(listLongCommandObject)).thenReturn(expected); assertThat(jedis.hpexpireTime(key, fields), equalTo(expected)); verify(commandExecutor).executeCommand(listLongCommandObject); verify(commandObjects).hpexpireTime(key, fields); } @Test public void httlBinary() { byte[] key = bfoo; byte[][] fields = { bbar1, bbar2, bbar3 }; List expected = asList( 10L, 20L, 30L ); when(commandObjects.httl(key, fields)).thenReturn(listLongCommandObject); when(commandExecutor.executeCommand(listLongCommandObject)).thenReturn(expected); assertThat(jedis.httl(key, fields), equalTo(expected)); verify(commandExecutor).executeCommand(listLongCommandObject); verify(commandObjects).httl(key, fields); } @Test public void hpttlBinary() { byte[] key = bfoo; byte[][] fields = { bbar1, bbar2, bbar3 }; List expected = asList( 1000L, 2000L, 3000L ); when(commandObjects.hpttl(key, fields)).thenReturn(listLongCommandObject); when(commandExecutor.executeCommand(listLongCommandObject)).thenReturn(expected); assertThat(jedis.hpttl(key, fields), equalTo(expected)); verify(commandExecutor).executeCommand(listLongCommandObject); verify(commandObjects).hpttl(key, fields); } @Test public void hpersistBinary() { byte[] key = bfoo; byte[][] fields = { bbar1, bbar2, bbar3 }; List expected = asList( 1L, 2L, 3L ); when(commandObjects.hpersist(key, fields)).thenReturn(listLongCommandObject); when(commandExecutor.executeCommand(listLongCommandObject)).thenReturn(expected); assertThat(jedis.hpersist(key, fields), equalTo(expected)); verify(commandExecutor).executeCommand(listLongCommandObject); verify(commandObjects).hpersist(key, fields); } } ================================================ FILE: src/test/java/redis/clients/jedis/mocked/unified/UnifiedJedisHyperloglogCommandsTest.java ================================================ package redis.clients.jedis.mocked.unified; import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.equalTo; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; import org.junit.jupiter.api.Test; public class UnifiedJedisHyperloglogCommandsTest extends UnifiedJedisMockedTestBase { @Test public void testPfadd() { String key = "hll"; String[] elements = { "element1", "element2" }; long expectedAdded = 1L; when(commandObjects.pfadd(key, elements)).thenReturn(longCommandObject); when(commandExecutor.executeCommand(longCommandObject)).thenReturn(expectedAdded); long result = jedis.pfadd(key, elements); assertThat(result, equalTo(expectedAdded)); verify(commandExecutor).executeCommand(longCommandObject); verify(commandObjects).pfadd(key, elements); } @Test public void testPfaddBinary() { byte[] key = "hll".getBytes(); byte[][] elements = { "element1".getBytes(), "element2".getBytes() }; long expectedAdded = 1L; when(commandObjects.pfadd(key, elements)).thenReturn(longCommandObject); when(commandExecutor.executeCommand(longCommandObject)).thenReturn(expectedAdded); long result = jedis.pfadd(key, elements); assertThat(result, equalTo(expectedAdded)); verify(commandExecutor).executeCommand(longCommandObject); verify(commandObjects).pfadd(key, elements); } @Test public void testPfcount() { String key = "hll"; long expectedCount = 42L; when(commandObjects.pfcount(key)).thenReturn(longCommandObject); when(commandExecutor.executeCommand(longCommandObject)).thenReturn(expectedCount); long result = jedis.pfcount(key); assertThat(result, equalTo(expectedCount)); verify(commandExecutor).executeCommand(longCommandObject); verify(commandObjects).pfcount(key); } @Test public void testPfcountBinary() { byte[] key = "hll".getBytes(); long expectedCount = 42L; when(commandObjects.pfcount(key)).thenReturn(longCommandObject); when(commandExecutor.executeCommand(longCommandObject)).thenReturn(expectedCount); long result = jedis.pfcount(key); assertThat(result, equalTo(expectedCount)); verify(commandExecutor).executeCommand(longCommandObject); verify(commandObjects).pfcount(key); } @Test public void testPfcountMultipleKeys() { String[] keys = { "hll1", "hll2" }; long expectedCount = 84L; when(commandObjects.pfcount(keys)).thenReturn(longCommandObject); when(commandExecutor.executeCommand(longCommandObject)).thenReturn(expectedCount); long result = jedis.pfcount(keys); assertThat(result, equalTo(expectedCount)); verify(commandExecutor).executeCommand(longCommandObject); verify(commandObjects).pfcount(keys); } @Test public void testPfcountMultipleKeysBinary() { byte[][] keys = { "hll1".getBytes(), "hll2".getBytes() }; long expectedCount = 84L; when(commandObjects.pfcount(keys)).thenReturn(longCommandObject); when(commandExecutor.executeCommand(longCommandObject)).thenReturn(expectedCount); long result = jedis.pfcount(keys); assertThat(result, equalTo(expectedCount)); verify(commandExecutor).executeCommand(longCommandObject); verify(commandObjects).pfcount(keys); } @Test public void testPfmergeString() { String destkey = "hll1"; String[] sourcekeys = { "hll2", "hll3" }; String expectedStatus = "OK"; when(commandObjects.pfmerge(destkey, sourcekeys)).thenReturn(stringCommandObject); when(commandExecutor.executeCommand(stringCommandObject)).thenReturn(expectedStatus); String result = jedis.pfmerge(destkey, sourcekeys); assertThat(result, equalTo(expectedStatus)); verify(commandExecutor).executeCommand(stringCommandObject); verify(commandObjects).pfmerge(destkey, sourcekeys); } @Test public void testPfmergeBinary() { byte[] destkey = "hll1".getBytes(); byte[][] sourcekeys = { "hll2".getBytes(), "hll3".getBytes() }; String expectedStatus = "OK"; when(commandObjects.pfmerge(destkey, sourcekeys)).thenReturn(stringCommandObject); when(commandExecutor.executeCommand(stringCommandObject)).thenReturn(expectedStatus); String result = jedis.pfmerge(destkey, sourcekeys); assertThat(result, equalTo(expectedStatus)); verify(commandExecutor).executeCommand(stringCommandObject); verify(commandObjects).pfmerge(destkey, sourcekeys); } } ================================================ FILE: src/test/java/redis/clients/jedis/mocked/unified/UnifiedJedisJsonCommandsTest.java ================================================ package redis.clients.jedis.mocked.unified; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; import java.util.Arrays; import java.util.Collections; import java.util.List; import com.google.gson.JsonObject; import org.json.JSONArray; import org.junit.jupiter.api.Test; import org.mockito.invocation.InvocationOnMock; import org.mockito.stubbing.Answer; import redis.clients.jedis.json.JsonObjectMapper; import redis.clients.jedis.json.JsonSetParams; import redis.clients.jedis.json.Path; import redis.clients.jedis.json.Path2; public class UnifiedJedisJsonCommandsTest extends UnifiedJedisMockedTestBase { @Test public void testJsonArrAppendWithPath() { String key = "testKey"; Path path = Path.of(".path.to.array"); Object[] pojos = new Object[]{ "value1", "value2" }; Long expectedResponse = 4L; when(commandObjects.jsonArrAppend(key, path, pojos)).thenReturn(longCommandObject); when(commandExecutor.executeCommand(longCommandObject)).thenReturn(expectedResponse); Long result = jedis.jsonArrAppend(key, path, pojos); assertEquals(expectedResponse, result); verify(commandExecutor).executeCommand(longCommandObject); verify(commandObjects).jsonArrAppend(key, path, pojos); } @Test public void testJsonArrAppendWithPath2() { String key = "testKey"; Path2 path = Path2.of(".path.to.array"); Object[] objects = new Object[]{ "value1", "value2" }; List expectedResponse = Arrays.asList(3L, 4L); when(commandObjects.jsonArrAppend(key, path, objects)).thenReturn(listLongCommandObject); when(commandExecutor.executeCommand(listLongCommandObject)).thenReturn(expectedResponse); List result = jedis.jsonArrAppend(key, path, objects); assertEquals(expectedResponse, result); verify(commandExecutor).executeCommand(listLongCommandObject); verify(commandObjects).jsonArrAppend(key, path, objects); } @Test public void testJsonArrAppendWithPath2WithEscape() { String key = "testKey"; Path2 path = Path2.of(".path.to.array"); Object[] objects = new Object[]{ "value1", "value2" }; List expectedResponse = Arrays.asList(3L, 4L); when(commandObjects.jsonArrAppendWithEscape(key, path, objects)).thenReturn(listLongCommandObject); when(commandExecutor.executeCommand(listLongCommandObject)).thenReturn(expectedResponse); List result = jedis.jsonArrAppendWithEscape(key, path, objects); assertEquals(expectedResponse, result); verify(commandExecutor).executeCommand(listLongCommandObject); verify(commandObjects).jsonArrAppendWithEscape(key, path, objects); } @Test public void testJsonArrIndexWithPath() { String key = "testKey"; Path path = Path.of(".path.to.array"); Object scalar = "value"; long expectedResponse = 2L; when(commandObjects.jsonArrIndex(key, path, scalar)).thenReturn(longCommandObject); when(commandExecutor.executeCommand(longCommandObject)).thenReturn(expectedResponse); long result = jedis.jsonArrIndex(key, path, scalar); assertEquals(expectedResponse, result); verify(commandExecutor).executeCommand(longCommandObject); verify(commandObjects).jsonArrIndex(key, path, scalar); } @Test public void testJsonArrIndexWithPath2() { String key = "testKey"; Path2 path = Path2.of(".path.to.array"); Object scalar = "value"; List expectedResponse = Collections.singletonList(2L); when(commandObjects.jsonArrIndex(key, path, scalar)).thenReturn(listLongCommandObject); when(commandExecutor.executeCommand(listLongCommandObject)).thenReturn(expectedResponse); List result = jedis.jsonArrIndex(key, path, scalar); assertEquals(expectedResponse, result); verify(commandExecutor).executeCommand(listLongCommandObject); verify(commandObjects).jsonArrIndex(key, path, scalar); } @Test public void testJsonArrIndexWithPath2WithEscape() { String key = "testKey"; Path2 path = Path2.of(".path.to.array"); Object scalar = "value"; List expectedResponse = Collections.singletonList(2L); when(commandObjects.jsonArrIndexWithEscape(key, path, scalar)).thenReturn(listLongCommandObject); when(commandExecutor.executeCommand(listLongCommandObject)).thenReturn(expectedResponse); List result = jedis.jsonArrIndexWithEscape(key, path, scalar); assertEquals(expectedResponse, result); verify(commandExecutor).executeCommand(listLongCommandObject); verify(commandObjects).jsonArrIndexWithEscape(key, path, scalar); } @Test public void testJsonArrInsertWithPath() { String key = "testKey"; Path path = Path.of(".path.to.array"); int index = 1; Object[] pojos = new Object[]{ "value1", "value2" }; long expectedResponse = 5L; when(commandObjects.jsonArrInsert(key, path, index, pojos)).thenReturn(longCommandObject); when(commandExecutor.executeCommand(longCommandObject)).thenReturn(expectedResponse); long result = jedis.jsonArrInsert(key, path, index, pojos); assertEquals(expectedResponse, result); verify(commandExecutor).executeCommand(longCommandObject); verify(commandObjects).jsonArrInsert(key, path, index, pojos); } @Test public void testJsonArrInsertWithPath2() { String key = "testKey"; Path2 path = Path2.of(".path.to.array"); int index = 1; Object[] objects = new Object[]{ "value1", "value2" }; List expectedResponse = Collections.singletonList(5L); when(commandObjects.jsonArrInsert(key, path, index, objects)).thenReturn(listLongCommandObject); when(commandExecutor.executeCommand(listLongCommandObject)).thenReturn(expectedResponse); List result = jedis.jsonArrInsert(key, path, index, objects); assertEquals(expectedResponse, result); verify(commandExecutor).executeCommand(listLongCommandObject); verify(commandObjects).jsonArrInsert(key, path, index, objects); } @Test public void testJsonArrInsertWithPath2WithEscape() { String key = "testKey"; Path2 path = Path2.of(".path.to.array"); int index = 1; Object[] objects = new Object[]{ "value1", "value2" }; List expectedResponse = Collections.singletonList(5L); when(commandObjects.jsonArrInsertWithEscape(key, path, index, objects)).thenReturn(listLongCommandObject); when(commandExecutor.executeCommand(listLongCommandObject)).thenReturn(expectedResponse); List result = jedis.jsonArrInsertWithEscape(key, path, index, objects); assertEquals(expectedResponse, result); verify(commandExecutor).executeCommand(listLongCommandObject); verify(commandObjects).jsonArrInsertWithEscape(key, path, index, objects); } @Test public void testJsonArrLen() { String key = "testKey"; Long expectedResponse = 10L; when(commandObjects.jsonArrLen(key)).thenReturn(longCommandObject); when(commandExecutor.executeCommand(longCommandObject)).thenReturn(expectedResponse); Long result = jedis.jsonArrLen(key); assertEquals(expectedResponse, result); verify(commandExecutor).executeCommand(longCommandObject); verify(commandObjects).jsonArrLen(key); } @Test public void testJsonArrLenWithPath() { String key = "testKey"; Path path = Path.of(".path.to.array"); Long expectedResponse = 10L; when(commandObjects.jsonArrLen(key, path)).thenReturn(longCommandObject); when(commandExecutor.executeCommand(longCommandObject)).thenReturn(expectedResponse); Long result = jedis.jsonArrLen(key, path); assertEquals(expectedResponse, result); verify(commandExecutor).executeCommand(longCommandObject); verify(commandObjects).jsonArrLen(key, path); } @Test public void testJsonArrLenWithPath2() { String key = "testKey"; Path2 path = Path2.of(".path.to.array"); List expectedResponse = Collections.singletonList(10L); when(commandObjects.jsonArrLen(key, path)).thenReturn(listLongCommandObject); when(commandExecutor.executeCommand(listLongCommandObject)).thenReturn(expectedResponse); List result = jedis.jsonArrLen(key, path); assertEquals(expectedResponse, result); verify(commandExecutor).executeCommand(listLongCommandObject); verify(commandObjects).jsonArrLen(key, path); } @Test public void testJsonArrPop() { String key = "testKey"; Object expectedResponse = "poppedValue"; when(commandObjects.jsonArrPop(key)).thenReturn(objectCommandObject); when(commandExecutor.executeCommand(objectCommandObject)).thenReturn(expectedResponse); Object result = jedis.jsonArrPop(key); assertEquals(expectedResponse, result); verify(commandExecutor).executeCommand(objectCommandObject); verify(commandObjects).jsonArrPop(key); } @Test public void testJsonArrPopWithPath() { String key = "testKey"; Path path = Path.of(".path.to.array"); Object expectedResponse = "poppedValue"; when(commandObjects.jsonArrPop(key, path)).thenReturn(objectCommandObject); when(commandExecutor.executeCommand(objectCommandObject)).thenReturn(expectedResponse); Object result = jedis.jsonArrPop(key, path); assertEquals(expectedResponse, result); verify(commandExecutor).executeCommand(objectCommandObject); verify(commandObjects).jsonArrPop(key, path); } @Test public void testJsonArrPopWithPathAndIndex() { String key = "testKey"; Path path = Path.of(".path.to.array"); int index = 1; Object expectedResponse = "poppedValueAtIndex"; when(commandObjects.jsonArrPop(key, path, index)).thenReturn(objectCommandObject); when(commandExecutor.executeCommand(objectCommandObject)).thenReturn(expectedResponse); Object result = jedis.jsonArrPop(key, path, index); assertEquals(expectedResponse, result); verify(commandExecutor).executeCommand(objectCommandObject); verify(commandObjects).jsonArrPop(key, path, index); } @Test public void testJsonArrPopWithClassAndPath() { String key = "testKey"; Class clazz = String.class; Path path = Path.of(".path.to.array"); String expectedResponse = "poppedValue"; when(commandObjects.jsonArrPop(key, clazz, path)).thenReturn(stringCommandObject); when(commandExecutor.executeCommand(stringCommandObject)).thenReturn(expectedResponse); String result = jedis.jsonArrPop(key, clazz, path); assertEquals(expectedResponse, result); verify(commandExecutor).executeCommand(stringCommandObject); verify(commandObjects).jsonArrPop(key, clazz, path); } @Test public void testJsonArrPopWithClassPathAndIndex() { String key = "testKey"; Class clazz = String.class; Path path = Path.of(".path.to.array"); int index = 1; String expectedResponse = "poppedValueAtIndex"; when(commandObjects.jsonArrPop(key, clazz, path, index)).thenReturn(stringCommandObject); when(commandExecutor.executeCommand(stringCommandObject)).thenReturn(expectedResponse); String result = jedis.jsonArrPop(key, clazz, path, index); assertEquals(expectedResponse, result); verify(commandExecutor).executeCommand(stringCommandObject); verify(commandObjects).jsonArrPop(key, clazz, path, index); } @Test public void testJsonArrPopWithPath2() { String key = "testKey"; Path2 path = Path2.of(".path.to.array"); List expectedResponse = Collections.singletonList("poppedValue"); when(commandObjects.jsonArrPop(key, path)).thenReturn(listObjectCommandObject); when(commandExecutor.executeCommand(listObjectCommandObject)).thenReturn(expectedResponse); List result = jedis.jsonArrPop(key, path); assertEquals(expectedResponse, result); verify(commandExecutor).executeCommand(listObjectCommandObject); verify(commandObjects).jsonArrPop(key, path); } @Test public void testJsonArrPopWithPath2AndIndex() { String key = "testKey"; Path2 path = Path2.of(".path.to.array"); int index = 1; List expectedResponse = Collections.singletonList("poppedValueAtIndex"); when(commandObjects.jsonArrPop(key, path, index)).thenReturn(listObjectCommandObject); when(commandExecutor.executeCommand(listObjectCommandObject)).thenReturn(expectedResponse); List result = jedis.jsonArrPop(key, path, index); assertEquals(expectedResponse, result); verify(commandExecutor).executeCommand(listObjectCommandObject); verify(commandObjects).jsonArrPop(key, path, index); } @Test public void testJsonArrPopWithClass() { String key = "testKey"; Class clazz = String.class; String expectedResponse = "poppedValue"; when(commandObjects.jsonArrPop(key, clazz)).thenReturn(stringCommandObject); when(commandExecutor.executeCommand(stringCommandObject)).thenReturn(expectedResponse); String result = jedis.jsonArrPop(key, clazz); assertEquals(expectedResponse, result); verify(commandExecutor).executeCommand(stringCommandObject); verify(commandObjects).jsonArrPop(key, clazz); } @Test public void testJsonArrTrimWithPath() { String key = "testKey"; Path path = Path.of(".path.to.array"); int start = 1; int stop = 3; Long expectedResponse = 3L; when(commandObjects.jsonArrTrim(key, path, start, stop)).thenReturn(longCommandObject); when(commandExecutor.executeCommand(longCommandObject)).thenReturn(expectedResponse); Long result = jedis.jsonArrTrim(key, path, start, stop); assertEquals(expectedResponse, result); verify(commandExecutor).executeCommand(longCommandObject); verify(commandObjects).jsonArrTrim(key, path, start, stop); } @Test public void testJsonArrTrimWithPath2() { String key = "testKey"; Path2 path = Path2.of(".path.to.array"); int start = 1; int stop = 3; List expectedResponse = Collections.singletonList(3L); when(commandObjects.jsonArrTrim(key, path, start, stop)).thenReturn(listLongCommandObject); when(commandExecutor.executeCommand(listLongCommandObject)).thenReturn(expectedResponse); List result = jedis.jsonArrTrim(key, path, start, stop); assertEquals(expectedResponse, result); verify(commandExecutor).executeCommand(listLongCommandObject); verify(commandObjects).jsonArrTrim(key, path, start, stop); } @Test public void testJsonClear() { String key = "testKey"; long expectedResponse = 1L; when(commandObjects.jsonClear(key)).thenReturn(longCommandObject); when(commandExecutor.executeCommand(longCommandObject)).thenReturn(expectedResponse); long result = jedis.jsonClear(key); assertEquals(expectedResponse, result); verify(commandExecutor).executeCommand(longCommandObject); verify(commandObjects).jsonClear(key); } @Test public void testJsonClearWithPath() { String key = "testKey"; Path path = Path.of(".path.to.element"); long expectedResponse = 1L; when(commandObjects.jsonClear(key, path)).thenReturn(longCommandObject); when(commandExecutor.executeCommand(longCommandObject)).thenReturn(expectedResponse); long result = jedis.jsonClear(key, path); assertEquals(expectedResponse, result); verify(commandExecutor).executeCommand(longCommandObject); verify(commandObjects).jsonClear(key, path); } @Test public void testJsonClearWithPath2() { String key = "testKey"; Path2 path = Path2.of(".path.to.element"); long expectedResponse = 1L; when(commandObjects.jsonClear(key, path)).thenReturn(longCommandObject); when(commandExecutor.executeCommand(longCommandObject)).thenReturn(expectedResponse); long result = jedis.jsonClear(key, path); assertEquals(expectedResponse, result); verify(commandExecutor).executeCommand(longCommandObject); verify(commandObjects).jsonClear(key, path); } @Test public void testJsonDebugMemory() { String key = "testKey"; long expectedResponse = 1024L; when(commandObjects.jsonDebugMemory(key)).thenReturn(longCommandObject); when(commandExecutor.executeCommand(longCommandObject)).thenReturn(expectedResponse); long result = jedis.jsonDebugMemory(key); assertEquals(expectedResponse, result); verify(commandExecutor).executeCommand(longCommandObject); verify(commandObjects).jsonDebugMemory(key); } @Test public void testJsonDebugMemoryWithPath() { String key = "testKey"; Path path = Path.of(".path.to.element"); long expectedResponse = 512L; when(commandObjects.jsonDebugMemory(key, path)).thenReturn(longCommandObject); when(commandExecutor.executeCommand(longCommandObject)).thenReturn(expectedResponse); long result = jedis.jsonDebugMemory(key, path); assertEquals(expectedResponse, result); verify(commandExecutor).executeCommand(longCommandObject); verify(commandObjects).jsonDebugMemory(key, path); } @Test public void testJsonDebugMemoryWithPath2() { String key = "testKey"; Path2 path = Path2.of(".path.to.element"); List expectedResponse = Collections.singletonList(512L); when(commandObjects.jsonDebugMemory(key, path)).thenReturn(listLongCommandObject); when(commandExecutor.executeCommand(listLongCommandObject)).thenReturn(expectedResponse); List result = jedis.jsonDebugMemory(key, path); assertEquals(expectedResponse, result); verify(commandExecutor).executeCommand(listLongCommandObject); verify(commandObjects).jsonDebugMemory(key, path); } @Test public void testJsonDel() { String key = "testKey"; long expectedResponse = 1L; when(commandObjects.jsonDel(key)).thenReturn(longCommandObject); when(commandExecutor.executeCommand(longCommandObject)).thenReturn(expectedResponse); long result = jedis.jsonDel(key); assertEquals(expectedResponse, result); verify(commandExecutor).executeCommand(longCommandObject); verify(commandObjects).jsonDel(key); } @Test public void testJsonDelWithPath() { String key = "testKey"; Path path = Path.of(".path.to.element"); long expectedResponse = 1L; when(commandObjects.jsonDel(key, path)).thenReturn(longCommandObject); when(commandExecutor.executeCommand(longCommandObject)).thenReturn(expectedResponse); long result = jedis.jsonDel(key, path); assertEquals(expectedResponse, result); verify(commandExecutor).executeCommand(longCommandObject); verify(commandObjects).jsonDel(key, path); } @Test public void testJsonDelWithPath2() { String key = "testKey"; Path2 path = Path2.of(".path.to.element"); long expectedResponse = 1L; when(commandObjects.jsonDel(key, path)).thenReturn(longCommandObject); when(commandExecutor.executeCommand(longCommandObject)).thenReturn(expectedResponse); long result = jedis.jsonDel(key, path); assertEquals(expectedResponse, result); verify(commandExecutor).executeCommand(longCommandObject); verify(commandObjects).jsonDel(key, path); } @Test public void testJsonGet() { String key = "testKey"; Object expectedResponse = new JsonObject(); when(commandObjects.jsonGet(key)).thenReturn(objectCommandObject); when(commandExecutor.executeCommand(objectCommandObject)).thenReturn(expectedResponse); Object result = jedis.jsonGet(key); assertEquals(expectedResponse, result); verify(commandExecutor).executeCommand(objectCommandObject); verify(commandObjects).jsonGet(key); } @Test public void testJsonGetWithClass() { String key = "testKey"; Class clazz = MyBean.class; MyBean expectedResponse = new MyBean(); when(commandObjects.jsonGet(key, clazz)).thenReturn(myBeanCommandObject); when(commandExecutor.executeCommand(myBeanCommandObject)).thenReturn(expectedResponse); MyBean result = jedis.jsonGet(key, clazz); assertEquals(expectedResponse, result); verify(commandExecutor).executeCommand(myBeanCommandObject); verify(commandObjects).jsonGet(key, clazz); } @Test public void testJsonGetWithPath() { String key = "testKey"; Path[] paths = new Path[]{ Path.of(".path.to.element1"), Path.of(".path.to.element2") }; Object expectedResponse = new JsonObject(); when(commandObjects.jsonGet(key, paths)).thenReturn(objectCommandObject); when(commandExecutor.executeCommand(objectCommandObject)).thenReturn(expectedResponse); Object result = jedis.jsonGet(key, paths); assertEquals(expectedResponse, result); verify(commandExecutor).executeCommand(objectCommandObject); verify(commandObjects).jsonGet(key, paths); } @Test public void testJsonGetWithPath2() { String key = "testKey"; Path2[] paths = new Path2[]{ Path2.of(".path.to.element1"), Path2.of(".path.to.element2") }; Object expectedResponse = new JsonObject(); when(commandObjects.jsonGet(key, paths)).thenReturn(objectCommandObject); when(commandExecutor.executeCommand(objectCommandObject)).thenReturn(expectedResponse); Object result = jedis.jsonGet(key, paths); assertEquals(expectedResponse, result); verify(commandExecutor).executeCommand(objectCommandObject); verify(commandObjects).jsonGet(key, paths); } @Test public void testJsonGetAsPlainString() { String key = "testKey"; Path path = Path.of(".path.to.element"); String expectedResponse = "{\"field\":\"value\"}"; when(commandObjects.jsonGetAsPlainString(key, path)).thenReturn(stringCommandObject); when(commandExecutor.executeCommand(stringCommandObject)).thenReturn(expectedResponse); String result = jedis.jsonGetAsPlainString(key, path); assertEquals(expectedResponse, result); verify(commandExecutor).executeCommand(stringCommandObject); verify(commandObjects).jsonGetAsPlainString(key, path); } @Test public void testJsonGetWithClassAndPath() { String key = "testKey"; Class clazz = MyBean.class; Path[] paths = new Path[]{ Path.of(".path.to.element1"), Path.of(".path.to.element2") }; MyBean expectedResponse = new MyBean(); when(commandObjects.jsonGet(key, clazz, paths)).thenReturn(myBeanCommandObject); when(commandExecutor.executeCommand(myBeanCommandObject)).thenReturn(expectedResponse); MyBean result = jedis.jsonGet(key, clazz, paths); assertEquals(expectedResponse, result); verify(commandExecutor).executeCommand(myBeanCommandObject); verify(commandObjects).jsonGet(key, clazz, paths); } @Test public void testJsonMergeWithPath() { String key = "testKey"; Path path = Path.of(".path.to.element"); Object pojo = new MyBean(); String expectedResponse = "OK"; when(commandObjects.jsonMerge(key, path, pojo)).thenReturn(stringCommandObject); when(commandExecutor.executeCommand(stringCommandObject)).thenReturn(expectedResponse); String result = jedis.jsonMerge(key, path, pojo); assertEquals(expectedResponse, result); verify(commandExecutor).executeCommand(stringCommandObject); verify(commandObjects).jsonMerge(key, path, pojo); } @Test public void testJsonMergeWithPath2() { String key = "testKey"; Path2 path = Path2.of(".path.to.element"); Object object = new JsonObject(); String expectedResponse = "OK"; when(commandObjects.jsonMerge(key, path, object)).thenReturn(stringCommandObject); when(commandExecutor.executeCommand(stringCommandObject)).thenReturn(expectedResponse); String result = jedis.jsonMerge(key, path, object); assertEquals(expectedResponse, result); verify(commandExecutor).executeCommand(stringCommandObject); verify(commandObjects).jsonMerge(key, path, object); } @Test public void testJsonMGetWithPathAndClass() { Path path = Path.of(".path.to.element"); Class clazz = MyBean.class; String[] keys = { "testKey1", "testKey2" }; List expectedResponse = Arrays.asList(new MyBean(), new MyBean()); when(commandObjects.jsonMGet(path, clazz, keys)).thenReturn(listMyBeanCommandObject); when(commandExecutor.executeCommand(listMyBeanCommandObject)).thenReturn(expectedResponse); List result = jedis.jsonMGet(path, clazz, keys); assertEquals(expectedResponse, result); verify(commandExecutor).executeCommand(listMyBeanCommandObject); verify(commandObjects).jsonMGet(path, clazz, keys); } @Test public void testJsonMGetWithPath2() { Path2 path = Path2.of(".path.to.element"); String[] keys = { "testKey1", "testKey2" }; List expectedResponse = Arrays.asList(new JSONArray(), new JSONArray()); when(commandObjects.jsonMGet(path, keys)).thenReturn(listJsonArrayCommandObject); when(commandExecutor.executeCommand(listJsonArrayCommandObject)).thenReturn(expectedResponse); List result = jedis.jsonMGet(path, keys); assertEquals(expectedResponse, result); verify(commandExecutor).executeCommand(listJsonArrayCommandObject); verify(commandObjects).jsonMGet(path, keys); } @Test public void testJsonNumIncrByWithPath() { String key = "testKey"; Path path = Path.of(".path.to.element"); double value = 10.5; double expectedResponse = 20.5; when(commandObjects.jsonNumIncrBy(key, path, value)).thenReturn(doubleCommandObject); when(commandExecutor.executeCommand(doubleCommandObject)).thenReturn(expectedResponse); double result = jedis.jsonNumIncrBy(key, path, value); assertEquals(expectedResponse, result, 0.0); verify(commandExecutor).executeCommand(doubleCommandObject); verify(commandObjects).jsonNumIncrBy(key, path, value); } @Test public void testJsonNumIncrByWithPath2() { String key = "testKey"; Path2 path = Path2.of(".path.to.element"); double value = 10.5; Object expectedResponse = 20.5; when(commandObjects.jsonNumIncrBy(key, path, value)).thenReturn(objectCommandObject); when(commandExecutor.executeCommand(objectCommandObject)).thenReturn(expectedResponse); Object result = jedis.jsonNumIncrBy(key, path, value); assertEquals(expectedResponse, result); verify(commandExecutor).executeCommand(objectCommandObject); verify(commandObjects).jsonNumIncrBy(key, path, value); } @Test public void testJsonSetWithPath() { String key = "testKey"; Path path = Path.of(".path.to.element"); Object pojo = new MyBean(); String expectedResponse = "OK"; when(commandObjects.jsonSet(key, path, pojo)).thenReturn(stringCommandObject); when(commandExecutor.executeCommand(stringCommandObject)).thenReturn(expectedResponse); String result = jedis.jsonSet(key, path, pojo); assertEquals(expectedResponse, result); verify(commandExecutor).executeCommand(stringCommandObject); verify(commandObjects).jsonSet(key, path, pojo); } @Test public void testJsonSetWithPathAndParams() { String key = "testKey"; Path path = Path.of(".path.to.element"); Object pojo = new MyBean(); JsonSetParams params = new JsonSetParams().nx(); String expectedResponse = "OK"; when(commandObjects.jsonSet(key, path, pojo, params)).thenReturn(stringCommandObject); when(commandExecutor.executeCommand(stringCommandObject)).thenReturn(expectedResponse); String result = jedis.jsonSet(key, path, pojo, params); assertEquals(expectedResponse, result); verify(commandExecutor).executeCommand(stringCommandObject); verify(commandObjects).jsonSet(key, path, pojo, params); } @Test public void testJsonSetWithPath2() { String key = "testKey"; Path2 path = Path2.of(".path.to.element"); Object object = new JsonObject(); String expectedResponse = "OK"; when(commandObjects.jsonSet(key, path, object)).thenReturn(stringCommandObject); when(commandExecutor.executeCommand(stringCommandObject)).thenReturn(expectedResponse); String result = jedis.jsonSet(key, path, object); assertEquals(expectedResponse, result); verify(commandExecutor).executeCommand(stringCommandObject); verify(commandObjects).jsonSet(key, path, object); } @Test public void testJsonSetWithPath2WithEscape() { String key = "testKey"; Path2 path = Path2.of(".path.to.element"); Object object = new JsonObject(); String expectedResponse = "OK"; when(commandObjects.jsonSetWithEscape(key, path, object)).thenReturn(stringCommandObject); when(commandExecutor.executeCommand(stringCommandObject)).thenReturn(expectedResponse); String result = jedis.jsonSetWithEscape(key, path, object); assertEquals(expectedResponse, result); verify(commandExecutor).executeCommand(stringCommandObject); verify(commandObjects).jsonSetWithEscape(key, path, object); } @Test public void testJsonSetWithPath2AndParams() { String key = "testKey"; Path2 path = Path2.of(".path.to.element"); Object object = new JsonObject(); JsonSetParams params = new JsonSetParams().nx(); String expectedResponse = "OK"; when(commandObjects.jsonSet(key, path, object, params)).thenReturn(stringCommandObject); when(commandExecutor.executeCommand(stringCommandObject)).thenReturn(expectedResponse); String result = jedis.jsonSet(key, path, object, params); assertEquals(expectedResponse, result); verify(commandExecutor).executeCommand(stringCommandObject); verify(commandObjects).jsonSet(key, path, object, params); } @Test public void testJsonSetWithPath2EscapeAndParams() { String key = "testKey"; Path2 path = Path2.of(".path.to.element"); Object object = new JsonObject(); JsonSetParams params = new JsonSetParams().nx(); String expectedResponse = "OK"; when(commandObjects.jsonSetWithEscape(key, path, object, params)).thenReturn(stringCommandObject); when(commandExecutor.executeCommand(stringCommandObject)).thenReturn(expectedResponse); String result = jedis.jsonSetWithEscape(key, path, object, params); assertEquals(expectedResponse, result); verify(commandExecutor).executeCommand(stringCommandObject); verify(commandObjects).jsonSetWithEscape(key, path, object, params); } @Test public void testJsonSetWithPlainString() { String key = "testKey"; Path path = Path.of(".path.to.element"); String jsonString = "{\"field\":\"value\"}"; String expectedResponse = "OK"; when(commandObjects.jsonSetWithPlainString(key, path, jsonString)).thenReturn(stringCommandObject); when(commandExecutor.executeCommand(stringCommandObject)).thenReturn(expectedResponse); String result = jedis.jsonSetWithPlainString(key, path, jsonString); assertEquals(expectedResponse, result); verify(commandExecutor).executeCommand(stringCommandObject); verify(commandObjects).jsonSetWithPlainString(key, path, jsonString); } @Test public void testJsonStrAppend() { String key = "testKey"; Object string = "additional string"; long expectedResponse = 20L; when(commandObjects.jsonStrAppend(key, string)).thenReturn(longCommandObject); when(commandExecutor.executeCommand(longCommandObject)).thenReturn(expectedResponse); long result = jedis.jsonStrAppend(key, string); assertEquals(expectedResponse, result); verify(commandExecutor).executeCommand(longCommandObject); verify(commandObjects).jsonStrAppend(key, string); } @Test public void testJsonStrAppendWithPath() { String key = "testKey"; Path path = Path.of(".path.to.element"); Object string = "additional string"; long expectedResponse = 20L; when(commandObjects.jsonStrAppend(key, path, string)).thenReturn(longCommandObject); when(commandExecutor.executeCommand(longCommandObject)).thenReturn(expectedResponse); long result = jedis.jsonStrAppend(key, path, string); assertEquals(expectedResponse, result); verify(commandExecutor).executeCommand(longCommandObject); verify(commandObjects).jsonStrAppend(key, path, string); } @Test public void testJsonStrAppendWithPath2() { String key = "testKey"; Path2 path = Path2.of(".path.to.element"); Object string = "additional string"; List expectedResponse = Collections.singletonList(20L); when(commandObjects.jsonStrAppend(key, path, string)).thenReturn(listLongCommandObject); when(commandExecutor.executeCommand(listLongCommandObject)).thenReturn(expectedResponse); List result = jedis.jsonStrAppend(key, path, string); assertEquals(expectedResponse, result); verify(commandExecutor).executeCommand(listLongCommandObject); verify(commandObjects).jsonStrAppend(key, path, string); } @Test public void testJsonStrLen() { String key = "testKey"; Long expectedResponse = 15L; when(commandObjects.jsonStrLen(key)).thenReturn(longCommandObject); when(commandExecutor.executeCommand(longCommandObject)).thenReturn(expectedResponse); Long result = jedis.jsonStrLen(key); assertEquals(expectedResponse, result); verify(commandExecutor).executeCommand(longCommandObject); verify(commandObjects).jsonStrLen(key); } @Test public void testJsonStrLenWithPath() { String key = "testKey"; Path path = Path.of(".path.to.element"); Long expectedResponse = 15L; when(commandObjects.jsonStrLen(key, path)).thenReturn(longCommandObject); when(commandExecutor.executeCommand(longCommandObject)).thenReturn(expectedResponse); Long result = jedis.jsonStrLen(key, path); assertEquals(expectedResponse, result); verify(commandExecutor).executeCommand(longCommandObject); verify(commandObjects).jsonStrLen(key, path); } @Test public void testJsonStrLenWithPath2() { String key = "testKey"; Path2 path = Path2.of(".path.to.element"); List expectedResponse = Collections.singletonList(15L); when(commandObjects.jsonStrLen(key, path)).thenReturn(listLongCommandObject); when(commandExecutor.executeCommand(listLongCommandObject)).thenReturn(expectedResponse); List result = jedis.jsonStrLen(key, path); assertEquals(expectedResponse, result); verify(commandExecutor).executeCommand(listLongCommandObject); verify(commandObjects).jsonStrLen(key, path); } @Test public void testJsonToggleWithPath() { String key = "testKey"; Path path = Path.of(".path.to.element"); String expectedResponse = "OK"; when(commandObjects.jsonToggle(key, path)).thenReturn(stringCommandObject); when(commandExecutor.executeCommand(stringCommandObject)).thenReturn(expectedResponse); String result = jedis.jsonToggle(key, path); assertEquals(expectedResponse, result); verify(commandExecutor).executeCommand(stringCommandObject); verify(commandObjects).jsonToggle(key, path); } @Test public void testJsonToggleWithPath2() { String key = "testKey"; Path2 path = Path2.of(".path.to.element"); List expectedResponse = Arrays.asList(true, false); when(commandObjects.jsonToggle(key, path)).thenReturn(listBooleanCommandObject); when(commandExecutor.executeCommand(listBooleanCommandObject)).thenReturn(expectedResponse); List result = jedis.jsonToggle(key, path); assertEquals(expectedResponse, result); verify(commandExecutor).executeCommand(listBooleanCommandObject); verify(commandObjects).jsonToggle(key, path); } @Test public void testJsonType() { String key = "testKey"; Class expectedResponse = String.class; when(commandObjects.jsonType(key)).thenReturn(classCommandObject); when(commandExecutor.executeCommand(classCommandObject)).thenAnswer(new Answer>() { @Override public Class answer(InvocationOnMock invocationOnMock) { return expectedResponse; } }); Class result = jedis.jsonType(key); assertEquals(expectedResponse, result); verify(commandExecutor).executeCommand(classCommandObject); verify(commandObjects).jsonType(key); } @Test public void testJsonTypeWithPath() { String key = "testKey"; Path path = Path.of(".path.to.element"); Class expectedResponse = String.class; when(commandObjects.jsonType(key, path)).thenReturn(classCommandObject); when(commandExecutor.executeCommand(classCommandObject)).thenAnswer(new Answer>() { @Override public Class answer(InvocationOnMock invocationOnMock) { return expectedResponse; } }); Class result = jedis.jsonType(key, path); assertEquals(expectedResponse, result); verify(commandExecutor).executeCommand(classCommandObject); verify(commandObjects).jsonType(key, path); } @Test public void testJsonTypeWithPath2() { String key = "testKey"; Path2 path = Path2.of(".path.to.element"); List> expectedResponse = Collections.singletonList(String.class); when(commandObjects.jsonType(key, path)).thenReturn(listClassCommandObject); when(commandExecutor.executeCommand(listClassCommandObject)).thenReturn(expectedResponse); List> result = jedis.jsonType(key, path); assertEquals(expectedResponse, result); verify(commandExecutor).executeCommand(listClassCommandObject); verify(commandObjects).jsonType(key, path); } @Test public void testJsonObjKeys() { String key = "testKey"; List expectedResponse = Arrays.asList("key1", "key2", "key3"); when(commandObjects.jsonObjKeys(key)).thenReturn(listStringCommandObject); when(commandExecutor.executeCommand(listStringCommandObject)).thenReturn(expectedResponse); List result = jedis.jsonObjKeys(key); assertEquals(expectedResponse, result); verify(commandExecutor).executeCommand(listStringCommandObject); verify(commandObjects).jsonObjKeys(key); } @Test public void testJsonObjKeysWithPath() { String key = "testKey"; Path path = Path.of(".path.to.object"); List expectedResponse = Arrays.asList("key1", "key2"); when(commandObjects.jsonObjKeys(key, path)).thenReturn(listStringCommandObject); when(commandExecutor.executeCommand(listStringCommandObject)).thenReturn(expectedResponse); List result = jedis.jsonObjKeys(key, path); assertEquals(expectedResponse, result); verify(commandExecutor).executeCommand(listStringCommandObject); verify(commandObjects).jsonObjKeys(key, path); } @Test public void testJsonObjKeysWithPath2() { String key = "testKey"; Path2 path = Path2.of(".path.to.object"); List> expectedResponse = Collections.singletonList(Arrays.asList("key1", "key2")); when(commandObjects.jsonObjKeys(key, path)).thenReturn(listListStringCommandObject); when(commandExecutor.executeCommand(listListStringCommandObject)).thenReturn(expectedResponse); List> result = jedis.jsonObjKeys(key, path); assertEquals(expectedResponse, result); verify(commandExecutor).executeCommand(listListStringCommandObject); verify(commandObjects).jsonObjKeys(key, path); } @Test public void testJsonObjLen() { String key = "testKey"; Long expectedResponse = 5L; when(commandObjects.jsonObjLen(key)).thenReturn(longCommandObject); when(commandExecutor.executeCommand(longCommandObject)).thenReturn(expectedResponse); Long result = jedis.jsonObjLen(key); assertEquals(expectedResponse, result); verify(commandExecutor).executeCommand(longCommandObject); verify(commandObjects).jsonObjLen(key); } @Test public void testJsonObjLenWithPath() { String key = "testKey"; Path path = Path.of(".path.to.object"); Long expectedResponse = 3L; when(commandObjects.jsonObjLen(key, path)).thenReturn(longCommandObject); when(commandExecutor.executeCommand(longCommandObject)).thenReturn(expectedResponse); Long result = jedis.jsonObjLen(key, path); assertEquals(expectedResponse, result); verify(commandExecutor).executeCommand(longCommandObject); verify(commandObjects).jsonObjLen(key, path); } @Test public void testJsonObjLenWithPath2() { String key = "testKey"; Path2 path = Path2.of(".path.to.object"); List expectedResponse = Collections.singletonList(3L); when(commandObjects.jsonObjLen(key, path)).thenReturn(listLongCommandObject); when(commandExecutor.executeCommand(listLongCommandObject)).thenReturn(expectedResponse); List result = jedis.jsonObjLen(key, path); assertEquals(expectedResponse, result); verify(commandExecutor).executeCommand(listLongCommandObject); verify(commandObjects).jsonObjLen(key, path); } @Test public void testSetJsonObjectMapper() { JsonObjectMapper jsonObjectMapper = mock(JsonObjectMapper.class); jedis.setJsonObjectMapper(jsonObjectMapper); verify(commandObjects).setJsonObjectMapper(jsonObjectMapper); } } ================================================ FILE: src/test/java/redis/clients/jedis/mocked/unified/UnifiedJedisListCommandsTest.java ================================================ package redis.clients.jedis.mocked.unified; import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.equalTo; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; import java.util.Arrays; import java.util.List; import org.junit.jupiter.api.Test; import redis.clients.jedis.args.ListDirection; import redis.clients.jedis.args.ListPosition; import redis.clients.jedis.params.LPosParams; import redis.clients.jedis.util.KeyValue; public class UnifiedJedisListCommandsTest extends UnifiedJedisMockedTestBase { @Test public void testBlmove() { String srcKey = "sourceList"; String dstKey = "destinationList"; ListDirection from = ListDirection.LEFT; ListDirection to = ListDirection.RIGHT; double timeout = 10.5; // Timeout in seconds String expectedMovedValue = "value"; when(commandObjects.blmove(srcKey, dstKey, from, to, timeout)).thenReturn(stringCommandObject); when(commandExecutor.executeCommand(stringCommandObject)).thenReturn(expectedMovedValue); String result = jedis.blmove(srcKey, dstKey, from, to, timeout); assertThat(result, equalTo(expectedMovedValue)); verify(commandExecutor).executeCommand(stringCommandObject); verify(commandObjects).blmove(srcKey, dstKey, from, to, timeout); } @Test public void testBlmoveBinary() { byte[] srcKey = "sourceList".getBytes(); byte[] dstKey = "destinationList".getBytes(); ListDirection from = ListDirection.LEFT; ListDirection to = ListDirection.RIGHT; double timeout = 10.5; // Timeout in seconds byte[] expectedMovedValue = "value".getBytes(); when(commandObjects.blmove(srcKey, dstKey, from, to, timeout)).thenReturn(bytesCommandObject); when(commandExecutor.executeCommand(bytesCommandObject)).thenReturn(expectedMovedValue); byte[] result = jedis.blmove(srcKey, dstKey, from, to, timeout); assertThat(result, equalTo(expectedMovedValue)); verify(commandExecutor).executeCommand(bytesCommandObject); verify(commandObjects).blmove(srcKey, dstKey, from, to, timeout); } @Test public void testBlmpop() { double timeout = 10.5; // Timeout in seconds ListDirection direction = ListDirection.LEFT; String[] keys = { "listKey1", "listKey2" }; KeyValue> expectedKeyValue = new KeyValue<>("listKey1", Arrays.asList("value1", "value2")); when(commandObjects.blmpop(timeout, direction, keys)).thenReturn(keyValueStringListStringCommandObject); when(commandExecutor.executeCommand(keyValueStringListStringCommandObject)).thenReturn(expectedKeyValue); KeyValue> result = jedis.blmpop(timeout, direction, keys); assertThat(result, equalTo(expectedKeyValue)); verify(commandExecutor).executeCommand(keyValueStringListStringCommandObject); verify(commandObjects).blmpop(timeout, direction, keys); } @Test public void testBlmpopBinary() { double timeout = 10.5; // Timeout in seconds ListDirection direction = ListDirection.LEFT; byte[][] keys = { "listKey1".getBytes(), "listKey2".getBytes() }; KeyValue> expectedKeyValue = new KeyValue<>("listKey1".getBytes(), Arrays.asList("value1".getBytes(), "value2".getBytes())); when(commandObjects.blmpop(timeout, direction, keys)).thenReturn(keyValueBytesListBytesCommandObject); when(commandExecutor.executeCommand(keyValueBytesListBytesCommandObject)).thenReturn(expectedKeyValue); KeyValue> result = jedis.blmpop(timeout, direction, keys); assertThat(result, equalTo(expectedKeyValue)); verify(commandExecutor).executeCommand(keyValueBytesListBytesCommandObject); verify(commandObjects).blmpop(timeout, direction, keys); } @Test public void testBlmpopCount() { double timeout = 10.5; // Timeout in seconds ListDirection direction = ListDirection.RIGHT; int count = 2; String[] keys = { "listKey1", "listKey2" }; KeyValue> expectedKeyValue = new KeyValue<>("listKey2", Arrays.asList("value3", "value4")); when(commandObjects.blmpop(timeout, direction, count, keys)).thenReturn(keyValueStringListStringCommandObject); when(commandExecutor.executeCommand(keyValueStringListStringCommandObject)).thenReturn(expectedKeyValue); KeyValue> result = jedis.blmpop(timeout, direction, count, keys); assertThat(result, equalTo(expectedKeyValue)); verify(commandExecutor).executeCommand(keyValueStringListStringCommandObject); verify(commandObjects).blmpop(timeout, direction, count, keys); } @Test public void testBlmpopCountBinary() { double timeout = 10.5; // Timeout in seconds ListDirection direction = ListDirection.RIGHT; int count = 2; byte[][] keys = { "listKey1".getBytes(), "listKey2".getBytes() }; KeyValue> expectedKeyValue = new KeyValue<>("listKey2".getBytes(), Arrays.asList("value3".getBytes(), "value4".getBytes())); when(commandObjects.blmpop(timeout, direction, count, keys)).thenReturn(keyValueBytesListBytesCommandObject); when(commandExecutor.executeCommand(keyValueBytesListBytesCommandObject)).thenReturn(expectedKeyValue); KeyValue> result = jedis.blmpop(timeout, direction, count, keys); assertThat(result, equalTo(expectedKeyValue)); verify(commandExecutor).executeCommand(keyValueBytesListBytesCommandObject); verify(commandObjects).blmpop(timeout, direction, count, keys); } @Test public void testBlpop() { int timeout = 10; // Timeout in seconds String key = "listKey"; List expectedValues = Arrays.asList(key, "value1"); when(commandObjects.blpop(timeout, key)).thenReturn(listStringCommandObject); when(commandExecutor.executeCommand(listStringCommandObject)).thenReturn(expectedValues); List result = jedis.blpop(timeout, key); assertThat(result, equalTo(expectedValues)); verify(commandExecutor).executeCommand(listStringCommandObject); verify(commandObjects).blpop(timeout, key); } @Test public void testBlpopBinary() { int timeout = 10; // Timeout in seconds byte[][] keys = { "listKey1".getBytes(), "listKey2".getBytes() }; List expectedValues = Arrays.asList("listKey1".getBytes(), "value1".getBytes()); when(commandObjects.blpop(timeout, keys)).thenReturn(listBytesCommandObject); when(commandExecutor.executeCommand(listBytesCommandObject)).thenReturn(expectedValues); List result = jedis.blpop(timeout, keys); assertThat(result, equalTo(expectedValues)); verify(commandExecutor).executeCommand(listBytesCommandObject); verify(commandObjects).blpop(timeout, keys); } @Test public void testBlpopDoubleTimeout() { double timeout = 10.5; // Timeout in seconds String key = "listKey"; KeyValue expectedKeyValue = new KeyValue<>(key, "value1"); when(commandObjects.blpop(timeout, key)).thenReturn(keyValueStringStringCommandObject); when(commandExecutor.executeCommand(keyValueStringStringCommandObject)).thenReturn(expectedKeyValue); KeyValue result = jedis.blpop(timeout, key); assertThat(result, equalTo(expectedKeyValue)); verify(commandExecutor).executeCommand(keyValueStringStringCommandObject); verify(commandObjects).blpop(timeout, key); } @Test public void testBlpopDoubleTimeoutBinary() { double timeout = 10.5; // Timeout in seconds byte[][] keys = { "listKey1".getBytes(), "listKey2".getBytes() }; KeyValue expectedKeyValue = new KeyValue<>("listKey1".getBytes(), "value1".getBytes()); when(commandObjects.blpop(timeout, keys)).thenReturn(keyValueBytesBytesCommandObject); when(commandExecutor.executeCommand(keyValueBytesBytesCommandObject)).thenReturn(expectedKeyValue); KeyValue result = jedis.blpop(timeout, keys); assertThat(result, equalTo(expectedKeyValue)); verify(commandExecutor).executeCommand(keyValueBytesBytesCommandObject); verify(commandObjects).blpop(timeout, keys); } @Test public void testBlpopMultipleKeys() { int timeout = 10; // Timeout in seconds String[] keys = { "listKey1", "listKey2" }; List expectedValues = Arrays.asList("listKey1", "value1"); when(commandObjects.blpop(timeout, keys)).thenReturn(listStringCommandObject); when(commandExecutor.executeCommand(listStringCommandObject)).thenReturn(expectedValues); List result = jedis.blpop(timeout, keys); assertThat(result, equalTo(expectedValues)); verify(commandExecutor).executeCommand(listStringCommandObject); verify(commandObjects).blpop(timeout, keys); } @Test public void testBlpopMultipleKeysDoubleTimeout() { double timeout = 10.5; // Timeout in seconds String[] keys = { "listKey1", "listKey2" }; KeyValue expectedKeyValue = new KeyValue<>("listKey1", "value1"); when(commandObjects.blpop(timeout, keys)).thenReturn(keyValueStringStringCommandObject); when(commandExecutor.executeCommand(keyValueStringStringCommandObject)).thenReturn(expectedKeyValue); KeyValue result = jedis.blpop(timeout, keys); assertThat(result, equalTo(expectedKeyValue)); verify(commandExecutor).executeCommand(keyValueStringStringCommandObject); verify(commandObjects).blpop(timeout, keys); } @Test public void testBrpop() { int timeout = 10; // Timeout in seconds String key = "listKey"; List expectedValues = Arrays.asList(key, "value1"); when(commandObjects.brpop(timeout, key)).thenReturn(listStringCommandObject); when(commandExecutor.executeCommand(listStringCommandObject)).thenReturn(expectedValues); List result = jedis.brpop(timeout, key); assertThat(result, equalTo(expectedValues)); verify(commandExecutor).executeCommand(listStringCommandObject); verify(commandObjects).brpop(timeout, key); } @Test public void testBrpopBinary() { int timeout = 10; // Timeout in seconds byte[][] keys = { "listKey1".getBytes(), "listKey2".getBytes() }; List expectedValues = Arrays.asList("listKey1".getBytes(), "value1".getBytes()); when(commandObjects.brpop(timeout, keys)).thenReturn(listBytesCommandObject); when(commandExecutor.executeCommand(listBytesCommandObject)).thenReturn(expectedValues); List result = jedis.brpop(timeout, keys); assertThat(result, equalTo(expectedValues)); verify(commandExecutor).executeCommand(listBytesCommandObject); verify(commandObjects).brpop(timeout, keys); } @Test public void testBrpopDoubleTimeout() { double timeout = 10.5; // Timeout in seconds String key = "listKey"; KeyValue expectedKeyValue = new KeyValue<>(key, "value1"); when(commandObjects.brpop(timeout, key)).thenReturn(keyValueStringStringCommandObject); when(commandExecutor.executeCommand(keyValueStringStringCommandObject)).thenReturn(expectedKeyValue); KeyValue result = jedis.brpop(timeout, key); assertThat(result, equalTo(expectedKeyValue)); verify(commandExecutor).executeCommand(keyValueStringStringCommandObject); verify(commandObjects).brpop(timeout, key); } @Test public void testBrpopDoubleTimeoutBinary() { double timeout = 10.5; // Timeout in seconds byte[][] keys = { "listKey1".getBytes(), "listKey2".getBytes() }; KeyValue expectedKeyValue = new KeyValue<>("listKey1".getBytes(), "value1".getBytes()); when(commandObjects.brpop(timeout, keys)).thenReturn(keyValueBytesBytesCommandObject); when(commandExecutor.executeCommand(keyValueBytesBytesCommandObject)).thenReturn(expectedKeyValue); KeyValue result = jedis.brpop(timeout, keys); assertThat(result, equalTo(expectedKeyValue)); verify(commandExecutor).executeCommand(keyValueBytesBytesCommandObject); verify(commandObjects).brpop(timeout, keys); } @Test public void testBrpopMultipleKeys() { int timeout = 10; // Timeout in seconds String[] keys = { "listKey1", "listKey2" }; List expectedValues = Arrays.asList("listKey1", "value1"); when(commandObjects.brpop(timeout, keys)).thenReturn(listStringCommandObject); when(commandExecutor.executeCommand(listStringCommandObject)).thenReturn(expectedValues); List result = jedis.brpop(timeout, keys); assertThat(result, equalTo(expectedValues)); verify(commandExecutor).executeCommand(listStringCommandObject); verify(commandObjects).brpop(timeout, keys); } @Test public void testBrpopMultipleKeysDoubleTimeout() { double timeout = 10.5; // Timeout in seconds String[] keys = { "listKey1", "listKey2" }; KeyValue expectedKeyValue = new KeyValue<>("listKey1", "value1"); when(commandObjects.brpop(timeout, keys)).thenReturn(keyValueStringStringCommandObject); when(commandExecutor.executeCommand(keyValueStringStringCommandObject)).thenReturn(expectedKeyValue); KeyValue result = jedis.brpop(timeout, keys); assertThat(result, equalTo(expectedKeyValue)); verify(commandExecutor).executeCommand(keyValueStringStringCommandObject); verify(commandObjects).brpop(timeout, keys); } @Test public void testBrpoplpush() { String source = "sourceList"; String destination = "destinationList"; int timeout = 10; // Timeout in seconds String expectedPoppedAndPushedValue = "value"; when(commandObjects.brpoplpush(source, destination, timeout)).thenReturn(stringCommandObject); when(commandExecutor.executeCommand(stringCommandObject)).thenReturn(expectedPoppedAndPushedValue); String result = jedis.brpoplpush(source, destination, timeout); assertThat(result, equalTo(expectedPoppedAndPushedValue)); verify(commandExecutor).executeCommand(stringCommandObject); verify(commandObjects).brpoplpush(source, destination, timeout); } @Test public void testBrpoplpushBinary() { byte[] source = "sourceList".getBytes(); byte[] destination = "destinationList".getBytes(); int timeout = 10; // Timeout in seconds byte[] expectedPoppedAndPushedValue = "value".getBytes(); when(commandObjects.brpoplpush(source, destination, timeout)).thenReturn(bytesCommandObject); when(commandExecutor.executeCommand(bytesCommandObject)).thenReturn(expectedPoppedAndPushedValue); byte[] result = jedis.brpoplpush(source, destination, timeout); assertThat(result, equalTo(expectedPoppedAndPushedValue)); verify(commandExecutor).executeCommand(bytesCommandObject); verify(commandObjects).brpoplpush(source, destination, timeout); } @Test public void testLindex() { String key = "listKey"; long index = 1; // Get the element at index 1 String expectedValue = "value2"; when(commandObjects.lindex(key, index)).thenReturn(stringCommandObject); when(commandExecutor.executeCommand(stringCommandObject)).thenReturn(expectedValue); String result = jedis.lindex(key, index); assertThat(result, equalTo(expectedValue)); verify(commandExecutor).executeCommand(stringCommandObject); verify(commandObjects).lindex(key, index); } @Test public void testLindexBinary() { byte[] key = "listKey".getBytes(); long index = 1; // Get the element at index 1 byte[] expectedValue = "value2".getBytes(); when(commandObjects.lindex(key, index)).thenReturn(bytesCommandObject); when(commandExecutor.executeCommand(bytesCommandObject)).thenReturn(expectedValue); byte[] result = jedis.lindex(key, index); assertThat(result, equalTo(expectedValue)); verify(commandExecutor).executeCommand(bytesCommandObject); verify(commandObjects).lindex(key, index); } @Test public void testLinsert() { String key = "listKey"; ListPosition where = ListPosition.BEFORE; String pivot = "pivotValue"; String value = "newValue"; long expectedInsertions = 1L; // Assuming one element was inserted when(commandObjects.linsert(key, where, pivot, value)).thenReturn(longCommandObject); when(commandExecutor.executeCommand(longCommandObject)).thenReturn(expectedInsertions); long result = jedis.linsert(key, where, pivot, value); assertThat(result, equalTo(expectedInsertions)); verify(commandExecutor).executeCommand(longCommandObject); verify(commandObjects).linsert(key, where, pivot, value); } @Test public void testLinsertBinary() { byte[] key = "listKey".getBytes(); ListPosition where = ListPosition.AFTER; byte[] pivot = "pivotValue".getBytes(); byte[] value = "newValue".getBytes(); long expectedInsertions = 1L; // Assuming one element was inserted when(commandObjects.linsert(key, where, pivot, value)).thenReturn(longCommandObject); when(commandExecutor.executeCommand(longCommandObject)).thenReturn(expectedInsertions); long result = jedis.linsert(key, where, pivot, value); assertThat(result, equalTo(expectedInsertions)); verify(commandExecutor).executeCommand(longCommandObject); verify(commandObjects).linsert(key, where, pivot, value); } @Test public void testLlen() { String key = "listKey"; long expectedLength = 5L; // Assuming the length of the list is 5 when(commandObjects.llen(key)).thenReturn(longCommandObject); when(commandExecutor.executeCommand(longCommandObject)).thenReturn(expectedLength); long result = jedis.llen(key); assertThat(result, equalTo(expectedLength)); verify(commandExecutor).executeCommand(longCommandObject); verify(commandObjects).llen(key); } @Test public void testLlenBinary() { byte[] key = "listKey".getBytes(); long expectedLength = 5L; // Assuming the length of the list is 5 when(commandObjects.llen(key)).thenReturn(longCommandObject); when(commandExecutor.executeCommand(longCommandObject)).thenReturn(expectedLength); long result = jedis.llen(key); assertThat(result, equalTo(expectedLength)); verify(commandExecutor).executeCommand(longCommandObject); verify(commandObjects).llen(key); } @Test public void testLmove() { String srcKey = "sourceList"; String dstKey = "destinationList"; ListDirection from = ListDirection.LEFT; ListDirection to = ListDirection.RIGHT; String expectedMovedValue = "value"; when(commandObjects.lmove(srcKey, dstKey, from, to)).thenReturn(stringCommandObject); when(commandExecutor.executeCommand(stringCommandObject)).thenReturn(expectedMovedValue); String result = jedis.lmove(srcKey, dstKey, from, to); assertThat(result, equalTo(expectedMovedValue)); verify(commandExecutor).executeCommand(stringCommandObject); verify(commandObjects).lmove(srcKey, dstKey, from, to); } @Test public void testLmoveBinary() { byte[] srcKey = "sourceList".getBytes(); byte[] dstKey = "destinationList".getBytes(); ListDirection from = ListDirection.LEFT; ListDirection to = ListDirection.RIGHT; byte[] expectedMovedValue = "value".getBytes(); when(commandObjects.lmove(srcKey, dstKey, from, to)).thenReturn(bytesCommandObject); when(commandExecutor.executeCommand(bytesCommandObject)).thenReturn(expectedMovedValue); byte[] result = jedis.lmove(srcKey, dstKey, from, to); assertThat(result, equalTo(expectedMovedValue)); verify(commandExecutor).executeCommand(bytesCommandObject); verify(commandObjects).lmove(srcKey, dstKey, from, to); } @Test public void testLmpop() { ListDirection direction = ListDirection.LEFT; String[] keys = { "listKey1", "listKey2" }; KeyValue> expectedKeyValue = new KeyValue<>("listKey1", Arrays.asList("value1", "value2")); when(commandObjects.lmpop(direction, keys)).thenReturn(keyValueStringListStringCommandObject); when(commandExecutor.executeCommand(keyValueStringListStringCommandObject)).thenReturn(expectedKeyValue); KeyValue> result = jedis.lmpop(direction, keys); assertThat(result, equalTo(expectedKeyValue)); verify(commandExecutor).executeCommand(keyValueStringListStringCommandObject); verify(commandObjects).lmpop(direction, keys); } @Test public void testLmpopBinary() { ListDirection direction = ListDirection.LEFT; byte[][] keys = { "listKey1".getBytes(), "listKey2".getBytes() }; KeyValue> expectedKeyValue = new KeyValue<>("listKey1".getBytes(), Arrays.asList("value1".getBytes(), "value2".getBytes())); when(commandObjects.lmpop(direction, keys)).thenReturn(keyValueBytesListBytesCommandObject); when(commandExecutor.executeCommand(keyValueBytesListBytesCommandObject)).thenReturn(expectedKeyValue); KeyValue> result = jedis.lmpop(direction, keys); assertThat(result, equalTo(expectedKeyValue)); verify(commandExecutor).executeCommand(keyValueBytesListBytesCommandObject); verify(commandObjects).lmpop(direction, keys); } @Test public void testLmpopCount() { ListDirection direction = ListDirection.RIGHT; int count = 2; String[] keys = { "listKey1", "listKey2" }; KeyValue> expectedKeyValue = new KeyValue<>("listKey2", Arrays.asList("value3", "value4")); when(commandObjects.lmpop(direction, count, keys)).thenReturn(keyValueStringListStringCommandObject); when(commandExecutor.executeCommand(keyValueStringListStringCommandObject)).thenReturn(expectedKeyValue); KeyValue> result = jedis.lmpop(direction, count, keys); assertThat(result, equalTo(expectedKeyValue)); verify(commandExecutor).executeCommand(keyValueStringListStringCommandObject); verify(commandObjects).lmpop(direction, count, keys); } @Test public void testLmpopCountBinary() { ListDirection direction = ListDirection.RIGHT; int count = 2; byte[][] keys = { "listKey1".getBytes(), "listKey2".getBytes() }; KeyValue> expectedKeyValue = new KeyValue<>("listKey2".getBytes(), Arrays.asList("value3".getBytes(), "value4".getBytes())); when(commandObjects.lmpop(direction, count, keys)).thenReturn(keyValueBytesListBytesCommandObject); when(commandExecutor.executeCommand(keyValueBytesListBytesCommandObject)).thenReturn(expectedKeyValue); KeyValue> result = jedis.lmpop(direction, count, keys); assertThat(result, equalTo(expectedKeyValue)); verify(commandExecutor).executeCommand(keyValueBytesListBytesCommandObject); verify(commandObjects).lmpop(direction, count, keys); } @Test public void testLpop() { String key = "listKey"; String expectedPoppedValue = "poppedValue"; when(commandObjects.lpop(key)).thenReturn(stringCommandObject); when(commandExecutor.executeCommand(stringCommandObject)).thenReturn(expectedPoppedValue); String result = jedis.lpop(key); assertThat(result, equalTo(expectedPoppedValue)); verify(commandExecutor).executeCommand(stringCommandObject); verify(commandObjects).lpop(key); } @Test public void testLpopBinary() { byte[] key = "listKey".getBytes(); byte[] expectedPoppedValue = "poppedValue".getBytes(); when(commandObjects.lpop(key)).thenReturn(bytesCommandObject); when(commandExecutor.executeCommand(bytesCommandObject)).thenReturn(expectedPoppedValue); byte[] result = jedis.lpop(key); assertThat(result, equalTo(expectedPoppedValue)); verify(commandExecutor).executeCommand(bytesCommandObject); verify(commandObjects).lpop(key); } @Test public void testLpopCount() { String key = "listKey"; int count = 2; // Pop two elements List expectedPoppedValues = Arrays.asList("value1", "value2"); when(commandObjects.lpop(key, count)).thenReturn(listStringCommandObject); when(commandExecutor.executeCommand(listStringCommandObject)).thenReturn(expectedPoppedValues); List result = jedis.lpop(key, count); assertThat(result, equalTo(expectedPoppedValues)); verify(commandExecutor).executeCommand(listStringCommandObject); verify(commandObjects).lpop(key, count); } @Test public void testLpopCountBinary() { byte[] key = "listKey".getBytes(); int count = 2; // Pop two elements List expectedPoppedValues = Arrays.asList("value1".getBytes(), "value2".getBytes()); when(commandObjects.lpop(key, count)).thenReturn(listBytesCommandObject); when(commandExecutor.executeCommand(listBytesCommandObject)).thenReturn(expectedPoppedValues); List result = jedis.lpop(key, count); assertThat(result, equalTo(expectedPoppedValues)); verify(commandExecutor).executeCommand(listBytesCommandObject); verify(commandObjects).lpop(key, count); } @Test public void testLpos() { String key = "listKey"; String element = "valueToFind"; Long expectedPosition = 1L; // Assuming the element is at index 1 when(commandObjects.lpos(key, element)).thenReturn(longCommandObject); when(commandExecutor.executeCommand(longCommandObject)).thenReturn(expectedPosition); Long result = jedis.lpos(key, element); assertThat(result, equalTo(expectedPosition)); verify(commandExecutor).executeCommand(longCommandObject); verify(commandObjects).lpos(key, element); } @Test public void testLposBinary() { byte[] key = "listKey".getBytes(); byte[] element = "valueToFind".getBytes(); Long expectedPosition = 1L; // Assuming the element is at index 1 when(commandObjects.lpos(key, element)).thenReturn(longCommandObject); when(commandExecutor.executeCommand(longCommandObject)).thenReturn(expectedPosition); Long result = jedis.lpos(key, element); assertThat(result, equalTo(expectedPosition)); verify(commandExecutor).executeCommand(longCommandObject); verify(commandObjects).lpos(key, element); } @Test public void testLposWithParams() { String key = "listKey"; String element = "valueToFind"; LPosParams params = new LPosParams().rank(2); // Find the second occurrence Long expectedPosition = 3L; // Assuming the second occurrence is at index 3 when(commandObjects.lpos(key, element, params)).thenReturn(longCommandObject); when(commandExecutor.executeCommand(longCommandObject)).thenReturn(expectedPosition); Long result = jedis.lpos(key, element, params); assertThat(result, equalTo(expectedPosition)); verify(commandExecutor).executeCommand(longCommandObject); verify(commandObjects).lpos(key, element, params); } @Test public void testLposWithParamsBinary() { byte[] key = "listKey".getBytes(); byte[] element = "valueToFind".getBytes(); LPosParams params = new LPosParams().rank(2); // Find the second occurrence Long expectedPosition = 3L; // Assuming the second occurrence is at index 3 when(commandObjects.lpos(key, element, params)).thenReturn(longCommandObject); when(commandExecutor.executeCommand(longCommandObject)).thenReturn(expectedPosition); Long result = jedis.lpos(key, element, params); assertThat(result, equalTo(expectedPosition)); verify(commandExecutor).executeCommand(longCommandObject); verify(commandObjects).lpos(key, element, params); } @Test public void testLposWithParamsCount() { String key = "listKey"; String element = "valueToFind"; LPosParams params = new LPosParams().rank(1); // Find the first occurrence long count = 2; // Find up to two positions List expectedPositions = Arrays.asList(1L, 4L); // Assuming occurrences at indexes 1 and 4 when(commandObjects.lpos(key, element, params, count)).thenReturn(listLongCommandObject); when(commandExecutor.executeCommand(listLongCommandObject)).thenReturn(expectedPositions); List result = jedis.lpos(key, element, params, count); assertThat(result, equalTo(expectedPositions)); verify(commandExecutor).executeCommand(listLongCommandObject); verify(commandObjects).lpos(key, element, params, count); } @Test public void testLposWithParamsCountBinary() { byte[] key = "listKey".getBytes(); byte[] element = "valueToFind".getBytes(); LPosParams params = new LPosParams().rank(1); // Find the first occurrence long count = 2; // Find up to two positions List expectedPositions = Arrays.asList(1L, 4L); // Assuming occurrences at indexes 1 and 4 when(commandObjects.lpos(key, element, params, count)).thenReturn(listLongCommandObject); when(commandExecutor.executeCommand(listLongCommandObject)).thenReturn(expectedPositions); List result = jedis.lpos(key, element, params, count); assertThat(result, equalTo(expectedPositions)); verify(commandExecutor).executeCommand(listLongCommandObject); verify(commandObjects).lpos(key, element, params, count); } @Test public void testLpush() { String key = "listKey"; String[] strings = { "value1", "value2", "value3" }; long expectedLength = 3L; // Assuming the new length of the list is 3 after LPUSH when(commandObjects.lpush(key, strings)).thenReturn(longCommandObject); when(commandExecutor.executeCommand(longCommandObject)).thenReturn(expectedLength); long result = jedis.lpush(key, strings); assertThat(result, equalTo(expectedLength)); verify(commandExecutor).executeCommand(longCommandObject); verify(commandObjects).lpush(key, strings); } @Test public void testLpushBinary() { byte[] key = "listKey".getBytes(); byte[][] args = { "value1".getBytes(), "value2".getBytes(), "value3".getBytes() }; long expectedLength = 3L; // Assuming the new length of the list is 3 after LPUSH when(commandObjects.lpush(key, args)).thenReturn(longCommandObject); when(commandExecutor.executeCommand(longCommandObject)).thenReturn(expectedLength); long result = jedis.lpush(key, args); assertThat(result, equalTo(expectedLength)); verify(commandExecutor).executeCommand(longCommandObject); verify(commandObjects).lpush(key, args); } @Test public void testLpushx() { String key = "listKey"; String[] strings = { "value1", "value2" }; long expectedLength = 5L; // Assuming the new length of the list is 5 after LPUSHX when(commandObjects.lpushx(key, strings)).thenReturn(longCommandObject); when(commandExecutor.executeCommand(longCommandObject)).thenReturn(expectedLength); long result = jedis.lpushx(key, strings); assertThat(result, equalTo(expectedLength)); verify(commandExecutor).executeCommand(longCommandObject); verify(commandObjects).lpushx(key, strings); } @Test public void testLpushxBinary() { byte[] key = "listKey".getBytes(); byte[][] args = { "value1".getBytes(), "value2".getBytes() }; long expectedLength = 5L; // Assuming the new length of the list is 5 after LPUSHX when(commandObjects.lpushx(key, args)).thenReturn(longCommandObject); when(commandExecutor.executeCommand(longCommandObject)).thenReturn(expectedLength); long result = jedis.lpushx(key, args); assertThat(result, equalTo(expectedLength)); verify(commandExecutor).executeCommand(longCommandObject); verify(commandObjects).lpushx(key, args); } @Test public void testLrange() { String key = "listKey"; long start = 0; long stop = -1; // Get all elements in the list List expectedValues = Arrays.asList("value1", "value2", "value3"); when(commandObjects.lrange(key, start, stop)).thenReturn(listStringCommandObject); when(commandExecutor.executeCommand(listStringCommandObject)).thenReturn(expectedValues); List result = jedis.lrange(key, start, stop); assertThat(result, equalTo(expectedValues)); verify(commandExecutor).executeCommand(listStringCommandObject); verify(commandObjects).lrange(key, start, stop); } @Test public void testLrangeBinary() { byte[] key = "listKey".getBytes(); long start = 0; long stop = -1; // Get all elements in the list List expectedValues = Arrays.asList("value1".getBytes(), "value2".getBytes(), "value3".getBytes()); when(commandObjects.lrange(key, start, stop)).thenReturn(listBytesCommandObject); when(commandExecutor.executeCommand(listBytesCommandObject)).thenReturn(expectedValues); List result = jedis.lrange(key, start, stop); assertThat(result, equalTo(expectedValues)); verify(commandExecutor).executeCommand(listBytesCommandObject); verify(commandObjects).lrange(key, start, stop); } @Test public void testLrem() { String key = "listKey"; long count = 1; // Remove the first occurrence String value = "valueToRemove"; long expectedRemovals = 1L; // Assuming one element was removed when(commandObjects.lrem(key, count, value)).thenReturn(longCommandObject); when(commandExecutor.executeCommand(longCommandObject)).thenReturn(expectedRemovals); long result = jedis.lrem(key, count, value); assertThat(result, equalTo(expectedRemovals)); verify(commandExecutor).executeCommand(longCommandObject); verify(commandObjects).lrem(key, count, value); } @Test public void testLremBinary() { byte[] key = "listKey".getBytes(); long count = 1; // Remove the first occurrence byte[] value = "valueToRemove".getBytes(); long expectedRemovals = 1L; // Assuming one element was removed when(commandObjects.lrem(key, count, value)).thenReturn(longCommandObject); when(commandExecutor.executeCommand(longCommandObject)).thenReturn(expectedRemovals); long result = jedis.lrem(key, count, value); assertThat(result, equalTo(expectedRemovals)); verify(commandExecutor).executeCommand(longCommandObject); verify(commandObjects).lrem(key, count, value); } @Test public void testLtrim() { String key = "listKey"; long start = 1; long stop = -1; // Trim the list to keep elements from index 1 to the end String expectedResponse = "OK"; when(commandObjects.ltrim(key, start, stop)).thenReturn(stringCommandObject); when(commandExecutor.executeCommand(stringCommandObject)).thenReturn(expectedResponse); String result = jedis.ltrim(key, start, stop); assertThat(result, equalTo(expectedResponse)); verify(commandExecutor).executeCommand(stringCommandObject); verify(commandObjects).ltrim(key, start, stop); } @Test public void testLtrimBinary() { byte[] key = "listKey".getBytes(); long start = 1; long stop = -1; // Trim the list to keep elements from index 1 to the end String expectedResponse = "OK"; when(commandObjects.ltrim(key, start, stop)).thenReturn(stringCommandObject); when(commandExecutor.executeCommand(stringCommandObject)).thenReturn(expectedResponse); String result = jedis.ltrim(key, start, stop); assertThat(result, equalTo(expectedResponse)); verify(commandExecutor).executeCommand(stringCommandObject); verify(commandObjects).ltrim(key, start, stop); } @Test public void testLset() { String key = "listKey"; long index = 1; // Set the element at index 1 String value = "newValue"; String expectedResponse = "OK"; when(commandObjects.lset(key, index, value)).thenReturn(stringCommandObject); when(commandExecutor.executeCommand(stringCommandObject)).thenReturn(expectedResponse); String result = jedis.lset(key, index, value); assertThat(result, equalTo(expectedResponse)); verify(commandExecutor).executeCommand(stringCommandObject); verify(commandObjects).lset(key, index, value); } @Test public void testLsetBinary() { byte[] key = "listKey".getBytes(); long index = 1; // Set the element at index 1 byte[] value = "newValue".getBytes(); String expectedResponse = "OK"; when(commandObjects.lset(key, index, value)).thenReturn(stringCommandObject); when(commandExecutor.executeCommand(stringCommandObject)).thenReturn(expectedResponse); String result = jedis.lset(key, index, value); assertThat(result, equalTo(expectedResponse)); verify(commandExecutor).executeCommand(stringCommandObject); verify(commandObjects).lset(key, index, value); } @Test public void testRpop() { String key = "listKey"; String expectedPoppedValue = "poppedValue"; when(commandObjects.rpop(key)).thenReturn(stringCommandObject); when(commandExecutor.executeCommand(stringCommandObject)).thenReturn(expectedPoppedValue); String result = jedis.rpop(key); assertThat(result, equalTo(expectedPoppedValue)); verify(commandExecutor).executeCommand(stringCommandObject); verify(commandObjects).rpop(key); } @Test public void testRpopBinary() { byte[] key = "listKey".getBytes(); byte[] expectedPoppedValue = "poppedValue".getBytes(); when(commandObjects.rpop(key)).thenReturn(bytesCommandObject); when(commandExecutor.executeCommand(bytesCommandObject)).thenReturn(expectedPoppedValue); byte[] result = jedis.rpop(key); assertThat(result, equalTo(expectedPoppedValue)); verify(commandExecutor).executeCommand(bytesCommandObject); verify(commandObjects).rpop(key); } @Test public void testRpopCount() { String key = "listKey"; int count = 2; // Pop two elements List expectedPoppedValues = Arrays.asList("value1", "value2"); when(commandObjects.rpop(key, count)).thenReturn(listStringCommandObject); when(commandExecutor.executeCommand(listStringCommandObject)).thenReturn(expectedPoppedValues); List result = jedis.rpop(key, count); assertThat(result, equalTo(expectedPoppedValues)); verify(commandExecutor).executeCommand(listStringCommandObject); verify(commandObjects).rpop(key, count); } @Test public void testRpopCountBinary() { byte[] key = "listKey".getBytes(); int count = 2; // Pop two elements List expectedPoppedValues = Arrays.asList("value1".getBytes(), "value2".getBytes()); when(commandObjects.rpop(key, count)).thenReturn(listBytesCommandObject); when(commandExecutor.executeCommand(listBytesCommandObject)).thenReturn(expectedPoppedValues); List result = jedis.rpop(key, count); assertThat(result, equalTo(expectedPoppedValues)); verify(commandExecutor).executeCommand(listBytesCommandObject); verify(commandObjects).rpop(key, count); } @Test public void testRpoplpush() { String srckey = "sourceList"; String dstkey = "destinationList"; String expectedPoppedAndPushedValue = "value"; when(commandObjects.rpoplpush(srckey, dstkey)).thenReturn(stringCommandObject); when(commandExecutor.executeCommand(stringCommandObject)).thenReturn(expectedPoppedAndPushedValue); String result = jedis.rpoplpush(srckey, dstkey); assertThat(result, equalTo(expectedPoppedAndPushedValue)); verify(commandExecutor).executeCommand(stringCommandObject); verify(commandObjects).rpoplpush(srckey, dstkey); } @Test public void testRpoplpushBinary() { byte[] srckey = "sourceList".getBytes(); byte[] dstkey = "destinationList".getBytes(); byte[] expectedPoppedAndPushedValue = "value".getBytes(); when(commandObjects.rpoplpush(srckey, dstkey)).thenReturn(bytesCommandObject); when(commandExecutor.executeCommand(bytesCommandObject)).thenReturn(expectedPoppedAndPushedValue); byte[] result = jedis.rpoplpush(srckey, dstkey); assertThat(result, equalTo(expectedPoppedAndPushedValue)); verify(commandExecutor).executeCommand(bytesCommandObject); verify(commandObjects).rpoplpush(srckey, dstkey); } @Test public void testRpush() { String key = "listKey"; String[] strings = { "value1", "value2", "value3" }; long expectedLength = 3L; // Assuming the new length of the list is 3 after RPUSH when(commandObjects.rpush(key, strings)).thenReturn(longCommandObject); when(commandExecutor.executeCommand(longCommandObject)).thenReturn(expectedLength); long result = jedis.rpush(key, strings); assertThat(result, equalTo(expectedLength)); verify(commandExecutor).executeCommand(longCommandObject); verify(commandObjects).rpush(key, strings); } @Test public void testRpushBinary() { byte[] key = "listKey".getBytes(); byte[][] args = { "value1".getBytes(), "value2".getBytes(), "value3".getBytes() }; long expectedLength = 3L; // Assuming the new length of the list is 3 after RPUSH when(commandObjects.rpush(key, args)).thenReturn(longCommandObject); when(commandExecutor.executeCommand(longCommandObject)).thenReturn(expectedLength); long result = jedis.rpush(key, args); assertThat(result, equalTo(expectedLength)); verify(commandExecutor).executeCommand(longCommandObject); verify(commandObjects).rpush(key, args); } @Test public void testRpushx() { String key = "listKey"; String[] strings = { "value1", "value2" }; long expectedLength = 7L; // Assuming the new length of the list is 7 after RPUSHX when(commandObjects.rpushx(key, strings)).thenReturn(longCommandObject); when(commandExecutor.executeCommand(longCommandObject)).thenReturn(expectedLength); long result = jedis.rpushx(key, strings); assertThat(result, equalTo(expectedLength)); verify(commandExecutor).executeCommand(longCommandObject); verify(commandObjects).rpushx(key, strings); } @Test public void testRpushxBinary() { byte[] key = "listKey".getBytes(); byte[][] args = { "value1".getBytes(), "value2".getBytes() }; long expectedLength = 7L; // Assuming the new length of the list is 7 after RPUSHX when(commandObjects.rpushx(key, args)).thenReturn(longCommandObject); when(commandExecutor.executeCommand(longCommandObject)).thenReturn(expectedLength); long result = jedis.rpushx(key, args); assertThat(result, equalTo(expectedLength)); verify(commandExecutor).executeCommand(longCommandObject); verify(commandObjects).rpushx(key, args); } } ================================================ FILE: src/test/java/redis/clients/jedis/mocked/unified/UnifiedJedisMockedTestBase.java ================================================ package redis.clients.jedis.mocked.unified; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.verifyNoMoreInteractions; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeEach; import org.mockito.Mock; import redis.clients.jedis.CommandObject; import redis.clients.jedis.CommandObjects; import redis.clients.jedis.UnifiedJedis; import redis.clients.jedis.executors.CommandExecutor; import redis.clients.jedis.mocked.MockedCommandObjectsTestBase; import redis.clients.jedis.providers.ConnectionProvider; /** * Base class for {@link UnifiedJedis} mocked unit tests. Exposes a {@link UnifiedJedis} instance that * uses mocked executors, providers and command objects, which can be asserted upon. */ public abstract class UnifiedJedisMockedTestBase extends MockedCommandObjectsTestBase { /** * The {@link UnifiedJedis} instance under-test. */ protected UnifiedJedis jedis; /** * Mocked {@link CommandExecutor} instance. Instead of going to the wire and exchanging data * with a real Redis server, this instance is trained to returned pre-packaged response data, * depending on what is being tested. */ @Mock protected CommandExecutor commandExecutor; /** * Mocked {@link ConnectionProvider}. This is not really used in tests, except in some very * specific test cases. */ @Mock protected ConnectionProvider connectionProvider; /** * {@link CommandObjects} instance used by the {@link UnifiedJedis} under-test. Depending on * the test case, it is trained to return one of the mock {@link CommandObject} instances inherited * from the superclass. */ @Mock protected CommandObjects commandObjects; @BeforeEach public void setUp() { jedis = new UnifiedJedis(commandExecutor, connectionProvider, commandObjects); } @AfterEach public void tearDown() { // We want to be accurate about our mocks, hence we verify no more interactions here. // This might mean that some methods need to verify their interactions in a more verbose way, // but overall the benefit should be greater than the cost. verify(connectionProvider).getConnection(); verifyNoMoreInteractions(connectionProvider); verifyNoMoreInteractions(commandExecutor); verifyNoMoreInteractions(commandObjects); } } ================================================ FILE: src/test/java/redis/clients/jedis/mocked/unified/UnifiedJedisPubSubCommandsTest.java ================================================ package redis.clients.jedis.mocked.unified; import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.equalTo; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; import org.junit.jupiter.api.Test; public class UnifiedJedisPubSubCommandsTest extends UnifiedJedisMockedTestBase { @Test public void testPublishWithStringChannelAndMessage() { String channel = "myChannel"; String message = "Hello, World!"; long expectedPublishCount = 10L; when(commandObjects.publish(channel, message)).thenReturn(longCommandObject); when(commandExecutor.executeCommand(longCommandObject)).thenReturn(expectedPublishCount); long result = jedis.publish(channel, message); assertThat(result, equalTo(expectedPublishCount)); verify(commandExecutor).executeCommand(longCommandObject); verify(commandObjects).publish(channel, message); } @Test public void testPublishWithByteArrayChannelAndMessage() { byte[] channel = "myChannel".getBytes(); byte[] message = "Hello, World!".getBytes(); long expectedPublishCount = 10L; when(commandObjects.publish(channel, message)).thenReturn(longCommandObject); when(commandExecutor.executeCommand(longCommandObject)).thenReturn(expectedPublishCount); long result = jedis.publish(channel, message); assertThat(result, equalTo(expectedPublishCount)); verify(commandExecutor).executeCommand(longCommandObject); verify(commandObjects).publish(channel, message); } } ================================================ FILE: src/test/java/redis/clients/jedis/mocked/unified/UnifiedJedisScriptingAndFunctionsCommandsTest.java ================================================ package redis.clients.jedis.mocked.unified; import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.equalTo; import static org.hamcrest.Matchers.sameInstance; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; import java.util.List; import org.junit.jupiter.api.Test; import redis.clients.jedis.args.FlushMode; import redis.clients.jedis.args.FunctionRestorePolicy; import redis.clients.jedis.resps.FunctionStats; import redis.clients.jedis.resps.LibraryInfo; public class UnifiedJedisScriptingAndFunctionsCommandsTest extends UnifiedJedisMockedTestBase { @Test public void testEval() { String script = "return 1"; Object expectedEvalResult = 1; when(commandObjects.eval(script)).thenReturn(objectCommandObject); when(commandExecutor.executeCommand(objectCommandObject)).thenReturn(expectedEvalResult); Object result = jedis.eval(script); assertThat(result, equalTo(expectedEvalResult)); verify(commandExecutor).executeCommand(objectCommandObject); verify(commandObjects).eval(script); } @Test public void testEvalBinary() { byte[] script = "return 1".getBytes(); Object expectedEvalResult = 1; when(commandObjects.eval(script)).thenReturn(objectCommandObject); when(commandExecutor.executeCommand(objectCommandObject)).thenReturn(expectedEvalResult); Object result = jedis.eval(script); assertThat(result, equalTo(expectedEvalResult)); verify(commandExecutor).executeCommand(objectCommandObject); verify(commandObjects).eval(script); } @Test public void testEvalWithParams() { String script = "return KEYS[1]"; int keyCount = 1; String[] params = { "key1" }; Object expectedEvalResult = "key1"; when(commandObjects.eval(script, keyCount, params)).thenReturn(objectCommandObject); when(commandExecutor.executeCommand(objectCommandObject)).thenReturn(expectedEvalResult); Object result = jedis.eval(script, keyCount, params); assertThat(result, equalTo(expectedEvalResult)); verify(commandExecutor).executeCommand(objectCommandObject); verify(commandObjects).eval(script, keyCount, params); } @Test public void testEvalWithParamsBinary() { byte[] script = "return KEYS[1]".getBytes(); int keyCount = 1; byte[][] params = { "key1".getBytes() }; Object expectedEvalResult = "key1".getBytes(); when(commandObjects.eval(script, keyCount, params)).thenReturn(objectCommandObject); when(commandExecutor.executeCommand(objectCommandObject)).thenReturn(expectedEvalResult); Object result = jedis.eval(script, keyCount, params); assertThat(result, equalTo(expectedEvalResult)); verify(commandExecutor).executeCommand(objectCommandObject); verify(commandObjects).eval(script, keyCount, params); } @Test public void testEvalWithLists() { String script = "return KEYS[1]"; List keys = Collections.singletonList("key1"); List args = Collections.emptyList(); Object expectedEvalResult = "key1"; when(commandObjects.eval(script, keys, args)).thenReturn(objectCommandObject); when(commandExecutor.executeCommand(objectCommandObject)).thenReturn(expectedEvalResult); Object result = jedis.eval(script, keys, args); assertThat(result, equalTo(expectedEvalResult)); verify(commandExecutor).executeCommand(objectCommandObject); verify(commandObjects).eval(script, keys, args); } @Test public void testEvalWithListsBinary() { byte[] script = "return KEYS[1]".getBytes(); List keys = Collections.singletonList("key1".getBytes()); List args = Collections.emptyList(); Object expectedEvalResult = "key1".getBytes(); when(commandObjects.eval(script, keys, args)).thenReturn(objectCommandObject); when(commandExecutor.executeCommand(objectCommandObject)).thenReturn(expectedEvalResult); Object result = jedis.eval(script, keys, args); assertThat(result, equalTo(expectedEvalResult)); verify(commandExecutor).executeCommand(objectCommandObject); verify(commandObjects).eval(script, keys, args); } @Test public void testEvalWithSampleKey() { String script = "return redis.call('get', KEYS[1])"; String sampleKey = "myKey"; Object expectedResponse = "value"; when(commandObjects.eval(script, sampleKey)).thenReturn(objectCommandObject); when(commandExecutor.executeCommand(objectCommandObject)).thenReturn(expectedResponse); Object result = jedis.eval(script, sampleKey); assertThat(result, equalTo(expectedResponse)); verify(commandExecutor).executeCommand(objectCommandObject); verify(commandObjects).eval(script, sampleKey); } @Test public void testEvalWithSampleKeyBinary() { byte[] script = "return redis.call('get', KEYS[1])".getBytes(); byte[] sampleKey = "myKey".getBytes(); Object expectedResponse = "value".getBytes(); when(commandObjects.eval(script, sampleKey)).thenReturn(objectCommandObject); when(commandExecutor.executeCommand(objectCommandObject)).thenReturn(expectedResponse); Object result = jedis.eval(script, sampleKey); assertThat(result, equalTo(expectedResponse)); verify(commandExecutor).executeCommand(objectCommandObject); verify(commandObjects).eval(script, sampleKey); } @Test public void testEvalReadonly() { String script = "return KEYS[1]"; List keys = Collections.singletonList("key1"); List args = Collections.emptyList(); Object expectedEvalResult = "key1"; when(commandObjects.evalReadonly(script, keys, args)).thenReturn(objectCommandObject); when(commandExecutor.executeCommand(objectCommandObject)).thenReturn(expectedEvalResult); Object result = jedis.evalReadonly(script, keys, args); assertThat(result, equalTo(expectedEvalResult)); verify(commandExecutor).executeCommand(objectCommandObject); verify(commandObjects).evalReadonly(script, keys, args); } @Test public void testEvalReadonlyBinary() { byte[] script = "return KEYS[1]".getBytes(); List keys = Collections.singletonList("key1".getBytes()); List args = Collections.emptyList(); Object expectedEvalResult = "key1".getBytes(); when(commandObjects.evalReadonly(script, keys, args)).thenReturn(objectCommandObject); when(commandExecutor.executeCommand(objectCommandObject)).thenReturn(expectedEvalResult); Object result = jedis.evalReadonly(script, keys, args); assertThat(result, equalTo(expectedEvalResult)); verify(commandExecutor).executeCommand(objectCommandObject); verify(commandObjects).evalReadonly(script, keys, args); } @Test public void testEvalsha() { String sha1 = "someSha1Hash"; Object expectedEvalshaResult = 1; when(commandObjects.evalsha(sha1)).thenReturn(objectCommandObject); when(commandExecutor.executeCommand(objectCommandObject)).thenReturn(expectedEvalshaResult); Object result = jedis.evalsha(sha1); assertThat(result, equalTo(expectedEvalshaResult)); verify(commandExecutor).executeCommand(objectCommandObject); verify(commandObjects).evalsha(sha1); } @Test public void testEvalshaBinary() { byte[] sha1 = "someSha1Hash".getBytes(); Object expectedEvalshaResult = 1; when(commandObjects.evalsha(sha1)).thenReturn(objectCommandObject); when(commandExecutor.executeCommand(objectCommandObject)).thenReturn(expectedEvalshaResult); Object result = jedis.evalsha(sha1); assertThat(result, equalTo(expectedEvalshaResult)); verify(commandExecutor).executeCommand(objectCommandObject); verify(commandObjects).evalsha(sha1); } @Test public void testEvalshaWithParams() { String sha1 = "someSha1Hash"; int keyCount = 1; String[] params = { "key1" }; Object expectedEvalshaResult = "key1"; when(commandObjects.evalsha(sha1, keyCount, params)).thenReturn(objectCommandObject); when(commandExecutor.executeCommand(objectCommandObject)).thenReturn(expectedEvalshaResult); Object result = jedis.evalsha(sha1, keyCount, params); assertThat(result, equalTo(expectedEvalshaResult)); verify(commandExecutor).executeCommand(objectCommandObject); verify(commandObjects).evalsha(sha1, keyCount, params); } @Test public void testEvalshaWithParamsBinary() { byte[] sha1 = "someSha1Hash".getBytes(); int keyCount = 1; byte[][] params = { "key1".getBytes() }; Object expectedEvalshaResult = "key1".getBytes(); when(commandObjects.evalsha(sha1, keyCount, params)).thenReturn(objectCommandObject); when(commandExecutor.executeCommand(objectCommandObject)).thenReturn(expectedEvalshaResult); Object result = jedis.evalsha(sha1, keyCount, params); assertThat(result, equalTo(expectedEvalshaResult)); verify(commandExecutor).executeCommand(objectCommandObject); verify(commandObjects).evalsha(sha1, keyCount, params); } @Test public void testEvalshaWithLists() { String sha1 = "someSha1Hash"; List keys = Collections.singletonList("key1"); List args = Collections.emptyList(); Object expectedEvalshaResult = "key1"; when(commandObjects.evalsha(sha1, keys, args)).thenReturn(objectCommandObject); when(commandExecutor.executeCommand(objectCommandObject)).thenReturn(expectedEvalshaResult); Object result = jedis.evalsha(sha1, keys, args); assertThat(result, equalTo(expectedEvalshaResult)); verify(commandExecutor).executeCommand(objectCommandObject); verify(commandObjects).evalsha(sha1, keys, args); } @Test public void testEvalshaWithListsBinary() { byte[] sha1 = "someSha1Hash".getBytes(); List keys = Collections.singletonList("key1".getBytes()); List args = Collections.emptyList(); Object expectedEvalshaResult = "key1".getBytes(); when(commandObjects.evalsha(sha1, keys, args)).thenReturn(objectCommandObject); when(commandExecutor.executeCommand(objectCommandObject)).thenReturn(expectedEvalshaResult); Object result = jedis.evalsha(sha1, keys, args); assertThat(result, equalTo(expectedEvalshaResult)); verify(commandExecutor).executeCommand(objectCommandObject); verify(commandObjects).evalsha(sha1, keys, args); } @Test public void testEvalshaWithSampleKey() { String sha1 = "someSha1Hash"; String sampleKey = "myKey"; Object expectedResponse = "value"; when(commandObjects.evalsha(sha1, sampleKey)).thenReturn(objectCommandObject); when(commandExecutor.executeCommand(objectCommandObject)).thenReturn(expectedResponse); Object result = jedis.evalsha(sha1, sampleKey); assertThat(result, equalTo(expectedResponse)); verify(commandExecutor).executeCommand(objectCommandObject); verify(commandObjects).evalsha(sha1, sampleKey); } @Test public void testEvalshaWithSampleKeyBinary() { byte[] sha1 = "someSha1Hash".getBytes(); byte[] sampleKey = "myKey".getBytes(); Object expectedResponse = "value".getBytes(); when(commandObjects.evalsha(sha1, sampleKey)).thenReturn(objectCommandObject); when(commandExecutor.executeCommand(objectCommandObject)).thenReturn(expectedResponse); Object result = jedis.evalsha(sha1, sampleKey); assertThat(result, equalTo(expectedResponse)); verify(commandExecutor).executeCommand(objectCommandObject); verify(commandObjects).evalsha(sha1, sampleKey); } @Test public void testEvalshaReadonly() { String sha1 = "someSha1Hash"; List keys = Collections.singletonList("key1"); List args = Collections.emptyList(); Object expectedEvalshaResult = "key1"; when(commandObjects.evalshaReadonly(sha1, keys, args)).thenReturn(objectCommandObject); when(commandExecutor.executeCommand(objectCommandObject)).thenReturn(expectedEvalshaResult); Object result = jedis.evalshaReadonly(sha1, keys, args); assertThat(result, equalTo(expectedEvalshaResult)); verify(commandExecutor).executeCommand(objectCommandObject); verify(commandObjects).evalshaReadonly(sha1, keys, args); } @Test public void testEvalshaReadonlyBinary() { byte[] sha1 = "someSha1Hash".getBytes(); List keys = Collections.singletonList("key1".getBytes()); List args = Collections.emptyList(); Object expectedEvalshaResult = "key1".getBytes(); when(commandObjects.evalshaReadonly(sha1, keys, args)).thenReturn(objectCommandObject); when(commandExecutor.executeCommand(objectCommandObject)).thenReturn(expectedEvalshaResult); Object result = jedis.evalshaReadonly(sha1, keys, args); assertThat(result, equalTo(expectedEvalshaResult)); verify(commandExecutor).executeCommand(objectCommandObject); verify(commandObjects).evalshaReadonly(sha1, keys, args); } @Test public void testFcall() { String name = "myFunction"; List keys = Collections.singletonList("key1"); List args = Collections.singletonList("arg1"); Object expectedFcallResult = "someResult"; when(commandObjects.fcall(name, keys, args)).thenReturn(objectCommandObject); when(commandExecutor.executeCommand(objectCommandObject)).thenReturn(expectedFcallResult); Object result = jedis.fcall(name, keys, args); assertThat(result, equalTo(expectedFcallResult)); verify(commandExecutor).executeCommand(objectCommandObject); verify(commandObjects).fcall(name, keys, args); } @Test public void testFcallBinary() { byte[] name = "myFunction".getBytes(); List keys = Collections.singletonList("key1".getBytes()); List args = Collections.singletonList("arg1".getBytes()); Object expectedFcallResult = "someResult".getBytes(); when(commandObjects.fcall(name, keys, args)).thenReturn(objectCommandObject); when(commandExecutor.executeCommand(objectCommandObject)).thenReturn(expectedFcallResult); Object result = jedis.fcall(name, keys, args); assertThat(result, equalTo(expectedFcallResult)); verify(commandExecutor).executeCommand(objectCommandObject); verify(commandObjects).fcall(name, keys, args); } @Test public void testFcallReadonly() { String name = "myFunction"; List keys = Collections.singletonList("key1"); List args = Collections.singletonList("arg1"); Object expectedFcallResult = "someResult"; when(commandObjects.fcallReadonly(name, keys, args)).thenReturn(objectCommandObject); when(commandExecutor.executeCommand(objectCommandObject)).thenReturn(expectedFcallResult); Object result = jedis.fcallReadonly(name, keys, args); assertThat(result, equalTo(expectedFcallResult)); verify(commandExecutor).executeCommand(objectCommandObject); verify(commandObjects).fcallReadonly(name, keys, args); } @Test public void testFcallReadonlyBinary() { byte[] name = "myFunction".getBytes(); List keys = Collections.singletonList("key1".getBytes()); List args = Collections.singletonList("arg1".getBytes()); Object expectedFcallResult = "someResult".getBytes(); when(commandObjects.fcallReadonly(name, keys, args)).thenReturn(objectCommandObject); when(commandExecutor.executeCommand(objectCommandObject)).thenReturn(expectedFcallResult); Object result = jedis.fcallReadonly(name, keys, args); assertThat(result, equalTo(expectedFcallResult)); verify(commandExecutor).executeCommand(objectCommandObject); verify(commandObjects).fcallReadonly(name, keys, args); } @Test public void testFunctionDelete() { String libraryName = "mylib"; String expectedResponse = "OK"; when(commandObjects.functionDelete(libraryName)).thenReturn(stringCommandObject); when(commandExecutor.executeCommand(stringCommandObject)).thenReturn(expectedResponse); String result = jedis.functionDelete(libraryName); assertThat(result, equalTo(expectedResponse)); verify(commandExecutor).executeCommand(stringCommandObject); verify(commandObjects).functionDelete(libraryName); } @Test public void testFunctionDeleteBinary() { byte[] libraryName = "mylib".getBytes(); String expectedResponse = "OK"; when(commandObjects.functionDelete(libraryName)).thenReturn(stringCommandObject); when(commandExecutor.executeCommand(stringCommandObject)).thenReturn(expectedResponse); String result = jedis.functionDelete(libraryName); assertThat(result, equalTo(expectedResponse)); verify(commandExecutor).executeCommand(stringCommandObject); verify(commandObjects).functionDelete(libraryName); } @Test public void testFunctionDump() { byte[] expectedDump = "someSerializedData".getBytes(); when(commandObjects.functionDump()).thenReturn(bytesCommandObject); when(commandExecutor.executeCommand(bytesCommandObject)).thenReturn(expectedDump); byte[] result = jedis.functionDump(); assertThat(result, equalTo(expectedDump)); verify(commandExecutor).executeCommand(bytesCommandObject); verify(commandObjects).functionDump(); } @Test public void testFunctionFlush() { String expectedResponse = "OK"; when(commandObjects.functionFlush()).thenReturn(stringCommandObject); when(commandExecutor.executeCommand(stringCommandObject)).thenReturn(expectedResponse); String result = jedis.functionFlush(); assertThat(result, equalTo(expectedResponse)); verify(commandExecutor).executeCommand(stringCommandObject); verify(commandObjects).functionFlush(); } @Test public void testFunctionFlushWithMode() { FlushMode mode = FlushMode.ASYNC; String expectedResponse = "OK"; when(commandObjects.functionFlush(mode)).thenReturn(stringCommandObject); when(commandExecutor.executeCommand(stringCommandObject)).thenReturn(expectedResponse); String result = jedis.functionFlush(mode); assertThat(result, equalTo(expectedResponse)); verify(commandExecutor).executeCommand(stringCommandObject); verify(commandObjects).functionFlush(mode); } @Test public void testFunctionKill() { String expectedResponse = "OK"; when(commandObjects.functionKill()).thenReturn(stringCommandObject); when(commandExecutor.executeCommand(stringCommandObject)).thenReturn(expectedResponse); String result = jedis.functionKill(); assertThat(result, equalTo(expectedResponse)); verify(commandExecutor).executeCommand(stringCommandObject); verify(commandObjects).functionKill(); } @Test public void testFunctionList() { List expectedLibraryInfoList = new ArrayList<>(); when(commandObjects.functionList()).thenReturn(listLibraryInfoCommandObject); when(commandExecutor.executeCommand(listLibraryInfoCommandObject)).thenReturn(expectedLibraryInfoList); List result = jedis.functionList(); assertThat(result, equalTo(expectedLibraryInfoList)); verify(commandExecutor).executeCommand(listLibraryInfoCommandObject); verify(commandObjects).functionList(); } @Test public void testFunctionListBinary() { List expectedFunctionListBinary = new ArrayList<>(); when(commandObjects.functionListBinary()).thenReturn(listObjectCommandObject); when(commandExecutor.executeCommand(listObjectCommandObject)).thenReturn(expectedFunctionListBinary); List result = jedis.functionListBinary(); assertThat(result, equalTo(expectedFunctionListBinary)); verify(commandExecutor).executeCommand(listObjectCommandObject); verify(commandObjects).functionListBinary(); } @Test public void testFunctionListWithPattern() { String libraryNamePattern = "mylib*"; List expectedLibraryInfoList = new ArrayList<>(); when(commandObjects.functionList(libraryNamePattern)).thenReturn(listLibraryInfoCommandObject); when(commandExecutor.executeCommand(listLibraryInfoCommandObject)).thenReturn(expectedLibraryInfoList); List result = jedis.functionList(libraryNamePattern); assertThat(result, equalTo(expectedLibraryInfoList)); verify(commandExecutor).executeCommand(listLibraryInfoCommandObject); verify(commandObjects).functionList(libraryNamePattern); } @Test public void testFunctionListWithPatternBinary() { byte[] libraryNamePattern = "mylib*".getBytes(); List expectedFunctionList = new ArrayList<>(); when(commandObjects.functionList(libraryNamePattern)).thenReturn(listObjectCommandObject); when(commandExecutor.executeCommand(listObjectCommandObject)).thenReturn(expectedFunctionList); List result = jedis.functionList(libraryNamePattern); assertThat(result, equalTo(expectedFunctionList)); verify(commandExecutor).executeCommand(listObjectCommandObject); verify(commandObjects).functionList(libraryNamePattern); } @Test public void testFunctionListWithCode() { List expectedLibraryInfoList = new ArrayList<>(); when(commandObjects.functionListWithCode()).thenReturn(listLibraryInfoCommandObject); when(commandExecutor.executeCommand(listLibraryInfoCommandObject)).thenReturn(expectedLibraryInfoList); List result = jedis.functionListWithCode(); assertThat(result, equalTo(expectedLibraryInfoList)); verify(commandExecutor).executeCommand(listLibraryInfoCommandObject); verify(commandObjects).functionListWithCode(); } @Test public void testFunctionListWithCodeBinary() { List expectedFunctionListWithCodeBinary = new ArrayList<>(); when(commandObjects.functionListWithCodeBinary()).thenReturn(listObjectCommandObject); when(commandExecutor.executeCommand(listObjectCommandObject)).thenReturn(expectedFunctionListWithCodeBinary); List result = jedis.functionListWithCodeBinary(); assertThat(result, equalTo(expectedFunctionListWithCodeBinary)); verify(commandExecutor).executeCommand(listObjectCommandObject); verify(commandObjects).functionListWithCodeBinary(); } @Test public void testFunctionListWithCodeAndPattern() { String libraryNamePattern = "mylib*"; List expectedLibraryInfoList = new ArrayList<>(); when(commandObjects.functionListWithCode(libraryNamePattern)).thenReturn(listLibraryInfoCommandObject); when(commandExecutor.executeCommand(listLibraryInfoCommandObject)).thenReturn(expectedLibraryInfoList); List result = jedis.functionListWithCode(libraryNamePattern); assertThat(result, equalTo(expectedLibraryInfoList)); verify(commandExecutor).executeCommand(listLibraryInfoCommandObject); verify(commandObjects).functionListWithCode(libraryNamePattern); } @Test public void testFunctionListWithCodeAndPatternBinary() { byte[] libraryNamePattern = "mylib*".getBytes(); List expectedFunctionListWithCode = new ArrayList<>(); when(commandObjects.functionListWithCode(libraryNamePattern)).thenReturn(listObjectCommandObject); when(commandExecutor.executeCommand(listObjectCommandObject)).thenReturn(expectedFunctionListWithCode); List result = jedis.functionListWithCode(libraryNamePattern); assertThat(result, equalTo(expectedFunctionListWithCode)); verify(commandExecutor).executeCommand(listObjectCommandObject); verify(commandObjects).functionListWithCode(libraryNamePattern); } @Test public void testFunctionLoad() { String functionCode = "function myfunc() return 'hello' end"; String expectedResponse = "OK"; when(commandObjects.functionLoad(functionCode)).thenReturn(stringCommandObject); when(commandExecutor.executeCommand(stringCommandObject)).thenReturn(expectedResponse); String result = jedis.functionLoad(functionCode); assertThat(result, equalTo(expectedResponse)); verify(commandExecutor).executeCommand(stringCommandObject); verify(commandObjects).functionLoad(functionCode); } @Test public void testFunctionLoadWithBinary() { byte[] functionCode = "function myfunc() return 'hello' end".getBytes(); String expectedResponse = "OK"; when(commandObjects.functionLoad(functionCode)).thenReturn(stringCommandObject); when(commandExecutor.executeCommand(stringCommandObject)).thenReturn(expectedResponse); String result = jedis.functionLoad(functionCode); assertThat(result, equalTo(expectedResponse)); verify(commandExecutor).executeCommand(stringCommandObject); verify(commandObjects).functionLoad(functionCode); } @Test public void testFunctionLoadReplace() { String functionCode = "function myfunc() return 'hello' end"; String expectedResponse = "OK"; when(commandObjects.functionLoadReplace(functionCode)).thenReturn(stringCommandObject); when(commandExecutor.executeCommand(stringCommandObject)).thenReturn(expectedResponse); String result = jedis.functionLoadReplace(functionCode); assertThat(result, equalTo(expectedResponse)); verify(commandExecutor).executeCommand(stringCommandObject); verify(commandObjects).functionLoadReplace(functionCode); } @Test public void testFunctionLoadReplaceBinary() { byte[] functionCode = "function myfunc() return 'hello' end".getBytes(); String expectedResponse = "OK"; when(commandObjects.functionLoadReplace(functionCode)).thenReturn(stringCommandObject); when(commandExecutor.executeCommand(stringCommandObject)).thenReturn(expectedResponse); String result = jedis.functionLoadReplace(functionCode); assertThat(result, equalTo(expectedResponse)); verify(commandExecutor).executeCommand(stringCommandObject); verify(commandObjects).functionLoadReplace(functionCode); } @Test public void testFunctionRestore() { byte[] serializedValue = "serializedData".getBytes(); String expectedResponse = "OK"; when(commandObjects.functionRestore(serializedValue)).thenReturn(stringCommandObject); when(commandExecutor.executeCommand(stringCommandObject)).thenReturn(expectedResponse); String result = jedis.functionRestore(serializedValue); assertThat(result, equalTo(expectedResponse)); verify(commandExecutor).executeCommand(stringCommandObject); verify(commandObjects).functionRestore(serializedValue); } @Test public void testFunctionRestoreWithPolicy() { byte[] serializedValue = "serializedData".getBytes(); FunctionRestorePolicy policy = FunctionRestorePolicy.FLUSH; String expectedResponse = "OK"; when(commandObjects.functionRestore(serializedValue, policy)).thenReturn(stringCommandObject); when(commandExecutor.executeCommand(stringCommandObject)).thenReturn(expectedResponse); String result = jedis.functionRestore(serializedValue, policy); assertThat(result, equalTo(expectedResponse)); verify(commandExecutor).executeCommand(stringCommandObject); verify(commandObjects).functionRestore(serializedValue, policy); } @Test public void testFunctionStats() { FunctionStats expectedFunctionStats = mock(FunctionStats.class); when(commandObjects.functionStats()).thenReturn(functionStatsCommandObject); when(commandExecutor.executeCommand(functionStatsCommandObject)).thenReturn(expectedFunctionStats); FunctionStats result = jedis.functionStats(); assertThat(result, sameInstance(expectedFunctionStats)); verify(commandExecutor).executeCommand(functionStatsCommandObject); verify(commandObjects).functionStats(); } @Test public void testFunctionStatsBinary() { Object expectedFunctionStatsBinary = new Object(); when(commandObjects.functionStatsBinary()).thenReturn(objectCommandObject); when(commandExecutor.executeCommand(objectCommandObject)).thenReturn(expectedFunctionStatsBinary); Object result = jedis.functionStatsBinary(); assertThat(result, equalTo(expectedFunctionStatsBinary)); verify(commandExecutor).executeCommand(objectCommandObject); verify(commandObjects).functionStatsBinary(); } @Test public void testScriptExistsWithSha1s() { List sha1s = Arrays.asList("sha1One", "sha1Two"); List expectedResponse = Arrays.asList(true, false); when(commandObjects.scriptExists(sha1s)).thenReturn(listBooleanCommandObject); when(commandExecutor.executeCommand(listBooleanCommandObject)).thenReturn(expectedResponse); List result = jedis.scriptExists(sha1s); assertThat(result, equalTo(expectedResponse)); verify(commandExecutor).executeCommand(listBooleanCommandObject); verify(commandObjects).scriptExists(sha1s); } @Test public void testScriptExistsWithSha1AndSampleKey() { String sha1 = "someSha1Hash"; String sampleKey = "myKey"; Boolean expectedResponse = true; when(commandObjects.scriptExists(sampleKey, sha1)).thenReturn(listBooleanCommandObject); when(commandExecutor.executeCommand(listBooleanCommandObject)).thenReturn(Collections.singletonList(expectedResponse)); Boolean result = jedis.scriptExists(sha1, sampleKey); assertThat(result, equalTo(expectedResponse)); verify(commandExecutor).executeCommand(listBooleanCommandObject); verify(commandObjects).scriptExists(sampleKey, sha1); } @Test public void testScriptExistsWithSha1AndSampleKeyBinary() { byte[] sha1 = "someSha1Hash".getBytes(); byte[] sampleKey = "myKey".getBytes(); Boolean expectedResponse = true; when(commandObjects.scriptExists(sampleKey, new byte[][]{ sha1 })).thenReturn(listBooleanCommandObject); when(commandExecutor.executeCommand(listBooleanCommandObject)).thenReturn(Collections.singletonList(expectedResponse)); Boolean result = jedis.scriptExists(sha1, sampleKey); assertThat(result, equalTo(expectedResponse)); verify(commandExecutor).executeCommand(listBooleanCommandObject); verify(commandObjects).scriptExists(sampleKey, new byte[][]{ sha1 }); } @Test public void testScriptExistsWithKeyAndSha1s() { String sampleKey = "myKey"; String[] sha1s = { "sha1One", "sha1Two" }; List expectedResponse = Arrays.asList(true, false); when(commandObjects.scriptExists(sampleKey, sha1s)).thenReturn(listBooleanCommandObject); when(commandExecutor.executeCommand(listBooleanCommandObject)).thenReturn(expectedResponse); List result = jedis.scriptExists(sampleKey, sha1s); assertThat(result, equalTo(expectedResponse)); verify(commandExecutor).executeCommand(listBooleanCommandObject); verify(commandObjects).scriptExists(sampleKey, sha1s); } @Test public void testScriptExistsWithKeyAndSha1sBinary() { byte[] sampleKey = "myKey".getBytes(); byte[][] sha1s = { "sha1One".getBytes(), "sha1Two".getBytes() }; List expectedResponse = Arrays.asList(true, false); when(commandObjects.scriptExists(sampleKey, sha1s)).thenReturn(listBooleanCommandObject); when(commandExecutor.executeCommand(listBooleanCommandObject)).thenReturn(expectedResponse); List result = jedis.scriptExists(sampleKey, sha1s); assertThat(result, equalTo(expectedResponse)); verify(commandExecutor).executeCommand(listBooleanCommandObject); verify(commandObjects).scriptExists(sampleKey, sha1s); } @Test public void testScriptFlushWithoutKey() { String expectedResponse = "OK"; when(commandObjects.scriptFlush()).thenReturn(stringCommandObject); when(commandExecutor.executeCommand(stringCommandObject)).thenReturn(expectedResponse); String result = jedis.scriptFlush(); assertThat(result, equalTo(expectedResponse)); verify(commandExecutor).executeCommand(stringCommandObject); verify(commandObjects).scriptFlush(); } @Test public void testScriptFlush() { String sampleKey = "myKey"; String expectedResponse = "OK"; when(commandObjects.scriptFlush(sampleKey)).thenReturn(stringCommandObject); when(commandExecutor.executeCommand(stringCommandObject)).thenReturn(expectedResponse); String result = jedis.scriptFlush(sampleKey); assertThat(result, equalTo(expectedResponse)); verify(commandExecutor).executeCommand(stringCommandObject); verify(commandObjects).scriptFlush(sampleKey); } @Test public void testScriptFlushBinary() { byte[] sampleKey = "myKey".getBytes(); String expectedResponse = "OK"; when(commandObjects.scriptFlush(sampleKey)).thenReturn(stringCommandObject); when(commandExecutor.executeCommand(stringCommandObject)).thenReturn(expectedResponse); String result = jedis.scriptFlush(sampleKey); assertThat(result, equalTo(expectedResponse)); verify(commandExecutor).executeCommand(stringCommandObject); verify(commandObjects).scriptFlush(sampleKey); } @Test public void testScriptFlushWithMode() { String sampleKey = "myKey"; FlushMode flushMode = FlushMode.SYNC; String expectedResponse = "OK"; when(commandObjects.scriptFlush(sampleKey, flushMode)).thenReturn(stringCommandObject); when(commandExecutor.executeCommand(stringCommandObject)).thenReturn(expectedResponse); String result = jedis.scriptFlush(sampleKey, flushMode); assertThat(result, equalTo(expectedResponse)); verify(commandExecutor).executeCommand(stringCommandObject); verify(commandObjects).scriptFlush(sampleKey, flushMode); } @Test public void testScriptFlushWithModeBinary() { byte[] sampleKey = "myKey".getBytes(); FlushMode flushMode = FlushMode.SYNC; String expectedResponse = "OK"; when(commandObjects.scriptFlush(sampleKey, flushMode)).thenReturn(stringCommandObject); when(commandExecutor.executeCommand(stringCommandObject)).thenReturn(expectedResponse); String result = jedis.scriptFlush(sampleKey, flushMode); assertThat(result, equalTo(expectedResponse)); verify(commandExecutor).executeCommand(stringCommandObject); verify(commandObjects).scriptFlush(sampleKey, flushMode); } @Test public void testScriptKillWithoutKey() { String expectedResponse = "OK"; when(commandObjects.scriptKill()).thenReturn(stringCommandObject); when(commandExecutor.executeCommand(stringCommandObject)).thenReturn(expectedResponse); String result = jedis.scriptKill(); assertThat(result, equalTo(expectedResponse)); verify(commandExecutor).executeCommand(stringCommandObject); verify(commandObjects).scriptKill(); } @Test public void testScriptKill() { String sampleKey = "myKey"; String expectedResponse = "OK"; when(commandObjects.scriptKill(sampleKey)).thenReturn(stringCommandObject); when(commandExecutor.executeCommand(stringCommandObject)).thenReturn(expectedResponse); String result = jedis.scriptKill(sampleKey); assertThat(result, equalTo(expectedResponse)); verify(commandExecutor).executeCommand(stringCommandObject); verify(commandObjects).scriptKill(sampleKey); } @Test public void testScriptKillBinary() { byte[] sampleKey = "myKey".getBytes(); String expectedResponse = "OK"; when(commandObjects.scriptKill(sampleKey)).thenReturn(stringCommandObject); when(commandExecutor.executeCommand(stringCommandObject)).thenReturn(expectedResponse); String result = jedis.scriptKill(sampleKey); assertThat(result, equalTo(expectedResponse)); verify(commandExecutor).executeCommand(stringCommandObject); verify(commandObjects).scriptKill(sampleKey); } @Test public void testScriptLoadWithoutKey() { String script = "return redis.call('get', 'constantKey')"; String expectedSha1 = "someSha1Hash"; when(commandObjects.scriptLoad(script)).thenReturn(stringCommandObject); when(commandExecutor.executeCommand(stringCommandObject)).thenReturn(expectedSha1); String result = jedis.scriptLoad(script); assertThat(result, equalTo(expectedSha1)); verify(commandExecutor).executeCommand(stringCommandObject); verify(commandObjects).scriptLoad(script); } @Test public void testScriptLoad() { String script = "return redis.call('get', KEYS[1])"; String sampleKey = "myKey"; String expectedSha1 = "someSha1Hash"; when(commandObjects.scriptLoad(script, sampleKey)).thenReturn(stringCommandObject); when(commandExecutor.executeCommand(stringCommandObject)).thenReturn(expectedSha1); String result = jedis.scriptLoad(script, sampleKey); assertThat(result, equalTo(expectedSha1)); verify(commandExecutor).executeCommand(stringCommandObject); verify(commandObjects).scriptLoad(script, sampleKey); } @Test public void testScriptLoadBinary() { byte[] script = "return redis.call('get', KEYS[1])".getBytes(); byte[] sampleKey = "myKey".getBytes(); byte[] expectedSha1 = "someSha1Hash".getBytes(); when(commandObjects.scriptLoad(script, sampleKey)).thenReturn(bytesCommandObject); when(commandExecutor.executeCommand(bytesCommandObject)).thenReturn(expectedSha1); byte[] result = jedis.scriptLoad(script, sampleKey); assertThat(result, equalTo(expectedSha1)); verify(commandExecutor).executeCommand(bytesCommandObject); verify(commandObjects).scriptLoad(script, sampleKey); } } ================================================ FILE: src/test/java/redis/clients/jedis/mocked/unified/UnifiedJedisSearchAndQueryCommandsTest.java ================================================ package redis.clients.jedis.mocked.unified; import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.equalTo; import static org.hamcrest.Matchers.sameInstance; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; import java.util.AbstractMap; import java.util.Arrays; import java.util.Collections; import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Set; import org.junit.jupiter.api.Test; import redis.clients.jedis.resps.Tuple; import redis.clients.jedis.search.*; import redis.clients.jedis.search.aggr.AggregationBuilder; import redis.clients.jedis.search.aggr.AggregationResult; import redis.clients.jedis.search.schemafields.SchemaField; import redis.clients.jedis.search.schemafields.TextField; public class UnifiedJedisSearchAndQueryCommandsTest extends UnifiedJedisMockedTestBase { @Test public void testFtAggregate() { String indexName = "myIndex"; AggregationBuilder aggr = new AggregationBuilder().groupBy("@field"); AggregationResult expectedResponse = mock(AggregationResult.class); when(commandObjects.ftAggregate(indexName, aggr)).thenReturn(aggregationResultCommandObject); when(commandExecutor.executeCommand(aggregationResultCommandObject)).thenReturn(expectedResponse); AggregationResult result = jedis.ftAggregate(indexName, aggr); assertThat(result, sameInstance(expectedResponse)); verify(commandExecutor).executeCommand(aggregationResultCommandObject); verify(commandObjects).ftAggregate(indexName, aggr); } @Test public void testFtAliasAdd() { String aliasName = "myAlias"; String indexName = "myIndex"; String expectedResponse = "OK"; when(commandObjects.ftAliasAdd(aliasName, indexName)).thenReturn(stringCommandObject); when(commandExecutor.executeCommand(stringCommandObject)).thenReturn(expectedResponse); String result = jedis.ftAliasAdd(aliasName, indexName); assertThat(result, equalTo(expectedResponse)); verify(commandExecutor).executeCommand(stringCommandObject); verify(commandObjects).ftAliasAdd(aliasName, indexName); } @Test public void testFtAliasDel() { String aliasName = "myAlias"; String expectedResponse = "OK"; when(commandObjects.ftAliasDel(aliasName)).thenReturn(stringCommandObject); when(commandExecutor.executeCommand(stringCommandObject)).thenReturn(expectedResponse); String result = jedis.ftAliasDel(aliasName); assertThat(result, equalTo(expectedResponse)); verify(commandExecutor).executeCommand(stringCommandObject); verify(commandObjects).ftAliasDel(aliasName); } @Test public void testFtAliasUpdate() { String aliasName = "myAlias"; String indexName = "myIndex"; String expectedResponse = "OK"; when(commandObjects.ftAliasUpdate(aliasName, indexName)).thenReturn(stringCommandObject); when(commandExecutor.executeCommand(stringCommandObject)).thenReturn(expectedResponse); String result = jedis.ftAliasUpdate(aliasName, indexName); assertThat(result, equalTo(expectedResponse)); verify(commandExecutor).executeCommand(stringCommandObject); verify(commandObjects).ftAliasUpdate(aliasName, indexName); } @Test public void testFtAlterWithSchema() { String indexName = "myIndex"; Schema schema = new Schema().addField(new Schema.Field("myField", Schema.FieldType.TEXT)); String expectedResponse = "OK"; when(commandObjects.ftAlter(indexName, schema)).thenReturn(stringCommandObject); when(commandExecutor.executeCommand(stringCommandObject)).thenReturn(expectedResponse); String result = jedis.ftAlter(indexName, schema); assertThat(result, equalTo(expectedResponse)); verify(commandExecutor).executeCommand(stringCommandObject); verify(commandObjects).ftAlter(indexName, schema); } @Test public void testFtAlterWithSchemaFields() { String indexName = "myIndex"; Iterable schemaFields = Collections.singletonList(new TextField("newField")); String expectedResponse = "OK"; when(commandObjects.ftAlter(indexName, schemaFields)).thenReturn(stringCommandObject); when(commandExecutor.executeCommand(stringCommandObject)).thenReturn(expectedResponse); String result = jedis.ftAlter(indexName, schemaFields); assertThat(result, equalTo(expectedResponse)); verify(commandExecutor).executeCommand(stringCommandObject); verify(commandObjects).ftAlter(indexName, schemaFields); } @Test public void testFtConfigGet() { String option = "TIMEOUT"; Map expectedResponse = Collections.singletonMap(option, "1000"); when(commandObjects.ftConfigGet(option)).thenReturn(mapStringObjectCommandObject); when(commandExecutor.executeCommand(mapStringObjectCommandObject)).thenReturn(expectedResponse); Map result = jedis.ftConfigGet(option); assertThat(result, equalTo(expectedResponse)); verify(commandExecutor).executeCommand(mapStringObjectCommandObject); verify(commandObjects).ftConfigGet(option); } @Test public void testFtConfigGetWithIndexName() { String indexName = "myIndex"; String option = "TIMEOUT"; Map expectedResponse = Collections.singletonMap(option, "1000"); when(commandObjects.ftConfigGet(indexName, option)).thenReturn(mapStringObjectCommandObject); when(commandExecutor.executeCommand(mapStringObjectCommandObject)).thenReturn(expectedResponse); Map result = jedis.ftConfigGet(indexName, option); assertThat(result, equalTo(expectedResponse)); verify(commandExecutor).executeCommand(mapStringObjectCommandObject); verify(commandObjects).ftConfigGet(indexName, option); } @Test public void testFtConfigSet() { String option = "TIMEOUT"; String value = "1000"; String expectedResponse = "OK"; when(commandObjects.ftConfigSet(option, value)).thenReturn(stringCommandObject); when(commandExecutor.executeCommand(stringCommandObject)).thenReturn(expectedResponse); String result = jedis.ftConfigSet(option, value); assertThat(result, equalTo(expectedResponse)); verify(commandExecutor).executeCommand(stringCommandObject); verify(commandObjects).ftConfigSet(option, value); } @Test public void testFtConfigSetWithIndexName() { String indexName = "myIndex"; String option = "TIMEOUT"; String value = "1000"; String expectedResponse = "OK"; when(commandObjects.ftConfigSet(indexName, option, value)).thenReturn(stringCommandObject); when(commandExecutor.executeCommand(stringCommandObject)).thenReturn(expectedResponse); String result = jedis.ftConfigSet(indexName, option, value); assertThat(result, equalTo(expectedResponse)); verify(commandExecutor).executeCommand(stringCommandObject); verify(commandObjects).ftConfigSet(indexName, option, value); } @Test public void testFtCreateWithOptionsAndSchema() { String indexName = "myIndex"; IndexOptions indexOptions = IndexOptions.defaultOptions(); Schema schema = new Schema().addField(new Schema.Field("myField", Schema.FieldType.TEXT)); String expectedResponse = "OK"; when(commandObjects.ftCreate(indexName, indexOptions, schema)).thenReturn(stringCommandObject); when(commandExecutor.executeCommand(stringCommandObject)).thenReturn(expectedResponse); String result = jedis.ftCreate(indexName, indexOptions, schema); assertThat(result, equalTo(expectedResponse)); verify(commandExecutor).executeCommand(stringCommandObject); verify(commandObjects).ftCreate(indexName, indexOptions, schema); } @Test public void testFtCreateWithCreateParamsAndSchemaFields() { String indexName = "myIndex"; FTCreateParams createParams = FTCreateParams.createParams(); Iterable schemaFields = Collections.singletonList(new TextField("myField")); String expectedResponse = "OK"; when(commandObjects.ftCreate(indexName, createParams, schemaFields)).thenReturn(stringCommandObject); when(commandExecutor.executeCommand(stringCommandObject)).thenReturn(expectedResponse); String result = jedis.ftCreate(indexName, createParams, schemaFields); assertThat(result, equalTo(expectedResponse)); verify(commandExecutor).executeCommand(stringCommandObject); verify(commandObjects).ftCreate(indexName, createParams, schemaFields); } @Test public void testFtCursorDel() { String indexName = "myIndex"; long cursorId = 123L; String expectedResponse = "OK"; when(commandObjects.ftCursorDel(indexName, cursorId)).thenReturn(stringCommandObject); when(commandExecutor.executeCommand(stringCommandObject)).thenReturn(expectedResponse); String result = jedis.ftCursorDel(indexName, cursorId); assertThat(result, equalTo(expectedResponse)); verify(commandExecutor).executeCommand(stringCommandObject); verify(commandObjects).ftCursorDel(indexName, cursorId); } @Test public void testFtCursorRead() { String indexName = "myIndex"; long cursorId = 123L; int count = 10; AggregationResult expectedResponse = mock(AggregationResult.class); when(commandObjects.ftCursorRead(indexName, cursorId, count)).thenReturn(aggregationResultCommandObject); when(commandExecutor.executeCommand(aggregationResultCommandObject)).thenReturn(expectedResponse); AggregationResult result = jedis.ftCursorRead(indexName, cursorId, count); assertThat(result, sameInstance(expectedResponse)); verify(commandExecutor).executeCommand(aggregationResultCommandObject); verify(commandObjects).ftCursorRead(indexName, cursorId, count); } @Test public void testFtDictAdd() { String dictionary = "myDict"; String[] terms = { "term1", "term2" }; long expectedResponse = 2L; when(commandObjects.ftDictAdd(dictionary, terms)).thenReturn(longCommandObject); when(commandExecutor.executeCommand(longCommandObject)).thenReturn(expectedResponse); long result = jedis.ftDictAdd(dictionary, terms); assertThat(result, equalTo(expectedResponse)); verify(commandExecutor).executeCommand(longCommandObject); verify(commandObjects).ftDictAdd(dictionary, terms); } @Test public void testFtDictAddBySampleKey() { String indexName = "myIndex"; String dictionary = "myDict"; String[] terms = { "term1", "term2" }; long expectedResponse = 2L; when(commandObjects.ftDictAddBySampleKey(indexName, dictionary, terms)).thenReturn(longCommandObject); when(commandExecutor.executeCommand(longCommandObject)).thenReturn(expectedResponse); long result = jedis.ftDictAddBySampleKey(indexName, dictionary, terms); assertThat(result, equalTo(expectedResponse)); verify(commandExecutor).executeCommand(longCommandObject); verify(commandObjects).ftDictAddBySampleKey(indexName, dictionary, terms); } @Test public void testFtDictDel() { String dictionary = "myDict"; String[] terms = { "term1", "term2" }; long expectedResponse = 1L; when(commandObjects.ftDictDel(dictionary, terms)).thenReturn(longCommandObject); when(commandExecutor.executeCommand(longCommandObject)).thenReturn(expectedResponse); long result = jedis.ftDictDel(dictionary, terms); assertThat(result, equalTo(expectedResponse)); verify(commandExecutor).executeCommand(longCommandObject); verify(commandObjects).ftDictDel(dictionary, terms); } @Test public void testFtDictDelBySampleKey() { String indexName = "myIndex"; String dictionary = "myDict"; String[] terms = { "term1", "term2" }; long expectedResponse = 1L; when(commandObjects.ftDictDelBySampleKey(indexName, dictionary, terms)).thenReturn(longCommandObject); when(commandExecutor.executeCommand(longCommandObject)).thenReturn(expectedResponse); long result = jedis.ftDictDelBySampleKey(indexName, dictionary, terms); assertThat(result, equalTo(expectedResponse)); verify(commandExecutor).executeCommand(longCommandObject); verify(commandObjects).ftDictDelBySampleKey(indexName, dictionary, terms); } @Test public void testFtDictDump() { String dictionary = "myDict"; Set expectedResponse = new HashSet<>(Arrays.asList("term1", "term2")); when(commandObjects.ftDictDump(dictionary)).thenReturn(setStringCommandObject); when(commandExecutor.executeCommand(setStringCommandObject)).thenReturn(expectedResponse); Set result = jedis.ftDictDump(dictionary); assertThat(result, equalTo(expectedResponse)); verify(commandExecutor).executeCommand(setStringCommandObject); verify(commandObjects).ftDictDump(dictionary); } @Test public void testFtDictDumpBySampleKey() { String indexName = "myIndex"; String dictionary = "myDict"; Set expectedResponse = new HashSet<>(Arrays.asList("term1", "term2")); when(commandObjects.ftDictDumpBySampleKey(indexName, dictionary)).thenReturn(setStringCommandObject); when(commandExecutor.executeCommand(setStringCommandObject)).thenReturn(expectedResponse); Set result = jedis.ftDictDumpBySampleKey(indexName, dictionary); assertThat(result, equalTo(expectedResponse)); verify(commandExecutor).executeCommand(setStringCommandObject); verify(commandObjects).ftDictDumpBySampleKey(indexName, dictionary); } @Test public void testFtDropIndex() { String indexName = "myIndex"; String expectedResponse = "OK"; when(commandObjects.ftDropIndex(indexName)).thenReturn(stringCommandObject); when(commandExecutor.executeCommand(stringCommandObject)).thenReturn(expectedResponse); String result = jedis.ftDropIndex(indexName); assertThat(result, equalTo(expectedResponse)); verify(commandExecutor).executeCommand(stringCommandObject); verify(commandObjects).ftDropIndex(indexName); } @Test public void testFtDropIndexDD() { String indexName = "myIndex"; String expectedResponse = "OK"; when(commandObjects.ftDropIndexDD(indexName)).thenReturn(stringCommandObject); when(commandExecutor.executeCommand(stringCommandObject)).thenReturn(expectedResponse); String result = jedis.ftDropIndexDD(indexName); assertThat(result, equalTo(expectedResponse)); verify(commandExecutor).executeCommand(stringCommandObject); verify(commandObjects).ftDropIndexDD(indexName); } @Test public void testFtExplain() { String indexName = "myIndex"; Query query = new Query("hello world").limit(0, 10); String expectedResponse = "QUERY PLAN"; when(commandObjects.ftExplain(indexName, query)).thenReturn(stringCommandObject); when(commandExecutor.executeCommand(stringCommandObject)).thenReturn(expectedResponse); String result = jedis.ftExplain(indexName, query); assertThat(result, equalTo(expectedResponse)); verify(commandExecutor).executeCommand(stringCommandObject); verify(commandObjects).ftExplain(indexName, query); } @Test public void testFtExplainCLI() { String indexName = "myIndex"; Query query = new Query("hello world").limit(0, 10); List expectedResponse = Arrays.asList("QUERY PLAN", "DETAILS"); when(commandObjects.ftExplainCLI(indexName, query)).thenReturn(listStringCommandObject); when(commandExecutor.executeCommand(listStringCommandObject)).thenReturn(expectedResponse); List result = jedis.ftExplainCLI(indexName, query); assertThat(result, equalTo(expectedResponse)); verify(commandExecutor).executeCommand(listStringCommandObject); verify(commandObjects).ftExplainCLI(indexName, query); } @Test public void testFtInfo() { String indexName = "myIndex"; Map expectedResponse = Collections.singletonMap("index_definition", Collections.singletonMap("key_type", "HASH")); when(commandObjects.ftInfo(indexName)).thenReturn(mapStringObjectCommandObject); when(commandExecutor.executeCommand(mapStringObjectCommandObject)).thenReturn(expectedResponse); Map result = jedis.ftInfo(indexName); assertThat(result, equalTo(expectedResponse)); verify(commandExecutor).executeCommand(mapStringObjectCommandObject); verify(commandObjects).ftInfo(indexName); } @Test public void testFtList() { Set expectedResponse = new HashSet<>(Arrays.asList("index1", "index2")); when(commandObjects.ftList()).thenReturn(setStringCommandObject); when(commandExecutor.executeCommand(setStringCommandObject)).thenReturn(expectedResponse); Set result = jedis.ftList(); assertThat(result, equalTo(expectedResponse)); verify(commandExecutor).executeCommand(setStringCommandObject); verify(commandObjects).ftList(); } @Test public void testFtSearch() { String indexName = "myIndex"; String query = "hello world"; SearchResult expectedResponse = mock(SearchResult.class); when(commandObjects.ftSearch(indexName, query)).thenReturn(searchResultCommandObject); when(commandExecutor.executeCommand(searchResultCommandObject)).thenReturn(expectedResponse); SearchResult result = jedis.ftSearch(indexName, query); assertThat(result, sameInstance(expectedResponse)); verify(commandExecutor).executeCommand(searchResultCommandObject); verify(commandObjects).ftSearch(indexName, query); } @Test public void testFtSearchWithParams() { String indexName = "myIndex"; String query = "hello world"; FTSearchParams params = new FTSearchParams().noContent().limit(0, 10); SearchResult expectedResponse = mock(SearchResult.class); when(commandObjects.ftSearch(indexName, query, params)).thenReturn(searchResultCommandObject); when(commandExecutor.executeCommand(searchResultCommandObject)).thenReturn(expectedResponse); SearchResult result = jedis.ftSearch(indexName, query, params); assertThat(result, sameInstance(expectedResponse)); verify(commandExecutor).executeCommand(searchResultCommandObject); verify(commandObjects).ftSearch(indexName, query, params); } @Test public void testFtSearchWithQueryObject() { String indexName = "myIndex"; Query query = new Query("hello world"); SearchResult expectedResponse = mock(SearchResult.class); when(commandObjects.ftSearch(indexName, query)).thenReturn(searchResultCommandObject); when(commandExecutor.executeCommand(searchResultCommandObject)).thenReturn(expectedResponse); SearchResult result = jedis.ftSearch(indexName, query); assertThat(result, sameInstance(expectedResponse)); verify(commandExecutor).executeCommand(searchResultCommandObject); verify(commandObjects).ftSearch(indexName, query); } @Test public void testFtSearchWithQueryObjectBinary() { byte[] indexName = "myIndex".getBytes(); Query query = new Query("hello world").limit(0, 10); SearchResult expectedResponse = mock(SearchResult.class); when(commandObjects.ftSearch(indexName, query)).thenReturn(searchResultCommandObject); when(commandExecutor.executeCommand(searchResultCommandObject)).thenReturn(expectedResponse); SearchResult result = jedis.ftSearch(indexName, query); assertThat(result, sameInstance(expectedResponse)); verify(commandExecutor).executeCommand(searchResultCommandObject); verify(commandObjects).ftSearch(indexName, query); } @Test public void testFtSpellCheck() { String index = "myIndex"; String query = "hello world"; Map> expectedResponse = Collections.singletonMap("term1", Collections.singletonMap("suggestion1", 1.0)); when(commandObjects.ftSpellCheck(index, query)).thenReturn(mapStringMapStringDoubleCommandObject); when(commandExecutor.executeCommand(mapStringMapStringDoubleCommandObject)).thenReturn(expectedResponse); Map> result = jedis.ftSpellCheck(index, query); assertThat(result, equalTo(expectedResponse)); verify(commandExecutor).executeCommand(mapStringMapStringDoubleCommandObject); verify(commandObjects).ftSpellCheck(index, query); } @Test public void testFtSpellCheckWithParams() { String index = "myIndex"; String query = "hello world"; FTSpellCheckParams spellCheckParams = new FTSpellCheckParams().distance(1); Map> expectedResponse = Collections.singletonMap("term1", Collections.singletonMap("suggestion1", 1.0)); when(commandObjects.ftSpellCheck(index, query, spellCheckParams)).thenReturn(mapStringMapStringDoubleCommandObject); when(commandExecutor.executeCommand(mapStringMapStringDoubleCommandObject)).thenReturn(expectedResponse); Map> result = jedis.ftSpellCheck(index, query, spellCheckParams); assertThat(result, equalTo(expectedResponse)); verify(commandExecutor).executeCommand(mapStringMapStringDoubleCommandObject); verify(commandObjects).ftSpellCheck(index, query, spellCheckParams); } @Test public void testFtSynDump() { String indexName = "myIndex"; Map> expectedResponse = Collections.singletonMap("group1", Arrays.asList("term1", "term2")); when(commandObjects.ftSynDump(indexName)).thenReturn(mapStringListStringCommandObject); when(commandExecutor.executeCommand(mapStringListStringCommandObject)).thenReturn(expectedResponse); Map> result = jedis.ftSynDump(indexName); assertThat(result, equalTo(expectedResponse)); verify(commandExecutor).executeCommand(mapStringListStringCommandObject); verify(commandObjects).ftSynDump(indexName); } @Test public void testFtSynUpdate() { String indexName = "myIndex"; String synonymGroupId = "group1"; String[] terms = { "term1", "term2" }; String expectedResponse = "OK"; when(commandObjects.ftSynUpdate(indexName, synonymGroupId, terms)).thenReturn(stringCommandObject); when(commandExecutor.executeCommand(stringCommandObject)).thenReturn(expectedResponse); String result = jedis.ftSynUpdate(indexName, synonymGroupId, terms); assertThat(result, equalTo(expectedResponse)); verify(commandExecutor).executeCommand(stringCommandObject); verify(commandObjects).ftSynUpdate(indexName, synonymGroupId, terms); } @Test public void testFtTagVals() { String indexName = "myIndex"; String fieldName = "myField"; Set expectedResponse = new HashSet<>(Arrays.asList("tag1", "tag2")); when(commandObjects.ftTagVals(indexName, fieldName)).thenReturn(setStringCommandObject); when(commandExecutor.executeCommand(setStringCommandObject)).thenReturn(expectedResponse); Set result = jedis.ftTagVals(indexName, fieldName); assertThat(result, equalTo(expectedResponse)); verify(commandExecutor).executeCommand(setStringCommandObject); verify(commandObjects).ftTagVals(indexName, fieldName); } @Test public void testFtSugAdd() { String key = "sugKey"; String string = "suggestion"; double score = 1.0; long expectedResponse = 1L; when(commandObjects.ftSugAdd(key, string, score)).thenReturn(longCommandObject); when(commandExecutor.executeCommand(longCommandObject)).thenReturn(expectedResponse); long result = jedis.ftSugAdd(key, string, score); assertThat(result, equalTo(expectedResponse)); verify(commandExecutor).executeCommand(longCommandObject); verify(commandObjects).ftSugAdd(key, string, score); } @Test public void testFtSugAddIncr() { String key = "sugKey"; String string = "suggestion"; double score = 1.0; long expectedResponse = 2L; when(commandObjects.ftSugAddIncr(key, string, score)).thenReturn(longCommandObject); when(commandExecutor.executeCommand(longCommandObject)).thenReturn(expectedResponse); long result = jedis.ftSugAddIncr(key, string, score); assertThat(result, equalTo(expectedResponse)); verify(commandExecutor).executeCommand(longCommandObject); verify(commandObjects).ftSugAddIncr(key, string, score); } @Test public void testFtSugDel() { String key = "sugKey"; String string = "suggestion"; boolean expectedResponse = true; when(commandObjects.ftSugDel(key, string)).thenReturn(booleanCommandObject); when(commandExecutor.executeCommand(booleanCommandObject)).thenReturn(expectedResponse); boolean result = jedis.ftSugDel(key, string); assertThat(result, equalTo(expectedResponse)); verify(commandExecutor).executeCommand(booleanCommandObject); verify(commandObjects).ftSugDel(key, string); } @Test public void testFtSugGet() { String key = "sugKey"; String prefix = "sug"; List expectedResponse = Arrays.asList("suggestion1", "suggestion2"); when(commandObjects.ftSugGet(key, prefix)).thenReturn(listStringCommandObject); when(commandExecutor.executeCommand(listStringCommandObject)).thenReturn(expectedResponse); List result = jedis.ftSugGet(key, prefix); assertThat(result, equalTo(expectedResponse)); verify(commandExecutor).executeCommand(listStringCommandObject); verify(commandObjects).ftSugGet(key, prefix); } @Test public void testFtSugGetWithFuzzyAndMax() { String key = "sugKey"; String prefix = "sug"; boolean fuzzy = true; int max = 10; List expectedResponse = Arrays.asList("suggestion1", "suggestion2"); when(commandObjects.ftSugGet(key, prefix, fuzzy, max)).thenReturn(listStringCommandObject); when(commandExecutor.executeCommand(listStringCommandObject)).thenReturn(expectedResponse); List result = jedis.ftSugGet(key, prefix, fuzzy, max); assertThat(result, equalTo(expectedResponse)); verify(commandExecutor).executeCommand(listStringCommandObject); verify(commandObjects).ftSugGet(key, prefix, fuzzy, max); } @Test public void testFtSugGetWithScores() { String key = "sugKey"; String prefix = "sug"; List expectedResponse = Arrays.asList(new Tuple("suggestion1", 1.0), new Tuple("suggestion2", 0.8)); when(commandObjects.ftSugGetWithScores(key, prefix)).thenReturn(listTupleCommandObject); when(commandExecutor.executeCommand(listTupleCommandObject)).thenReturn(expectedResponse); List result = jedis.ftSugGetWithScores(key, prefix); assertThat(result, equalTo(expectedResponse)); verify(commandExecutor).executeCommand(listTupleCommandObject); verify(commandObjects).ftSugGetWithScores(key, prefix); } @Test public void testFtSugGetWithScoresAndFuzzyMax() { String key = "sugKey"; String prefix = "sug"; boolean fuzzy = true; int max = 10; List expectedResponse = Arrays.asList(new Tuple("suggestion1", 1.0), new Tuple("suggestion2", 0.8)); when(commandObjects.ftSugGetWithScores(key, prefix, fuzzy, max)).thenReturn(listTupleCommandObject); when(commandExecutor.executeCommand(listTupleCommandObject)).thenReturn(expectedResponse); List result = jedis.ftSugGetWithScores(key, prefix, fuzzy, max); assertThat(result, equalTo(expectedResponse)); verify(commandExecutor).executeCommand(listTupleCommandObject); verify(commandObjects).ftSugGetWithScores(key, prefix, fuzzy, max); } @Test public void testFtSugLen() { String key = "sugKey"; long expectedResponse = 42L; when(commandObjects.ftSugLen(key)).thenReturn(longCommandObject); when(commandExecutor.executeCommand(longCommandObject)).thenReturn(expectedResponse); long result = jedis.ftSugLen(key); assertThat(result, equalTo(expectedResponse)); verify(commandExecutor).executeCommand(longCommandObject); verify(commandObjects).ftSugLen(key); } @Test public void testFtProfileAggregate() { String indexName = "myIndex"; FTProfileParams profileParams = new FTProfileParams(); AggregationBuilder aggr = new AggregationBuilder().groupBy("@field"); Map.Entry expectedResponse = new AbstractMap.SimpleEntry<>( mock(AggregationResult.class), mock(ProfilingInfo.class)); when(commandObjects.ftProfileAggregate(indexName, profileParams, aggr)).thenReturn(entryAggregationResultMapStringObjectCommandObject); when(commandExecutor.executeCommand(entryAggregationResultMapStringObjectCommandObject)).thenReturn(expectedResponse); Map.Entry result = jedis.ftProfileAggregate(indexName, profileParams, aggr); assertThat(result, equalTo(expectedResponse)); verify(commandExecutor).executeCommand(entryAggregationResultMapStringObjectCommandObject); verify(commandObjects).ftProfileAggregate(indexName, profileParams, aggr); } @Test public void testFtProfileSearchWithQueryObject() { String indexName = "myIndex"; FTProfileParams profileParams = new FTProfileParams(); Query query = new Query("hello world").limit(0, 10); Map.Entry expectedResponse = new AbstractMap.SimpleEntry<>( mock(SearchResult.class), mock(ProfilingInfo.class)); when(commandObjects.ftProfileSearch(indexName, profileParams, query)).thenReturn(entrySearchResultMapStringObjectCommandObject); when(commandExecutor.executeCommand(entrySearchResultMapStringObjectCommandObject)).thenReturn(expectedResponse); Map.Entry result = jedis.ftProfileSearch(indexName, profileParams, query); assertThat(result, equalTo(expectedResponse)); verify(commandExecutor).executeCommand(entrySearchResultMapStringObjectCommandObject); verify(commandObjects).ftProfileSearch(indexName, profileParams, query); } @Test public void testFtProfileSearchWithQueryAndSearchParams() { String indexName = "myIndex"; FTProfileParams profileParams = new FTProfileParams(); String query = "hello world"; FTSearchParams searchParams = new FTSearchParams().noContent().limit(0, 10); Map.Entry expectedResponse = new AbstractMap.SimpleEntry<>( mock(SearchResult.class), mock(ProfilingInfo.class)); when(commandObjects.ftProfileSearch(indexName, profileParams, query, searchParams)).thenReturn(entrySearchResultMapStringObjectCommandObject); when(commandExecutor.executeCommand(entrySearchResultMapStringObjectCommandObject)).thenReturn(expectedResponse); Map.Entry result = jedis.ftProfileSearch(indexName, profileParams, query, searchParams); assertThat(result, equalTo(expectedResponse)); verify(commandExecutor).executeCommand(entrySearchResultMapStringObjectCommandObject); verify(commandObjects).ftProfileSearch(indexName, profileParams, query, searchParams); } @Test public void testSetDefaultSearchDialect() { int dialect = 1; jedis.setDefaultSearchDialect(dialect); verify(commandObjects).setDefaultSearchDialect(dialect); } } ================================================ FILE: src/test/java/redis/clients/jedis/mocked/unified/UnifiedJedisServerManagementCommandsTest.java ================================================ package redis.clients.jedis.mocked.unified; import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.equalTo; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; import org.junit.jupiter.api.Test; public class UnifiedJedisServerManagementCommandsTest extends UnifiedJedisMockedTestBase { @Test public void testConfigSet() { String parameter = "param"; String value = "value"; when(commandObjects.configSet(parameter, value)).thenReturn(stringCommandObject); when(commandExecutor.executeCommand(stringCommandObject)).thenReturn("OK"); String result = jedis.configSet(parameter, value); assertThat(result, equalTo("OK")); verify(commandExecutor).executeCommand(stringCommandObject); verify(commandObjects).configSet(parameter, value); } @Test public void testDbSize() { long expectedSize = 42L; when(commandObjects.dbSize()).thenReturn(longCommandObject); when(commandExecutor.executeCommand(longCommandObject)).thenReturn(expectedSize); long result = jedis.dbSize(); assertThat(result, equalTo(expectedSize)); verify(commandExecutor).executeCommand(longCommandObject); verify(commandObjects).dbSize(); } @Test public void testFlushAll() { when(commandObjects.flushAll()).thenReturn(stringCommandObject); when(commandExecutor.executeCommand(stringCommandObject)).thenReturn("OK"); String result = jedis.flushAll(); assertThat(result, equalTo("OK")); verify(commandExecutor).executeCommand(stringCommandObject); verify(commandObjects).flushAll(); } @Test public void testFlushDB() { when(commandObjects.flushDB()).thenReturn(stringCommandObject); when(commandExecutor.executeCommand(stringCommandObject)).thenReturn("OK"); String result = jedis.flushDB(); assertThat(result, equalTo("OK")); verify(commandExecutor).executeCommand(stringCommandObject); verify(commandObjects).flushDB(); } @Test public void testMemoryUsage() { String key = "key1"; Long expectedMemoryUsage = 1024L; when(commandObjects.memoryUsage(key)).thenReturn(longCommandObject); when(commandExecutor.executeCommand(longCommandObject)).thenReturn(expectedMemoryUsage); Long result = jedis.memoryUsage(key); assertThat(result, equalTo(expectedMemoryUsage)); verify(commandExecutor).executeCommand(longCommandObject); verify(commandObjects).memoryUsage(key); } @Test public void testMemoryUsageWithSamples() { String key = "key1"; int samples = 5; Long expectedMemoryUsage = 2048L; when(commandObjects.memoryUsage(key, samples)).thenReturn(longCommandObject); when(commandExecutor.executeCommand(longCommandObject)).thenReturn(expectedMemoryUsage); Long result = jedis.memoryUsage(key, samples); assertThat(result, equalTo(expectedMemoryUsage)); verify(commandExecutor).executeCommand(longCommandObject); verify(commandObjects).memoryUsage(key, samples); } @Test public void testMemoryUsageBinary() { byte[] key = new byte[]{ 1, 2, 3 }; Long expectedMemoryUsage = 512L; when(commandObjects.memoryUsage(key)).thenReturn(longCommandObject); when(commandExecutor.executeCommand(longCommandObject)).thenReturn(expectedMemoryUsage); Long result = jedis.memoryUsage(key); assertThat(result, equalTo(expectedMemoryUsage)); verify(commandExecutor).executeCommand(longCommandObject); verify(commandObjects).memoryUsage(key); } @Test public void testMemoryUsageWithSamplesBinary() { byte[] key = new byte[]{ 1, 2, 3 }; int samples = 5; Long expectedMemoryUsage = 1024L; when(commandObjects.memoryUsage(key, samples)).thenReturn(longCommandObject); when(commandExecutor.executeCommand(longCommandObject)).thenReturn(expectedMemoryUsage); Long result = jedis.memoryUsage(key, samples); assertThat(result, equalTo(expectedMemoryUsage)); verify(commandExecutor).executeCommand(longCommandObject); verify(commandObjects).memoryUsage(key, samples); } @Test public void testSlowlogReset() { String expectedResponse = "OK"; when(commandObjects.slowlogReset()).thenReturn(stringCommandObject); when(commandExecutor.executeCommand(stringCommandObject)).thenReturn(expectedResponse); String result = jedis.slowlogReset(); assertThat(result, equalTo(expectedResponse)); verify(commandExecutor).executeCommand(stringCommandObject); verify(commandObjects).slowlogReset(); } } ================================================ FILE: src/test/java/redis/clients/jedis/mocked/unified/UnifiedJedisSetCommandsTest.java ================================================ package redis.clients.jedis.mocked.unified; import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.equalTo; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; import java.util.Arrays; import java.util.HashSet; import java.util.List; import java.util.Set; import org.junit.jupiter.api.Test; import redis.clients.jedis.params.ScanParams; import redis.clients.jedis.resps.ScanResult; public class UnifiedJedisSetCommandsTest extends UnifiedJedisMockedTestBase { @Test public void testSadd() { String key = "setKey"; String[] members = { "member1", "member2" }; long expectedAdded = 2L; // Assuming both members were added when(commandObjects.sadd(key, members)).thenReturn(longCommandObject); when(commandExecutor.executeCommand(longCommandObject)).thenReturn(expectedAdded); long result = jedis.sadd(key, members); assertThat(result, equalTo(expectedAdded)); verify(commandExecutor).executeCommand(longCommandObject); verify(commandObjects).sadd(key, members); } @Test public void testSaddBinary() { byte[] key = "setKey".getBytes(); byte[][] members = { "member1".getBytes(), "member2".getBytes() }; long expectedAdded = 2L; // Assuming both members were added when(commandObjects.sadd(key, members)).thenReturn(longCommandObject); when(commandExecutor.executeCommand(longCommandObject)).thenReturn(expectedAdded); long result = jedis.sadd(key, members); assertThat(result, equalTo(expectedAdded)); verify(commandExecutor).executeCommand(longCommandObject); verify(commandObjects).sadd(key, members); } @Test public void testScard() { String key = "setKey"; long expectedCardinality = 3L; // Assuming the set has 3 members when(commandObjects.scard(key)).thenReturn(longCommandObject); when(commandExecutor.executeCommand(longCommandObject)).thenReturn(expectedCardinality); long result = jedis.scard(key); assertThat(result, equalTo(expectedCardinality)); verify(commandExecutor).executeCommand(longCommandObject); verify(commandObjects).scard(key); } @Test public void testScardBinary() { byte[] key = "setKey".getBytes(); long expectedCardinality = 3L; // Assuming the set has 3 members when(commandObjects.scard(key)).thenReturn(longCommandObject); when(commandExecutor.executeCommand(longCommandObject)).thenReturn(expectedCardinality); long result = jedis.scard(key); assertThat(result, equalTo(expectedCardinality)); verify(commandExecutor).executeCommand(longCommandObject); verify(commandObjects).scard(key); } @Test public void testSdiff() { String[] keys = { "setKey1", "setKey2" }; Set expectedDifference = new HashSet<>(Arrays.asList("member1", "member3")); // Assuming these members are in setKey1 but not in setKey2 when(commandObjects.sdiff(keys)).thenReturn(setStringCommandObject); when(commandExecutor.executeCommand(setStringCommandObject)).thenReturn(expectedDifference); Set result = jedis.sdiff(keys); assertThat(result, equalTo(expectedDifference)); verify(commandExecutor).executeCommand(setStringCommandObject); verify(commandObjects).sdiff(keys); } @Test public void testSdiffBinary() { byte[][] keys = { "setKey1".getBytes(), "setKey2".getBytes() }; Set expectedDifference = new HashSet<>(Arrays.asList("member1".getBytes(), "member3".getBytes())); // Assuming these members are in setKey1 but not in setKey2 when(commandObjects.sdiff(keys)).thenReturn(setBytesCommandObject); when(commandExecutor.executeCommand(setBytesCommandObject)).thenReturn(expectedDifference); Set result = jedis.sdiff(keys); assertThat(result, equalTo(expectedDifference)); verify(commandExecutor).executeCommand(setBytesCommandObject); verify(commandObjects).sdiff(keys); } @Test public void testSdiffstore() { String dstkey = "destinationKey"; String[] keys = { "setKey1", "setKey2" }; long expectedStored = 2L; // Assuming two members were stored in the destination set when(commandObjects.sdiffstore(dstkey, keys)).thenReturn(longCommandObject); when(commandExecutor.executeCommand(longCommandObject)).thenReturn(expectedStored); long result = jedis.sdiffstore(dstkey, keys); assertThat(result, equalTo(expectedStored)); verify(commandExecutor).executeCommand(longCommandObject); verify(commandObjects).sdiffstore(dstkey, keys); } @Test public void testSdiffstoreBinary() { byte[] dstkey = "destinationKey".getBytes(); byte[][] keys = { "setKey1".getBytes(), "setKey2".getBytes() }; long expectedStored = 2L; // Assuming two members were stored in the destination set when(commandObjects.sdiffstore(dstkey, keys)).thenReturn(longCommandObject); when(commandExecutor.executeCommand(longCommandObject)).thenReturn(expectedStored); long result = jedis.sdiffstore(dstkey, keys); assertThat(result, equalTo(expectedStored)); verify(commandExecutor).executeCommand(longCommandObject); verify(commandObjects).sdiffstore(dstkey, keys); } @Test public void testSinter() { String[] keys = { "setKey1", "setKey2" }; Set expectedIntersection = new HashSet<>(Arrays.asList("member2", "member4")); // Assuming these members are common to setKey1 and setKey2 when(commandObjects.sinter(keys)).thenReturn(setStringCommandObject); when(commandExecutor.executeCommand(setStringCommandObject)).thenReturn(expectedIntersection); Set result = jedis.sinter(keys); assertThat(result, equalTo(expectedIntersection)); verify(commandExecutor).executeCommand(setStringCommandObject); verify(commandObjects).sinter(keys); } @Test public void testSinterBinary() { byte[][] keys = { "setKey1".getBytes(), "setKey2".getBytes() }; Set expectedIntersection = new HashSet<>(Arrays.asList("member2".getBytes(), "member4".getBytes())); // Assuming these members are common to setKey1 and setKey2 when(commandObjects.sinter(keys)).thenReturn(setBytesCommandObject); when(commandExecutor.executeCommand(setBytesCommandObject)).thenReturn(expectedIntersection); Set result = jedis.sinter(keys); assertThat(result, equalTo(expectedIntersection)); verify(commandExecutor).executeCommand(setBytesCommandObject); verify(commandObjects).sinter(keys); } @Test public void testSintercard() { String[] keys = { "setKey1", "setKey2" }; long expectedCardinality = 2L; // Assuming there are two common members when(commandObjects.sintercard(keys)).thenReturn(longCommandObject); when(commandExecutor.executeCommand(longCommandObject)).thenReturn(expectedCardinality); long result = jedis.sintercard(keys); assertThat(result, equalTo(expectedCardinality)); verify(commandExecutor).executeCommand(longCommandObject); verify(commandObjects).sintercard(keys); } @Test public void testSintercardBinary() { byte[][] keys = { "setKey1".getBytes(), "setKey2".getBytes() }; long expectedCardinality = 2L; // Assuming there are two common members when(commandObjects.sintercard(keys)).thenReturn(longCommandObject); when(commandExecutor.executeCommand(longCommandObject)).thenReturn(expectedCardinality); long result = jedis.sintercard(keys); assertThat(result, equalTo(expectedCardinality)); verify(commandExecutor).executeCommand(longCommandObject); verify(commandObjects).sintercard(keys); } @Test public void testSintercardWithLimit() { int limit = 1; String[] keys = { "setKey1", "setKey2" }; long expectedCardinality = 1L; // Assuming the limit is set to 1 and there is at least one common member when(commandObjects.sintercard(limit, keys)).thenReturn(longCommandObject); when(commandExecutor.executeCommand(longCommandObject)).thenReturn(expectedCardinality); long result = jedis.sintercard(limit, keys); assertThat(result, equalTo(expectedCardinality)); verify(commandExecutor).executeCommand(longCommandObject); verify(commandObjects).sintercard(limit, keys); } @Test public void testSintercardWithLimitBinary() { int limit = 1; byte[][] keys = { "setKey1".getBytes(), "setKey2".getBytes() }; long expectedCardinality = 1L; // Assuming the limit is set to 1 and there is at least one common member when(commandObjects.sintercard(limit, keys)).thenReturn(longCommandObject); when(commandExecutor.executeCommand(longCommandObject)).thenReturn(expectedCardinality); long result = jedis.sintercard(limit, keys); assertThat(result, equalTo(expectedCardinality)); verify(commandExecutor).executeCommand(longCommandObject); verify(commandObjects).sintercard(limit, keys); } @Test public void testSinterstore() { String dstkey = "destinationKey"; String[] keys = { "setKey1", "setKey2" }; long expectedStored = 2L; // Assuming two members were stored in the destination set when(commandObjects.sinterstore(dstkey, keys)).thenReturn(longCommandObject); when(commandExecutor.executeCommand(longCommandObject)).thenReturn(expectedStored); long result = jedis.sinterstore(dstkey, keys); assertThat(result, equalTo(expectedStored)); verify(commandExecutor).executeCommand(longCommandObject); verify(commandObjects).sinterstore(dstkey, keys); } @Test public void testSinterstoreBinary() { byte[] dstkey = "destinationKey".getBytes(); byte[][] keys = { "setKey1".getBytes(), "setKey2".getBytes() }; long expectedStored = 2L; // Assuming two members were stored in the destination set when(commandObjects.sinterstore(dstkey, keys)).thenReturn(longCommandObject); when(commandExecutor.executeCommand(longCommandObject)).thenReturn(expectedStored); long result = jedis.sinterstore(dstkey, keys); assertThat(result, equalTo(expectedStored)); verify(commandExecutor).executeCommand(longCommandObject); verify(commandObjects).sinterstore(dstkey, keys); } @Test public void testSismember() { String key = "setKey"; String member = "member1"; boolean expectedIsMember = true; // Assuming the member is part of the set when(commandObjects.sismember(key, member)).thenReturn(booleanCommandObject); when(commandExecutor.executeCommand(booleanCommandObject)).thenReturn(expectedIsMember); boolean result = jedis.sismember(key, member); assertThat(result, equalTo(expectedIsMember)); verify(commandExecutor).executeCommand(booleanCommandObject); verify(commandObjects).sismember(key, member); } @Test public void testSismemberBinary() { byte[] key = "setKey".getBytes(); byte[] member = "member1".getBytes(); boolean expectedIsMember = true; // Assuming the member is part of the set when(commandObjects.sismember(key, member)).thenReturn(booleanCommandObject); when(commandExecutor.executeCommand(booleanCommandObject)).thenReturn(expectedIsMember); boolean result = jedis.sismember(key, member); assertThat(result, equalTo(expectedIsMember)); verify(commandExecutor).executeCommand(booleanCommandObject); verify(commandObjects).sismember(key, member); } @Test public void testSmembers() { String key = "setKey"; Set expectedMembers = new HashSet<>(Arrays.asList("member1", "member2")); when(commandObjects.smembers(key)).thenReturn(setStringCommandObject); when(commandExecutor.executeCommand(setStringCommandObject)).thenReturn(expectedMembers); Set result = jedis.smembers(key); assertThat(result, equalTo(expectedMembers)); verify(commandExecutor).executeCommand(setStringCommandObject); verify(commandObjects).smembers(key); } @Test public void testSmembersBinary() { byte[] key = "setKey".getBytes(); Set expectedMembers = new HashSet<>(Arrays.asList("member1".getBytes(), "member2".getBytes())); when(commandObjects.smembers(key)).thenReturn(setBytesCommandObject); when(commandExecutor.executeCommand(setBytesCommandObject)).thenReturn(expectedMembers); Set result = jedis.smembers(key); assertThat(result, equalTo(expectedMembers)); verify(commandExecutor).executeCommand(setBytesCommandObject); verify(commandObjects).smembers(key); } @Test public void testSmismember() { String key = "setKey"; String[] members = { "member1", "member2", "member3" }; List expectedMembership = Arrays.asList(true, false, true); // Assuming the first and last members are part of the set when(commandObjects.smismember(key, members)).thenReturn(listBooleanCommandObject); when(commandExecutor.executeCommand(listBooleanCommandObject)).thenReturn(expectedMembership); List result = jedis.smismember(key, members); assertThat(result, equalTo(expectedMembership)); verify(commandExecutor).executeCommand(listBooleanCommandObject); verify(commandObjects).smismember(key, members); } @Test public void testSmismemberBinary() { byte[] key = "setKey".getBytes(); byte[][] members = { "member1".getBytes(), "member2".getBytes(), "member3".getBytes() }; List expectedMembership = Arrays.asList(true, false, true); // Assuming the first and last members are part of the set when(commandObjects.smismember(key, members)).thenReturn(listBooleanCommandObject); when(commandExecutor.executeCommand(listBooleanCommandObject)).thenReturn(expectedMembership); List result = jedis.smismember(key, members); assertThat(result, equalTo(expectedMembership)); verify(commandExecutor).executeCommand(listBooleanCommandObject); verify(commandObjects).smismember(key, members); } @Test public void testSmove() { String srckey = "sourceKey"; String dstkey = "destinationKey"; String member = "member1"; long expectedMoved = 1L; // Assuming the member was successfully moved when(commandObjects.smove(srckey, dstkey, member)).thenReturn(longCommandObject); when(commandExecutor.executeCommand(longCommandObject)).thenReturn(expectedMoved); long result = jedis.smove(srckey, dstkey, member); assertThat(result, equalTo(expectedMoved)); verify(commandExecutor).executeCommand(longCommandObject); verify(commandObjects).smove(srckey, dstkey, member); } @Test public void testSmoveBinary() { byte[] srckey = "sourceKey".getBytes(); byte[] dstkey = "destinationKey".getBytes(); byte[] member = "member1".getBytes(); long expectedMoved = 1L; // Assuming the member was successfully moved when(commandObjects.smove(srckey, dstkey, member)).thenReturn(longCommandObject); when(commandExecutor.executeCommand(longCommandObject)).thenReturn(expectedMoved); long result = jedis.smove(srckey, dstkey, member); assertThat(result, equalTo(expectedMoved)); verify(commandExecutor).executeCommand(longCommandObject); verify(commandObjects).smove(srckey, dstkey, member); } @Test public void testSpop() { String key = "setKey"; String expectedPopped = "member1"; // Assuming "member1" was popped when(commandObjects.spop(key)).thenReturn(stringCommandObject); when(commandExecutor.executeCommand(stringCommandObject)).thenReturn(expectedPopped); String result = jedis.spop(key); assertThat(result, equalTo(expectedPopped)); verify(commandExecutor).executeCommand(stringCommandObject); verify(commandObjects).spop(key); } @Test public void testSpopBinary() { byte[] key = "setKey".getBytes(); byte[] expectedPopped = "member1".getBytes(); // Assuming "member1" was popped when(commandObjects.spop(key)).thenReturn(bytesCommandObject); when(commandExecutor.executeCommand(bytesCommandObject)).thenReturn(expectedPopped); byte[] result = jedis.spop(key); assertThat(result, equalTo(expectedPopped)); verify(commandExecutor).executeCommand(bytesCommandObject); verify(commandObjects).spop(key); } @Test public void testSpopCount() { String key = "setKey"; long count = 2; Set expectedPopped = new HashSet<>(Arrays.asList("member1", "member2")); // Assuming these members were popped when(commandObjects.spop(key, count)).thenReturn(setStringCommandObject); when(commandExecutor.executeCommand(setStringCommandObject)).thenReturn(expectedPopped); Set result = jedis.spop(key, count); assertThat(result, equalTo(expectedPopped)); verify(commandExecutor).executeCommand(setStringCommandObject); verify(commandObjects).spop(key, count); } @Test public void testSpopCountBinary() { byte[] key = "setKey".getBytes(); long count = 2; Set expectedPopped = new HashSet<>(Arrays.asList("member1".getBytes(), "member2".getBytes())); // Assuming these members were popped when(commandObjects.spop(key, count)).thenReturn(setBytesCommandObject); when(commandExecutor.executeCommand(setBytesCommandObject)).thenReturn(expectedPopped); Set result = jedis.spop(key, count); assertThat(result, equalTo(expectedPopped)); verify(commandExecutor).executeCommand(setBytesCommandObject); verify(commandObjects).spop(key, count); } @Test public void testSrandmember() { String key = "setKey"; String expectedRandomMember = "member1"; // Assuming "member1" is randomly selected when(commandObjects.srandmember(key)).thenReturn(stringCommandObject); when(commandExecutor.executeCommand(stringCommandObject)).thenReturn(expectedRandomMember); String result = jedis.srandmember(key); assertThat(result, equalTo(expectedRandomMember)); verify(commandExecutor).executeCommand(stringCommandObject); verify(commandObjects).srandmember(key); } @Test public void testSrandmemberBinary() { byte[] key = "setKey".getBytes(); byte[] expectedRandomMember = "member1".getBytes(); // Assuming "member1" is randomly selected when(commandObjects.srandmember(key)).thenReturn(bytesCommandObject); when(commandExecutor.executeCommand(bytesCommandObject)).thenReturn(expectedRandomMember); byte[] result = jedis.srandmember(key); assertThat(result, equalTo(expectedRandomMember)); verify(commandExecutor).executeCommand(bytesCommandObject); verify(commandObjects).srandmember(key); } @Test public void testSrandmemberCount() { String key = "setKey"; int count = 2; List expectedRandomMembers = Arrays.asList("member1", "member2"); // Assuming these members are randomly selected when(commandObjects.srandmember(key, count)).thenReturn(listStringCommandObject); when(commandExecutor.executeCommand(listStringCommandObject)).thenReturn(expectedRandomMembers); List result = jedis.srandmember(key, count); assertThat(result, equalTo(expectedRandomMembers)); verify(commandExecutor).executeCommand(listStringCommandObject); verify(commandObjects).srandmember(key, count); } @Test public void testSrandmemberCountBinary() { byte[] key = "setKey".getBytes(); int count = 2; List expectedRandomMembers = Arrays.asList("member1".getBytes(), "member2".getBytes()); // Assuming these members are randomly selected when(commandObjects.srandmember(key, count)).thenReturn(listBytesCommandObject); when(commandExecutor.executeCommand(listBytesCommandObject)).thenReturn(expectedRandomMembers); List result = jedis.srandmember(key, count); assertThat(result, equalTo(expectedRandomMembers)); verify(commandExecutor).executeCommand(listBytesCommandObject); verify(commandObjects).srandmember(key, count); } @Test public void testSrem() { String key = "setKey"; String[] members = { "member1", "member2" }; long expectedRemoved = 2L; // Assuming both members were removed when(commandObjects.srem(key, members)).thenReturn(longCommandObject); when(commandExecutor.executeCommand(longCommandObject)).thenReturn(expectedRemoved); long result = jedis.srem(key, members); assertThat(result, equalTo(expectedRemoved)); verify(commandExecutor).executeCommand(longCommandObject); verify(commandObjects).srem(key, members); } @Test public void testSremBinary() { byte[] key = "setKey".getBytes(); byte[][] members = { "member1".getBytes(), "member2".getBytes() }; long expectedRemoved = 2L; // Assuming both members were removed when(commandObjects.srem(key, members)).thenReturn(longCommandObject); when(commandExecutor.executeCommand(longCommandObject)).thenReturn(expectedRemoved); long result = jedis.srem(key, members); assertThat(result, equalTo(expectedRemoved)); verify(commandExecutor).executeCommand(longCommandObject); verify(commandObjects).srem(key, members); } @Test public void testSscan() { String key = "setKey"; String cursor = "0"; ScanParams params = new ScanParams().match("*").count(10); List scanResultData = Arrays.asList("member1", "member2", "member3"); ScanResult expectedScanResult = new ScanResult<>(cursor, scanResultData); when(commandObjects.sscan(key, cursor, params)).thenReturn(scanResultStringCommandObject); when(commandExecutor.executeCommand(scanResultStringCommandObject)).thenReturn(expectedScanResult); ScanResult result = jedis.sscan(key, cursor, params); assertThat(result, equalTo(expectedScanResult)); verify(commandExecutor).executeCommand(scanResultStringCommandObject); verify(commandObjects).sscan(key, cursor, params); } @Test public void testSscanBinary() { byte[] key = "setKey".getBytes(); byte[] cursor = ScanParams.SCAN_POINTER_START_BINARY; ScanParams params = new ScanParams().match("*".getBytes()).count(10); List scanResultData = Arrays.asList("member1".getBytes(), "member2".getBytes(), "member3".getBytes()); ScanResult expectedScanResult = new ScanResult<>(cursor, scanResultData); when(commandObjects.sscan(key, cursor, params)).thenReturn(scanResultBytesCommandObject); when(commandExecutor.executeCommand(scanResultBytesCommandObject)).thenReturn(expectedScanResult); ScanResult result = jedis.sscan(key, cursor, params); assertThat(result, equalTo(expectedScanResult)); verify(commandExecutor).executeCommand(scanResultBytesCommandObject); verify(commandObjects).sscan(key, cursor, params); } @Test public void testSunion() { String[] keys = { "setKey1", "setKey2" }; Set expectedUnion = new HashSet<>(Arrays.asList("member1", "member2", "member3", "member4")); // Assuming these members are in either setKey1 or setKey2 when(commandObjects.sunion(keys)).thenReturn(setStringCommandObject); when(commandExecutor.executeCommand(setStringCommandObject)).thenReturn(expectedUnion); Set result = jedis.sunion(keys); assertThat(result, equalTo(expectedUnion)); verify(commandExecutor).executeCommand(setStringCommandObject); verify(commandObjects).sunion(keys); } @Test public void testSunionBinary() { byte[][] keys = { "setKey1".getBytes(), "setKey2".getBytes() }; Set expectedUnion = new HashSet<>(Arrays.asList("member1".getBytes(), "member2".getBytes(), "member3".getBytes(), "member4".getBytes())); // Assuming these members are in either setKey1 or setKey2 when(commandObjects.sunion(keys)).thenReturn(setBytesCommandObject); when(commandExecutor.executeCommand(setBytesCommandObject)).thenReturn(expectedUnion); Set result = jedis.sunion(keys); assertThat(result, equalTo(expectedUnion)); verify(commandExecutor).executeCommand(setBytesCommandObject); verify(commandObjects).sunion(keys); } @Test public void testSunionstore() { String dstkey = "destinationKey"; String[] keys = { "setKey1", "setKey2" }; long expectedStored = 4L; // Assuming four unique members were stored in the destination set when(commandObjects.sunionstore(dstkey, keys)).thenReturn(longCommandObject); when(commandExecutor.executeCommand(longCommandObject)).thenReturn(expectedStored); long result = jedis.sunionstore(dstkey, keys); assertThat(result, equalTo(expectedStored)); verify(commandExecutor).executeCommand(longCommandObject); verify(commandObjects).sunionstore(dstkey, keys); } @Test public void testSunionstoreBinary() { byte[] dstkey = "destinationKey".getBytes(); byte[][] keys = { "setKey1".getBytes(), "setKey2".getBytes() }; long expectedStored = 4L; // Assuming four unique members were stored in the destination set when(commandObjects.sunionstore(dstkey, keys)).thenReturn(longCommandObject); when(commandExecutor.executeCommand(longCommandObject)).thenReturn(expectedStored); long result = jedis.sunionstore(dstkey, keys); assertThat(result, equalTo(expectedStored)); verify(commandExecutor).executeCommand(longCommandObject); verify(commandObjects).sunionstore(dstkey, keys); } } ================================================ FILE: src/test/java/redis/clients/jedis/mocked/unified/UnifiedJedisSortedSetCommandsTest.java ================================================ package redis.clients.jedis.mocked.unified; import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.equalTo; import static org.junit.jupiter.api.Assertions.assertArrayEquals; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; import java.util.Arrays; import java.util.Collections; import java.util.HashMap; import java.util.List; import java.util.Map; import org.junit.jupiter.api.Test; import redis.clients.jedis.args.SortedSetOption; import redis.clients.jedis.params.ScanParams; import redis.clients.jedis.params.ZAddParams; import redis.clients.jedis.params.ZIncrByParams; import redis.clients.jedis.params.ZParams; import redis.clients.jedis.params.ZRangeParams; import redis.clients.jedis.resps.ScanResult; import redis.clients.jedis.resps.Tuple; import redis.clients.jedis.util.KeyValue; public class UnifiedJedisSortedSetCommandsTest extends UnifiedJedisMockedTestBase { @Test public void testBzmpop() { double timeout = 2.0; SortedSetOption option = SortedSetOption.MAX; String[] keys = { "zset1", "zset2" }; KeyValue> expectedPopResult = new KeyValue<>("zset1", Collections.singletonList(new Tuple("member1", 1.0))); when(commandObjects.bzmpop(timeout, option, keys)).thenReturn(keyValueStringListTupleCommandObject); when(commandExecutor.executeCommand(keyValueStringListTupleCommandObject)).thenReturn(expectedPopResult); KeyValue> result = jedis.bzmpop(timeout, option, keys); assertThat(result, equalTo(expectedPopResult)); verify(commandExecutor).executeCommand(keyValueStringListTupleCommandObject); verify(commandObjects).bzmpop(timeout, option, keys); } @Test public void testBzmpopBinary() { double timeout = 2.0; SortedSetOption option = SortedSetOption.MAX; byte[][] keys = { "zset1".getBytes(), "zset2".getBytes() }; KeyValue> expectedPopResult = new KeyValue<>("zset1".getBytes(), Collections.singletonList(new Tuple("member1", 1.0))); when(commandObjects.bzmpop(timeout, option, keys)).thenReturn(keyValueBytesListTupleCommandObject); when(commandExecutor.executeCommand(keyValueBytesListTupleCommandObject)).thenReturn(expectedPopResult); KeyValue> result = jedis.bzmpop(timeout, option, keys); assertThat(result, equalTo(expectedPopResult)); verify(commandExecutor).executeCommand(keyValueBytesListTupleCommandObject); verify(commandObjects).bzmpop(timeout, option, keys); } @Test public void testBzmpopWithCount() { double timeout = 2.0; SortedSetOption option = SortedSetOption.MAX; int count = 2; String[] keys = { "zset1", "zset2" }; KeyValue> expectedPopResult = new KeyValue<>("zset1", Arrays.asList(new Tuple("member1", 1.0), new Tuple("member2", 2.0))); when(commandObjects.bzmpop(timeout, option, count, keys)).thenReturn(keyValueStringListTupleCommandObject); when(commandExecutor.executeCommand(keyValueStringListTupleCommandObject)).thenReturn(expectedPopResult); KeyValue> result = jedis.bzmpop(timeout, option, count, keys); assertThat(result, equalTo(expectedPopResult)); verify(commandExecutor).executeCommand(keyValueStringListTupleCommandObject); verify(commandObjects).bzmpop(timeout, option, count, keys); } @Test public void testBzmpopWithCountBinary() { double timeout = 2.0; SortedSetOption option = SortedSetOption.MAX; int count = 2; byte[][] keys = { "zset1".getBytes(), "zset2".getBytes() }; KeyValue> expectedPopResult = new KeyValue<>("zset1".getBytes(), Arrays.asList(new Tuple("member1", 1.0), new Tuple("member2", 2.0))); when(commandObjects.bzmpop(timeout, option, count, keys)).thenReturn(keyValueBytesListTupleCommandObject); when(commandExecutor.executeCommand(keyValueBytesListTupleCommandObject)).thenReturn(expectedPopResult); KeyValue> result = jedis.bzmpop(timeout, option, count, keys); assertThat(result, equalTo(expectedPopResult)); verify(commandExecutor).executeCommand(keyValueBytesListTupleCommandObject); verify(commandObjects).bzmpop(timeout, option, count, keys); } @Test public void testBzpopmax() { double timeout = 2.0; String[] keys = { "zset1", "zset2" }; Tuple expectedTuple = new Tuple("member1", 1.0); KeyValue expectedKeyValue = new KeyValue<>("zset1", expectedTuple); when(commandObjects.bzpopmax(timeout, keys)).thenReturn(keyValueStringTupleCommandObject); when(commandExecutor.executeCommand(keyValueStringTupleCommandObject)).thenReturn(expectedKeyValue); KeyValue result = jedis.bzpopmax(timeout, keys); assertThat(result, equalTo(expectedKeyValue)); verify(commandExecutor).executeCommand(keyValueStringTupleCommandObject); verify(commandObjects).bzpopmax(timeout, keys); } @Test public void testBzpopmaxBinary() { double timeout = 2.0; byte[][] keys = { "zset1".getBytes(), "zset2".getBytes() }; Tuple expectedTuple = new Tuple("member1".getBytes(), 1.0); KeyValue expectedKeyValue = new KeyValue<>("zset1".getBytes(), expectedTuple); when(commandObjects.bzpopmax(timeout, keys)).thenReturn(keyValueBytesTupleCommandObject); when(commandExecutor.executeCommand(keyValueBytesTupleCommandObject)).thenReturn(expectedKeyValue); KeyValue result = jedis.bzpopmax(timeout, keys); assertThat(result, equalTo(expectedKeyValue)); verify(commandExecutor).executeCommand(keyValueBytesTupleCommandObject); verify(commandObjects).bzpopmax(timeout, keys); } @Test public void testBzpopmin() { double timeout = 2.0; String[] keys = { "zset1", "zset2" }; Tuple expectedTuple = new Tuple("member1", 1.0); KeyValue expectedKeyValue = new KeyValue<>("zset1", expectedTuple); when(commandObjects.bzpopmin(timeout, keys)).thenReturn(keyValueStringTupleCommandObject); when(commandExecutor.executeCommand(keyValueStringTupleCommandObject)).thenReturn(expectedKeyValue); KeyValue result = jedis.bzpopmin(timeout, keys); assertThat(result, equalTo(expectedKeyValue)); verify(commandExecutor).executeCommand(keyValueStringTupleCommandObject); verify(commandObjects).bzpopmin(timeout, keys); } @Test public void testBzpopminBinary() { double timeout = 2.0; byte[][] keys = { "zset1".getBytes(), "zset2".getBytes() }; Tuple expectedTuple = new Tuple("member1".getBytes(), 1.0); KeyValue expectedKeyValue = new KeyValue<>("zset1".getBytes(), expectedTuple); when(commandObjects.bzpopmin(timeout, keys)).thenReturn(keyValueBytesTupleCommandObject); when(commandExecutor.executeCommand(keyValueBytesTupleCommandObject)).thenReturn(expectedKeyValue); KeyValue result = jedis.bzpopmin(timeout, keys); assertThat(result, equalTo(expectedKeyValue)); verify(commandExecutor).executeCommand(keyValueBytesTupleCommandObject); verify(commandObjects).bzpopmin(timeout, keys); } @Test public void testZadd() { String key = "zsetKey"; double score = 1.0; String member = "member1"; long expectedAdded = 1L; // Assuming the member was successfully added when(commandObjects.zadd(key, score, member)).thenReturn(longCommandObject); when(commandExecutor.executeCommand(longCommandObject)).thenReturn(expectedAdded); long result = jedis.zadd(key, score, member); assertThat(result, equalTo(expectedAdded)); verify(commandExecutor).executeCommand(longCommandObject); verify(commandObjects).zadd(key, score, member); } @Test public void testZaddBinary() { byte[] key = "zsetKey".getBytes(); double score = 1.0; byte[] member = "member1".getBytes(); long expectedAdded = 1L; // Assuming the member was successfully added when(commandObjects.zadd(key, score, member)).thenReturn(longCommandObject); when(commandExecutor.executeCommand(longCommandObject)).thenReturn(expectedAdded); long result = jedis.zadd(key, score, member); assertThat(result, equalTo(expectedAdded)); verify(commandExecutor).executeCommand(longCommandObject); verify(commandObjects).zadd(key, score, member); } @Test public void testZaddWithParams() { String key = "zsetKey"; double score = 1.0; String member = "member1"; ZAddParams params = ZAddParams.zAddParams().nx(); long expectedAdded = 1L; // Assuming the member was successfully added with NX flag when(commandObjects.zadd(key, score, member, params)).thenReturn(longCommandObject); when(commandExecutor.executeCommand(longCommandObject)).thenReturn(expectedAdded); long result = jedis.zadd(key, score, member, params); assertThat(result, equalTo(expectedAdded)); verify(commandExecutor).executeCommand(longCommandObject); verify(commandObjects).zadd(key, score, member, params); } @Test public void testZaddWithParamsBinary() { byte[] key = "zsetKey".getBytes(); double score = 1.0; byte[] member = "member1".getBytes(); ZAddParams params = ZAddParams.zAddParams().nx(); long expectedAdded = 1L; // Assuming the member was successfully added with NX flag when(commandObjects.zadd(key, score, member, params)).thenReturn(longCommandObject); when(commandExecutor.executeCommand(longCommandObject)).thenReturn(expectedAdded); long result = jedis.zadd(key, score, member, params); assertThat(result, equalTo(expectedAdded)); verify(commandExecutor).executeCommand(longCommandObject); verify(commandObjects).zadd(key, score, member, params); } @Test public void testZaddMultiple() { String key = "zsetKey"; Map scoreMembers = new HashMap<>(); scoreMembers.put("member1", 1.0); scoreMembers.put("member2", 2.0); long expectedAdded = 2L; // Assuming both members were successfully added when(commandObjects.zadd(key, scoreMembers)).thenReturn(longCommandObject); when(commandExecutor.executeCommand(longCommandObject)).thenReturn(expectedAdded); long result = jedis.zadd(key, scoreMembers); assertThat(result, equalTo(expectedAdded)); verify(commandExecutor).executeCommand(longCommandObject); verify(commandObjects).zadd(key, scoreMembers); } @Test public void testZaddMultipleBinary() { byte[] key = "zsetKey".getBytes(); Map scoreMembers = new HashMap<>(); scoreMembers.put("member1".getBytes(), 1.0); scoreMembers.put("member2".getBytes(), 2.0); long expectedAdded = 2L; // Assuming both members were successfully added when(commandObjects.zadd(key, scoreMembers)).thenReturn(longCommandObject); when(commandExecutor.executeCommand(longCommandObject)).thenReturn(expectedAdded); long result = jedis.zadd(key, scoreMembers); assertThat(result, equalTo(expectedAdded)); verify(commandExecutor).executeCommand(longCommandObject); verify(commandObjects).zadd(key, scoreMembers); } @Test public void testZaddMultipleWithParams() { String key = "zsetKey"; Map scoreMembers = new HashMap<>(); scoreMembers.put("member1", 1.0); scoreMembers.put("member2", 2.0); ZAddParams params = ZAddParams.zAddParams().xx(); long expectedAdded = 2L; // Assuming both members were successfully added with XX flag when(commandObjects.zadd(key, scoreMembers, params)).thenReturn(longCommandObject); when(commandExecutor.executeCommand(longCommandObject)).thenReturn(expectedAdded); long result = jedis.zadd(key, scoreMembers, params); assertThat(result, equalTo(expectedAdded)); verify(commandExecutor).executeCommand(longCommandObject); verify(commandObjects).zadd(key, scoreMembers, params); } @Test public void testZaddMultipleWithParamsBinary() { byte[] key = "zsetKey".getBytes(); Map scoreMembers = new HashMap<>(); scoreMembers.put("member1".getBytes(), 1.0); scoreMembers.put("member2".getBytes(), 2.0); ZAddParams params = ZAddParams.zAddParams().xx(); long expectedAdded = 2L; // Assuming both members were successfully added with XX flag when(commandObjects.zadd(key, scoreMembers, params)).thenReturn(longCommandObject); when(commandExecutor.executeCommand(longCommandObject)).thenReturn(expectedAdded); long result = jedis.zadd(key, scoreMembers, params); assertThat(result, equalTo(expectedAdded)); verify(commandExecutor).executeCommand(longCommandObject); verify(commandObjects).zadd(key, scoreMembers, params); } @Test public void testZaddIncr() { String key = "zsetKey"; double score = 1.0; String member = "member1"; ZAddParams params = ZAddParams.zAddParams().ch(); Double expectedNewScore = 2.0; // Assuming the member's score was incremented to 2.0 when(commandObjects.zaddIncr(key, score, member, params)).thenReturn(doubleCommandObject); when(commandExecutor.executeCommand(doubleCommandObject)).thenReturn(expectedNewScore); Double result = jedis.zaddIncr(key, score, member, params); assertThat(result, equalTo(expectedNewScore)); verify(commandExecutor).executeCommand(doubleCommandObject); verify(commandObjects).zaddIncr(key, score, member, params); } @Test public void testZaddIncrBinary() { byte[] key = "zsetKey".getBytes(); double score = 1.0; byte[] member = "member1".getBytes(); ZAddParams params = ZAddParams.zAddParams().ch(); Double expectedNewScore = 2.0; // Assuming the member's score was incremented to 2.0 when(commandObjects.zaddIncr(key, score, member, params)).thenReturn(doubleCommandObject); when(commandExecutor.executeCommand(doubleCommandObject)).thenReturn(expectedNewScore); Double result = jedis.zaddIncr(key, score, member, params); assertThat(result, equalTo(expectedNewScore)); verify(commandExecutor).executeCommand(doubleCommandObject); verify(commandObjects).zaddIncr(key, score, member, params); } @Test public void testZcard() { String key = "zsetKey"; long expectedCardinality = 5L; // Assuming the sorted set has 5 members when(commandObjects.zcard(key)).thenReturn(longCommandObject); when(commandExecutor.executeCommand(longCommandObject)).thenReturn(expectedCardinality); long result = jedis.zcard(key); assertThat(result, equalTo(expectedCardinality)); verify(commandExecutor).executeCommand(longCommandObject); verify(commandObjects).zcard(key); } @Test public void testZcardBinary() { byte[] key = "zsetKey".getBytes(); long expectedCardinality = 5L; // Assuming the sorted set has 5 members when(commandObjects.zcard(key)).thenReturn(longCommandObject); when(commandExecutor.executeCommand(longCommandObject)).thenReturn(expectedCardinality); long result = jedis.zcard(key); assertThat(result, equalTo(expectedCardinality)); verify(commandExecutor).executeCommand(longCommandObject); verify(commandObjects).zcard(key); } @Test public void testZcount() { String key = "zsetKey"; String min = "1"; String max = "2"; long expectedCount = 3L; // Assuming there are 3 members within the score range when(commandObjects.zcount(key, min, max)).thenReturn(longCommandObject); when(commandExecutor.executeCommand(longCommandObject)).thenReturn(expectedCount); long result = jedis.zcount(key, min, max); assertThat(result, equalTo(expectedCount)); verify(commandExecutor).executeCommand(longCommandObject); verify(commandObjects).zcount(key, min, max); } @Test public void testZcountBinary() { byte[] key = "zsetKey".getBytes(); byte[] min = "1".getBytes(); byte[] max = "2".getBytes(); long expectedCount = 3L; // Assuming there are 3 members within the score range when(commandObjects.zcount(key, min, max)).thenReturn(longCommandObject); when(commandExecutor.executeCommand(longCommandObject)).thenReturn(expectedCount); long result = jedis.zcount(key, min, max); assertThat(result, equalTo(expectedCount)); verify(commandExecutor).executeCommand(longCommandObject); verify(commandObjects).zcount(key, min, max); } @Test public void testZcountDouble() { String key = "zsetKey"; double min = 1.0; double max = 2.0; long expectedCount = 3L; // Assuming there are 3 members within the score range when(commandObjects.zcount(key, min, max)).thenReturn(longCommandObject); when(commandExecutor.executeCommand(longCommandObject)).thenReturn(expectedCount); long result = jedis.zcount(key, min, max); assertThat(result, equalTo(expectedCount)); verify(commandExecutor).executeCommand(longCommandObject); verify(commandObjects).zcount(key, min, max); } @Test public void testZcountDoubleBinary() { byte[] key = "zsetKey".getBytes(); double min = 1.0; double max = 2.0; long expectedCount = 3L; // Assuming there are 3 members within the score range when(commandObjects.zcount(key, min, max)).thenReturn(longCommandObject); when(commandExecutor.executeCommand(longCommandObject)).thenReturn(expectedCount); long result = jedis.zcount(key, min, max); assertThat(result, equalTo(expectedCount)); verify(commandExecutor).executeCommand(longCommandObject); verify(commandObjects).zcount(key, min, max); } @Test public void testZdiff() { String[] keys = { "zset1", "zset2", "zset3" }; List expectedDifference = Arrays.asList("member1", "member3"); when(commandObjects.zdiff(keys)).thenReturn(listStringCommandObject); when(commandExecutor.executeCommand(listStringCommandObject)).thenReturn(expectedDifference); List result = jedis.zdiff(keys); assertThat(result, equalTo(expectedDifference)); verify(commandExecutor).executeCommand(listStringCommandObject); verify(commandObjects).zdiff(keys); } @Test public void testZdiffBinary() { byte[][] keys = { "zset1".getBytes(), "zset2".getBytes(), "zset3".getBytes() }; List expectedDifference = Arrays.asList("member1".getBytes(), "member3".getBytes()); when(commandObjects.zdiff(keys)).thenReturn(listBytesCommandObject); when(commandExecutor.executeCommand(listBytesCommandObject)).thenReturn(expectedDifference); List result = jedis.zdiff(keys); assertThat(result, equalTo(expectedDifference)); verify(commandExecutor).executeCommand(listBytesCommandObject); verify(commandObjects).zdiff(keys); } @Test public void testZdiffWithScores() { String[] keys = { "zset1", "zset2", "zset3" }; List expectedDifferenceWithScores = Arrays.asList( new Tuple("member1", 1.0), new Tuple("member3", 3.0) ); when(commandObjects.zdiffWithScores(keys)).thenReturn(listTupleCommandObject); when(commandExecutor.executeCommand(listTupleCommandObject)).thenReturn(expectedDifferenceWithScores); List result = jedis.zdiffWithScores(keys); assertThat(result, equalTo(expectedDifferenceWithScores)); verify(commandExecutor).executeCommand(listTupleCommandObject); verify(commandObjects).zdiffWithScores(keys); } @Test public void testZdiffWithScoresBinary() { byte[][] keys = { "zset1".getBytes(), "zset2".getBytes(), "zset3".getBytes() }; List expectedDifferenceWithScores = Arrays.asList( new Tuple("member1".getBytes(), 1.0), new Tuple("member3".getBytes(), 3.0) ); when(commandObjects.zdiffWithScores(keys)).thenReturn(listTupleCommandObject); when(commandExecutor.executeCommand(listTupleCommandObject)).thenReturn(expectedDifferenceWithScores); List result = jedis.zdiffWithScores(keys); assertThat(result, equalTo(expectedDifferenceWithScores)); verify(commandExecutor).executeCommand(listTupleCommandObject); verify(commandObjects).zdiffWithScores(keys); } @Test public void testZdiffStore() { String dstkey = "zsetDiff"; String[] keys = { "zset1", "zset2", "zset3" }; long expectedStoredCount = 2L; // Assuming 2 elements were stored when(commandObjects.zdiffStore(dstkey, keys)).thenReturn(longCommandObject); when(commandExecutor.executeCommand(longCommandObject)).thenReturn(expectedStoredCount); long result = jedis.zdiffStore(dstkey, keys); assertThat(result, equalTo(expectedStoredCount)); verify(commandExecutor).executeCommand(longCommandObject); verify(commandObjects).zdiffStore(dstkey, keys); } @Test public void testZdiffStoreBinary() { byte[] dstkey = "zsetDiff".getBytes(); byte[][] keys = { "zset1".getBytes(), "zset2".getBytes(), "zset3".getBytes() }; long expectedStoredCount = 2L; // Assuming 2 elements were stored when(commandObjects.zdiffStore(dstkey, keys)).thenReturn(longCommandObject); when(commandExecutor.executeCommand(longCommandObject)).thenReturn(expectedStoredCount); long result = jedis.zdiffStore(dstkey, keys); assertThat(result, equalTo(expectedStoredCount)); verify(commandExecutor).executeCommand(longCommandObject); verify(commandObjects).zdiffStore(dstkey, keys); } @Test public void testZdiffstore() { String dstkey = "zsetDiff"; String[] keys = { "zset1", "zset2", "zset3" }; long expectedStoredCount = 2L; // Assuming 2 elements were stored when(commandObjects.zdiffstore(dstkey, keys)).thenReturn(longCommandObject); when(commandExecutor.executeCommand(longCommandObject)).thenReturn(expectedStoredCount); long result = jedis.zdiffstore(dstkey, keys); assertThat(result, equalTo(expectedStoredCount)); verify(commandExecutor).executeCommand(longCommandObject); verify(commandObjects).zdiffstore(dstkey, keys); } @Test public void testZdiffstoreBinary() { byte[] dstkey = "zsetDiff".getBytes(); byte[][] keys = { "zset1".getBytes(), "zset2".getBytes(), "zset3".getBytes() }; long expectedStoredCount = 2L; // Assuming 2 elements were stored when(commandObjects.zdiffstore(dstkey, keys)).thenReturn(longCommandObject); when(commandExecutor.executeCommand(longCommandObject)).thenReturn(expectedStoredCount); long result = jedis.zdiffstore(dstkey, keys); assertThat(result, equalTo(expectedStoredCount)); verify(commandExecutor).executeCommand(longCommandObject); verify(commandObjects).zdiffstore(dstkey, keys); } @Test public void testZincrby() { String key = "zsetKey"; double increment = 2.0; String member = "member1"; double expectedScore = 3.0; // Assuming the member's score was incremented to 3.0 when(commandObjects.zincrby(key, increment, member)).thenReturn(doubleCommandObject); when(commandExecutor.executeCommand(doubleCommandObject)).thenReturn(expectedScore); double result = jedis.zincrby(key, increment, member); assertThat(result, equalTo(expectedScore)); verify(commandExecutor).executeCommand(doubleCommandObject); verify(commandObjects).zincrby(key, increment, member); } @Test public void testZincrbyBinary() { byte[] key = "zsetKey".getBytes(); double increment = 2.0; byte[] member = "member1".getBytes(); double expectedScore = 3.0; // Assuming the member's score was incremented to 3.0 when(commandObjects.zincrby(key, increment, member)).thenReturn(doubleCommandObject); when(commandExecutor.executeCommand(doubleCommandObject)).thenReturn(expectedScore); double result = jedis.zincrby(key, increment, member); assertThat(result, equalTo(expectedScore)); verify(commandExecutor).executeCommand(doubleCommandObject); verify(commandObjects).zincrby(key, increment, member); } @Test public void testZincrbyWithParams() { String key = "zsetKey"; double increment = 1.5; String member = "member1"; ZIncrByParams params = ZIncrByParams.zIncrByParams().xx(); Double expectedNewScore = 4.5; // Assuming the member's score was incremented to 4.5 with XX flag when(commandObjects.zincrby(key, increment, member, params)).thenReturn(doubleCommandObject); when(commandExecutor.executeCommand(doubleCommandObject)).thenReturn(expectedNewScore); Double result = jedis.zincrby(key, increment, member, params); assertThat(result, equalTo(expectedNewScore)); verify(commandExecutor).executeCommand(doubleCommandObject); verify(commandObjects).zincrby(key, increment, member, params); } @Test public void testZincrbyWithParamsBinary() { byte[] key = "zsetKey".getBytes(); double increment = 1.5; byte[] member = "member1".getBytes(); ZIncrByParams params = ZIncrByParams.zIncrByParams().xx(); Double expectedNewScore = 4.5; // Assuming the member's score was incremented to 4.5 with XX flag when(commandObjects.zincrby(key, increment, member, params)).thenReturn(doubleCommandObject); when(commandExecutor.executeCommand(doubleCommandObject)).thenReturn(expectedNewScore); Double result = jedis.zincrby(key, increment, member, params); assertThat(result, equalTo(expectedNewScore)); verify(commandExecutor).executeCommand(doubleCommandObject); verify(commandObjects).zincrby(key, increment, member, params); } @Test public void testZinter() { ZParams params = new ZParams().weights(2, 3).aggregate(ZParams.Aggregate.SUM); String[] keys = { "zset1", "zset2" }; List expectedIntersection = Arrays.asList("member1", "member2"); when(commandObjects.zinter(params, keys)).thenReturn(listStringCommandObject); when(commandExecutor.executeCommand(listStringCommandObject)).thenReturn(expectedIntersection); List result = jedis.zinter(params, keys); assertThat(result, equalTo(expectedIntersection)); verify(commandExecutor).executeCommand(listStringCommandObject); verify(commandObjects).zinter(params, keys); } @Test public void testZinterBinary() { ZParams params = new ZParams().weights(1, 2).aggregate(ZParams.Aggregate.MAX); byte[][] keys = { "zset1".getBytes(), "zset2".getBytes() }; List expectedIntersection = Arrays.asList("member1".getBytes(), "member2".getBytes()); when(commandObjects.zinter(params, keys)).thenReturn(listBytesCommandObject); when(commandExecutor.executeCommand(listBytesCommandObject)).thenReturn(expectedIntersection); List result = jedis.zinter(params, keys); assertThat(result, equalTo(expectedIntersection)); verify(commandExecutor).executeCommand(listBytesCommandObject); verify(commandObjects).zinter(params, keys); } @Test public void testZinterWithScores() { ZParams params = new ZParams().weights(2, 3).aggregate(ZParams.Aggregate.SUM); String[] keys = { "zset1", "zset2" }; List expectedIntersectionWithScores = Arrays.asList( new Tuple("member1", 5.0), new Tuple("member2", 9.0) ); when(commandObjects.zinterWithScores(params, keys)).thenReturn(listTupleCommandObject); when(commandExecutor.executeCommand(listTupleCommandObject)).thenReturn(expectedIntersectionWithScores); List result = jedis.zinterWithScores(params, keys); assertThat(result, equalTo(expectedIntersectionWithScores)); verify(commandExecutor).executeCommand(listTupleCommandObject); verify(commandObjects).zinterWithScores(params, keys); } @Test public void testZinterWithScoresBinary() { ZParams params = new ZParams().weights(1, 2).aggregate(ZParams.Aggregate.MAX); byte[][] keys = { "zset1".getBytes(), "zset2".getBytes() }; List expectedIntersectionWithScores = Arrays.asList(new Tuple("member1", 1.0), new Tuple("member2", 2.0)); when(commandObjects.zinterWithScores(params, keys)).thenReturn(listTupleCommandObject); when(commandExecutor.executeCommand(listTupleCommandObject)).thenReturn(expectedIntersectionWithScores); List result = jedis.zinterWithScores(params, keys); assertThat(result, equalTo(expectedIntersectionWithScores)); verify(commandExecutor).executeCommand(listTupleCommandObject); verify(commandObjects).zinterWithScores(params, keys); } @Test public void testZintercard() { String[] keys = { "zset1", "zset2" }; long expectedCardinality = 2L; when(commandObjects.zintercard(keys)).thenReturn(longCommandObject); when(commandExecutor.executeCommand(longCommandObject)).thenReturn(expectedCardinality); long result = jedis.zintercard(keys); assertThat(result, equalTo(expectedCardinality)); verify(commandExecutor).executeCommand(longCommandObject); verify(commandObjects).zintercard(keys); } @Test public void testZintercardBinary() { byte[][] keys = { "zset1".getBytes(), "zset2".getBytes() }; long expectedCardinality = 2L; // Assuming the cardinality of the intersection is 2 when(commandObjects.zintercard(keys)).thenReturn(longCommandObject); when(commandExecutor.executeCommand(longCommandObject)).thenReturn(expectedCardinality); long result = jedis.zintercard(keys); assertThat(result, equalTo(expectedCardinality)); verify(commandExecutor).executeCommand(longCommandObject); verify(commandObjects).zintercard(keys); } @Test public void testZintercardWithLimit() { String[] keys = { "zset1", "zset2" }; long limit = 1000L; long expectedCardinality = 2L; when(commandObjects.zintercard(limit, keys)).thenReturn(longCommandObject); when(commandExecutor.executeCommand(longCommandObject)).thenReturn(expectedCardinality); long result = jedis.zintercard(limit, keys); assertThat(result, equalTo(expectedCardinality)); verify(commandExecutor).executeCommand(longCommandObject); verify(commandObjects).zintercard(limit, keys); } @Test public void testZintercardWithLimitBinary() { byte[][] keys = { "zset1".getBytes(), "zset2".getBytes() }; long limit = 1000L; long expectedIntersectionCardinality = 5L; when(commandObjects.zintercard(limit, keys)).thenReturn(longCommandObject); when(commandExecutor.executeCommand(longCommandObject)).thenReturn(expectedIntersectionCardinality); long result = jedis.zintercard(limit, keys); assertThat(result, equalTo(expectedIntersectionCardinality)); verify(commandExecutor).executeCommand(longCommandObject); verify(commandObjects).zintercard(limit, keys); } @Test public void testZinterstore() { String dstkey = "zsetInter"; String[] sets = { "zset1", "zset2" }; long expectedStoredCount = 3L; // Assuming 3 elements were stored when(commandObjects.zinterstore(dstkey, sets)).thenReturn(longCommandObject); when(commandExecutor.executeCommand(longCommandObject)).thenReturn(expectedStoredCount); long result = jedis.zinterstore(dstkey, sets); assertThat(result, equalTo(expectedStoredCount)); verify(commandExecutor).executeCommand(longCommandObject); verify(commandObjects).zinterstore(dstkey, sets); } @Test public void testZinterstoreBinary() { byte[] dstkey = "zsetInter".getBytes(); byte[][] sets = { "zset1".getBytes(), "zset2".getBytes() }; long expectedStoredCount = 3L; // Assuming 3 elements were stored when(commandObjects.zinterstore(dstkey, sets)).thenReturn(longCommandObject); when(commandExecutor.executeCommand(longCommandObject)).thenReturn(expectedStoredCount); long result = jedis.zinterstore(dstkey, sets); assertThat(result, equalTo(expectedStoredCount)); verify(commandExecutor).executeCommand(longCommandObject); verify(commandObjects).zinterstore(dstkey, sets); } @Test public void testZinterstoreWithParams() { String dstkey = "zsetInter"; ZParams params = new ZParams().weights(2, 3).aggregate(ZParams.Aggregate.SUM); String[] sets = { "zset1", "zset2" }; long expectedStoredCount = 3L; // Assuming 3 elements were stored with the specified weights and aggregation when(commandObjects.zinterstore(dstkey, params, sets)).thenReturn(longCommandObject); when(commandExecutor.executeCommand(longCommandObject)).thenReturn(expectedStoredCount); long result = jedis.zinterstore(dstkey, params, sets); assertThat(result, equalTo(expectedStoredCount)); verify(commandExecutor).executeCommand(longCommandObject); verify(commandObjects).zinterstore(dstkey, params, sets); } @Test public void testZinterstoreWithParamsBinary() { byte[] dstkey = "zsetInter".getBytes(); ZParams params = new ZParams().weights(2, 3).aggregate(ZParams.Aggregate.SUM); byte[][] sets = { "zset1".getBytes(), "zset2".getBytes() }; long expectedStoredCount = 3L; // Assuming 3 elements were stored with the specified weights and aggregation when(commandObjects.zinterstore(dstkey, params, sets)).thenReturn(longCommandObject); when(commandExecutor.executeCommand(longCommandObject)).thenReturn(expectedStoredCount); long result = jedis.zinterstore(dstkey, params, sets); assertThat(result, equalTo(expectedStoredCount)); verify(commandExecutor).executeCommand(longCommandObject); verify(commandObjects).zinterstore(dstkey, params, sets); } @Test public void testZlexcount() { String key = "zsetKey"; String min = "[a"; String max = "(b"; long expectedCount = 5L; // Assuming there are 5 elements in the lex range when(commandObjects.zlexcount(key, min, max)).thenReturn(longCommandObject); when(commandExecutor.executeCommand(longCommandObject)).thenReturn(expectedCount); long result = jedis.zlexcount(key, min, max); assertThat(result, equalTo(expectedCount)); verify(commandExecutor).executeCommand(longCommandObject); verify(commandObjects).zlexcount(key, min, max); } @Test public void testZlexcountBinary() { byte[] key = "zsetKey".getBytes(); byte[] min = "[a".getBytes(); byte[] max = "[b".getBytes(); long expectedCount = 5L; // Assuming there are 5 elements in the lex range when(commandObjects.zlexcount(key, min, max)).thenReturn(longCommandObject); when(commandExecutor.executeCommand(longCommandObject)).thenReturn(expectedCount); long result = jedis.zlexcount(key, min, max); assertThat(result, equalTo(expectedCount)); verify(commandExecutor).executeCommand(longCommandObject); verify(commandObjects).zlexcount(key, min, max); } @Test public void testZmpop() { SortedSetOption option = SortedSetOption.MAX; String[] keys = { "zset1", "zset2" }; KeyValue> expectedPopResult = new KeyValue<>("zset1", Collections.singletonList(new Tuple("member1", 1.0))); when(commandObjects.zmpop(option, keys)).thenReturn(keyValueStringListTupleCommandObject); when(commandExecutor.executeCommand(keyValueStringListTupleCommandObject)).thenReturn(expectedPopResult); KeyValue> result = jedis.zmpop(option, keys); assertThat(result, equalTo(expectedPopResult)); verify(commandExecutor).executeCommand(keyValueStringListTupleCommandObject); verify(commandObjects).zmpop(option, keys); } @Test public void testZmpopBinary() { SortedSetOption option = SortedSetOption.MAX; byte[][] keys = { "zset1".getBytes(), "zset2".getBytes() }; KeyValue> expectedPopResult = new KeyValue<>("zset1".getBytes(), Collections.singletonList(new Tuple("member1", 1.0))); when(commandObjects.zmpop(option, keys)).thenReturn(keyValueBytesListTupleCommandObject); when(commandExecutor.executeCommand(keyValueBytesListTupleCommandObject)).thenReturn(expectedPopResult); KeyValue> result = jedis.zmpop(option, keys); assertThat(result, equalTo(expectedPopResult)); verify(commandExecutor).executeCommand(keyValueBytesListTupleCommandObject); verify(commandObjects).zmpop(option, keys); } @Test public void testZmpopWithCount() { SortedSetOption option = SortedSetOption.MAX; int count = 2; String[] keys = { "zset1", "zset2" }; KeyValue> expectedPopResult = new KeyValue<>("zset1", Arrays.asList(new Tuple("member1", 1.0), new Tuple("member2", 2.0))); when(commandObjects.zmpop(option, count, keys)).thenReturn(keyValueStringListTupleCommandObject); when(commandExecutor.executeCommand(keyValueStringListTupleCommandObject)).thenReturn(expectedPopResult); KeyValue> result = jedis.zmpop(option, count, keys); assertThat(result, equalTo(expectedPopResult)); verify(commandExecutor).executeCommand(keyValueStringListTupleCommandObject); verify(commandObjects).zmpop(option, count, keys); } @Test public void testZmpopWithCountBinary() { SortedSetOption option = SortedSetOption.MAX; int count = 2; byte[][] keys = { "zset1".getBytes(), "zset2".getBytes() }; KeyValue> expectedPopResult = new KeyValue<>("zset1".getBytes(), Arrays.asList(new Tuple("member1", 1.0), new Tuple("member2", 2.0))); when(commandObjects.zmpop(option, count, keys)).thenReturn(keyValueBytesListTupleCommandObject); when(commandExecutor.executeCommand(keyValueBytesListTupleCommandObject)).thenReturn(expectedPopResult); KeyValue> result = jedis.zmpop(option, count, keys); assertThat(result, equalTo(expectedPopResult)); verify(commandExecutor).executeCommand(keyValueBytesListTupleCommandObject); verify(commandObjects).zmpop(option, count, keys); } @Test public void testZmscore() { String key = "zsetKey"; String[] members = { "member1", "member2" }; List expectedScores = Arrays.asList(1.0, 2.0); // Assuming the members have scores of 1.0 and 2.0 respectively when(commandObjects.zmscore(key, members)).thenReturn(listDoubleCommandObject); when(commandExecutor.executeCommand(listDoubleCommandObject)).thenReturn(expectedScores); List result = jedis.zmscore(key, members); assertThat(result, equalTo(expectedScores)); verify(commandExecutor).executeCommand(listDoubleCommandObject); verify(commandObjects).zmscore(key, members); } @Test public void testZmscoreBinary() { byte[] key = "zsetKey".getBytes(); byte[][] members = { "member1".getBytes(), "member2".getBytes() }; List expectedScores = Arrays.asList(1.0, 2.0); // Assuming the members have scores of 1.0 and 2.0 respectively when(commandObjects.zmscore(key, members)).thenReturn(listDoubleCommandObject); when(commandExecutor.executeCommand(listDoubleCommandObject)).thenReturn(expectedScores); List result = jedis.zmscore(key, members); assertThat(result, equalTo(expectedScores)); verify(commandExecutor).executeCommand(listDoubleCommandObject); verify(commandObjects).zmscore(key, members); } @Test public void testZpopmax() { String key = "zsetKey"; Tuple expectedTuple = new Tuple("member1", 2.0); // Assuming this member has the highest score when(commandObjects.zpopmax(key)).thenReturn(tupleCommandObject); when(commandExecutor.executeCommand(tupleCommandObject)).thenReturn(expectedTuple); Tuple result = jedis.zpopmax(key); assertThat(result, equalTo(expectedTuple)); verify(commandExecutor).executeCommand(tupleCommandObject); verify(commandObjects).zpopmax(key); } @Test public void testZpopmaxBinary() { byte[] key = "zsetKey".getBytes(); Tuple expectedTuple = new Tuple("member1".getBytes(), 2.0); // Assuming this member has the highest score when(commandObjects.zpopmax(key)).thenReturn(tupleCommandObject); when(commandExecutor.executeCommand(tupleCommandObject)).thenReturn(expectedTuple); Tuple result = jedis.zpopmax(key); assertThat(result, equalTo(expectedTuple)); verify(commandExecutor).executeCommand(tupleCommandObject); verify(commandObjects).zpopmax(key); } @Test public void testZpopmaxWithCount() { String key = "zsetKey"; int count = 2; List expectedTuples = Arrays.asList( new Tuple("member1", 2.0), new Tuple("member2", 1.5) ); // Assuming these members have the highest scores when(commandObjects.zpopmax(key, count)).thenReturn(listTupleCommandObject); when(commandExecutor.executeCommand(listTupleCommandObject)).thenReturn(expectedTuples); List result = jedis.zpopmax(key, count); assertThat(result, equalTo(expectedTuples)); verify(commandExecutor).executeCommand(listTupleCommandObject); verify(commandObjects).zpopmax(key, count); } @Test public void testZpopmaxWithCountBinary() { byte[] key = "zsetKey".getBytes(); int count = 2; List expectedTuples = Arrays.asList( new Tuple("member1".getBytes(), 2.0), new Tuple("member2".getBytes(), 1.5) ); // Assuming these members have the highest scores when(commandObjects.zpopmax(key, count)).thenReturn(listTupleCommandObject); when(commandExecutor.executeCommand(listTupleCommandObject)).thenReturn(expectedTuples); List result = jedis.zpopmax(key, count); assertThat(result, equalTo(expectedTuples)); verify(commandExecutor).executeCommand(listTupleCommandObject); verify(commandObjects).zpopmax(key, count); } @Test public void testZpopmin() { String key = "zsetKey"; Tuple expectedTuple = new Tuple("member1", 1.0); // Assuming this member has the lowest score when(commandObjects.zpopmin(key)).thenReturn(tupleCommandObject); when(commandExecutor.executeCommand(tupleCommandObject)).thenReturn(expectedTuple); Tuple result = jedis.zpopmin(key); assertThat(result, equalTo(expectedTuple)); verify(commandExecutor).executeCommand(tupleCommandObject); verify(commandObjects).zpopmin(key); } @Test public void testZpopminBinary() { byte[] key = "zsetKey".getBytes(); Tuple expectedTuple = new Tuple("member1".getBytes(), 1.0); // Assuming this member has the lowest score when(commandObjects.zpopmin(key)).thenReturn(tupleCommandObject); when(commandExecutor.executeCommand(tupleCommandObject)).thenReturn(expectedTuple); Tuple result = jedis.zpopmin(key); assertThat(result, equalTo(expectedTuple)); verify(commandExecutor).executeCommand(tupleCommandObject); verify(commandObjects).zpopmin(key); } @Test public void testZpopminWithCount() { String key = "zsetKey"; int count = 2; List expectedTuples = Arrays.asList( new Tuple("member1", 1.0), new Tuple("member2", 1.5) ); // Assuming these members have the lowest scores when(commandObjects.zpopmin(key, count)).thenReturn(listTupleCommandObject); when(commandExecutor.executeCommand(listTupleCommandObject)).thenReturn(expectedTuples); List result = jedis.zpopmin(key, count); assertThat(result, equalTo(expectedTuples)); verify(commandExecutor).executeCommand(listTupleCommandObject); verify(commandObjects).zpopmin(key, count); } @Test public void testZpopminWithCountBinary() { byte[] key = "zsetKey".getBytes(); int count = 2; List expectedTuples = Arrays.asList( new Tuple("member1".getBytes(), 1.0), new Tuple("member2".getBytes(), 1.5) ); // Assuming these members have the lowest scores when(commandObjects.zpopmin(key, count)).thenReturn(listTupleCommandObject); when(commandExecutor.executeCommand(listTupleCommandObject)).thenReturn(expectedTuples); List result = jedis.zpopmin(key, count); assertThat(result, equalTo(expectedTuples)); verify(commandExecutor).executeCommand(listTupleCommandObject); verify(commandObjects).zpopmin(key, count); } @Test public void testZrandmember() { String key = "zsetKey"; String expectedMember = "member1"; // Assuming this member is randomly selected when(commandObjects.zrandmember(key)).thenReturn(stringCommandObject); when(commandExecutor.executeCommand(stringCommandObject)).thenReturn(expectedMember); String result = jedis.zrandmember(key); assertThat(result, equalTo(expectedMember)); verify(commandExecutor).executeCommand(stringCommandObject); verify(commandObjects).zrandmember(key); } @Test public void testZrandmemberBinary() { byte[] key = "zsetKey".getBytes(); byte[] expectedMember = "member1".getBytes(); // Assuming this member is randomly selected when(commandObjects.zrandmember(key)).thenReturn(bytesCommandObject); when(commandExecutor.executeCommand(bytesCommandObject)).thenReturn(expectedMember); byte[] result = jedis.zrandmember(key); assertArrayEquals(expectedMember, result); verify(commandExecutor).executeCommand(bytesCommandObject); verify(commandObjects).zrandmember(key); } @Test public void testZrandmemberWithCount() { String key = "zsetKey"; long count = 2; List expectedMembers = Arrays.asList("member1", "member2"); // Assuming these members are randomly selected when(commandObjects.zrandmember(key, count)).thenReturn(listStringCommandObject); when(commandExecutor.executeCommand(listStringCommandObject)).thenReturn(expectedMembers); List result = jedis.zrandmember(key, count); assertThat(result, equalTo(expectedMembers)); verify(commandExecutor).executeCommand(listStringCommandObject); verify(commandObjects).zrandmember(key, count); } @Test public void testZrandmemberBytesWithCount() { byte[] key = "zsetKey".getBytes(); long count = 2; List expectedMembers = Arrays.asList("member1".getBytes(), "member2".getBytes()); // Assuming these members are randomly selected when(commandObjects.zrandmember(key, count)).thenReturn(listBytesCommandObject); when(commandExecutor.executeCommand(listBytesCommandObject)).thenReturn(expectedMembers); List result = jedis.zrandmember(key, count); for (int i = 0; i < expectedMembers.size(); i++) { assertArrayEquals(expectedMembers.get(i), result.get(i)); } verify(commandExecutor).executeCommand(listBytesCommandObject); verify(commandObjects).zrandmember(key, count); } @Test public void testZrandmemberWithScores() { String key = "zsetKey"; long count = 2; List expectedMembersWithScores = Arrays.asList( new Tuple("member1", 1.0), new Tuple("member2", 2.0) ); // Assuming these members with scores are randomly selected when(commandObjects.zrandmemberWithScores(key, count)).thenReturn(listTupleCommandObject); when(commandExecutor.executeCommand(listTupleCommandObject)).thenReturn(expectedMembersWithScores); List result = jedis.zrandmemberWithScores(key, count); assertThat(result, equalTo(expectedMembersWithScores)); verify(commandExecutor).executeCommand(listTupleCommandObject); verify(commandObjects).zrandmemberWithScores(key, count); } @Test public void testZrandmemberWithScoresBinary() { byte[] key = "zsetKey".getBytes(); long count = 2; List expectedMembersWithScores = Arrays.asList( new Tuple("member1".getBytes(), 1.0), new Tuple("member2".getBytes(), 2.0) ); // Assuming these members with scores are randomly selected when(commandObjects.zrandmemberWithScores(key, count)).thenReturn(listTupleCommandObject); when(commandExecutor.executeCommand(listTupleCommandObject)).thenReturn(expectedMembersWithScores); List result = jedis.zrandmemberWithScores(key, count); assertThat(result, equalTo(expectedMembersWithScores)); verify(commandExecutor).executeCommand(listTupleCommandObject); verify(commandObjects).zrandmemberWithScores(key, count); } @Test public void testZrange() { String key = "zsetKey"; long start = 0; long stop = -1; // This typically means all elements in the sorted set List expectedMembers = Arrays.asList("member1", "member2", "member3"); when(commandObjects.zrange(key, start, stop)).thenReturn(listStringCommandObject); when(commandExecutor.executeCommand(listStringCommandObject)).thenReturn(expectedMembers); List result = jedis.zrange(key, start, stop); assertThat(result, equalTo(expectedMembers)); verify(commandExecutor).executeCommand(listStringCommandObject); verify(commandObjects).zrange(key, start, stop); } @Test public void testZrangeBinary() { byte[] key = "zsetKey".getBytes(); long start = 0; long stop = -1; // This typically means all elements in the sorted set List expectedMembers = Arrays.asList("member1".getBytes(), "member2".getBytes(), "member3".getBytes()); when(commandObjects.zrange(key, start, stop)).thenReturn(listBytesCommandObject); when(commandExecutor.executeCommand(listBytesCommandObject)).thenReturn(expectedMembers); List result = jedis.zrange(key, start, stop); assertThat(result, equalTo(expectedMembers)); verify(commandExecutor).executeCommand(listBytesCommandObject); verify(commandObjects).zrange(key, start, stop); } @Test public void testZrangeWithScores() { String key = "zsetKey"; long start = 0; long stop = -1; // This typically means all elements in the sorted set with their scores List expectedMembersWithScores = Arrays.asList( new Tuple("member1", 1.0), new Tuple("member2", 2.0), new Tuple("member3", 3.0) ); when(commandObjects.zrangeWithScores(key, start, stop)).thenReturn(listTupleCommandObject); when(commandExecutor.executeCommand(listTupleCommandObject)).thenReturn(expectedMembersWithScores); List result = jedis.zrangeWithScores(key, start, stop); assertThat(result, equalTo(expectedMembersWithScores)); verify(commandExecutor).executeCommand(listTupleCommandObject); verify(commandObjects).zrangeWithScores(key, start, stop); } @Test public void testZrangeWithScoresBinary() { byte[] key = "zsetKey".getBytes(); long start = 0; long stop = -1; // This typically means all elements in the sorted set with their scores List expectedMembersWithScores = Arrays.asList( new Tuple("member1".getBytes(), 1.0), new Tuple("member2".getBytes(), 2.0), new Tuple("member3".getBytes(), 3.0) ); when(commandObjects.zrangeWithScores(key, start, stop)).thenReturn(listTupleCommandObject); when(commandExecutor.executeCommand(listTupleCommandObject)).thenReturn(expectedMembersWithScores); List result = jedis.zrangeWithScores(key, start, stop); assertThat(result, equalTo(expectedMembersWithScores)); verify(commandExecutor).executeCommand(listTupleCommandObject); verify(commandObjects).zrangeWithScores(key, start, stop); } @Test public void testZrangeWithZRangeParams() { String key = "zsetKey"; ZRangeParams zRangeParams = ZRangeParams.zrangeParams(1, 3); List expectedMembers = Arrays.asList("member1", "member2"); when(commandObjects.zrange(key, zRangeParams)).thenReturn(listStringCommandObject); when(commandExecutor.executeCommand(listStringCommandObject)).thenReturn(expectedMembers); List result = jedis.zrange(key, zRangeParams); assertThat(result, equalTo(expectedMembers)); verify(commandExecutor).executeCommand(listStringCommandObject); verify(commandObjects).zrange(key, zRangeParams); } @Test public void testZrangeWithZRangeParamsBinary() { byte[] key = "zsetKey".getBytes(); ZRangeParams zRangeParams = ZRangeParams.zrangeParams(1, 3); List expectedMembers = Arrays.asList("member1".getBytes(), "member2".getBytes()); when(commandObjects.zrange(key, zRangeParams)).thenReturn(listBytesCommandObject); when(commandExecutor.executeCommand(listBytesCommandObject)).thenReturn(expectedMembers); List result = jedis.zrange(key, zRangeParams); assertThat(result, equalTo(expectedMembers)); verify(commandExecutor).executeCommand(listBytesCommandObject); verify(commandObjects).zrange(key, zRangeParams); } @Test public void testZrangeWithScoresWithZRangeParams() { String key = "zsetKey"; ZRangeParams zRangeParams = ZRangeParams.zrangeParams(1, 3); List expectedMembersWithScores = Arrays.asList( new Tuple("member1", 1.0), new Tuple("member2", 2.0) ); when(commandObjects.zrangeWithScores(key, zRangeParams)).thenReturn(listTupleCommandObject); when(commandExecutor.executeCommand(listTupleCommandObject)).thenReturn(expectedMembersWithScores); List result = jedis.zrangeWithScores(key, zRangeParams); assertThat(result, equalTo(expectedMembersWithScores)); verify(commandExecutor).executeCommand(listTupleCommandObject); verify(commandObjects).zrangeWithScores(key, zRangeParams); } @Test public void testZrangeWithScoresWithZRangeParamsBinary() { byte[] key = "zsetKey".getBytes(); ZRangeParams zRangeParams = ZRangeParams.zrangeParams(1, 3); List expectedMembersWithScores = Arrays.asList( new Tuple("member1".getBytes(), 1.0), new Tuple("member2".getBytes(), 2.0) ); when(commandObjects.zrangeWithScores(key, zRangeParams)).thenReturn(listTupleCommandObject); when(commandExecutor.executeCommand(listTupleCommandObject)).thenReturn(expectedMembersWithScores); List result = jedis.zrangeWithScores(key, zRangeParams); assertThat(result, equalTo(expectedMembersWithScores)); verify(commandExecutor).executeCommand(listTupleCommandObject); verify(commandObjects).zrangeWithScores(key, zRangeParams); } @Test public void testZrangeByLex() { String key = "zsetKey"; String min = "[a"; String max = "(b"; List expectedMembers = Arrays.asList("alpha", "beta"); when(commandObjects.zrangeByLex(key, min, max)).thenReturn(listStringCommandObject); when(commandExecutor.executeCommand(listStringCommandObject)).thenReturn(expectedMembers); List result = jedis.zrangeByLex(key, min, max); assertThat(result, equalTo(expectedMembers)); verify(commandExecutor).executeCommand(listStringCommandObject); verify(commandObjects).zrangeByLex(key, min, max); } @Test public void testZrangeByLexBinary() { byte[] key = "zsetKey".getBytes(); byte[] min = "[a".getBytes(); byte[] max = "[b".getBytes(); List expectedMembers = Arrays.asList("alpha".getBytes(), "beta".getBytes()); when(commandObjects.zrangeByLex(key, min, max)).thenReturn(listBytesCommandObject); when(commandExecutor.executeCommand(listBytesCommandObject)).thenReturn(expectedMembers); List result = jedis.zrangeByLex(key, min, max); assertThat(result, equalTo(expectedMembers)); verify(commandExecutor).executeCommand(listBytesCommandObject); verify(commandObjects).zrangeByLex(key, min, max); } @Test public void testZrangeByLexWithOffsetCount() { String key = "zsetKey"; String min = "[a"; String max = "(b"; int offset = 1; int count = 2; List expectedMembers = Arrays.asList("beta", "gamma"); when(commandObjects.zrangeByLex(key, min, max, offset, count)).thenReturn(listStringCommandObject); when(commandExecutor.executeCommand(listStringCommandObject)).thenReturn(expectedMembers); List result = jedis.zrangeByLex(key, min, max, offset, count); assertThat(result, equalTo(expectedMembers)); verify(commandExecutor).executeCommand(listStringCommandObject); verify(commandObjects).zrangeByLex(key, min, max, offset, count); } @Test public void testZrangeByLexWithOffsetCountBinary() { byte[] key = "zsetKey".getBytes(); byte[] min = "[a".getBytes(); byte[] max = "[b".getBytes(); int offset = 1; int count = 2; List expectedMembers = Arrays.asList("beta".getBytes(), "gamma".getBytes()); when(commandObjects.zrangeByLex(key, min, max, offset, count)).thenReturn(listBytesCommandObject); when(commandExecutor.executeCommand(listBytesCommandObject)).thenReturn(expectedMembers); List result = jedis.zrangeByLex(key, min, max, offset, count); assertThat(result, equalTo(expectedMembers)); verify(commandExecutor).executeCommand(listBytesCommandObject); verify(commandObjects).zrangeByLex(key, min, max, offset, count); } @Test public void testZrangeByScore() { String key = "zsetKey"; String min = "1"; String max = "3"; List expectedMembers = Arrays.asList("member1", "member2"); when(commandObjects.zrangeByScore(key, min, max)).thenReturn(listStringCommandObject); when(commandExecutor.executeCommand(listStringCommandObject)).thenReturn(expectedMembers); List result = jedis.zrangeByScore(key, min, max); assertThat(result, equalTo(expectedMembers)); verify(commandExecutor).executeCommand(listStringCommandObject); verify(commandObjects).zrangeByScore(key, min, max); } @Test public void testZrangeByScoreBinary() { byte[] key = "zsetKey".getBytes(); byte[] min = "1".getBytes(); byte[] max = "3".getBytes(); List expectedMembers = Arrays.asList("member1".getBytes(), "member2".getBytes()); when(commandObjects.zrangeByScore(key, min, max)).thenReturn(listBytesCommandObject); when(commandExecutor.executeCommand(listBytesCommandObject)).thenReturn(expectedMembers); List result = jedis.zrangeByScore(key, min, max); assertThat(result, equalTo(expectedMembers)); verify(commandExecutor).executeCommand(listBytesCommandObject); verify(commandObjects).zrangeByScore(key, min, max); } @Test public void testZrangeByScoreDouble() { String key = "zsetKey"; double min = 1.0; double max = 3.0; List expectedMembers = Arrays.asList("member1", "member2"); when(commandObjects.zrangeByScore(key, min, max)).thenReturn(listStringCommandObject); when(commandExecutor.executeCommand(listStringCommandObject)).thenReturn(expectedMembers); List result = jedis.zrangeByScore(key, min, max); assertThat(result, equalTo(expectedMembers)); verify(commandExecutor).executeCommand(listStringCommandObject); verify(commandObjects).zrangeByScore(key, min, max); } @Test public void testZrangeByScoreDoubleBinary() { byte[] key = "zsetKey".getBytes(); double min = 1.0; double max = 3.0; List expectedMembers = Arrays.asList("member1".getBytes(), "member2".getBytes()); when(commandObjects.zrangeByScore(key, min, max)).thenReturn(listBytesCommandObject); when(commandExecutor.executeCommand(listBytesCommandObject)).thenReturn(expectedMembers); List result = jedis.zrangeByScore(key, min, max); assertThat(result, equalTo(expectedMembers)); verify(commandExecutor).executeCommand(listBytesCommandObject); verify(commandObjects).zrangeByScore(key, min, max); } @Test public void testZrangeByScoreWithOffsetCount() { String key = "zsetKey"; String min = "1"; String max = "3"; int offset = 1; int count = 2; List expectedMembers = Arrays.asList("member2", "member3"); when(commandObjects.zrangeByScore(key, min, max, offset, count)).thenReturn(listStringCommandObject); when(commandExecutor.executeCommand(listStringCommandObject)).thenReturn(expectedMembers); List result = jedis.zrangeByScore(key, min, max, offset, count); assertThat(result, equalTo(expectedMembers)); verify(commandExecutor).executeCommand(listStringCommandObject); verify(commandObjects).zrangeByScore(key, min, max, offset, count); } @Test public void testZrangeByScoreWithOffsetCountBinary() { byte[] key = "zsetKey".getBytes(); byte[] min = "1".getBytes(); byte[] max = "3".getBytes(); int offset = 1; int count = 2; List expectedMembers = Arrays.asList("member2".getBytes(), "member3".getBytes()); when(commandObjects.zrangeByScore(key, min, max, offset, count)).thenReturn(listBytesCommandObject); when(commandExecutor.executeCommand(listBytesCommandObject)).thenReturn(expectedMembers); List result = jedis.zrangeByScore(key, min, max, offset, count); assertThat(result, equalTo(expectedMembers)); verify(commandExecutor).executeCommand(listBytesCommandObject); verify(commandObjects).zrangeByScore(key, min, max, offset, count); } @Test public void testZrangeByScoreDoubleWithOffsetCount() { String key = "zsetKey"; double min = 1.0; double max = 3.0; int offset = 1; int count = 2; List expectedMembers = Arrays.asList("member2", "member3"); when(commandObjects.zrangeByScore(key, min, max, offset, count)).thenReturn(listStringCommandObject); when(commandExecutor.executeCommand(listStringCommandObject)).thenReturn(expectedMembers); List result = jedis.zrangeByScore(key, min, max, offset, count); assertThat(result, equalTo(expectedMembers)); verify(commandExecutor).executeCommand(listStringCommandObject); verify(commandObjects).zrangeByScore(key, min, max, offset, count); } @Test public void testZrangeByScoreDoubleWithOffsetCountBinary() { byte[] key = "zsetKey".getBytes(); double min = 1.0; double max = 3.0; int offset = 1; int count = 2; List expectedMembers = Arrays.asList("member2".getBytes(), "member3".getBytes()); when(commandObjects.zrangeByScore(key, min, max, offset, count)).thenReturn(listBytesCommandObject); when(commandExecutor.executeCommand(listBytesCommandObject)).thenReturn(expectedMembers); List result = jedis.zrangeByScore(key, min, max, offset, count); assertThat(result, equalTo(expectedMembers)); verify(commandExecutor).executeCommand(listBytesCommandObject); verify(commandObjects).zrangeByScore(key, min, max, offset, count); } @Test public void testZrangeByScoreWithScores() { String key = "zsetKey"; String min = "1"; String max = "3"; List expectedMembersWithScores = Arrays.asList( new Tuple("member1", 1.0), new Tuple("member2", 2.0) ); when(commandObjects.zrangeByScoreWithScores(key, min, max)).thenReturn(listTupleCommandObject); when(commandExecutor.executeCommand(listTupleCommandObject)).thenReturn(expectedMembersWithScores); List result = jedis.zrangeByScoreWithScores(key, min, max); assertThat(result, equalTo(expectedMembersWithScores)); verify(commandExecutor).executeCommand(listTupleCommandObject); verify(commandObjects).zrangeByScoreWithScores(key, min, max); } @Test public void testZrangeByScoreWithScoresBinary() { byte[] key = "zsetKey".getBytes(); byte[] min = "1".getBytes(); byte[] max = "3".getBytes(); List expectedMembersWithScores = Arrays.asList( new Tuple("member1".getBytes(), 1.0), new Tuple("member2".getBytes(), 2.0) ); when(commandObjects.zrangeByScoreWithScores(key, min, max)).thenReturn(listTupleCommandObject); when(commandExecutor.executeCommand(listTupleCommandObject)).thenReturn(expectedMembersWithScores); List result = jedis.zrangeByScoreWithScores(key, min, max); assertThat(result, equalTo(expectedMembersWithScores)); verify(commandExecutor).executeCommand(listTupleCommandObject); verify(commandObjects).zrangeByScoreWithScores(key, min, max); } @Test public void testZrangeByScoreWithScoresDouble() { String key = "zsetKey"; double min = 1.0; double max = 3.0; List expectedMembersWithScores = Arrays.asList( new Tuple("member1", 1.0), new Tuple("member2", 2.0) ); when(commandObjects.zrangeByScoreWithScores(key, min, max)).thenReturn(listTupleCommandObject); when(commandExecutor.executeCommand(listTupleCommandObject)).thenReturn(expectedMembersWithScores); List result = jedis.zrangeByScoreWithScores(key, min, max); assertThat(result, equalTo(expectedMembersWithScores)); verify(commandExecutor).executeCommand(listTupleCommandObject); verify(commandObjects).zrangeByScoreWithScores(key, min, max); } @Test public void testZrangeByScoreWithScoresDoubleBinary() { byte[] key = "zsetKey".getBytes(); double min = 1.0; double max = 3.0; List expectedMembersWithScores = Arrays.asList( new Tuple("member1".getBytes(), 1.0), new Tuple("member2".getBytes(), 2.0) ); when(commandObjects.zrangeByScoreWithScores(key, min, max)).thenReturn(listTupleCommandObject); when(commandExecutor.executeCommand(listTupleCommandObject)).thenReturn(expectedMembersWithScores); List result = jedis.zrangeByScoreWithScores(key, min, max); assertThat(result, equalTo(expectedMembersWithScores)); verify(commandExecutor).executeCommand(listTupleCommandObject); verify(commandObjects).zrangeByScoreWithScores(key, min, max); } @Test public void testZrangeByScoreWithScoresWithOffsetCount() { String key = "zsetKey"; String min = "1"; String max = "3"; int offset = 1; int count = 2; List expectedMembersWithScores = Arrays.asList( new Tuple("member2", 2.0), new Tuple("member3", 3.0) ); when(commandObjects.zrangeByScoreWithScores(key, min, max, offset, count)).thenReturn(listTupleCommandObject); when(commandExecutor.executeCommand(listTupleCommandObject)).thenReturn(expectedMembersWithScores); List result = jedis.zrangeByScoreWithScores(key, min, max, offset, count); assertThat(result, equalTo(expectedMembersWithScores)); verify(commandExecutor).executeCommand(listTupleCommandObject); verify(commandObjects).zrangeByScoreWithScores(key, min, max, offset, count); } @Test public void testZrangeByScoreWithScoresWithOffsetCountBinary() { byte[] key = "zsetKey".getBytes(); byte[] min = "1".getBytes(); byte[] max = "3".getBytes(); int offset = 1; int count = 2; List expectedMembersWithScores = Arrays.asList( new Tuple("member2".getBytes(), 2.0), new Tuple("member3".getBytes(), 3.0) ); when(commandObjects.zrangeByScoreWithScores(key, min, max, offset, count)).thenReturn(listTupleCommandObject); when(commandExecutor.executeCommand(listTupleCommandObject)).thenReturn(expectedMembersWithScores); List result = jedis.zrangeByScoreWithScores(key, min, max, offset, count); assertThat(result, equalTo(expectedMembersWithScores)); verify(commandExecutor).executeCommand(listTupleCommandObject); verify(commandObjects).zrangeByScoreWithScores(key, min, max, offset, count); } @Test public void testZrangeByScoreWithScoresDoubleWithOffsetCount() { String key = "zsetKey"; double min = 1.0; double max = 3.0; int offset = 1; int count = 2; List expectedMembersWithScores = Arrays.asList( new Tuple("member2", 2.0), new Tuple("member3", 3.0) ); when(commandObjects.zrangeByScoreWithScores(key, min, max, offset, count)).thenReturn(listTupleCommandObject); when(commandExecutor.executeCommand(listTupleCommandObject)).thenReturn(expectedMembersWithScores); List result = jedis.zrangeByScoreWithScores(key, min, max, offset, count); assertThat(result, equalTo(expectedMembersWithScores)); verify(commandExecutor).executeCommand(listTupleCommandObject); verify(commandObjects).zrangeByScoreWithScores(key, min, max, offset, count); } @Test public void testZrangeByScoreWithScoresDoubleWithOffsetCountBinary() { byte[] key = "zsetKey".getBytes(); double min = 1.0; double max = 3.0; int offset = 1; int count = 2; List expectedMembersWithScores = Arrays.asList( new Tuple("member2".getBytes(), 2.0), new Tuple("member3".getBytes(), 3.0) ); when(commandObjects.zrangeByScoreWithScores(key, min, max, offset, count)).thenReturn(listTupleCommandObject); when(commandExecutor.executeCommand(listTupleCommandObject)).thenReturn(expectedMembersWithScores); List result = jedis.zrangeByScoreWithScores(key, min, max, offset, count); assertThat(result, equalTo(expectedMembersWithScores)); verify(commandExecutor).executeCommand(listTupleCommandObject); verify(commandObjects).zrangeByScoreWithScores(key, min, max, offset, count); } @Test public void testZrangestore() { String dest = "destinationKey"; String src = "sourceKey"; ZRangeParams zRangeParams = ZRangeParams.zrangeParams(1, 3); long expectedStoredCount = 2L; // Assuming 2 members were within the range and stored when(commandObjects.zrangestore(dest, src, zRangeParams)).thenReturn(longCommandObject); when(commandExecutor.executeCommand(longCommandObject)).thenReturn(expectedStoredCount); long result = jedis.zrangestore(dest, src, zRangeParams); assertThat(result, equalTo(expectedStoredCount)); verify(commandExecutor).executeCommand(longCommandObject); verify(commandObjects).zrangestore(dest, src, zRangeParams); } @Test public void testZrangestoreBinary() { byte[] dest = "destinationKey".getBytes(); byte[] src = "sourceKey".getBytes(); ZRangeParams zRangeParams = ZRangeParams.zrangeParams(1, 3); long expectedStoredCount = 2L; // Assuming 2 members were within the range and stored when(commandObjects.zrangestore(dest, src, zRangeParams)).thenReturn(longCommandObject); when(commandExecutor.executeCommand(longCommandObject)).thenReturn(expectedStoredCount); long result = jedis.zrangestore(dest, src, zRangeParams); assertThat(result, equalTo(expectedStoredCount)); verify(commandExecutor).executeCommand(longCommandObject); verify(commandObjects).zrangestore(dest, src, zRangeParams); } @Test public void testZrank() { String key = "zsetKey"; String member = "member1"; Long expectedRank = 0L; // Assuming the member is the first in the sorted set when(commandObjects.zrank(key, member)).thenReturn(longCommandObject); when(commandExecutor.executeCommand(longCommandObject)).thenReturn(expectedRank); Long result = jedis.zrank(key, member); assertThat(result, equalTo(expectedRank)); verify(commandExecutor).executeCommand(longCommandObject); verify(commandObjects).zrank(key, member); } @Test public void testZrankBinary() { byte[] key = "zsetKey".getBytes(); byte[] member = "member1".getBytes(); Long expectedRank = 0L; // Assuming the member is the first in the sorted set when(commandObjects.zrank(key, member)).thenReturn(longCommandObject); when(commandExecutor.executeCommand(longCommandObject)).thenReturn(expectedRank); Long result = jedis.zrank(key, member); assertThat(result, equalTo(expectedRank)); verify(commandExecutor).executeCommand(longCommandObject); verify(commandObjects).zrank(key, member); } @Test public void testZrankWithScore() { String key = "zsetKey"; String member = "member1"; KeyValue expectedRankWithScore = new KeyValue<>(0L, 1.0); // Assuming the member is the first with a score of 1.0 when(commandObjects.zrankWithScore(key, member)).thenReturn(keyValueLongDoubleCommandObject); when(commandExecutor.executeCommand(keyValueLongDoubleCommandObject)).thenReturn(expectedRankWithScore); KeyValue result = jedis.zrankWithScore(key, member); assertThat(result, equalTo(expectedRankWithScore)); verify(commandExecutor).executeCommand(keyValueLongDoubleCommandObject); verify(commandObjects).zrankWithScore(key, member); } @Test public void testZrankWithScoreBinary() { byte[] key = "zsetKey".getBytes(); byte[] member = "member1".getBytes(); KeyValue expectedRankWithScore = new KeyValue<>(0L, 1.0); // Assuming the member is the first with a score of 1.0 when(commandObjects.zrankWithScore(key, member)).thenReturn(keyValueLongDoubleCommandObject); when(commandExecutor.executeCommand(keyValueLongDoubleCommandObject)).thenReturn(expectedRankWithScore); KeyValue result = jedis.zrankWithScore(key, member); assertThat(result, equalTo(expectedRankWithScore)); verify(commandExecutor).executeCommand(keyValueLongDoubleCommandObject); verify(commandObjects).zrankWithScore(key, member); } @Test public void testZrem() { String key = "zsetKey"; String[] members = { "member1", "member2" }; long expectedRemoved = 2L; // Assuming both members were successfully removed when(commandObjects.zrem(key, members)).thenReturn(longCommandObject); when(commandExecutor.executeCommand(longCommandObject)).thenReturn(expectedRemoved); long result = jedis.zrem(key, members); assertThat(result, equalTo(expectedRemoved)); verify(commandExecutor).executeCommand(longCommandObject); verify(commandObjects).zrem(key, members); } @Test public void testZremBinary() { byte[] key = "zsetKey".getBytes(); byte[][] members = { "member1".getBytes(), "member2".getBytes() }; long expectedRemoved = 2L; // Assuming both members were successfully removed when(commandObjects.zrem(key, members)).thenReturn(longCommandObject); when(commandExecutor.executeCommand(longCommandObject)).thenReturn(expectedRemoved); long result = jedis.zrem(key, members); assertThat(result, equalTo(expectedRemoved)); verify(commandExecutor).executeCommand(longCommandObject); verify(commandObjects).zrem(key, members); } @Test public void testZremrangeByLex() { String key = "zsetKey"; String min = "[a"; String max = "[b"; long expectedRemovals = 2L; // Assuming 2 elements were removed when(commandObjects.zremrangeByLex(key, min, max)).thenReturn(longCommandObject); when(commandExecutor.executeCommand(longCommandObject)).thenReturn(expectedRemovals); long result = jedis.zremrangeByLex(key, min, max); assertThat(result, equalTo(expectedRemovals)); verify(commandExecutor).executeCommand(longCommandObject); verify(commandObjects).zremrangeByLex(key, min, max); } @Test public void testZremrangeByLexBinary() { byte[] key = "zsetKey".getBytes(); byte[] min = "[a".getBytes(); byte[] max = "[b".getBytes(); long expectedRemovals = 2L; // Assuming 2 elements were removed when(commandObjects.zremrangeByLex(key, min, max)).thenReturn(longCommandObject); when(commandExecutor.executeCommand(longCommandObject)).thenReturn(expectedRemovals); long result = jedis.zremrangeByLex(key, min, max); assertThat(result, equalTo(expectedRemovals)); verify(commandExecutor).executeCommand(longCommandObject); verify(commandObjects).zremrangeByLex(key, min, max); } @Test public void testZremrangeByRank() { String key = "zsetKey"; long start = 0; long stop = 2; long expectedRemovals = 3L; // Assuming 3 elements were removed when(commandObjects.zremrangeByRank(key, start, stop)).thenReturn(longCommandObject); when(commandExecutor.executeCommand(longCommandObject)).thenReturn(expectedRemovals); long result = jedis.zremrangeByRank(key, start, stop); assertThat(result, equalTo(expectedRemovals)); verify(commandExecutor).executeCommand(longCommandObject); verify(commandObjects).zremrangeByRank(key, start, stop); } @Test public void testZremrangeByRankBinary() { byte[] key = "zsetKey".getBytes(); long start = 0; long stop = 2; long expectedRemovals = 3L; // Assuming 3 elements were removed when(commandObjects.zremrangeByRank(key, start, stop)).thenReturn(longCommandObject); when(commandExecutor.executeCommand(longCommandObject)).thenReturn(expectedRemovals); long result = jedis.zremrangeByRank(key, start, stop); assertThat(result, equalTo(expectedRemovals)); verify(commandExecutor).executeCommand(longCommandObject); verify(commandObjects).zremrangeByRank(key, start, stop); } @Test public void testZremrangeByScore() { String key = "zsetKey"; String min = "1"; String max = "3"; long expectedRemovals = 2L; // Assuming 2 elements were removed when(commandObjects.zremrangeByScore(key, min, max)).thenReturn(longCommandObject); when(commandExecutor.executeCommand(longCommandObject)).thenReturn(expectedRemovals); long result = jedis.zremrangeByScore(key, min, max); assertThat(result, equalTo(expectedRemovals)); verify(commandExecutor).executeCommand(longCommandObject); verify(commandObjects).zremrangeByScore(key, min, max); } @Test public void testZremrangeByScoreBinary() { byte[] key = "zsetKey".getBytes(); byte[] min = "1".getBytes(); byte[] max = "3".getBytes(); long expectedRemovals = 2L; // Assuming 2 elements were removed when(commandObjects.zremrangeByScore(key, min, max)).thenReturn(longCommandObject); when(commandExecutor.executeCommand(longCommandObject)).thenReturn(expectedRemovals); long result = jedis.zremrangeByScore(key, min, max); assertThat(result, equalTo(expectedRemovals)); verify(commandExecutor).executeCommand(longCommandObject); verify(commandObjects).zremrangeByScore(key, min, max); } @Test public void testZremrangeByScoreDouble() { String key = "zsetKey"; double min = 1.0; double max = 3.0; long expectedRemovals = 2L; // Assuming 2 elements were removed when(commandObjects.zremrangeByScore(key, min, max)).thenReturn(longCommandObject); when(commandExecutor.executeCommand(longCommandObject)).thenReturn(expectedRemovals); long result = jedis.zremrangeByScore(key, min, max); assertThat(result, equalTo(expectedRemovals)); verify(commandExecutor).executeCommand(longCommandObject); verify(commandObjects).zremrangeByScore(key, min, max); } @Test public void testZremrangeByScoreDoubleBinary() { byte[] key = "zsetKey".getBytes(); double min = 1.0; double max = 3.0; long expectedRemovals = 2L; // Assuming 2 elements were removed when(commandObjects.zremrangeByScore(key, min, max)).thenReturn(longCommandObject); when(commandExecutor.executeCommand(longCommandObject)).thenReturn(expectedRemovals); long result = jedis.zremrangeByScore(key, min, max); assertThat(result, equalTo(expectedRemovals)); verify(commandExecutor).executeCommand(longCommandObject); verify(commandObjects).zremrangeByScore(key, min, max); } @Test public void testZrevrange() { String key = "zsetKey"; long start = 0; long stop = -1; // This typically means all elements in the sorted set, in reverse order List expectedMembers = Arrays.asList("member3", "member2", "member1"); when(commandObjects.zrevrange(key, start, stop)).thenReturn(listStringCommandObject); when(commandExecutor.executeCommand(listStringCommandObject)).thenReturn(expectedMembers); List result = jedis.zrevrange(key, start, stop); assertThat(result, equalTo(expectedMembers)); verify(commandExecutor).executeCommand(listStringCommandObject); verify(commandObjects).zrevrange(key, start, stop); } @Test public void testZrevrangeBinary() { byte[] key = "zsetKey".getBytes(); long start = 0; long stop = -1; // This typically means all elements in the sorted set, in reverse order List expectedMembers = Arrays.asList("member3".getBytes(), "member2".getBytes(), "member1".getBytes()); when(commandObjects.zrevrange(key, start, stop)).thenReturn(listBytesCommandObject); when(commandExecutor.executeCommand(listBytesCommandObject)).thenReturn(expectedMembers); List result = jedis.zrevrange(key, start, stop); assertThat(result, equalTo(expectedMembers)); verify(commandExecutor).executeCommand(listBytesCommandObject); verify(commandObjects).zrevrange(key, start, stop); } @Test public void testZrevrangeWithScores() { String key = "zsetKey"; long start = 0; long stop = -1; // This typically means all elements in the sorted set with their scores, in reverse order List expectedMembersWithScores = Arrays.asList( new Tuple("member3", 3.0), new Tuple("member2", 2.0), new Tuple("member1", 1.0) ); when(commandObjects.zrevrangeWithScores(key, start, stop)).thenReturn(listTupleCommandObject); when(commandExecutor.executeCommand(listTupleCommandObject)).thenReturn(expectedMembersWithScores); List result = jedis.zrevrangeWithScores(key, start, stop); assertThat(result, equalTo(expectedMembersWithScores)); verify(commandExecutor).executeCommand(listTupleCommandObject); verify(commandObjects).zrevrangeWithScores(key, start, stop); } @Test public void testZrevrangeWithScoresBinary() { byte[] key = "zsetKey".getBytes(); long start = 0; long stop = -1; // This typically means all elements in the sorted set with their scores, in reverse order List expectedMembersWithScores = Arrays.asList( new Tuple("member3".getBytes(), 3.0), new Tuple("member2".getBytes(), 2.0), new Tuple("member1".getBytes(), 1.0) ); when(commandObjects.zrevrangeWithScores(key, start, stop)).thenReturn(listTupleCommandObject); when(commandExecutor.executeCommand(listTupleCommandObject)).thenReturn(expectedMembersWithScores); List result = jedis.zrevrangeWithScores(key, start, stop); assertThat(result, equalTo(expectedMembersWithScores)); verify(commandExecutor).executeCommand(listTupleCommandObject); verify(commandObjects).zrevrangeWithScores(key, start, stop); } @Test public void testZrevrangeByLex() { String key = "zsetKey"; String max = "[z"; String min = "[a"; List expectedMembers = Arrays.asList("omega", "mu", "alpha"); when(commandObjects.zrevrangeByLex(key, max, min)).thenReturn(listStringCommandObject); when(commandExecutor.executeCommand(listStringCommandObject)).thenReturn(expectedMembers); List result = jedis.zrevrangeByLex(key, max, min); assertThat(result, equalTo(expectedMembers)); verify(commandExecutor).executeCommand(listStringCommandObject); verify(commandObjects).zrevrangeByLex(key, max, min); } @Test public void testZrevrangeByLexBinary() { byte[] key = "zsetKey".getBytes(); byte[] max = "[z".getBytes(); byte[] min = "[a".getBytes(); List expectedMembers = Arrays.asList("omega".getBytes(), "mu".getBytes(), "alpha".getBytes()); when(commandObjects.zrevrangeByLex(key, max, min)).thenReturn(listBytesCommandObject); when(commandExecutor.executeCommand(listBytesCommandObject)).thenReturn(expectedMembers); List result = jedis.zrevrangeByLex(key, max, min); assertThat(result, equalTo(expectedMembers)); verify(commandExecutor).executeCommand(listBytesCommandObject); verify(commandObjects).zrevrangeByLex(key, max, min); } @Test public void testZrevrangeByLexWithOffsetCount() { String key = "zsetKey"; String max = "[z"; String min = "[a"; int offset = 1; int count = 2; List expectedMembers = Arrays.asList("mu", "alpha"); when(commandObjects.zrevrangeByLex(key, max, min, offset, count)).thenReturn(listStringCommandObject); when(commandExecutor.executeCommand(listStringCommandObject)).thenReturn(expectedMembers); List result = jedis.zrevrangeByLex(key, max, min, offset, count); assertThat(result, equalTo(expectedMembers)); verify(commandExecutor).executeCommand(listStringCommandObject); verify(commandObjects).zrevrangeByLex(key, max, min, offset, count); } @Test public void testZrevrangeByLexWithOffsetCountBinary() { byte[] key = "zsetKey".getBytes(); byte[] max = "[z".getBytes(); byte[] min = "[a".getBytes(); int offset = 1; int count = 2; List expectedMembers = Arrays.asList("mu".getBytes(), "alpha".getBytes()); when(commandObjects.zrevrangeByLex(key, max, min, offset, count)).thenReturn(listBytesCommandObject); when(commandExecutor.executeCommand(listBytesCommandObject)).thenReturn(expectedMembers); List result = jedis.zrevrangeByLex(key, max, min, offset, count); assertThat(result, equalTo(expectedMembers)); verify(commandExecutor).executeCommand(listBytesCommandObject); verify(commandObjects).zrevrangeByLex(key, max, min, offset, count); } @Test public void testZrevrangeByScore() { String key = "zsetKey"; String max = "3"; String min = "1"; List expectedMembers = Arrays.asList("member2", "member1"); when(commandObjects.zrevrangeByScore(key, max, min)).thenReturn(listStringCommandObject); when(commandExecutor.executeCommand(listStringCommandObject)).thenReturn(expectedMembers); List result = jedis.zrevrangeByScore(key, max, min); assertThat(result, equalTo(expectedMembers)); verify(commandExecutor).executeCommand(listStringCommandObject); verify(commandObjects).zrevrangeByScore(key, max, min); } @Test public void testZrevrangeByScoreBinary() { byte[] key = "zsetKey".getBytes(); byte[] max = "3".getBytes(); byte[] min = "1".getBytes(); List expectedMembers = Arrays.asList("member2".getBytes(), "member1".getBytes()); when(commandObjects.zrevrangeByScore(key, max, min)).thenReturn(listBytesCommandObject); when(commandExecutor.executeCommand(listBytesCommandObject)).thenReturn(expectedMembers); List result = jedis.zrevrangeByScore(key, max, min); assertThat(result, equalTo(expectedMembers)); verify(commandExecutor).executeCommand(listBytesCommandObject); verify(commandObjects).zrevrangeByScore(key, max, min); } @Test public void testZrevrangeByScoreDouble() { String key = "zsetKey"; double max = 3.0; double min = 1.0; List expectedMembers = Arrays.asList("member2", "member1"); when(commandObjects.zrevrangeByScore(key, max, min)).thenReturn(listStringCommandObject); when(commandExecutor.executeCommand(listStringCommandObject)).thenReturn(expectedMembers); List result = jedis.zrevrangeByScore(key, max, min); assertThat(result, equalTo(expectedMembers)); verify(commandExecutor).executeCommand(listStringCommandObject); verify(commandObjects).zrevrangeByScore(key, max, min); } @Test public void testZrevrangeByScoreDoubleBinary() { byte[] key = "zsetKey".getBytes(); double max = 3.0; double min = 1.0; List expectedMembers = Arrays.asList("member2".getBytes(), "member1".getBytes()); when(commandObjects.zrevrangeByScore(key, max, min)).thenReturn(listBytesCommandObject); when(commandExecutor.executeCommand(listBytesCommandObject)).thenReturn(expectedMembers); List result = jedis.zrevrangeByScore(key, max, min); assertThat(result, equalTo(expectedMembers)); verify(commandExecutor).executeCommand(listBytesCommandObject); verify(commandObjects).zrevrangeByScore(key, max, min); } @Test public void testZrevrangeByScoreDoubleWithLimit() { String key = "zsetKey"; double max = 3.0; double min = 1.0; int offset = 1; int count = 2; List expectedMembers = Arrays.asList("member2", "member1"); when(commandObjects.zrevrangeByScore(key, max, min, offset, count)).thenReturn(listStringCommandObject); when(commandExecutor.executeCommand(listStringCommandObject)).thenReturn(expectedMembers); List result = jedis.zrevrangeByScore(key, max, min, offset, count); assertThat(result, equalTo(expectedMembers)); verify(commandExecutor).executeCommand(listStringCommandObject); verify(commandObjects).zrevrangeByScore(key, max, min, offset, count); } @Test public void testZrevrangeByScoreDoubleWithLimitBinary() { byte[] key = "zsetKey".getBytes(); double max = 3.0; double min = 1.0; int offset = 1; int count = 2; List expectedMembers = Arrays.asList("member2".getBytes(), "member1".getBytes()); when(commandObjects.zrevrangeByScore(key, max, min, offset, count)).thenReturn(listBytesCommandObject); when(commandExecutor.executeCommand(listBytesCommandObject)).thenReturn(expectedMembers); List result = jedis.zrevrangeByScore(key, max, min, offset, count); assertThat(result, equalTo(expectedMembers)); verify(commandExecutor).executeCommand(listBytesCommandObject); verify(commandObjects).zrevrangeByScore(key, max, min, offset, count); } @Test public void testZrevrangeByScoreWithScoresDouble() { String key = "zsetKey"; double max = 3.0; double min = 1.0; List expectedMembersWithScores = Arrays.asList( new Tuple("member2", 2.0), new Tuple("member1", 1.0) ); when(commandObjects.zrevrangeByScoreWithScores(key, max, min)).thenReturn(listTupleCommandObject); when(commandExecutor.executeCommand(listTupleCommandObject)).thenReturn(expectedMembersWithScores); List result = jedis.zrevrangeByScoreWithScores(key, max, min); assertThat(result, equalTo(expectedMembersWithScores)); verify(commandExecutor).executeCommand(listTupleCommandObject); verify(commandObjects).zrevrangeByScoreWithScores(key, max, min); } @Test public void testZrevrangeByScoreWithScoresDoubleBinary() { byte[] key = "zsetKey".getBytes(); double max = 3.0; double min = 1.0; List expectedMembersWithScores = Arrays.asList( new Tuple("member2".getBytes(), 2.0), new Tuple("member1".getBytes(), 1.0) ); when(commandObjects.zrevrangeByScoreWithScores(key, max, min)).thenReturn(listTupleCommandObject); when(commandExecutor.executeCommand(listTupleCommandObject)).thenReturn(expectedMembersWithScores); List result = jedis.zrevrangeByScoreWithScores(key, max, min); assertThat(result, equalTo(expectedMembersWithScores)); verify(commandExecutor).executeCommand(listTupleCommandObject); verify(commandObjects).zrevrangeByScoreWithScores(key, max, min); } @Test public void testZrevrangeByScoreWithLimit() { String key = "zsetKey"; String max = "3"; String min = "1"; int offset = 1; int count = 2; List expectedMembers = Arrays.asList("member2", "member1"); when(commandObjects.zrevrangeByScore(key, max, min, offset, count)).thenReturn(listStringCommandObject); when(commandExecutor.executeCommand(listStringCommandObject)).thenReturn(expectedMembers); List result = jedis.zrevrangeByScore(key, max, min, offset, count); assertThat(result, equalTo(expectedMembers)); verify(commandExecutor).executeCommand(listStringCommandObject); verify(commandObjects).zrevrangeByScore(key, max, min, offset, count); } @Test public void testZrevrangeByScoreWithLimitBinary() { byte[] key = "zsetKey".getBytes(); byte[] max = "3".getBytes(); byte[] min = "1".getBytes(); int offset = 1; int count = 2; List expectedMembers = Arrays.asList("member2".getBytes(), "member1".getBytes()); when(commandObjects.zrevrangeByScore(key, max, min, offset, count)).thenReturn(listBytesCommandObject); when(commandExecutor.executeCommand(listBytesCommandObject)).thenReturn(expectedMembers); List result = jedis.zrevrangeByScore(key, max, min, offset, count); assertThat(result, equalTo(expectedMembers)); verify(commandExecutor).executeCommand(listBytesCommandObject); verify(commandObjects).zrevrangeByScore(key, max, min, offset, count); } @Test public void testZrevrangeByScoreWithScores() { String key = "zsetKey"; String max = "3"; String min = "1"; List expectedMembersWithScores = Arrays.asList( new Tuple("member2", 2.0), new Tuple("member1", 1.0) ); when(commandObjects.zrevrangeByScoreWithScores(key, max, min)).thenReturn(listTupleCommandObject); when(commandExecutor.executeCommand(listTupleCommandObject)).thenReturn(expectedMembersWithScores); List result = jedis.zrevrangeByScoreWithScores(key, max, min); assertThat(result, equalTo(expectedMembersWithScores)); verify(commandExecutor).executeCommand(listTupleCommandObject); verify(commandObjects).zrevrangeByScoreWithScores(key, max, min); } @Test public void testZrevrangeByScoreWithScoresBinary() { byte[] key = "zsetKey".getBytes(); byte[] max = "3".getBytes(); byte[] min = "1".getBytes(); List expectedMembersWithScores = Arrays.asList( new Tuple("member2".getBytes(), 2.0), new Tuple("member1".getBytes(), 1.0) ); when(commandObjects.zrevrangeByScoreWithScores(key, max, min)).thenReturn(listTupleCommandObject); when(commandExecutor.executeCommand(listTupleCommandObject)).thenReturn(expectedMembersWithScores); List result = jedis.zrevrangeByScoreWithScores(key, max, min); assertThat(result, equalTo(expectedMembersWithScores)); verify(commandExecutor).executeCommand(listTupleCommandObject); verify(commandObjects).zrevrangeByScoreWithScores(key, max, min); } @Test public void testZrevrangeByScoreWithScoresWithLimit() { String key = "zsetKey"; String max = "3"; String min = "1"; int offset = 1; int count = 2; List expectedMembersWithScores = Arrays.asList( new Tuple("member2", 2.0), new Tuple("member1", 1.0) ); when(commandObjects.zrevrangeByScoreWithScores(key, max, min, offset, count)).thenReturn(listTupleCommandObject); when(commandExecutor.executeCommand(listTupleCommandObject)).thenReturn(expectedMembersWithScores); List result = jedis.zrevrangeByScoreWithScores(key, max, min, offset, count); assertThat(result, equalTo(expectedMembersWithScores)); verify(commandExecutor).executeCommand(listTupleCommandObject); verify(commandObjects).zrevrangeByScoreWithScores(key, max, min, offset, count); } @Test public void testZrevrangeByScoreWithScoresWithLimitBinary() { byte[] key = "zsetKey".getBytes(); byte[] max = "3".getBytes(); byte[] min = "1".getBytes(); int offset = 1; int count = 2; List expectedMembersWithScores = Arrays.asList( new Tuple("member2".getBytes(), 2.0), new Tuple("member1".getBytes(), 1.0) ); when(commandObjects.zrevrangeByScoreWithScores(key, max, min, offset, count)).thenReturn(listTupleCommandObject); when(commandExecutor.executeCommand(listTupleCommandObject)).thenReturn(expectedMembersWithScores); List result = jedis.zrevrangeByScoreWithScores(key, max, min, offset, count); assertThat(result, equalTo(expectedMembersWithScores)); verify(commandExecutor).executeCommand(listTupleCommandObject); verify(commandObjects).zrevrangeByScoreWithScores(key, max, min, offset, count); } @Test public void testZrevrangeByScoreWithScoresDoubleWithLimit() { String key = "zsetKey"; double max = 3.0; double min = 1.0; int offset = 1; int count = 2; List expectedMembersWithScores = Arrays.asList( new Tuple("member2", 2.0), new Tuple("member1", 1.0) ); when(commandObjects.zrevrangeByScoreWithScores(key, max, min, offset, count)).thenReturn(listTupleCommandObject); when(commandExecutor.executeCommand(listTupleCommandObject)).thenReturn(expectedMembersWithScores); List result = jedis.zrevrangeByScoreWithScores(key, max, min, offset, count); assertThat(result, equalTo(expectedMembersWithScores)); verify(commandExecutor).executeCommand(listTupleCommandObject); verify(commandObjects).zrevrangeByScoreWithScores(key, max, min, offset, count); } @Test public void testZrevrangeByScoreWithScoresDoubleWithLimitBinary() { byte[] key = "zsetKey".getBytes(); double max = 3.0; double min = 1.0; int offset = 1; int count = 2; List expectedMembersWithScores = Arrays.asList( new Tuple("member2".getBytes(), 2.0), new Tuple("member1".getBytes(), 1.0) ); when(commandObjects.zrevrangeByScoreWithScores(key, max, min, offset, count)).thenReturn(listTupleCommandObject); when(commandExecutor.executeCommand(listTupleCommandObject)).thenReturn(expectedMembersWithScores); List result = jedis.zrevrangeByScoreWithScores(key, max, min, offset, count); assertThat(result, equalTo(expectedMembersWithScores)); verify(commandExecutor).executeCommand(listTupleCommandObject); verify(commandObjects).zrevrangeByScoreWithScores(key, max, min, offset, count); } @Test public void testZrevrank() { String key = "zsetKey"; String member = "member1"; Long expectedRevRank = 10L; // Assuming the member is the eleventh from the end in the sorted set when(commandObjects.zrevrank(key, member)).thenReturn(longCommandObject); when(commandExecutor.executeCommand(longCommandObject)).thenReturn(expectedRevRank); Long result = jedis.zrevrank(key, member); assertThat(result, equalTo(expectedRevRank)); verify(commandExecutor).executeCommand(longCommandObject); verify(commandObjects).zrevrank(key, member); } @Test public void testZrevrankBinary() { byte[] key = "zsetKey".getBytes(); byte[] member = "member1".getBytes(); Long expectedRevRank = 10L; // Assuming the member is the eleventh from the end in the sorted set when(commandObjects.zrevrank(key, member)).thenReturn(longCommandObject); when(commandExecutor.executeCommand(longCommandObject)).thenReturn(expectedRevRank); Long result = jedis.zrevrank(key, member); assertThat(result, equalTo(expectedRevRank)); verify(commandExecutor).executeCommand(longCommandObject); verify(commandObjects).zrevrank(key, member); } @Test public void testZrevrankWithScore() { String key = "zsetKey"; String member = "member1"; KeyValue expectedRevRankWithScore = new KeyValue<>(10L, 1.0); // Assuming the member is the eleventh from the end with a score of 1.0 when(commandObjects.zrevrankWithScore(key, member)).thenReturn(keyValueLongDoubleCommandObject); when(commandExecutor.executeCommand(keyValueLongDoubleCommandObject)).thenReturn(expectedRevRankWithScore); KeyValue result = jedis.zrevrankWithScore(key, member); assertThat(result, equalTo(expectedRevRankWithScore)); verify(commandExecutor).executeCommand(keyValueLongDoubleCommandObject); verify(commandObjects).zrevrankWithScore(key, member); } @Test public void testZrevrankWithScoreBinary() { byte[] key = "zsetKey".getBytes(); byte[] member = "member1".getBytes(); KeyValue expectedRevRankWithScore = new KeyValue<>(10L, 1.0); // Assuming the member is the eleventh from the end with a score of 1.0 when(commandObjects.zrevrankWithScore(key, member)).thenReturn(keyValueLongDoubleCommandObject); when(commandExecutor.executeCommand(keyValueLongDoubleCommandObject)).thenReturn(expectedRevRankWithScore); KeyValue result = jedis.zrevrankWithScore(key, member); assertThat(result, equalTo(expectedRevRankWithScore)); verify(commandExecutor).executeCommand(keyValueLongDoubleCommandObject); verify(commandObjects).zrevrankWithScore(key, member); } @Test public void testZscan() { String key = "zsetKey"; String cursor = "0"; ScanParams params = new ScanParams().match("*").count(10); List expectedTuples = Arrays.asList( new Tuple("member1", 1.0), new Tuple("member2", 2.0) ); ScanResult expectedScanResult = new ScanResult<>(cursor, expectedTuples); when(commandObjects.zscan(key, cursor, params)).thenReturn(scanResultTupleCommandObject); when(commandExecutor.executeCommand(scanResultTupleCommandObject)).thenReturn(expectedScanResult); ScanResult result = jedis.zscan(key, cursor, params); assertThat(result, equalTo(expectedScanResult)); verify(commandExecutor).executeCommand(scanResultTupleCommandObject); verify(commandObjects).zscan(key, cursor, params); } @Test public void testZscanBinary() { byte[] key = "zsetKey".getBytes(); byte[] cursor = "0".getBytes(); ScanParams params = new ScanParams().match("*").count(10); List expectedTuples = Arrays.asList( new Tuple("member1".getBytes(), 1.0), new Tuple("member2".getBytes(), 2.0) ); ScanResult expectedScanResult = new ScanResult<>(cursor, expectedTuples); when(commandObjects.zscan(key, cursor, params)).thenReturn(scanResultTupleCommandObject); when(commandExecutor.executeCommand(scanResultTupleCommandObject)).thenReturn(expectedScanResult); ScanResult result = jedis.zscan(key, cursor, params); assertThat(result, equalTo(expectedScanResult)); verify(commandExecutor).executeCommand(scanResultTupleCommandObject); verify(commandObjects).zscan(key, cursor, params); } @Test public void testZscore() { String key = "zsetKey"; String member = "member1"; Double expectedScore = 1.0; // Assuming the member has a score of 1.0 when(commandObjects.zscore(key, member)).thenReturn(doubleCommandObject); when(commandExecutor.executeCommand(doubleCommandObject)).thenReturn(expectedScore); Double result = jedis.zscore(key, member); assertThat(result, equalTo(expectedScore)); verify(commandExecutor).executeCommand(doubleCommandObject); verify(commandObjects).zscore(key, member); } @Test public void testZscoreBinary() { byte[] key = "zsetKey".getBytes(); byte[] member = "member1".getBytes(); Double expectedScore = 1.0; // Assuming the member has a score of 1.0 when(commandObjects.zscore(key, member)).thenReturn(doubleCommandObject); when(commandExecutor.executeCommand(doubleCommandObject)).thenReturn(expectedScore); Double result = jedis.zscore(key, member); assertThat(result, equalTo(expectedScore)); verify(commandExecutor).executeCommand(doubleCommandObject); verify(commandObjects).zscore(key, member); } @Test public void testZunion() { ZParams params = new ZParams().weights(1, 2).aggregate(ZParams.Aggregate.MIN); String[] keys = { "zset1", "zset2" }; List expectedUnion = Arrays.asList("member1", "member2"); when(commandObjects.zunion(params, keys)).thenReturn(listStringCommandObject); when(commandExecutor.executeCommand(listStringCommandObject)).thenReturn(expectedUnion); List result = jedis.zunion(params, keys); assertThat(result, equalTo(expectedUnion)); verify(commandExecutor).executeCommand(listStringCommandObject); verify(commandObjects).zunion(params, keys); } @Test public void testZunionBinary() { ZParams params = new ZParams().weights(1, 2).aggregate(ZParams.Aggregate.MAX); byte[][] keys = { "zset1".getBytes(), "zset2".getBytes() }; List expectedUnion = Arrays.asList("member1".getBytes(), "member2".getBytes()); when(commandObjects.zunion(params, keys)).thenReturn(listBytesCommandObject); when(commandExecutor.executeCommand(listBytesCommandObject)).thenReturn(expectedUnion); List result = jedis.zunion(params, keys); assertThat(result, equalTo(expectedUnion)); verify(commandExecutor).executeCommand(listBytesCommandObject); verify(commandObjects).zunion(params, keys); } @Test public void testZunionWithScores() { ZParams params = new ZParams().weights(1, 2).aggregate(ZParams.Aggregate.MAX); String[] keys = { "zset1", "zset2" }; List expectedUnionWithScores = Arrays.asList(new Tuple("member1", 1.0), new Tuple("member2", 2.0)); when(commandObjects.zunionWithScores(params, keys)).thenReturn(listTupleCommandObject); when(commandExecutor.executeCommand(listTupleCommandObject)).thenReturn(expectedUnionWithScores); List result = jedis.zunionWithScores(params, keys); assertThat(result, equalTo(expectedUnionWithScores)); verify(commandExecutor).executeCommand(listTupleCommandObject); verify(commandObjects).zunionWithScores(params, keys); } @Test public void testZunionWithScoresBinary() { ZParams params = new ZParams().weights(1, 2).aggregate(ZParams.Aggregate.MAX); byte[][] keys = { "zset1".getBytes(), "zset2".getBytes() }; List expectedUnionWithScores = Arrays.asList(new Tuple("member1", 1.0), new Tuple("member2", 2.0)); when(commandObjects.zunionWithScores(params, keys)).thenReturn(listTupleCommandObject); when(commandExecutor.executeCommand(listTupleCommandObject)).thenReturn(expectedUnionWithScores); List result = jedis.zunionWithScores(params, keys); assertThat(result, equalTo(expectedUnionWithScores)); verify(commandExecutor).executeCommand(listTupleCommandObject); verify(commandObjects).zunionWithScores(params, keys); } @Test public void testZunionstore() { String dstkey = "zsetUnion"; String[] sets = { "zset1", "zset2" }; long expectedStoredCount = 3L; when(commandObjects.zunionstore(dstkey, sets)).thenReturn(longCommandObject); when(commandExecutor.executeCommand(longCommandObject)).thenReturn(expectedStoredCount); long result = jedis.zunionstore(dstkey, sets); assertThat(result, equalTo(expectedStoredCount)); verify(commandExecutor).executeCommand(longCommandObject); verify(commandObjects).zunionstore(dstkey, sets); } @Test public void testZunionstoreBinary() { byte[] dstkey = "zsetUnion".getBytes(); byte[][] sets = { "zset1".getBytes(), "zset2".getBytes() }; long expectedStoredCount = 3L; when(commandObjects.zunionstore(dstkey, sets)).thenReturn(longCommandObject); when(commandExecutor.executeCommand(longCommandObject)).thenReturn(expectedStoredCount); long result = jedis.zunionstore(dstkey, sets); assertThat(result, equalTo(expectedStoredCount)); verify(commandExecutor).executeCommand(longCommandObject); verify(commandObjects).zunionstore(dstkey, sets); } @Test public void testZunionstoreWithParams() { String dstkey = "zsetUnion"; ZParams params = new ZParams().weights(1, 2).aggregate(ZParams.Aggregate.MAX); String[] sets = { "zset1", "zset2" }; long expectedStoredCount = 3L; when(commandObjects.zunionstore(dstkey, params, sets)).thenReturn(longCommandObject); when(commandExecutor.executeCommand(longCommandObject)).thenReturn(expectedStoredCount); long result = jedis.zunionstore(dstkey, params, sets); assertThat(result, equalTo(expectedStoredCount)); verify(commandExecutor).executeCommand(longCommandObject); verify(commandObjects).zunionstore(dstkey, params, sets); } @Test public void testZunionstoreWithParamsBinary() { byte[] dstkey = "zsetUnion".getBytes(); ZParams params = new ZParams().weights(1, 2).aggregate(ZParams.Aggregate.MAX); byte[][] sets = { "zset1".getBytes(), "zset2".getBytes() }; long expectedStoredCount = 3L; when(commandObjects.zunionstore(dstkey, params, sets)).thenReturn(longCommandObject); when(commandExecutor.executeCommand(longCommandObject)).thenReturn(expectedStoredCount); long result = jedis.zunionstore(dstkey, params, sets); assertThat(result, equalTo(expectedStoredCount)); verify(commandExecutor).executeCommand(longCommandObject); verify(commandObjects).zunionstore(dstkey, params, sets); } } ================================================ FILE: src/test/java/redis/clients/jedis/mocked/unified/UnifiedJedisStreamCommandsTest.java ================================================ package redis.clients.jedis.mocked.unified; import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.equalTo; import static org.hamcrest.Matchers.sameInstance; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; import java.util.AbstractMap; import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; import java.util.HashMap; import java.util.List; import java.util.Map; import org.junit.jupiter.api.Test; import redis.clients.jedis.StreamEntryID; import redis.clients.jedis.params.XAddParams; import redis.clients.jedis.params.XAutoClaimParams; import redis.clients.jedis.params.XClaimParams; import redis.clients.jedis.params.XPendingParams; import redis.clients.jedis.params.XReadGroupParams; import redis.clients.jedis.params.XReadParams; import redis.clients.jedis.params.XTrimParams; import redis.clients.jedis.resps.StreamConsumerInfo; import redis.clients.jedis.resps.StreamConsumersInfo; import redis.clients.jedis.resps.StreamEntry; import redis.clients.jedis.resps.StreamFullInfo; import redis.clients.jedis.resps.StreamGroupInfo; import redis.clients.jedis.resps.StreamInfo; import redis.clients.jedis.resps.StreamPendingEntry; import redis.clients.jedis.resps.StreamPendingSummary; public class UnifiedJedisStreamCommandsTest extends UnifiedJedisMockedTestBase { @Test public void testXack() { String key = "mystream"; String group = "mygroup"; StreamEntryID[] ids = { new StreamEntryID("0-0"), new StreamEntryID("0-1") }; long expectedAcked = 2L; when(commandObjects.xack(key, group, ids)).thenReturn(longCommandObject); when(commandExecutor.executeCommand(longCommandObject)).thenReturn(expectedAcked); long result = jedis.xack(key, group, ids); assertThat(result, equalTo(expectedAcked)); verify(commandExecutor).executeCommand(longCommandObject); verify(commandObjects).xack(key, group, ids); } @Test public void testXackBinary() { byte[] key = "mystream".getBytes(); byte[] group = "mygroup".getBytes(); byte[][] ids = { "0-0".getBytes(), "0-1".getBytes() }; long expectedAcked = 2L; when(commandObjects.xack(key, group, ids)).thenReturn(longCommandObject); when(commandExecutor.executeCommand(longCommandObject)).thenReturn(expectedAcked); long result = jedis.xack(key, group, ids); assertThat(result, equalTo(expectedAcked)); verify(commandExecutor).executeCommand(longCommandObject); verify(commandObjects).xack(key, group, ids); } @Test public void testXadd() { String key = "mystream"; StreamEntryID id = new StreamEntryID("0-0"); Map hash = new HashMap<>(); hash.put("field1", "value1"); hash.put("field2", "value2"); StreamEntryID expectedEntryID = new StreamEntryID("0-1"); when(commandObjects.xadd(key, id, hash)).thenReturn(streamEntryIdCommandObject); when(commandExecutor.executeCommand(streamEntryIdCommandObject)).thenReturn(expectedEntryID); StreamEntryID result = jedis.xadd(key, id, hash); assertThat(result, equalTo(expectedEntryID)); verify(commandExecutor).executeCommand(streamEntryIdCommandObject); verify(commandObjects).xadd(key, id, hash); } @Test public void testXaddBinary() { byte[] key = "mystream".getBytes(); XAddParams params = new XAddParams().id("0-1"); Map hash = new HashMap<>(); hash.put("field1".getBytes(), "value1".getBytes()); byte[] expectedEntryId = "0-1".getBytes(); when(commandObjects.xadd(key, params, hash)).thenReturn(bytesCommandObject); when(commandExecutor.executeCommand(bytesCommandObject)).thenReturn(expectedEntryId); byte[] result = jedis.xadd(key, params, hash); assertThat(result, equalTo(expectedEntryId)); verify(commandExecutor).executeCommand(bytesCommandObject); verify(commandObjects).xadd(key, params, hash); } @Test public void testXaddWithParams() { String key = "mystream"; XAddParams params = new XAddParams(); Map hash = new HashMap<>(); hash.put("field1", "value1"); hash.put("field2", "value2"); StreamEntryID expectedEntryID = new StreamEntryID("0-1"); when(commandObjects.xadd(key, params, hash)).thenReturn(streamEntryIdCommandObject); when(commandExecutor.executeCommand(streamEntryIdCommandObject)).thenReturn(expectedEntryID); StreamEntryID result = jedis.xadd(key, params, hash); assertThat(result, equalTo(expectedEntryID)); verify(commandExecutor).executeCommand(streamEntryIdCommandObject); verify(commandObjects).xadd(key, params, hash); } @Test public void testXautoclaim() { String key = "mystream"; String group = "mygroup"; String consumerName = "myconsumer"; long minIdleTime = 10000L; StreamEntryID start = new StreamEntryID("0-0"); XAutoClaimParams params = new XAutoClaimParams(); StreamEntryID nextStart = new StreamEntryID("0-1"); List claimedEntries = new ArrayList<>(); AbstractMap.SimpleImmutableEntry> expectedResponse = new AbstractMap.SimpleImmutableEntry<>(nextStart, claimedEntries); when(commandObjects.xautoclaim(key, group, consumerName, minIdleTime, start, params)).thenReturn(entryStreamEntryIdListStreamEntryCommandObject); when(commandExecutor.executeCommand(entryStreamEntryIdListStreamEntryCommandObject)).thenReturn(expectedResponse); Map.Entry> result = jedis.xautoclaim(key, group, consumerName, minIdleTime, start, params); assertThat(result, equalTo(expectedResponse)); verify(commandExecutor).executeCommand(entryStreamEntryIdListStreamEntryCommandObject); verify(commandObjects).xautoclaim(key, group, consumerName, minIdleTime, start, params); } @Test public void testXautoclaimBinary() { byte[] key = "mystream".getBytes(); byte[] groupName = "mygroup".getBytes(); byte[] consumerName = "myconsumer".getBytes(); long minIdleTime = 10000L; byte[] start = "0-0".getBytes(); XAutoClaimParams params = new XAutoClaimParams(); List expectedAutoClaimResult = new ArrayList<>(); when(commandObjects.xautoclaim(key, groupName, consumerName, minIdleTime, start, params)).thenReturn(listObjectCommandObject); when(commandExecutor.executeCommand(listObjectCommandObject)).thenReturn(expectedAutoClaimResult); List result = jedis.xautoclaim(key, groupName, consumerName, minIdleTime, start, params); assertThat(result, equalTo(expectedAutoClaimResult)); verify(commandExecutor).executeCommand(listObjectCommandObject); verify(commandObjects).xautoclaim(key, groupName, consumerName, minIdleTime, start, params); } @Test public void testXautoclaimJustId() { String key = "mystream"; String group = "mygroup"; String consumerName = "myconsumer"; long minIdleTime = 10000L; StreamEntryID start = new StreamEntryID("0-0"); XAutoClaimParams params = new XAutoClaimParams(); StreamEntryID nextStart = new StreamEntryID("0-1"); List claimedEntryIds = Arrays.asList(new StreamEntryID("0-0"), new StreamEntryID("0-1")); AbstractMap.SimpleImmutableEntry> expectedResponse = new AbstractMap.SimpleImmutableEntry<>(nextStart, claimedEntryIds); when(commandObjects.xautoclaimJustId(key, group, consumerName, minIdleTime, start, params)).thenReturn(entryStreamEntryIdListStreamEntryIdCommandObject); when(commandExecutor.executeCommand(entryStreamEntryIdListStreamEntryIdCommandObject)).thenReturn(expectedResponse); Map.Entry> result = jedis.xautoclaimJustId(key, group, consumerName, minIdleTime, start, params); assertThat(result, equalTo(expectedResponse)); verify(commandExecutor).executeCommand(entryStreamEntryIdListStreamEntryIdCommandObject); verify(commandObjects).xautoclaimJustId(key, group, consumerName, minIdleTime, start, params); } @Test public void testXautoclaimJustIdBinary() { byte[] key = "mystream".getBytes(); byte[] groupName = "mygroup".getBytes(); byte[] consumerName = "myconsumer".getBytes(); long minIdleTime = 10000L; byte[] start = "0-0".getBytes(); XAutoClaimParams params = new XAutoClaimParams(); List expectedAutoClaimResult = new ArrayList<>(); when(commandObjects.xautoclaimJustId(key, groupName, consumerName, minIdleTime, start, params)).thenReturn(listObjectCommandObject); when(commandExecutor.executeCommand(listObjectCommandObject)).thenReturn(expectedAutoClaimResult); List result = jedis.xautoclaimJustId(key, groupName, consumerName, minIdleTime, start, params); assertThat(result, equalTo(expectedAutoClaimResult)); verify(commandExecutor).executeCommand(listObjectCommandObject); verify(commandObjects).xautoclaimJustId(key, groupName, consumerName, minIdleTime, start, params); } @Test public void testXclaim() { String key = "mystream"; String group = "mygroup"; String consumerName = "myconsumer"; long minIdleTime = 10000L; XClaimParams params = new XClaimParams(); StreamEntryID[] ids = { new StreamEntryID("0-0"), new StreamEntryID("0-1") }; List expectedEntries = new ArrayList<>(); when(commandObjects.xclaim(key, group, consumerName, minIdleTime, params, ids)).thenReturn(listStreamEntryCommandObject); when(commandExecutor.executeCommand(listStreamEntryCommandObject)).thenReturn(expectedEntries); List result = jedis.xclaim(key, group, consumerName, minIdleTime, params, ids); assertThat(result, equalTo(expectedEntries)); verify(commandExecutor).executeCommand(listStreamEntryCommandObject); verify(commandObjects).xclaim(key, group, consumerName, minIdleTime, params, ids); } @Test public void testXclaimBinary() { byte[] key = "mystream".getBytes(); byte[] group = "mygroup".getBytes(); byte[] consumerName = "myconsumer".getBytes(); long minIdleTime = 10000L; XClaimParams params = new XClaimParams(); byte[][] ids = { "0-0".getBytes(), "0-1".getBytes() }; List expectedClaimedIds = Arrays.asList("0-0".getBytes(), "0-1".getBytes()); when(commandObjects.xclaim(key, group, consumerName, minIdleTime, params, ids)).thenReturn(listBytesCommandObject); when(commandExecutor.executeCommand(listBytesCommandObject)).thenReturn(expectedClaimedIds); List result = jedis.xclaim(key, group, consumerName, minIdleTime, params, ids); assertThat(result, equalTo(expectedClaimedIds)); verify(commandExecutor).executeCommand(listBytesCommandObject); verify(commandObjects).xclaim(key, group, consumerName, minIdleTime, params, ids); } @Test public void testXclaimJustId() { String key = "mystream"; String group = "mygroup"; String consumerName = "myconsumer"; long minIdleTime = 10000L; XClaimParams params = new XClaimParams(); StreamEntryID[] ids = { new StreamEntryID("0-0"), new StreamEntryID("0-1") }; List expectedEntryIds = Arrays.asList(ids); when(commandObjects.xclaimJustId(key, group, consumerName, minIdleTime, params, ids)).thenReturn(listStreamEntryIdCommandObject); when(commandExecutor.executeCommand(listStreamEntryIdCommandObject)).thenReturn(expectedEntryIds); List result = jedis.xclaimJustId(key, group, consumerName, minIdleTime, params, ids); assertThat(result, equalTo(expectedEntryIds)); verify(commandExecutor).executeCommand(listStreamEntryIdCommandObject); verify(commandObjects).xclaimJustId(key, group, consumerName, minIdleTime, params, ids); } @Test public void testXclaimJustIdBinary() { byte[] key = "mystream".getBytes(); byte[] group = "mygroup".getBytes(); byte[] consumerName = "myconsumer".getBytes(); long minIdleTime = 10000L; XClaimParams params = new XClaimParams(); byte[][] ids = { "0-0".getBytes(), "0-1".getBytes() }; List expectedClaimedIds = Arrays.asList("0-0".getBytes(), "0-1".getBytes()); when(commandObjects.xclaimJustId(key, group, consumerName, minIdleTime, params, ids)).thenReturn(listBytesCommandObject); when(commandExecutor.executeCommand(listBytesCommandObject)).thenReturn(expectedClaimedIds); List result = jedis.xclaimJustId(key, group, consumerName, minIdleTime, params, ids); assertThat(result, equalTo(expectedClaimedIds)); verify(commandExecutor).executeCommand(listBytesCommandObject); verify(commandObjects).xclaimJustId(key, group, consumerName, minIdleTime, params, ids); } @Test public void testXdel() { String key = "mystream"; StreamEntryID[] ids = { new StreamEntryID("0-0"), new StreamEntryID("0-1") }; long expectedDeletedCount = 2L; // Assuming the entries were successfully deleted when(commandObjects.xdel(key, ids)).thenReturn(longCommandObject); when(commandExecutor.executeCommand(longCommandObject)).thenReturn(expectedDeletedCount); long result = jedis.xdel(key, ids); assertThat(result, equalTo(expectedDeletedCount)); verify(commandExecutor).executeCommand(longCommandObject); verify(commandObjects).xdel(key, ids); } @Test public void testXdelBinary() { byte[] key = "mystream".getBytes(); byte[][] ids = { "0-0".getBytes(), "0-1".getBytes() }; long expectedDeleted = 2L; when(commandObjects.xdel(key, ids)).thenReturn(longCommandObject); when(commandExecutor.executeCommand(longCommandObject)).thenReturn(expectedDeleted); long result = jedis.xdel(key, ids); assertThat(result, equalTo(expectedDeleted)); verify(commandExecutor).executeCommand(longCommandObject); verify(commandObjects).xdel(key, ids); } @Test public void testXgroupCreate() { String key = "mystream"; String groupName = "mygroup"; StreamEntryID id = new StreamEntryID("0-0"); boolean makeStream = true; String expectedResponse = "OK"; when(commandObjects.xgroupCreate(key, groupName, id, makeStream)).thenReturn(stringCommandObject); when(commandExecutor.executeCommand(stringCommandObject)).thenReturn(expectedResponse); String result = jedis.xgroupCreate(key, groupName, id, makeStream); assertThat(result, equalTo(expectedResponse)); verify(commandExecutor).executeCommand(stringCommandObject); verify(commandObjects).xgroupCreate(key, groupName, id, makeStream); } @Test public void testXgroupCreateBinary() { byte[] key = "mystream".getBytes(); byte[] groupName = "mygroup".getBytes(); byte[] id = "0-0".getBytes(); boolean makeStream = true; String expectedResponse = "OK"; when(commandObjects.xgroupCreate(key, groupName, id, makeStream)).thenReturn(stringCommandObject); when(commandExecutor.executeCommand(stringCommandObject)).thenReturn(expectedResponse); String result = jedis.xgroupCreate(key, groupName, id, makeStream); assertThat(result, equalTo(expectedResponse)); verify(commandExecutor).executeCommand(stringCommandObject); verify(commandObjects).xgroupCreate(key, groupName, id, makeStream); } @Test public void testXgroupCreateConsumer() { String key = "mystream"; String groupName = "mygroup"; String consumerName = "myconsumer"; boolean expectedResponse = true; // Assuming the consumer was successfully created when(commandObjects.xgroupCreateConsumer(key, groupName, consumerName)).thenReturn(booleanCommandObject); when(commandExecutor.executeCommand(booleanCommandObject)).thenReturn(expectedResponse); boolean result = jedis.xgroupCreateConsumer(key, groupName, consumerName); assertThat(result, equalTo(expectedResponse)); verify(commandExecutor).executeCommand(booleanCommandObject); verify(commandObjects).xgroupCreateConsumer(key, groupName, consumerName); } @Test public void testXgroupCreateConsumerBinary() { byte[] key = "mystream".getBytes(); byte[] groupName = "mygroup".getBytes(); byte[] consumerName = "myconsumer".getBytes(); boolean expectedResponse = true; when(commandObjects.xgroupCreateConsumer(key, groupName, consumerName)).thenReturn(booleanCommandObject); when(commandExecutor.executeCommand(booleanCommandObject)).thenReturn(expectedResponse); boolean result = jedis.xgroupCreateConsumer(key, groupName, consumerName); assertThat(result, equalTo(expectedResponse)); verify(commandExecutor).executeCommand(booleanCommandObject); verify(commandObjects).xgroupCreateConsumer(key, groupName, consumerName); } @Test public void testXgroupDelConsumer() { String key = "mystream"; String groupName = "mygroup"; String consumerName = "myconsumer"; long expectedDeletedCount = 1L; // Assuming the consumer was successfully deleted when(commandObjects.xgroupDelConsumer(key, groupName, consumerName)).thenReturn(longCommandObject); when(commandExecutor.executeCommand(longCommandObject)).thenReturn(expectedDeletedCount); long result = jedis.xgroupDelConsumer(key, groupName, consumerName); assertThat(result, equalTo(expectedDeletedCount)); verify(commandExecutor).executeCommand(longCommandObject); verify(commandObjects).xgroupDelConsumer(key, groupName, consumerName); } @Test public void testXgroupDelConsumerBinary() { byte[] key = "mystream".getBytes(); byte[] groupName = "mygroup".getBytes(); byte[] consumerName = "myconsumer".getBytes(); long expectedDeleted = 1L; when(commandObjects.xgroupDelConsumer(key, groupName, consumerName)).thenReturn(longCommandObject); when(commandExecutor.executeCommand(longCommandObject)).thenReturn(expectedDeleted); long result = jedis.xgroupDelConsumer(key, groupName, consumerName); assertThat(result, equalTo(expectedDeleted)); verify(commandExecutor).executeCommand(longCommandObject); verify(commandObjects).xgroupDelConsumer(key, groupName, consumerName); } @Test public void testXgroupDestroy() { String key = "mystream"; String groupName = "mygroup"; long expectedResponse = 1L; // Assuming the group was successfully destroyed when(commandObjects.xgroupDestroy(key, groupName)).thenReturn(longCommandObject); when(commandExecutor.executeCommand(longCommandObject)).thenReturn(expectedResponse); long result = jedis.xgroupDestroy(key, groupName); assertThat(result, equalTo(expectedResponse)); verify(commandExecutor).executeCommand(longCommandObject); verify(commandObjects).xgroupDestroy(key, groupName); } @Test public void testXgroupDestroyBinary() { byte[] key = "mystream".getBytes(); byte[] groupName = "mygroup".getBytes(); long expectedDestroyed = 1L; when(commandObjects.xgroupDestroy(key, groupName)).thenReturn(longCommandObject); when(commandExecutor.executeCommand(longCommandObject)).thenReturn(expectedDestroyed); long result = jedis.xgroupDestroy(key, groupName); assertThat(result, equalTo(expectedDestroyed)); verify(commandExecutor).executeCommand(longCommandObject); verify(commandObjects).xgroupDestroy(key, groupName); } @Test public void testXgroupSetID() { String key = "mystream"; String groupName = "mygroup"; StreamEntryID id = new StreamEntryID("0-0"); String expectedResponse = "OK"; when(commandObjects.xgroupSetID(key, groupName, id)).thenReturn(stringCommandObject); when(commandExecutor.executeCommand(stringCommandObject)).thenReturn(expectedResponse); String result = jedis.xgroupSetID(key, groupName, id); assertThat(result, equalTo(expectedResponse)); verify(commandExecutor).executeCommand(stringCommandObject); verify(commandObjects).xgroupSetID(key, groupName, id); } @Test public void testXgroupSetIDBinary() { byte[] key = "mystream".getBytes(); byte[] groupName = "mygroup".getBytes(); byte[] id = "0-1".getBytes(); String expectedResponse = "OK"; when(commandObjects.xgroupSetID(key, groupName, id)).thenReturn(stringCommandObject); when(commandExecutor.executeCommand(stringCommandObject)).thenReturn(expectedResponse); String result = jedis.xgroupSetID(key, groupName, id); assertThat(result, equalTo(expectedResponse)); verify(commandExecutor).executeCommand(stringCommandObject); verify(commandObjects).xgroupSetID(key, groupName, id); } @Test public void testXinfoConsumers() { String key = "mystream"; String group = "mygroup"; List expectedConsumers = Collections.singletonList(mock(StreamConsumersInfo.class)); when(commandObjects.xinfoConsumers(key, group)).thenReturn(listStreamConsumersInfoCommandObject); when(commandExecutor.executeCommand(listStreamConsumersInfoCommandObject)).thenReturn(expectedConsumers); List result = jedis.xinfoConsumers(key, group); assertThat(result, equalTo(expectedConsumers)); verify(commandExecutor).executeCommand(listStreamConsumersInfoCommandObject); verify(commandObjects).xinfoConsumers(key, group); } @Test public void testXinfoConsumersBinary() { byte[] key = "mystream".getBytes(); byte[] group = "mygroup".getBytes(); List expectedConsumersInfo = new ArrayList<>(); when(commandObjects.xinfoConsumers(key, group)).thenReturn(listObjectCommandObject); when(commandExecutor.executeCommand(listObjectCommandObject)).thenReturn(expectedConsumersInfo); List result = jedis.xinfoConsumers(key, group); assertThat(result, equalTo(expectedConsumersInfo)); verify(commandExecutor).executeCommand(listObjectCommandObject); verify(commandObjects).xinfoConsumers(key, group); } @Test public void testXinfoConsumers2() { String key = "mystream"; String group = "mygroup"; List expectedConsumerInfos = Collections.singletonList(mock(StreamConsumerInfo.class)); when(commandObjects.xinfoConsumers2(key, group)).thenReturn(listStreamConsumerInfoCommandObject); when(commandExecutor.executeCommand(listStreamConsumerInfoCommandObject)).thenReturn(expectedConsumerInfos); List result = jedis.xinfoConsumers2(key, group); assertThat(result, equalTo(expectedConsumerInfos)); verify(commandExecutor).executeCommand(listStreamConsumerInfoCommandObject); verify(commandObjects).xinfoConsumers2(key, group); } @Test public void testXinfoGroups() { String key = "mystream"; List expectedGroups = Collections.singletonList(mock(StreamGroupInfo.class)); when(commandObjects.xinfoGroups(key)).thenReturn(listStreamGroupInfoCommandObject); when(commandExecutor.executeCommand(listStreamGroupInfoCommandObject)).thenReturn(expectedGroups); List result = jedis.xinfoGroups(key); assertThat(result, equalTo(expectedGroups)); verify(commandExecutor).executeCommand(listStreamGroupInfoCommandObject); verify(commandObjects).xinfoGroups(key); } @Test public void testXinfoGroupsBinary() { byte[] key = "mystream".getBytes(); List expectedGroupsInfo = new ArrayList<>(); when(commandObjects.xinfoGroups(key)).thenReturn(listObjectCommandObject); when(commandExecutor.executeCommand(listObjectCommandObject)).thenReturn(expectedGroupsInfo); List result = jedis.xinfoGroups(key); assertThat(result, equalTo(expectedGroupsInfo)); verify(commandExecutor).executeCommand(listObjectCommandObject); verify(commandObjects).xinfoGroups(key); } @Test public void testXinfoStream() { String key = "mystream"; StreamInfo expectedStreamInfo = mock(StreamInfo.class); when(commandObjects.xinfoStream(key)).thenReturn(streamInfoCommandObject); when(commandExecutor.executeCommand(streamInfoCommandObject)).thenReturn(expectedStreamInfo); StreamInfo result = jedis.xinfoStream(key); assertThat(result, sameInstance(expectedStreamInfo)); verify(commandExecutor).executeCommand(streamInfoCommandObject); verify(commandObjects).xinfoStream(key); } @Test public void testXinfoStreamBinary() { byte[] key = "mystream".getBytes(); Object expectedStreamInfo = new Object(); when(commandObjects.xinfoStream(key)).thenReturn(objectCommandObject); when(commandExecutor.executeCommand(objectCommandObject)).thenReturn(expectedStreamInfo); Object result = jedis.xinfoStream(key); assertThat(result, sameInstance(expectedStreamInfo)); verify(commandExecutor).executeCommand(objectCommandObject); verify(commandObjects).xinfoStream(key); } @Test public void testXinfoStreamFull() { String key = "mystream"; StreamFullInfo expectedStreamFullInfo = mock(StreamFullInfo.class); when(commandObjects.xinfoStreamFull(key)).thenReturn(streamFullInfoCommandObject); when(commandExecutor.executeCommand(streamFullInfoCommandObject)).thenReturn(expectedStreamFullInfo); StreamFullInfo result = jedis.xinfoStreamFull(key); assertThat(result, sameInstance(expectedStreamFullInfo)); verify(commandExecutor).executeCommand(streamFullInfoCommandObject); verify(commandObjects).xinfoStreamFull(key); } @Test public void testXinfoStreamFullBinary() { byte[] key = "mystream".getBytes(); Object expectedStreamInfoFull = new Object(); when(commandObjects.xinfoStreamFull(key)).thenReturn(objectCommandObject); when(commandExecutor.executeCommand(objectCommandObject)).thenReturn(expectedStreamInfoFull); Object result = jedis.xinfoStreamFull(key); assertThat(result, sameInstance(expectedStreamInfoFull)); verify(commandExecutor).executeCommand(objectCommandObject); verify(commandObjects).xinfoStreamFull(key); } @Test public void testXinfoStreamFullWithCount() { String key = "mystream"; int count = 10; StreamFullInfo expectedStreamFullInfo = mock(StreamFullInfo.class); when(commandObjects.xinfoStreamFull(key, count)).thenReturn(streamFullInfoCommandObject); when(commandExecutor.executeCommand(streamFullInfoCommandObject)).thenReturn(expectedStreamFullInfo); StreamFullInfo result = jedis.xinfoStreamFull(key, count); assertThat(result, sameInstance(expectedStreamFullInfo)); verify(commandExecutor).executeCommand(streamFullInfoCommandObject); verify(commandObjects).xinfoStreamFull(key, count); } @Test public void testXinfoStreamFullWithCountBinary() { byte[] key = "mystream".getBytes(); int count = 10; Object expectedStreamInfoFull = new Object(); when(commandObjects.xinfoStreamFull(key, count)).thenReturn(objectCommandObject); when(commandExecutor.executeCommand(objectCommandObject)).thenReturn(expectedStreamInfoFull); Object result = jedis.xinfoStreamFull(key, count); assertThat(result, sameInstance(expectedStreamInfoFull)); verify(commandExecutor).executeCommand(objectCommandObject); verify(commandObjects).xinfoStreamFull(key, count); } @Test public void testXlen() { String key = "mystream"; long expectedLength = 10L; when(commandObjects.xlen(key)).thenReturn(longCommandObject); when(commandExecutor.executeCommand(longCommandObject)).thenReturn(expectedLength); long result = jedis.xlen(key); assertThat(result, equalTo(expectedLength)); verify(commandExecutor).executeCommand(longCommandObject); verify(commandObjects).xlen(key); } @Test public void testXlenBinary() { byte[] key = "mystream".getBytes(); long expectedLength = 100L; when(commandObjects.xlen(key)).thenReturn(longCommandObject); when(commandExecutor.executeCommand(longCommandObject)).thenReturn(expectedLength); long result = jedis.xlen(key); assertThat(result, equalTo(expectedLength)); verify(commandExecutor).executeCommand(longCommandObject); verify(commandObjects).xlen(key); } @Test public void testXpending() { String key = "mystream"; String groupName = "mygroup"; StreamPendingSummary expectedSummary = new StreamPendingSummary(10L, new StreamEntryID("0-0"), new StreamEntryID("0-1"), Collections.emptyMap()); when(commandObjects.xpending(key, groupName)).thenReturn(streamPendingSummaryCommandObject); when(commandExecutor.executeCommand(streamPendingSummaryCommandObject)).thenReturn(expectedSummary); StreamPendingSummary result = jedis.xpending(key, groupName); assertThat(result, equalTo(expectedSummary)); verify(commandExecutor).executeCommand(streamPendingSummaryCommandObject); verify(commandObjects).xpending(key, groupName); } @Test public void testXpendingBinary() { byte[] key = "mystream".getBytes(); byte[] groupName = "mygroup".getBytes(); Object expectedPendingInfo = new Object(); when(commandObjects.xpending(key, groupName)).thenReturn(objectCommandObject); when(commandExecutor.executeCommand(objectCommandObject)).thenReturn(expectedPendingInfo); Object result = jedis.xpending(key, groupName); assertThat(result, sameInstance(expectedPendingInfo)); verify(commandExecutor).executeCommand(objectCommandObject); verify(commandObjects).xpending(key, groupName); } @Test public void testXpendingWithParams() { String key = "mystream"; String groupName = "mygroup"; XPendingParams params = new XPendingParams(); List expectedPendingEntries = new ArrayList<>(); when(commandObjects.xpending(key, groupName, params)).thenReturn(listStreamPendingEntryCommandObject); when(commandExecutor.executeCommand(listStreamPendingEntryCommandObject)).thenReturn(expectedPendingEntries); List result = jedis.xpending(key, groupName, params); assertThat(result, equalTo(expectedPendingEntries)); verify(commandExecutor).executeCommand(listStreamPendingEntryCommandObject); verify(commandObjects).xpending(key, groupName, params); } @Test public void testXpendingWithParamsBinary() { byte[] key = "mystream".getBytes(); byte[] groupName = "mygroup".getBytes(); XPendingParams params = new XPendingParams().count(10); List expectedPendingList = new ArrayList<>(); when(commandObjects.xpending(key, groupName, params)).thenReturn(listObjectCommandObject); when(commandExecutor.executeCommand(listObjectCommandObject)).thenReturn(expectedPendingList); List result = jedis.xpending(key, groupName, params); assertThat(result, equalTo(expectedPendingList)); verify(commandExecutor).executeCommand(listObjectCommandObject); verify(commandObjects).xpending(key, groupName, params); } @Test public void testXrange() { String key = "mystream"; String start = "-"; String end = "+"; Map hash = new HashMap<>(); hash.put("field1", "value1"); hash.put("field2", "value2"); List expectedEntries = Collections.singletonList(new StreamEntry(new StreamEntryID("0-1"), hash)); when(commandObjects.xrange(key, start, end)).thenReturn(listStreamEntryCommandObject); when(commandExecutor.executeCommand(listStreamEntryCommandObject)).thenReturn(expectedEntries); List result = jedis.xrange(key, start, end); assertThat(result, equalTo(expectedEntries)); verify(commandExecutor).executeCommand(listStreamEntryCommandObject); verify(commandObjects).xrange(key, start, end); } @Test public void testXrangeBinary() { byte[] key = "mystream".getBytes(); byte[] start = "0-0".getBytes(); byte[] end = "+".getBytes(); List expectedRange = Arrays.asList( new StreamEntry(new StreamEntryID("0-0"), Collections.singletonMap("field1", "value1")), new StreamEntry(new StreamEntryID("0-1"), Collections.singletonMap("field2", "value2"))); when(commandObjects.xrange(key, start, end)).thenReturn(listObjectCommandObject); when(commandExecutor.executeCommand(listObjectCommandObject)).thenReturn(expectedRange); List result = jedis.xrange(key, start, end); assertThat(result, equalTo(expectedRange)); verify(commandExecutor).executeCommand(listObjectCommandObject); verify(commandObjects).xrange(key, start, end); } @Test public void testXrangeWithCount() { String key = "mystream"; String start = "-"; String end = "+"; int count = 10; Map hash = new HashMap<>(); hash.put("field1", "value1"); hash.put("field2", "value2"); List expectedEntries = Collections.singletonList(new StreamEntry(new StreamEntryID("0-1"), hash)); when(commandObjects.xrange(key, start, end, count)).thenReturn(listStreamEntryCommandObject); when(commandExecutor.executeCommand(listStreamEntryCommandObject)).thenReturn(expectedEntries); List result = jedis.xrange(key, start, end, count); assertThat(result, equalTo(expectedEntries)); verify(commandExecutor).executeCommand(listStreamEntryCommandObject); verify(commandObjects).xrange(key, start, end, count); } @Test public void testXrangeWithCountBinary() { byte[] key = "mystream".getBytes(); byte[] start = "0-0".getBytes(); byte[] end = "+".getBytes(); int count = 2; List expectedRange = Arrays.asList( new StreamEntry(new StreamEntryID("0-0"), Collections.singletonMap("field1", "value1")), new StreamEntry(new StreamEntryID("0-1"), Collections.singletonMap("field2", "value2"))); when(commandObjects.xrange(key, start, end, count)).thenReturn(listObjectCommandObject); when(commandExecutor.executeCommand(listObjectCommandObject)).thenReturn(expectedRange); List result = jedis.xrange(key, start, end, count); assertThat(result, equalTo(expectedRange)); verify(commandExecutor).executeCommand(listObjectCommandObject); verify(commandObjects).xrange(key, start, end, count); } @Test public void testXrangeIds() { String key = "mystream"; StreamEntryID start = new StreamEntryID("0-0"); StreamEntryID end = new StreamEntryID("0-1"); List expectedEntries = new ArrayList<>(); Map hash = new HashMap<>(); hash.put("field1", "value1"); hash.put("field2", "value2"); expectedEntries.add(new StreamEntry(new StreamEntryID("0-1"), hash)); when(commandObjects.xrange(key, start, end)).thenReturn(listStreamEntryCommandObject); when(commandExecutor.executeCommand(listStreamEntryCommandObject)).thenReturn(expectedEntries); List result = jedis.xrange(key, start, end); assertThat(result, equalTo(expectedEntries)); verify(commandExecutor).executeCommand(listStreamEntryCommandObject); verify(commandObjects).xrange(key, start, end); } @Test public void testXrangeIdsWithCount() { String key = "mystream"; StreamEntryID start = new StreamEntryID("0-0"); StreamEntryID end = new StreamEntryID("0-1"); int count = 10; Map hash = new HashMap<>(); hash.put("field1", "value1"); hash.put("field2", "value2"); List expectedEntries = Collections.singletonList(new StreamEntry(new StreamEntryID("0-1"), hash)); when(commandObjects.xrange(key, start, end, count)).thenReturn(listStreamEntryCommandObject); when(commandExecutor.executeCommand(listStreamEntryCommandObject)).thenReturn(expectedEntries); List result = jedis.xrange(key, start, end, count); assertThat(result, equalTo(expectedEntries)); verify(commandExecutor).executeCommand(listStreamEntryCommandObject); verify(commandObjects).xrange(key, start, end, count); } @Test public void testXread() { XReadParams xReadParams = new XReadParams().count(2).block(0); Map streams = Collections.singletonMap("mystream", new StreamEntryID("0-0")); List>> expectedEntries = new ArrayList<>(); when(commandObjects.xread(xReadParams, streams)).thenReturn(listEntryStringListStreamEntryCommandObject); when(commandExecutor.executeCommand(listEntryStringListStreamEntryCommandObject)).thenReturn(expectedEntries); List>> result = jedis.xread(xReadParams, streams); assertThat(result, equalTo(expectedEntries)); verify(commandExecutor).executeCommand(listEntryStringListStreamEntryCommandObject); verify(commandObjects).xread(xReadParams, streams); } @Test public void testXreadBinary() { XReadParams xReadParams = new XReadParams().count(2).block(0); Map.Entry stream1 = new AbstractMap.SimpleEntry<>("mystream".getBytes(), "0-0".getBytes()); List expectedReadResult = new ArrayList<>(); when(commandObjects.xread(xReadParams, stream1)).thenReturn(listObjectCommandObject); when(commandExecutor.executeCommand(listObjectCommandObject)).thenReturn(expectedReadResult); List result = jedis.xread(xReadParams, stream1); assertThat(result, equalTo(expectedReadResult)); verify(commandExecutor).executeCommand(listObjectCommandObject); verify(commandObjects).xread(xReadParams, stream1); } @Test public void testXreadAsMap() { XReadParams xReadParams = new XReadParams().count(2).block(0); Map stream = Collections.singletonMap("mystream", new StreamEntryID("0-0")); Map> expectedResult = new HashMap<>(); when(commandObjects.xreadAsMap(xReadParams, stream)).thenReturn(mapStringListStreamEntryCommandObject); when(commandExecutor.executeCommand(mapStringListStreamEntryCommandObject)).thenReturn(expectedResult); Map> result = jedis.xreadAsMap(xReadParams, stream); assertThat(result, sameInstance(expectedResult)); verify(commandExecutor).executeCommand(mapStringListStreamEntryCommandObject); verify(commandObjects).xreadAsMap(xReadParams, stream); } @Test public void testXreadGroup() { String groupName = "mygroup"; String consumer = "myconsumer"; XReadGroupParams xReadGroupParams = new XReadGroupParams().count(2).block(0); Map streams = Collections.singletonMap("mystream", new StreamEntryID("0-0")); List>> expectedEntries = new ArrayList<>(); when(commandObjects.xreadGroup(groupName, consumer, xReadGroupParams, streams)).thenReturn(listEntryStringListStreamEntryCommandObject); when(commandExecutor.executeCommand(listEntryStringListStreamEntryCommandObject)).thenReturn(expectedEntries); List>> result = jedis.xreadGroup(groupName, consumer, xReadGroupParams, streams); assertThat(result, equalTo(expectedEntries)); verify(commandExecutor).executeCommand(listEntryStringListStreamEntryCommandObject); verify(commandObjects).xreadGroup(groupName, consumer, xReadGroupParams, streams); } @Test public void testXreadGroupBinary() { byte[] groupName = "mygroup".getBytes(); byte[] consumer = "myconsumer".getBytes(); XReadGroupParams xReadGroupParams = new XReadGroupParams().count(2).block(0); Map.Entry stream1 = new AbstractMap.SimpleEntry<>("mystream".getBytes(), "0-0".getBytes()); List expectedReadGroupResult = new ArrayList<>(); when(commandObjects.xreadGroup(groupName, consumer, xReadGroupParams, stream1)).thenReturn(listObjectCommandObject); when(commandExecutor.executeCommand(listObjectCommandObject)).thenReturn(expectedReadGroupResult); List result = jedis.xreadGroup(groupName, consumer, xReadGroupParams, stream1); assertThat(result, equalTo(expectedReadGroupResult)); verify(commandExecutor).executeCommand(listObjectCommandObject); verify(commandObjects).xreadGroup(groupName, consumer, xReadGroupParams, stream1); } @Test public void testXreadGroupAsMap() { String groupName = "mygroup"; String consumer = "myconsumer"; XReadGroupParams xReadGroupParams = new XReadGroupParams().count(2).block(0); Map stream1 = Collections.singletonMap("mystream", new StreamEntryID()); Map> expectedReadGroupAsMapResult = new HashMap<>(); when(commandObjects.xreadGroupAsMap(groupName, consumer, xReadGroupParams, stream1)).thenReturn(mapStringListStreamEntryCommandObject); when(commandExecutor.executeCommand(mapStringListStreamEntryCommandObject)).thenReturn(expectedReadGroupAsMapResult); Map> result = jedis.xreadGroupAsMap(groupName, consumer, xReadGroupParams, stream1); assertThat(result, sameInstance(expectedReadGroupAsMapResult)); verify(commandExecutor).executeCommand(mapStringListStreamEntryCommandObject); verify(commandObjects).xreadGroupAsMap(groupName, consumer, xReadGroupParams, stream1); } @Test public void testXrevrange() { String key = "mystream"; String end = "+"; String start = "-"; Map hash = new HashMap<>(); hash.put("field1", "value1"); hash.put("field2", "value2"); List expectedEntries = Collections.singletonList(new StreamEntry(new StreamEntryID("0-0"), hash)); when(commandObjects.xrevrange(key, end, start)).thenReturn(listStreamEntryCommandObject); when(commandExecutor.executeCommand(listStreamEntryCommandObject)).thenReturn(expectedEntries); List result = jedis.xrevrange(key, end, start); assertThat(result, equalTo(expectedEntries)); verify(commandExecutor).executeCommand(listStreamEntryCommandObject); verify(commandObjects).xrevrange(key, end, start); } @Test public void testXrevrangeBinary() { byte[] key = "mystream".getBytes(); byte[] end = "+".getBytes(); byte[] start = "0-0".getBytes(); List expectedReverseRange = Arrays.asList( new StreamEntry(new StreamEntryID("0-1"), Collections.singletonMap("field2", "value2")), new StreamEntry(new StreamEntryID("0-0"), Collections.singletonMap("field1", "value1"))); when(commandObjects.xrevrange(key, end, start)).thenReturn(listObjectCommandObject); when(commandExecutor.executeCommand(listObjectCommandObject)).thenReturn(expectedReverseRange); List result = jedis.xrevrange(key, end, start); assertThat(result, equalTo(expectedReverseRange)); verify(commandExecutor).executeCommand(listObjectCommandObject); verify(commandObjects).xrevrange(key, end, start); } @Test public void testXrevrangeWithCount() { String key = "mystream"; String end = "+"; String start = "-"; int count = 10; Map hash = new HashMap<>(); hash.put("field1", "value1"); hash.put("field2", "value2"); List expectedEntries = Collections.singletonList(new StreamEntry(new StreamEntryID("0-0"), hash)); when(commandObjects.xrevrange(key, end, start, count)).thenReturn(listStreamEntryCommandObject); when(commandExecutor.executeCommand(listStreamEntryCommandObject)).thenReturn(expectedEntries); List result = jedis.xrevrange(key, end, start, count); assertThat(result, equalTo(expectedEntries)); verify(commandExecutor).executeCommand(listStreamEntryCommandObject); verify(commandObjects).xrevrange(key, end, start, count); } @Test public void testXrevrangeWithCountBinary() { byte[] key = "mystream".getBytes(); byte[] end = "+".getBytes(); byte[] start = "0-0".getBytes(); int count = 1; List expectedReverseRange = Collections.singletonList( new StreamEntry(new StreamEntryID("0-1"), Collections.singletonMap("field2", "value2"))); when(commandObjects.xrevrange(key, end, start, count)).thenReturn(listObjectCommandObject); when(commandExecutor.executeCommand(listObjectCommandObject)).thenReturn(expectedReverseRange); List result = jedis.xrevrange(key, end, start, count); assertThat(result, equalTo(expectedReverseRange)); verify(commandExecutor).executeCommand(listObjectCommandObject); verify(commandObjects).xrevrange(key, end, start, count); } @Test public void testXrevrangeIds() { String key = "mystream"; StreamEntryID end = new StreamEntryID("0-1"); StreamEntryID start = new StreamEntryID("0-0"); Map hash = new HashMap<>(); hash.put("field1", "value1"); hash.put("field2", "value2"); List expectedEntries = Collections.singletonList(new StreamEntry(new StreamEntryID("0-0"), hash)); when(commandObjects.xrevrange(key, end, start)).thenReturn(listStreamEntryCommandObject); when(commandExecutor.executeCommand(listStreamEntryCommandObject)).thenReturn(expectedEntries); List result = jedis.xrevrange(key, end, start); assertThat(result, equalTo(expectedEntries)); verify(commandExecutor).executeCommand(listStreamEntryCommandObject); verify(commandObjects).xrevrange(key, end, start); } @Test public void testXrevrangeIdsWithCount() { String key = "mystream"; StreamEntryID end = new StreamEntryID("0-1"); StreamEntryID start = new StreamEntryID("0-0"); int count = 10; Map hash = new HashMap<>(); hash.put("field1", "value1"); hash.put("field2", "value2"); List expectedEntries = Collections.singletonList(new StreamEntry(new StreamEntryID("0-0"), hash)); when(commandObjects.xrevrange(key, end, start, count)).thenReturn(listStreamEntryCommandObject); when(commandExecutor.executeCommand(listStreamEntryCommandObject)).thenReturn(expectedEntries); List result = jedis.xrevrange(key, end, start, count); assertThat(result, equalTo(expectedEntries)); verify(commandExecutor).executeCommand(listStreamEntryCommandObject); verify(commandObjects).xrevrange(key, end, start, count); } @Test public void testXtrim() { String key = "mystream"; long maxLen = 1000L; boolean approximate = false; long expectedTrimmedCount = 10L; // Assuming 10 entries were trimmed when(commandObjects.xtrim(key, maxLen, approximate)).thenReturn(longCommandObject); when(commandExecutor.executeCommand(longCommandObject)).thenReturn(expectedTrimmedCount); long result = jedis.xtrim(key, maxLen, approximate); assertThat(result, equalTo(expectedTrimmedCount)); verify(commandExecutor).executeCommand(longCommandObject); verify(commandObjects).xtrim(key, maxLen, approximate); } @Test public void testXtrimBinary() { byte[] key = "mystream".getBytes(); long maxLen = 1000L; boolean approximateLength = true; long expectedTrimmed = 10L; when(commandObjects.xtrim(key, maxLen, approximateLength)).thenReturn(longCommandObject); when(commandExecutor.executeCommand(longCommandObject)).thenReturn(expectedTrimmed); long result = jedis.xtrim(key, maxLen, approximateLength); assertThat(result, equalTo(expectedTrimmed)); verify(commandExecutor).executeCommand(longCommandObject); verify(commandObjects).xtrim(key, maxLen, approximateLength); } @Test public void testXtrimWithParams() { String key = "mystream"; XTrimParams params = new XTrimParams().maxLen(1000L); long expectedTrimmedCount = 10L; // Assuming 10 entries were trimmed when(commandObjects.xtrim(key, params)).thenReturn(longCommandObject); when(commandExecutor.executeCommand(longCommandObject)).thenReturn(expectedTrimmedCount); long result = jedis.xtrim(key, params); assertThat(result, equalTo(expectedTrimmedCount)); verify(commandExecutor).executeCommand(longCommandObject); verify(commandObjects).xtrim(key, params); } @Test public void testXtrimWithParamsBinary() { byte[] key = "mystream".getBytes(); XTrimParams params = new XTrimParams().maxLen(1000L); long expectedTrimmed = 10L; when(commandObjects.xtrim(key, params)).thenReturn(longCommandObject); when(commandExecutor.executeCommand(longCommandObject)).thenReturn(expectedTrimmed); long result = jedis.xtrim(key, params); assertThat(result, equalTo(expectedTrimmed)); verify(commandExecutor).executeCommand(longCommandObject); verify(commandObjects).xtrim(key, params); } } ================================================ FILE: src/test/java/redis/clients/jedis/mocked/unified/UnifiedJedisStringCommandsTest.java ================================================ package redis.clients.jedis.mocked.unified; import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.equalTo; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; import java.util.Arrays; import java.util.List; import org.junit.jupiter.api.Test; import redis.clients.jedis.params.GetExParams; import redis.clients.jedis.params.LCSParams; import redis.clients.jedis.params.SetParams; import redis.clients.jedis.params.MSetExParams; import redis.clients.jedis.resps.LCSMatchResult; public class UnifiedJedisStringCommandsTest extends UnifiedJedisMockedTestBase { @Test public void testAppend() { String key = "key"; String value = "value"; long expectedLength = 10L; // Assuming the new length of the string is 10 after append when(commandObjects.append(key, value)).thenReturn(longCommandObject); when(commandExecutor.executeCommand(longCommandObject)).thenReturn(expectedLength); long result = jedis.append(key, value); assertThat(result, equalTo(expectedLength)); verify(commandExecutor).executeCommand(longCommandObject); verify(commandObjects).append(key, value); } @Test public void testAppendBinary() { byte[] key = "key".getBytes(); byte[] value = "value".getBytes(); long expectedLength = 10L; // Assuming the new length of the string is 10 after append when(commandObjects.append(key, value)).thenReturn(longCommandObject); when(commandExecutor.executeCommand(longCommandObject)).thenReturn(expectedLength); long result = jedis.append(key, value); assertThat(result, equalTo(expectedLength)); verify(commandExecutor).executeCommand(longCommandObject); verify(commandObjects).append(key, value); } @Test public void testDecr() { String key = "key"; long expectedValue = -1L; // Assuming the key was decremented successfully when(commandObjects.decr(key)).thenReturn(longCommandObject); when(commandExecutor.executeCommand(longCommandObject)).thenReturn(expectedValue); long result = jedis.decr(key); assertThat(result, equalTo(expectedValue)); verify(commandExecutor).executeCommand(longCommandObject); verify(commandObjects).decr(key); } @Test public void testDecrBinary() { byte[] key = "key".getBytes(); long expectedValue = -1L; // Assuming the key was decremented successfully when(commandObjects.decr(key)).thenReturn(longCommandObject); when(commandExecutor.executeCommand(longCommandObject)).thenReturn(expectedValue); long result = jedis.decr(key); assertThat(result, equalTo(expectedValue)); verify(commandExecutor).executeCommand(longCommandObject); verify(commandObjects).decr(key); } @Test public void testDecrBy() { String key = "key"; long decrement = 2L; long expectedValue = -2L; // Assuming the key was decremented by 2 successfully when(commandObjects.decrBy(key, decrement)).thenReturn(longCommandObject); when(commandExecutor.executeCommand(longCommandObject)).thenReturn(expectedValue); long result = jedis.decrBy(key, decrement); assertThat(result, equalTo(expectedValue)); verify(commandExecutor).executeCommand(longCommandObject); verify(commandObjects).decrBy(key, decrement); } @Test public void testDecrByBinary() { byte[] key = "key".getBytes(); long decrement = 2L; long expectedValue = -2L; // Assuming the key was decremented by 2 successfully when(commandObjects.decrBy(key, decrement)).thenReturn(longCommandObject); when(commandExecutor.executeCommand(longCommandObject)).thenReturn(expectedValue); long result = jedis.decrBy(key, decrement); assertThat(result, equalTo(expectedValue)); verify(commandExecutor).executeCommand(longCommandObject); verify(commandObjects).decrBy(key, decrement); } @Test public void testGet() { String key = "key"; String expectedValue = "value"; when(commandObjects.get(key)).thenReturn(stringCommandObject); when(commandExecutor.executeCommand(stringCommandObject)).thenReturn(expectedValue); String result = jedis.get(key); assertThat(result, equalTo(expectedValue)); verify(commandExecutor).executeCommand(stringCommandObject); verify(commandObjects).get(key); } @Test public void testGetBinary() { byte[] key = "key".getBytes(); byte[] expectedValue = "value".getBytes(); when(commandObjects.get(key)).thenReturn(bytesCommandObject); when(commandExecutor.executeCommand(bytesCommandObject)).thenReturn(expectedValue); byte[] result = jedis.get(key); assertThat(result, equalTo(expectedValue)); verify(commandExecutor).executeCommand(bytesCommandObject); verify(commandObjects).get(key); } @Test public void testGetDel() { String key = "key"; String expectedValue = "value"; when(commandObjects.getDel(key)).thenReturn(stringCommandObject); when(commandExecutor.executeCommand(stringCommandObject)).thenReturn(expectedValue); String result = jedis.getDel(key); assertThat(result, equalTo(expectedValue)); verify(commandExecutor).executeCommand(stringCommandObject); verify(commandObjects).getDel(key); } @Test public void testGetDelBinary() { byte[] key = "key".getBytes(); byte[] expectedValue = "value".getBytes(); when(commandObjects.getDel(key)).thenReturn(bytesCommandObject); when(commandExecutor.executeCommand(bytesCommandObject)).thenReturn(expectedValue); byte[] result = jedis.getDel(key); assertThat(result, equalTo(expectedValue)); verify(commandExecutor).executeCommand(bytesCommandObject); verify(commandObjects).getDel(key); } @Test public void testGetEx() { String key = "key"; GetExParams params = new GetExParams().ex(10); String expectedValue = "value"; when(commandObjects.getEx(key, params)).thenReturn(stringCommandObject); when(commandExecutor.executeCommand(stringCommandObject)).thenReturn(expectedValue); String result = jedis.getEx(key, params); assertThat(result, equalTo(expectedValue)); verify(commandExecutor).executeCommand(stringCommandObject); verify(commandObjects).getEx(key, params); } @Test public void testGetExBinary() { byte[] key = "key".getBytes(); GetExParams params = new GetExParams().ex(10); byte[] expectedValue = "value".getBytes(); when(commandObjects.getEx(key, params)).thenReturn(bytesCommandObject); when(commandExecutor.executeCommand(bytesCommandObject)).thenReturn(expectedValue); byte[] result = jedis.getEx(key, params); assertThat(result, equalTo(expectedValue)); verify(commandExecutor).executeCommand(bytesCommandObject); verify(commandObjects).getEx(key, params); } @Test public void testGetrange() { String key = "key"; long startOffset = 0L; long endOffset = 10L; String expectedResponse = "value"; when(commandObjects.getrange(key, startOffset, endOffset)).thenReturn(stringCommandObject); when(commandExecutor.executeCommand(stringCommandObject)).thenReturn(expectedResponse); String result = jedis.getrange(key, startOffset, endOffset); assertThat(result, equalTo(expectedResponse)); verify(commandExecutor).executeCommand(stringCommandObject); verify(commandObjects).getrange(key, startOffset, endOffset); } @Test public void testGetrangeBinary() { byte[] key = "key".getBytes(); long startOffset = 0L; long endOffset = 10L; byte[] expectedResponse = "value".getBytes(); when(commandObjects.getrange(key, startOffset, endOffset)).thenReturn(bytesCommandObject); when(commandExecutor.executeCommand(bytesCommandObject)).thenReturn(expectedResponse); byte[] result = jedis.getrange(key, startOffset, endOffset); assertThat(result, equalTo(expectedResponse)); verify(commandExecutor).executeCommand(bytesCommandObject); verify(commandObjects).getrange(key, startOffset, endOffset); } @Test public void testGetSet() { String key = "key"; String value = "newValue"; String expectedPreviousValue = "oldValue"; when(commandObjects.getSet(key, value)).thenReturn(stringCommandObject); when(commandExecutor.executeCommand(stringCommandObject)).thenReturn(expectedPreviousValue); String result = jedis.getSet(key, value); assertThat(result, equalTo(expectedPreviousValue)); verify(commandExecutor).executeCommand(stringCommandObject); verify(commandObjects).getSet(key, value); } @Test public void testGetSetBinary() { byte[] key = "key".getBytes(); byte[] value = "newValue".getBytes(); byte[] expectedPreviousValue = "oldValue".getBytes(); when(commandObjects.getSet(key, value)).thenReturn(bytesCommandObject); when(commandExecutor.executeCommand(bytesCommandObject)).thenReturn(expectedPreviousValue); byte[] result = jedis.getSet(key, value); assertThat(result, equalTo(expectedPreviousValue)); verify(commandExecutor).executeCommand(bytesCommandObject); verify(commandObjects).getSet(key, value); } @Test public void testIncr() { String key = "key"; long expectedValue = 1L; when(commandObjects.incr(key)).thenReturn(longCommandObject); when(commandExecutor.executeCommand(longCommandObject)).thenReturn(expectedValue); long result = jedis.incr(key); assertThat(result, equalTo(expectedValue)); verify(commandExecutor).executeCommand(longCommandObject); verify(commandObjects).incr(key); } @Test public void testIncrBinary() { byte[] key = "key".getBytes(); long expectedValue = 1L; // Assuming the key was incremented successfully when(commandObjects.incr(key)).thenReturn(longCommandObject); when(commandExecutor.executeCommand(longCommandObject)).thenReturn(expectedValue); long result = jedis.incr(key); assertThat(result, equalTo(expectedValue)); verify(commandExecutor).executeCommand(longCommandObject); verify(commandObjects).incr(key); } @Test public void testIncrBy() { String key = "key"; long increment = 2L; long expectedValue = 3L; // Assuming the key was incremented by 2 successfully when(commandObjects.incrBy(key, increment)).thenReturn(longCommandObject); when(commandExecutor.executeCommand(longCommandObject)).thenReturn(expectedValue); long result = jedis.incrBy(key, increment); assertThat(result, equalTo(expectedValue)); verify(commandExecutor).executeCommand(longCommandObject); verify(commandObjects).incrBy(key, increment); } @Test public void testIncrByBinary() { byte[] key = "key".getBytes(); long increment = 2L; long expectedValue = 3L; // Assuming the key was incremented by 2 successfully when(commandObjects.incrBy(key, increment)).thenReturn(longCommandObject); when(commandExecutor.executeCommand(longCommandObject)).thenReturn(expectedValue); long result = jedis.incrBy(key, increment); assertThat(result, equalTo(expectedValue)); verify(commandExecutor).executeCommand(longCommandObject); verify(commandObjects).incrBy(key, increment); } @Test public void testIncrByFloat() { String key = "key"; double increment = 2.5; double expectedValue = 3.5; // Assuming the key was incremented by 2.5 successfully when(commandObjects.incrByFloat(key, increment)).thenReturn(doubleCommandObject); when(commandExecutor.executeCommand(doubleCommandObject)).thenReturn(expectedValue); double result = jedis.incrByFloat(key, increment); assertThat(result, equalTo(expectedValue)); verify(commandExecutor).executeCommand(doubleCommandObject); verify(commandObjects).incrByFloat(key, increment); } @Test public void testIncrByFloatBinary() { byte[] key = "key".getBytes(); double increment = 2.5; double expectedValue = 3.5; // Assuming the key was incremented by 2.5 successfully when(commandObjects.incrByFloat(key, increment)).thenReturn(doubleCommandObject); when(commandExecutor.executeCommand(doubleCommandObject)).thenReturn(expectedValue); double result = jedis.incrByFloat(key, increment); assertThat(result, equalTo(expectedValue)); verify(commandExecutor).executeCommand(doubleCommandObject); verify(commandObjects).incrByFloat(key, increment); } @Test public void testLcs() { String keyA = "keyA"; String keyB = "keyB"; LCSParams params = new LCSParams().withMatchLen(); LCSMatchResult expectedResult = new LCSMatchResult(5); // Assuming the LCS length is 5 when(commandObjects.lcs(keyA, keyB, params)).thenReturn(lcsMatchResultCommandObject); when(commandExecutor.executeCommand(lcsMatchResultCommandObject)).thenReturn(expectedResult); LCSMatchResult result = jedis.lcs(keyA, keyB, params); assertThat(result, equalTo(expectedResult)); verify(commandExecutor).executeCommand(lcsMatchResultCommandObject); verify(commandObjects).lcs(keyA, keyB, params); } @Test public void testLcsBinary() { byte[] keyA = "keyA".getBytes(); byte[] keyB = "keyB".getBytes(); LCSParams params = new LCSParams().withMatchLen(); LCSMatchResult expectedResult = new LCSMatchResult(5); // Assuming the LCS length is 5 when(commandObjects.lcs(keyA, keyB, params)).thenReturn(lcsMatchResultCommandObject); when(commandExecutor.executeCommand(lcsMatchResultCommandObject)).thenReturn(expectedResult); LCSMatchResult result = jedis.lcs(keyA, keyB, params); assertThat(result, equalTo(expectedResult)); verify(commandExecutor).executeCommand(lcsMatchResultCommandObject); verify(commandObjects).lcs(keyA, keyB, params); } @Test public void testMget() { String[] keys = { "key1", "key2", "key3" }; List expectedValues = Arrays.asList("value1", "value2", "value3"); when(commandObjects.mget(keys)).thenReturn(listStringCommandObject); when(commandExecutor.executeCommand(listStringCommandObject)).thenReturn(expectedValues); List result = jedis.mget(keys); assertThat(result, equalTo(expectedValues)); verify(commandExecutor).executeCommand(listStringCommandObject); verify(commandObjects).mget(keys); } @Test public void testMgetBinary() { byte[][] keys = { "key1".getBytes(), "key2".getBytes(), "key3".getBytes() }; List expectedValues = Arrays.asList("value1".getBytes(), "value2".getBytes(), "value3".getBytes()); when(commandObjects.mget(keys)).thenReturn(listBytesCommandObject); when(commandExecutor.executeCommand(listBytesCommandObject)).thenReturn(expectedValues); List result = jedis.mget(keys); assertThat(result, equalTo(expectedValues)); verify(commandExecutor).executeCommand(listBytesCommandObject); verify(commandObjects).mget(keys); } @Test public void testMset() { String[] keysvalues = { "key1", "value1", "key2", "value2" }; String expectedResponse = "OK"; when(commandObjects.mset(keysvalues)).thenReturn(stringCommandObject); when(commandExecutor.executeCommand(stringCommandObject)).thenReturn(expectedResponse); String result = jedis.mset(keysvalues); assertThat(result, equalTo(expectedResponse)); verify(commandExecutor).executeCommand(stringCommandObject); verify(commandObjects).mset(keysvalues); } @Test public void testMsetBinary() { byte[][] keysvalues = { "key1".getBytes(), "value1".getBytes(), "key2".getBytes(), "value2".getBytes() }; String expectedResponse = "OK"; when(commandObjects.mset(keysvalues)).thenReturn(stringCommandObject); when(commandExecutor.executeCommand(stringCommandObject)).thenReturn(expectedResponse); String result = jedis.mset(keysvalues); assertThat(result, equalTo(expectedResponse)); verify(commandExecutor).executeCommand(stringCommandObject); verify(commandObjects).mset(keysvalues); } @Test public void testMsetnx() { String[] keysvalues = { "key1", "value1", "key2", "value2" }; long expectedResponse = 1L; // Assuming the keys were set successfully when(commandObjects.msetnx(keysvalues)).thenReturn(longCommandObject); when(commandExecutor.executeCommand(longCommandObject)).thenReturn(expectedResponse); long result = jedis.msetnx(keysvalues); assertThat(result, equalTo(expectedResponse)); verify(commandExecutor).executeCommand(longCommandObject); verify(commandObjects).msetnx(keysvalues); } @Test public void testMsetnxBinary() { byte[][] keysvalues = { "key1".getBytes(), "value1".getBytes(), "key2".getBytes(), "value2".getBytes() }; long expectedResponse = 1L; // Assuming the keys were set successfully when(commandObjects.msetnx(keysvalues)).thenReturn(longCommandObject); when(commandExecutor.executeCommand(longCommandObject)).thenReturn(expectedResponse); long result = jedis.msetnx(keysvalues); assertThat(result, equalTo(expectedResponse)); verify(commandExecutor).executeCommand(longCommandObject); verify(commandObjects).msetnx(keysvalues); } @Test public void testMsetexDelegates() { MSetExParams params = new MSetExParams().nx().ex(5); String[] keysvalues = { "k1", "v1", "k2", "v2" }; when(commandObjects.msetex(params, keysvalues)).thenReturn(booleanCommandObject); when(commandExecutor.executeCommand(booleanCommandObject)).thenReturn(true); boolean result = jedis.msetex(params, keysvalues); assertThat(result, equalTo(true)); verify(commandExecutor).executeCommand(booleanCommandObject); verify(commandObjects).msetex(params, keysvalues); } @Test public void testMsetexBinaryDelegates() { MSetExParams params = new MSetExParams().xx().keepTtl(); byte[][] keysvalues = { "k1".getBytes(), "v1".getBytes(), "k2".getBytes(), "v2".getBytes() }; when(commandObjects.msetex(params, keysvalues)).thenReturn(booleanCommandObject); when(commandExecutor.executeCommand(booleanCommandObject)).thenReturn(false); boolean result = jedis.msetex(params, keysvalues); assertThat(result, equalTo(false)); verify(commandExecutor).executeCommand(booleanCommandObject); verify(commandObjects).msetex(params, keysvalues); } @Test public void testPsetex() { String key = "key"; long milliseconds = 1000L; String value = "value"; String expectedResponse = "OK"; when(commandObjects.psetex(key, milliseconds, value)).thenReturn(stringCommandObject); when(commandExecutor.executeCommand(stringCommandObject)).thenReturn(expectedResponse); String result = jedis.psetex(key, milliseconds, value); assertThat(result, equalTo(expectedResponse)); verify(commandExecutor).executeCommand(stringCommandObject); verify(commandObjects).psetex(key, milliseconds, value); } @Test public void testPsetexBinary() { byte[] key = "key".getBytes(); long milliseconds = 1000L; byte[] value = "value".getBytes(); String expectedResponse = "OK"; when(commandObjects.psetex(key, milliseconds, value)).thenReturn(stringCommandObject); when(commandExecutor.executeCommand(stringCommandObject)).thenReturn(expectedResponse); String result = jedis.psetex(key, milliseconds, value); assertThat(result, equalTo(expectedResponse)); verify(commandExecutor).executeCommand(stringCommandObject); verify(commandObjects).psetex(key, milliseconds, value); } @Test public void testSet() { String key = "key"; String value = "value"; String expectedResponse = "OK"; when(commandObjects.set(key, value)).thenReturn(stringCommandObject); when(commandExecutor.executeCommand(stringCommandObject)).thenReturn(expectedResponse); String result = jedis.set(key, value); assertThat(result, equalTo(expectedResponse)); verify(commandExecutor).executeCommand(stringCommandObject); verify(commandObjects).set(key, value); } @Test public void testSetBinary() { byte[] key = "key".getBytes(); byte[] value = "value".getBytes(); String expectedResponse = "OK"; when(commandObjects.set(key, value)).thenReturn(stringCommandObject); when(commandExecutor.executeCommand(stringCommandObject)).thenReturn(expectedResponse); String result = jedis.set(key, value); assertThat(result, equalTo(expectedResponse)); verify(commandExecutor).executeCommand(stringCommandObject); verify(commandObjects).set(key, value); } @Test public void testSetWithParams() { String key = "key"; String value = "value"; SetParams params = new SetParams().nx().ex(10); String expectedResponse = "OK"; when(commandObjects.set(key, value, params)).thenReturn(stringCommandObject); when(commandExecutor.executeCommand(stringCommandObject)).thenReturn(expectedResponse); String result = jedis.set(key, value, params); assertThat(result, equalTo(expectedResponse)); verify(commandExecutor).executeCommand(stringCommandObject); verify(commandObjects).set(key, value, params); } @Test public void testSetWithParamsBinary() { byte[] key = "key".getBytes(); byte[] value = "value".getBytes(); SetParams params = new SetParams().nx().ex(10); String expectedResponse = "OK"; when(commandObjects.set(key, value, params)).thenReturn(stringCommandObject); when(commandExecutor.executeCommand(stringCommandObject)).thenReturn(expectedResponse); String result = jedis.set(key, value, params); assertThat(result, equalTo(expectedResponse)); verify(commandExecutor).executeCommand(stringCommandObject); verify(commandObjects).set(key, value, params); } @Test public void testSetGet() { String key = "key"; String value = "value"; String expectedPreviousValue = "previousValue"; when(commandObjects.setGet(key, value)).thenReturn(stringCommandObject); when(commandExecutor.executeCommand(stringCommandObject)).thenReturn(expectedPreviousValue); String result = jedis.setGet(key, value); assertThat(result, equalTo(expectedPreviousValue)); verify(commandExecutor).executeCommand(stringCommandObject); verify(commandObjects).setGet(key, value); } @Test public void testSetGetBinary() { byte[] key = "key".getBytes(); byte[] value = "value".getBytes(); byte[] expectedPreviousValue = "previousValue".getBytes(); when(commandObjects.setGet(key, value)).thenReturn(bytesCommandObject); when(commandExecutor.executeCommand(bytesCommandObject)).thenReturn(expectedPreviousValue); byte[] result = jedis.setGet(key, value); assertThat(result, equalTo(expectedPreviousValue)); verify(commandExecutor).executeCommand(bytesCommandObject); verify(commandObjects).setGet(key, value); } @Test public void testSetGetWithParams() { String key = "key"; String value = "value"; SetParams params = new SetParams().nx().ex(10); String expectedPreviousValue = "previousValue"; when(commandObjects.setGet(key, value, params)).thenReturn(stringCommandObject); when(commandExecutor.executeCommand(stringCommandObject)).thenReturn(expectedPreviousValue); String result = jedis.setGet(key, value, params); assertThat(result, equalTo(expectedPreviousValue)); verify(commandExecutor).executeCommand(stringCommandObject); verify(commandObjects).setGet(key, value, params); } @Test public void testSetGetWithParamsBinary() { byte[] key = "key".getBytes(); byte[] value = "value".getBytes(); SetParams params = new SetParams().nx().ex(10); byte[] expectedPreviousValue = "previousValue".getBytes(); when(commandObjects.setGet(key, value, params)).thenReturn(bytesCommandObject); when(commandExecutor.executeCommand(bytesCommandObject)).thenReturn(expectedPreviousValue); byte[] result = jedis.setGet(key, value, params); assertThat(result, equalTo(expectedPreviousValue)); verify(commandExecutor).executeCommand(bytesCommandObject); verify(commandObjects).setGet(key, value, params); } @Test public void testSetex() { String key = "key"; long seconds = 60L; String value = "value"; String expectedResponse = "OK"; when(commandObjects.setex(key, seconds, value)).thenReturn(stringCommandObject); when(commandExecutor.executeCommand(stringCommandObject)).thenReturn(expectedResponse); String result = jedis.setex(key, seconds, value); assertThat(result, equalTo(expectedResponse)); verify(commandExecutor).executeCommand(stringCommandObject); verify(commandObjects).setex(key, seconds, value); } @Test public void testSetexBinary() { byte[] key = "key".getBytes(); long seconds = 60L; byte[] value = "value".getBytes(); String expectedResponse = "OK"; when(commandObjects.setex(key, seconds, value)).thenReturn(stringCommandObject); when(commandExecutor.executeCommand(stringCommandObject)).thenReturn(expectedResponse); String result = jedis.setex(key, seconds, value); assertThat(result, equalTo(expectedResponse)); verify(commandExecutor).executeCommand(stringCommandObject); verify(commandObjects).setex(key, seconds, value); } @Test public void testSetnx() { String key = "key"; String value = "value"; long expectedResponse = 1L; // Assuming the key was set successfully when(commandObjects.setnx(key, value)).thenReturn(longCommandObject); when(commandExecutor.executeCommand(longCommandObject)).thenReturn(expectedResponse); long result = jedis.setnx(key, value); assertThat(result, equalTo(expectedResponse)); verify(commandExecutor).executeCommand(longCommandObject); verify(commandObjects).setnx(key, value); } @Test public void testSetnxBinary() { byte[] key = "key".getBytes(); byte[] value = "value".getBytes(); long expectedResponse = 1L; // Assuming the key was set successfully when(commandObjects.setnx(key, value)).thenReturn(longCommandObject); when(commandExecutor.executeCommand(longCommandObject)).thenReturn(expectedResponse); long result = jedis.setnx(key, value); assertThat(result, equalTo(expectedResponse)); verify(commandExecutor).executeCommand(longCommandObject); verify(commandObjects).setnx(key, value); } @Test public void testSetrange() { String key = "key"; long offset = 10L; String value = "value"; long expectedResponse = value.length(); when(commandObjects.setrange(key, offset, value)).thenReturn(longCommandObject); when(commandExecutor.executeCommand(longCommandObject)).thenReturn(expectedResponse); long result = jedis.setrange(key, offset, value); assertThat(result, equalTo(expectedResponse)); verify(commandExecutor).executeCommand(longCommandObject); verify(commandObjects).setrange(key, offset, value); } @Test public void testSetrangeBinary() { byte[] key = "key".getBytes(); long offset = 10L; byte[] value = "value".getBytes(); long expectedResponse = value.length; when(commandObjects.setrange(key, offset, value)).thenReturn(longCommandObject); when(commandExecutor.executeCommand(longCommandObject)).thenReturn(expectedResponse); long result = jedis.setrange(key, offset, value); assertThat(result, equalTo(expectedResponse)); verify(commandExecutor).executeCommand(longCommandObject); verify(commandObjects).setrange(key, offset, value); } @Test public void testStrlen() { String key = "key"; long expectedLength = 5L; // Assuming the length of the string value is 5 when(commandObjects.strlen(key)).thenReturn(longCommandObject); when(commandExecutor.executeCommand(longCommandObject)).thenReturn(expectedLength); long result = jedis.strlen(key); assertThat(result, equalTo(expectedLength)); verify(commandExecutor).executeCommand(longCommandObject); verify(commandObjects).strlen(key); } @Test public void testStrlenBinary() { byte[] key = "key".getBytes(); long expectedLength = 5L; // Assuming the length of the string value is 5 when(commandObjects.strlen(key)).thenReturn(longCommandObject); when(commandExecutor.executeCommand(longCommandObject)).thenReturn(expectedLength); long result = jedis.strlen(key); assertThat(result, equalTo(expectedLength)); verify(commandExecutor).executeCommand(longCommandObject); verify(commandObjects).strlen(key); } @Test public void testSubstr() { String key = "key"; int start = 0; int end = 3; String expectedSubstring = "valu"; when(commandObjects.substr(key, start, end)).thenReturn(stringCommandObject); when(commandExecutor.executeCommand(stringCommandObject)).thenReturn(expectedSubstring); String result = jedis.substr(key, start, end); assertThat(result, equalTo(expectedSubstring)); verify(commandExecutor).executeCommand(stringCommandObject); verify(commandObjects).substr(key, start, end); } @Test public void testSubstrBinary() { byte[] key = "key".getBytes(); int start = 0; int end = 3; byte[] expectedSubstring = "valu".getBytes(); when(commandObjects.substr(key, start, end)).thenReturn(bytesCommandObject); when(commandExecutor.executeCommand(bytesCommandObject)).thenReturn(expectedSubstring); byte[] result = jedis.substr(key, start, end); assertThat(result, equalTo(expectedSubstring)); verify(commandExecutor).executeCommand(bytesCommandObject); verify(commandObjects).substr(key, start, end); } } ================================================ FILE: src/test/java/redis/clients/jedis/mocked/unified/UnifiedJedisTDigestCommandsTest.java ================================================ package redis.clients.jedis.mocked.unified; import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.equalTo; import static org.hamcrest.Matchers.sameInstance; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; import java.util.Arrays; import java.util.HashMap; import java.util.List; import java.util.Map; import org.junit.jupiter.api.Test; import redis.clients.jedis.bloom.TDigestMergeParams; public class UnifiedJedisTDigestCommandsTest extends UnifiedJedisMockedTestBase { @Test public void testTdigestAdd() { String key = "testTDigest"; double[] values = { 1.0, 2.0, 3.0 }; String expectedResponse = "OK"; when(commandObjects.tdigestAdd(key, values)).thenReturn(stringCommandObject); when(commandExecutor.executeCommand(stringCommandObject)).thenReturn(expectedResponse); String result = jedis.tdigestAdd(key, values); assertThat(result, sameInstance(expectedResponse)); verify(commandExecutor).executeCommand(stringCommandObject); verify(commandObjects).tdigestAdd(key, values); } @Test public void testTdigestByRank() { String key = "testTDigest"; long[] ranks = { 1, 2 }; List expectedResponse = Arrays.asList(0.1, 0.2); when(commandObjects.tdigestByRank(key, ranks)).thenReturn(listDoubleCommandObject); when(commandExecutor.executeCommand(listDoubleCommandObject)).thenReturn(expectedResponse); List result = jedis.tdigestByRank(key, ranks); assertThat(result, sameInstance(expectedResponse)); verify(commandExecutor).executeCommand(listDoubleCommandObject); verify(commandObjects).tdigestByRank(key, ranks); } @Test public void testTdigestByRevRank() { String key = "testTDigest"; long[] ranks = { 1, 2 }; List expectedResponse = Arrays.asList(9.9, 9.8); when(commandObjects.tdigestByRevRank(key, ranks)).thenReturn(listDoubleCommandObject); when(commandExecutor.executeCommand(listDoubleCommandObject)).thenReturn(expectedResponse); List result = jedis.tdigestByRevRank(key, ranks); assertThat(result, sameInstance(expectedResponse)); verify(commandExecutor).executeCommand(listDoubleCommandObject); verify(commandObjects).tdigestByRevRank(key, ranks); } @Test public void testTdigestCDF() { String key = "testTDigest"; double[] values = { 0.5, 0.9 }; List expectedResponse = Arrays.asList(0.1, 0.95); when(commandObjects.tdigestCDF(key, values)).thenReturn(listDoubleCommandObject); when(commandExecutor.executeCommand(listDoubleCommandObject)).thenReturn(expectedResponse); List result = jedis.tdigestCDF(key, values); assertThat(result, sameInstance(expectedResponse)); verify(commandExecutor).executeCommand(listDoubleCommandObject); verify(commandObjects).tdigestCDF(key, values); } @Test public void testTdigestCreate() { String key = "testTDigest"; String expectedResponse = "OK"; when(commandObjects.tdigestCreate(key)).thenReturn(stringCommandObject); when(commandExecutor.executeCommand(stringCommandObject)).thenReturn(expectedResponse); String result = jedis.tdigestCreate(key); assertThat(result, sameInstance(expectedResponse)); verify(commandExecutor).executeCommand(stringCommandObject); verify(commandObjects).tdigestCreate(key); } @Test public void testTdigestCreateWithCompression() { String key = "testTDigest"; int compression = 100; String expectedResponse = "OK"; when(commandObjects.tdigestCreate(key, compression)).thenReturn(stringCommandObject); when(commandExecutor.executeCommand(stringCommandObject)).thenReturn(expectedResponse); String result = jedis.tdigestCreate(key, compression); assertThat(result, sameInstance(expectedResponse)); verify(commandExecutor).executeCommand(stringCommandObject); verify(commandObjects).tdigestCreate(key, compression); } @Test public void testTdigestInfo() { String key = "testTDigest"; Map expectedResponse = new HashMap<>(); expectedResponse.put("compression", 100); expectedResponse.put("capacity", 1000); expectedResponse.put("merged_nodes", 500); expectedResponse.put("unmerged_nodes", 50); expectedResponse.put("total_compressions", 10); when(commandObjects.tdigestInfo(key)).thenReturn(mapStringObjectCommandObject); when(commandExecutor.executeCommand(mapStringObjectCommandObject)).thenReturn(expectedResponse); Map result = jedis.tdigestInfo(key); assertThat(result, sameInstance(expectedResponse)); verify(commandExecutor).executeCommand(mapStringObjectCommandObject); verify(commandObjects).tdigestInfo(key); } @Test public void testTdigestMax() { String key = "testTDigest"; double expectedResponse = 10.0; when(commandObjects.tdigestMax(key)).thenReturn(doubleCommandObject); when(commandExecutor.executeCommand(doubleCommandObject)).thenReturn(expectedResponse); double result = jedis.tdigestMax(key); assertThat(result, equalTo(expectedResponse)); verify(commandExecutor).executeCommand(doubleCommandObject); verify(commandObjects).tdigestMax(key); } @Test public void testTdigestMerge() { String destinationKey = "destTDigest"; String[] sourceKeys = { "sourceTDigest1", "sourceTDigest2" }; String expectedResponse = "OK"; when(commandObjects.tdigestMerge(destinationKey, sourceKeys)).thenReturn(stringCommandObject); when(commandExecutor.executeCommand(stringCommandObject)).thenReturn(expectedResponse); String result = jedis.tdigestMerge(destinationKey, sourceKeys); assertThat(result, sameInstance(expectedResponse)); verify(commandExecutor).executeCommand(stringCommandObject); verify(commandObjects).tdigestMerge(destinationKey, sourceKeys); } @Test public void testTdigestMergeWithParams() { TDigestMergeParams mergeParams = new TDigestMergeParams().compression(200); String destinationKey = "destTDigest"; String[] sourceKeys = { "sourceTDigest1", "sourceTDigest2" }; String expectedResponse = "OK"; when(commandObjects.tdigestMerge(mergeParams, destinationKey, sourceKeys)).thenReturn(stringCommandObject); when(commandExecutor.executeCommand(stringCommandObject)).thenReturn(expectedResponse); String result = jedis.tdigestMerge(mergeParams, destinationKey, sourceKeys); assertThat(result, sameInstance(expectedResponse)); verify(commandExecutor).executeCommand(stringCommandObject); verify(commandObjects).tdigestMerge(mergeParams, destinationKey, sourceKeys); } @Test public void testTdigestMin() { String key = "testTDigest"; double expectedResponse = 0.1; when(commandObjects.tdigestMin(key)).thenReturn(doubleCommandObject); when(commandExecutor.executeCommand(doubleCommandObject)).thenReturn(expectedResponse); double result = jedis.tdigestMin(key); assertThat(result, equalTo(expectedResponse)); verify(commandExecutor).executeCommand(doubleCommandObject); verify(commandObjects).tdigestMin(key); } @Test public void testTdigestQuantile() { String key = "testTDigest"; double[] quantiles = { 0.1, 0.5, 0.9 }; List expectedResponse = Arrays.asList(1.0, 2.0, 3.0); when(commandObjects.tdigestQuantile(key, quantiles)).thenReturn(listDoubleCommandObject); when(commandExecutor.executeCommand(listDoubleCommandObject)).thenReturn(expectedResponse); List result = jedis.tdigestQuantile(key, quantiles); assertThat(result, sameInstance(expectedResponse)); verify(commandExecutor).executeCommand(listDoubleCommandObject); verify(commandObjects).tdigestQuantile(key, quantiles); } @Test public void testTdigestRank() { String key = "testTDigest"; double[] values = { 1.0, 2.0 }; List expectedResponse = Arrays.asList(10L, 20L); when(commandObjects.tdigestRank(key, values)).thenReturn(listLongCommandObject); when(commandExecutor.executeCommand(listLongCommandObject)).thenReturn(expectedResponse); List result = jedis.tdigestRank(key, values); assertThat(result, sameInstance(expectedResponse)); verify(commandExecutor).executeCommand(listLongCommandObject); verify(commandObjects).tdigestRank(key, values); } @Test public void testTdigestReset() { String key = "testTDigest"; String expectedResponse = "OK"; when(commandObjects.tdigestReset(key)).thenReturn(stringCommandObject); when(commandExecutor.executeCommand(stringCommandObject)).thenReturn(expectedResponse); String result = jedis.tdigestReset(key); assertThat(result, sameInstance(expectedResponse)); verify(commandExecutor).executeCommand(stringCommandObject); verify(commandObjects).tdigestReset(key); } @Test public void testTdigestRevRank() { String key = "testTDigest"; double[] values = { 1.0, 2.0 }; List expectedResponse = Arrays.asList(90L, 80L); when(commandObjects.tdigestRevRank(key, values)).thenReturn(listLongCommandObject); when(commandExecutor.executeCommand(listLongCommandObject)).thenReturn(expectedResponse); List result = jedis.tdigestRevRank(key, values); assertThat(result, sameInstance(expectedResponse)); verify(commandExecutor).executeCommand(listLongCommandObject); verify(commandObjects).tdigestRevRank(key, values); } @Test public void testTdigestTrimmedMean() { String key = "testTDigest"; double lowCutQuantile = 0.1; double highCutQuantile = 0.9; double expectedResponse = 5.0; when(commandObjects.tdigestTrimmedMean(key, lowCutQuantile, highCutQuantile)).thenReturn(doubleCommandObject); when(commandExecutor.executeCommand(doubleCommandObject)).thenReturn(expectedResponse); double result = jedis.tdigestTrimmedMean(key, lowCutQuantile, highCutQuantile); assertThat(result, equalTo(expectedResponse)); verify(commandExecutor).executeCommand(doubleCommandObject); verify(commandObjects).tdigestTrimmedMean(key, lowCutQuantile, highCutQuantile); } } ================================================ FILE: src/test/java/redis/clients/jedis/mocked/unified/UnifiedJedisTimeSeriesCommandsTest.java ================================================ package redis.clients.jedis.mocked.unified; import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.sameInstance; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; import java.util.AbstractMap; import java.util.Arrays; import java.util.Collections; import java.util.HashMap; import java.util.List; import java.util.Map; import org.junit.jupiter.api.Test; import redis.clients.jedis.timeseries.*; public class UnifiedJedisTimeSeriesCommandsTest extends UnifiedJedisMockedTestBase { @Test public void testTsAdd() { String key = "testKey"; double value = 123.45; long expectedResponse = 1582605077000L; // Timestamp of the added value when(commandObjects.tsAdd(key, value)).thenReturn(longCommandObject); when(commandExecutor.executeCommand(longCommandObject)).thenReturn(expectedResponse); long result = jedis.tsAdd(key, value); assertEquals(expectedResponse, result); verify(commandExecutor).executeCommand(longCommandObject); verify(commandObjects).tsAdd(key, value); } @Test public void testTsAddWithTimestamp() { String key = "testKey"; long timestamp = 1582605077000L; double value = 123.45; long expectedResponse = timestamp; // Timestamp of the added value when(commandObjects.tsAdd(key, timestamp, value)).thenReturn(longCommandObject); when(commandExecutor.executeCommand(longCommandObject)).thenReturn(expectedResponse); long result = jedis.tsAdd(key, timestamp, value); assertEquals(expectedResponse, result); verify(commandExecutor).executeCommand(longCommandObject); verify(commandObjects).tsAdd(key, timestamp, value); } @Test public void testTsAddWithTimestampAndParams() { String key = "testKey"; long timestamp = 1582605077000L; double value = 123.45; TSCreateParams createParams = new TSCreateParams().retention(86400000L); // 1 day retention long expectedResponse = timestamp; // Timestamp of the added value when(commandObjects.tsAdd(key, timestamp, value, createParams)).thenReturn(longCommandObject); when(commandExecutor.executeCommand(longCommandObject)).thenReturn(expectedResponse); long result = jedis.tsAdd(key, timestamp, value, createParams); assertEquals(expectedResponse, result); verify(commandExecutor).executeCommand(longCommandObject); verify(commandObjects).tsAdd(key, timestamp, value, createParams); } @Test public void testTsAddWithParams() { String key = "testKey"; long timestamp = 1582605077000L; double value = 123.45; TSAddParams createParams = mock(TSAddParams.class); long expectedResponse = timestamp; // Timestamp of the added value when(commandObjects.tsAdd(key, timestamp, value, createParams)).thenReturn(longCommandObject); when(commandExecutor.executeCommand(longCommandObject)).thenReturn(expectedResponse); long result = jedis.tsAdd(key, timestamp, value, createParams); assertEquals(expectedResponse, result); verify(commandExecutor).executeCommand(longCommandObject); verify(commandObjects).tsAdd(key, timestamp, value, createParams); } @Test public void testTsAlter() { String key = "testKey"; TSAlterParams alterParams = new TSAlterParams().retention(86400000L); // 1 day retention String expectedResponse = "OK"; when(commandObjects.tsAlter(key, alterParams)).thenReturn(stringCommandObject); when(commandExecutor.executeCommand(stringCommandObject)).thenReturn(expectedResponse); String result = jedis.tsAlter(key, alterParams); assertEquals(expectedResponse, result); verify(commandExecutor).executeCommand(stringCommandObject); verify(commandObjects).tsAlter(key, alterParams); } @Test public void testTsCreate() { String key = "testKey"; String expectedResponse = "OK"; when(commandObjects.tsCreate(key)).thenReturn(stringCommandObject); when(commandExecutor.executeCommand(stringCommandObject)).thenReturn(expectedResponse); String result = jedis.tsCreate(key); assertEquals(expectedResponse, result); verify(commandExecutor).executeCommand(stringCommandObject); verify(commandObjects).tsCreate(key); } @Test public void testTsCreateWithParams() { String key = "testKey"; TSCreateParams createParams = new TSCreateParams().retention(86400000L); // 1 day retention String expectedResponse = "OK"; when(commandObjects.tsCreate(key, createParams)).thenReturn(stringCommandObject); when(commandExecutor.executeCommand(stringCommandObject)).thenReturn(expectedResponse); String result = jedis.tsCreate(key, createParams); assertEquals(expectedResponse, result); verify(commandExecutor).executeCommand(stringCommandObject); verify(commandObjects).tsCreate(key, createParams); } @Test public void testTsCreateRule() { String sourceKey = "sourceKey"; String destKey = "destKey"; AggregationType aggregationType = AggregationType.AVG; long timeBucket = 60000L; // 1 minute String expectedResponse = "OK"; when(commandObjects.tsCreateRule(sourceKey, destKey, aggregationType, timeBucket)).thenReturn(stringCommandObject); when(commandExecutor.executeCommand(stringCommandObject)).thenReturn(expectedResponse); String result = jedis.tsCreateRule(sourceKey, destKey, aggregationType, timeBucket); assertEquals(expectedResponse, result); verify(commandExecutor).executeCommand(stringCommandObject); verify(commandObjects).tsCreateRule(sourceKey, destKey, aggregationType, timeBucket); } @Test public void testTsCreateRuleWithAlignTimestamp() { String sourceKey = "sourceKey"; String destKey = "destKey"; AggregationType aggregationType = AggregationType.AVG; long bucketDuration = 60000L; // 1 minute long alignTimestamp = 1582600000000L; String expectedResponse = "OK"; when(commandObjects.tsCreateRule(sourceKey, destKey, aggregationType, bucketDuration, alignTimestamp)).thenReturn(stringCommandObject); when(commandExecutor.executeCommand(stringCommandObject)).thenReturn(expectedResponse); String result = jedis.tsCreateRule(sourceKey, destKey, aggregationType, bucketDuration, alignTimestamp); assertThat(result, sameInstance(expectedResponse)); verify(commandExecutor).executeCommand(stringCommandObject); verify(commandObjects).tsCreateRule(sourceKey, destKey, aggregationType, bucketDuration, alignTimestamp); } @Test public void testTsDecrBy() { String key = "testKey"; double value = 1.5; long expectedResponse = -1L; // Assuming the decrement results in a total of -1 when(commandObjects.tsDecrBy(key, value)).thenReturn(longCommandObject); when(commandExecutor.executeCommand(longCommandObject)).thenReturn(expectedResponse); long result = jedis.tsDecrBy(key, value); assertEquals(expectedResponse, result); verify(commandExecutor).executeCommand(longCommandObject); verify(commandObjects).tsDecrBy(key, value); } @Test public void testTsDecrByWithTimestamp() { String key = "testKey"; double value = 1.5; long timestamp = 1582605077000L; long expectedResponse = 5L; when(commandObjects.tsDecrBy(key, value, timestamp)).thenReturn(longCommandObject); when(commandExecutor.executeCommand(longCommandObject)).thenReturn(expectedResponse); long result = jedis.tsDecrBy(key, value, timestamp); assertEquals(expectedResponse, result); verify(commandExecutor).executeCommand(longCommandObject); verify(commandObjects).tsDecrBy(key, value, timestamp); } @Test public void testTsDecrByWithParams() { String key = "testKey"; double value = 1.5; TSDecrByParams decrByParams = mock(TSDecrByParams.class); long expectedResponse = 5L; when(commandObjects.tsDecrBy(key, value, decrByParams)).thenReturn(longCommandObject); when(commandExecutor.executeCommand(longCommandObject)).thenReturn(expectedResponse); long result = jedis.tsDecrBy(key, value, decrByParams); assertEquals(expectedResponse, result); verify(commandExecutor).executeCommand(longCommandObject); verify(commandObjects).tsDecrBy(key, value, decrByParams); } @Test public void testTsDel() { String key = "testKey"; long fromTimestamp = 1582605077000L; long toTimestamp = 1582605079000L; long expectedResponse = 2L; // Number of deleted entries when(commandObjects.tsDel(key, fromTimestamp, toTimestamp)).thenReturn(longCommandObject); when(commandExecutor.executeCommand(longCommandObject)).thenReturn(expectedResponse); long result = jedis.tsDel(key, fromTimestamp, toTimestamp); assertEquals(expectedResponse, result); verify(commandExecutor).executeCommand(longCommandObject); verify(commandObjects).tsDel(key, fromTimestamp, toTimestamp); } @Test public void testTsDeleteRule() { String sourceKey = "sourceKey"; String destKey = "destKey"; String expectedResponse = "OK"; when(commandObjects.tsDeleteRule(sourceKey, destKey)).thenReturn(stringCommandObject); when(commandExecutor.executeCommand(stringCommandObject)).thenReturn(expectedResponse); String result = jedis.tsDeleteRule(sourceKey, destKey); assertThat(result, sameInstance(expectedResponse)); verify(commandExecutor).executeCommand(stringCommandObject); verify(commandObjects).tsDeleteRule(sourceKey, destKey); } @Test public void testTsGet() { String key = "testKey"; TSElement expectedResponse = new TSElement(1582605077000L, 123.45); when(commandObjects.tsGet(key)).thenReturn(tsElementCommandObject); when(commandExecutor.executeCommand(tsElementCommandObject)).thenReturn(expectedResponse); TSElement result = jedis.tsGet(key); assertEquals(expectedResponse, result); verify(commandExecutor).executeCommand(tsElementCommandObject); verify(commandObjects).tsGet(key); } @Test public void testTsGetWithParams() { String key = "testKey"; TSGetParams getParams = new TSGetParams().latest(); TSElement expectedResponse = new TSElement(1582605077000L, 123.45); when(commandObjects.tsGet(key, getParams)).thenReturn(tsElementCommandObject); when(commandExecutor.executeCommand(tsElementCommandObject)).thenReturn(expectedResponse); TSElement result = jedis.tsGet(key, getParams); assertEquals(expectedResponse, result); verify(commandExecutor).executeCommand(tsElementCommandObject); verify(commandObjects).tsGet(key, getParams); } @Test public void testTsIncrBy() { String key = "testKey"; double value = 2.5; long expectedResponse = 5L; // Assuming the increment results in a total of 5 when(commandObjects.tsIncrBy(key, value)).thenReturn(longCommandObject); when(commandExecutor.executeCommand(longCommandObject)).thenReturn(expectedResponse); long result = jedis.tsIncrBy(key, value); assertEquals(expectedResponse, result); verify(commandExecutor).executeCommand(longCommandObject); verify(commandObjects).tsIncrBy(key, value); } @Test public void testTsIncrByWithTimestamp() { String key = "testKey"; double value = 2.5; long timestamp = 1582605077000L; long expectedResponse = 5L; when(commandObjects.tsIncrBy(key, value, timestamp)).thenReturn(longCommandObject); when(commandExecutor.executeCommand(longCommandObject)).thenReturn(expectedResponse); long result = jedis.tsIncrBy(key, value, timestamp); assertEquals(expectedResponse, result); verify(commandExecutor).executeCommand(longCommandObject); verify(commandObjects).tsIncrBy(key, value, timestamp); } @Test public void testTsIncrByWithParams() { String key = "testKey"; double value = 2.5; TSIncrByParams incrByParams = mock(TSIncrByParams.class); long expectedResponse = 5L; when(commandObjects.tsIncrBy(key, value, incrByParams)).thenReturn(longCommandObject); when(commandExecutor.executeCommand(longCommandObject)).thenReturn(expectedResponse); long result = jedis.tsIncrBy(key, value, incrByParams); assertEquals(expectedResponse, result); verify(commandExecutor).executeCommand(longCommandObject); verify(commandObjects).tsIncrBy(key, value, incrByParams); } @Test public void testTsInfo() { String key = "testKey"; TSInfo expectedResponse = mock(TSInfo.class); when(commandObjects.tsInfo(key)).thenReturn(tsInfoCommandObject); when(commandExecutor.executeCommand(tsInfoCommandObject)).thenReturn(expectedResponse); TSInfo result = jedis.tsInfo(key); assertThat(result, sameInstance(expectedResponse)); verify(commandExecutor).executeCommand(tsInfoCommandObject); verify(commandObjects).tsInfo(key); } @Test public void testTsInfoDebug() { String key = "testKey"; TSInfo expectedResponse = mock(TSInfo.class); when(commandObjects.tsInfoDebug(key)).thenReturn(tsInfoCommandObject); when(commandExecutor.executeCommand(tsInfoCommandObject)).thenReturn(expectedResponse); TSInfo result = jedis.tsInfoDebug(key); assertThat(result, sameInstance(expectedResponse)); verify(commandExecutor).executeCommand(tsInfoCommandObject); verify(commandObjects).tsInfoDebug(key); } @Test public void testTsMAdd() { Map.Entry entry1 = new AbstractMap.SimpleEntry<>("key1", new TSElement(1582605077000L, 123.45)); Map.Entry entry2 = new AbstractMap.SimpleEntry<>("key2", new TSElement(1582605078000L, 234.56)); List expectedResponse = Arrays.asList(1582605077000L, 1582605078000L); // Timestamps of the added values when(commandObjects.tsMAdd(entry1, entry2)).thenReturn(listLongCommandObject); when(commandExecutor.executeCommand(listLongCommandObject)).thenReturn(expectedResponse); List result = jedis.tsMAdd(entry1, entry2); assertEquals(expectedResponse, result); verify(commandExecutor).executeCommand(listLongCommandObject); verify(commandObjects).tsMAdd(entry1, entry2); } @Test public void testTsMGet() { TSMGetParams multiGetParams = new TSMGetParams().withLabels(); String[] filters = { "sensor=temperature" }; Map expectedResponse = new HashMap<>(); when(commandObjects.tsMGet(multiGetParams, filters)).thenReturn(mapStringTsmGetElementCommandObject); when(commandExecutor.executeCommand(mapStringTsmGetElementCommandObject)).thenReturn(expectedResponse); Map result = jedis.tsMGet(multiGetParams, filters); assertEquals(expectedResponse, result); verify(commandExecutor).executeCommand(mapStringTsmGetElementCommandObject); verify(commandObjects).tsMGet(multiGetParams, filters); } @Test public void testTsMRange() { long fromTimestamp = 1582600000000L; long toTimestamp = 1582605077000L; String[] filters = { "sensor=temperature" }; Map expectedResponse = new HashMap<>(); when(commandObjects.tsMRange(fromTimestamp, toTimestamp, filters)).thenReturn(mapStringTsmRangeElementsCommandObject); when(commandExecutor.executeCommand(mapStringTsmRangeElementsCommandObject)).thenReturn(expectedResponse); Map result = jedis.tsMRange(fromTimestamp, toTimestamp, filters); assertEquals(expectedResponse, result); verify(commandExecutor).executeCommand(mapStringTsmRangeElementsCommandObject); verify(commandObjects).tsMRange(fromTimestamp, toTimestamp, filters); } @Test public void testTsMRangeWithParams() { TSMRangeParams multiRangeParams = TSMRangeParams.multiRangeParams(1582600000000L, 1582605077000L).filter("sensor=temperature"); Map expectedResponse = new HashMap<>(); when(commandObjects.tsMRange(multiRangeParams)).thenReturn(mapStringTsmRangeElementsCommandObject); when(commandExecutor.executeCommand(mapStringTsmRangeElementsCommandObject)).thenReturn(expectedResponse); Map result = jedis.tsMRange(multiRangeParams); assertEquals(expectedResponse, result); verify(commandExecutor).executeCommand(mapStringTsmRangeElementsCommandObject); verify(commandObjects).tsMRange(multiRangeParams); } @Test public void testTsMRevRange() { long fromTimestamp = 1582600000000L; long toTimestamp = 1582605077000L; String[] filters = { "sensor=temperature" }; Map expectedResponse = new HashMap<>(); when(commandObjects.tsMRevRange(fromTimestamp, toTimestamp, filters)).thenReturn(mapStringTsmRangeElementsCommandObject); when(commandExecutor.executeCommand(mapStringTsmRangeElementsCommandObject)).thenReturn(expectedResponse); Map result = jedis.tsMRevRange(fromTimestamp, toTimestamp, filters); assertEquals(expectedResponse, result); verify(commandExecutor).executeCommand(mapStringTsmRangeElementsCommandObject); verify(commandObjects).tsMRevRange(fromTimestamp, toTimestamp, filters); } @Test public void testTsMRevRangeWithParams() { TSMRangeParams multiRangeParams = TSMRangeParams.multiRangeParams(1582600000000L, 1582605077000L).filter("sensor=temperature"); Map expectedResponse = new HashMap<>(); when(commandObjects.tsMRevRange(multiRangeParams)).thenReturn(mapStringTsmRangeElementsCommandObject); when(commandExecutor.executeCommand(mapStringTsmRangeElementsCommandObject)).thenReturn(expectedResponse); Map result = jedis.tsMRevRange(multiRangeParams); assertEquals(expectedResponse, result); verify(commandExecutor).executeCommand(mapStringTsmRangeElementsCommandObject); verify(commandObjects).tsMRevRange(multiRangeParams); } @Test public void testTsQueryIndex() { String[] filters = { "sensor=temperature", "location=warehouse" }; List expectedResponse = Arrays.asList("series1", "series2"); when(commandObjects.tsQueryIndex(filters)).thenReturn(listStringCommandObject); when(commandExecutor.executeCommand(listStringCommandObject)).thenReturn(expectedResponse); List result = jedis.tsQueryIndex(filters); assertThat(result, sameInstance(expectedResponse)); verify(commandExecutor).executeCommand(listStringCommandObject); verify(commandObjects).tsQueryIndex(filters); } @Test public void testTsRange() { String key = "testKey"; long fromTimestamp = 1582600000000L; long toTimestamp = 1582605077000L; List expectedResponse = Collections.singletonList(new TSElement(fromTimestamp, 123.45)); when(commandObjects.tsRange(key, fromTimestamp, toTimestamp)).thenReturn(listTsElementCommandObject); when(commandExecutor.executeCommand(listTsElementCommandObject)).thenReturn(expectedResponse); List result = jedis.tsRange(key, fromTimestamp, toTimestamp); assertEquals(expectedResponse, result); verify(commandExecutor).executeCommand(listTsElementCommandObject); verify(commandObjects).tsRange(key, fromTimestamp, toTimestamp); } @Test public void testTsRangeWithParams() { String key = "testKey"; TSRangeParams rangeParams = TSRangeParams.rangeParams(1582600000000L, 1582605077000L); List expectedResponse = Arrays.asList( new TSElement(1582600000000L, 123.45), new TSElement(1582605077000L, 234.56)); when(commandObjects.tsRange(key, rangeParams)).thenReturn(listTsElementCommandObject); when(commandExecutor.executeCommand(listTsElementCommandObject)).thenReturn(expectedResponse); List result = jedis.tsRange(key, rangeParams); assertEquals(expectedResponse, result); verify(commandExecutor).executeCommand(listTsElementCommandObject); verify(commandObjects).tsRange(key, rangeParams); } @Test public void testTsRevRange() { String key = "testKey"; long fromTimestamp = 1582600000000L; long toTimestamp = 1582605077000L; List expectedResponse = Collections.singletonList(new TSElement(toTimestamp, 234.56)); when(commandObjects.tsRevRange(key, fromTimestamp, toTimestamp)).thenReturn(listTsElementCommandObject); when(commandExecutor.executeCommand(listTsElementCommandObject)).thenReturn(expectedResponse); List result = jedis.tsRevRange(key, fromTimestamp, toTimestamp); assertEquals(expectedResponse, result); verify(commandExecutor).executeCommand(listTsElementCommandObject); verify(commandObjects).tsRevRange(key, fromTimestamp, toTimestamp); } @Test public void testTsRevRangeWithParams() { String key = "testKey"; TSRangeParams rangeParams = TSRangeParams.rangeParams(1582600000000L, 1582605077000L); List expectedResponse = Arrays.asList( new TSElement(1582605077000L, 234.56), new TSElement(1582600000000L, 123.45)); when(commandObjects.tsRevRange(key, rangeParams)).thenReturn(listTsElementCommandObject); when(commandExecutor.executeCommand(listTsElementCommandObject)).thenReturn(expectedResponse); List result = jedis.tsRevRange(key, rangeParams); assertEquals(expectedResponse, result); verify(commandExecutor).executeCommand(listTsElementCommandObject); verify(commandObjects).tsRevRange(key, rangeParams); } } ================================================ FILE: src/test/java/redis/clients/jedis/mocked/unified/UnifiedJedisTopKCommandsTest.java ================================================ package redis.clients.jedis.mocked.unified; import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.sameInstance; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; import java.util.Arrays; import java.util.HashMap; import java.util.List; import java.util.Map; import org.junit.jupiter.api.Test; public class UnifiedJedisTopKCommandsTest extends UnifiedJedisMockedTestBase { @Test public void testTopkAdd() { String key = "testTopK"; String[] items = { "item1", "item2" }; List expectedResponse = Arrays.asList("item3", "item4"); when(commandObjects.topkAdd(key, items)).thenReturn(listStringCommandObject); when(commandExecutor.executeCommand(listStringCommandObject)).thenReturn(expectedResponse); List result = jedis.topkAdd(key, items); assertThat(result, sameInstance(expectedResponse)); verify(commandExecutor).executeCommand(listStringCommandObject); verify(commandObjects).topkAdd(key, items); } @Test public void testTopkIncrBy() { String key = "testTopK"; Map itemIncrements = new HashMap<>(); itemIncrements.put("item1", 1L); itemIncrements.put("item2", 2L); List expectedResponse = Arrays.asList("item3", "item4"); when(commandObjects.topkIncrBy(key, itemIncrements)).thenReturn(listStringCommandObject); when(commandExecutor.executeCommand(listStringCommandObject)).thenReturn(expectedResponse); List result = jedis.topkIncrBy(key, itemIncrements); assertThat(result, sameInstance(expectedResponse)); verify(commandExecutor).executeCommand(listStringCommandObject); verify(commandObjects).topkIncrBy(key, itemIncrements); } @Test public void testTopkInfo() { String key = "testTopK"; Map expectedResponse = new HashMap<>(); expectedResponse.put("k", 10L); expectedResponse.put("width", 50L); expectedResponse.put("depth", 5L); expectedResponse.put("decay", 0.9); when(commandObjects.topkInfo(key)).thenReturn(mapStringObjectCommandObject); when(commandExecutor.executeCommand(mapStringObjectCommandObject)).thenReturn(expectedResponse); Map result = jedis.topkInfo(key); assertThat(result, sameInstance(expectedResponse)); verify(commandExecutor).executeCommand(mapStringObjectCommandObject); verify(commandObjects).topkInfo(key); } @Test public void testTopkList() { String key = "testTopK"; List expectedResponse = Arrays.asList("item1", "item2", "item3"); when(commandObjects.topkList(key)).thenReturn(listStringCommandObject); when(commandExecutor.executeCommand(listStringCommandObject)).thenReturn(expectedResponse); List result = jedis.topkList(key); assertThat(result, sameInstance(expectedResponse)); verify(commandExecutor).executeCommand(listStringCommandObject); verify(commandObjects).topkList(key); } @Test public void testTopkListWithCount() { String key = "testTopK"; Map expectedResponse = new HashMap<>(); expectedResponse.put("item1", 1L); expectedResponse.put("item2", 2L); when(commandObjects.topkListWithCount(key)).thenReturn(mapStringLongCommandObject); when(commandExecutor.executeCommand(mapStringLongCommandObject)).thenReturn(expectedResponse); Map result = jedis.topkListWithCount(key); assertThat(result, sameInstance(expectedResponse)); verify(commandExecutor).executeCommand(mapStringLongCommandObject); verify(commandObjects).topkListWithCount(key); } @Test public void testTopkQuery() { String key = "testTopK"; String[] items = { "item1", "item2" }; List expectedResponse = Arrays.asList(true, false); when(commandObjects.topkQuery(key, items)).thenReturn(listBooleanCommandObject); when(commandExecutor.executeCommand(listBooleanCommandObject)).thenReturn(expectedResponse); List result = jedis.topkQuery(key, items); assertThat(result, sameInstance(expectedResponse)); verify(commandExecutor).executeCommand(listBooleanCommandObject); verify(commandObjects).topkQuery(key, items); } @Test public void testTopkReserve() { String key = "testTopK"; long topk = 10L; String expectedResponse = "OK"; when(commandObjects.topkReserve(key, topk)).thenReturn(stringCommandObject); when(commandExecutor.executeCommand(stringCommandObject)).thenReturn(expectedResponse); String result = jedis.topkReserve(key, topk); assertThat(result, sameInstance(expectedResponse)); verify(commandExecutor).executeCommand(stringCommandObject); verify(commandObjects).topkReserve(key, topk); } @Test public void testTopkReserveWidth() { String key = "testTopK"; long topk = 10L; long width = 50L; long depth = 5L; double decay = 0.9; String expectedResponse = "OK"; when(commandObjects.topkReserve(key, topk, width, depth, decay)).thenReturn(stringCommandObject); when(commandExecutor.executeCommand(stringCommandObject)).thenReturn(expectedResponse); String result = jedis.topkReserve(key, topk, width, depth, decay); assertThat(result, sameInstance(expectedResponse)); verify(commandExecutor).executeCommand(stringCommandObject); verify(commandObjects).topkReserve(key, topk, width, depth, decay); } } ================================================ FILE: src/test/java/redis/clients/jedis/modules/ConsolidatedAccessControlListCommandsTest.java ================================================ package redis.clients.jedis.modules; import static org.hamcrest.MatcherAssert.assertThat; import static org.junit.jupiter.api.Assertions.assertThrows; import io.redis.test.annotations.ConditionalOnEnv; import io.redis.test.annotations.SinceRedisVersion; import java.util.Locale; import java.util.function.Consumer; import org.hamcrest.Matchers; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Tag; import org.junit.jupiter.api.extension.RegisterExtension; import org.junit.jupiter.params.ParameterizedClass; import org.junit.jupiter.params.provider.MethodSource; import redis.clients.jedis.DefaultJedisClientConfig; import redis.clients.jedis.RedisClient; import redis.clients.jedis.RedisProtocol; import redis.clients.jedis.UnifiedJedis; import redis.clients.jedis.bloom.RedisBloomProtocol.*; import redis.clients.jedis.commands.ProtocolCommand; import redis.clients.jedis.exceptions.JedisAccessControlException; import redis.clients.jedis.json.JsonProtocol.JsonCommand; import redis.clients.jedis.search.SearchProtocol.SearchCommand; import redis.clients.jedis.search.schemafields.TextField; import redis.clients.jedis.timeseries.TimeSeriesProtocol.TimeSeriesCommand; import redis.clients.jedis.util.EnvCondition; import redis.clients.jedis.util.SafeEncoder; import redis.clients.jedis.util.TestEnvUtil; @SinceRedisVersion(value = "7.9.0") @ParameterizedClass @MethodSource("redis.clients.jedis.commands.CommandsTestsParameters#respVersions") @Tag("integration") @ConditionalOnEnv(value = TestEnvUtil.ENV_REDIS_ENTERPRISE, enabled = false) public class ConsolidatedAccessControlListCommandsTest extends RedisModuleCommandsTestBase { @RegisterExtension public static EnvCondition envCondition = new EnvCondition(); public static final String USER_NAME = "moduser"; public static final String USER_PASSWORD = "secret"; public ConsolidatedAccessControlListCommandsTest(RedisProtocol protocol) { super(protocol); } @BeforeAll public static void prepare() { RedisModuleCommandsTestBase.prepare(); } @AfterEach @Override public void tearDown() throws Exception { try { jedis.aclDelUser(USER_NAME); } catch (Exception e) { } super.tearDown(); } @Test public void listACLCategoriesTest() { assertThat(jedis.aclCat(), Matchers.hasItems("bloom", "cuckoo", "cms", "topk", "tdigest", "search", "timeseries", "json")); } @Test public void grantBloomCommandTest() { grantModuleCommandTest(BloomFilterCommand.RESERVE, client -> client.bfReserve("foo", 0.01, 10_000)); } @Test public void grantBloomCommandCatTest() { grantModuleCommandCatTest("bloom", BloomFilterCommand.RESERVE, client -> client.bfReserve("foo", 0.01, 10_000)); } @Test public void grantCuckooCommandTest() { grantModuleCommandTest(CuckooFilterCommand.RESERVE, client -> client.cfReserve("foo", 10_000)); } @Test public void grantCuckooCommandCatTest() { grantModuleCommandCatTest("cuckoo", CuckooFilterCommand.RESERVE, client -> client.cfReserve("foo", 10_000)); } @Test public void grantCmsCommandTest() { grantModuleCommandTest(CountMinSketchCommand.INITBYDIM, client -> client.cmsInitByDim("foo", 16, 4)); } @Test public void grantCmsCommandCatTest() { grantModuleCommandCatTest("cms", CountMinSketchCommand.INITBYDIM, client -> client.cmsInitByDim("foo", 16, 4)); } @Test public void grantTopkCommandTest() { grantModuleCommandTest(TopKCommand.RESERVE, client -> client.topkReserve("foo", 1000)); } @Test public void grantTopkCommandCatTest() { grantModuleCommandCatTest("topk", TopKCommand.RESERVE, client -> client.topkReserve("foo", 1000)); } @Test public void grantTdigestCommandTest() { grantModuleCommandTest(TDigestCommand.CREATE, client -> client.tdigestCreate("foo")); } @Test public void grantTdigestCommandCatTest() { grantModuleCommandCatTest("tdigest", TDigestCommand.CREATE, client -> client.tdigestCreate("foo")); } @Test public void grantSearchCommandTest() { grantModuleCommandTest(SearchCommand.CREATE, client -> client.ftCreate("foo", TextField.of("bar"))); } @Test public void grantSearchCommandCatTest() { grantModuleCommandCatTest("search", SearchCommand.CREATE, client -> client.ftCreate("foo", TextField.of("bar"))); } @Test public void grantTimeseriesCommandTest() { grantModuleCommandTest(TimeSeriesCommand.CREATE, client -> client.tsCreate("foo")); } @Test public void grantTimeseriesCommandCatTest() { grantModuleCommandCatTest("timeseries", TimeSeriesCommand.CREATE, client -> client.tsCreate("foo")); } @Test public void grantJsonCommandTest() { grantModuleCommandTest(JsonCommand.GET, client -> client.jsonGet("foo")); } @Test public void grantJsonCommandCatTest() { grantModuleCommandCatTest("json", JsonCommand.GET, client -> client.jsonGet("foo")); } private void grantModuleCommandTest(ProtocolCommand command, Consumer operation) { // create and enable an user with permission to all keys but no commands jedis.aclSetUser(USER_NAME, ">" + USER_PASSWORD, "on", "~*"); // client object with new user try (UnifiedJedis client = RedisClient.builder().hostAndPort(endpoint.getHostAndPort()) .clientConfig( DefaultJedisClientConfig.builder().user(USER_NAME).password(USER_PASSWORD).build()) .build()) { // user can't execute commands JedisAccessControlException noperm = assertThrows(JedisAccessControlException.class, () -> operation.accept(client), "Should throw a NOPERM exception"); assertThat(noperm.getMessage(), Matchers.oneOf(getNopermErrorMessage(false, command), getNopermErrorMessage(true, command))); // permit user to commands jedis.aclSetUser(USER_NAME, "+" + SafeEncoder.encode(command.getRaw())); // user can now execute commands operation.accept(client); } } private void grantModuleCommandCatTest(String category, ProtocolCommand command, Consumer operation) { // create and enable an user with permission to all keys but no commands jedis.aclSetUser(USER_NAME, ">" + USER_PASSWORD, "on", "~*"); // client object with new user try (UnifiedJedis client = RedisClient.builder().hostAndPort(endpoint.getHostAndPort()) .clientConfig( DefaultJedisClientConfig.builder().user(USER_NAME).password(USER_PASSWORD).build()) .build()) { // user can't execute category commands JedisAccessControlException noperm = assertThrows(JedisAccessControlException.class, () -> operation.accept(client), "Should throw a NOPERM exception"); assertThat(noperm.getMessage(), Matchers.oneOf(getNopermErrorMessage(false, command), getNopermErrorMessage(true, command))); // permit user to category commands jedis.aclSetUser(USER_NAME, "+@" + category); // user can now execute category commands operation.accept(client); } } private static String getNopermErrorMessage(boolean commandNameUpperCase, ProtocolCommand protocolCommand) { String command = SafeEncoder.encode(protocolCommand.getRaw()); return String.format("NOPERM User %s has no permissions to run the '%s' command", USER_NAME, commandNameUpperCase ? command.toUpperCase(Locale.ENGLISH) : command.toLowerCase(Locale.ENGLISH)); } } ================================================ FILE: src/test/java/redis/clients/jedis/modules/ConsolidatedConfigurationCommandsTest.java ================================================ package redis.clients.jedis.modules; import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.aMapWithSize; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertThrows; import io.redis.test.annotations.ConditionalOnEnv; import io.redis.test.annotations.SinceRedisVersion; import java.util.Collections; import org.hamcrest.Matchers; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Tag; import org.junit.jupiter.params.ParameterizedClass; import org.junit.jupiter.params.provider.MethodSource; import redis.clients.jedis.RedisProtocol; import redis.clients.jedis.exceptions.JedisDataException; import redis.clients.jedis.util.TestEnvUtil; @SinceRedisVersion(value = "7.9.0") @ParameterizedClass @MethodSource("redis.clients.jedis.commands.CommandsTestsParameters#respVersions") @Tag("integration") public class ConsolidatedConfigurationCommandsTest extends RedisModuleCommandsTestBase { public ConsolidatedConfigurationCommandsTest(RedisProtocol redisProtocol) { super(redisProtocol); } @BeforeAll public static void prepare() { RedisModuleCommandsTestBase.prepare(); } @Test public void setSearchConfigGloballyTest() { final String configParam = "search-default-dialect"; // confirm default assertEquals(Collections.singletonMap(configParam, "1"), jedis.configGet(configParam)); try { assertEquals("OK", jedis.configSet(configParam, "2")); assertEquals(Collections.singletonMap(configParam, "2"), jedis.configGet(configParam)); } finally { // restore to default assertEquals("OK", jedis.configSet(configParam, "1")); } } @Test public void setReadOnlySearchConfigTest() { JedisDataException de = assertThrows(JedisDataException.class, () -> jedis.configSet("search-max-doctablesize", "10")); assertThat(de.getMessage(), Matchers.not(Matchers.emptyOrNullString())); } @Test @ConditionalOnEnv(value = TestEnvUtil.ENV_REDIS_ENTERPRISE, enabled = false) public void getSearchConfigSettingTest() { assertThat(jedis.configGet("search-timeout"), aMapWithSize(1)); } @Test public void getTSConfigSettingTest() { assertThat(jedis.configGet("ts-retention-policy"), aMapWithSize(1)); } @Test public void getBFConfigSettingTest() { assertThat(jedis.configGet("bf-error-rate"), aMapWithSize(1)); } @Test public void getCFConfigSettingTest() { assertThat(jedis.configGet("cf-initial-size"), aMapWithSize(1)); } @Test public void getAllConfigSettings() { assertThat(jedis.configGet("*").size(), Matchers.greaterThan(0)); } } ================================================ FILE: src/test/java/redis/clients/jedis/modules/RedisModuleCommandsTestBase.java ================================================ package redis.clients.jedis.modules; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Tag; import org.junit.jupiter.api.extension.RegisterExtension; import redis.clients.jedis.*; import redis.clients.jedis.commands.CommandsTestsParameters; import redis.clients.jedis.util.EnvCondition; import redis.clients.jedis.util.RedisVersionCondition; import redis.clients.jedis.util.TestEnvUtil; @Tag("integration") public abstract class RedisModuleCommandsTestBase { protected static String preferredEndpointId = "modules-docker"; @RegisterExtension public RedisVersionCondition versionCondition = new RedisVersionCondition( () -> Endpoints.getRedisEndpoint(preferredEndpointId)); @RegisterExtension public static EnvCondition envCondition = new EnvCondition(); protected static EndpointConfig endpoint; /** * Input data for parameterized tests. In principle all subclasses of this * class should be parameterized tests, to run with several versions of RESP. * * @see CommandsTestsParameters#respVersions() */ protected final RedisProtocol protocol; protected Jedis jedis; protected UnifiedJedis client; /** * The RESP protocol is to be injected by the subclasses, usually via JUnit * parameterized tests, because most of the subclassed tests are meant to be * executed against multiple RESP versions. For the special cases where a single * RESP version is relevant, we still force the subclass to be explicit and * call this constructor. * * @param protocol The RESP protocol to use during the tests. */ public RedisModuleCommandsTestBase(RedisProtocol protocol) { this.protocol = protocol; } // BeforeClass public static void prepare() { endpoint = Endpoints.getRedisEndpoint(preferredEndpointId); } @BeforeEach public void setUp() { if (endpoint == null) { return; } jedis = new Jedis(endpoint.getHostAndPort(), endpoint.getClientConfigBuilder().protocol(protocol).timeoutMillis(500).build()); jedis.flushAll(); client = RedisClient.builder().hostAndPort(endpoint.getHostAndPort()) .clientConfig(endpoint.getClientConfigBuilder().protocol(protocol).build()).build(); } @AfterEach public void tearDown() throws Exception { if (endpoint == null) { return; } client.close(); jedis.close(); } } ================================================ FILE: src/test/java/redis/clients/jedis/modules/RedisModulesPipelineTest.java ================================================ package redis.clients.jedis.modules; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertNotNull; import static org.junit.jupiter.api.Assumptions.assumeFalse; import static redis.clients.jedis.json.Path.ROOT_PATH; import static redis.clients.jedis.modules.json.JsonObjects.Baz; import static redis.clients.jedis.modules.json.JsonObjects.IRLObject; import static redis.clients.jedis.search.RediSearchUtil.toStringMap; import com.google.gson.Gson; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Collections; import org.json.JSONArray; import org.json.JSONObject; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.Test; import org.junit.jupiter.params.ParameterizedClass; import org.junit.jupiter.params.provider.MethodSource; import redis.clients.jedis.Pipeline; import redis.clients.jedis.RedisProtocol; import redis.clients.jedis.Response; import redis.clients.jedis.json.JsonSetParams; import redis.clients.jedis.json.Path; import redis.clients.jedis.json.Path2; import redis.clients.jedis.search.*; import redis.clients.jedis.search.aggr.*; @ParameterizedClass @MethodSource("redis.clients.jedis.commands.CommandsTestsParameters#respVersions") public class RedisModulesPipelineTest extends RedisModuleCommandsTestBase { private static final Gson gson = new Gson(); @BeforeAll public static void prepare() { RedisModuleCommandsTestBase.prepare(); } public RedisModulesPipelineTest(RedisProtocol protocol) { super(protocol); } @Test public void search() { Schema sc = new Schema().addTextField("title", 1.0).addTextField("body", 1.0); String index = "testindex"; Map fields = new HashMap<>(); fields.put("title", "hello world"); fields.put("body", "lorem ipsum"); // Connection c = createConnection(); // Pipeline p = new Pipeline(c); Pipeline p = (Pipeline) client.pipelined(); Response create = p.ftCreate(index, IndexOptions.defaultOptions(), sc); Response alter = p.ftAlter(index, new Schema().addTextField("foo", 1.0)); p.hset("doc1", toStringMap(fields)); p.hset("doc2", toStringMap(fields)); Response searchResult = p.ftSearch(index, new Query("hello world")); // Response searchBytesResult = p.ftSearch(index.getBytes(), new Query("hello world")); // not RESP3 supported Response aggregateResult = p.ftAggregate(index, new AggregationBuilder().groupBy("@title")); Response explain = p.ftExplain(index, new Query("@title:title_val")); Response> explainCLI = p.ftExplainCLI(index, new Query("@title:title_val")); Response> info = p.ftInfo(index); // // @org.junit.Ignore // Response configSet = p.ftConfigSet("timeout", "100"); // Response> configGet = p.ftConfigGet("*"); // Response configSetIndex = p.ftConfigSet(index, "timeout", "100"); // Response> configGetIndex = p.ftConfigGet(index, "*"); Response synUpdate = p.ftSynUpdate(index, "foo", "bar"); Response>> synDump = p.ftSynDump(index); p.sync(); // c.close(); assertEquals("OK", create.get()); assertEquals("OK", alter.get()); assertEquals("OK", alter.get()); assertEquals(2, searchResult.get().getTotalResults()); // assertEquals(2, searchBytesResult.get().getTotalResults()); assertEquals(1, aggregateResult.get().getTotalResults()); assertNotNull(explain.get()); assertNotNull(explainCLI.get().get(0)); assertEquals(index, info.get().get("index_name")); // // @org.junit.Ignore // assertEquals("OK", configSet.get()); // assertEquals("100", configGet.get().get("TIMEOUT")); // assertEquals("OK", configSetIndex.get()); // assertEquals("100", configGetIndex.get().get("TIMEOUT")); assertEquals("OK", synUpdate.get()); Map> expected = new HashMap<>(); expected.put("bar", Collections.singletonList("foo")); assertEquals(expected, synDump.get()); } @Test public void jsonV1() { assumeFalse(protocol == RedisProtocol.RESP3); Map hm1 = new HashMap<>(); hm1.put("hello", "world"); hm1.put("oh", "snap"); Map hm2 = new HashMap<>(); hm2.put("array", new String[]{"a", "b", "c"}); hm2.put("boolean", true); hm2.put("number", 3); Baz baz1 = new Baz("quuz1", "grault1", "waldo1"); Baz baz2 = new Baz("quuz2", "grault2", "waldo2"); Baz baz3 = new Baz("quuz3", "grault3", "waldo3"); // Connection c = createConnection(); // Pipeline p = new Pipeline(c); Pipeline p = (Pipeline) client.pipelined(); Response set1 = p.jsonSet("foo", Path.ROOT_PATH, hm1); Response get = p.jsonGet("foo"); Response getObject = p.jsonGet("foo", Map.class); Response getWithPath = p.jsonGet("foo", Path.ROOT_PATH); Response getObjectWithPath = p.jsonGet("foo", Map.class, Path.ROOT_PATH); Response> mget = p.jsonMGet("foo"); p.jsonSet("baz", new JSONObject(gson.toJson(baz1))); Response> mgetClass = p.jsonMGet(Path.ROOT_PATH, Baz.class, "baz"); Response strLenPath = p.jsonStrLen("foo", new Path("hello")); Response strAppPath = p.jsonStrAppend("foo", new Path("hello"), "!"); Response delPath = p.jsonDel("foo", new Path("hello")); Response delKey = p.jsonDel("foo"); Response set2 = p.jsonSet("foo", Path.ROOT_PATH, hm2, new JsonSetParams().nx()); Response popPath = p.jsonArrPop("foo", new Path("array")); Response indexPop = p.jsonArrPop("foo", new Path("array"), 2); Response append = p.jsonArrAppend("foo", new Path("array"), "b", "c", "d"); Response index = p.jsonArrIndex("foo", new Path("array"), "c"); Response insert = p.jsonArrInsert("foo", new Path("array"), 0, "x"); Response arrLenWithPath = p.jsonArrLen("foo", new Path("array")); Response trim = p.jsonArrTrim("foo", new Path("array"), 1, 4); Response toggle = p.jsonToggle("foo", new Path("boolean")); Response> type = p.jsonType("foo", new Path("boolean")); Response> keyType = p.jsonType("foo"); Response clearPath = p.jsonClear("foo", new Path("boolean")); Response clearKey = p.jsonClear("foo"); Response set3 = p.jsonSet("foo", Path.ROOT_PATH, "newStr"); Response strLen = p.jsonStrLen("foo"); Response strApp = p.jsonStrAppend("foo", "?"); Response set4 = p.jsonSetWithEscape("obj", new IRLObject()); p.jsonSet("arr", ROOT_PATH, new int[]{ 0, 1, 2, 3 }); Response pop = p.jsonArrPop("arr"); Response arrLen = p.jsonArrLen("arr"); p.jsonSet("baz", ROOT_PATH, new Baz[]{ baz1, baz2, baz3 }); Response popClass = p.jsonArrPop("baz", Baz.class); Response popClassWithPath = p.jsonArrPop("baz", Baz.class, Path.ROOT_PATH); Response popClassWithIndex = p.jsonArrPop("baz", Baz.class, Path.ROOT_PATH, 0); p.sync(); // c.close(); assertEquals("OK", set1.get()); assertEquals(hm1, get.get()); assertEquals(hm1, getObject.get()); assertEquals(hm1, getWithPath.get()); assertEquals(hm1, getObjectWithPath.get()); assertEquals(1, mget.get().size()); assertEquals(baz1, mgetClass.get().get(0)); assertEquals(Long.valueOf(5), strLenPath.get()); assertEquals(Long.valueOf(6), strAppPath.get()); assertEquals(Long.valueOf(1), delPath.get()); assertEquals(Long.valueOf(1), delKey.get()); assertEquals("OK", set2.get()); assertEquals("c", popPath.get()); assertEquals("b", indexPop.get()); assertEquals(Long.valueOf(4), append.get()); assertEquals(Long.valueOf(2), index.get()); assertEquals(Long.valueOf(5), insert.get()); assertEquals(Long.valueOf(5), arrLenWithPath.get()); assertEquals(Long.valueOf(4), trim.get()); assertEquals("false", toggle.get()); assertEquals(boolean.class, type.get()); assertEquals(Object.class, keyType.get()); assertEquals(Long.valueOf(0), clearPath.get()); assertEquals(Long.valueOf(1), clearKey.get()); assertEquals("OK", set3.get()); assertEquals(Long.valueOf(6), strLen.get()); assertEquals(Long.valueOf(7), strApp.get()); assertEquals("OK", set4.get()); assertEquals(3.0, pop.get()); assertEquals(Long.valueOf(3), arrLen.get()); assertEquals(baz3, popClass.get()); assertEquals(baz2, popClassWithPath.get()); assertEquals(baz1, popClassWithIndex.get()); } @Test public void jsonV2() { Map hm1 = new HashMap<>(); hm1.put("hello", "world"); hm1.put("oh", "snap"); Map hm2 = new HashMap<>(); hm2.put("array", new String[]{"a", "b", "c"}); hm2.put("boolean", true); hm2.put("number", 3); // Connection c = createConnection(); // Pipeline p = new Pipeline(c); Pipeline p = (Pipeline) client.pipelined(); Response setWithEscape = p.jsonSetWithEscape("foo", Path2.ROOT_PATH, hm1); Response get = p.jsonGet("foo", Path2.ROOT_PATH); Response> mget = p.jsonMGet(Path2.ROOT_PATH, "foo"); Response> strLen = p.jsonStrLen("foo", new Path2("hello")); Response> strApp = p.jsonStrAppend("foo", new Path2("hello"), "!"); Response del = p.jsonDel("foo", new Path2("hello")); Response set = p.jsonSet("bar", Path2.ROOT_PATH, gson.toJson("strung")); Response setWithParams = p.jsonSet("foo", Path2.ROOT_PATH, gson.toJson(hm2), new JsonSetParams().xx()); Response setWithEscapeWithParams = p.jsonSetWithEscape("foo", Path2.ROOT_PATH, hm2, new JsonSetParams().xx()); Response> pop = p.jsonArrPop("foo", new Path2("array")); Response> popWithIndex = p.jsonArrPop("foo", new Path2("array"), 2); Response> append = p.jsonArrAppend("foo", Path2.of("$.array"), gson.toJson("b"), gson.toJson("d")); Response> appendWithEscape = p.jsonArrAppendWithEscape("foo", Path2.of("$.array"), "e"); Response> index = p.jsonArrIndex("foo", Path2.of("$.array"), gson.toJson("b")); Response> indexWithEscape = p.jsonArrIndexWithEscape("foo", Path2.of("$.array"), "b"); Response> insert = p.jsonArrInsert("foo", new Path2("array"), 0, gson.toJson("x")); Response> insertWithEscape = p.jsonArrInsertWithEscape("foo", new Path2("array"), 0, "x"); Response> arrLen = p.jsonArrLen("foo", new Path2("array")); Response> trim = p.jsonArrTrim("foo", new Path2("array"), 1, 2); Response> toggle = p.jsonToggle("foo", new Path2("boolean")); Response>> type = p.jsonType("foo", new Path2("boolean")); Response clear = p.jsonClear("foo", new Path2("array")); p.sync(); // c.close(); assertEquals("OK", setWithEscape.get()); assertNotNull(get.get()); assertEquals(1, mget.get().size()); assertEquals(Long.valueOf(5), strLen.get().get(0)); assertEquals(1, strApp.get().size()); assertEquals(Long.valueOf(1), del.get()); assertEquals("OK", set.get()); assertEquals("OK", setWithParams.get()); assertEquals("OK", setWithEscapeWithParams.get()); assertEquals("c", pop.get().get(0)); assertEquals("b", popWithIndex.get().get(0)); assertEquals(Long.valueOf(3), append.get().get(0)); assertEquals(Long.valueOf(4), appendWithEscape.get().get(0)); assertEquals(Long.valueOf(1), index.get().get(0)); assertEquals(Long.valueOf(1), indexWithEscape.get().get(0)); assertEquals(Long.valueOf(5), insert.get().get(0)); assertEquals(Long.valueOf(6), insertWithEscape.get().get(0)); assertEquals(Long.valueOf(6), arrLen.get().get(0)); assertEquals(Long.valueOf(2), trim.get().get(0)); assertEquals(false, toggle.get().get(0)); assertEquals(boolean.class, type.get().get(0)); assertEquals(Long.valueOf(1), clear.get()); } } ================================================ FILE: src/test/java/redis/clients/jedis/modules/bloom/BloomTest.java ================================================ package redis.clients.jedis.modules.bloom; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertThrows; import static org.junit.jupiter.api.Assertions.assertTrue; import java.util.Arrays; import java.util.List; import java.util.Map; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Timeout; import org.junit.jupiter.params.ParameterizedClass; import org.junit.jupiter.params.provider.MethodSource; import redis.clients.jedis.RedisProtocol; import redis.clients.jedis.bloom.BFInsertParams; import redis.clients.jedis.bloom.BFReserveParams; import redis.clients.jedis.exceptions.JedisDataException; import redis.clients.jedis.modules.RedisModuleCommandsTestBase; @ParameterizedClass @MethodSource("redis.clients.jedis.commands.CommandsTestsParameters#respVersions") public class BloomTest extends RedisModuleCommandsTestBase { @BeforeAll public static void prepare() { RedisModuleCommandsTestBase.prepare(); } public BloomTest(RedisProtocol protocol) { super(protocol); } @Test public void reserveBasic() { client.bfReserve("myBloom", 0.001, 100L); assertTrue(client.bfAdd("myBloom", "val1")); assertTrue(client.bfExists("myBloom", "val1")); assertFalse(client.bfExists("myBloom", "val2")); } @Test public void reserveValidateZeroCapacity() { assertThrows(JedisDataException.class, () -> client.bfReserve("myBloom", 0.001, 0)); } @Test public void reserveValidateZeroError() { assertThrows(JedisDataException.class, () -> client.bfReserve("myBloom", 0d, 100L)); } @Test public void reserveAlreadyExists() { client.bfReserve("myBloom", 0.1, 100); assertThrows(JedisDataException.class, () -> client.bfReserve("myBloom", 0.1, 100)); } @Test public void reserveV2() { client.bfReserve("reserve-basic", 0.001, 2); assertEquals(Arrays.asList(true), client.bfInsert("reserve-basic", "a")); assertEquals(Arrays.asList(true), client.bfInsert("reserve-basic", "b")); assertEquals(Arrays.asList(true), client.bfInsert("reserve-basic", "c")); } @Test public void reserveEmptyParams() { client.bfReserve("empty-param", 0.001, 2, BFReserveParams.reserveParams()); assertEquals(Arrays.asList(true), client.bfInsert("empty-param", "a")); assertEquals(Arrays.asList(true), client.bfInsert("empty-param", "b")); assertEquals(Arrays.asList(true), client.bfInsert("empty-param", "c")); } @Test public void reserveNonScaling() { client.bfReserve("nonscaling", 0.001, 2, BFReserveParams.reserveParams().nonScaling()); assertEquals(Arrays.asList(true), client.bfInsert("nonscaling", "a")); assertEquals(Arrays.asList(true), client.bfInsert("nonscaling", "b")); assertEquals(Arrays.asList((Boolean) null), client.bfInsert("nonscaling", "c")); } @Test public void reserveExpansion() { // bf.reserve bfexpansion 0.001 1000 expansion 4 client.bfReserve("bfexpansion", 0.001, 1000, BFReserveParams.reserveParams().expansion(4)); assertEquals(Arrays.asList(true), client.bfInsert("bfexpansion", "a")); assertEquals(Arrays.asList(true), client.bfInsert("bfexpansion", BFInsertParams.insertParams().noCreate(), "b")); } @Test public void addExistsString() { assertTrue(client.bfAdd("newFilter", "foo")); assertTrue(client.bfExists("newFilter", "foo")); assertFalse(client.bfExists("newFilter", "bar")); assertFalse(client.bfAdd("newFilter", "foo")); } @Test public void testExistsNonExist() { assertFalse(client.bfExists("nonExist", "foo")); } @Test public void addExistsMulti() { List rv = client.bfMAdd("newFilter", "foo", "bar", "baz"); assertEquals(Arrays.asList(true, true, true), rv); rv = client.bfMAdd("newFilter", "newElem", "bar", "baz"); assertEquals(Arrays.asList(true, false, false), rv); } @Test public void testExample() { // Simple bloom filter using default module settings client.bfAdd("simpleBloom", "Mark"); // Does "Mark" now exist? client.bfExists("simpleBloom", "Mark"); // true client.bfExists("simpleBloom", "Farnsworth"); // False // If you have a long list of items to check/add, you can use the // "multi" methods client.bfMAdd("simpleBloom", "foo", "bar", "baz", "bat", "bag"); // Check if they exist: List rv = client.bfMExists("simpleBloom", "foo", "bar", "baz", "bat", "Mark", "nonexist"); // All items except the last one will be 'true' assertEquals(Arrays.asList(true, true, true, true, true, false), rv); // Reserve a "customized" bloom filter client.bfReserve("specialBloom", 0.0001, 10000); client.bfAdd("specialBloom", "foo"); } @Test public void testInsert() { client.bfInsert("b1", new BFInsertParams().capacity(1L), "1"); assertTrue(client.bfExists("b1", "1")); // returning an error if the filter does not already exist JedisDataException jde = assertThrows(JedisDataException.class, () -> client.bfInsert("b2", new BFInsertParams().noCreate(), "1"), "Should error if the filter does not already exist."); assertEquals("ERR not found", jde.getMessage()); client.bfInsert("b3", new BFInsertParams().capacity(1L).error(0.0001), "2"); assertTrue(client.bfExists("b3", "2")); } @Test public void issue49() { BFInsertParams insertOptions = new BFInsertParams(); List insert = client.bfInsert("mykey", insertOptions, "a", "b", "c"); assertEquals(3, insert.size()); } @Test public void card() { client.bfInsert("test_card", new BFInsertParams().capacity(1L), "1"); assertEquals(1L, client.bfCard("test_card")); // returning '0' if the filter does not already exist assertEquals(0L, client.bfCard("not_exist")); // returning an error if the filter is not a bloom filter client.set("foo", "bar"); assertThrows(JedisDataException.class, () -> client.bfCard("foo"), "Should error if the filter is not a bloom filter"); } @Test public void info() { client.bfInsert("test_info", new BFInsertParams().capacity(1L), "1"); Map info = client.bfInfo("test_info"); assertEquals(1L, info.get("Number of items inserted")); // returning an error if the filter does not already exist JedisDataException jde = assertThrows(JedisDataException.class, () -> client.bfInfo("not_exist"), "Should error if the filter does not already exist."); assertEquals("ERR not found", jde.getMessage()); } @Test public void insertNonScaling() { List insert = client.bfInsert("nonscaling_err", BFInsertParams.insertParams().capacity(4).nonScaling(), "a", "b", "c"); assertEquals(Arrays.asList(true, true, true), insert); insert = client.bfInsert("nonscaling_err", "d", "e"); assertEquals(Arrays.asList(true, null), insert); } @Test public void insertExpansion() { // BF.INSERT bfexpansion CAPACITY 3 expansion 3 ITEMS a b c d e f g h j k l o i u y t r e w q List insert = client.bfInsert("bfexpansion", BFInsertParams.insertParams().capacity(3).expansion(3), "a", "b", "c", "d", "e", "f", "g", "h", "j", "k", "l", "o", "i", "u", "y", "t", "r", "e", "w", "q"); assertEquals(20, insert.size()); } @Test @Timeout(2) public void testScanDumpAndLoadChunk() { client.bfAdd("bloom-dump", "a"); long iterator = 0; while (true) { Map.Entry chunkData = client.bfScanDump("bloom-dump", iterator); iterator = chunkData.getKey(); if (iterator == 0L) break; assertEquals("OK", client.bfLoadChunk("bloom-load", iterator, chunkData.getValue())); } // check for properties assertEquals(client.bfInfo("bloom-dump"), client.bfInfo("bloom-load")); // check for existing items assertTrue(client.bfExists("bloom-load", "a")); } } ================================================ FILE: src/test/java/redis/clients/jedis/modules/bloom/CMSTest.java ================================================ package redis.clients.jedis.modules.bloom; import java.util.Arrays; import java.util.HashMap; import java.util.LinkedHashMap; import java.util.List; import java.util.Map; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.Test; import org.junit.jupiter.params.ParameterizedClass; import org.junit.jupiter.params.provider.MethodSource; import redis.clients.jedis.RedisProtocol; import redis.clients.jedis.exceptions.JedisException; import redis.clients.jedis.modules.RedisModuleCommandsTestBase; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertThrows; /** * Tests for the Count-Min-Sketch Implementation */ @ParameterizedClass @MethodSource("redis.clients.jedis.commands.CommandsTestsParameters#respVersions") public class CMSTest extends RedisModuleCommandsTestBase { @BeforeAll public static void prepare() { RedisModuleCommandsTestBase.prepare(); } public CMSTest(RedisProtocol protocol) { super(protocol); } @Test public void testInitByDim() { client.cmsInitByDim("cms1", 16L, 4L); Map info = client.cmsInfo("cms1"); assertEquals(16L, info.get("width")); assertEquals(4L, info.get("depth")); assertEquals(0L, info.get("count")); } @Test public void testInitByProb() { client.cmsInitByProb("cms2", 0.01, 0.01); Map info = client.cmsInfo("cms2"); assertEquals(200L, info.get("width")); assertEquals(7L, info.get("depth")); assertEquals(0L, info.get("count")); } @Test public void testKeyAlreadyExists() { client.cmsInitByDim("dup", 16L, 4L); JedisException thrown = assertThrows(JedisException.class, () -> { client.cmsInitByDim("dup", 8L, 6L); }); assertEquals("CMS: key already exists", thrown.getMessage()); } @Test public void testIncrBy() { client.cmsInitByDim("cms3", 1000L, 5L); long resp = client.cmsIncrBy("cms3", "foo", 5L); assertEquals(5L, resp); Map info = client.cmsInfo("cms3"); assertEquals(1000L, info.get("width")); assertEquals(5L, info.get("depth")); assertEquals(5L, info.get("count")); } @Test public void testIncrByMultipleArgs() { client.cmsInitByDim("cms4", 1000L, 5L); client.cmsIncrBy("cms4", "foo", 5L); Map itemIncrements = new LinkedHashMap<>(); itemIncrements.put("foo", 5L); itemIncrements.put("bar", 15L); List resp = client.cmsIncrBy("cms4", itemIncrements); // assertArrayEquals(new Long[] { 15L, 10L }, resp.toArray(new Long[0])); assertEquals(Arrays.asList(10L, 15L), resp); Map info = client.cmsInfo("cms4"); assertEquals(1000L, info.get("width")); assertEquals(5L, info.get("depth")); assertEquals(25L, info.get("count")); } @Test public void testQuery() { client.cmsInitByDim("cms5", 1000L, 5L); Map itemIncrements = new HashMap<>(); itemIncrements.put("foo", 10L); itemIncrements.put("bar", 15L); client.cmsIncrBy("cms5", itemIncrements); List resp = client.cmsQuery("cms5", "foo", "bar"); assertEquals(Arrays.asList(10L, 15L), resp); } @Test public void testMerge() { client.cmsInitByDim("A", 1000L, 5L); client.cmsInitByDim("B", 1000L, 5L); client.cmsInitByDim("C", 1000L, 5L); Map aValues = new HashMap<>(); aValues.put("foo", 5L); aValues.put("bar", 3L); aValues.put("baz", 9L); client.cmsIncrBy("A", aValues); Map bValues = new HashMap<>(); bValues.put("foo", 2L); bValues.put("bar", 3L); bValues.put("baz", 1L); client.cmsIncrBy("B", bValues); List q1 = client.cmsQuery("A", "foo", "bar", "baz"); assertEquals(Arrays.asList(5L, 3L, 9L), q1); List q2 = client.cmsQuery("B", "foo", "bar", "baz"); assertEquals(Arrays.asList(2L, 3L, 1L), q2); client.cmsMerge("C", "A", "B"); List q3 = client.cmsQuery("C", "foo", "bar", "baz"); assertEquals(Arrays.asList(7L, 6L, 10L), q3); Map keysAndWeights = new HashMap<>(); keysAndWeights.put("A", 1L); keysAndWeights.put("B", 2L); client.cmsMerge("C", keysAndWeights); List q4 = client.cmsQuery("C", "foo", "bar", "baz"); assertEquals(Arrays.asList(9L, 9L, 11L), q4); keysAndWeights.clear(); keysAndWeights.put("A", 2L); keysAndWeights.put("B", 3L); client.cmsMerge("C", keysAndWeights); List q5 = client.cmsQuery("C", "foo", "bar", "baz"); assertEquals(Arrays.asList(16L, 15L, 21L), q5); } } ================================================ FILE: src/test/java/redis/clients/jedis/modules/bloom/CuckooTest.java ================================================ package redis.clients.jedis.modules.bloom; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertThrows; import static org.junit.jupiter.api.Assertions.assertTrue; import static org.junit.jupiter.api.Assertions.fail; import java.util.Arrays; import java.util.Collections; import java.util.Map; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Timeout; import org.junit.jupiter.params.ParameterizedClass; import org.junit.jupiter.params.provider.MethodSource; import redis.clients.jedis.RedisProtocol; import redis.clients.jedis.bloom.CFInsertParams; import redis.clients.jedis.bloom.CFReserveParams; import redis.clients.jedis.exceptions.JedisDataException; import redis.clients.jedis.modules.RedisModuleCommandsTestBase; /** * Tests for the Cuckoo Filter Implementation */ @ParameterizedClass @MethodSource("redis.clients.jedis.commands.CommandsTestsParameters#respVersions") public class CuckooTest extends RedisModuleCommandsTestBase { @BeforeAll public static void prepare() { RedisModuleCommandsTestBase.prepare(); } public CuckooTest(RedisProtocol protocol) { super(protocol); } @Test public void testReservationCapacityOnly() { client.cfReserve("cuckoo1", 10); Map info = client.cfInfo("cuckoo1"); assertEquals(8L, info.get("Number of buckets")); assertEquals(0L, info.get("Number of items inserted")); assertEquals(72L, info.get("Size")); assertEquals(1L, info.get("Expansion rate")); assertEquals(1L, info.get("Number of filters")); assertEquals(2L, info.get("Bucket size")); assertEquals(20L, info.get("Max iterations")); assertEquals(0L, info.get("Number of items deleted")); } @Test public void testReservationCapacityAndBucketSize() { client.cfReserve("cuckoo2", 200, CFReserveParams.reserveParams().bucketSize(10)); Map info = client.cfInfo("cuckoo2"); assertEquals(32L, info.get("Number of buckets")); assertEquals(0L, info.get("Number of items inserted")); assertEquals(376L, info.get("Size")); assertEquals(1L, info.get("Expansion rate")); assertEquals(1L, info.get("Number of filters")); assertEquals(10L, info.get("Bucket size")); assertEquals(20L, info.get("Max iterations")); assertEquals(0L, info.get("Number of items deleted")); } @Test public void testReservationCapacityAndBucketSizeAndMaxIterations() { client.cfReserve("cuckoo3", 200, CFReserveParams.reserveParams() .bucketSize(10).maxIterations(20)); Map info = client.cfInfo("cuckoo3"); assertEquals(32L, info.get("Number of buckets")); assertEquals(0L, info.get("Number of items inserted")); assertEquals(376L, info.get("Size")); assertEquals(1L, info.get("Expansion rate")); assertEquals(1L, info.get("Number of filters")); assertEquals(10L, info.get("Bucket size")); assertEquals(20L, info.get("Max iterations")); assertEquals(0L, info.get("Number of items deleted")); } @Test public void testReservationAllParams() { client.cfReserve("cuckoo4", 200, CFReserveParams.reserveParams() .bucketSize(10).expansion(4).maxIterations(20)); Map info = client.cfInfo("cuckoo4"); assertEquals(32L, info.get("Number of buckets")); assertEquals(0L, info.get("Number of items inserted")); assertEquals(376L, info.get("Size")); assertEquals(4L, info.get("Expansion rate")); assertEquals(1L, info.get("Number of filters")); assertEquals(10L, info.get("Bucket size")); assertEquals(20L, info.get("Max iterations")); assertEquals(0L, info.get("Number of items deleted")); } @Test public void testAdd() { client.cfReserve("cuckoo5", 64000); assertTrue(client.cfAdd("cuckoo5", "test")); Map info = client.cfInfo("cuckoo5"); assertEquals(1L, info.get("Number of items inserted")); } @Test public void testAddNxItemDoesExist() { client.cfReserve("cuckoo6", 64000); assertTrue(client.cfAddNx("cuckoo6", "filter")); } @Test public void testAddNxItemExists() { client.cfReserve("cuckoo7", 64000); client.cfAdd("cuckoo7", "filter"); assertFalse(client.cfAddNx("cuckoo7", "filter")); } @Test public void testInsert() { assertEquals(Arrays.asList(true), client.cfInsert("cuckoo8", "foo")); } @Test public void testInsertWithCapacity() { assertEquals(Arrays.asList(true), client.cfInsert("cuckoo9", CFInsertParams.insertParams().capacity(1000), "foo")); } @Test public void testInsertNoCreateFilterDoesNotExist() { try { client.cfInsert("cuckoo10", CFInsertParams.insertParams().noCreate(), "foo", "bar"); fail(); } catch (JedisDataException e) { assertEquals("ERR not found", e.getMessage()); } } @Test public void testInsertNoCreateFilterExists() { client.cfInsert("cuckoo11", "bar"); assertEquals(Arrays.asList(true, true), client.cfInsert("cuckoo11", CFInsertParams.insertParams().noCreate(), "foo", "bar")); } @Test public void testInsertNx() { assertEquals(Arrays.asList(true), client.cfInsertNx("cuckoo12", "bar")); } @Test public void testInsertNxWithCapacity() { client.cfInsertNx("cuckoo13", "bar"); assertEquals(Arrays.asList(false), client.cfInsertNx("cuckoo13", CFInsertParams.insertParams().capacity(1000), "bar")); } @Test public void testInsertNxMultiple() { client.cfInsertNx("cuckoo14", "foo"); client.cfInsertNx("cuckoo14", "bar"); assertEquals(Arrays.asList(false, false, true), client.cfInsertNx("cuckoo14", "foo", "bar", "baz")); } @Test public void testInsertNxNoCreate() { try { client.cfInsertNx("cuckoo15", CFInsertParams.insertParams().noCreate(), "foo", "bar"); fail(); } catch (JedisDataException e) { assertEquals("ERR not found", e.getMessage()); } } @Test public void testExistsItemDoesntExist() { assertFalse(client.cfExists("cuckoo16", "foo")); assertEquals(Collections.singletonList(false), client.cfMExists("cuckoo16", "foo")); } @Test public void testExistsItemExists() { client.cfInsert("cuckoo17", "foo"); assertTrue(client.cfExists("cuckoo17", "foo")); assertEquals(Collections.singletonList(true), client.cfMExists("cuckoo17", "foo")); } @Test public void testMExistsMixedItems() { client.cfInsert("cuckoo27", "foo"); assertEquals(Arrays.asList(true, false), client.cfMExists("cuckoo27", "foo", "bar")); assertEquals(Arrays.asList(false, true), client.cfMExists("cuckoo27", "bar", "foo")); } @Test public void testDeleteItemDoesntExist() { client.cfInsert("cuckoo8", "bar"); assertFalse(client.cfDel("cuckoo8", "foo")); } @Test public void testDeleteItemExists() { client.cfInsert("cuckoo18", "foo"); assertTrue(client.cfDel("cuckoo18", "foo")); } @Test public void testDeleteFilterDoesNotExist() { Exception ex = assertThrows(JedisDataException.class, () -> { client.cfDel("cuckoo19", "foo"); }); assertTrue(ex.getMessage().contains("Not found")); } @Test public void testCountFilterDoesNotExist() { assertEquals(0L, client.cfCount("cuckoo20", "filter")); } @Test public void testCountFilterExist() { client.cfInsert("cuckoo21", "foo"); assertEquals(0L, client.cfCount("cuckoo21", "filter")); } @Test public void testCountItemExists() { client.cfInsert("cuckoo22", "foo"); assertEquals(1L, client.cfCount("cuckoo22", "foo")); } @Test public void testInfoFilterDoesNotExists() { Exception ex = assertThrows(JedisDataException.class, () -> { client.cfInfo("cuckoo23"); }); assertTrue(ex.getMessage().contains("ERR not found")); } @Test @Timeout(2) public void testScanDumpAndLoadChunk() { client.cfReserve("cuckoo24", 100L /*capacity*/, CFReserveParams.reserveParams().bucketSize(50)); client.cfAdd("cuckoo24-dump", "a"); long iterator = 0; while (true) { Map.Entry chunkData = client.cfScanDump("cuckoo24-dump", iterator); iterator = chunkData.getKey(); if (iterator == 0L) break; assertEquals("OK", client.cfLoadChunk("cuckoo24-load", iterator, chunkData.getValue())); } // check for properties assertEquals(client.cfInfo("cuckoo24-dump"), client.cfInfo("cuckoo24-load")); // check for existing items assertTrue(client.cfExists("cuckoo24-load", "a")); } } ================================================ FILE: src/test/java/redis/clients/jedis/modules/bloom/TDigestTest.java ================================================ package redis.clients.jedis.modules.bloom; import static java.util.Collections.singletonList; import static org.junit.jupiter.api.Assertions.assertEquals; import java.util.Arrays; import java.util.Map; import java.util.Random; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.Test; import org.junit.jupiter.params.ParameterizedClass; import org.junit.jupiter.params.provider.MethodSource; import redis.clients.jedis.RedisProtocol; import redis.clients.jedis.bloom.TDigestMergeParams; import redis.clients.jedis.modules.RedisModuleCommandsTestBase; @ParameterizedClass @MethodSource("redis.clients.jedis.commands.CommandsTestsParameters#respVersions") public class TDigestTest extends RedisModuleCommandsTestBase { private static final Random random = new Random(); @BeforeAll public static void prepare() { RedisModuleCommandsTestBase.prepare(); } public TDigestTest(RedisProtocol protocol) { super(protocol); } private void assertMergedUnmergedNodes(String key, int mergedNodes, int unmergedNodes) { Map info = client.tdigestInfo(key); assertEquals(Long.valueOf(mergedNodes), info.get("Merged nodes")); assertEquals(Long.valueOf(unmergedNodes), info.get("Unmerged nodes")); } private void assertTotalWeight(String key, long totalWeight) { Map info = client.tdigestInfo(key); assertEquals(totalWeight, (Long) info.get("Merged weight") + (Long) info.get("Unmerged weight")); } @Test public void createSimple() { assertEquals("OK", client.tdigestCreate("td-simple")); Map info = client.tdigestInfo("td-simple"); assertEquals(100L, info.get("Compression")); } @Test public void createAndInfo() { for (int i = 100; i < 1000; i += 100) { String key = "td-" + i; assertEquals("OK", client.tdigestCreate(key, i)); Map info = client.tdigestInfo(key); assertEquals(Long.valueOf(i), info.get("Compression")); } } @Test public void reset() { client.tdigestCreate("reset", 100); assertMergedUnmergedNodes("reset", 0, 0); // on empty assertEquals("OK", client.tdigestReset("reset")); assertMergedUnmergedNodes("reset", 0, 0); // client.tdigestAdd("reset", randomValueWeight(), randomValueWeight(), randomValueWeight()); client.tdigestAdd("reset", randomValue(), randomValue(), randomValue()); assertMergedUnmergedNodes("reset", 0, 3); assertEquals("OK", client.tdigestReset("reset")); assertMergedUnmergedNodes("reset", 0, 0); } @Test public void add() { client.tdigestCreate("tdadd", 100); // assertEquals("OK", client.tdigestAdd("tdadd", randomValueWeight())); assertEquals("OK", client.tdigestAdd("tdadd", randomValue())); assertMergedUnmergedNodes("tdadd", 0, 1); // assertEquals("OK", client.tdigestAdd("tdadd", randomValueWeight(), randomValueWeight(), randomValueWeight(), randomValueWeight())); assertEquals("OK", client.tdigestAdd("tdadd", randomValue(), randomValue(), randomValue(), randomValue())); assertMergedUnmergedNodes("tdadd", 0, 5); } @Test public void merge() { client.tdigestCreate("td2", 100); client.tdigestCreate("td4m", 100); assertEquals("OK", client.tdigestMerge("td2", "td4m")); assertMergedUnmergedNodes("td2", 0, 0); // client.tdigestAdd("td2", definedValueWeight(1, 1), definedValueWeight(1, 1), definedValueWeight(1, 1)); // client.tdigestAdd("td4m", definedValueWeight(1, 100), definedValueWeight(1, 100)); client.tdigestAdd("td2", 1, 1, 1); client.tdigestAdd("td4m", 1, 1); assertEquals("OK", client.tdigestMerge("td2", "td4m")); assertMergedUnmergedNodes("td2", 3, 2); } @Test public void mergeMultiAndParams() { client.tdigestCreate("from1", 100); client.tdigestCreate("from2", 200); client.tdigestAdd("from1", 1d); client.tdigestAdd("from2", weightedValue(1d, 10)); assertEquals("OK", client.tdigestMerge("to", "from1", "from2")); assertTotalWeight("to", 11L); assertEquals("OK", client.tdigestMerge(TDigestMergeParams.mergeParams() .compression(50).override(), "to", "from1", "from2")); assertEquals(50L, client.tdigestInfo("to").get("Compression")); } @Test public void cdf() { client.tdigestCreate("tdcdf", 100); assertEquals(singletonList(Double.NaN), client.tdigestCDF("tdcdf", 50)); client.tdigestAdd("tdcdf", 1, 1, 1); client.tdigestAdd("tdcdf", 100, 100); assertEquals(singletonList(0.6), client.tdigestCDF("tdcdf", 50)); client.tdigestCDF("tdcdf", 25, 50, 75); } @Test public void quantile() { client.tdigestCreate("tdqnt", 100); assertEquals(singletonList(Double.NaN), client.tdigestQuantile("tdqnt", 0.5)); // client.tdigestAdd("tdqnt", definedValueWeight(1, 1), definedValueWeight(1, 1), definedValueWeight(1, 1)); // client.tdigestAdd("tdqnt", definedValueWeight(100, 1), definedValueWeight(100, 1)); client.tdigestAdd("tdqnt", 1, 1, 1); client.tdigestAdd("tdqnt", 100, 100); assertEquals(singletonList(1.0), client.tdigestQuantile("tdqnt", 0.5)); } @Test public void minAndMax() { final String key = "tdmnmx"; client.tdigestCreate(key, 100); assertEquals(Double.NaN, client.tdigestMin(key), 0d); assertEquals(Double.NaN, client.tdigestMax(key), 0d); // client.tdigestAdd(key, definedValueWeight(2, 1)); // client.tdigestAdd(key, definedValueWeight(5, 1)); client.tdigestAdd(key, 2); client.tdigestAdd(key, 5); assertEquals(2d, client.tdigestMin(key), 0.01); assertEquals(5d, client.tdigestMax(key), 0.01); } @Test public void trimmedMean() { final String key = "trimmed_mean"; client.tdigestCreate(key, 500); for (int i = 0; i < 20; i++) { // client.tdigestAdd(key, KeyValue.of(Double.valueOf(i), 1l)); client.tdigestAdd(key, (double) i); } assertEquals(9.5, client.tdigestTrimmedMean(key, 0.1, 0.9), 0.01); assertEquals(9.5, client.tdigestTrimmedMean(key, 0.0, 1.0), 0.01); assertEquals(4.5, client.tdigestTrimmedMean(key, 0.0, 0.5), 0.01); assertEquals(14.5, client.tdigestTrimmedMean(key, 0.5, 1.0), 0.01); } @Test public void rankCommands() { final String key = "ranks"; client.tdigestCreate(key); client.tdigestAdd(key, 2d, 3d, 5d); assertEquals(Arrays.asList(0L, 2L), client.tdigestRank(key, 2d, 4d)); assertEquals(Arrays.asList(0L, 1L), client.tdigestRevRank(key, 5d, 4d)); assertEquals(Arrays.asList(2d, 3d), client.tdigestByRank(key, 0L, 1L)); assertEquals(Arrays.asList(5d, 3d), client.tdigestByRevRank(key, 0L, 1L)); } private static double randomValue() { return random.nextDouble() * 10000; } private static double[] weightedValue(double value, int weight) { double[] values = new double[weight]; Arrays.fill(values, value); return values; } } ================================================ FILE: src/test/java/redis/clients/jedis/modules/bloom/TopKTest.java ================================================ package redis.clients.jedis.modules.bloom; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertNull; import static org.junit.jupiter.api.Assertions.assertTrue; import java.util.Arrays; import java.util.Collections; import java.util.Map; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.Test; import org.junit.jupiter.params.ParameterizedClass; import org.junit.jupiter.params.provider.MethodSource; import redis.clients.jedis.RedisProtocol; import redis.clients.jedis.modules.RedisModuleCommandsTestBase; @ParameterizedClass @MethodSource("redis.clients.jedis.commands.CommandsTestsParameters#respVersions") public class TopKTest extends RedisModuleCommandsTestBase { @BeforeAll public static void prepare() { RedisModuleCommandsTestBase.prepare(); } public TopKTest(RedisProtocol protocol) { super(protocol); } @Test public void createTopKFilter() { client.topkReserve("aaa", 30, 2000, 7, 0.925); assertEquals(Arrays.asList(null, null), client.topkAdd("aaa", "bb", "cc")); assertEquals(Arrays.asList(true, false, true), client.topkQuery("aaa", "bb", "gg", "cc")); assertEquals(Arrays.asList("bb", "cc"), client.topkList("aaa")); Map listWithCount = client.topkListWithCount("aaa"); assertEquals(2, listWithCount.size()); listWithCount.forEach((item, count) -> { assertTrue(Arrays.asList("bb", "cc").contains(item)); assertEquals(Long.valueOf(1), count); }); assertNull(client.topkIncrBy("aaa", "ff", 5)); assertEquals(Arrays.asList("ff", "bb", "cc"), client.topkList("aaa")); assertEquals(Collections.singletonList(null), client.topkIncrBy("aaa", Collections.singletonMap("ff", 8L))); assertEquals(Long.valueOf(13), client.topkListWithCount("aaa").get("ff")); } } ================================================ FILE: src/test/java/redis/clients/jedis/modules/json/JsonObjects.java ================================================ package redis.clients.jedis.modules.json; import java.time.Instant; import java.util.List; import java.util.Objects; public class JsonObjects { /* A simple class that represents an object in real life */ @SuppressWarnings("unused") public static class IRLObject { public String str; public boolean bool; public IRLObject() { this.str = "string"; this.bool = true; } @Override public boolean equals(Object obj) { if (this == obj) return true; if (obj == null) return false; if (getClass() != obj.getClass()) return false; final IRLObject other = (IRLObject) obj; return Objects.equals(str, other.str) && Objects.equals(bool, other.bool); } } @SuppressWarnings("unused") public static class FooBarObject { public String foo; public boolean fooB; public int fooI; public float fooF; public String[] fooArr; public FooBarObject() { this.foo = "bar"; this.fooB = true; this.fooI = 6574; this.fooF = 435.345f; this.fooArr = new String[]{"a", "b", "c"}; } } public static class Baz { String quuz; private String grault; private String waldo; public Baz(final String quuz, final String grault, final String waldo) { this.quuz = quuz; this.grault = grault; this.waldo = waldo; } @Override public boolean equals(Object o) { if (this == o) { return true; } if (o == null) { return false; } if (getClass() != o.getClass()) { return false; } Baz other = (Baz) o; return Objects.equals(quuz, other.quuz) && Objects.equals(grault, other.grault) && Objects.equals(waldo, other.waldo); } } public static class Qux { private String quux; private String corge; private String garply; private Baz baz; public Qux(final String quux, final String corge, final String garply, final Baz baz) { this.quux = quux; this.corge = corge; this.garply = garply; this.baz = baz; } @Override public boolean equals(Object o) { if (this == o) { return true; } if (o == null) { return false; } if (getClass() != o.getClass()) { return false; } Qux other = (Qux) o; return Objects.equals(quux, other.quux) && Objects.equals(corge, other.corge) && Objects.equals(garply, other.garply) && Objects.equals(baz, other.baz); } } public static class Person { public String name; public int age; public String address; public String phone; public List childrens; public Person(String name, int age, String address, String phone, List childrens) { this.name = name; this.age = age; this.address = address; this.phone = phone; this.childrens = childrens; } @Override public boolean equals(Object o) { if (this == o) { return true; } if (o == null) { return false; } // if (getClass() != o.getClass()) { // return false; // } Person other = (Person) o; return Objects.equals(name, other.name) && Objects.equals(age, other.age) && Objects.equals(address, other.address) && Objects.equals(phone, other.phone) && Objects.equals(childrens, other.childrens); } } public static class Tick { private final String id; private final Instant created; public Tick(String id, Instant created) { this.id = id; this.created = created; } public String getId() { return id; } public Instant getCreated() { return created; } } } ================================================ FILE: src/test/java/redis/clients/jedis/modules/json/Path2Test.java ================================================ package redis.clients.jedis.modules.json; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertThrows; import org.junit.jupiter.api.Test; import redis.clients.jedis.json.Path2; public class Path2Test { @Test public void _null() { assertThrows(NullPointerException.class, ()->Path2.of(null)); } @Test public void empty() { assertThrows(IllegalArgumentException.class,()->Path2.of("")); } @Test public void root() { assertEquals("$", Path2.ROOT_PATH.toString()); assertEquals(Path2.ROOT_PATH, new Path2("$")); assertEquals(Path2.ROOT_PATH, Path2.of("$")); } @Test public void test() { assertEquals("$.a.b", Path2.of("$.a.b").toString()); assertEquals("$.a.b", new Path2("$.a.b").toString()); assertEquals("$.a.b", Path2.of(".a.b").toString()); assertEquals("$.a.b", new Path2(".a.b").toString()); assertEquals("$.a.b", Path2.of("a.b").toString()); assertEquals("$.a.b", new Path2("a.b").toString()); } @Test public void equals() { assertEquals(new Path2("a.b"), Path2.of(".a.b")); assertEquals(Path2.of("a.b"), new Path2(".a.b")); } } ================================================ FILE: src/test/java/redis/clients/jedis/modules/json/PathTest.java ================================================ package redis.clients.jedis.modules.json; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertNotEquals; import org.junit.jupiter.api.Test; import redis.clients.jedis.json.Path; public class PathTest { @Test public void testRootPathConstant() { assertEquals(".", Path.ROOT_PATH.toString()); } @Test public void testStaticFactoryMethod() { assertEquals(".a.b", Path.of(".a.b").toString()); } @Test public void testPathEquals() { assertEquals(Path.of(".a.b.c"), Path.of(".a.b.c")); assertNotEquals(Path.of(".a.b.c"), Path.of(".b.c")); assertNotEquals(Path.of(".a.b.c"), null); assertNotEquals(Path.of(".a.b.c"), ".a.b.c"); Path aPath = Path.of(".a"); assertEquals(aPath, aPath); } @Test public void testPathHashCode() { assertEquals(Path.of(".a.b.c").hashCode(), Path.of(".a.b.c").hashCode()); assertNotEquals(Path.of(".a.b.c").hashCode(), Path.of(".b.c").hashCode()); } } ================================================ FILE: src/test/java/redis/clients/jedis/modules/json/RedisJsonV1Test.java ================================================ package redis.clients.jedis.modules.json; import static org.junit.jupiter.api.Assertions.assertArrayEquals; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertNotEquals; import static org.junit.jupiter.api.Assertions.assertSame; import static org.junit.jupiter.api.Assertions.assertThrows; import static org.junit.jupiter.api.Assertions.assertTrue; import static org.junit.jupiter.api.Assertions.fail; import static org.junit.jupiter.api.Assertions.assertNull; import static redis.clients.jedis.json.Path.ROOT_PATH; import static redis.clients.jedis.modules.json.JsonObjects.*; import com.google.gson.Gson; import com.google.gson.JsonArray; import com.google.gson.JsonObject; import java.time.Instant; import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; import java.util.List; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import redis.clients.jedis.RedisProtocol; import redis.clients.jedis.exceptions.JedisDataException; import redis.clients.jedis.json.JsonSetParams; import redis.clients.jedis.json.Path; import redis.clients.jedis.json.commands.RedisJsonV1Commands; import redis.clients.jedis.modules.RedisModuleCommandsTestBase; import redis.clients.jedis.util.JsonObjectMapperTestUtil; /** * V1 of the RedisJSON is only supported with RESP2, hence this test is not parameterized. */ public class RedisJsonV1Test extends RedisModuleCommandsTestBase { private final Gson gson = new Gson(); private RedisJsonV1Commands jsonV1; @BeforeAll public static void prepare() { RedisModuleCommandsTestBase.prepare(); } public RedisJsonV1Test() { super(RedisProtocol.RESP2); } @BeforeEach @Override public void setUp() { super.setUp(); this.jsonV1 = super.client; } @Test public void basicSetGetShouldSucceed() { // naive set with a path // jsonClient.jsonSet("null", null, ROOT_PATH); jsonV1.jsonSet("null", ROOT_PATH, (Object) null); assertNull(jsonV1.jsonGet("null", String.class, ROOT_PATH)); // real scalar value and no path jsonV1.jsonSet("str", ROOT_PATH, "strong"); assertEquals("strong", jsonV1.jsonGet("str")); // a slightly more complex object IRLObject obj = new IRLObject(); jsonV1.jsonSet("obj", ROOT_PATH, obj); Object expected = gson.fromJson(gson.toJson(obj), Object.class); assertTrue(expected.equals(jsonV1.jsonGet("obj"))); // check an update Path p = Path.of(".str"); jsonV1.jsonSet("obj", p, "strung"); assertEquals("strung", jsonV1.jsonGet("obj", String.class, p)); } @Test public void setExistingPathOnlyIfExistsShouldSucceed() { jsonV1.jsonSet("obj", ROOT_PATH, new IRLObject()); Path p = Path.of(".str"); jsonV1.jsonSet("obj", p, "strangle", JsonSetParams.jsonSetParams().xx()); assertEquals("strangle", jsonV1.jsonGet("obj", String.class, p)); } @Test public void setNonExistingOnlyIfNotExistsShouldSucceed() { jsonV1.jsonSet("obj", ROOT_PATH, new IRLObject()); Path p = Path.of(".none"); jsonV1.jsonSet("obj", p, "strangle", JsonSetParams.jsonSetParams().nx()); assertEquals("strangle", jsonV1.jsonGet("obj", String.class, p)); } @Test public void setWithoutAPathDefaultsToRootPath() { jsonV1.jsonSet("obj1", ROOT_PATH, new IRLObject()); // jsonClient.jsonSet("obj1", "strangle", JsonSetParams.jsonSetParams().xx()); jsonV1.jsonSetLegacy("obj1", (Object) "strangle", JsonSetParams.jsonSetParams().xx()); assertEquals("strangle", jsonV1.jsonGet("obj1", String.class, ROOT_PATH)); } @Test public void setExistingPathOnlyIfNotExistsShouldFail() { jsonV1.jsonSet("obj", ROOT_PATH, new IRLObject()); Path p = Path.of(".str"); assertNull(jsonV1.jsonSet("obj", p, "strangle", JsonSetParams.jsonSetParams().nx())); } @Test public void setNonExistingPathOnlyIfExistsShouldFail() { jsonV1.jsonSet("obj", ROOT_PATH, new IRLObject()); Path p = Path.of(".none"); assertNull(jsonV1.jsonSet("obj", p, "strangle", JsonSetParams.jsonSetParams().xx())); } @Test public void setException() { // should error on non root path for new key assertThrows(JedisDataException.class, () -> jsonV1.jsonSet("test", Path.of(".foo"), "bar")); } @Test public void getMultiplePathsShouldSucceed() { // check multiple paths IRLObject obj = new IRLObject(); jsonV1.jsonSetLegacy("obj", obj); Object expected = gson.fromJson(gson.toJson(obj), Object.class); assertTrue(expected.equals(jsonV1.jsonGet("obj", Object.class, Path.of("bool"), Path.of("str")))); } @Test public void toggle() { IRLObject obj = new IRLObject(); jsonV1.jsonSetLegacy("obj", obj); Path pbool = Path.of(".bool"); // check initial value assertTrue(jsonV1.jsonGet("obj", Boolean.class, pbool)); // true -> false jsonV1.jsonToggle("obj", pbool); assertFalse(jsonV1.jsonGet("obj", Boolean.class, pbool)); // false -> true jsonV1.jsonToggle("obj", pbool); assertTrue(jsonV1.jsonGet("obj", Boolean.class, pbool)); // ignore non-boolean field Path pstr = Path.of(".str"); try { jsonV1.jsonToggle("obj", pstr); fail("String not a bool"); } catch (JedisDataException jde) { assertTrue(jde.getMessage().contains("not a bool")); } assertEquals("string", jsonV1.jsonGet("obj", String.class, pstr)); } @Test public void getAbsent() { jsonV1.jsonSet("test", ROOT_PATH, "foo"); assertThrows(JedisDataException.class, () -> jsonV1.jsonGet("test", String.class, Path.of(".bar"))); } @Test public void delValidShouldSucceed() { // check deletion of a single path jsonV1.jsonSet("obj", ROOT_PATH, new IRLObject()); assertEquals(1L, jsonV1.jsonDel("obj", Path.of(".str"))); assertTrue(client.exists("obj")); // check deletion root using default root -> key is removed assertEquals(1L, jsonV1.jsonDel("obj")); assertFalse(client.exists("obj")); } @Test public void delNonExistingPathsAreIgnored() { jsonV1.jsonSet("foobar", ROOT_PATH, new FooBarObject()); assertEquals(0L, jsonV1.jsonDel("foobar", Path.of(".foo[1]"))); } @Test public void typeChecksShouldSucceed() { assertNull(jsonV1.jsonType("foobar")); jsonV1.jsonSet("foobar", ROOT_PATH, new FooBarObject()); assertSame(Object.class, jsonV1.jsonType("foobar")); assertSame(Object.class, jsonV1.jsonType("foobar", ROOT_PATH)); assertSame(String.class, jsonV1.jsonType("foobar", Path.of(".foo"))); assertSame(int.class, jsonV1.jsonType("foobar", Path.of(".fooI"))); assertSame(float.class, jsonV1.jsonType("foobar", Path.of(".fooF"))); assertSame(List.class, jsonV1.jsonType("foobar", Path.of(".fooArr"))); assertSame(boolean.class, jsonV1.jsonType("foobar", Path.of(".fooB"))); assertNull(jsonV1.jsonType("foobar", Path.of(".fooErr"))); } @Test public void testJsonMerge() { // create data List childrens = new ArrayList<>(); childrens.add("Child 1"); Person person = new Person("John Doe", 25, "123 Main Street", "123-456-7890", childrens); assertEquals("OK", jsonV1.jsonSet("test_merge", ROOT_PATH, person)); // After 5 years: person.age = 30; person.childrens.add("Child 2"); person.childrens.add("Child 3"); // merge the new data assertEquals("OK", jsonV1.jsonMerge("test_merge", Path.of((".childrens")), person.childrens)); assertEquals("OK", jsonV1.jsonMerge("test_merge", Path.of((".age")), person.age)); assertEquals(person, jsonV1.jsonGet("test_merge", Person.class)); } @Test public void mgetWithPathWithAllKeysExist() { Baz baz1 = new Baz("quuz1", "grault1", "waldo1"); Baz baz2 = new Baz("quuz2", "grault2", "waldo2"); Qux qux1 = new Qux("quux1", "corge1", "garply1", baz1); Qux qux2 = new Qux("quux2", "corge2", "garply2", baz2); jsonV1.jsonSetLegacy("qux1", qux1); jsonV1.jsonSetLegacy("qux2", qux2); List allBaz = jsonV1.jsonMGet(Path.of("baz"), Baz.class, "qux1", "qux2"); assertEquals(2, allBaz.size()); Baz testBaz1 = allBaz.stream() // .filter(b -> b.quuz.equals("quuz1")) // .findFirst() // .orElseThrow(() -> new NullPointerException("")); Baz testBaz2 = allBaz.stream() // .filter(q -> q.quuz.equals("quuz2")) // .findFirst() // .orElseThrow(() -> new NullPointerException("")); assertEquals(baz1, testBaz1); assertEquals(baz2, testBaz2); } @Test public void mgetAtRootPathWithMissingKeys() { Baz baz1 = new Baz("quuz1", "grault1", "waldo1"); Baz baz2 = new Baz("quuz2", "grault2", "waldo2"); Qux qux1 = new Qux("quux1", "corge1", "garply1", baz1); Qux qux2 = new Qux("quux2", "corge2", "garply2", baz2); jsonV1.jsonSetLegacy("qux1", qux1); jsonV1.jsonSetLegacy("qux2", qux2); List allQux = jsonV1.jsonMGet(Qux.class, "qux1", "qux2", "qux3"); assertEquals(3, allQux.size()); assertNull(allQux.get(2)); allQux.removeAll(Collections.singleton(null)); assertEquals(2, allQux.size()); } @Test public void arrLen() { jsonV1.jsonSet("foobar", ROOT_PATH, new FooBarObject()); assertEquals(Long.valueOf(3), jsonV1.jsonArrLen("foobar", Path.of(".fooArr"))); } @Test public void arrLenDefaultPath() { assertNull(jsonV1.jsonArrLen("array")); jsonV1.jsonSetLegacy("array", new int[]{1, 2, 3}); assertEquals(Long.valueOf(3), jsonV1.jsonArrLen("array")); } @Test public void clearArray() { jsonV1.jsonSet("foobar", ROOT_PATH, new FooBarObject()); Path arrPath = Path.of(".fooArr"); assertEquals(Long.valueOf(3), jsonV1.jsonArrLen("foobar", arrPath)); assertEquals(1L, jsonV1.jsonClear("foobar", arrPath)); assertEquals(Long.valueOf(0), jsonV1.jsonArrLen("foobar", arrPath)); // ignore non-array Path strPath = Path.of("foo"); assertEquals(0L, jsonV1.jsonClear("foobar", strPath)); assertEquals("bar", jsonV1.jsonGet("foobar", String.class, strPath)); } @Test public void clearObject() { Baz baz = new Baz("quuz", "grault", "waldo"); Qux qux = new Qux("quux", "corge", "garply", baz); jsonV1.jsonSetLegacy("qux", qux); Path objPath = Path.of("baz"); assertEquals(baz, jsonV1.jsonGet("qux", Baz.class, objPath)); assertEquals(1L, jsonV1.jsonClear("qux", objPath)); assertEquals(new Baz(null, null, null), jsonV1.jsonGet("qux", Baz.class, objPath)); } @Test public void arrAppendSameType() { String json = "{ a: 'hello', b: [1, 2, 3], c: { d: ['ello'] }}"; JsonObject jsonObject = gson.fromJson(json, JsonObject.class); jsonV1.jsonSet("test_arrappend", ROOT_PATH, jsonObject); assertEquals(Long.valueOf(6), jsonV1.jsonArrAppend("test_arrappend", Path.of(".b"), 4, 5, 6)); Integer[] array = jsonV1.jsonGet("test_arrappend", Integer[].class, Path.of(".b")); assertArrayEquals(new Integer[]{1, 2, 3, 4, 5, 6}, array); } @Test public void arrAppendMultipleTypes() { String json = "{ a: 'hello', b: [1, 2, 3], c: { d: ['ello'] }}"; JsonObject jsonObject = gson.fromJson(json, JsonObject.class); jsonV1.jsonSet("test_arrappend", ROOT_PATH, jsonObject); assertEquals(Long.valueOf(6), jsonV1.jsonArrAppend("test_arrappend", Path.of(".b"), "foo", true, null)); Object[] array = jsonV1.jsonGet("test_arrappend", Object[].class, Path.of(".b")); // NOTE: GSon converts numeric types to the most accommodating type (Double) // when type information is not provided (as in the Object[] below) assertArrayEquals(new Object[]{1.0, 2.0, 3.0, "foo", true, null}, array); } @Test public void arrAppendMultipleTypesWithDeepPath() { String json = "{ a: 'hello', b: [1, 2, 3], c: { d: ['ello'] }}"; JsonObject jsonObject = gson.fromJson(json, JsonObject.class); jsonV1.jsonSet("test_arrappend", ROOT_PATH, jsonObject); assertEquals(Long.valueOf(4), jsonV1.jsonArrAppend("test_arrappend", Path.of(".c.d"), "foo", true, null)); Object[] array = jsonV1.jsonGet("test_arrappend", Object[].class, Path.of(".c.d")); assertArrayEquals(new Object[]{"ello", "foo", true, null}, array); } @Test public void arrAppendAgaintsEmptyArray() { String json = "{ a: 'hello', b: [1, 2, 3], c: { d: [] }}"; JsonObject jsonObject = gson.fromJson(json, JsonObject.class); jsonV1.jsonSet("test_arrappend", ROOT_PATH, jsonObject); assertEquals(Long.valueOf(3), jsonV1.jsonArrAppend("test_arrappend", Path.of(".c.d"), "a", "b", "c")); String[] array = jsonV1.jsonGet("test_arrappend", String[].class, Path.of(".c.d")); assertArrayEquals(new String[]{"a", "b", "c"}, array); } @Test public void arrAppendPathIsNotArray() { String json = "{ a: 'hello', b: [1, 2, 3], c: { d: ['ello'] }}"; JsonObject jsonObject = gson.fromJson(json, JsonObject.class); jsonV1.jsonSet("test_arrappend", ROOT_PATH, jsonObject); assertThrows(JedisDataException.class, () -> jsonV1.jsonArrAppend("test_arrappend", Path.of(".a"), 1)); } @Test public void arrIndexAbsentKey() { assertThrows(JedisDataException.class, () -> jsonV1.jsonArrIndex("quxquux", ROOT_PATH, gson.toJson(new Object()))); } @Test public void arrIndexWithInts() { jsonV1.jsonSet("quxquux", ROOT_PATH, new int[]{8, 6, 7, 5, 3, 0, 9}); assertEquals(2L, jsonV1.jsonArrIndex("quxquux", ROOT_PATH, 7)); assertEquals(-1L, jsonV1.jsonArrIndex("quxquux", ROOT_PATH, "7")); } @Test public void arrIndexWithStrings() { jsonV1.jsonSet("quxquux", ROOT_PATH, new String[]{"8", "6", "7", "5", "3", "0", "9"}); assertEquals(2L, jsonV1.jsonArrIndex("quxquux", ROOT_PATH, "7")); } @Test public void arrIndexWithStringsAndPath() { jsonV1.jsonSet("foobar", ROOT_PATH, new FooBarObject()); assertEquals(1L, jsonV1.jsonArrIndex("foobar", Path.of(".fooArr"), "b")); } @Test public void arrIndexNonExistentPath() { jsonV1.jsonSet("foobar", ROOT_PATH, new FooBarObject()); assertThrows(JedisDataException.class, () -> assertEquals(1L, jsonV1.jsonArrIndex("foobar", Path.of(".barArr"), "x"))); } @Test public void arrInsert() { String json = "['hello', 'world', true, 1, 3, null, false]"; JsonArray jsonArray = gson.fromJson(json, JsonArray.class); jsonV1.jsonSet("test_arrinsert", ROOT_PATH, jsonArray); assertEquals(8L, jsonV1.jsonArrInsert("test_arrinsert", ROOT_PATH, 1, "foo")); Object[] array = jsonV1.jsonGet("test_arrinsert", Object[].class, ROOT_PATH); // NOTE: GSon converts numeric types to the most accommodating type (Double) // when type information is not provided (as in the Object[] below) assertArrayEquals(new Object[]{"hello", "foo", "world", true, 1.0, 3.0, null, false}, array); } @Test public void arrInsertWithNegativeIndex() { String json = "['hello', 'world', true, 1, 3, null, false]"; JsonArray jsonArray = gson.fromJson(json, JsonArray.class); jsonV1.jsonSet("test_arrinsert", ROOT_PATH, jsonArray); assertEquals(8L, jsonV1.jsonArrInsert("test_arrinsert", ROOT_PATH, -1, "foo")); Object[] array = jsonV1.jsonGet("test_arrinsert", Object[].class, ROOT_PATH); assertArrayEquals(new Object[]{"hello", "world", true, 1.0, 3.0, null, "foo", false}, array); } @Test public void testArrayPop() { jsonV1.jsonSet("arr", ROOT_PATH, new int[]{0, 1, 2, 3, 4}); assertEquals(Long.valueOf(4), jsonV1.jsonArrPop("arr", Long.class, ROOT_PATH)); assertEquals(Long.valueOf(3), jsonV1.jsonArrPop("arr", Long.class, ROOT_PATH, -1)); assertEquals(Long.valueOf(2), jsonV1.jsonArrPop("arr", Long.class)); assertEquals(Long.valueOf(0), jsonV1.jsonArrPop("arr", Long.class, ROOT_PATH, 0)); assertEquals(Double.valueOf(1), jsonV1.jsonArrPop("arr")); } @Test public void arrTrim() { jsonV1.jsonSet("arr", ROOT_PATH, new int[]{0, 1, 2, 3, 4}); assertEquals(Long.valueOf(3), jsonV1.jsonArrTrim("arr", ROOT_PATH, 1, 3)); assertArrayEquals(new Integer[]{1, 2, 3}, jsonV1.jsonGet("arr", Integer[].class, ROOT_PATH)); } @Test public void strAppend() { jsonV1.jsonSet("str", ROOT_PATH, "foo"); assertEquals(6L, jsonV1.jsonStrAppend("str", ROOT_PATH, "bar")); assertEquals("foobar", jsonV1.jsonGet("str", String.class, ROOT_PATH)); assertEquals(8L, jsonV1.jsonStrAppend("str", "ed")); // assertEquals("foobared", jsonClient.jsonGet("str", String.class)); assertEquals("foobared", jsonV1.jsonGet("str")); } @Test public void strLen() { assertNull(jsonV1.jsonStrLen("str")); jsonV1.jsonSet("str", ROOT_PATH, "foo"); assertEquals(Long.valueOf(3), jsonV1.jsonStrLen("str")); assertEquals(Long.valueOf(3), jsonV1.jsonStrLen("str", ROOT_PATH)); } @Test public void numIncrBy() { jsonV1.jsonSetLegacy("doc", gson.fromJson("{a:3}", JsonObject.class)); assertEquals(5d, jsonV1.jsonNumIncrBy("doc", Path.of(".a"), 2), 0d); } @Test public void obj() { assertNull(jsonV1.jsonObjLen("doc")); assertNull(jsonV1.jsonObjKeys("doc")); assertNull(jsonV1.jsonObjLen("doc", ROOT_PATH)); assertNull(jsonV1.jsonObjKeys("doc", ROOT_PATH)); String json = "{\"a\":[3], \"nested\": {\"a\": {\"b\":2, \"c\": 1}}}"; jsonV1.jsonSetWithPlainString("doc", ROOT_PATH, json); assertEquals(Long.valueOf(2), jsonV1.jsonObjLen("doc")); assertEquals(Arrays.asList("a", "nested"), jsonV1.jsonObjKeys("doc")); assertEquals(Long.valueOf(2), jsonV1.jsonObjLen("doc", Path.of(".nested.a"))); assertEquals(Arrays.asList("b", "c"), jsonV1.jsonObjKeys("doc", Path.of(".nested.a"))); } @Test public void debugMemory() { assertEquals(0L, jsonV1.jsonDebugMemory("json")); assertEquals(0L, jsonV1.jsonDebugMemory("json", ROOT_PATH)); String json = "{ foo: 'bar', bar: { foo: 10 }}"; JsonObject jsonObject = gson.fromJson(json, JsonObject.class); jsonV1.jsonSet("json", ROOT_PATH, jsonObject); // it is okay as long as any 'long' is returned jsonV1.jsonDebugMemory("json"); jsonV1.jsonDebugMemory("json", ROOT_PATH); jsonV1.jsonDebugMemory("json", Path.of(".bar")); } @Test public void plainString() { String json = "{\"foo\":\"bar\",\"bar\":{\"foo\":10}}"; assertEquals("OK", jsonV1.jsonSetWithPlainString("plain", ROOT_PATH, json)); assertEquals(json, jsonV1.jsonGetAsPlainString("plain", ROOT_PATH)); } @Test public void testJsonGsonParser() { Tick person = new Tick("foo", Instant.now()); // setting the custom json gson parser client.setJsonObjectMapper(JsonObjectMapperTestUtil.getCustomGsonObjectMapper()); jsonV1.jsonSet(person.getId(), ROOT_PATH, person); String valueExpected = jsonV1.jsonGet(person.getId(), String.class, Path.of(".created")); assertEquals(valueExpected, person.getCreated().toString()); } @Test public void testDefaultJsonGsonParserStringsMustBeDifferent() { Tick tick = new Tick("foo", Instant.now()); // using the default json gson parser which is automatically configured jsonV1.jsonSet(tick.getId(), ROOT_PATH, tick); Object valueExpected = jsonV1.jsonGet(tick.getId(), Path.of(".created")); assertNotEquals(valueExpected, tick.getCreated().toString()); } @Test public void testJsonJacksonParser() { Tick person = new Tick("foo", Instant.now()); // setting the custom json jackson parser client.setJsonObjectMapper(JsonObjectMapperTestUtil.getCustomJacksonObjectMapper()); jsonV1.jsonSet(person.getId(), ROOT_PATH, person); String valueExpected = jsonV1.jsonGet(person.getId(), String.class, Path.of(".created")); assertEquals(valueExpected, person.getCreated().toString()); } } ================================================ FILE: src/test/java/redis/clients/jedis/modules/json/RedisJsonV2Test.java ================================================ package redis.clients.jedis.modules.json; import static java.util.Collections.singletonList; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertNull; import static org.junit.jupiter.api.Assertions.assertThrows; import static org.junit.jupiter.api.Assertions.assertTrue; import static org.junit.jupiter.api.Assertions.fail; import static org.junit.jupiter.api.Assumptions.assumeFalse; import static org.junit.jupiter.api.Assumptions.assumeTrue; import static redis.clients.jedis.json.Path2.ROOT_PATH; import static redis.clients.jedis.modules.json.JsonObjects.*; import com.google.gson.Gson; import java.util.Arrays; import java.util.Collections; import java.util.List; import org.json.JSONArray; import org.json.JSONObject; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.junit.jupiter.params.ParameterizedClass; import org.junit.jupiter.params.provider.MethodSource; import redis.clients.jedis.RedisProtocol; import redis.clients.jedis.exceptions.JedisDataException; import redis.clients.jedis.json.JsonSetParams; import redis.clients.jedis.json.Path2; import redis.clients.jedis.json.commands.RedisJsonV2Commands; import redis.clients.jedis.modules.RedisModuleCommandsTestBase; @ParameterizedClass @MethodSource("redis.clients.jedis.commands.CommandsTestsParameters#respVersions") public class RedisJsonV2Test extends RedisModuleCommandsTestBase { private static final Gson gson = new Gson(); private RedisJsonV2Commands jsonV2; @BeforeAll public static void prepare() { RedisModuleCommandsTestBase.prepare(); } public RedisJsonV2Test(RedisProtocol protocol) { super(protocol); } @BeforeEach @Override public void setUp() { super.setUp(); this.jsonV2 = super.client; } @Test public void basicSetGetShouldSucceed() { // naive set with a path jsonV2.jsonSetWithEscape("null", ROOT_PATH, (Object) null); assertJsonArrayEquals(jsonArray((Object) null), jsonV2.jsonGet("null", ROOT_PATH)); // real scalar value and no path jsonV2.jsonSetWithEscape("str", "strong"); assertEquals("strong", jsonV2.jsonGet("str")); // a slightly more complex object IRLObject obj = new IRLObject(); jsonV2.jsonSetWithEscape("obj", obj); Object expected = gson.fromJson(gson.toJson(obj), Object.class); assertEquals(expected, jsonV2.jsonGet("obj")); // check an update Path2 p = Path2.of(".str"); jsonV2.jsonSet("obj", p, gson.toJson("strung")); assertJsonArrayEquals(jsonArray("strung"), jsonV2.jsonGet("obj", p)); } @Test public void setExistingPathOnlyIfExistsShouldSucceed() { jsonV2.jsonSetWithEscape("obj", new IRLObject()); Path2 p = Path2.of(".str"); jsonV2.jsonSetWithEscape("obj", p, "strangle", JsonSetParams.jsonSetParams().xx()); assertJsonArrayEquals(jsonArray("strangle"), jsonV2.jsonGet("obj", p)); } @Test public void setNonExistingOnlyIfNotExistsShouldSucceed() { jsonV2.jsonSet("obj", gson.toJson(new IRLObject())); Path2 p = Path2.of(".none"); jsonV2.jsonSet("obj", p, gson.toJson("strangle"), JsonSetParams.jsonSetParams().nx()); assertJsonArrayEquals(jsonArray("strangle"), jsonV2.jsonGet("obj", p)); } @Test public void setWithoutAPathDefaultsToRootPath() { String objStr = gson.toJson(new IRLObject()); jsonV2.jsonSet("obj1", new JSONObject(objStr)); // jsonClient.jsonSet("obj1", "strangle", JsonSetParams.jsonSetParams().xx()); jsonV2.jsonSetWithEscape("obj1", (Object) "strangle", JsonSetParams.jsonSetParams().xx()); assertJsonArrayEquals(jsonArray("strangle"), jsonV2.jsonGet("obj1", ROOT_PATH)); } @Test public void setExistingPathOnlyIfNotExistsShouldFail() { jsonV2.jsonSetWithEscape("obj", new IRLObject()); Path2 p = Path2.of(".str"); assertNull(jsonV2.jsonSetWithEscape("obj", p, "strangle", JsonSetParams.jsonSetParams().nx())); } @Test public void setNonExistingPathOnlyIfExistsShouldFail() { jsonV2.jsonSetWithEscape("obj", new IRLObject()); Path2 p = Path2.of(".none"); assertNull(jsonV2.jsonSetWithEscape("obj", p, "strangle", JsonSetParams.jsonSetParams().xx())); } @Test public void setException() { // should error on non root path for new key assertThrows(JedisDataException.class, () -> jsonV2.jsonSet("test", Path2.of(".foo"), "bar")); } @Test public void getMultiplePathsShouldSucceed() { // check multiple paths IRLObject obj = new IRLObject(); jsonV2.jsonSetWithEscape("obj", obj); JSONObject result = (JSONObject) jsonV2.jsonGet("obj", Path2.of("bool"), Path2.of("str")); assertJsonArrayEquals(jsonArray(true), result.get("$.bool")); assertJsonArrayEquals(jsonArray("string"), result.get("$.str")); } @Test public void getMultiLevels() { JSONObject obj = new JSONObject(); obj.put("foo", "John"); JSONObject inner = new JSONObject(); inner.put("foo", "Jane"); obj.put("bar", inner); jsonV2.jsonSet("multi", obj); assertJsonArrayEquals(jsonArray("John", "Jane"), jsonV2.jsonGet("multi", new Path2("..foo"))); } @Test public void toggle() { IRLObject obj = new IRLObject(); jsonV2.jsonSetWithEscape("obj", obj); Path2 pbool = Path2.of(".bool"); // check initial value assertJsonArrayEquals(jsonArray(true), jsonV2.jsonGet("obj", pbool)); // true -> false jsonV2.jsonToggle("obj", pbool); assertJsonArrayEquals(jsonArray(false), jsonV2.jsonGet("obj", pbool)); // false -> true jsonV2.jsonToggle("obj", pbool); assertJsonArrayEquals(jsonArray(true), jsonV2.jsonGet("obj", pbool)); // ignore non-boolean field Path2 pstr = Path2.of(".str"); assertEquals(singletonList(null), jsonV2.jsonToggle("obj", pstr)); assertJsonArrayEquals(jsonArray("string"), jsonV2.jsonGet("obj", pstr)); } @Test public void getAbsent() { jsonV2.jsonSetWithEscape("test", ROOT_PATH, "foo"); assertJsonArrayEquals(jsonArray(), jsonV2.jsonGet("test", Path2.of(".bar"))); } @Test public void delValidShouldSucceed() { // check deletion of a single path jsonV2.jsonSetWithEscape("obj", ROOT_PATH, new IRLObject()); assertEquals(1L, jsonV2.jsonDel("obj", Path2.of(".str"))); assertTrue(client.exists("obj")); // check deletion root using default root -> key is removed assertEquals(1L, jsonV2.jsonDel("obj")); assertFalse(client.exists("obj")); } @Test public void delNonExistingPathsAreIgnored() { jsonV2.jsonSetWithEscape("foobar", ROOT_PATH, new FooBarObject()); assertEquals(0L, jsonV2.jsonDel("foobar", Path2.of(".foo[1]"))); } @Test public void typeChecksShouldSucceed() { jsonV2.jsonSet("foobar", ROOT_PATH, new JSONObject(gson.toJson(new FooBarObject()))); assertEquals(singletonList(Object.class), jsonV2.jsonType("foobar", ROOT_PATH)); assertEquals(singletonList(String.class), jsonV2.jsonType("foobar", Path2.of(".foo"))); assertEquals(singletonList(int.class), jsonV2.jsonType("foobar", Path2.of(".fooI"))); assertEquals(singletonList(float.class), jsonV2.jsonType("foobar", Path2.of(".fooF"))); assertEquals(singletonList(List.class), jsonV2.jsonType("foobar", Path2.of(".fooArr"))); assertEquals(singletonList(boolean.class), jsonV2.jsonType("foobar", Path2.of(".fooB"))); assertEquals(Collections.emptyList(), jsonV2.jsonType("foobar", Path2.of(".fooErr"))); } @Test public void testJsonMerge() { // Test with root path JSONObject json = new JSONObject("{\"person\":{\"name\":\"John Doe\",\"age\":25,\"address\":{\"home\":\"123 Main Street\"},\"phone\":\"123-456-7890\"}}"); assertEquals("OK", jsonV2.jsonSet("test_merge", json)); json = new JSONObject("{\"person\":{\"name\":\"John Doe\",\"age\":30,\"address\":{\"home\":\"123 Main Street\"},\"phone\":\"123-456-7890\"}}"); assertEquals("OK", jsonV2.jsonMerge("test_merge", Path2.of("$"), "{\"person\":{\"age\":30}}")); assertJsonArrayEquals(jsonArray(json), jsonV2.jsonGet("test_merge", Path2.of("$"))); // Test with root path path $.a.b assertEquals("OK", jsonV2.jsonMerge("test_merge", Path2.of("$.person.address"), "{\"work\":\"Redis office\"}")); json = new JSONObject("{\"person\":{\"name\":\"John Doe\",\"age\":30,\"address\":{\"home\":\"123 Main Street\",\"work\":\"Redis office\"},\"phone\":\"123-456-7890\"}}"); assertJsonArrayEquals(jsonArray(json), jsonV2.jsonGet("test_merge", Path2.of("$"))); // Test with null value to delete a value assertEquals("OK", jsonV2.jsonMerge("test_merge", Path2.of("$.person"), "{\"age\":null}")); json = new JSONObject("{\"person\":{\"name\":\"John Doe\",\"address\":{\"home\":\"123 Main Street\",\"work\":\"Redis office\"},\"phone\":\"123-456-7890\"}}"); assertJsonArrayEquals(jsonArray(json), jsonV2.jsonGet("test_merge", Path2.of("$"))); // cleanup assertEquals(1L, client.del("test_merge")); } @Test public void testJsonMergeArray() { // Test merge on an array JSONObject json = new JSONObject("{\"a\":{\"b\":{\"c\":[\"d\",\"e\"]}}}"); assertEquals("OK", jsonV2.jsonSet("test_merge_array", Path2.of("$"), json)); assertEquals("OK", jsonV2.jsonMerge("test_merge_array", Path2.of("$.a.b.c"), "[\"f\"]")); json = new JSONObject("{\"a\":{\"b\":{\"c\":[\"f\"]}}}"); assertJsonArrayEquals(jsonArray(json), jsonV2.jsonGet("test_merge_array", Path2.of("$"))); // assertEquals("{{a={b={c=[f]}}}", jsonClient.jsonGet("test_merge_array", Path2.of("$"))); // Test merge an array on a value assertEquals("OK", jsonV2.jsonSet("test_merge_array", Path2.of("$"), "{\"a\":{\"b\":{\"c\":\"d\"}}}")); assertEquals("OK", jsonV2.jsonMerge("test_merge_array", Path2.of("$.a.b.c"), "[\"f\"]")); json = new JSONObject("{\"a\":{\"b\":{\"c\":[\"f\"]}}}"); assertJsonArrayEquals(jsonArray(json), jsonV2.jsonGet("test_merge_array", Path2.of("$"))); // Test with null value to delete an array value assertEquals("OK", jsonV2.jsonSet("test_merge_array", Path2.of("$"), "{\"a\":{\"b\":{\"c\":[\"d\",\"e\"]}}}")); assertEquals("OK", jsonV2.jsonMerge("test_merge_array", Path2.of("$.a.b"), "{\"c\":null}")); json = new JSONObject("{\"a\":{\"b\":{}}}"); assertJsonArrayEquals(jsonArray(json), jsonV2.jsonGet("test_merge_array", Path2.of("$"))); } @Test public void mgetWithPathWithAllKeysExist() { Baz baz1 = new Baz("quuz1", "grault1", "waldo1"); Baz baz2 = new Baz("quuz2", "grault2", "waldo2"); Qux qux1 = new Qux("quux1", "corge1", "garply1", baz1); Qux qux2 = new Qux("quux2", "corge2", "garply2", baz2); jsonV2.jsonSet("qux1", new JSONObject(gson.toJson(qux1))); jsonV2.jsonSet("qux2", new JSONObject(gson.toJson(qux2))); List list = jsonV2.jsonMGet(Path2.of("baz"), "qux1", "qux2"); assertEquals(2, list.size()); assertJsonArrayEquals(jsonArray(new JSONObject(gson.toJson(baz1))), list.get(0)); assertJsonArrayEquals(jsonArray(new JSONObject(gson.toJson(baz2))), list.get(1)); } @Test public void mgetAtRootPathWithMissingKeys() { Baz baz1 = new Baz("quuz1", "grault1", "waldo1"); Baz baz2 = new Baz("quuz2", "grault2", "waldo2"); Qux qux1 = new Qux("quux1", "corge1", "garply1", baz1); Qux qux2 = new Qux("quux2", "corge2", "garply2", baz2); jsonV2.jsonSetWithEscape("qux1", qux1); jsonV2.jsonSetWithEscape("qux2", qux2); List list = jsonV2.jsonMGet("qux1", "qux2", "qux3"); assertEquals(3, list.size()); assertNull(list.get(2)); list.removeAll(singletonList(null)); assertEquals(2, list.size()); } @Test public void arrLen() { jsonV2.jsonSet("arr", ROOT_PATH, new JSONArray(new int[]{0, 1, 2, 3, 4})); assertEquals(singletonList(5L), jsonV2.jsonArrLen("arr", ROOT_PATH)); } @Test public void clearArray() { jsonV2.jsonSet("foobar", ROOT_PATH, gson.toJson(new FooBarObject())); Path2 arrPath = Path2.of(".fooArr"); assertEquals(singletonList(3L), jsonV2.jsonArrLen("foobar", arrPath)); assertEquals(1L, jsonV2.jsonClear("foobar", arrPath)); assertEquals(singletonList(0L), jsonV2.jsonArrLen("foobar", arrPath)); // ignore non-array Path2 strPath = Path2.of(".foo"); assertEquals(0L, jsonV2.jsonClear("foobar", strPath)); assertJsonArrayEquals(jsonArray("bar"), jsonV2.jsonGet("foobar", strPath)); } @Test public void clearObject() { Baz baz = new Baz("quuz", "grault", "waldo"); Qux qux = new Qux("quux", "corge", "garply", baz); jsonV2.jsonSet("qux", gson.toJson(qux)); Path2 objPath = Path2.of(".baz"); // assertEquals(baz, jsonClient.jsonGet("qux", objPath)); assertEquals(1L, jsonV2.jsonClear("qux", objPath)); // assertEquals(new Baz(null, null, null), jsonClient.jsonGet("qux", objPath)); assertJsonArrayEquals(jsonArray(new JSONObject()), jsonV2.jsonGet("qux", objPath)); } @Test public void arrAppendSameType() { String json = "{ a: 'hello', b: [1, 2, 3], c: { d: ['ello'] }}"; jsonV2.jsonSet("test_arrappend", ROOT_PATH, new JSONObject(json)); assertEquals(singletonList(6L), jsonV2.jsonArrAppend("test_arrappend", Path2.of(".b"), 4, 5, 6)); assertJsonArrayEquals(jsonArray(jsonArray(1, 2, 3, 4, 5, 6)), jsonV2.jsonGet("test_arrappend", Path2.of(".b"))); } @Test public void arrAppendMultipleTypes() { Object fooObject = gson.toJson("foo"); Object trueObject = gson.toJson(true); Object nullObject = gson.toJson(null); String json = "{ a: 'hello', b: [1, 2, 3], c: { d: ['ello'] }}"; jsonV2.jsonSet("test_arrappend", ROOT_PATH, new JSONObject(json)); assertEquals(singletonList(6L), jsonV2.jsonArrAppend("test_arrappend", Path2.of(".b"), fooObject, trueObject, nullObject)); assertJsonArrayEquals(jsonArray(jsonArray(1, 2, 3, "foo", true, null)), jsonV2.jsonGet("test_arrappend", Path2.of(".b"))); } @Test public void arrAppendMultipleTypesWithDeepPath() { String json = "{ a: 'hello', b: [1, 2, 3], c: { d: ['ello'] }}"; jsonV2.jsonSet("test_arrappend", ROOT_PATH, new JSONObject(json)); assertEquals(singletonList(4L), jsonV2.jsonArrAppendWithEscape("test_arrappend", Path2.of(".c.d"), "foo", true, null)); assertJsonArrayEquals(jsonArray(jsonArray("ello", "foo", true, null)), jsonV2.jsonGet("test_arrappend", Path2.of(".c.d"))); } @Test public void arrAppendAgaintsEmptyArray() { String json = "{ a: 'hello', b: [1, 2, 3], c: { d: [] }}"; jsonV2.jsonSet("test_arrappend", ROOT_PATH, new JSONObject(json)); assertEquals(singletonList(3L), jsonV2.jsonArrAppendWithEscape("test_arrappend", Path2.of(".c.d"), "a", "b", "c")); assertJsonArrayEquals(jsonArray(jsonArray("a", "b", "c")), jsonV2.jsonGet("test_arrappend", Path2.of(".c.d"))); } @Test public void arrAppendPathIsNotArray() { String json = "{ a: 'hello', b: [1, 2, 3], c: { d: ['ello'] }}"; jsonV2.jsonSet("test_arrappend", ROOT_PATH, new JSONObject(json)); assertEquals(singletonList(null), jsonV2.jsonArrAppend("test_arrappend", Path2.of(".a"), 1)); assertEquals(singletonList(null), jsonV2.jsonArrAppend("test_arrappend", Path2.of(".a"), gson.toJson(1))); assertEquals(singletonList(null), jsonV2.jsonArrAppendWithEscape("test_arrappend", Path2.of(".a"), 1)); } @Test public void arrIndexAbsentKey() { assertThrows(JedisDataException.class, () -> jsonV2.jsonArrIndexWithEscape("quxquux", ROOT_PATH, new JSONObject())); } @Test public void arrIndexWithInts() { jsonV2.jsonSetWithEscape("quxquux", ROOT_PATH, new int[]{8, 6, 7, 5, 3, 0, 9}); assertEquals(singletonList(2L), jsonV2.jsonArrIndexWithEscape("quxquux", ROOT_PATH, 7)); assertEquals(singletonList(-1L), jsonV2.jsonArrIndexWithEscape("quxquux", ROOT_PATH, "7")); } @Test public void arrIndexWithStrings() { jsonV2.jsonSetWithEscape("quxquux", ROOT_PATH, new String[]{"8", "6", "7", "5", "3", "0", "9"}); assertEquals(singletonList(2L), jsonV2.jsonArrIndexWithEscape("quxquux", ROOT_PATH, "7")); } @Test public void arrIndexWithStringsAndPath() { jsonV2.jsonSetWithEscape("foobar", ROOT_PATH, new FooBarObject()); assertEquals(singletonList(1L), jsonV2.jsonArrIndexWithEscape("foobar", Path2.of(".fooArr"), "b")); } @Test public void arrIndexNonExistentPath() { jsonV2.jsonSet("foobar", ROOT_PATH, gson.toJson(new FooBarObject())); assertEquals(Collections.emptyList(), jsonV2.jsonArrIndex("foobar", Path2.of(".barArr"), gson.toJson("x"))); } @Test public void arrInsert() { String json = "['hello', 'world', true, 1, 3, null, false]"; jsonV2.jsonSet("test_arrinsert", ROOT_PATH, new JSONArray(json)); assertEquals(singletonList(8L), jsonV2.jsonArrInsertWithEscape("test_arrinsert", ROOT_PATH, 1, "foo")); assertJsonArrayEquals(jsonArray(jsonArray("hello", "foo", "world", true, 1, 3, null, false)), jsonV2.jsonGet("test_arrinsert", ROOT_PATH)); } @Test public void arrInsertWithNegativeIndex() { String json = "['hello', 'world', true, 1, 3, null, false]"; jsonV2.jsonSet("test_arrinsert", ROOT_PATH, new JSONArray(json)); assertEquals(singletonList(8L), jsonV2.jsonArrInsertWithEscape("test_arrinsert", ROOT_PATH, -1, "foo")); assertJsonArrayEquals(jsonArray(jsonArray("hello", "world", true, 1, 3, null, "foo", false)), jsonV2.jsonGet("test_arrinsert", ROOT_PATH)); } @Test public void arrPop() { jsonV2.jsonSet("arr", ROOT_PATH, new JSONArray(new int[]{0, 1, 2, 3, 4})); assertEquals(singletonList(4d), jsonV2.jsonArrPop("arr", ROOT_PATH)); assertEquals(singletonList(3d), jsonV2.jsonArrPop("arr", ROOT_PATH, -1)); assertEquals(singletonList(0d), jsonV2.jsonArrPop("arr", ROOT_PATH, 0)); } @Test public void arrTrim() { // jsonClient.jsonSet("arr", ROOT_PATH, new int[]{0, 1, 2, 3, 4}); jsonV2.jsonSet("arr", ROOT_PATH, new JSONArray(new int[]{0, 1, 2, 3, 4})); assertEquals(singletonList(3L), jsonV2.jsonArrTrim("arr", ROOT_PATH, 1, 3)); // assertArrayEquals(new Integer[]{1, 2, 3}, jsonClient.jsonGet("arr", Integer[].class, ROOT_PATH)); assertJsonArrayEquals(jsonArray(jsonArray(1, 2, 3)), jsonV2.jsonGet("arr", ROOT_PATH)); } @Test public void strAppend() { // jsonClient.jsonSet("str", ROOT_PATH, "foo"); jsonV2.jsonSet("str", ROOT_PATH, gson.toJson("foo")); assertEquals(singletonList(6L), jsonV2.jsonStrAppend("str", ROOT_PATH, "bar")); assertJsonArrayEquals(jsonArray("foobar"), jsonV2.jsonGet("str", ROOT_PATH)); } @Test public void strLen() { jsonV2.jsonSetWithEscape("str", "foobar"); assertEquals(singletonList(6L), jsonV2.jsonStrLen("str", ROOT_PATH)); } @Test public void numIncrBy() { assumeFalse(protocol == RedisProtocol.RESP3); jsonV2.jsonSet("doc", "{\"a\":\"b\",\"b\":[{\"a\":2}, {\"a\":5}, {\"a\":\"c\"}]}"); assertJsonArrayEquals(jsonArray((Object) null), jsonV2.jsonNumIncrBy("doc", Path2.of(".a"), 1d)); assertJsonArrayEquals(jsonArray(null, 4, 7, null), jsonV2.jsonNumIncrBy("doc", Path2.of("..a"), 2d)); assertJsonArrayEquals(jsonArray((Object) null), jsonV2.jsonNumIncrBy("doc", Path2.of("..b"), 0d)); assertJsonArrayEquals(jsonArray(), jsonV2.jsonNumIncrBy("doc", Path2.of("..c"), 0d)); } @Test public void numIncrByResp3() { assumeTrue(protocol == RedisProtocol.RESP3); jsonV2.jsonSet("doc", "{\"a\":\"b\",\"b\":[{\"a\":2}, {\"a\":5}, {\"a\":\"c\"}]}"); assertEquals(singletonList((Object) null), jsonV2.jsonNumIncrBy("doc", Path2.of(".a"), 1d)); assertEquals(Arrays.asList(null, 4d, 7d, null), jsonV2.jsonNumIncrBy("doc", Path2.of("..a"), 2d)); assertEquals(singletonList((Object) null), jsonV2.jsonNumIncrBy("doc", Path2.of("..b"), 0d)); assertEquals(Collections.emptyList(), jsonV2.jsonNumIncrBy("doc", Path2.of("..c"), 0d)); } @Test public void obj() { String json = "{\"a\":[3], \"nested\": {\"a\": {\"b\":2, \"c\": 1}}}"; jsonV2.jsonSet("doc", ROOT_PATH, json); assertEquals(Arrays.asList(2L), jsonV2.jsonObjLen("doc", ROOT_PATH)); assertEquals(Arrays.asList(Arrays.asList("a", "nested")), jsonV2.jsonObjKeys("doc", ROOT_PATH)); assertEquals(Arrays.asList(null, 2L), jsonV2.jsonObjLen("doc", Path2.of("..a"))); assertEquals(Arrays.asList(null, Arrays.asList("b", "c")), jsonV2.jsonObjKeys("doc", Path2.of("..a"))); } @Test public void debugMemory() { assertEquals(Collections.emptyList(), jsonV2.jsonDebugMemory("json", ROOT_PATH)); jsonV2.jsonSet("json", new JSONObject("{ foo: 'bar', bar: { foo: 10 }}")); assertEquals(1, jsonV2.jsonDebugMemory("json", ROOT_PATH).size()); assertEquals(2, jsonV2.jsonDebugMemory("json", Path2.of("$..foo")).size()); assertEquals(1, jsonV2.jsonDebugMemory("json", Path2.of("$..bar")).size()); } private void assertJsonArrayEquals(JSONArray a, Object _b) { if (!(_b instanceof JSONArray)) { fail("Actual value is not JSONArray."); } JSONArray b = (JSONArray) _b; assertEquals(a.length(), b.length(), "JSONArray length mismatch"); int length = a.length(); for (int index = 0; index < length; index++) { if (a.isNull(index)) { assertTrue(b.isNull(index), index + "'th element is not null"); continue; } Object ia = a.get(index); Object ib = b.get(index); if (ia instanceof JSONArray) { assertJsonArrayEquals((JSONArray) ia, ib); } else if (ia instanceof JSONObject) { assertJsonObjectEquals((JSONObject) ia, ib); } else if (ia instanceof Number && ib instanceof Number) { assertEquals(((Number) ia).doubleValue(), ((Number) ib).doubleValue(), 0d, index + "'th element mismatch"); } else { assertEquals(ia, ib, index + "'th element mismatch"); } } } private void assertJsonObjectEquals(JSONObject a, Object _b) { if (!(_b instanceof JSONObject)) { fail("Actual value is not JSONObject."); } JSONObject b = (JSONObject) _b; assertEquals(a.length(), b.length(), "JSONObject length mismatch"); assertEquals(a.keySet(), b.keySet()); for (String key : a.keySet()) { if (a.isNull(key)) { assertTrue(b.isNull(key), key + "'s value is not null"); continue; } Object oa = a.get(key); Object ob = b.get(key); if (oa instanceof JSONArray) { assertJsonArrayEquals((JSONArray) oa, ob); } else if (oa instanceof JSONObject) { assertJsonObjectEquals((JSONObject) oa, ob); } else { assertEquals(oa, ob, key + "'s value mismatch"); } } } private static JSONArray jsonArray(Object... objects) { JSONArray arr = new JSONArray(); for (Object o : objects) { arr.put(o); } return arr; } } ================================================ FILE: src/test/java/redis/clients/jedis/modules/search/AggregationBuilderTest.java ================================================ package redis.clients.jedis.modules.search; import org.junit.jupiter.api.Test; import redis.clients.jedis.search.aggr.Reducer; import redis.clients.jedis.search.aggr.Reducers; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertNull; public class AggregationBuilderTest { @Test public void reducerObject() { Reducer reducer = Reducers.sum("@count").as("total"); assertEquals("SUM", reducer.getName()); assertEquals("@count", reducer.getField()); assertEquals("total", reducer.getAlias()); } @Test public void countObject() { Reducer count = Reducers.count(); assertEquals("COUNT", count.getName()); assertNull(count.getField()); assertNull(count.getAlias()); } } ================================================ FILE: src/test/java/redis/clients/jedis/modules/search/AggregationTest.java ================================================ package redis.clients.jedis.modules.search; import static org.hamcrest.MatcherAssert.assertThat; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertNotNull; import static org.junit.jupiter.api.Assertions.assertNull; import static org.junit.jupiter.api.Assertions.fail; import static redis.clients.jedis.util.RedisConditions.ModuleVersion.SEARCH_MOD_VER_80M3; import static redis.clients.jedis.util.RedisConditions.ModuleVersion.SEARCH_MOD_VER_84RC1; import io.redis.test.annotations.SinceRedisVersion; import io.redis.test.utils.RedisVersion; import org.hamcrest.Matchers; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Tag; import org.junit.jupiter.params.ParameterizedClass; import org.junit.jupiter.params.provider.MethodSource; import java.util.ArrayList; import java.util.HashMap; import java.util.LinkedHashMap; import java.util.List; import java.util.Map; import redis.clients.jedis.RedisProtocol; import redis.clients.jedis.exceptions.JedisDataException; import redis.clients.jedis.modules.RedisModuleCommandsTestBase; import redis.clients.jedis.search.*; import redis.clients.jedis.search.aggr.*; import redis.clients.jedis.search.schemafields.NumericField; import redis.clients.jedis.search.schemafields.TextField; import redis.clients.jedis.util.RedisConditions; import redis.clients.jedis.util.RedisVersionUtil; @ParameterizedClass @MethodSource("redis.clients.jedis.commands.CommandsTestsParameters#respVersions") @Tag("integration") public class AggregationTest extends RedisModuleCommandsTestBase { private static final String index = "aggbindex"; @BeforeAll public static void prepare() { RedisModuleCommandsTestBase.prepare(); } public AggregationTest(RedisProtocol redisProtocol) { super(redisProtocol); } private void addDocument(Document doc) { String key = doc.getId(); Map map = new LinkedHashMap<>(); doc.getProperties().forEach(entry -> map.put(entry.getKey(), String.valueOf(entry.getValue()))); client.hset(key, map); } private void addDocument(String key, Map objMap) { Map strMap = new HashMap<>(); objMap.entrySet().forEach(entry -> strMap.put(entry.getKey(), String.valueOf(entry.getValue()))); client.hset(key, strMap); } @Test public void testAggregations() { Schema sc = new Schema(); sc.addSortableTextField("name", 1.0); sc.addSortableNumericField("count"); client.ftCreate(index, IndexOptions.defaultOptions(), sc); // client.addDocument(new Document("data1").set("name", "abc").set("count", 10)); // client.addDocument(new Document("data2").set("name", "def").set("count", 5)); // client.addDocument(new Document("data3").set("name", "def").set("count", 25)); addDocument(new Document("data1").set("name", "abc").set("count", 10)); addDocument(new Document("data2").set("name", "def").set("count", 5)); addDocument(new Document("data3").set("name", "def").set("count", 25)); AggregationBuilder r = new AggregationBuilder() .groupBy("@name", Reducers.sum("@count").as("sum")) .sortBy(10, SortedField.desc("@sum")); // actual search AggregationResult res = client.ftAggregate(index, r); assertEquals(2, res.getTotalResults()); Row r1 = res.getRow(0); assertNotNull(r1); assertEquals("def", r1.getString("name")); assertEquals(30, r1.getLong("sum")); assertEquals(30., r1.getDouble("sum"), 0); assertEquals(0L, r1.getLong("nosuchcol")); assertEquals(0.0, r1.getDouble("nosuchcol"), 0); assertEquals("", r1.getString("nosuchcol")); Row r2 = res.getRow(1); assertNotNull(r2); assertEquals("abc", r2.getString("name")); assertEquals(10, r2.getLong("sum")); } @Test public void testAggregations2() { Schema sc = new Schema(); sc.addSortableTextField("name", 1.0); sc.addSortableNumericField("count"); client.ftCreate(index, IndexOptions.defaultOptions(), sc); addDocument(new Document("data1").set("name", "abc").set("count", 10)); addDocument(new Document("data2").set("name", "def").set("count", 5)); addDocument(new Document("data3").set("name", "def").set("count", 25)); AggregationBuilder r = new AggregationBuilder() .groupBy("@name", Reducers.sum("@count").as("sum")) .sortBy(10, SortedField.desc("@sum")); // actual search AggregationResult res = client.ftAggregate(index, r); assertEquals(2, res.getTotalResults()); List rows = res.getRows(); assertEquals("def", rows.get(0).get("name")); assertEquals("30", rows.get(0).get("sum")); assertNull(rows.get(0).get("nosuchcol")); assertEquals("abc", rows.get(1).get("name")); assertEquals("10", rows.get(1).get("sum")); } @Test public void testAggregations2Profile() { Schema sc = new Schema(); sc.addSortableTextField("name", 1.0); sc.addSortableNumericField("count"); client.ftCreate(index, IndexOptions.defaultOptions(), sc); addDocument(new Document("data1").set("name", "abc").set("count", 10)); addDocument(new Document("data2").set("name", "def").set("count", 5)); addDocument(new Document("data3").set("name", "def").set("count", 25)); AggregationBuilder aggr = new AggregationBuilder() .groupBy("@name", Reducers.sum("@count").as("sum")) .sortBy(10, SortedField.desc("@sum")); Map.Entry reply = client.ftProfileAggregate(index, FTProfileParams.profileParams(), aggr); // actual search AggregationResult result = reply.getKey(); assertEquals(2, result.getTotalResults()); List rows = result.getRows(); assertEquals("def", rows.get(0).get("name")); assertEquals("30", rows.get(0).get("sum")); assertNull(rows.get(0).get("nosuchcol")); assertEquals("abc", rows.get(1).get("name")); assertEquals("10", rows.get(1).get("sum")); // profile Object profileObject = reply.getValue().getProfilingInfo(); if (protocol != RedisProtocol.RESP3) { assertThat(profileObject, Matchers.isA(List.class)); if (RedisVersionUtil.getRedisVersion(client).isGreaterThanOrEqualTo(RedisVersion.V8_0_0_PRE)) { assertThat((List) profileObject, Matchers.hasItems("Shards", "Coordinator")); } } else { assertThat(profileObject, Matchers.isA(Map.class)); if (RedisVersionUtil.getRedisVersion(client).isGreaterThanOrEqualTo(RedisVersion.V8_0_0_PRE)) { assertThat(((Map) profileObject).keySet(), Matchers.hasItems("Shards", "Coordinator")); } } } @Test public void testAggregationBuilderVerbatim() { Schema sc = new Schema(); sc.addSortableTextField("name", 1.0); client.ftCreate(index, IndexOptions.defaultOptions(), sc); addDocument(new Document("data1").set("name", "hello kitty")); AggregationBuilder r = new AggregationBuilder("kitti"); AggregationResult res = client.ftAggregate(index, r); assertEquals(1, res.getTotalResults()); r = new AggregationBuilder("kitti") .verbatim(); res = client.ftAggregate(index, r); assertEquals(0, res.getTotalResults()); } @Test @SinceRedisVersion(value = "7.4.0", message = "ADDSCORES") public void testAggregationBuilderAddScores() { Schema sc = new Schema(); sc.addSortableTextField("name", 1.0); sc.addSortableNumericField("age"); client.ftCreate(index, IndexOptions.defaultOptions(), sc); addDocument(new Document("data1").set("name", "Adam").set("age", 33)); addDocument(new Document("data2").set("name", "Sara").set("age", 44)); AggregationBuilder r = new AggregationBuilder("sara").addScores() .apply("@__score * 100", "normalized_score").dialect(3); AggregationResult res = client.ftAggregate(index, r); if (RedisConditions.of(client).moduleVersionIsGreaterThanOrEqual(SEARCH_MOD_VER_80M3)) { // Default scorer is BM25 assertEquals(0.6931, res.getRow(0).getDouble("__score"), 0.0001); assertEquals(69.31, res.getRow(0).getDouble("normalized_score"), 0.01); } else { // Default scorer is TF-IDF assertEquals(2, res.getRow(0).getLong("__score")); assertEquals(200, res.getRow(0).getLong("normalized_score")); } } @Test public void testAggregationBuilderTimeout() { Schema sc = new Schema(); sc.addSortableTextField("name", 1.0); sc.addSortableNumericField("count"); client.ftCreate(index, IndexOptions.defaultOptions(), sc); addDocument(new Document("data1").set("name", "abc").set("count", 10)); addDocument(new Document("data2").set("name", "def").set("count", 5)); addDocument(new Document("data3").set("name", "def").set("count", 25)); AggregationBuilder r = new AggregationBuilder() .groupBy("@name", Reducers.sum("@count").as("sum")) .timeout(5000); AggregationResult res = client.ftAggregate(index, r); assertEquals(2, res.getTotalResults()); } @Test public void testAggregationBuilderParamsDialect() { Schema sc = new Schema(); sc.addSortableTextField("name", 1.0); sc.addSortableNumericField("count"); client.ftCreate(index, IndexOptions.defaultOptions(), sc); addDocument(new Document("data1").set("name", "abc").set("count", 10)); addDocument(new Document("data2").set("name", "def").set("count", 5)); addDocument(new Document("data3").set("name", "def").set("count", 25)); Map params = new HashMap<>(); params.put("name", "abc"); AggregationBuilder r = new AggregationBuilder("$name") .groupBy("@name", Reducers.sum("@count").as("sum")) .params(params) .dialect(2); // From documentation - To use PARAMS, DIALECT must be set to 2 AggregationResult res = client.ftAggregate(index, r); assertEquals(1, res.getTotalResults()); Row r1 = res.getRow(0); assertNotNull(r1); assertEquals("abc", r1.getString("name")); assertEquals(10, r1.getLong("sum")); } @Test public void testApplyAndFilterAggregations() { Schema sc = new Schema(); sc.addSortableTextField("name", 1.0); sc.addSortableNumericField("subj1"); sc.addSortableNumericField("subj2"); client.ftCreate(index, IndexOptions.defaultOptions(), sc); addDocument(new Document("data1").set("name", "abc").set("subj1", 20).set("subj2", 70)); addDocument(new Document("data2").set("name", "def").set("subj1", 60).set("subj2", 40)); addDocument(new Document("data3").set("name", "ghi").set("subj1", 50).set("subj2", 80)); addDocument(new Document("data4").set("name", "abc").set("subj1", 30).set("subj2", 20)); addDocument(new Document("data5").set("name", "def").set("subj1", 65).set("subj2", 45)); addDocument(new Document("data6").set("name", "ghi").set("subj1", 70).set("subj2", 70)); AggregationBuilder r = new AggregationBuilder().apply("(@subj1+@subj2)/2", "attemptavg") .groupBy("@name", Reducers.avg("@attemptavg").as("avgscore")) .filter("@avgscore>=50") .sortBy(10, SortedField.asc("@name")); // actual search AggregationResult res = client.ftAggregate(index, r); if (RedisConditions.of(client).moduleVersionIsGreaterThanOrEqual(SEARCH_MOD_VER_84RC1)) { //prior to 8.4rc1, the returned total result was reported as 3 (number of results before filter), // while 2 rows were actually returned assertEquals(2, res.getTotalResults()); } Row r1 = res.getRow(0); assertNotNull(r1); assertEquals("def", r1.getString("name")); assertEquals(52.5, r1.getDouble("avgscore"), 0); Row r2 = res.getRow(1); assertNotNull(r2); assertEquals("ghi", r2.getString("name")); assertEquals(67.5, r2.getDouble("avgscore"), 0); } @Test public void load() { Schema sc = new Schema(); sc.addSortableTextField("name", 1.0); sc.addSortableNumericField("subj1"); sc.addSortableNumericField("subj2"); client.ftCreate(index, IndexOptions.defaultOptions(), sc); // client.addDocument(new Document("data1").set("name", "abc").set("subj1", 20).set("subj2", 70)); // client.addDocument(new Document("data2").set("name", "def").set("subj1", 60).set("subj2", 40)); addDocument(new Document("data1").set("name", "abc").set("subj1", 20).set("subj2", 70)); addDocument(new Document("data2").set("name", "def").set("subj1", 60).set("subj2", 40)); AggregationBuilder builder = new AggregationBuilder() .load(FieldName.of("@subj1").as("a"), FieldName.of("@subj2").as("b")) .apply("(@a+@b)/2", "avg").sortByDesc("@avg"); AggregationResult result = client.ftAggregate(index, builder); assertEquals(50.0, result.getRow(0).getDouble("avg"), 0d); assertEquals(45.0, result.getRow(1).getDouble("avg"), 0d); } @Test public void loadAll() { Schema sc = new Schema(); sc.addSortableTextField("name", 1.0); sc.addSortableNumericField("subj1"); sc.addSortableNumericField("subj2"); client.ftCreate(index, IndexOptions.defaultOptions(), sc); addDocument(new Document("data1").set("name", "abc").set("subj1", 20).set("subj2", 70)); addDocument(new Document("data2").set("name", "def").set("subj1", 60).set("subj2", 40)); AggregationBuilder builder = new AggregationBuilder() .loadAll() .apply("(@subj1+@subj2)/2", "avg").sortByDesc("@avg"); AggregationResult result = client.ftAggregate(index, builder); assertEquals(50.0, result.getRow(0).getDouble("avg"), 0d); assertEquals(45.0, result.getRow(1).getDouble("avg"), 0d); } @Test public void cursor() { Schema sc = new Schema(); sc.addSortableTextField("name", 1.0); sc.addSortableNumericField("count"); client.ftCreate(index, IndexOptions.defaultOptions(), sc); // client.addDocument(new Document("data1").set("name", "abc").set("count", 10)); // client.addDocument(new Document("data2").set("name", "def").set("count", 5)); // client.addDocument(new Document("data3").set("name", "def").set("count", 25)); addDocument(new Document("data1").set("name", "abc").set("count", 10)); addDocument(new Document("data2").set("name", "def").set("count", 5)); addDocument(new Document("data3").set("name", "def").set("count", 25)); AggregationBuilder r = new AggregationBuilder() .groupBy("@name", Reducers.sum("@count").as("sum")) .sortBy(10, SortedField.desc("@sum")) .cursor(1, 3000); // actual search AggregationResult res = client.ftAggregate(index, r); assertEquals(2, res.getTotalResults()); Row row = res.getRow(0); assertNotNull(row); assertEquals("def", row.getString("name")); assertEquals(30, row.getLong("sum")); assertEquals(30., row.getDouble("sum"), 0); assertEquals(0L, row.getLong("nosuchcol")); assertEquals(0.0, row.getDouble("nosuchcol"), 0); assertEquals("", row.getString("nosuchcol")); res = client.ftCursorRead(index, res.getCursorId(), 1); Row row2 = res.getRow(0); assertNotNull(row2); assertEquals("abc", row2.getString("name")); assertEquals(10, row2.getLong("sum")); assertEquals("OK", client.ftCursorDel(index, res.getCursorId())); try { client.ftCursorRead(index, res.getCursorId(), 1); fail(); } catch (JedisDataException e) { // ignore } } @Test public void aggregateIteration() { client.ftCreate(index, TextField.of("name").sortable(), NumericField.of("count")); addDocument(new Document("data1").set("name", "abc").set("count", 10)); addDocument(new Document("data2").set("name", "def").set("count", 5)); addDocument(new Document("data3").set("name", "def").set("count", 25)); addDocument(new Document("data4").set("name", "ghi").set("count", 15)); addDocument(new Document("data5").set("name", "jkl").set("count", 20)); AggregationBuilder agg = new AggregationBuilder() .groupBy("@name", Reducers.sum("@count").as("sum")) .sortBy(10, SortedField.desc("@sum")) .cursor(2, 10000); FtAggregateIteration rr = client.ftAggregateIteration(index, agg); int total = 0; while (!rr.isIterationCompleted()) { AggregationResult res = rr.nextBatch(); int count = res.getRows().size(); assertThat(count, Matchers.lessThanOrEqualTo(2)); total += count; } assertEquals(4, total); } @Test public void aggregateIterationCollect() { client.ftCreate(index, TextField.of("name").sortable(), NumericField.of("count")); addDocument(new Document("data1").set("name", "abc").set("count", 10)); addDocument(new Document("data2").set("name", "def").set("count", 5)); addDocument(new Document("data3").set("name", "def").set("count", 25)); addDocument(new Document("data4").set("name", "ghi").set("count", 15)); addDocument(new Document("data5").set("name", "jkl").set("count", 20)); AggregationBuilder agg = new AggregationBuilder() .groupBy("@name", Reducers.sum("@count").as("sum")) .sortBy(10, SortedField.desc("@sum")) .cursor(2, 10000); assertEquals(4, client.ftAggregateIteration(index, agg).collect(new ArrayList<>()).size()); } @Test public void testWrongAggregation() throws InterruptedException { Schema sc = new Schema() .addTextField("title", 5.0) .addTextField("body", 1.0) .addTextField("state", 1.0) .addNumericField("price"); client.ftCreate(index, IndexOptions.defaultOptions(), sc); // insert document(s) Map fields = new HashMap<>(); fields.put("title", "hello world"); fields.put("state", "NY"); fields.put("body", "lorem ipsum"); fields.put("price", "1337"); // client.addDocument("doc1", fields); addDocument("doc1", fields); // wrong aggregation query AggregationBuilder builder = new AggregationBuilder("hello") .apply("@price/1000", "k") .groupBy("@state", Reducers.avg("@k").as("avgprice")) .filter("@avgprice>=2") .sortBy(10, SortedField.asc("@state")); try { client.ftAggregate(index, builder); fail(); } catch (JedisDataException e) { // should throw JedisDataException on wrong aggregation query } } } ================================================ FILE: src/test/java/redis/clients/jedis/modules/search/CreateTest.java ================================================ //package redis.clients.jedis.modules.search; // //import static org.junit.jupiter.api.Assertions.*; // //import java.util.ArrayList; //import java.util.Arrays; //import java.util.List; //import org.junit.Test; // //import redis.clients.jedis.search.IndexDefinition; //import redis.clients.jedis.search.IndexOptions; // //public class CreateTest { // // @Test // public void defaultOptions() throws Exception { // IndexOptions defaultOptions = IndexOptions.defaultOptions(); // List arrayList = new ArrayList<>(); // defaultOptions.serializeRedisArgs(arrayList); // // assertEquals(Arrays.asList(), arrayList); // } // // @Test // public void allOptions() throws Exception { // IndexOptions defaultOptions = new Client.IndexOptions(0); // // defaultOptions.setStopwords("stop", "run"); // defaultOptions.setTemporary(1234L); // defaultOptions.setDefinition(new IndexDefinition()); // // List arrayList = new ArrayList<>(); // defaultOptions.serializeRedisArgs(arrayList); // // assertEquals( // Arrays.asList("NOOFFSETS", "NOFIELDS", "NOFREQS", "TEMPORARY", "1234", "STOPWORDS", "2", "stop", "run"), // arrayList); // } // // @Test // public void allIndexDefinition() throws Exception { // IndexDefinition indexRule = new IndexDefinition(IndexDefinition.Type.HASH); // // indexRule.setAsync(true); // indexRule.setFilter("@sum<30"); // indexRule.setLanguage("FR"); // indexRule.setLanguageField("myLanguage"); // indexRule.setPayloadField("myPayload"); // indexRule.setPrefixes("person:"); // indexRule.setScore(0.818656); // indexRule.setScoreFiled("myScore"); // // List arrayList = new ArrayList<>(); // indexRule.serializeRedisArgs(arrayList); // // assertEquals(Arrays.asList("ON", "HASH", "ASYNC", "PREFIX", "1", "person:", "FILTER", "@sum<30", "LANGUAGE_FIELD", // "myLanguage", "LANGUAGE", "FR", "SCORE_FIELD", "myScore", "SCORE", "0.818656", "PAYLOAD_FIELD", "myPayload"), // arrayList); // // assertEquals(true, indexRule.isAsync()); // assertEquals("@sum<30", indexRule.getFilter()); // assertEquals("FR", indexRule.getLanguage()); // assertEquals("myLanguage", indexRule.getLanguageField()); // assertEquals("myPayload", indexRule.getPayloadField()); // assertArrayEquals(new String[]{"person:"}, indexRule.getPrefixes()); // assertEquals(0.818656, indexRule.getScore(), 0.0); // assertEquals("myScore", indexRule.getScoreFiled()); // assertEquals(IndexDefinition.Type.HASH, indexRule.getType()); // } //} ================================================ FILE: src/test/java/redis/clients/jedis/modules/search/DocumentTest.java ================================================ package redis.clients.jedis.modules.search; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.IOException; import java.io.ObjectInputStream; import java.io.ObjectOutputStream; import java.util.HashMap; import java.util.Locale; import java.util.Map; import org.junit.jupiter.api.Test; import redis.clients.jedis.search.Document; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertTrue; public class DocumentTest { @Test public void serialize() throws IOException, ClassNotFoundException { String id = "9f"; double score = 10d; Map map = new HashMap<>(); map.put("string", "c"); map.put("float", 12d); Document document = new Document(id, map, score); ByteArrayOutputStream aos = new ByteArrayOutputStream(); ObjectOutputStream oos = new ObjectOutputStream(aos); oos.writeObject(document); oos.flush(); oos.close(); ByteArrayInputStream ais = new ByteArrayInputStream(aos.toByteArray()); ObjectInputStream ois = new ObjectInputStream(ais); Document read = (Document) ois.readObject(); ois.close(); assertEquals(id, read.getId()); assertEquals(score, read.getScore(), 0d); assertEquals("c", read.getString("string")); assertEquals(12d, read.get("float")); } @Test public void toStringTest() { String id = "9f"; double score = 10d; Map map = new HashMap<>(); map.put("string", "c"); map.put("float", 12d); Document document = new Document(id, map, score); // use english language to make sure the decimal separator is the same as the toString String expected1 = String.format(Locale.ENGLISH, "id:%s, score: %.1f, properties:%s", id, score, "[string=c, float=12.0]"); String expected2 = String.format(Locale.ENGLISH, "id:%s, score: %.1f, properties:%s", id, score, "[float=12.0, string=c]"); // the order of the properties is not guaranteed, so we check both possible outcomes String actual = document.toString(); assertTrue(actual.equals(expected1) || actual.equals(expected2)); } } ================================================ FILE: src/test/java/redis/clients/jedis/modules/search/JsonSearchTest.java ================================================ package redis.clients.jedis.modules.search; import org.json.JSONObject; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.Test; import org.junit.jupiter.params.ParameterizedClass; import org.junit.jupiter.params.provider.MethodSource; import redis.clients.jedis.BuilderFactory; import redis.clients.jedis.CommandArguments; import redis.clients.jedis.CommandObject; import redis.clients.jedis.RedisProtocol; import redis.clients.jedis.json.JsonProtocol; import redis.clients.jedis.json.Path2; import redis.clients.jedis.search.*; import redis.clients.jedis.search.Schema.*; import redis.clients.jedis.search.SearchResult; import redis.clients.jedis.modules.RedisModuleCommandsTestBase; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertNull; @ParameterizedClass @MethodSource("redis.clients.jedis.commands.CommandsTestsParameters#respVersions") public class JsonSearchTest extends RedisModuleCommandsTestBase { public static final String JSON_ROOT = "$"; private static final String index = "json-index"; @BeforeAll public static void prepare() { RedisModuleCommandsTestBase.prepare(); } public JsonSearchTest(RedisProtocol protocol) { super(protocol); } private void setJson(String key, JSONObject json) { CommandObject command = new CommandObject<>( new CommandArguments(JsonProtocol.JsonCommand.SET).key(key).add(Path2.ROOT_PATH).add(json), BuilderFactory.STRING); client.executeCommand(command); } private JSONObject toJson(Object... values) { JSONObject json = new JSONObject(); for (int i = 0; i < values.length; i += 2) { json.put((String) values[i], values[i + 1]); } return json; } @Test public void create() { Schema schema = new Schema().addTextField("$.first", 1.0).addTextField("$.last", 1.0) .addNumericField("$.age"); IndexDefinition rule = new IndexDefinition(IndexDefinition.Type.JSON) .setPrefixes(new String[]{"student:", "pupil:"}); assertEquals("OK", client.ftCreate(index, IndexOptions.defaultOptions().setDefinition(rule), schema)); // try (Jedis jedis = client.connection()) { // setJson(jedis, "profesor:5555", toJson("first", "Albert", "last", "Blue", "age", 55)); // setJson(jedis, "student:1111", toJson("first", "Joe", "last", "Dod", "age", 18)); // setJson(jedis, "pupil:2222", toJson("first", "Jen", "last", "Rod", "age", 14)); // setJson(jedis, "student:3333", toJson("first", "El", "last", "Mark", "age", 17)); // setJson(jedis, "pupil:4444", toJson("first", "Pat", "last", "Shu", "age", 21)); // setJson(jedis, "student:5555", toJson("first", "Joen", "last", "Ko", "age", 20)); // setJson(jedis, "teacher:6666", toJson("first", "Pat", "last", "Rod", "age", 20)); // } setJson("profesor:5555", toJson("first", "Albert", "last", "Blue", "age", 55)); setJson("student:1111", toJson("first", "Joe", "last", "Dod", "age", 18)); setJson("pupil:2222", toJson("first", "Jen", "last", "Rod", "age", 14)); setJson("student:3333", toJson("first", "El", "last", "Mark", "age", 17)); setJson("pupil:4444", toJson("first", "Pat", "last", "Shu", "age", 21)); setJson("student:5555", toJson("first", "Joen", "last", "Ko", "age", 20)); setJson("teacher:6666", toJson("first", "Pat", "last", "Rod", "age", 20)); SearchResult noFilters = client.ftSearch(index, new Query()); assertEquals(5, noFilters.getTotalResults()); SearchResult res1 = client.ftSearch(index, new Query("@\\$\\.first:Jo*")); assertEquals(2, res1.getTotalResults()); SearchResult res2 = client.ftSearch(index, new Query("@\\$\\.first:Pat")); assertEquals(1, res2.getTotalResults()); } @Test public void createWithFieldNames() { Schema schema = new Schema() .addField(new TextField(FieldName.of("$.first").as("first"))) .addField(new TextField(FieldName.of("$.last"))) .addField(new Field(FieldName.of("$.age").as("age"), FieldType.NUMERIC)); IndexDefinition rule = new IndexDefinition(IndexDefinition.Type.JSON) .setPrefixes(new String[]{"student:", "pupil:"}); assertEquals("OK", client.ftCreate(index, IndexOptions.defaultOptions().setDefinition(rule), schema)); // try (Jedis jedis = client.connection()) { // setJson(jedis, "profesor:5555", toJson("first", "Albert", "last", "Blue", "age", 55)); // setJson(jedis, "student:1111", toJson("first", "Joe", "last", "Dod", "age", 18)); // setJson(jedis, "pupil:2222", toJson("first", "Jen", "last", "Rod", "age", 14)); // setJson(jedis, "student:3333", toJson("first", "El", "last", "Mark", "age", 17)); // setJson(jedis, "pupil:4444", toJson("first", "Pat", "last", "Shu", "age", 21)); // setJson(jedis, "student:5555", toJson("first", "Joen", "last", "Ko", "age", 20)); // setJson(jedis, "teacher:6666", toJson("first", "Pat", "last", "Rod", "age", 20)); // } setJson("profesor:5555", toJson("first", "Albert", "last", "Blue", "age", 55)); setJson("student:1111", toJson("first", "Joe", "last", "Dod", "age", 18)); setJson("pupil:2222", toJson("first", "Jen", "last", "Rod", "age", 14)); setJson("student:3333", toJson("first", "El", "last", "Mark", "age", 17)); setJson("pupil:4444", toJson("first", "Pat", "last", "Shu", "age", 21)); setJson("student:5555", toJson("first", "Joen", "last", "Ko", "age", 20)); setJson("teacher:6666", toJson("first", "Pat", "last", "Rod", "age", 20)); SearchResult noFilters = client.ftSearch(index, new Query()); assertEquals(5, noFilters.getTotalResults()); SearchResult asAttribute = client.ftSearch(index, new Query("@first:Jo*")); assertEquals(2, asAttribute.getTotalResults()); SearchResult nonAttribute = client.ftSearch(index, new Query("@\\$\\.last:Rod")); assertEquals(1, nonAttribute.getTotalResults()); } @Test public void parseJson() { Schema schema = new Schema() .addField(new TextField(FieldName.of("$.first").as("first"))) .addField(new TextField(FieldName.of("$.last"))) .addField(new Field(FieldName.of("$.age").as("age"), FieldType.NUMERIC)); IndexDefinition rule = new IndexDefinition(IndexDefinition.Type.JSON); assertEquals("OK", client.ftCreate(index, IndexOptions.defaultOptions().setDefinition(rule), schema)); String id = "student:1111"; JSONObject json = toJson("first", "Joe", "last", "Dod", "age", 18); // try (Jedis jedis = client.connection()) { // setJson(jedis, id, json); // } setJson(id, json); // query SearchResult sr = client.ftSearch(index, new Query().setWithScores()); assertEquals(1, sr.getTotalResults()); Document doc = sr.getDocuments().get(0); assertEquals(1.0, doc.getScore(), 0); assertEquals(json.toString(), doc.get(JSON_ROOT)); // query repeat sr = client.ftSearch(index, new Query().setWithScores()); doc = sr.getDocuments().get(0); JSONObject jsonRead = new JSONObject((String) doc.get(JSON_ROOT)); assertEquals(json.toString(), jsonRead.toString()); // query repeat sr = client.ftSearch(index, new Query().setWithScores()); doc = sr.getDocuments().get(0); jsonRead = new JSONObject(doc.getString(JSON_ROOT)); assertEquals(json.toString(), jsonRead.toString()); } @Test public void parseJsonPartial() { Schema schema = new Schema() .addField(new TextField(FieldName.of("$.first").as("first"))) .addField(new TextField(FieldName.of("$.last"))) .addField(new Field(FieldName.of("$.age").as("age"), FieldType.NUMERIC)); IndexDefinition rule = new IndexDefinition(IndexDefinition.Type.JSON); assertEquals("OK", client.ftCreate(index, IndexOptions.defaultOptions().setDefinition(rule), schema)); String id = "student:1111"; JSONObject json = toJson("first", "Joe", "last", "Dod", "age", 18); // try (Jedis jedis = client.connection()) { // setJson(jedis, id, json); // } setJson(id, json); // query SearchResult sr = client.ftSearch(index, new Query().returnFields("$.first", "$.last", "$.age")); assertEquals(1, sr.getTotalResults()); Document doc = sr.getDocuments().get(0); assertEquals("Joe", doc.get("$.first")); assertEquals("Dod", doc.get("$.last")); assertEquals(Integer.toString(18), doc.get("$.age")); // query repeat sr = client.ftSearch(index, new Query().returnFields("$.first", "$.last", "$.age")); doc = sr.getDocuments().get(0); assertEquals("Joe", doc.getString("$.first")); assertEquals("Dod", doc.getString("$.last")); assertEquals(18, Integer.parseInt((String) doc.get("$.age"))); } @Test public void parseJsonPartialWithFieldNames() { Schema schema = new Schema() .addField(new TextField(FieldName.of("$.first").as("first"))) .addField(new TextField(FieldName.of("$.last"))) .addField(new Field(FieldName.of("$.age").as("age"), FieldType.NUMERIC)); IndexDefinition rule = new IndexDefinition(IndexDefinition.Type.JSON); assertEquals("OK", client.ftCreate(index, IndexOptions.defaultOptions().setDefinition(rule), schema)); String id = "student:1111"; JSONObject json = toJson("first", "Joe", "last", "Dod", "age", 18); // try (Jedis jedis = client.connection()) { // setJson(jedis, id, json); // } setJson(id, json); // query SearchResult sr = client.ftSearch(index, new Query().returnFields(FieldName.of("$.first").as("first"), FieldName.of("$.last").as("last"), FieldName.of("$.age"))); assertEquals(1, sr.getTotalResults()); Document doc = sr.getDocuments().get(0); assertNull(doc.get("$.first")); assertNull(doc.get("$.last")); assertEquals(Integer.toString(18), doc.get("$.age")); assertEquals("Joe", doc.get("first")); assertEquals("Dod", doc.get("last")); assertNull(doc.get("age")); } @Test public void dialect() { Schema schema = new Schema() .addField(new TextField(FieldName.of("$.first").as("first"))) .addField(new TextField(FieldName.of("$.last"))) .addField(new Field(FieldName.of("$.age").as("age"), FieldType.NUMERIC)); IndexDefinition rule = new IndexDefinition(IndexDefinition.Type.JSON); assertEquals("OK", client.ftCreate(index, IndexOptions.defaultOptions().setDefinition(rule), schema)); String id = "student:1111"; JSONObject json = toJson("first", "Joe", "last", "Dod", "age", 18); setJson(id, json); SearchResult sr = client.ftSearch(index, new Query().returnFields(FieldName.of("$.first").as("first"), FieldName.of("$.last").as("last"), FieldName.of("$.age")).dialect(1)); assertEquals(1, sr.getTotalResults()); assertEquals("Joe", sr.getDocuments().get(0).get("first")); assertEquals("Dod", sr.getDocuments().get(0).get("last")); } @Test public void slop() { Schema schema = new Schema() .addField(new TextField(FieldName.of("$.first").as("first"))) .addField(new TextField(FieldName.of("$.last"))) .addField(new Field(FieldName.of("$.age").as("age"), FieldType.NUMERIC)); IndexDefinition rule = new IndexDefinition(IndexDefinition.Type.JSON); assertEquals("OK", client.ftCreate(index, IndexOptions.defaultOptions().setDefinition(rule), schema)); String id = "student:1111"; JSONObject json = toJson("first", "Joe is first ok", "last", "Dod will be first next", "age", 18); setJson(id, json); SearchResult sr = client.ftSearch(index, new Query("Dod next").returnFields(FieldName.of("$.first").as("first"), FieldName.of("$.last").as("last"), FieldName.of("$.age")).slop(0)); assertEquals(0, sr.getTotalResults()); sr = client.ftSearch(index, new Query("Dod next").returnFields(FieldName.of("$.first").as("first"), FieldName.of("$.last").as("last"), FieldName.of("$.age")).slop(1)); assertEquals(1, sr.getTotalResults()); } @Test public void timeout() { Schema schema = new Schema() .addField(new TextField(FieldName.of("$.first").as("first"))) .addField(new TextField(FieldName.of("$.last"))) .addField(new Field(FieldName.of("$.age").as("age"), FieldType.NUMERIC)); IndexDefinition rule = new IndexDefinition(IndexDefinition.Type.JSON); assertEquals("OK", client.ftCreate(index, IndexOptions.defaultOptions().setDefinition(rule), schema)); String id = "student:1111"; JSONObject json = toJson("first", "Joe is first ok", "last", "Dod will be first next", "age", 18); setJson(id, json); SearchResult sr = client.ftSearch(index, new Query("Dod next").returnFields(FieldName.of("$.first").as("first"), FieldName.of("$.last").as("last"), FieldName.of("$.age")).timeout(2000)); assertEquals(1, sr.getTotalResults()); } @Test public void inOrder() { Schema schema = new Schema() .addField(new TextField(FieldName.of("$.first").as("first"))) .addField(new TextField(FieldName.of("$.last"))) .addField(new Field(FieldName.of("$.age").as("age"), FieldType.NUMERIC)); IndexDefinition rule = new IndexDefinition(IndexDefinition.Type.JSON); assertEquals("OK", client.ftCreate(index, IndexOptions.defaultOptions().setDefinition(rule), schema)); String id = "student:1112"; JSONObject json = toJson("first", "Joe is first ok", "last", "Dod will be first next", "age", 18); setJson(id, json); id = "student:1113"; json = toJson("first", "Joe is first ok", "last", "Dod will be first next", "age", 18); setJson(id, json); id = "student:1111"; json = toJson("first", "Joe is first ok", "last", "Dod will be first next", "age", 18); setJson(id, json); SearchResult sr = client.ftSearch(index, new Query().setInOrder()); assertEquals(3, sr.getTotalResults()); assertEquals("student:1112", sr.getDocuments().get(0).getId()); } } ================================================ FILE: src/test/java/redis/clients/jedis/modules/search/JsonSearchWithGsonTest.java ================================================ package redis.clients.jedis.modules.search; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertNull; import static redis.clients.jedis.util.AssertUtil.assertOK; import com.google.gson.Gson; import com.google.gson.GsonBuilder; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.Test; import org.junit.jupiter.params.ParameterizedClass; import org.junit.jupiter.params.provider.MethodSource; import redis.clients.jedis.RedisProtocol; import redis.clients.jedis.search.*; import redis.clients.jedis.modules.RedisModuleCommandsTestBase; @ParameterizedClass @MethodSource("redis.clients.jedis.commands.CommandsTestsParameters#respVersions") public class JsonSearchWithGsonTest extends RedisModuleCommandsTestBase { private static final String index = "gson-index"; @BeforeAll public static void prepare() { RedisModuleCommandsTestBase.prepare(); } public JsonSearchWithGsonTest(RedisProtocol protocol) { super(protocol); } class Account { String name; String phone; Integer age; public Account(String name, String phone, Integer age) { this.name = name; this.phone = phone; this.age = age; } } @Test public void returnNullField() { Gson nullGson = new GsonBuilder().serializeNulls().create(); assertOK(client.ftCreate(index, FTCreateParams.createParams().on(IndexDataType.JSON), redis.clients.jedis.search.schemafields.TextField.of(FieldName.of("$.name").as("name")), redis.clients.jedis.search.schemafields.TextField.of(FieldName.of("$.phone").as("phone")), redis.clients.jedis.search.schemafields.NumericField.of(FieldName.of("$.age").as("age")))); Account object = new Account("Jane", null, null); String jsonString = nullGson.toJson(object); client.jsonSet("account:2", jsonString); SearchResult sr = client.ftSearch(index, "*", FTSearchParams.searchParams().returnFields("name", "phone", "age")); assertEquals(1, sr.getTotalResults()); Document doc = sr.getDocuments().get(0); assertEquals("Jane", doc.get("name")); assertNull(doc.get("phone")); assertNull(doc.get("age")); sr = client.ftSearch(index, "*", FTSearchParams.searchParams().returnFields("name")); assertEquals(1, sr.getTotalResults()); doc = sr.getDocuments().get(0); assertEquals("Jane", doc.get("name")); sr = client.ftSearch(index, "*", FTSearchParams.searchParams().returnFields("phone")); assertEquals(1, sr.getTotalResults()); doc = sr.getDocuments().get(0); assertNull(doc.get("phone")); sr = client.ftSearch(index, "*", FTSearchParams.searchParams().returnFields("age")); assertEquals(1, sr.getTotalResults()); doc = sr.getDocuments().get(0); assertNull(doc.get("age")); } } ================================================ FILE: src/test/java/redis/clients/jedis/modules/search/QueryBuilderTest.java ================================================ package redis.clients.jedis.modules.search; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertThrows; import static redis.clients.jedis.search.querybuilder.QueryBuilders.*; import static redis.clients.jedis.search.querybuilder.Values.*; import java.util.Arrays; import org.junit.jupiter.api.Test; import redis.clients.jedis.GeoCoordinate; import redis.clients.jedis.args.GeoUnit; import redis.clients.jedis.search.querybuilder.Node; import redis.clients.jedis.search.querybuilder.Value; import redis.clients.jedis.search.querybuilder.Values; /** * Created by mnunberg on 2/23/18. */ public class QueryBuilderTest { @Test public void testTag() { Value v = tags("foo"); assertEquals("{foo}", v.toString()); v = tags("foo", "bar"); assertEquals("{foo | bar}", v.toString()); } @Test public void testEmptyTag() { assertThrows(IllegalArgumentException.class, () -> tags()); } @Test public void testRange() { Value v = between(1, 10); assertEquals("[1 10]", v.toString()); v = between(1, 10).inclusiveMax(false); assertEquals("[1 (10]", v.toString()); v = between(1, 10).inclusiveMin(false); assertEquals("[(1 10]", v.toString()); v = between(1.0, 10.1); assertEquals("[1.0 10.1]", v.toString()); v = between(-1.0, 10.1).inclusiveMax(false); assertEquals("[-1.0 (10.1]", v.toString()); v = between(-1.1, 150.61).inclusiveMin(false); assertEquals("[(-1.1 150.61]", v.toString()); // le, gt, etc. // le, gt, etc. assertEquals("[42 42]", eq(42).toString()); assertEquals("[-inf (42]", lt(42).toString()); assertEquals("[-inf 42]", le(42).toString()); assertEquals("[(-42 inf]", gt(-42).toString()); assertEquals("[42 inf]", ge(42).toString()); assertEquals("[42.0 42.0]", eq(42.0).toString()); assertEquals("[-inf (42.0]", lt(42.0).toString()); assertEquals("[-inf 42.0]", le(42.0).toString()); assertEquals("[(42.0 inf]", gt(42.0).toString()); assertEquals("[42.0 inf]", ge(42.0).toString()); assertEquals("[(1587058030 inf]", gt(1587058030).toString()); // string value assertEquals("s", value("s").toString()); // Geo value assertEquals("[1.0 2.0 3.0 km]", geo(new GeoCoordinate(1.0, 2.0), 3.0, GeoUnit.KM).toString()); } @Test public void testIntersectionBasic() { Node n = intersect().add("name", "mark"); assertEquals("@name:mark", n.toString()); n = intersect().add("name", "mark", "dvir"); assertEquals("@name:(mark dvir)", n.toString()); n = intersect().add("name", Arrays.asList(Values.value("mark"), Values.value("shay"))); assertEquals("@name:(mark shay)", n.toString()); n = intersect("name", "meir"); assertEquals("@name:meir", n.toString()); n = intersect("name", Values.value("meir"), Values.value("rafi")); assertEquals("@name:(meir rafi)", n.toString()); } @Test public void testIntersectionNested() { Node n = intersect() .add(union("name", value("mark"), value("dvir"))) .add("time", between(100, 200)) .add(disjunct("created", lt(1000))); assertEquals("(@name:(mark|dvir) @time:[100 200] -@created:[-inf (1000])", n.toString()); } @Test public void testOptional() { Node n = optional("name", tags("foo", "bar")); assertEquals("~@name:{foo | bar}", n.toString()); n = optional(n, n); assertEquals("~(~@name:{foo | bar} ~@name:{foo | bar})", n.toString()); } } ================================================ FILE: src/test/java/redis/clients/jedis/modules/search/QueryTest.java ================================================ //package redis.clients.jedis.modules.search; // //import static org.junit.jupiter.api.Assertions.*; // //import java.util.ArrayList; //import org.junit.Before; //import org.junit.Test; //import redis.clients.jedis.search.Query; // //public class QueryTest { // // Query query; // // @Before // public void setUp() throws Exception { // query = new Query("hello world"); // } // // @Test // public void getNoContent() throws Exception { // assertFalse(query.getNoContent()); // assertEquals(query, query.setNoContent()); // assertTrue(query.getNoContent()); // } // // @Test // public void getWithScores() throws Exception { // assertFalse(query.getWithScores()); // assertEquals(query, query.setWithScores()); // assertTrue(query.getWithScores()); // } // // @Test // public void serializeRedisArgs() throws Exception { // query.setNoContent().setLanguage("xx").setNoStopwords().setVerbatim().setWithPayload().setWithScores().setScorer("My.Scorer"); // // ArrayList args = new ArrayList<>(1); // query.serializeRedisArgs(args); // // assertEquals(10, args.size()); // assertEquals(query._queryString, new String(args.get(0))); //// assertTrue(args.contains("xx".getBytes())); //// assertTrue(args.contains("NOSTOPWORDS".getBytes())); //// assertTrue(args.contains("VERBATIM".getBytes())); //// assertTrue(args.contains("PAYLOADS".getBytes())); //// assertTrue(args.contains("WITHSCORES".getBytes())); // } // // @Test // public void limit() throws Exception { // assertEquals(0, query._paging.offset); // assertEquals(10, query._paging.num); // assertEquals(query, query.limit(1, 30)); // assertEquals(1, query._paging.offset); // assertEquals(30, query._paging.num); // // } // // @Test // public void addFilter() throws Exception { // assertEquals(0, query._filters.size()); // Query.NumericFilter f = new Query.NumericFilter("foo", 0, 100); // assertEquals(query, query.addFilter(f)); // assertEquals(f, query._filters.get(0)); // } // // @Test // public void setVerbatim() throws Exception { // assertFalse(query._verbatim); // assertEquals(query, query.setVerbatim()); // assertTrue(query._verbatim); // } // // @Test // public void setNoStopwords() throws Exception { // assertFalse(query._noStopwords); // assertEquals(query, query.setNoStopwords()); // assertTrue(query._noStopwords); // // } // // @Test // public void setLanguage() throws Exception { // assertEquals(null, query._language); // assertEquals(query, query.setLanguage("chinese")); // assertEquals("chinese", query._language); // } // // @Test // public void setScorer() throws Exception { // assertEquals(null, query._scorer); // assertEquals(query, query.setScorer("the.scroer")); // assertEquals("the.scroer", query._scorer); // } // // @Test // public void limitFields() throws Exception { // assertNull(query._fields); // assertEquals(query, query.limitFields("foo", "bar")); // assertEquals(2, query._fields.length); // } // // @Test // public void returnFields() throws Exception { // assertNull(query._returnFields); // assertEquals(query, query.returnFields("foo", "bar")); // assertEquals(2, query._returnFields.length); // } // // @Test // public void highlightFields() throws Exception { // assertEquals(false, query.wantsHighlight); // assertNull(query.highlightFields); // // query = new Query("Hello"); // assertEquals(query, query.highlightFields("foo", "bar")); // assertEquals(2, query.highlightFields.length); // assertNull(query.highlightTags); // assertEquals(true, query.wantsHighlight); // // query = new Query("Hello").highlightFields(); // assertNull(query.highlightFields); // assertNull(query.highlightTags); // assertEquals(true, query.wantsHighlight); // // assertEquals(query, query.highlightFields(new Query.HighlightTags("", ""))); // assertNull(query.highlightFields); // assertEquals(2, query.highlightTags.length); // assertEquals("", query.highlightTags[0]); // assertEquals("", query.highlightTags[1]); // } // // @Test // public void summarizeFields() throws Exception { // assertEquals(false, query.wantsSummarize); // assertNull(query.summarizeFields); // // query = new Query("Hello"); // assertEquals(query, query.summarizeFields()); // assertEquals(true, query.wantsSummarize); // assertNull(query.summarizeFields); // assertEquals(-1, query.summarizeFragmentLen); // assertEquals(-1, query.summarizeNumFragments); // // query = new Query("Hello"); // assertEquals(query, query.summarizeFields("someField")); // assertEquals(true, query.wantsSummarize); // assertEquals(1, query.summarizeFields.length); // assertEquals(-1, query.summarizeFragmentLen); // assertEquals(-1, query.summarizeNumFragments); // } // //} ================================================ FILE: src/test/java/redis/clients/jedis/modules/search/SchemaTest.java ================================================ package redis.clients.jedis.modules.search; import static org.hamcrest.MatcherAssert.assertThat; import static org.junit.jupiter.api.Assertions.assertThrows; import java.util.Collections; import java.util.HashMap; import java.util.Map; import org.hamcrest.Matchers; import org.junit.jupiter.api.Test; import redis.clients.jedis.search.FieldName; import redis.clients.jedis.search.Schema; public class SchemaTest { private final static String TITLE = "title"; private final static String GENRE = "genre"; private final static String VOTES = "votes"; private final static String RATING = "rating"; private final static String RELEASE_YEAR = "release_year"; private final static String PLOT = "plot"; private final static String VECTOR = "vector"; @Test public void printSchemaTest() throws Exception { Schema sc = new Schema() .addTextField(TITLE, 5.0) .addSortableTextField(PLOT, 1.0) .addSortableTagField(GENRE, ",") .addSortableNumericField(RELEASE_YEAR) .addSortableNumericField(RATING) .addSortableNumericField(VOTES) .addVectorField(VECTOR, Schema.VectorField.VectorAlgo.HNSW, Collections.emptyMap()); String schemaPrint = sc.toString(); assertThat(schemaPrint, Matchers.startsWith("Schema{fields=[TextField{name='title'")); assertThat(schemaPrint, Matchers.containsString("{name='release_year', type=NUMERIC, sortable=true, noindex=false}")); assertThat(schemaPrint, Matchers.containsString("VectorField{name='vector', type=VECTOR, algorithm=HNSW")); } @Test public void printSvsVamanaSchemaTest() throws Exception { Map vamanaAttributes = new HashMap<>(); vamanaAttributes.put("TYPE", "FLOAT32"); vamanaAttributes.put("DIM", 128); vamanaAttributes.put("DISTANCE_METRIC", "COSINE"); vamanaAttributes.put("COMPRESSION", "LVQ8"); Schema sc = new Schema() .addTextField(TITLE, 5.0) .addVectorField("embedding", Schema.VectorField.VectorAlgo.SVS_VAMANA, vamanaAttributes); String schemaPrint = sc.toString(); assertThat(schemaPrint, Matchers.containsString("VectorField{name='embedding', type=VECTOR, algorithm=SVS_VAMANA")); assertThat(schemaPrint, Matchers.containsString("TYPE=FLOAT32")); assertThat(schemaPrint, Matchers.containsString("COMPRESSION=LVQ8")); } @Test public void fieldAttributeNull() { assertThrows(IllegalArgumentException.class, () -> FieldName.of("identifier").as(null)); } @Test public void fieldAttributeMultiple() { assertThrows(IllegalStateException.class, () -> FieldName.of("identifier").as("attribute").as("attribute")); assertThrows(IllegalStateException.class, () -> new FieldName("identifier", "attribute").as("attribute")); } @Test public void addSvsVamanaVectorFieldBasicTest() { Map attributes = new HashMap<>(); attributes.put("TYPE", "FLOAT32"); attributes.put("DIM", 256); attributes.put("DISTANCE_METRIC", "L2"); Schema schema = new Schema() .addTextField(TITLE, 1.0) .addSvsVamanaVectorField("embedding", attributes); // Verify the schema contains the correct number of fields assertThat(schema.fields.size(), Matchers.equalTo(2)); // Verify the vector field is correctly configured Schema.VectorField vectorField = (Schema.VectorField) schema.fields.get(1); assertThat(vectorField.toString(), Matchers.containsString("VectorField{name='embedding', type=VECTOR, algorithm=SVS_VAMANA")); // Verify the schema string representation String schemaString = schema.toString(); assertThat(schemaString, Matchers.containsString("algorithm=SVS_VAMANA")); assertThat(schemaString, Matchers.containsString("TYPE=FLOAT32")); assertThat(schemaString, Matchers.containsString("DIM=256")); assertThat(schemaString, Matchers.containsString("DISTANCE_METRIC=L2")); } } ================================================ FILE: src/test/java/redis/clients/jedis/modules/search/SearchDefaultDialectTest.java ================================================ package redis.clients.jedis.modules.search; import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.containsString; import static org.hamcrest.Matchers.emptyOrNullString; import static org.hamcrest.Matchers.not; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertNotNull; import static org.junit.jupiter.api.Assertions.assertThrows; import static redis.clients.jedis.util.AssertUtil.assertEqualsByProtocol; import static redis.clients.jedis.util.AssertUtil.assertOK; import java.util.*; import io.redis.test.annotations.SinceRedisVersion; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Tag; import org.junit.jupiter.params.ParameterizedClass; import org.junit.jupiter.params.provider.MethodSource; import redis.clients.jedis.RedisProtocol; import redis.clients.jedis.UnifiedJedis; import redis.clients.jedis.exceptions.JedisDataException; import redis.clients.jedis.search.*; import redis.clients.jedis.search.schemafields.NumericField; import redis.clients.jedis.search.schemafields.TagField; import redis.clients.jedis.search.schemafields.TextField; import redis.clients.jedis.modules.RedisModuleCommandsTestBase; import redis.clients.jedis.search.aggr.AggregationBuilder; import redis.clients.jedis.search.aggr.AggregationResult; import redis.clients.jedis.search.aggr.Reducers; import redis.clients.jedis.search.aggr.Row; @ParameterizedClass @MethodSource("redis.clients.jedis.commands.CommandsTestsParameters#respVersions") @Tag("integration") public class SearchDefaultDialectTest extends RedisModuleCommandsTestBase { private static final String INDEX = "dialect-INDEX"; @BeforeAll public static void prepare() { RedisModuleCommandsTestBase.prepare(); } public SearchDefaultDialectTest(RedisProtocol protocol) { super(protocol); } @Override @BeforeEach public void setUp() { super.setUp(); client.setDefaultSearchDialect(SearchProtocol.DEFAULT_DIALECT); } private void addDocument(Document doc) { String key = doc.getId(); Map map = new LinkedHashMap<>(); doc.getProperties().forEach(entry -> map.put(entry.getKey(), String.valueOf(entry.getValue()))); client.hset(key, map); } private static Map toMap(String... values) { Map map = new HashMap<>(); for (int i = 0; i < values.length; i += 2) { map.put(values[i], values[i + 1]); } return map; } @Test public void testQueryParams() { Schema sc = new Schema().addNumericField("numval"); assertEquals("OK", client.ftCreate(INDEX, IndexOptions.defaultOptions(), sc)); client.hset("1", "numval", "1"); client.hset("2", "numval", "2"); client.hset("3", "numval", "3"); Query query = new Query("@numval:[$min $max]").addParam("min", 1).addParam("max", 2); assertEquals(2, client.ftSearch(INDEX, query).getTotalResults()); } @Test public void testQueryParamsWithParams() { assertOK(client.ftCreate(INDEX, NumericField.of("numval"))); client.hset("1", "numval", "1"); client.hset("2", "numval", "2"); client.hset("3", "numval", "3"); assertEquals(2, client.ftSearch(INDEX, "@numval:[$min $max]", FTSearchParams.searchParams().addParam("min", 1).addParam("max", 2)).getTotalResults()); Map paramValues = new HashMap<>(); paramValues.put("min", 1); paramValues.put("max", 2); assertEquals(2, client.ftSearch(INDEX, "@numval:[$min $max]", FTSearchParams.searchParams().params(paramValues)).getTotalResults()); } @Test public void testDialectsWithFTExplain() throws Exception { Map attr = new HashMap<>(); attr.put("TYPE", "FLOAT32"); attr.put("DIM", 2); attr.put("DISTANCE_METRIC", "L2"); Schema sc = new Schema() .addFlatVectorField("v", attr) .addTagField("title") .addTextField("t1", 1.0) .addTextField("t2", 1.0) .addNumericField("num"); assertEquals("OK", client.ftCreate(INDEX, IndexOptions.defaultOptions(), sc)); client.hset("1", "t1", "hello"); String q = "(*)"; Query query = new Query(q).dialect(1); assertSyntaxError(query, client); // dialect=1 throws syntax error query = new Query(q); // dialect=default=2 should return execution plan assertThat(client.ftExplain(INDEX, query), containsString("WILDCARD")); q = "$hello"; query = new Query(q).dialect(1); assertSyntaxError(query, client); // dialect=1 throws syntax error query = new Query(q).addParam("hello", "hello"); // dialect=default=2 should return execution plan assertThat(client.ftExplain(INDEX, query), not(emptyOrNullString())); q = "@title:(@num:[0 10])"; query = new Query(q).dialect(1); // dialect=1 should return execution plan assertThat(client.ftExplain(INDEX, query), not(emptyOrNullString())); query = new Query(q); // dialect=default=2 assertSyntaxError(query, client); // dialect=2 throws syntax error q = "@t1:@t2:@t3:hello"; query = new Query(q).dialect(1); // dialect=1 should return execution plan assertThat(client.ftExplain(INDEX, query), not(emptyOrNullString())); query = new Query(q); // dialect=default=2 assertSyntaxError(query, client); // dialect=2 throws syntax error q = "@title:{foo}}}}}"; query = new Query(q).dialect(1); // dialect=1 should return execution plan assertThat(client.ftExplain(INDEX, query), not(emptyOrNullString())); query = new Query(q); // dialect=default=2 assertSyntaxError(query, client); // dialect=2 throws syntax error } @Test public void testAggregationBuilderParamsDialect() { Schema sc = new Schema(); sc.addSortableTextField("name", 1.0); sc.addSortableNumericField("count"); client.ftCreate(INDEX, IndexOptions.defaultOptions(), sc); addDocument(new Document("data1").set("name", "abc").set("count", 10)); addDocument(new Document("data2").set("name", "def").set("count", 5)); addDocument(new Document("data3").set("name", "def").set("count", 25)); Map params = new HashMap<>(); params.put("name", "abc"); AggregationBuilder r = new AggregationBuilder("$name") .groupBy("@name", Reducers.sum("@count").as("sum")) .params(params); AggregationResult res = client.ftAggregate(INDEX, r); assertEquals(1, res.getTotalResults()); Row r1 = res.getRow(0); assertNotNull(r1); assertEquals("abc", r1.getString("name")); assertEquals(10, r1.getLong("sum")); } @Test public void dialectBoundSpellCheck() { client.ftCreate(INDEX, TextField.of("t")); JedisDataException error = assertThrows(JedisDataException.class, () -> client.ftSpellCheck(INDEX, "Tooni toque kerfuffle", FTSpellCheckParams.spellCheckParams().dialect(0))); assertThat(error.getMessage(), containsString("DIALECT requires a non negative integer")); } private void assertSyntaxError(Query query, UnifiedJedis client) { JedisDataException error = assertThrows(JedisDataException.class, () -> client.ftExplain(INDEX, query)); assertThat(error.getMessage(), containsString("Syntax error")); } @Test @SinceRedisVersion(value = "7.9.0") public void warningMaxPrefixExpansions() { final String configParam = "search-max-prefix-expansions"; String defaultConfigValue = jedis.configGet(configParam).get(configParam); try { assertOK(client.ftCreate(INDEX, FTCreateParams.createParams().on(IndexDataType.HASH), TextField.of("t"), TagField.of("t2"))); client.hset("doc13", toMap("t", "foo", "t2", "foo")); jedis.configSet(configParam, "1"); SearchResult srcResult = client.ftSearch(INDEX, "fo*"); assertEqualsByProtocol(protocol, null, Arrays.asList(), srcResult.getWarnings()); client.hset("doc23", toMap("t", "fooo", "t2", "fooo")); AggregationResult aggResult = client.ftAggregate(INDEX, new AggregationBuilder("fo*").loadAll()); assertEqualsByProtocol(protocol, /* resp2 */ null, Arrays.asList("Max prefix expansions limit was reached"), aggResult.getWarnings()); } finally { jedis.configSet(configParam, defaultConfigValue); } } } ================================================ FILE: src/test/java/redis/clients/jedis/modules/search/SearchTest.java ================================================ package redis.clients.jedis.modules.search; import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.containsString; import static org.hamcrest.Matchers.emptyOrNullString; import static org.hamcrest.Matchers.not; import static org.junit.jupiter.api.Assertions.assertArrayEquals; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertNotNull; import static org.junit.jupiter.api.Assertions.assertNull; import static org.junit.jupiter.api.Assertions.assertThrows; import static org.junit.jupiter.api.Assertions.assertTrue; import static org.junit.jupiter.api.Assertions.fail; import static org.junit.jupiter.api.Assumptions.assumeFalse; import java.util.*; import java.util.stream.Collectors; import io.redis.test.annotations.SinceRedisVersion; import io.redis.test.utils.RedisVersion; import org.hamcrest.Matchers; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Tag; import org.junit.jupiter.params.ParameterizedClass; import org.junit.jupiter.params.provider.MethodSource; import redis.clients.jedis.RedisProtocol; import redis.clients.jedis.UnifiedJedis; import redis.clients.jedis.exceptions.JedisDataException; import redis.clients.jedis.json.Path; import redis.clients.jedis.search.*; import redis.clients.jedis.search.Schema.*; import redis.clients.jedis.modules.RedisModuleCommandsTestBase; import redis.clients.jedis.util.RedisVersionUtil; import redis.clients.jedis.util.SafeEncoder; @ParameterizedClass @MethodSource("redis.clients.jedis.commands.CommandsTestsParameters#respVersions") @Tag("integration") public class SearchTest extends RedisModuleCommandsTestBase { private static final String index = "testindex"; @BeforeAll public static void prepare() { RedisModuleCommandsTestBase.prepare(); } public SearchTest(RedisProtocol protocol) { super(protocol); } private void addDocument(String key, Map map) { client.hset(key, RediSearchUtil.toStringMap(map)); } private static Map toMap(Object... values) { Map map = new HashMap<>(); for (int i = 0; i < values.length; i += 2) { map.put((String) values[i], values[i + 1]); } return map; } private static Map toMap(String... values) { Map map = new HashMap<>(); for (int i = 0; i < values.length; i += 2) { map.put(values[i], values[i + 1]); } return map; } @Test public void create() throws Exception { Schema sc = new Schema().addTextField("first", 1.0).addTextField("last", 1.0).addNumericField("age"); IndexDefinition rule = new IndexDefinition() .setFilter("@age>16") .setPrefixes(new String[]{"student:", "pupil:"}); assertEquals("OK", client.ftCreate(index, IndexOptions.defaultOptions().setDefinition(rule), sc)); client.hset("profesor:5555", toMap("first", "Albert", "last", "Blue", "age", "55")); client.hset("student:1111", toMap("first", "Joe", "last", "Dod", "age", "18")); client.hset("pupil:2222", toMap("first", "Jen", "last", "Rod", "age", "14")); client.hset("student:3333", toMap("first", "El", "last", "Mark", "age", "17")); client.hset("pupil:4444", toMap("first", "Pat", "last", "Shu", "age", "21")); client.hset("student:5555", toMap("first", "Joen", "last", "Ko", "age", "20")); client.hset("teacher:6666", toMap("first", "Pat", "last", "Rod", "age", "20")); SearchResult noFilters = client.ftSearch(index, new Query()); assertEquals(4, noFilters.getTotalResults()); SearchResult res1 = client.ftSearch(index, new Query("@first:Jo*")); assertEquals(2, res1.getTotalResults()); SearchResult res2 = client.ftSearch(index, new Query("@first:Pat")); assertEquals(1, res2.getTotalResults()); SearchResult res3 = client.ftSearch(index, new Query("@last:Rod")); assertEquals(0, res3.getTotalResults()); } @Test public void createNoParams() { Schema sc = new Schema().addTextField("first", 1.0).addTextField("last", 1.0).addNumericField("age"); assertEquals("OK", client.ftCreate(index, IndexOptions.defaultOptions(), sc)); addDocument("student:1111", toMap("first", "Joe", "last", "Dod", "age", 18)); addDocument("student:3333", toMap("first", "El", "last", "Mark", "age", 17)); addDocument("pupil:4444", toMap("first", "Pat", "last", "Shu", "age", 21)); addDocument("student:5555", toMap("first", "Joen", "last", "Ko", "age", 20)); SearchResult noFilters = client.ftSearch(index, new Query()); assertEquals(4, noFilters.getTotalResults()); SearchResult res1 = client.ftSearch(index, new Query("@first:Jo*")); assertEquals(2, res1.getTotalResults()); SearchResult res2 = client.ftSearch(index, new Query("@first:Pat")); assertEquals(1, res2.getTotalResults()); SearchResult res3 = client.ftSearch(index, new Query("@last:Rod")); assertEquals(0, res3.getTotalResults()); } @Test public void createWithFieldNames() { Schema sc = new Schema().addField(new TextField(FieldName.of("first").as("given"))) .addField(new TextField(FieldName.of("last"))); IndexDefinition rule = new IndexDefinition() //.setFilter("@age>16") .setPrefixes(new String[]{"student:", "pupil:"}); assertEquals("OK", client.ftCreate(index, IndexOptions.defaultOptions().setDefinition(rule), sc)); client.hset("profesor:5555", toMap("first", "Albert", "last", "Blue", "age", "55")); client.hset("student:1111", toMap("first", "Joe", "last", "Dod", "age", "18")); client.hset("pupil:2222", toMap("first", "Jen", "last", "Rod", "age", "14")); client.hset("student:3333", toMap("first", "El", "last", "Mark", "age", "17")); client.hset("pupil:4444", toMap("first", "Pat", "last", "Shu", "age", "21")); client.hset("student:5555", toMap("first", "Joen", "last", "Ko", "age", "20")); client.hset("teacher:6666", toMap("first", "Pat", "last", "Rod", "age", "20")); SearchResult noFilters = client.ftSearch(index, new Query()); assertEquals(5, noFilters.getTotalResults()); SearchResult asAttribute = client.ftSearch(index, new Query("@given:Jo*")); assertEquals(2, asAttribute.getTotalResults()); SearchResult nonAttribute = client.ftSearch(index, new Query("@last:Rod")); assertEquals(1, nonAttribute.getTotalResults()); } @Test public void alterAdd() { Schema sc = new Schema().addTextField("title", 1.0); assertEquals("OK", client.ftCreate(index, IndexOptions.defaultOptions(), sc)); Map fields = new HashMap<>(); fields.put("title", "hello world"); for (int i = 0; i < 100; i++) { addDocument(String.format("doc%d", i), fields); } SearchResult res = client.ftSearch(index, new Query("hello world")); assertEquals(100, res.getTotalResults()); assertEquals("OK", client.ftAlter(index, new TagField("tags", ","), new TextField("name", 0.5))); for (int i = 0; i < 100; i++) { Map fields2 = new HashMap<>(); fields2.put("name", "name" + i); fields2.put("tags", String.format("tagA,tagB,tag%d", i)); // assertTrue(client.updateDocument(String.format("doc%d", i), 1.0, fields2)); addDocument(String.format("doc%d", i), fields2); } SearchResult res2 = client.ftSearch(index, new Query("@tags:{tagA}")); assertEquals(100, res2.getTotalResults()); } @Test public void search() throws Exception { Schema sc = new Schema().addTextField("title", 1.0).addTextField("body", 1.0); assertEquals("OK", client.ftCreate(index, IndexOptions.defaultOptions(), sc)); Map fields = new HashMap<>(); fields.put("title", "hello world"); fields.put("body", "lorem ipsum"); for (int i = 0; i < 100; i++) { // assertTrue(client.addDocument(String.format("doc%d", i), (double) i / 100.0, fields)); addDocument(String.format("doc%d", i), fields); } SearchResult res = client.ftSearch(index, new Query("hello world").limit(0, 5).setWithScores()); assertEquals(100, res.getTotalResults()); assertEquals(5, res.getDocuments().size()); for (Document d : res.getDocuments()) { assertTrue(d.getId().startsWith("doc")); assertTrue(d.getScore() < 100); // assertEquals( // String.format( // "{\"id\":\"%s\",\"score\":%s,\"properties\":{\"title\":\"hello world\",\"body\":\"lorem ipsum\"}}", // d.getId(), Double.toString(d.getScore())), // d.toString()); } // assertTrue(client.deleteDocument("doc0", true)); // assertFalse(client.deleteDocument("doc0")); client.del("doc0"); res = client.ftSearch(index, new Query("hello world")); assertEquals(99, res.getTotalResults()); assertEquals("OK", client.ftDropIndex(index)); try { client.ftSearch(index, new Query("hello world")); fail(); } catch (JedisDataException e) { } } @Test public void numericFilter() throws Exception { Schema sc = new Schema().addTextField("title", 1.0).addNumericField("price"); assertEquals("OK", client.ftCreate(index, IndexOptions.defaultOptions(), sc)); Map fields = new HashMap<>(); fields.put("title", "hello world"); for (int i = 0; i < 100; i++) { fields.put("price", i); // assertTrue(client.addDocument(String.format("doc%d", i), fields)); addDocument(String.format("doc%d", i), fields); } SearchResult res = client.ftSearch(index, new Query("hello world"). addFilter(new Query.NumericFilter("price", 0, 49))); assertEquals(50, res.getTotalResults()); assertEquals(10, res.getDocuments().size()); for (Document d : res.getDocuments()) { long price = Long.valueOf((String) d.get("price")); assertTrue(price >= 0); assertTrue(price <= 49); } res = client.ftSearch(index, new Query("hello world"). addFilter(new Query.NumericFilter("price", 0, true, 49, true))); assertEquals(48, res.getTotalResults()); assertEquals(10, res.getDocuments().size()); for (Document d : res.getDocuments()) { long price = Long.valueOf((String) d.get("price")); assertTrue(price > 0); assertTrue(price < 49); } res = client.ftSearch(index, new Query("hello world"). addFilter(new Query.NumericFilter("price", 50, 100))); assertEquals(50, res.getTotalResults()); assertEquals(10, res.getDocuments().size()); for (Document d : res.getDocuments()) { long price = Long.valueOf((String) d.get("price")); assertTrue(price >= 50); assertTrue(price <= 100); } res = client.ftSearch(index, new Query("hello world"). addFilter(new Query.NumericFilter("price", 20, Double.POSITIVE_INFINITY))); assertEquals(80, res.getTotalResults()); assertEquals(10, res.getDocuments().size()); res = client.ftSearch(index, new Query("hello world"). addFilter(new Query.NumericFilter("price", Double.NEGATIVE_INFINITY, 10))); assertEquals(11, res.getTotalResults()); assertEquals(10, res.getDocuments().size()); } @Test public void stopwords() { Schema sc = new Schema().addTextField("title", 1.0); assertEquals("OK", client.ftCreate(index, IndexOptions.defaultOptions().setStopwords("foo", "bar", "baz"), sc)); Map fields = new HashMap<>(); fields.put("title", "hello world foo bar"); // assertTrue(client.addDocument("doc1", fields)); addDocument("doc1", fields); SearchResult res = client.ftSearch(index, new Query("hello world")); assertEquals(1, res.getTotalResults()); res = client.ftSearch(index, new Query("foo bar")); assertEquals(0, res.getTotalResults()); } @Test public void noStopwords() { Schema sc = new Schema().addTextField("title", 1.0); assertEquals("OK", client.ftCreate(index, IndexOptions.defaultOptions().setNoStopwords(), sc)); Map fields = new HashMap<>(); fields.put("title", "hello world foo bar"); fields.put("title", "hello world foo bar to be or not to be"); addDocument("doc1", fields); assertEquals(1, client.ftSearch(index, new Query("hello world")).getTotalResults()); assertEquals(1, client.ftSearch(index, new Query("foo bar")).getTotalResults()); assertEquals(1, client.ftSearch(index, new Query("to be or not to be")).getTotalResults()); } @Test public void geoFilter() { Schema sc = new Schema().addTextField("title", 1.0).addGeoField("loc"); assertEquals("OK", client.ftCreate(index, IndexOptions.defaultOptions(), sc)); Map fields = new HashMap<>(); fields.put("title", "hello world"); fields.put("loc", "-0.441,51.458"); // assertTrue(client.addDocument("doc1", fields)); addDocument("doc1", fields); fields.put("loc", "-0.1,51.2"); // assertTrue(client.addDocument("doc2", fields)); addDocument("doc2", fields); SearchResult res = client.ftSearch(index, new Query("hello world"). addFilter( new Query.GeoFilter("loc", -0.44, 51.45, 10, Query.GeoFilter.KILOMETERS) )); assertEquals(1, res.getTotalResults()); res = client.ftSearch(index, new Query("hello world"). addFilter( new Query.GeoFilter("loc", -0.44, 51.45, 100, Query.GeoFilter.KILOMETERS) )); assertEquals(2, res.getTotalResults()); } @Test public void geoFilterAndGeoCoordinateObject() { Schema schema = new Schema().addTextField("title", 1.0).addGeoField("loc"); assertEquals("OK", client.ftCreate(index, IndexOptions.defaultOptions(), schema)); Map fields = new HashMap<>(); fields.put("title", "hello world"); fields.put("loc", new redis.clients.jedis.GeoCoordinate(-0.441, 51.458)); // assertTrue(client.addDocument("doc1", fields)); addDocument("doc1", fields); fields.put("loc", new redis.clients.jedis.GeoCoordinate(-0.1, 51.2)); // assertTrue(client.addDocument("doc2", fields)); addDocument("doc2", fields); SearchResult res = client.ftSearch(index, new Query("hello world").addFilter( new Query.GeoFilter("loc", -0.44, 51.45, 10, Query.GeoFilter.KILOMETERS))); assertEquals(1, res.getTotalResults()); res = client.ftSearch(index, new Query("hello world").addFilter( new Query.GeoFilter("loc", -0.44, 51.45, 100, Query.GeoFilter.KILOMETERS))); assertEquals(2, res.getTotalResults()); } @Test public void testQueryFlags() { Schema sc = new Schema().addTextField("title", 1.0); assertEquals("OK", client.ftCreate(index, IndexOptions.defaultOptions(), sc)); Map fields = new HashMap<>(); for (int i = 0; i < 100; i++) { fields.put("title", i % 2 != 0 ? "hello worlds" : "hello world"); // assertTrue(client.addDocument(String.format("doc%d", i), (double) i / 100.0, fields)); addDocument(String.format("doc%d", i), fields); } Query q = new Query("hello").setWithScores(); SearchResult res = client.ftSearch(index, q); assertEquals(100, res.getTotalResults()); assertEquals(10, res.getDocuments().size()); for (Document d : res.getDocuments()) { assertTrue(d.getId().startsWith("doc")); // assertNotEquals(1.0, d.getScore()); assertTrue(((String) d.get("title")).startsWith("hello world")); } q = new Query("hello").setNoContent(); res = client.ftSearch(index, q); for (Document d : res.getDocuments()) { assertTrue(d.getId().startsWith("doc")); if (protocol != RedisProtocol.RESP3) { assertEquals(1.0, d.getScore(), 0); assertNull(d.get("title")); } else { assertNull(d.getScore()); assertThrows(NullPointerException.class, () -> d.get("title")); } } // test verbatim vs. stemming res = client.ftSearch(index, new Query("hello worlds")); assertEquals(100, res.getTotalResults()); res = client.ftSearch(index, new Query("hello worlds").setVerbatim()); assertEquals(50, res.getTotalResults()); res = client.ftSearch(index, new Query("hello a world").setVerbatim()); assertEquals(50, res.getTotalResults()); res = client.ftSearch(index, new Query("hello a worlds").setVerbatim()); assertEquals(50, res.getTotalResults()); res = client.ftSearch(index, new Query("hello a world").setVerbatim().setNoStopwords()); assertEquals(0, res.getTotalResults()); } @Test public void testQueryParams() { Schema sc = new Schema().addNumericField("numval"); assertEquals("OK", client.ftCreate(index, IndexOptions.defaultOptions(), sc)); client.hset("1", "numval", "1"); client.hset("2", "numval", "2"); client.hset("3", "numval", "3"); Query query = new Query("@numval:[$min $max]").addParam("min", 1).addParam("max", 2).dialect(2); assertEquals(2, client.ftSearch(index, query).getTotalResults()); if (RedisVersionUtil.getRedisVersion(client).isGreaterThanOrEqualTo(RedisVersion.V7_4) ) { query = new Query("@numval:[$eq]").addParam("eq", 2).dialect(4); assertEquals(1, client.ftSearch(index, query).getTotalResults()); } } @Test public void testSortQueryFlags() { Schema sc = new Schema().addSortableTextField("title", 1.0); assertEquals("OK", client.ftCreate(index, IndexOptions.defaultOptions(), sc)); Map fields = new HashMap<>(); fields.put("title", "b title"); // client.addDocument("doc1", 1.0, fields, false, true, null); addDocument("doc1", fields); fields.put("title", "a title"); // client.addDocument("doc2", 1.0, fields, false, true, null); addDocument("doc2", fields); fields.put("title", "c title"); // client.addDocument("doc3", 1.0, fields, false, true, null); addDocument("doc3", fields); Query q = new Query("title").setSortBy("title", true); SearchResult res = client.ftSearch(index, q); assertEquals(3, res.getTotalResults()); Document doc1 = res.getDocuments().get(0); assertEquals("a title", doc1.get("title")); doc1 = res.getDocuments().get(1); assertEquals("b title", doc1.get("title")); doc1 = res.getDocuments().get(2); assertEquals("c title", doc1.get("title")); } @Test public void testNullField() { Schema sc = new Schema() .addTextField("title", 1.0) .addTextField("genre", 1.0) .addTextField("plot", 1.0) .addSortableNumericField("release_year") .addTagField("tag") .addGeoField("loc"); assertEquals("OK", client.ftCreate(index, IndexOptions.defaultOptions(), sc)); // create a document with a field set to null Map fields = new HashMap<>(); fields.put("title", "another test with title "); fields.put("genre", "Comedy"); fields.put("plot", "this is the plot for the test"); fields.put("tag", "fun"); fields.put("release_year", 2019); fields.put("loc", "-0.1,51.2"); // client.addDocument("doc1", fields); addDocument("doc1", fields); SearchResult res = client.ftSearch(index, new Query("title")); assertEquals(1, res.getTotalResults()); fields = new HashMap<>(); fields.put("title", "another title another test"); fields.put("genre", "Action"); fields.put("plot", null); fields.put("tag", null); try { // client.addDocument("doc2", fields); addDocument("doc2", fields); fail("Should throw NullPointerException."); } catch (NullPointerException e) { // assertEquals("Document attribute 'tag' is null. (Remove it, or set a value)", e.getMessage()); } res = client.ftSearch(index, new Query("title")); assertEquals(1, res.getTotalResults()); // Testing with numerical value fields = new HashMap<>(); fields.put("title", "another title another test"); fields.put("genre", "Action"); fields.put("release_year", null); try { // client.addDocument("doc2", fields); addDocument("doc2", fields); fail("Should throw NullPointerException."); } catch (NullPointerException e) { // assertEquals("Document attribute 'release_year' is null. (Remove it, or set a value)", e.getMessage()); } res = client.ftSearch(index, new Query("title")); assertEquals(1, res.getTotalResults()); } @Test public void testJsonWithAlias() { Schema sc = new Schema() .addTextField("$.name", 1.0).as("name") .addNumericField("$.num").as("num"); IndexDefinition definition = new IndexDefinition(IndexDefinition.Type.JSON).setPrefixes("king:"); assertEquals("OK", client.ftCreate(index, IndexOptions.defaultOptions().setDefinition(definition), sc)); Map king1 = new HashMap<>(); king1.put("name", "henry"); king1.put("num", 42); client.jsonSet("king:1", Path.ROOT_PATH, king1); Map king2 = new HashMap<>(); king2.put("name", "james"); king2.put("num", 3.14); client.jsonSet("king:2", Path.ROOT_PATH, king2); SearchResult res = client.ftSearch(index, new Query("@name:henry")); assertEquals(1, res.getTotalResults()); assertEquals("king:1", res.getDocuments().get(0).getId()); res = client.ftSearch(index, new Query("@num:[0 10]")); assertEquals(1, res.getTotalResults()); assertEquals("king:2", res.getDocuments().get(0).getId()); res = client.ftSearch(index, new Query("@num:[42 42]")); assertEquals(1, res.getTotalResults()); assertEquals("king:1", res.getDocuments().get(0).getId()); if (RedisVersionUtil.getRedisVersion(client).isGreaterThanOrEqualTo(RedisVersion.V7_4) ) { res = client.ftSearch(index, new Query("@num:[42]").dialect(4)); assertEquals(1, res.getTotalResults()); assertEquals("king:1", res.getDocuments().get(0).getId()); } } @Test public void dropIndex() { Schema sc = new Schema().addTextField("title", 1.0); assertEquals("OK", client.ftCreate(index, IndexOptions.defaultOptions(), sc)); Map fields = new HashMap<>(); fields.put("title", "hello world"); for (int i = 0; i < 100; i++) { addDocument(String.format("doc%d", i), fields); } SearchResult res = client.ftSearch(index, new Query("hello world")); assertEquals(100, res.getTotalResults()); assertEquals("OK", client.ftDropIndex(index)); try { client.ftSearch(index, new Query("hello world")); fail("Index should not exist."); } catch (JedisDataException de) { // error message updated to "No such index" with Redis 8.0.0 assertTrue(de.getMessage().toLowerCase().contains("no such index")); } assertEquals(100, client.dbSize()); } @Test public void dropIndexDD() { Schema sc = new Schema().addTextField("title", 1.0); assertEquals("OK", client.ftCreate(index, IndexOptions.defaultOptions(), sc)); Map fields = new HashMap<>(); fields.put("title", "hello world"); for (int i = 0; i < 100; i++) { addDocument(String.format("doc%d", i), fields); } SearchResult res = client.ftSearch(index, new Query("hello world")); assertEquals(100, res.getTotalResults()); assertEquals("OK", client.ftDropIndexDD(index)); Set keys = client.keys("*"); assertTrue(keys.isEmpty()); assertEquals(0, client.dbSize()); } @Test public void noStem() { Schema sc = new Schema().addTextField("stemmed", 1.0).addField(new Schema.TextField("notStemmed", 1.0, false, true)); assertEquals("OK", client.ftCreate(index, IndexOptions.defaultOptions(), sc)); Map doc = new HashMap<>(); doc.put("stemmed", "located"); doc.put("notStemmed", "located"); // Store it // assertTrue(client.addDocument("doc", doc)); addDocument("doc", doc); // Query SearchResult res = client.ftSearch(index, new Query("@stemmed:location")); assertEquals(1, res.getTotalResults()); res = client.ftSearch(index, new Query("@notStemmed:location")); assertEquals(0, res.getTotalResults()); } @Test public void phoneticMatch() { Schema sc = new Schema() .addTextField("noPhonetic", 1.0) .addField(new Schema.TextField("withPhonetic", 1.0, false, false, false, "dm:en")); assertEquals("OK", client.ftCreate(index, IndexOptions.defaultOptions(), sc)); Map doc = new HashMap<>(); doc.put("noPhonetic", "morfix"); doc.put("withPhonetic", "morfix"); // Store it // assertTrue(client.addDocument("doc", doc)); addDocument("doc", doc); // Query SearchResult res = client.ftSearch(index, new Query("@withPhonetic:morphix=>{$phonetic:true}")); assertEquals(1, res.getTotalResults()); try { client.ftSearch(index, new Query("@noPhonetic:morphix=>{$phonetic:true}")); fail(); } catch (JedisDataException e) {/*field does not support phonetics*/ } SearchResult res3 = client.ftSearch(index, new Query("@withPhonetic:morphix=>{$phonetic:false}")); assertEquals(0, res3.getTotalResults()); } @Test public void info() throws Exception { String MOVIE_ID = "movie_id"; String TITLE = "title"; String GENRE = "genre"; String VOTES = "votes"; String RATING = "rating"; String RELEASE_YEAR = "release_year"; String PLOT = "plot"; String POSTER = "poster"; Schema sc = new Schema() .addTextField(TITLE, 5.0) .addSortableTextField(PLOT, 1.0) .addSortableTagField(GENRE, ",") .addSortableNumericField(RELEASE_YEAR) .addSortableNumericField(RATING) .addSortableNumericField(VOTES); assertEquals("OK", client.ftCreate(index, IndexOptions.defaultOptions(), sc)); Map info = client.ftInfo(index); assertEquals(index, info.get("index_name")); assertEquals(6, ((List) info.get("attributes")).size()); if (protocol != RedisProtocol.RESP3) { assertEquals("global_idle", ((List) info.get("cursor_stats")).get(0)); assertEquals(0L, ((List) info.get("cursor_stats")).get(1)); } else { assertEquals(0L, ((Map) info.get("cursor_stats")).get("global_idle")); assertEquals(128L, ((Map) info.get("cursor_stats")).get("index_capacity")); } } @Test public void noIndex() { Schema sc = new Schema() .addField(new Schema.TextField("f1", 1.0, true, false, true)) .addField(new Schema.TextField("f2", 1.0)); client.ftCreate(index, IndexOptions.defaultOptions(), sc); Map mm = new HashMap<>(); mm.put("f1", "MarkZZ"); mm.put("f2", "MarkZZ"); // client.addDocument("doc1", mm); addDocument("doc1", mm); mm.clear(); mm.put("f1", "MarkAA"); mm.put("f2", "MarkBB"); // client.addDocument("doc2", mm); addDocument("doc2", mm); SearchResult res = client.ftSearch(index, new Query("@f1:Mark*")); assertEquals(0, res.getTotalResults()); res = client.ftSearch(index, new Query("@f2:Mark*")); assertEquals(2, res.getTotalResults()); Document[] docs = new Document[2]; res = client.ftSearch(index, new Query("@f2:Mark*").setSortBy("f1", false)); assertEquals(2, res.getTotalResults()); res.getDocuments().toArray(docs); assertEquals("doc1", docs[0].getId()); res = client.ftSearch(index, new Query("@f2:Mark*").setSortBy("f1", true)); res.getDocuments().toArray(docs); assertEquals("doc2", docs[0].getId()); } @Test public void testExplain() { Schema sc = new Schema() .addTextField("f1", 1.0) .addTextField("f2", 1.0) .addTextField("f3", 1.0); client.ftCreate(index, IndexOptions.defaultOptions(), sc); String res = client.ftExplain(index, new Query("@f3:f3_val @f2:f2_val @f1:f1_val")); assertNotNull(res); assertFalse(res.isEmpty()); } @Test public void testHighlightSummarize() { Schema sc = new Schema().addTextField("text", 1.0); client.ftCreate(index, IndexOptions.defaultOptions(), sc); Map doc = new HashMap<>(); doc.put("text", "Redis is often referred as a data structures server. What this means is that Redis provides access to mutable data structures via a set of commands, which are sent using a server-client model with TCP sockets and a simple protocol. So different processes can query and modify the same data structures in a shared way"); // Add a document // client.addDocument("foo", 1.0, doc); addDocument("foo", doc); Query q = new Query("data").highlightFields().summarizeFields(); SearchResult res = client.ftSearch(index, q); assertEquals("is often referred as a data structures server. What this means is that Redis provides... What this means is that Redis provides access to mutable data structures via a set of commands, which are sent using a... So different processes can query and modify the same data structures in a shared... ", res.getDocuments().get(0).get("text")); q = new Query("data").highlightFields(new Query.HighlightTags("", "")).summarizeFields(); res = client.ftSearch(index, q); assertEquals("is often referred as a data structures server. What this means is that Redis provides... What this means is that Redis provides access to mutable data structures via a set of commands, which are sent using a... So different processes can query and modify the same data structures in a shared... ", res.getDocuments().get(0).get("text")); } @Test public void getTagField() { Schema sc = new Schema() .addTextField("title", 1.0) .addTagField("category"); assertEquals("OK", client.ftCreate(index, IndexOptions.defaultOptions(), sc)); Map fields1 = new HashMap<>(); fields1.put("title", "hello world"); fields1.put("category", "red"); // assertTrue(client.addDocument("foo", fields1)); addDocument("foo", fields1); Map fields2 = new HashMap<>(); fields2.put("title", "hello world"); fields2.put("category", "blue"); // assertTrue(client.addDocument("bar", fields2)); addDocument("bar", fields2); Map fields3 = new HashMap<>(); fields3.put("title", "hello world"); fields3.put("category", "green,yellow"); // assertTrue(client.addDocument("baz", fields3)); addDocument("baz", fields3); Map fields4 = new HashMap<>(); fields4.put("title", "hello world"); fields4.put("category", "orange;purple"); // assertTrue(client.addDocument("qux", fields4)); addDocument("qux", fields4); assertEquals(1, client.ftSearch(index, new Query("@category:{red}")).getTotalResults()); assertEquals(1, client.ftSearch(index, new Query("@category:{blue}")).getTotalResults()); assertEquals(1, client.ftSearch(index, new Query("hello @category:{red}")).getTotalResults()); assertEquals(1, client.ftSearch(index, new Query("hello @category:{blue}")).getTotalResults()); assertEquals(1, client.ftSearch(index, new Query("@category:{yellow}")).getTotalResults()); assertEquals(0, client.ftSearch(index, new Query("@category:{purple}")).getTotalResults()); assertEquals(1, client.ftSearch(index, new Query("@category:{orange\\;purple}")).getTotalResults()); assertEquals(4, client.ftSearch(index, new Query("hello")).getTotalResults()); assertEquals(new HashSet<>(Arrays.asList("red", "blue", "green", "yellow", "orange;purple")), client.ftTagVals(index, "category")); } @Test public void testGetTagFieldWithNonDefaultSeparator() { Schema sc = new Schema() .addTextField("title", 1.0) .addTagField("category", ";"); assertEquals("OK", client.ftCreate(index, IndexOptions.defaultOptions(), sc)); Map fields1 = new HashMap<>(); fields1.put("title", "hello world"); fields1.put("category", "red"); // assertTrue(client.addDocument("foo", fields1)); addDocument("foo", fields1); Map fields2 = new HashMap<>(); fields2.put("title", "hello world"); fields2.put("category", "blue"); // assertTrue(client.addDocument("bar", fields2)); addDocument("bar", fields2); Map fields3 = new HashMap<>(); fields3.put("title", "hello world"); fields3.put("category", "green;yellow"); addDocument("baz", fields3); // assertTrue(client.addDocument("baz", fields3)); Map fields4 = new HashMap<>(); fields4.put("title", "hello world"); fields4.put("category", "orange,purple"); // assertTrue(client.addDocument("qux", fields4)); addDocument("qux", fields4); assertEquals(1, client.ftSearch(index, new Query("@category:{red}")).getTotalResults()); assertEquals(1, client.ftSearch(index, new Query("@category:{blue}")).getTotalResults()); assertEquals(1, client.ftSearch(index, new Query("hello @category:{red}")).getTotalResults()); assertEquals(1, client.ftSearch(index, new Query("hello @category:{blue}")).getTotalResults()); assertEquals(1, client.ftSearch(index, new Query("hello @category:{yellow}")).getTotalResults()); assertEquals(0, client.ftSearch(index, new Query("@category:{purple}")).getTotalResults()); assertEquals(1, client.ftSearch(index, new Query("@category:{orange\\,purple}")).getTotalResults()); assertEquals(4, client.ftSearch(index, new Query("hello")).getTotalResults()); assertEquals(new HashSet<>(Arrays.asList("red", "blue", "green", "yellow", "orange,purple")), client.ftTagVals(index, "category")); } @Test public void caseSensitiveTagField() { Schema sc = new Schema() .addTextField("title", 1.0) .addTagField("category", true /*casesensitive*/); assertEquals("OK", client.ftCreate(index, IndexOptions.defaultOptions(), sc)); Map fields = new HashMap<>(); fields.put("title", "hello world"); fields.put("category", "RedX"); addDocument("foo", fields); assertEquals(0, client.ftSearch(index, new Query("@category:{redx}")).getTotalResults()); assertEquals(0, client.ftSearch(index, new Query("@category:{redX}")).getTotalResults()); assertEquals(0, client.ftSearch(index, new Query("@category:{Redx}")).getTotalResults()); assertEquals(1, client.ftSearch(index, new Query("@category:{RedX}")).getTotalResults()); assertEquals(1, client.ftSearch(index, new Query("hello")).getTotalResults()); } @Test public void testReturnFields() { Schema sc = new Schema().addTextField("field1", 1.0).addTextField("field2", 1.0); assertEquals("OK", client.ftCreate(index, IndexOptions.defaultOptions(), sc)); Map doc = new HashMap<>(); doc.put("field1", "value1"); doc.put("field2", "value2"); // Store it // assertTrue(client.addDocument("doc", doc)); addDocument("doc", doc); // Query SearchResult res = client.ftSearch(index, new Query("*").returnFields("field1")); assertEquals(1, res.getTotalResults()); assertEquals("value1", res.getDocuments().get(0).get("field1")); assertNull(res.getDocuments().get(0).get("field2")); } @Test public void returnWithFieldNames() { Schema sc = new Schema().addTextField("a", 1).addTextField("b", 1).addTextField("c", 1); assertEquals("OK", client.ftCreate(index, IndexOptions.defaultOptions(), sc)); Map map = new HashMap<>(); map.put("a", "value1"); map.put("b", "value2"); map.put("c", "value3"); // assertTrue(client.addDocument("doc", map)); addDocument("doc", map); // Query SearchResult res = client.ftSearch(index, new Query().returnFields(FieldName.of("a"), FieldName.of("b").as("d"))); assertEquals(1, res.getTotalResults()); Document doc = res.getDocuments().get(0); assertEquals("value1", doc.get("a")); assertNull(doc.get("b")); assertEquals("value2", doc.get("d")); assertNull(doc.get("c")); } @Test public void inKeys() { Schema sc = new Schema().addTextField("field1", 1.0).addTextField("field2", 1.0); assertEquals("OK", client.ftCreate(index, IndexOptions.defaultOptions(), sc)); Map doc = new HashMap<>(); doc.put("field1", "value"); doc.put("field2", "not"); // Store it // assertTrue(client.addDocument("doc1", doc)); addDocument("doc1", doc); // assertTrue(client.addDocument("doc2", doc)); addDocument("doc2", doc); // Query SearchResult res = client.ftSearch(index, new Query("value").limitKeys("doc1")); assertEquals(1, res.getTotalResults()); assertEquals("doc1", res.getDocuments().get(0).getId()); assertEquals("value", res.getDocuments().get(0).get("field1")); assertNull(res.getDocuments().get(0).get("value")); } @Test public void blobField() { assumeFalse(protocol == RedisProtocol.RESP3); // not supporting Schema sc = new Schema().addTextField("field1", 1.0); assertEquals("OK", client.ftCreate(index, IndexOptions.defaultOptions(), sc)); byte[] blob = new byte[]{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12}; Map doc = new HashMap<>(); doc.put("field1", "value"); doc.put("field2", blob); // Store it // assertTrue(client.addDocument("doc1", doc)); addDocument("doc1", doc); // Query // SearchResult res = client.ftSearch(index, new Query("value"), false); SearchResult res = client.ftSearch(SafeEncoder.encode(index), new Query("value")); assertEquals(1, res.getTotalResults()); assertEquals("doc1", res.getDocuments().get(0).getId()); assertEquals("value", res.getDocuments().get(0).getString("field1")); assertArrayEquals(blob, (byte[]) res.getDocuments().get(0).get("field2")); } @Test public void alias() { Schema sc = new Schema().addTextField("field1", 1.0); assertEquals("OK", client.ftCreate(index, IndexOptions.defaultOptions(), sc)); Map doc = new HashMap<>(); doc.put("field1", "value"); // assertTrue(client.addDocument("doc1", doc)); addDocument("doc1", doc); assertEquals("OK", client.ftAliasAdd("ALIAS1", index)); SearchResult res1 = client.ftSearch("ALIAS1", new Query("*").returnFields("field1")); assertEquals(1, res1.getTotalResults()); assertEquals("value", res1.getDocuments().get(0).get("field1")); assertEquals("OK", client.ftAliasUpdate("ALIAS2", index)); SearchResult res2 = client.ftSearch("ALIAS2", new Query("*").returnFields("field1")); assertEquals(1, res2.getTotalResults()); assertEquals("value", res2.getDocuments().get(0).get("field1")); try { client.ftAliasDel("ALIAS3"); fail("Should throw JedisDataException"); } catch (JedisDataException e) { // Alias does not exist } assertEquals("OK", client.ftAliasDel("ALIAS2")); try { client.ftAliasDel("ALIAS2"); fail("Should throw JedisDataException"); } catch (JedisDataException e) { // Alias does not exist } } @Test public void synonym() { Schema sc = new Schema().addTextField("name", 1.0).addTextField("addr", 1.0); assertEquals("OK", client.ftCreate(index, IndexOptions.defaultOptions(), sc)); long group1 = 345L; long group2 = 789L; String group1_str = Long.toString(group1); String group2_str = Long.toString(group2); assertEquals("OK", client.ftSynUpdate(index, group1_str, "girl", "baby")); assertEquals("OK", client.ftSynUpdate(index, group1_str, "child")); assertEquals("OK", client.ftSynUpdate(index, group2_str, "child")); Map> dump = client.ftSynDump(index); Map> expected = new HashMap<>(); expected.put("girl", Arrays.asList(group1_str)); expected.put("baby", Arrays.asList(group1_str)); expected.put("child", Arrays.asList(group1_str, group2_str)); assertEquals(expected, dump); } @Test public void slop() { Schema sc = new Schema().addTextField("field1", 1.0).addTextField("field2", 1.0); assertEquals("OK", client.ftCreate(index, IndexOptions.defaultOptions(), sc)); Map doc = new HashMap<>(); doc.put("field1", "ok hi jedis"); addDocument("doc1", doc); SearchResult res = client.ftSearch(index, new Query("ok jedis").slop(0)); assertEquals(0, res.getTotalResults()); res = client.ftSearch(index, new Query("ok jedis").slop(1)); assertEquals(1, res.getTotalResults()); assertEquals("doc1", res.getDocuments().get(0).getId()); assertEquals("ok hi jedis", res.getDocuments().get(0).get("field1")); } @Test public void timeout() { Schema sc = new Schema().addTextField("field1", 1.0).addTextField("field2", 1.0); assertEquals("OK", client.ftCreate(index, IndexOptions.defaultOptions(), sc)); Map map = new HashMap<>(); map.put("field1", "value"); map.put("field2", "not"); client.hset("doc1", map); SearchResult res = client.ftSearch(index, new Query("value").timeout(1000)); assertEquals(1, res.getTotalResults()); assertEquals("doc1", res.getDocuments().get(0).getId()); assertEquals("value", res.getDocuments().get(0).get("field1")); assertEquals("not", res.getDocuments().get(0).get("field2")); } @Test public void inOrder() { Schema sc = new Schema().addTextField("field1", 1.0).addTextField("field2", 1.0); assertEquals("OK", client.ftCreate(index, IndexOptions.defaultOptions(), sc)); Map map = new HashMap<>(); map.put("field1", "value"); map.put("field2", "not"); client.hset("doc2", map); client.hset("doc1", map); SearchResult res = client.ftSearch(index, new Query("value").setInOrder()); assertEquals(2, res.getTotalResults()); assertEquals("doc2", res.getDocuments().get(0).getId()); assertEquals("value", res.getDocuments().get(0).get("field1")); assertEquals("not", res.getDocuments().get(0).get("field2")); } @Test public void testDialectsWithFTExplain() { Map attr = new HashMap<>(); attr.put("TYPE", "FLOAT32"); attr.put("DIM", 2); attr.put("DISTANCE_METRIC", "L2"); Schema sc = new Schema() .addFlatVectorField("v", attr) .addTagField("title") .addTextField("t1", 1.0) .addTextField("t2", 1.0) .addNumericField("num"); assertEquals("OK", client.ftCreate(index, IndexOptions.defaultOptions(), sc)); client.hset("1", "t1", "hello"); String q = "(*)"; Query query = new Query(q).dialect(1); assertSyntaxError(query, client); // dialect=1 throws syntax error query = new Query(q).dialect(2); assertThat(client.ftExplain(index, query), containsString("WILDCARD")); q = "$hello"; query = new Query(q).dialect(1); assertSyntaxError(query, client); // dialect=1 throws syntax error query = new Query(q).dialect(2).addParam("hello", "hello"); assertThat(client.ftExplain(index, query), not(emptyOrNullString())); q = "@title:(@num:[0 10])"; query = new Query(q).dialect(1); assertThat(client.ftExplain(index, query), not(emptyOrNullString())); query = new Query(q).dialect(2); assertSyntaxError(query, client); // dialect=2 throws syntax error q = "@t1:@t2:@t3:hello"; query = new Query(q).dialect(1); assertThat(client.ftExplain(index, query), not(emptyOrNullString())); query = new Query(q).dialect(2); assertSyntaxError(query, client); // dialect=2 throws syntax error q = "@title:{foo}}}}}"; query = new Query(q).dialect(1); assertThat(client.ftExplain(index, query), not(emptyOrNullString())); query = new Query(q).dialect(2); assertSyntaxError(query, client); // dialect=2 throws syntax error q = "*=>[KNN 10 @v $BLOB]"; query = new Query(q).addParam("BLOB", "aaaa").dialect(1); assertSyntaxError(query, client); // dialect=1 throws syntax error query = new Query(q).addParam("BLOB", "aaaa").dialect(2); assertThat(client.ftExplain(index, query), not(emptyOrNullString())); q = "*=>[knn $K @v $BLOB as score]"; query = new Query(q).addParam("BLOB", "aaaa").addParam("K", "10").dialect(1); assertSyntaxError(query, client); // dialect=1 throws syntax error query = new Query(q).addParam("BLOB", "aaaa").addParam("K", "10").dialect(2); assertThat(client.ftExplain(index, query), not(emptyOrNullString())); } @Test public void searchProfile() { Schema sc = new Schema().addTextField("t1", 1.0).addTextField("t2", 1.0); assertEquals("OK", client.ftCreate(index, IndexOptions.defaultOptions(), sc)); Map hash = new HashMap<>(); hash.put("t1", "foo"); hash.put("t2", "bar"); client.hset("doc1", hash); Map.Entry reply = client.ftProfileSearch(index, FTProfileParams.profileParams(), new Query("foo")); SearchResult result = reply.getKey(); assertEquals(1, result.getTotalResults()); assertEquals(Collections.singletonList("doc1"), result.getDocuments().stream().map(Document::getId).collect(Collectors.toList())); Object profileObject = reply.getValue().getProfilingInfo(); if (protocol != RedisProtocol.RESP3) { assertThat(profileObject, Matchers.isA(List.class)); if (RedisVersionUtil.getRedisVersion(client).isGreaterThanOrEqualTo(RedisVersion.V8_0_0)) { assertThat((List) profileObject, Matchers.hasItems("Shards", "Coordinator")); } } else { assertThat(profileObject, Matchers.isA(Map.class)); if (RedisVersionUtil.getRedisVersion(client).isGreaterThanOrEqualTo(RedisVersion.V8_0_0)) { assertThat(((Map) profileObject).keySet(), Matchers.hasItems("Shards", "Coordinator")); } } } @Test public void testHNSWVectorSimilarity() { Map attr = new HashMap<>(); attr.put("TYPE", "FLOAT32"); attr.put("DIM", 2); attr.put("DISTANCE_METRIC", "L2"); Schema sc = new Schema().addHNSWVectorField("v", attr); assertEquals("OK", client.ftCreate(index, IndexOptions.defaultOptions(), sc)); client.hset("a", "v", "aaaaaaaa"); client.hset("b", "v", "aaaabaaa"); client.hset("c", "v", "aaaaabaa"); Query query = new Query("*=>[KNN 2 @v $vec]") .addParam("vec", "aaaaaaaa") .setSortBy("__v_score", true) .returnFields("__v_score") .dialect(2); Document doc1 = client.ftSearch(index, query).getDocuments().get(0); assertEquals("a", doc1.getId()); assertEquals("0", doc1.get("__v_score")); } @Test @SinceRedisVersion("8.1.240") public void testSvsVamanaVectorSimilarity() { Map attr = new HashMap<>(); attr.put("TYPE", "FLOAT32"); attr.put("DIM", 2); attr.put("DISTANCE_METRIC", "L2"); Schema sc = new Schema().addSvsVamanaVectorField("v", attr); assertEquals("OK", client.ftCreate(index, IndexOptions.defaultOptions(), sc)); client.hset("a", "v", "aaaaaaaa"); client.hset("b", "v", "aaaabaaa"); client.hset("c", "v", "aaaaabaa"); Query query = new Query("*=>[KNN 2 @v $vec]") .addParam("vec", "aaaaaaaa") .setSortBy("__v_score", true) .returnFields("__v_score") .dialect(2); Document doc1 = client.ftSearch(index, query).getDocuments().get(0); assertEquals("a", doc1.getId()); assertEquals("0", doc1.get("__v_score")); } @Test public void testFlatVectorSimilarity() { Map attr = new HashMap<>(); attr.put("TYPE", "FLOAT32"); attr.put("DIM", 2); attr.put("DISTANCE_METRIC", "L2"); Schema sc = new Schema().addFlatVectorField("v", attr); assertEquals("OK", client.ftCreate(index, IndexOptions.defaultOptions(), sc)); client.hset("a", "v", "aaaaaaaa"); client.hset("b", "v", "aaaabaaa"); client.hset("c", "v", "aaaaabaa"); Query query = new Query("*=>[KNN 2 @v $vec]") .addParam("vec", "aaaaaaaa") .setSortBy("__v_score", true) .returnFields("__v_score") .dialect(2); Document doc1 = client.ftSearch(index, query).getDocuments().get(0); assertEquals("a", doc1.getId()); assertEquals("0", doc1.get("__v_score")); } @Test public void searchIteration() { Schema sc = new Schema().addTextField("first", 1.0).addTextField("last", 1.0).addNumericField("age"); IndexDefinition rule = new IndexDefinition(); assertEquals("OK", client.ftCreate(index, IndexOptions.defaultOptions().setDefinition(rule), sc)); client.hset("profesor:5555", toMap("first", "Albert", "last", "Blue", "age", "55")); client.hset("student:1111", toMap("first", "Joe", "last", "Dod", "age", "18")); client.hset("pupil:2222", toMap("first", "Jen", "last", "Rod", "age", "14")); client.hset("student:3333", toMap("first", "El", "last", "Mark", "age", "17")); client.hset("pupil:4444", toMap("first", "Pat", "last", "Shu", "age", "21")); client.hset("student:5555", toMap("first", "Joen", "last", "Ko", "age", "20")); client.hset("teacher:6666", toMap("first", "Pat", "last", "Rod", "age", "20")); FtSearchIteration search = client.ftSearchIteration(3, index, new Query()); int total = 0; while (!search.isIterationCompleted()) { SearchResult result = search.nextBatch(); int count = result.getDocuments().size(); assertThat(count, Matchers.lessThanOrEqualTo(3)); total += count; } assertEquals(7, total); } void assertSyntaxError(Query query, UnifiedJedis client) { JedisDataException error = assertThrows(JedisDataException.class, () -> client.ftExplain(index, query)); assertThat(error.getMessage(), containsString("Syntax error")); } } ================================================ FILE: src/test/java/redis/clients/jedis/modules/search/SearchWithParamsTest.java ================================================ package redis.clients.jedis.modules.search; import static org.hamcrest.MatcherAssert.assertThat; import static org.junit.jupiter.api.Assertions.assertArrayEquals; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertNotEquals; import static org.junit.jupiter.api.Assertions.assertNull; import static org.junit.jupiter.api.Assertions.assertThrows; import static org.junit.jupiter.api.Assertions.assertTrue; import static org.junit.jupiter.api.Assertions.fail; import static org.junit.jupiter.api.Assumptions.assumeTrue; import static redis.clients.jedis.util.AssertUtil.assertOK; import static redis.clients.jedis.util.RedisConditions.ModuleVersion.SEARCH_MOD_VER_80M3; import java.util.*; import java.util.stream.Collectors; import io.redis.test.annotations.SinceRedisVersion; import io.redis.test.utils.RedisVersion; import org.hamcrest.Matchers; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Tag; import org.junit.jupiter.params.ParameterizedClass; import org.junit.jupiter.params.provider.MethodSource; import org.locationtech.jts.geom.Coordinate; import org.locationtech.jts.geom.GeometryFactory; import org.locationtech.jts.geom.Point; import org.locationtech.jts.geom.Polygon; import org.locationtech.jts.io.ParseException; import org.locationtech.jts.io.WKTReader; import redis.clients.jedis.GeoCoordinate; import redis.clients.jedis.RedisProtocol; import redis.clients.jedis.args.GeoUnit; import redis.clients.jedis.args.SortingOrder; import redis.clients.jedis.exceptions.JedisDataException; import redis.clients.jedis.json.Path; import redis.clients.jedis.search.*; import redis.clients.jedis.search.RediSearchUtil; import redis.clients.jedis.search.schemafields.*; import redis.clients.jedis.search.schemafields.GeoShapeField.CoordinateSystem; import redis.clients.jedis.search.schemafields.VectorField.VectorAlgorithm; import redis.clients.jedis.modules.RedisModuleCommandsTestBase; import redis.clients.jedis.util.RedisConditions; import redis.clients.jedis.util.RedisVersionUtil; @ParameterizedClass @MethodSource("redis.clients.jedis.commands.CommandsTestsParameters#respVersions") @Tag("integration") public class SearchWithParamsTest extends RedisModuleCommandsTestBase { private static final String index = "testindex"; @BeforeAll public static void prepare() { RedisModuleCommandsTestBase.prepare(); } @AfterEach public void cleanUp() { if (client.ftList().contains(index)) { client.ftDropIndex(index); } } public SearchWithParamsTest(RedisProtocol protocol) { super(protocol); } private void addDocument(String key, Map map) { client.hset(key, RediSearchUtil.toStringMap(map)); } private static Map toMap(Object... values) { Map map = new HashMap<>(); for (int i = 0; i < values.length; i += 2) { map.put((String) values[i], values[i + 1]); } return map; } private static Map toMap(String... values) { Map map = new HashMap<>(); for (int i = 0; i < values.length; i += 2) { map.put(values[i], values[i + 1]); } return map; } @Test public void create() { assertOK(client.ftCreate(index, FTCreateParams.createParams() .filter("@age>16") .prefix("student:", "pupil:"), TextField.of("first"), TextField.of("last"), NumericField.of("age"))); client.hset("profesor:5555", toMap("first", "Albert", "last", "Blue", "age", "55")); client.hset("student:1111", toMap("first", "Joe", "last", "Dod", "age", "18")); client.hset("pupil:2222", toMap("first", "Jen", "last", "Rod", "age", "14")); client.hset("student:3333", toMap("first", "El", "last", "Mark", "age", "17")); client.hset("pupil:4444", toMap("first", "Pat", "last", "Shu", "age", "21")); client.hset("student:5555", toMap("first", "Joen", "last", "Ko", "age", "20")); client.hset("teacher:6666", toMap("first", "Pat", "last", "Rod", "age", "20")); SearchResult noFilters = client.ftSearch(index); assertEquals(4, noFilters.getTotalResults()); SearchResult res1 = client.ftSearch(index, "@first:Jo*"); assertEquals(2, res1.getTotalResults()); SearchResult res2 = client.ftSearch(index, "@first:Pat"); assertEquals(1, res2.getTotalResults()); SearchResult res3 = client.ftSearch(index, "@last:Rod"); assertEquals(0, res3.getTotalResults()); } @Test public void createNoParams() { assertOK(client.ftCreate(index, TextField.of("first").weight(1), TextField.of("last").weight(1), NumericField.of("age"))); addDocument("student:1111", toMap("first", "Joe", "last", "Dod", "age", 18)); addDocument("student:3333", toMap("first", "El", "last", "Mark", "age", 17)); addDocument("pupil:4444", toMap("first", "Pat", "last", "Shu", "age", 21)); addDocument("student:5555", toMap("first", "Joen", "last", "Ko", "age", 20)); SearchResult noFilters = client.ftSearch(index); assertEquals(4, noFilters.getTotalResults()); SearchResult res1 = client.ftSearch(index, "@first:Jo*"); assertEquals(2, res1.getTotalResults()); SearchResult res2 = client.ftSearch(index, "@first:Pat"); assertEquals(1, res2.getTotalResults()); SearchResult res3 = client.ftSearch(index, "@last:Rod"); assertEquals(0, res3.getTotalResults()); } @Test public void createWithFieldNames() { assertOK(client.ftCreate(index, FTCreateParams.createParams() .addPrefix("student:").addPrefix("pupil:"), TextField.of("first").as("given"), TextField.of(FieldName.of("last").as("family")))); client.hset("profesor:5555", toMap("first", "Albert", "last", "Blue", "age", "55")); client.hset("student:1111", toMap("first", "Joe", "last", "Dod", "age", "18")); client.hset("pupil:2222", toMap("first", "Jen", "last", "Rod", "age", "14")); client.hset("student:3333", toMap("first", "El", "last", "Mark", "age", "17")); client.hset("pupil:4444", toMap("first", "Pat", "last", "Shu", "age", "21")); client.hset("student:5555", toMap("first", "Joen", "last", "Ko", "age", "20")); client.hset("teacher:6666", toMap("first", "Pat", "last", "Rod", "age", "20")); SearchResult noFilters = client.ftSearch(index); assertEquals(5, noFilters.getTotalResults()); SearchResult asGiven = client.ftSearch(index, "@given:Jo*"); assertEquals(2, asGiven.getTotalResults()); SearchResult nonLast = client.ftSearch(index, "@last:Rod"); assertEquals(0, nonLast.getTotalResults()); SearchResult asFamily = client.ftSearch(index, "@family:Rod"); assertEquals(1, asFamily.getTotalResults()); } @Test public void alterAdd() { assertOK(client.ftCreate(index, TextField.of("title"))); Map fields = new HashMap<>(); fields.put("title", "hello world"); for (int i = 0; i < 100; i++) { addDocument(String.format("doc%d", i), fields); } SearchResult res = client.ftSearch(index, "hello world"); assertEquals(100, res.getTotalResults()); assertOK(client.ftAlter(index, TagField.of("tags"), TextField.of("name").weight(0.5))); for (int i = 0; i < 100; i++) { Map fields2 = new HashMap<>(); fields2.put("name", "name" + i); fields2.put("tags", String.format("tagA,tagB,tag%d", i)); addDocument(String.format("doc%d", i), fields2); } SearchResult res2 = client.ftSearch(index, "@tags:{tagA}"); assertEquals(100, res2.getTotalResults()); } @Test public void search() { assertOK(client.ftCreate(index, FTCreateParams.createParams(), TextField.of("title"), TextField.of("body"))); Map fields = new HashMap<>(); fields.put("title", "hello world"); fields.put("body", "lorem ipsum"); for (int i = 0; i < 100; i++) { addDocument(String.format("doc%d", i), fields); } SearchResult result = client.ftSearch(index, "hello world", FTSearchParams.searchParams().limit(0, 5).withScores()); assertEquals(100, result.getTotalResults()); assertEquals(5, result.getDocuments().size()); for (Document d : result.getDocuments()) { assertTrue(d.getId().startsWith("doc")); assertTrue(d.getScore() < 100); } client.del("doc0"); result = client.ftSearch(index, "hello world"); assertEquals(99, result.getTotalResults()); assertEquals("OK", client.ftDropIndex(index)); try { client.ftSearch(index, "hello world"); fail(); } catch (JedisDataException e) { } } @Test public void textFieldParams() { assertOK(client.ftCreate("testindex", TextField.of("title") .weight(2.5).noStem().phonetic("dm:en").withSuffixTrie().sortable())); assertOK(client.ftCreate("testunfindex", TextField.of("title") .weight(2.5).noStem().phonetic("dm:en").withSuffixTrie().sortableUNF())); if (RedisVersionUtil.getRedisVersion(client).isGreaterThanOrEqualTo(RedisVersion.V7_4)) { assertOK(client.ftCreate("testindex-missing", TextField.of("title").indexMissing().indexEmpty().weight(2.5).noStem().phonetic("dm:en") .withSuffixTrie().sortable())); assertOK(client.ftCreate("testunfindex-missing", TextField.of("title").indexMissing().indexEmpty().weight(2.5).noStem().phonetic("dm:en") .withSuffixTrie().sortableUNF())); } assertOK(client.ftCreate("testnoindex", TextField.of("title").sortable().noIndex())); assertOK(client.ftCreate("testunfnoindex", TextField.of("title").sortableUNF().noIndex())); } @Test @SinceRedisVersion(value = "7.4.0", message = "optional params since 7.4.0 are being tested") public void searchTextFieldsCondition() { assertOK(client.ftCreate(index, FTCreateParams.createParams(), TextField.of("title"), TextField.of("body").indexMissing().indexEmpty())); Map regular = new HashMap<>(); regular.put("title", "hello world"); regular.put("body", "lorem ipsum"); client.hset("regular-doc", regular); Map empty = new HashMap<>(); empty.put("title", "hello world"); empty.put("body", ""); client.hset("empty-doc", empty); Map missing = new HashMap<>(); missing.put("title", "hello world"); client.hset("missing-doc", missing); SearchResult result = client.ftSearch(index, "", FTSearchParams.searchParams().dialect(2)); assertEquals(0, result.getTotalResults()); assertEquals(0, result.getDocuments().size()); result = client.ftSearch(index, "*", FTSearchParams.searchParams().dialect(2)); assertEquals(3, result.getTotalResults()); assertEquals(3, result.getDocuments().size()); result = client.ftSearch(index, "@body:''", FTSearchParams.searchParams().dialect(2)); assertEquals(1, result.getTotalResults()); assertEquals(1, result.getDocuments().size()); assertEquals("empty-doc", result.getDocuments().get(0).getId()); result = client.ftSearch(index, "ismissing(@body)", FTSearchParams.searchParams().dialect(2)); assertEquals(1, result.getTotalResults()); assertEquals(1, result.getDocuments().size()); assertEquals("missing-doc", result.getDocuments().get(0).getId()); } @Test public void numericFilter() { assertOK(client.ftCreate(index, TextField.of("title"), NumericField.of("price"))); Map fields = new HashMap<>(); fields.put("title", "hello world"); for (int i = 0; i < 100; i++) { fields.put("price", i); addDocument(String.format("doc%d", i), fields); } SearchResult res = client.ftSearch(index, "hello world", FTSearchParams.searchParams().filter("price", 0, 49)); assertEquals(50, res.getTotalResults()); assertEquals(10, res.getDocuments().size()); for (Document d : res.getDocuments()) { long price = Long.valueOf((String) d.get("price")); assertTrue(price >= 0); assertTrue(price <= 49); } res = client.ftSearch(index, "hello world", FTSearchParams.searchParams().filter("price", 0, true, 49, true)); assertEquals(48, res.getTotalResults()); assertEquals(10, res.getDocuments().size()); for (Document d : res.getDocuments()) { long price = Long.valueOf((String) d.get("price")); assertTrue(price > 0); assertTrue(price < 49); } res = client.ftSearch(index, "hello world", FTSearchParams.searchParams().filter("price", 50, 100)); assertEquals(50, res.getTotalResults()); assertEquals(10, res.getDocuments().size()); for (Document d : res.getDocuments()) { long price = Long.valueOf((String) d.get("price")); assertTrue(price >= 50); assertTrue(price <= 100); } res = client.ftSearch(index, "hello world", FTSearchParams.searchParams() .filter("price", 20, Double.POSITIVE_INFINITY)); assertEquals(80, res.getTotalResults()); assertEquals(10, res.getDocuments().size()); res = client.ftSearch(index, "hello world", FTSearchParams.searchParams() .filter("price", Double.NEGATIVE_INFINITY, 10)); assertEquals(11, res.getTotalResults()); assertEquals(10, res.getDocuments().size()); } @Test public void numericFieldParams() { assertOK(client.ftCreate("testindex", TextField.of("title"), NumericField.of("price").as("px").sortable())); if (RedisVersionUtil.getRedisVersion(client).isGreaterThanOrEqualTo(RedisVersion.V7_4)) { assertOK(client.ftCreate("testindex-missing", TextField.of("title"), NumericField.of("price").as("px").indexMissing().sortable())); } assertOK(client.ftCreate("testnoindex", TextField.of("title"), NumericField.of("price").as("px").sortable().noIndex())); } @Test public void stopwords() { assertOK(client.ftCreate(index, FTCreateParams.createParams() .stopwords("foo", "bar", "baz"), TextField.of("title"))); Map fields = new HashMap<>(); fields.put("title", "hello world foo bar"); addDocument("doc1", fields); SearchResult res = client.ftSearch(index, "hello world"); assertEquals(1, res.getTotalResults()); res = client.ftSearch(index, "foo bar"); assertEquals(0, res.getTotalResults()); } @Test public void noStopwords() { assertOK(client.ftCreate(index, FTCreateParams.createParams().noStopwords(), TextField.of("title"))); Map fields = new HashMap<>(); fields.put("title", "hello world foo bar"); fields.put("title", "hello world foo bar to be or not to be"); addDocument("doc1", fields); assertEquals(1, client.ftSearch(index, "hello world").getTotalResults()); assertEquals(1, client.ftSearch(index, "foo bar").getTotalResults()); assertEquals(1, client.ftSearch(index, "to be or not to be").getTotalResults()); } @Test public void geoFilter() { assertOK(client.ftCreate(index, TextField.of("title"), GeoField.of("loc"))); Map fields = new HashMap<>(); fields.put("title", "hello world"); fields.put("loc", "-0.441,51.458"); addDocument("doc1", fields); fields.put("loc", "-0.1,51.2"); addDocument("doc2", fields); SearchResult res = client.ftSearch(index, "hello world", FTSearchParams.searchParams(). geoFilter("loc", -0.44, 51.45, 10, GeoUnit.KM)); assertEquals(1, res.getTotalResults()); res = client.ftSearch(index, "hello world", FTSearchParams.searchParams(). geoFilter("loc", -0.44, 51.45, 100, GeoUnit.KM)); assertEquals(2, res.getTotalResults()); } @Test public void geoFilterAndGeoCoordinateObject() { assertOK(client.ftCreate(index, TextField.of("title"), GeoField.of("loc"))); Map fields = new HashMap<>(); fields.put("title", "hello world"); fields.put("loc", new GeoCoordinate(-0.441, 51.458)); addDocument("doc1", fields); fields.put("loc", new GeoCoordinate(-0.1, 51.2)); addDocument("doc2", fields); SearchResult res = client.ftSearch(index, "hello world", FTSearchParams.searchParams() .geoFilter(new FTSearchParams.GeoFilter("loc", -0.44, 51.45, 10, GeoUnit.KM))); assertEquals(1, res.getTotalResults()); res = client.ftSearch(index, "hello world", FTSearchParams.searchParams() .geoFilter(new FTSearchParams.GeoFilter("loc", -0.44, 51.45, 100, GeoUnit.KM))); assertEquals(2, res.getTotalResults()); } @Test public void geoFieldParams() { assertOK(client.ftCreate("testindex", TextField.of("title"), GeoField.of("location").as("loc").sortable())); if (RedisVersionUtil.getRedisVersion(client).isGreaterThanOrEqualTo(RedisVersion.V7_4)) { assertOK(client.ftCreate("testindex-missing", TextField.of("title"), GeoField.of("location").as("loc").indexMissing().sortable())); } assertOK(client.ftCreate("testnoindex", TextField.of("title"), GeoField.of("location").as("loc").sortable().noIndex())); } @Test public void geoShapeFilterSpherical() throws ParseException { final WKTReader reader = new WKTReader(); final GeometryFactory factory = new GeometryFactory(); assertOK(client.ftCreate(index, GeoShapeField.of("geom", CoordinateSystem.SPHERICAL))); // polygon type final Polygon small = factory.createPolygon(new Coordinate[]{new Coordinate(34.9001, 29.7001), new Coordinate(34.9001, 29.7100), new Coordinate(34.9100, 29.7100), new Coordinate(34.9100, 29.7001), new Coordinate(34.9001, 29.7001)}); client.hsetObject("small", "geom", small); final Polygon large = factory.createPolygon(new Coordinate[]{new Coordinate(34.9001, 29.7001), new Coordinate(34.9001, 29.7200), new Coordinate(34.9200, 29.7200), new Coordinate(34.9200, 29.7001), new Coordinate(34.9001, 29.7001)}); client.hsetObject("large", "geom", large); // within condition final Polygon within = factory.createPolygon(new Coordinate[]{new Coordinate(34.9000, 29.7000), new Coordinate(34.9000, 29.7150), new Coordinate(34.9150, 29.7150), new Coordinate(34.9150, 29.7000), new Coordinate(34.9000, 29.7000)}); SearchResult result = client.ftSearch(index, "@geom:[within $poly]", FTSearchParams.searchParams().addParam("poly", within).dialect(3)); assertEquals(1, result.getTotalResults()); assertEquals(1, result.getDocuments().size()); assertEquals(small, reader.read(result.getDocuments().get(0).getString("geom"))); // contains condition final Polygon contains = factory.createPolygon(new Coordinate[]{new Coordinate(34.9002, 29.7002), new Coordinate(34.9002, 29.7050), new Coordinate(34.9050, 29.7050), new Coordinate(34.9050, 29.7002), new Coordinate(34.9002, 29.7002)}); result = client.ftSearch(index, "@geom:[contains $poly]", FTSearchParams.searchParams().addParam("poly", contains).dialect(3)); assertEquals(2, result.getTotalResults()); assertEquals(2, result.getDocuments().size()); // point type final Point point = factory.createPoint(new Coordinate(34.9010, 29.7010)); client.hset("point", "geom", point.toString()); result = client.ftSearch(index, "@geom:[within $poly]", FTSearchParams.searchParams().addParam("poly", within).dialect(3)); assertEquals(2, result.getTotalResults()); assertEquals(2, result.getDocuments().size()); } @Test public void geoShapeFilterFlat() throws ParseException { final WKTReader reader = new WKTReader(); final GeometryFactory factory = new GeometryFactory(); assertOK(client.ftCreate(index, GeoShapeField.of("geom", CoordinateSystem.FLAT))); // polygon type final Polygon small = factory.createPolygon(new Coordinate[]{new Coordinate(20, 20), new Coordinate(20, 100), new Coordinate(100, 100), new Coordinate(100, 20), new Coordinate(20, 20)}); client.hsetObject("small", "geom", small); final Polygon large = factory.createPolygon(new Coordinate[]{new Coordinate(10, 10), new Coordinate(10, 200), new Coordinate(200, 200), new Coordinate(200, 10), new Coordinate(10, 10)}); client.hsetObject("large", "geom", large); // within condition final Polygon within = factory.createPolygon(new Coordinate[]{new Coordinate(0, 0), new Coordinate(0, 150), new Coordinate(150, 150), new Coordinate(150, 0), new Coordinate(0, 0)}); SearchResult result = client.ftSearch(index, "@geom:[within $poly]", FTSearchParams.searchParams().addParam("poly", within).dialect(3)); assertEquals(1, result.getTotalResults()); assertEquals(1, result.getDocuments().size()); assertEquals(small, reader.read(result.getDocuments().get(0).getString("geom"))); // contains condition final Polygon contains = factory.createPolygon(new Coordinate[]{new Coordinate(25, 25), new Coordinate(25, 50), new Coordinate(50, 50), new Coordinate(50, 25), new Coordinate(25, 25)}); result = client.ftSearch(index, "@geom:[contains $poly]", FTSearchParams.searchParams().addParam("poly", contains).dialect(3)); assertEquals(2, result.getTotalResults()); assertEquals(2, result.getDocuments().size()); // intersects and disjoint if (RedisVersionUtil.getRedisVersion(client).isGreaterThanOrEqualTo(RedisVersion.V7_4)) { final Polygon disjointersect = factory.createPolygon(new Coordinate[]{new Coordinate(150, 150), new Coordinate(150, 250), new Coordinate(250, 250), new Coordinate(250, 150), new Coordinate(150, 150)}); result = client.ftSearch(index, "@geom:[intersects $poly]", FTSearchParams.searchParams().addParam("poly", disjointersect).dialect(3)); assertEquals(1, result.getTotalResults()); assertEquals(1, result.getDocuments().size()); assertEquals(large, reader.read(result.getDocuments().get(0).getString("geom"))); result = client.ftSearch(index, "@geom:[disjoint $poly]", FTSearchParams.searchParams().addParam("poly", disjointersect).dialect(3)); assertEquals(1, result.getTotalResults()); assertEquals(1, result.getDocuments().size()); assertEquals(small, reader.read(result.getDocuments().get(0).getString("geom"))); } // point type final Point point = factory.createPoint(new Coordinate(30, 30)); client.hsetObject("point", "geom", point); result = client.ftSearch(index, "@geom:[within $poly]", FTSearchParams.searchParams().addParam("poly", within).dialect(3)); assertEquals(2, result.getTotalResults()); assertEquals(2, result.getDocuments().size()); } @Test public void geoShapeFieldParams() { if (RedisVersionUtil.getRedisVersion(client).isGreaterThanOrEqualTo(RedisVersion.V7_4)) { assertOK(client.ftCreate("testindex-missing", GeoShapeField.of("geometry", CoordinateSystem.SPHERICAL).as("geom").indexMissing())); } assertOK(client.ftCreate("testnoindex", GeoShapeField.of("geometry", CoordinateSystem.SPHERICAL).as("geom").noIndex())); } @Test public void testQueryFlags() { assertOK(client.ftCreate(index, TextField.of("title"))); Map fields = new HashMap<>(); for (int i = 0; i < 100; i++) { fields.put("title", i % 2 != 0 ? "hello worlds" : "hello world"); addDocument(String.format("doc%d", i), fields); } SearchResult res = client.ftSearch(index, "hello", FTSearchParams.searchParams().withScores()); assertEquals(100, res.getTotalResults()); assertEquals(10, res.getDocuments().size()); for (Document d : res.getDocuments()) { assertTrue(d.getId().startsWith("doc")); assertTrue(((String) d.get("title")).startsWith("hello world")); } // // res = client.ftSearch(index, "hello", // FTSearchParams.searchParams().withScores().explainScore()); // assertEquals(100, res.getTotalResults()); // assertEquals(10, res.getDocuments().size()); // // for (Document d : res.getDocuments()) { // assertTrue(d.getId().startsWith("doc")); // assertTrue(((String) d.get("title")).startsWith("hello world")); // } res = client.ftSearch(index, "hello", FTSearchParams.searchParams().noContent()); for (Document d : res.getDocuments()) { assertTrue(d.getId().startsWith("doc")); if (protocol != RedisProtocol.RESP3) { assertEquals(1.0, d.getScore(), 0); assertNull(d.get("title")); } else { assertNull(d.getScore()); assertThrows(NullPointerException.class, () -> d.get("title")); } } // test verbatim vs. stemming res = client.ftSearch(index, "hello worlds"); assertEquals(100, res.getTotalResults()); res = client.ftSearch(index, "hello worlds", FTSearchParams.searchParams().verbatim()); assertEquals(50, res.getTotalResults()); res = client.ftSearch(index, "hello a world", FTSearchParams.searchParams().verbatim()); assertEquals(50, res.getTotalResults()); res = client.ftSearch(index, "hello a worlds", FTSearchParams.searchParams().verbatim()); assertEquals(50, res.getTotalResults()); res = client.ftSearch(index, "hello a world", FTSearchParams.searchParams().verbatim().noStopwords()); assertEquals(0, res.getTotalResults()); } @Test public void testQueryParams() { assertOK(client.ftCreate(index, NumericField.of("numval"))); client.hset("1", "numval", "1"); client.hset("2", "numval", "2"); client.hset("3", "numval", "3"); assertEquals(2, client.ftSearch(index, "@numval:[$min $max]", FTSearchParams.searchParams().addParam("min", 1).addParam("max", 2) .dialect(2)).getTotalResults()); Map paramValues = new HashMap<>(); paramValues.put("min", 1); paramValues.put("max", 2); assertEquals(2, client.ftSearch(index, "@numval:[$min $max]", FTSearchParams.searchParams().params(paramValues) .dialect(2)).getTotalResults()); if (RedisVersionUtil.getRedisVersion(client).isGreaterThanOrEqualTo(RedisVersion.V7_4) ) { assertEquals(1, client.ftSearch(index, "@numval:[$eq]", FTSearchParams.searchParams().addParam("eq", 2).dialect(4)).getTotalResults()); } } @Test public void testSortQueryFlags() { assertOK(client.ftCreate(index, TextField.of("title").sortable())); Map fields = new HashMap<>(); fields.put("title", "b title"); addDocument("doc1", fields); fields.put("title", "a title"); addDocument("doc2", fields); fields.put("title", "c title"); addDocument("doc3", fields); SearchResult res = client.ftSearch(index, "title", FTSearchParams.searchParams().sortBy("title", SortingOrder.ASC)); assertEquals(3, res.getTotalResults()); Document doc1 = res.getDocuments().get(0); assertEquals("a title", doc1.get("title")); doc1 = res.getDocuments().get(1); assertEquals("b title", doc1.get("title")); doc1 = res.getDocuments().get(2); assertEquals("c title", doc1.get("title")); } @Test public void testJsonWithAlias() { assertOK(client.ftCreate(index, FTCreateParams.createParams() .on(IndexDataType.JSON) .prefix("king:"), TextField.of("$.name").as("name"), NumericField.of("$.num").as("num"))); Map king1 = new HashMap<>(); king1.put("name", "henry"); king1.put("num", 42); client.jsonSet("king:1", Path.ROOT_PATH, king1); Map king2 = new HashMap<>(); king2.put("name", "james"); king2.put("num", 3.14); client.jsonSet("king:2", Path.ROOT_PATH, king2); SearchResult res = client.ftSearch(index, "@name:henry"); assertEquals(1, res.getTotalResults()); assertEquals("king:1", res.getDocuments().get(0).getId()); res = client.ftSearch(index, "@num:[0 10]"); assertEquals(1, res.getTotalResults()); assertEquals("king:2", res.getDocuments().get(0).getId()); res = client.ftSearch(index, "@num:[42 42]", FTSearchParams.searchParams()); assertEquals(1, res.getTotalResults()); assertEquals("king:1", res.getDocuments().get(0).getId()); if (RedisVersionUtil.getRedisVersion(client).isGreaterThanOrEqualTo(RedisVersion.V7_4)) { res = client.ftSearch(index, "@num:[42]", FTSearchParams.searchParams().dialect(4)); assertEquals(1, res.getTotalResults()); assertEquals("king:1", res.getDocuments().get(0).getId()); } } @Test public void dropIndex() { assertOK(client.ftCreate(index, TextField.of("title"))); Map fields = new HashMap<>(); fields.put("title", "hello world"); for (int i = 0; i < 100; i++) { addDocument(String.format("doc%d", i), fields); } SearchResult res = client.ftSearch(index, "hello world"); assertEquals(100, res.getTotalResults()); assertEquals("OK", client.ftDropIndex(index)); try { client.ftSearch(index, "hello world"); fail("Index should not exist."); } catch (JedisDataException de) { // toLowerCase - Error message updated to "No such index" with Redis 8.0.0 assertTrue(de.getMessage().toLowerCase().contains("no such index")); } assertEquals(100, client.dbSize()); } @Test public void dropIndexDD() { assertOK(client.ftCreate(index, TextField.of("title"))); Map fields = new HashMap<>(); fields.put("title", "hello world"); for (int i = 0; i < 100; i++) { addDocument(String.format("doc%d", i), fields); } SearchResult res = client.ftSearch(index, "hello world"); assertEquals(100, res.getTotalResults()); assertEquals("OK", client.ftDropIndexDD(index)); Set keys = client.keys("*"); assertTrue(keys.isEmpty()); assertEquals(0, client.dbSize()); } @Test public void noStem() { assertOK(client.ftCreate(index, new TextField("stemmed").weight(1.0), new TextField("notStemmed").weight(1.0).noStem())); Map doc = new HashMap<>(); doc.put("stemmed", "located"); doc.put("notStemmed", "located"); addDocument("doc", doc); // Query SearchResult res = client.ftSearch(index, "@stemmed:location"); assertEquals(1, res.getTotalResults()); res = client.ftSearch(index, "@notStemmed:location"); assertEquals(0, res.getTotalResults()); } @Test public void phoneticMatch() { assertOK(client.ftCreate(index, new TextField("noPhonetic").weight(1.0), new TextField("withPhonetic").weight(1.0).phonetic("dm:en"))); Map doc = new HashMap<>(); doc.put("noPhonetic", "morfix"); doc.put("withPhonetic", "morfix"); addDocument("doc", doc); // Query SearchResult res = client.ftSearch(index, "@withPhonetic:morphix=>{$phonetic:true}"); assertEquals(1, res.getTotalResults()); try { client.ftSearch(index, "@noPhonetic:morphix=>{$phonetic:true}"); fail(); } catch (JedisDataException e) {/*field does not support phonetics*/ } SearchResult res3 = client.ftSearch(index, "@withPhonetic:morphix=>{$phonetic:false}"); assertEquals(0, res3.getTotalResults()); } @Test public void info() { Collection sc = new ArrayList<>(); sc.add(TextField.of("title").weight(5)); sc.add(TextField.of("plot").sortable()); sc.add(TagField.of("genre").separator(',').sortable()); sc.add(NumericField.of("release_year").sortable()); sc.add(NumericField.of("rating").sortable()); sc.add(NumericField.of("votes").sortable()); assertOK(client.ftCreate(index, sc)); Map info = client.ftInfo(index); assertEquals(index, info.get("index_name")); assertEquals(6, ((List) info.get("attributes")).size()); if (protocol != RedisProtocol.RESP3) { assertEquals("global_idle", ((List) info.get("cursor_stats")).get(0)); assertEquals(0L, ((List) info.get("cursor_stats")).get(1)); } else { assertEquals(0L, ((Map) info.get("cursor_stats")).get("global_idle")); } } @Test public void noIndexAndSortBy() { assertOK(client.ftCreate(index, TextField.of("f1").sortable().noIndex(), TextField.of("f2"))); Map mm = new HashMap<>(); mm.put("f1", "MarkZZ"); mm.put("f2", "MarkZZ"); addDocument("doc1", mm); mm.clear(); mm.put("f1", "MarkAA"); mm.put("f2", "MarkBB"); addDocument("doc2", mm); SearchResult res = client.ftSearch(index, "@f1:Mark*"); assertEquals(0, res.getTotalResults()); res = client.ftSearch(index, "@f2:Mark*"); assertEquals(2, res.getTotalResults()); res = client.ftSearch(index, "@f2:Mark*", FTSearchParams.searchParams().sortBy("f1", SortingOrder.DESC)); assertEquals(2, res.getTotalResults()); assertEquals("doc1", res.getDocuments().get(0).getId()); res = client.ftSearch(index, "@f2:Mark*", FTSearchParams.searchParams().sortBy("f1", SortingOrder.ASC)); assertEquals("doc2", res.getDocuments().get(0).getId()); } @Test public void testHighlightSummarize() { assertOK(client.ftCreate(index, TextField.of("text").weight(1))); Map doc = new HashMap<>(); doc.put("text", "Redis is often referred as a data structures server. What this means is that " + "Redis provides access to mutable data structures via a set of commands, which are sent " + "using a server-client model with TCP sockets and a simple protocol. So different " + "processes can query and modify the same data structures in a shared way"); // Add a document addDocument("foo", doc); SearchResult res = client.ftSearch(index, "data", FTSearchParams.searchParams().highlight().summarize()); assertEquals("is often referred as a data structures server. What this means is that " + "Redis provides... What this means is that Redis provides access to mutable data " + "structures via a set of commands, which are sent using a... So different processes can " + "query and modify the same data structures in a shared... ", res.getDocuments().get(0).get("text")); res = client.ftSearch(index, "data", FTSearchParams.searchParams() .highlight(FTSearchParams.highlightParams().tags("", "")) .summarize()); assertEquals("is often referred as a data structures server. What this means is that " + "Redis provides... What this means is that Redis provides access to mutable data " + "structures via a set of commands, which are sent using a... So different processes can " + "query and modify the same data structures in a shared... ", res.getDocuments().get(0).get("text")); } @Test public void getTagField() { assertOK(client.ftCreate(index, TextField.of("title"), TagField.of("category"))); Map fields1 = new HashMap<>(); fields1.put("title", "hello world"); fields1.put("category", "red"); addDocument("foo", fields1); Map fields2 = new HashMap<>(); fields2.put("title", "hello world"); fields2.put("category", "blue"); addDocument("bar", fields2); Map fields3 = new HashMap<>(); fields3.put("title", "hello world"); fields3.put("category", "green,yellow"); addDocument("baz", fields3); Map fields4 = new HashMap<>(); fields4.put("title", "hello world"); fields4.put("category", "orange;purple"); addDocument("qux", fields4); assertEquals(1, client.ftSearch(index, "@category:{red}").getTotalResults()); assertEquals(1, client.ftSearch(index, "@category:{blue}").getTotalResults()); assertEquals(1, client.ftSearch(index, "hello @category:{red}").getTotalResults()); assertEquals(1, client.ftSearch(index, "hello @category:{blue}").getTotalResults()); assertEquals(1, client.ftSearch(index, "@category:{yellow}").getTotalResults()); assertEquals(0, client.ftSearch(index, "@category:{purple}").getTotalResults()); assertEquals(1, client.ftSearch(index, "@category:{orange\\;purple}").getTotalResults()); assertEquals(4, client.ftSearch(index, "hello").getTotalResults()); assertEquals(new HashSet<>(Arrays.asList("red", "blue", "green", "yellow", "orange;purple")), client.ftTagVals(index, "category")); } @Test public void testGetTagFieldWithNonDefaultSeparator() { assertOK(client.ftCreate(index, TextField.of("title"), TagField.of("category").separator(';'))); Map fields1 = new HashMap<>(); fields1.put("title", "hello world"); fields1.put("category", "red"); addDocument("foo", fields1); Map fields2 = new HashMap<>(); fields2.put("title", "hello world"); fields2.put("category", "blue"); addDocument("bar", fields2); Map fields3 = new HashMap<>(); fields3.put("title", "hello world"); fields3.put("category", "green;yellow"); addDocument("baz", fields3); Map fields4 = new HashMap<>(); fields4.put("title", "hello world"); fields4.put("category", "orange,purple"); addDocument("qux", fields4); assertEquals(1, client.ftSearch(index, "@category:{red}").getTotalResults()); assertEquals(1, client.ftSearch(index, "@category:{blue}").getTotalResults()); assertEquals(1, client.ftSearch(index, "hello @category:{red}").getTotalResults()); assertEquals(1, client.ftSearch(index, "hello @category:{blue}").getTotalResults()); assertEquals(1, client.ftSearch(index, "hello @category:{yellow}").getTotalResults()); assertEquals(0, client.ftSearch(index, "@category:{purple}").getTotalResults()); assertEquals(1, client.ftSearch(index, "@category:{orange\\,purple}").getTotalResults()); assertEquals(4, client.ftSearch(index, "hello").getTotalResults()); assertEquals(new HashSet<>(Arrays.asList("red", "blue", "green", "yellow", "orange,purple")), client.ftTagVals(index, "category")); } @Test public void caseSensitiveTagField() { assertOK(client.ftCreate(index, TextField.of("title"), TagField.of("category").caseSensitive())); Map fields = new HashMap<>(); fields.put("title", "hello world"); fields.put("category", "RedX"); addDocument("foo", fields); assertEquals(0, client.ftSearch(index, "@category:{redx}").getTotalResults()); assertEquals(0, client.ftSearch(index, "@category:{redX}").getTotalResults()); assertEquals(0, client.ftSearch(index, "@category:{Redx}").getTotalResults()); assertEquals(1, client.ftSearch(index, "@category:{RedX}").getTotalResults()); assertEquals(1, client.ftSearch(index, "hello").getTotalResults()); } @Test public void tagFieldParams() { assertOK(client.ftCreate("testindex", TextField.of("title"), TagField.of("category").as("cat").separator(',') .caseSensitive().withSuffixTrie().sortable())); assertOK(client.ftCreate("testunfindex", TextField.of("title"), TagField.of("category").as("cat").separator(',') .caseSensitive().withSuffixTrie().sortableUNF())); if (RedisVersionUtil.getRedisVersion(client).isGreaterThanOrEqualTo(RedisVersion.V7_4)) { assertOK(client.ftCreate("testindex-missing", TextField.of("title"), TagField.of("category").as("cat").indexMissing().indexEmpty().separator(',') .caseSensitive().withSuffixTrie().sortable())); assertOK(client.ftCreate("testunfindex-missing", TextField.of("title"), TagField.of("category").as("cat").indexMissing().indexEmpty().separator(',') .caseSensitive().withSuffixTrie().sortableUNF())); } assertOK(client.ftCreate("testnoindex", TextField.of("title"), TagField.of("category").as("cat").sortable().noIndex())); assertOK(client.ftCreate("testunfnoindex", TextField.of("title"), TagField.of("category").as("cat").sortableUNF().noIndex())); } @Test public void returnFields() { assertOK(client.ftCreate(index, TextField.of("field1"), TextField.of("field2"))); Map doc = new HashMap<>(); doc.put("field1", "value1"); doc.put("field2", "value2"); addDocument("doc", doc); // Query SearchResult res = client.ftSearch(index, "*", FTSearchParams.searchParams().returnFields("field1")); assertEquals(1, res.getTotalResults()); Document ret = res.getDocuments().get(0); assertEquals("value1", ret.get("field1")); assertNull(ret.get("field2")); res = client.ftSearch(index, "*", FTSearchParams.searchParams().returnField("field1", true)); assertEquals("value1", res.getDocuments().get(0).get("field1")); res = client.ftSearch(index, "*", FTSearchParams.searchParams().returnField("field1", false)); assertArrayEquals("value1".getBytes(), (byte[]) res.getDocuments().get(0).get("field1")); } @Test public void returnFieldsNames() { assertOK(client.ftCreate(index, TextField.of("a"), TextField.of("b"), TextField.of("c"))); Map map = new HashMap<>(); map.put("a", "value1"); map.put("b", "value2"); map.put("c", "value3"); addDocument("doc", map); // Query SearchResult res = client.ftSearch(index, "*", FTSearchParams.searchParams() .returnFields(FieldName.of("a"), FieldName.of("b").as("d"))); assertEquals(1, res.getTotalResults()); Document doc = res.getDocuments().get(0); assertEquals("value1", doc.get("a")); assertNull(doc.get("b")); assertEquals("value2", doc.get("d")); assertNull(doc.get("c")); res = client.ftSearch(index, "*", FTSearchParams.searchParams() .returnField(FieldName.of("a")) .returnField(FieldName.of("b").as("d"))); assertEquals(1, res.getTotalResults()); assertEquals("value1", res.getDocuments().get(0).get("a")); assertEquals("value2", res.getDocuments().get(0).get("d")); res = client.ftSearch(index, "*", FTSearchParams.searchParams() .returnField(FieldName.of("a"), true) .returnField(FieldName.of("b").as("d"), true)); assertEquals(1, res.getTotalResults()); assertEquals("value1", res.getDocuments().get(0).get("a")); assertEquals("value2", res.getDocuments().get(0).get("d")); res = client.ftSearch(index, "*", FTSearchParams.searchParams() .returnField(FieldName.of("a"), false) .returnField(FieldName.of("b").as("d"), false)); assertEquals(1, res.getTotalResults()); assertArrayEquals("value1".getBytes(), (byte[]) res.getDocuments().get(0).get("a")); assertArrayEquals("value2".getBytes(), (byte[]) res.getDocuments().get(0).get("d")); } @Test public void inKeys() { assertOK(client.ftCreate(index, TextField.of("field1"), TextField.of("field2"))); Map doc = new HashMap<>(); doc.put("field1", "value"); doc.put("field2", "not"); // Store it addDocument("doc1", doc); addDocument("doc2", doc); // Query SearchResult res = client.ftSearch(index, "value", FTSearchParams.searchParams().inKeys("doc1")); assertEquals(1, res.getTotalResults()); assertEquals("doc1", res.getDocuments().get(0).getId()); assertEquals("value", res.getDocuments().get(0).get("field1")); assertNull(res.getDocuments().get(0).get("value")); } @Test public void alias() { assertOK(client.ftCreate(index, TextField.of("field1"))); Map doc = new HashMap<>(); doc.put("field1", "value"); addDocument("doc1", doc); assertEquals("OK", client.ftAliasAdd("ALIAS1", index)); SearchResult res1 = client.ftSearch("ALIAS1", "*", FTSearchParams.searchParams().returnFields("field1")); assertEquals(1, res1.getTotalResults()); assertEquals("value", res1.getDocuments().get(0).get("field1")); assertEquals("OK", client.ftAliasUpdate("ALIAS2", index)); SearchResult res2 = client.ftSearch("ALIAS2", "*", FTSearchParams.searchParams().returnFields("field1")); assertEquals(1, res2.getTotalResults()); assertEquals("value", res2.getDocuments().get(0).get("field1")); try { client.ftAliasDel("ALIAS3"); fail("Should throw JedisDataException"); } catch (JedisDataException e) { // Alias does not exist } assertEquals("OK", client.ftAliasDel("ALIAS2")); try { client.ftAliasDel("ALIAS2"); fail("Should throw JedisDataException"); } catch (JedisDataException e) { // Alias does not exist } } @Test public void synonym() { assertOK(client.ftCreate(index, TextField.of("name").weight(1), TextField.of("addr").weight(1))); long group1 = 345L; long group2 = 789L; String group1_str = Long.toString(group1); String group2_str = Long.toString(group2); assertEquals("OK", client.ftSynUpdate(index, group1_str, "girl", "baby")); assertEquals("OK", client.ftSynUpdate(index, group1_str, "child")); assertEquals("OK", client.ftSynUpdate(index, group2_str, "child")); Map> dump = client.ftSynDump(index); Map> expected = new HashMap<>(); expected.put("girl", Arrays.asList(group1_str)); expected.put("baby", Arrays.asList(group1_str)); expected.put("child", Arrays.asList(group1_str, group2_str)); assertEquals(expected, dump); } @Test public void slop() { assertOK(client.ftCreate(index, TextField.of("field1"), TextField.of("field2"))); Map doc = new HashMap<>(); doc.put("field1", "ok hi jedis"); addDocument("doc1", doc); SearchResult res = client.ftSearch(index, "ok jedis", FTSearchParams.searchParams().slop(0)); assertEquals(0, res.getTotalResults()); res = client.ftSearch(index, "ok jedis", FTSearchParams.searchParams().slop(1)); assertEquals(1, res.getTotalResults()); assertEquals("doc1", res.getDocuments().get(0).getId()); assertEquals("ok hi jedis", res.getDocuments().get(0).get("field1")); } @Test public void timeout() { assertOK(client.ftCreate(index, TextField.of("field1"), TextField.of("field2"))); Map map = new HashMap<>(); map.put("field1", "value"); map.put("field2", "not"); client.hset("doc1", map); SearchResult res = client.ftSearch(index, "value", FTSearchParams.searchParams().timeout(1000)); assertEquals(1, res.getTotalResults()); assertEquals("doc1", res.getDocuments().get(0).getId()); assertEquals("value", res.getDocuments().get(0).get("field1")); assertEquals("not", res.getDocuments().get(0).get("field2")); } @Test public void inOrder() { assertOK(client.ftCreate(index, TextField.of("field1"), TextField.of("field2"))); Map map = new HashMap<>(); map.put("field1", "value"); map.put("field2", "not"); client.hset("doc2", map); client.hset("doc1", map); SearchResult res = client.ftSearch(index, "value", FTSearchParams.searchParams().inOrder()); assertEquals(2, res.getTotalResults()); assertEquals("doc2", res.getDocuments().get(0).getId()); assertEquals("value", res.getDocuments().get(0).get("field1")); assertEquals("not", res.getDocuments().get(0).get("field2")); } @Test public void testHNSWVectorSimilarity() { Map attr = new HashMap<>(); attr.put("TYPE", "FLOAT32"); attr.put("DIM", 2); attr.put("DISTANCE_METRIC", "L2"); assertOK(client.ftCreate(index, VectorField.builder().fieldName("v") .algorithm(VectorAlgorithm.HNSW).attributes(attr).build())); client.hset("a", "v", "aaaaaaaa"); client.hset("b", "v", "aaaabaaa"); client.hset("c", "v", "aaaaabaa"); FTSearchParams searchParams = FTSearchParams.searchParams() .addParam("vec", "aaaaaaaa") .sortBy("__v_score", SortingOrder.ASC) .returnFields("__v_score") .dialect(2); Document doc1 = client.ftSearch(index, "*=>[KNN 2 @v $vec]", searchParams).getDocuments().get(0); assertEquals("a", doc1.getId()); assertEquals("0", doc1.get("__v_score")); } @Test public void testFlatVectorSimilarity() { assertOK(client.ftCreate(index, VectorField.builder().fieldName("v") .algorithm(VectorAlgorithm.FLAT) .addAttribute("TYPE", "FLOAT32") .addAttribute("DIM", 2) .addAttribute("DISTANCE_METRIC", "L2") .build() )); client.hset("a", "v", "aaaaaaaa"); client.hset("b", "v", "aaaabaaa"); client.hset("c", "v", "aaaaabaa"); FTSearchParams searchParams = FTSearchParams.searchParams() .addParam("vec", "aaaaaaaa") .sortBy("__v_score", SortingOrder.ASC) .returnFields("__v_score") .dialect(2); Document doc1 = client.ftSearch(index, "*=>[KNN 2 @v $vec]", searchParams).getDocuments().get(0); assertEquals("a", doc1.getId()); assertEquals("0", doc1.get("__v_score")); } @Test public void testFlatVectorSimilarityInt8() { assumeTrue(RedisConditions.of(client).moduleVersionIsGreaterThanOrEqual(SEARCH_MOD_VER_80M3), "INT8"); assertOK(client.ftCreate(index, VectorField.builder().fieldName("v").algorithm(VectorAlgorithm.FLAT) .addAttribute("TYPE", "INT8").addAttribute("DIM", 2) .addAttribute("DISTANCE_METRIC", "L2").build())); byte[] a = { 127, 1 }; byte[] b = { 127, 10 }; byte[] c = { 127, 100 }; client.hset("a".getBytes(), "v".getBytes(), a); client.hset("b".getBytes(), "v".getBytes(), b); client.hset("c".getBytes(), "v".getBytes(), c); FTSearchParams searchParams = FTSearchParams.searchParams().addParam("vec", a) .sortBy("__v_score", SortingOrder.ASC).returnFields("__v_score"); Document doc1 = client.ftSearch(index, "*=>[KNN 2 @v $vec]", searchParams).getDocuments() .get(0); assertEquals("a", doc1.getId()); assertEquals("0", doc1.get("__v_score")); } @Test @SinceRedisVersion(value = "7.4.0", message = "no optional params before 7.4.0") public void vectorFieldParams() { Map attr = new HashMap<>(); attr.put("TYPE", "FLOAT32"); attr.put("DIM", 2); attr.put("DISTANCE_METRIC", "L2"); assertOK(client.ftCreate("testindex-missing", new VectorField("vector", VectorAlgorithm.HNSW, attr).as("vec").indexMissing())); // assertOK(client.ftCreate("testnoindex", new VectorField("vector", VectorAlgorithm.HNSW, attr).as("vec").noIndex())); // throws Field `NOINDEX` does not have a type } @Test @SinceRedisVersion(value = "7.4.0", message = "FLOAT16") public void float16StorageType() { assertOK(client.ftCreate(index, VectorField.builder().fieldName("v") .algorithm(VectorAlgorithm.HNSW) .addAttribute("TYPE", "FLOAT16") .addAttribute("DIM", 4) .addAttribute("DISTANCE_METRIC", "L2") .build())); } @Test @SinceRedisVersion(value = "7.4.0", message = "BFLOAT16") public void bfloat16StorageType() { assertOK(client.ftCreate(index, VectorField.builder().fieldName("v") .algorithm(VectorAlgorithm.HNSW) .addAttribute("TYPE", "BFLOAT16") .addAttribute("DIM", 4) .addAttribute("DISTANCE_METRIC", "L2") .build())); } @Test public void int8StorageType() { assumeTrue(RedisConditions.of(client).moduleVersionIsGreaterThanOrEqual(SEARCH_MOD_VER_80M3), "INT8"); assertOK(client.ftCreate(index, VectorField.builder().fieldName("v").algorithm(VectorAlgorithm.HNSW) .addAttribute("TYPE", "INT8").addAttribute("DIM", 4) .addAttribute("DISTANCE_METRIC", "L2").build())); } @Test public void uint8StorageType() { assumeTrue(RedisConditions.of(client).moduleVersionIsGreaterThanOrEqual(SEARCH_MOD_VER_80M3), "UINT8"); assertOK(client.ftCreate(index, VectorField.builder().fieldName("v").algorithm(VectorAlgorithm.HNSW) .addAttribute("TYPE", "UINT8").addAttribute("DIM", 4) .addAttribute("DISTANCE_METRIC", "L2").build())); } @Test @SinceRedisVersion("8.1.240") public void testSvsVamanaVectorSimilarity() { Map attr = new HashMap<>(); attr.put("TYPE", "FLOAT32"); attr.put("DIM", 2); attr.put("DISTANCE_METRIC", "L2"); assertOK(client.ftCreate(index, VectorField.builder().fieldName("v") .algorithm(VectorAlgorithm.SVS_VAMANA).attributes(attr).build())); // Create proper float vectors float[] vectorA = {1.0f, 2.0f}; float[] vectorB = {1.1f, 2.1f}; float[] vectorC = {2.0f, 3.0f}; // Convert to byte arrays using RediSearchUtil byte[] bytesA = RediSearchUtil.toByteArray(vectorA); byte[] bytesB = RediSearchUtil.toByteArray(vectorB); byte[] bytesC = RediSearchUtil.toByteArray(vectorC); client.hset("a".getBytes(), "v".getBytes(), bytesA); client.hset("b".getBytes(), "v".getBytes(), bytesB); client.hset("c".getBytes(), "v".getBytes(), bytesC); FTSearchParams searchParams = FTSearchParams.searchParams() .addParam("vec", bytesA) .sortBy("__v_score", SortingOrder.ASC) .returnFields("__v_score") .dialect(2); Document doc1 = client.ftSearch(index, "*=>[KNN 2 @v $vec]", searchParams).getDocuments().get(0); assertEquals("a", doc1.getId()); assertEquals("0", doc1.get("__v_score")); } @Test @SinceRedisVersion("8.1.240") public void testSvsVamanaVectorWithAdvancedParameters() { assertOK(client.ftCreate(index, VectorField.builder().fieldName("v") .algorithm(VectorAlgorithm.SVS_VAMANA) .addAttribute("TYPE", "FLOAT32") .addAttribute("DIM", 4) .addAttribute("DISTANCE_METRIC", "L2") .addAttribute("CONSTRUCTION_WINDOW_SIZE", 200) .addAttribute("GRAPH_MAX_DEGREE", 64) .addAttribute("SEARCH_WINDOW_SIZE", 100) .addAttribute("EPSILON", 0.01) .build() )); } @Test public void searchProfile() { assertOK(client.ftCreate(index, TextField.of("t1"), TextField.of("t2"))); Map hash = new HashMap<>(); hash.put("t1", "foo"); hash.put("t2", "bar"); client.hset("doc1", hash); Map.Entry reply = client.ftProfileSearch(index, FTProfileParams.profileParams(), "foo", FTSearchParams.searchParams()); SearchResult result = reply.getKey(); assertEquals(1, result.getTotalResults()); assertEquals(Collections.singletonList("doc1"), result.getDocuments().stream().map(Document::getId).collect(Collectors.toList())); // profile Object profileObject = reply.getValue().getProfilingInfo(); if (protocol != RedisProtocol.RESP3) { assertThat(profileObject, Matchers.isA(List.class)); if (RedisVersionUtil.getRedisVersion(client).isGreaterThanOrEqualTo(RedisVersion.V8_0_0)) { assertThat((List) profileObject, Matchers.hasItems("Shards", "Coordinator")); } } else { assertThat(profileObject, Matchers.isA(Map.class)); if (RedisVersionUtil.getRedisVersion(client).isGreaterThanOrEqualTo(RedisVersion.V8_0_0)) { assertThat(((Map) profileObject).keySet(), Matchers.hasItems("Shards", "Coordinator")); } } } @Test public void vectorSearchProfile() { assertOK(client.ftCreate(index, VectorField.builder().fieldName("v") .algorithm(VectorAlgorithm.FLAT).addAttribute("TYPE", "FLOAT32") .addAttribute("DIM", 2).addAttribute("DISTANCE_METRIC", "L2").build(), TextField.of("t"))); client.hset("1", toMap("v", "bababaca", "t", "hello")); client.hset("2", toMap("v", "babababa", "t", "hello")); client.hset("3", toMap("v", "aabbaabb", "t", "hello")); client.hset("4", toMap("v", "bbaabbaa", "t", "hello world")); client.hset("5", toMap("v", "aaaabbbb", "t", "hello world")); FTSearchParams searchParams = FTSearchParams.searchParams().addParam("vec", "aaaaaaaa") .sortBy("__v_score", SortingOrder.ASC).noContent().dialect(2); Map.Entry reply = client.ftProfileSearch(index, FTProfileParams.profileParams(), "*=>[KNN 3 @v $vec]", searchParams); assertEquals(3, reply.getKey().getTotalResults()); assertEquals(Arrays.asList(4, 2, 1).toString(), reply.getKey().getDocuments() .stream().map(Document::getId).collect(Collectors.toList()).toString()); // profile Object profileObject = reply.getValue().getProfilingInfo(); if (protocol != RedisProtocol.RESP3) { assertThat(profileObject, Matchers.isA(List.class)); if (RedisVersionUtil.getRedisVersion(client).isGreaterThanOrEqualTo(RedisVersion.V8_0_0)) { assertThat((List) profileObject, Matchers.hasItems("Shards", "Coordinator")); } } else { assertThat(profileObject, Matchers.isA(Map.class)); if (RedisVersionUtil.getRedisVersion(client).isGreaterThanOrEqualTo(RedisVersion.V8_0_0)) { assertThat(((Map) profileObject).keySet(), Matchers.hasItems("Shards", "Coordinator")); } } } @Test public void limitedSearchProfile() { assertOK(client.ftCreate(index, TextField.of("t"))); client.hset("1", "t", "hello"); client.hset("2", "t", "hell"); client.hset("3", "t", "help"); client.hset("4", "t", "helowa"); Map.Entry reply = client.ftProfileSearch(index, FTProfileParams.profileParams().limited(), "%hell% hel*", FTSearchParams.searchParams().noContent()); // profile Object profileObject = reply.getValue().getProfilingInfo(); if (protocol != RedisProtocol.RESP3) { assertThat(profileObject, Matchers.isA(List.class)); if (RedisVersionUtil.getRedisVersion(client).isGreaterThanOrEqualTo(RedisVersion.V8_0_0)) { assertThat((List) profileObject, Matchers.hasItems("Shards", "Coordinator")); } } else { assertThat(profileObject, Matchers.isA(Map.class)); if (RedisVersionUtil.getRedisVersion(client).isGreaterThanOrEqualTo(RedisVersion.V8_0_0)) { assertThat(((Map) profileObject).keySet(), Matchers.hasItems("Shards", "Coordinator")); } } } @Test public void list() { assertEquals(Collections.emptySet(), client.ftList()); final int count = 20; Set names = new HashSet<>(); for (int i = 0; i < count; i++) { final String name = "idx" + i; assertOK(client.ftCreate(name, TextField.of("t" + i))); names.add(name); } assertEquals(names, client.ftList()); } @Test public void broadcast() { String reply = client.ftCreate(index, TextField.of("t")); assertOK(reply); } @Test public void searchIteration() { assertOK(client.ftCreate(index, FTCreateParams.createParams(), TextField.of("first"), TextField.of("last"), NumericField.of("age"))); client.hset("profesor:5555", toMap("first", "Albert", "last", "Blue", "age", "55")); client.hset("student:1111", toMap("first", "Joe", "last", "Dod", "age", "18")); client.hset("pupil:2222", toMap("first", "Jen", "last", "Rod", "age", "14")); client.hset("student:3333", toMap("first", "El", "last", "Mark", "age", "17")); client.hset("pupil:4444", toMap("first", "Pat", "last", "Shu", "age", "21")); client.hset("student:5555", toMap("first", "Joen", "last", "Ko", "age", "20")); client.hset("teacher:6666", toMap("first", "Pat", "last", "Rod", "age", "20")); FtSearchIteration search = client.ftSearchIteration(3, index, "*", FTSearchParams.searchParams()); int total = 0; while (!search.isIterationCompleted()) { SearchResult result = search.nextBatch(); int count = result.getDocuments().size(); assertThat(count, Matchers.lessThanOrEqualTo(3)); total += count; } assertEquals(7, total); } @Test public void searchIterationCollect() { assertOK(client.ftCreate(index, FTCreateParams.createParams(), TextField.of("first"), TextField.of("last"), NumericField.of("age"))); client.hset("profesor:5555", toMap("first", "Albert", "last", "Blue", "age", "55")); client.hset("student:1111", toMap("first", "Joe", "last", "Dod", "age", "18")); client.hset("pupil:2222", toMap("first", "Jen", "last", "Rod", "age", "14")); client.hset("student:3333", toMap("first", "El", "last", "Mark", "age", "17")); client.hset("pupil:4444", toMap("first", "Pat", "last", "Shu", "age", "21")); client.hset("student:5555", toMap("first", "Joen", "last", "Ko", "age", "20")); client.hset("teacher:6666", toMap("first", "Pat", "last", "Rod", "age", "20")); ArrayList collect = new ArrayList<>(); client.ftSearchIteration(3, index, "*", FTSearchParams.searchParams()).collect(collect); assertEquals(7, collect.size()); assertEquals(Arrays.asList("profesor:5555", "student:1111", "pupil:2222", "student:3333", "pupil:4444", "student:5555", "teacher:6666").stream().collect(Collectors.toSet()), collect.stream().map(Document::getId).collect(Collectors.toSet())); } @Test public void escapeUtil() { assertOK(client.ftCreate(index, TextField.of("txt"))); client.hset("doc1", "txt", RediSearchUtil.escape("hello-world")); assertNotEquals("hello-world", client.hget("doc1", "txt")); assertEquals("hello-world", RediSearchUtil.unescape(client.hget("doc1", "txt"))); SearchResult resultNoEscape = client.ftSearch(index, "hello-world"); assertEquals(0, resultNoEscape.getTotalResults()); SearchResult resultEscaped = client.ftSearch(index, RediSearchUtil.escapeQuery("hello-world")); assertEquals(1, resultEscaped.getTotalResults()); } @Test public void escapeMapUtil() { client.hset("doc2", RediSearchUtil.toStringMap(Collections.singletonMap("txt", "hello-world"), true)); assertNotEquals("hello-world", client.hget("doc2", "txt")); assertEquals("hello-world", RediSearchUtil.unescape(client.hget("doc2", "txt"))); } @Test public void hsetObject() { float[] floats = new float[]{0.2f}; assertEquals(1L, client.hsetObject("obj", "floats", floats)); assertArrayEquals(RediSearchUtil.toByteArray(floats), client.hget("obj".getBytes(), "floats".getBytes())); GeoCoordinate geo = new GeoCoordinate(-0.441, 51.458); Map fields = new HashMap<>(); fields.put("title", "hello world"); fields.put("loc", geo); assertEquals(2L, client.hsetObject("obj", fields)); Map stringMap = client.hgetAll("obj"); assertEquals(3, stringMap.size()); assertEquals("hello world", stringMap.get("title")); assertEquals(geo.getLongitude() + "," + geo.getLatitude(), stringMap.get("loc")); } } ================================================ FILE: src/test/java/redis/clients/jedis/modules/search/SpellCheckTest.java ================================================ package redis.clients.jedis.modules.search; import java.util.Arrays; import java.util.Collections; import java.util.HashMap; import java.util.HashSet; import java.util.Map; import org.hamcrest.MatcherAssert; import org.hamcrest.Matchers; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.Test; import org.junit.jupiter.params.ParameterizedClass; import org.junit.jupiter.params.provider.MethodSource; import redis.clients.jedis.RedisProtocol; import redis.clients.jedis.exceptions.JedisDataException; import redis.clients.jedis.modules.RedisModuleCommandsTestBase; import redis.clients.jedis.search.FTSpellCheckParams; import redis.clients.jedis.search.schemafields.TextField; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertThrows; @ParameterizedClass @MethodSource("redis.clients.jedis.commands.CommandsTestsParameters#respVersions") public class SpellCheckTest extends RedisModuleCommandsTestBase { private static final String index = "spellcheck"; @BeforeAll public static void prepare() { RedisModuleCommandsTestBase.prepare(); } public SpellCheckTest(RedisProtocol protocol) { super(protocol); } private static Map toMap(String... values) { Map map = new HashMap<>(); for (int i = 0; i < values.length; i += 2) { map.put(values[i], values[i + 1]); } return map; } @Test public void dictionary() { assertEquals(3L, client.ftDictAdd("dict", "foo", "bar", "hello world")); assertEquals(new HashSet<>(Arrays.asList("foo", "bar", "hello world")), client.ftDictDump("dict")); assertEquals(3L, client.ftDictDel("dict", "foo", "bar", "hello world")); assertEquals(Collections.emptySet(), client.ftDictDump("dict")); } @Test public void dictionaryBySampleKey() { assertEquals(3L, client.ftDictAddBySampleKey(index, "dict", "foo", "bar", "hello world")); assertEquals(new HashSet<>(Arrays.asList("foo", "bar", "hello world")), client.ftDictDumpBySampleKey(index, "dict")); assertEquals(3L, client.ftDictDelBySampleKey(index, "dict", "foo", "bar", "hello world")); assertEquals(Collections.emptySet(), client.ftDictDumpBySampleKey(index, "dict")); } @Test public void basicSpellCheck() { client.ftCreate(index, TextField.of("name"), TextField.of("body")); client.hset("doc1", toMap("name", "name1", "body", "body1")); client.hset("doc2", toMap("name", "name2", "body", "body2")); client.hset("doc3", toMap("name", "name2", "body", "name2")); Map> reply = client.ftSpellCheck(index, "name"); assertEquals(Collections.singleton("name"), reply.keySet()); assertEquals(new HashSet<>(Arrays.asList("name1", "name2")), reply.get("name").keySet()); } @Test public void crossTermDictionary() { client.ftCreate(index, TextField.of("report")); client.ftDictAdd("slang", "timmies", "toque", "toonie", "serviette", "kerfuffle", "chesterfield"); Map> expected = Collections.singletonMap("tooni", Collections.singletonMap("toonie", 0d)); assertEquals(expected, client.ftSpellCheck(index, "Tooni toque kerfuffle", FTSpellCheckParams.spellCheckParams().includeTerm("slang").excludeTerm("slang"))); } @Test public void distanceBound() { client.ftCreate(index, TextField.of("name"), TextField.of("body")); assertThrows(JedisDataException.class, () -> client.ftSpellCheck(index, "name", FTSpellCheckParams.spellCheckParams().distance(0))); } @Test public void dialectBound() { client.ftCreate(index, TextField.of("t")); JedisDataException error = assertThrows(JedisDataException.class, () -> client.ftSpellCheck(index, "Tooni toque kerfuffle", FTSpellCheckParams.spellCheckParams().dialect(0))); MatcherAssert.assertThat(error.getMessage(), Matchers.containsString("DIALECT requires a non negative integer")); } } ================================================ FILE: src/test/java/redis/clients/jedis/modules/search/SuggestionTest.java ================================================ package redis.clients.jedis.modules.search; import static java.util.Collections.emptyList; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertTrue; import java.util.List; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.Test; import org.junit.jupiter.params.ParameterizedClass; import org.junit.jupiter.params.provider.MethodSource; import redis.clients.jedis.RedisProtocol; import redis.clients.jedis.modules.RedisModuleCommandsTestBase; import redis.clients.jedis.resps.Tuple; @ParameterizedClass @MethodSource("redis.clients.jedis.commands.CommandsTestsParameters#respVersions") public class SuggestionTest extends RedisModuleCommandsTestBase { private static final String key = "suggest"; @BeforeAll public static void prepare() { RedisModuleCommandsTestBase.prepare(); } public SuggestionTest(RedisProtocol protocol) { super(protocol); } @Test public void addSuggestionAndGetSuggestion() { String suggestion = "ANOTHER_WORD"; String noMatch = "_WORD MISSED"; assertTrue(client.ftSugAdd(key, suggestion, 1d) > 0, suggestion + " should of inserted at least 1"); assertTrue(client.ftSugAdd(key, noMatch, 1d) > 0, noMatch + " should of inserted at least 1"); // test that with a partial part of that string will have the entire word returned assertEquals(1, client.ftSugGet(key, suggestion.substring(0, 3), true, 5).size(), suggestion + " did not get a match with 3 characters"); // turn off fuzzy start at second word no hit assertEquals(0, client.ftSugGet(key, noMatch.substring(1, 6), false, 5).size(), noMatch + " no fuzzy and starting at 1, should not match"); // my attempt to trigger the fuzzy by 1 character assertEquals(1, client.ftSugGet(key, noMatch.substring(1, 6), true, 5).size(), noMatch + " fuzzy is on starting at 1 position should match"); } @Test public void addSuggestionIncrAndGetSuggestionFuzzy() { String suggestion = "TOPIC OF WORDS"; // test can add a suggestion string assertTrue(client.ftSugAddIncr(key, suggestion, 1d) > 0, suggestion + " insert should of returned at least 1"); // test that the partial part of that string will be returned using fuzzy assertEquals(suggestion, client.ftSugGet(key, suggestion.substring(0, 3)).get(0)); } @Test public void getSuggestionScores() { client.ftSugAdd(key, "COUNT_ME TOO", 1); client.ftSugAdd(key, "COUNT", 1); client.ftSugAdd(key, "COUNT_ANOTHER", 1); String noScoreOrPayload = "COUNT NO PAYLOAD OR COUNT"; assertTrue(client.ftSugAddIncr(key, noScoreOrPayload, 1) > 1, "Count single added should return more than 1"); List result = client.ftSugGetWithScores(key, "COU"); assertEquals(4, result.size()); result.forEach(tuple -> assertTrue(tuple.getScore() < .999, "Assert that a suggestion has a score not default 1 ")); } @Test public void getSuggestionMax() { client.ftSugAdd(key, "COUNT_ME TOO", 1); client.ftSugAdd(key, "COUNT", 1); client.ftSugAdd(key, "COUNTNO PAYLOAD OR COUNT", 1); // test that with a partial part of that string will have the entire word returned assertEquals( 3, client.ftSugGetWithScores(key, "COU", true, 10).size(),"3 suggestions"); assertEquals(2, client.ftSugGetWithScores(key, "COU", true, 2).size()); } @Test public void getSuggestionNoHit() { client.ftSugAdd(key, "NO WORD", 0.4); assertEquals(emptyList(), client.ftSugGetWithScores(key, "DIF")); assertEquals(emptyList(), client.ftSugGet(key, "DIF")); } @Test public void getSuggestionLengthAndDeleteSuggestion() { client.ftSugAddIncr(key, "TOPIC OF WORDS", 1); client.ftSugAddIncr(key, "ANOTHER ENTRY", 1); assertEquals(2L, client.ftSugLen(key)); assertTrue(client.ftSugDel(key, "ANOTHER ENTRY"), "Delete suggestion should succeed."); assertEquals(1L, client.ftSugLen(key)); assertFalse(client.ftSugDel(key, "ANOTHER ENTRY"), "Delete suggestion should succeed."); assertEquals(1L, client.ftSugLen(key)); assertFalse(client.ftSugDel(key, "ANOTHER ENTRY THAT IS NOT PRESENT"), "Delete suggestion should succeed."); assertEquals(1L, client.ftSugLen(key)); client.ftSugAdd(key, "LAST ENTRY", 1); assertEquals(2L, client.ftSugLen(key)); } } ================================================ FILE: src/test/java/redis/clients/jedis/modules/search/UtilTest.java ================================================ package redis.clients.jedis.modules.search; import static org.junit.jupiter.api.Assertions.assertArrayEquals; import static org.junit.jupiter.api.Assertions.assertEquals; import org.junit.jupiter.api.Test; import redis.clients.jedis.search.RediSearchUtil; import redis.clients.jedis.search.schemafields.NumericField; import redis.clients.jedis.search.schemafields.SchemaField; public class UtilTest { @Test public void floatArrayToByteArray() { float[] floats = new float[] { 0.2f }; byte[] bytes = RediSearchUtil.toByteArray(floats); byte[] expected = new byte[] { -51, -52, 76, 62 }; assertArrayEquals(expected, bytes); } @Test public void getSchemaFieldName() { SchemaField field = NumericField.of("$.num").as("num"); assertEquals("$.num", field.getFieldName().getName()); assertEquals("num", field.getFieldName().getAttribute()); assertEquals("$.num", field.getName()); } } ================================================ FILE: src/test/java/redis/clients/jedis/modules/timeseries/TimeSeriesTest.java ================================================ package redis.clients.jedis.modules.timeseries; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertNotEquals; import static org.junit.jupiter.api.Assertions.assertNotNull; import static org.junit.jupiter.api.Assertions.assertNull; import static org.junit.jupiter.api.Assertions.assertTrue; import static org.junit.jupiter.api.Assertions.fail; import static redis.clients.jedis.util.AssertUtil.assertEqualsByProtocol; import java.util.*; import io.redis.test.annotations.ConditionalOnEnv; import io.redis.test.annotations.SinceRedisVersion; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.Test; import org.junit.jupiter.params.ParameterizedClass; import org.junit.jupiter.params.provider.MethodSource; import redis.clients.jedis.RedisProtocol; import redis.clients.jedis.exceptions.JedisDataException; import redis.clients.jedis.modules.RedisModuleCommandsTestBase; import redis.clients.jedis.timeseries.*; import redis.clients.jedis.util.KeyValue; import redis.clients.jedis.util.TestEnvUtil; @ParameterizedClass @MethodSource("redis.clients.jedis.commands.CommandsTestsParameters#respVersions") public class TimeSeriesTest extends RedisModuleCommandsTestBase { @BeforeAll public static void prepare() { RedisModuleCommandsTestBase.prepare(); } public TimeSeriesTest(RedisProtocol protocol) { super(protocol); } @Test public void testCreate() { Map labels = new HashMap<>(); labels.put("l1", "v1"); labels.put("l2", "v2"); assertEquals("OK", client.tsCreate("series1", TSCreateParams.createParams().retention(10).labels(labels))); assertEquals("TSDB-TYPE", client.type("series1")); assertEquals("OK", client.tsCreate("series2", TSCreateParams.createParams().labels(labels))); assertEquals("TSDB-TYPE", client.type("series2")); assertEquals("OK", client.tsCreate("series3", TSCreateParams.createParams().retention(10))); assertEquals("TSDB-TYPE", client.type("series3")); assertEquals("OK", client.tsCreate("series4")); assertEquals("TSDB-TYPE", client.type("series4")); assertEquals("OK", client.tsCreate("series5", TSCreateParams.createParams().retention(0).uncompressed().labels(labels))); assertEquals("TSDB-TYPE", client.type("series5")); assertEquals("OK", client.tsCreate("series6", TSCreateParams.createParams().retention(7898) .uncompressed().duplicatePolicy(DuplicatePolicy.MAX).labels(labels))); assertEquals("TSDB-TYPE", client.type("series6")); try { assertEquals("OK", client.tsCreate("series1", TSCreateParams.createParams().retention(10).labels(labels))); fail(); } catch (JedisDataException e) { } try { assertEquals("OK", client.tsCreate("series1", TSCreateParams.createParams().labels(labels))); fail(); } catch (JedisDataException e) { } try { assertEquals("OK", client.tsCreate("series1", TSCreateParams.createParams().retention(10))); fail(); } catch (JedisDataException e) { } try { assertEquals("OK", client.tsCreate("series1")); fail(); } catch (JedisDataException e) { } try { assertEquals("OK", client.tsCreate("series1")); fail(); } catch (JedisDataException e) { } try { assertEquals("OK", client.tsCreate("series7", TSCreateParams.createParams().retention(7898) .uncompressed().chunkSize(-10).duplicatePolicy(DuplicatePolicy.MAX).labels(labels))); fail(); } catch (JedisDataException e) { } } @Test public void testAlter() { Map labels = new HashMap<>(); labels.put("l1", "v1"); labels.put("l2", "v2"); assertEquals("OK", client.tsCreate("seriesAlter", TSCreateParams.createParams().retention(60000).labels(labels))); assertEquals(Collections.emptyList(), client.tsQueryIndex("l2=v22")); labels.put("l1", "v11"); labels.remove("l2"); labels.put("l3", "v33"); assertEquals("OK", client.tsAlter("seriesAlter", TSAlterParams.alterParams().retention(15000).chunkSize(8192) .duplicatePolicy(DuplicatePolicy.SUM).labels(labels))); TSInfo info = client.tsInfo("seriesAlter"); assertEquals(Long.valueOf(15000), info.getProperty("retentionTime")); assertEquals(Long.valueOf(8192), info.getProperty("chunkSize")); assertEquals(DuplicatePolicy.SUM, info.getProperty("duplicatePolicy")); assertEquals("v11", info.getLabel("l1")); assertNull(info.getLabel("l2")); assertEquals("v33", info.getLabel("l3")); } @Test public void createAndAlterParams() { Map labels = new HashMap<>(); labels.put("l1", "v1"); labels.put("l2", "v2"); assertEquals("OK", client.tsCreate("ts-params", TSCreateParams.createParams().retention(60000).encoding(EncodingFormat.UNCOMPRESSED).chunkSize(4096) .duplicatePolicy(DuplicatePolicy.BLOCK).ignore(50, 12.5).labels(labels))); labels.put("l1", "v11"); labels.remove("l2"); labels.put("l3", "v33"); assertEquals("OK", client.tsAlter("ts-params", TSAlterParams.alterParams().retention(15000).chunkSize(8192) .duplicatePolicy(DuplicatePolicy.SUM).ignore(50, 12.5).labels(labels))); } @Test public void testRule() { assertEquals("OK", client.tsCreate("source")); assertEquals("OK", client.tsCreate("dest", TSCreateParams.createParams().retention(10))); assertEquals("OK", client.tsCreateRule("source", "dest", AggregationType.AVG, 100)); try { client.tsCreateRule("source", "dest", AggregationType.COUNT, 100); fail(); } catch (JedisDataException e) { // Error on creating same rule twice } assertEquals("OK", client.tsDeleteRule("source", "dest")); assertEquals("OK", client.tsCreateRule("source", "dest", AggregationType.COUNT, 100)); try { assertEquals("OK", client.tsDeleteRule("source", "dest1")); fail(); } catch (JedisDataException e) { // Error on creating same rule twice } } @Test public void addParams() { Map labels = new HashMap<>(); labels.put("l1", "v1"); labels.put("l2", "v2"); assertEquals(1000L, client.tsAdd("add1", 1000L, 1.1, TSAddParams.addParams().retention(10000).encoding(EncodingFormat.UNCOMPRESSED).chunkSize(1000) .duplicatePolicy(DuplicatePolicy.FIRST).onDuplicate(DuplicatePolicy.LAST).ignore(50, 12.5).labels(labels))); assertEquals(1000L, client.tsAdd("add2", 1000L, 1.1, TSAddParams.addParams().retention(10000).encoding(EncodingFormat.COMPRESSED).chunkSize(1000) .duplicatePolicy(DuplicatePolicy.MIN).onDuplicate(DuplicatePolicy.MAX).ignore(50, 12.5).labels(labels))); } @Test public void testAdd() { Map labels = new HashMap<>(); labels.put("l1", "v1"); labels.put("l2", "v2"); assertEquals("OK", client.tsCreate("seriesAdd", TSCreateParams.createParams().retention(10000).labels(labels))); assertEquals(0, client.tsRange("seriesAdd", TSRangeParams.rangeParams()).size()); assertEquals(1000L, client.tsAdd("seriesAdd", 1000L, 1.1, TSCreateParams.createParams().retention(10000).labels(null))); assertEquals(2000L, client.tsAdd("seriesAdd", 2000L, 0.9, TSCreateParams.createParams().labels(null))); assertEquals(3200L, client.tsAdd("seriesAdd", 3200L, 1.1, TSCreateParams.createParams().retention(10000))); assertEquals(4500L, client.tsAdd("seriesAdd", 4500L, -1.1)); TSElement[] rawValues = new TSElement[]{ new TSElement(1000L, 1.1), new TSElement(2000L, 0.9), new TSElement(3200L, 1.1), new TSElement(4500L, -1.1) }; List values = client.tsRange("seriesAdd", 800L, 3000L); assertEquals(2, values.size()); assertEquals(Arrays.asList(rawValues[0], rawValues[1]), values); values = client.tsRange("seriesAdd", 800L, 5000L); assertEquals(4, values.size()); assertEquals(Arrays.asList(rawValues), values); assertEquals(Arrays.asList(rawValues), client.tsRange("seriesAdd", TSRangeParams.rangeParams())); List expectedCountValues = Arrays.asList( new TSElement(2000L, 1), new TSElement(3200L, 1), new TSElement(4500L, 1)); values = client.tsRange("seriesAdd", TSRangeParams.rangeParams(1200L, 4600L).aggregation(AggregationType.COUNT, 1)); assertEquals(3, values.size()); assertEquals(expectedCountValues, values); List expectedAvgValues = Arrays.asList( new TSElement(0L, 1.1), new TSElement(2000L, 1), new TSElement(4000L, -1.1)); values = client.tsRange("seriesAdd", TSRangeParams.rangeParams(500L, 4600L).aggregation(AggregationType.AVG, 2000L)); assertEquals(3, values.size()); assertEquals(expectedAvgValues, values); // ensure zero-based index List valuesZeroBased = client.tsRange("seriesAdd", TSRangeParams.rangeParams(0L, 4600L).aggregation(AggregationType.AVG, 2000L)); assertEquals(3, valuesZeroBased.size()); assertEquals(values, valuesZeroBased); List expectedOverallSumValues = Arrays.asList(new TSElement(0L, 2.0)); values = client.tsRange("seriesAdd", TSRangeParams.rangeParams(0L, 5000L).aggregation(AggregationType.SUM, 5000L)); assertEquals(1, values.size()); assertEquals(expectedOverallSumValues, values); List expectedOverallMinValues = Arrays.asList(new TSElement(0L, -1.1)); values = client.tsRange("seriesAdd", TSRangeParams.rangeParams(0L, 5000L).aggregation(AggregationType.MIN, 5000L)); assertEquals(1, values.size()); assertEquals(expectedOverallMinValues, values); List expectedOverallMaxValues = Arrays.asList(new TSElement(0L, 1.1)); values = client.tsRange("seriesAdd", TSRangeParams.rangeParams(0L, 5000L).aggregation(AggregationType.MAX, 5000L)); assertEquals(1, values.size()); assertEquals(expectedOverallMaxValues, values); // MRANGE assertEquals(Collections.emptyMap(), client.tsMRange(TSMRangeParams.multiRangeParams().filter("l=v"))); try { client.tsMRange(TSMRangeParams.multiRangeParams(500L, 4600L).aggregation(AggregationType.COUNT, 1)); fail(); // } catch (JedisDataException e) { } catch (IllegalArgumentException e) { } try { client.tsMRange(TSMRangeParams.multiRangeParams(500L, 4600L).aggregation(AggregationType.COUNT, 1).filter((String) null)); fail(); // } catch (JedisDataException e) { } catch (IllegalArgumentException e) { } Map ranges = client.tsMRange(TSMRangeParams.multiRangeParams(500L, 4600L) .aggregation(AggregationType.COUNT, 1).filter("l1=v1")); assertEquals(1, ranges.size()); TSMRangeElements range = ranges.values().stream().findAny().get(); assertEquals("seriesAdd", range.getKey()); assertEquals(Collections.emptyMap(), range.getLabels()); List rangeValues = range.getValue(); assertEquals(4, rangeValues.size()); assertEquals(new TSElement(1000, 1), rangeValues.get(0)); assertNotEquals(new TSElement(1000, 1.1), rangeValues.get(0)); assertEquals(2000L, rangeValues.get(1).getTimestamp()); assertEquals("(2000:1.0)", rangeValues.get(1).toString()); // Add with labels Map labels2 = new HashMap<>(); labels2.put("l3", "v3"); labels2.put("l4", "v4"); assertEquals(1000L, client.tsAdd("seriesAdd2", 1000L, 1.1, TSCreateParams.createParams().retention(10000).labels(labels2))); Map ranges2 = client.tsMRange(TSMRangeParams.multiRangeParams(500L, 4600L) .aggregation(AggregationType.COUNT, 1).withLabels().filter("l4=v4")); assertEquals(1, ranges2.size()); TSMRangeElements elements2 = ranges2.values().stream().findAny().get(); assertEquals(labels2, elements2.getLabels()); assertEqualsByProtocol(protocol, null, Arrays.asList(AggregationType.COUNT), elements2.getAggregators()); Map labels3 = new HashMap<>(); labels3.put("l3", "v33"); labels3.put("l4", "v4"); assertEquals(1000L, client.tsAdd("seriesAdd3", 1000L, 1.1, TSCreateParams.createParams().labels(labels3))); assertEquals(2000L, client.tsAdd("seriesAdd3", 2000L, 1.1, TSCreateParams.createParams().labels(labels3))); assertEquals(3000L, client.tsAdd("seriesAdd3", 3000L, 1.1, TSCreateParams.createParams().labels(labels3))); Map ranges3 = client.tsMRange(TSMRangeParams.multiRangeParams(500L, 4600L) .aggregation(AggregationType.AVG, 1L).withLabels(true).count(2).filter("l4=v4")); assertEquals(2, ranges3.size()); ArrayList ranges3List = new ArrayList<>(ranges3.values()); assertEquals(1, ranges3List.get(0).getValue().size()); assertEquals(labels2, ranges3List.get(0).getLabels()); assertEqualsByProtocol(protocol, null, Arrays.asList(AggregationType.AVG), ranges3List.get(0).getAggregators()); assertEquals(2, ranges3List.get(1).getValue().size()); assertEquals(labels3, ranges3List.get(1).getLabels()); assertEqualsByProtocol(protocol, null, Arrays.asList(AggregationType.AVG), ranges3List.get(1).getAggregators()); assertEquals(800L, client.tsAdd("seriesAdd", 800L, 1.1)); assertEquals(700L, client.tsAdd("seriesAdd", 700L, 1.1, TSCreateParams.createParams().retention(10000))); assertEquals(600L, client.tsAdd("seriesAdd", 600L, 1.1, TSCreateParams.createParams().retention(10000).labels(null))); assertEquals(400L, client.tsAdd("seriesAdd4", 400L, 0.4, TSCreateParams.createParams() .retention(7898L).uncompressed().chunkSize(1000L).duplicatePolicy(DuplicatePolicy.SUM) .labels(labels))); assertEquals("TSDB-TYPE", client.type("seriesAdd4")); assertEquals(400L, client.tsAdd("seriesAdd4", 400L, 0.3, TSCreateParams.createParams() .retention(7898L).uncompressed().chunkSize(1000L).duplicatePolicy(DuplicatePolicy.SUM) .labels(labels))); assertEquals(Arrays.asList(new TSElement(400L, 0.7)), client.tsRange("seriesAdd4", 0L, Long.MAX_VALUE)); // Range on none existing key try { client.tsRange("seriesAdd1", TSRangeParams.rangeParams(500L, 4000L).aggregation(AggregationType.COUNT, 1)); fail(); } catch (JedisDataException e) { } } @Test public void issue75() { client.tsMRange(TSMRangeParams.multiRangeParams().filter("id=1")); } @Test public void del() { try { client.tsDel("ts-del", 0, 1); fail(); } catch (JedisDataException jde) { // expected } assertEquals("OK", client.tsCreate("ts-del", TSCreateParams.createParams().retention(10000L))); assertEquals(0, client.tsDel("ts-del", 0, 1)); assertEquals(1000L, client.tsAdd("ts-del", 1000L, 1.1, TSCreateParams.createParams().retention(10000))); assertEquals(2000L, client.tsAdd("ts-del", 2000L, 0.9)); assertEquals(3200L, client.tsAdd("ts-del", 3200L, 1.1, TSCreateParams.createParams().retention(10000))); assertEquals(4500L, client.tsAdd("ts-del", 4500L, -1.1)); assertEquals(4, client.tsRange("ts-del", 0, 5000).size()); assertEquals(2, client.tsDel("ts-del", 2000, 4000)); assertEquals(2, client.tsRange("ts-del", 0, 5000).size()); assertEquals(1, client.tsRange("ts-del", 0, 2500).size()); assertEquals(1, client.tsRange("ts-del", 2500, 5000).size()); } @Test public void testValue() { TSElement v = new TSElement(1234, 234.89634); assertEquals(1234, v.getTimestamp()); assertEquals(234.89634, v.getValue(), 0); assertEquals(v, new TSElement(1234, 234.89634)); assertNotEquals(v, new TSElement(1334, 234.89634)); assertNotEquals(v, new TSElement(1234, 234.8934)); assertNotEquals(1234, v.getValue()); assertEquals("(1234:234.89634)", v.toString()); // assertEquals(-1856758940, v.hashCode()); assertEquals(-1856719580, v.hashCode()); } @Test public void testAddStar() throws InterruptedException { Map labels = new HashMap<>(); labels.put("l11", "v11"); labels.put("l22", "v22"); assertEquals("OK", client.tsCreate("seriesAdd2", TSCreateParams.createParams().retention(10000L).labels(labels))); // Use 50ms for cases when Redis is not running locally int delayInMillis = 50; long startTime = System.currentTimeMillis(); Thread.sleep(delayInMillis); long add1 = client.tsAdd("seriesAdd2", 1.1); assertTrue(add1 > startTime); Thread.sleep(delayInMillis); long add2 = client.tsAdd("seriesAdd2", 3.2); assertTrue(add2 > add1); Thread.sleep(delayInMillis); long add3 = client.tsAdd("seriesAdd2", 3.2); assertTrue(add3 > add2); Thread.sleep(delayInMillis); long add4 = client.tsAdd("seriesAdd2", -1.2); assertTrue(add4 > add3); Thread.sleep(delayInMillis); long endTime = System.currentTimeMillis(); assertTrue(endTime > add4); List values = client.tsRange("seriesAdd2", startTime, add3); assertEquals(3, values.size()); } @Test @ConditionalOnEnv(value = TestEnvUtil.ENV_REDIS_ENTERPRISE, enabled = false) public void testMadd() { Map labels = new HashMap<>(); labels.put("l1", "v1"); labels.put("l2", "v2"); assertEquals("OK", client.tsCreate("seriesAdd1", TSCreateParams.createParams().retention(10000L).labels(labels))); assertEquals("OK", client.tsCreate("seriesAdd2", TSCreateParams.createParams().retention(10000L).labels(labels))); List result = client.tsMAdd( new KeyValue<>("seriesAdd1", new TSElement(1000L, 1.1)), new KeyValue<>("seriesAdd2", new TSElement(2000L, 3.2)), new KeyValue<>("seriesAdd1", new TSElement(1500L, 2.67)), new KeyValue<>("seriesAdd2", new TSElement(3200L, 54.2)), new KeyValue<>("seriesAdd2", new TSElement(4300L, 21.2))); assertEquals(1000L, result.get(0).longValue()); assertEquals(2000L, result.get(1).longValue()); assertEquals(1500L, result.get(2).longValue()); assertEquals(3200L, result.get(3).longValue()); assertEquals(4300L, result.get(4).longValue()); List values1 = client.tsRange("seriesAdd1", 0, Long.MAX_VALUE); assertEquals(2, values1.size()); assertEquals(1.1, values1.get(0).getValue(), 0.001); assertEquals(2.67, values1.get(1).getValue(), 0.001); List values2 = client.tsRange("seriesAdd2", TSRangeParams.rangeParams(0, Long.MAX_VALUE).count(2)); assertEquals(2, values2.size()); assertEquals(3.2, values2.get(0).getValue(), 0.001); assertEquals(54.2, values2.get(1).getValue(), 0.001); } @Test public void testIncrByDecrBy() throws InterruptedException { assertEquals("OK", client.tsCreate("seriesIncDec", TSCreateParams.createParams().retention(100 * 1000 /*100 sec*/))); assertEquals(1L, client.tsAdd("seriesIncDec", 1L, 1), 0); assertEquals(2L, client.tsIncrBy("seriesIncDec", 3, 2L), 0); assertEquals(3L, client.tsDecrBy("seriesIncDec", 2, 3L), 0); List values = client.tsRange("seriesIncDec", 1L, 3L); assertEquals(3, values.size()); assertEquals(2, values.get(2).getValue(), 0); assertEquals(3L, client.tsDecrBy("seriesIncDec", 2, 3L), 0); values = client.tsRange("seriesIncDec", 1L, Long.MAX_VALUE); assertEquals(3, values.size()); client.tsIncrBy("seriesIncDec", 100); client.tsDecrBy("seriesIncDec", 33); } @Test public void incrByDecrByParams() { Map labels = new HashMap<>(); labels.put("l1", "v1"); labels.put("l2", "v2"); assertEquals(1000L, client.tsIncrBy("incr1", 1.1, TSIncrByParams.incrByParams().timestamp(1000).retention(10000).encoding(EncodingFormat.UNCOMPRESSED) .chunkSize(1000).duplicatePolicy(DuplicatePolicy.FIRST).ignore(50, 12.5).labels(labels))); assertEquals(1000L, client.tsIncrBy("incr2", 1.1, TSIncrByParams.incrByParams().timestamp(1000).retention(10000).encoding(EncodingFormat.COMPRESSED) .chunkSize(1000).duplicatePolicy(DuplicatePolicy.MIN).ignore(50, 12.5).labels(labels))); assertEquals(1000L, client.tsDecrBy("decr1", 1.1, TSDecrByParams.decrByParams().timestamp(1000).retention(10000).encoding(EncodingFormat.COMPRESSED) .chunkSize(1000).duplicatePolicy(DuplicatePolicy.LAST).ignore(50, 12.5).labels(labels))); assertEquals(1000L, client.tsDecrBy("decr2", 1.1, TSDecrByParams.decrByParams().timestamp(1000).retention(10000).encoding(EncodingFormat.UNCOMPRESSED) .chunkSize(1000).duplicatePolicy(DuplicatePolicy.MAX).ignore(50, 12.5).labels(labels))); } @Test public void align() { client.tsAdd("align", 1, 10d); client.tsAdd("align", 3, 5d); client.tsAdd("align", 11, 10d); client.tsAdd("align", 25, 11d); List values = client.tsRange("align", TSRangeParams.rangeParams(1L, 30L).aggregation(AggregationType.COUNT, 10)); assertEquals(Arrays.asList(new TSElement(1, 2), new TSElement(11, 1), new TSElement(21, 1)), values); values = client.tsRange("align", TSRangeParams.rangeParams(1L, 30L).alignStart().aggregation(AggregationType.COUNT, 10)); assertEquals(Arrays.asList(new TSElement(1, 2), new TSElement(11, 1), new TSElement(21, 1)), values); values = client.tsRange("align", TSRangeParams.rangeParams(1L, 30L).alignEnd().aggregation(AggregationType.COUNT, 10)); assertEquals(Arrays.asList(new TSElement(1, 2), new TSElement(11, 1), new TSElement(21, 1)), values); values = client.tsRange("align", TSRangeParams.rangeParams(1L, 30L).align(5).aggregation(AggregationType.COUNT, 10)); assertEquals(Arrays.asList(new TSElement(1, 2), new TSElement(11, 1), new TSElement(21, 1)), values); } @Test public void rangeFilterBy() { TSElement[] rawValues = new TSElement[] { new TSElement(1000L, 1.0), new TSElement(2000L, 0.9), new TSElement(3200L, 1.1), new TSElement(4500L, -1.1) }; for (TSElement value : rawValues) { client.tsAdd("filterBy", value.getTimestamp(), value.getValue()); } // RANGE List values = client.tsRange("filterBy", 0L, 5000L); assertEquals(Arrays.asList(rawValues), values); values = client.tsRange("filterBy", TSRangeParams.rangeParams(0L, 5000L).filterByTS(1000L, 2000L)); assertEquals(Arrays.asList(rawValues[0], rawValues[1]), values); values = client.tsRange("filterBy", TSRangeParams.rangeParams(0L, 5000L).filterByValues(1.0, 1.2)); assertEquals(Arrays.asList(rawValues[0], rawValues[2]), values); values = client.tsRange("filterBy", TSRangeParams.rangeParams(0L, 5000L).filterByTS(1000L, 2000L).filterByValues(1.0, 1.2)); assertEquals(Arrays.asList(rawValues[0]), values); // REVRANGE values = client.tsRevRange("filterBy", 0L, 5000L); assertEquals(Arrays.asList(rawValues[3], rawValues[2], rawValues[1], rawValues[0]), values); values = client.tsRevRange("filterBy", TSRangeParams.rangeParams(0L, 5000L).filterByTS(1000L, 2000L)); assertEquals(Arrays.asList(rawValues[1], rawValues[0]), values); values = client.tsRevRange("filterBy", TSRangeParams.rangeParams(0L, 5000L).filterByValues(1.0, 1.2)); assertEquals(Arrays.asList(rawValues[2], rawValues[0]), values); values = client.tsRevRange("filterBy", TSRangeParams.rangeParams(0L, 5000L).filterByTS(1000L, 2000L).filterByValues(1.0, 1.2)); assertEquals(Arrays.asList(rawValues[0]), values); } @Test public void mrangeFilterBy() { Map labels = Collections.singletonMap("label", "multi"); client.tsCreate("ts1", TSCreateParams.createParams().labels(labels)); client.tsCreate("ts2", TSCreateParams.createParams().labels(labels)); String filter = "label=multi"; TSElement[] rawValues = new TSElement[]{ new TSElement(1000L, 1.0), new TSElement(2000L, 0.9), new TSElement(3200L, 1.1), new TSElement(4500L, -1.1) }; client.tsAdd("ts1", rawValues[0].getTimestamp(), rawValues[0].getValue()); client.tsAdd("ts2", rawValues[1].getTimestamp(), rawValues[1].getValue()); client.tsAdd("ts2", rawValues[2].getTimestamp(), rawValues[2].getValue()); client.tsAdd("ts1", rawValues[3].getTimestamp(), rawValues[3].getValue()); // MRANGE Map range = client.tsMRange(0L, 5000L, filter); ArrayList rangeList = new ArrayList<>(range.values()); assertEquals("ts1", rangeList.get(0).getKey()); assertEquals(Arrays.asList(rawValues[0], rawValues[3]), rangeList.get(0).getValue()); assertEquals("ts2", rangeList.get(1).getKey()); assertEquals(Arrays.asList(rawValues[1], rawValues[2]), rangeList.get(1).getValue()); range = client.tsMRange(TSMRangeParams.multiRangeParams(0L, 5000L).filterByTS(1000L, 2000L).filter(filter)); rangeList = new ArrayList<>(range.values()); assertEquals("ts1", rangeList.get(0).getKey()); assertEquals(Arrays.asList(rawValues[0]), rangeList.get(0).getValue()); assertEquals("ts2", rangeList.get(1).getKey()); assertEquals(Arrays.asList(rawValues[1]), rangeList.get(1).getValue()); range = client.tsMRange(TSMRangeParams.multiRangeParams(0L, 5000L).filterByValues(1.0, 1.2).filter(filter)); rangeList = new ArrayList<>(range.values()); assertEquals("ts1", rangeList.get(0).getKey()); assertEquals(Arrays.asList(rawValues[0]), rangeList.get(0).getValue()); assertEquals("ts2", rangeList.get(1).getKey()); assertEquals(Arrays.asList(rawValues[2]), rangeList.get(1).getValue()); range = client.tsMRange(TSMRangeParams.multiRangeParams(0L, 5000L) .filterByTS(1000L, 2000L).filterByValues(1.0, 1.2).filter(filter)); rangeList = new ArrayList<>(range.values()); assertEquals(Arrays.asList(rawValues[0]), rangeList.get(0).getValue()); // MREVRANGE range = client.tsMRevRange(0L, 5000L, filter); rangeList = new ArrayList<>(range.values()); assertEquals("ts1", rangeList.get(0).getKey()); assertEquals(Arrays.asList(rawValues[3], rawValues[0]), rangeList.get(0).getValue()); assertEquals("ts2", rangeList.get(1).getKey()); assertEquals(Arrays.asList(rawValues[2], rawValues[1]), rangeList.get(1).getValue()); range = client.tsMRevRange(TSMRangeParams.multiRangeParams(0L, 5000L).filterByTS(1000L, 2000L).filter(filter)); rangeList = new ArrayList<>(range.values()); assertEquals("ts1", rangeList.get(0).getKey()); assertEquals(Arrays.asList(rawValues[0]), rangeList.get(0).getValue()); assertEquals("ts2", rangeList.get(1).getKey()); assertEquals(Arrays.asList(rawValues[1]), rangeList.get(1).getValue()); range = client.tsMRevRange(TSMRangeParams.multiRangeParams(0L, 5000L).filterByValues(1.0, 1.2).filter(filter)); rangeList = new ArrayList<>(range.values()); assertEquals("ts1", rangeList.get(0).getKey()); assertEquals(Arrays.asList(rawValues[0]), rangeList.get(0).getValue()); assertEquals("ts2", rangeList.get(1).getKey()); assertEquals(Arrays.asList(rawValues[2]), rangeList.get(1).getValue()); range = client.tsMRevRange(TSMRangeParams.multiRangeParams(0L, 5000L) .filterByTS(1000L, 2000L).filterByValues(1.0, 1.2).filter(filter)); rangeList = new ArrayList<>(range.values()); assertEquals(Arrays.asList(rawValues[0]), rangeList.get(0).getValue()); } @Test public void groupByReduce() { client.tsCreate("ts1", TSCreateParams.createParams().labels(convertMap("metric", "cpu", "metric_name", "system"))); client.tsCreate("ts2", TSCreateParams.createParams().labels(convertMap("metric", "cpu", "metric_name", "user"))); client.tsAdd("ts1", 1L, 90.0); client.tsAdd("ts1", 2L, 45.0); client.tsAdd("ts2", 2L, 99.0); // List range = client.tsMRange(TSMRangeParams.multiGetParams(0L, 100L).withLabels() // .groupByReduce("metric_name", "max"), "metric=cpu"); Map range = client.tsMRange(TSMRangeParams.multiRangeParams(0L, 100L).withLabels() .filter("metric=cpu").groupBy("metric_name", "max")); assertEquals(2, range.size()); ArrayList rangeList = new ArrayList<>(range.values()); assertEquals("metric_name=system", rangeList.get(0).getKey()); assertEquals("system", rangeList.get(0).getLabels().get("metric_name")); if (protocol != RedisProtocol.RESP3) { assertEquals("max", rangeList.get(0).getLabels().get("__reducer__")); assertEquals("ts1", rangeList.get(0).getLabels().get("__source__")); } else { assertEquals(Arrays.asList("max"), rangeList.get(0).getReducers()); assertEquals(Arrays.asList("ts1"), rangeList.get(0).getSources()); } assertEquals(Arrays.asList(new TSElement(1, 90), new TSElement(2, 45)), rangeList.get(0).getValue()); assertEquals("metric_name=user", rangeList.get(1).getKey()); assertEquals("user", rangeList.get(1).getLabels().get("metric_name")); if (protocol != RedisProtocol.RESP3) { assertEquals("max", rangeList.get(1).getLabels().get("__reducer__")); assertEquals("ts2", rangeList.get(1).getLabels().get("__source__")); } else { assertEquals(Arrays.asList("max"), rangeList.get(1).getReducers()); assertEquals(Arrays.asList("ts2"), rangeList.get(1).getSources()); } assertEquals(Arrays.asList(new TSElement(2, 99)), rangeList.get(1).getValue()); } private Map convertMap(String... array) { Map map = new HashMap<>(array.length / 2); for (int i = 0; i < array.length; i += 2) { map.put(array[i], array[i + 1]); } return map; } @Test public void testGet() { // Test for empty result none existing series try { client.tsGet("seriesGet"); fail(); } catch (JedisDataException e) { } assertEquals("OK", client.tsCreate("seriesGet", TSCreateParams.createParams() .retention(100 * 1000 /*100sec retentionTime*/))); // Test for empty result assertNull(client.tsGet("seriesGet")); // Test returned last Value client.tsAdd("seriesGet", 2558, 8.7); assertEquals(new TSElement(2558, 8.7), client.tsGet("seriesGet")); client.tsAdd("seriesGet", 3458, 1.117); assertEquals(new TSElement(3458, 1.117), client.tsGet("seriesGet")); } @Test public void testMGet() { Map labels = new HashMap<>(); labels.put("l1", "v1"); labels.put("l2", "v2"); assertEquals("OK", client.tsCreate("seriesMGet1", TSCreateParams.createParams() .retention(100 * 1000 /*100sec retentionTime*/).labels(labels))); assertEquals("OK", client.tsCreate("seriesMGet2", TSCreateParams.createParams() .retention(100 * 1000 /*100sec retentionTime*/).labels(labels))); // Test for empty result Map ranges1 = client.tsMGet(TSMGetParams.multiGetParams().withLabels(false), "l1=v2"); assertEquals(0, ranges1.size()); // Test for empty ranges Map ranges2 = client.tsMGet(TSMGetParams.multiGetParams().withLabels(true), "l1=v1"); assertEquals(2, ranges2.size()); ArrayList ranges2List = new ArrayList<>(ranges2.values()); assertEquals(labels, ranges2List.get(0).getLabels()); assertEquals(labels, ranges2List.get(1).getLabels()); assertNull(ranges2List.get(0).getValue()); // Test for returned result on MGet client.tsAdd("seriesMGet1", 1500, 1.3); Map ranges3 = client.tsMGet(TSMGetParams.multiGetParams().withLabels(false), "l1=v1"); assertEquals(2, ranges3.size()); ArrayList ranges3List = new ArrayList<>(ranges3.values()); assertEquals(Collections.emptyMap(), ranges3List.get(0).getLabels()); assertEquals(Collections.emptyMap(), ranges3List.get(1).getLabels()); assertEquals(new TSElement(1500, 1.3), ranges3List.get(0).getValue()); assertNull(ranges3List.get(1).getValue()); } @Test public void testQueryIndex() { Map labels = new HashMap<>(); labels.put("l1", "v1"); labels.put("l2", "v2"); assertEquals("OK", client.tsCreate("seriesQueryIndex1", TSCreateParams.createParams() .retention(100 * 1000 /*100sec retentionTime*/).labels(labels))); labels.put("l2", "v22"); labels.put("l3", "v33"); assertEquals("OK", client.tsCreate("seriesQueryIndex2", TSCreateParams.createParams() .retention(100 * 1000 /*100sec retentionTime*/).labels(labels))); assertEquals(Arrays.asList(), client.tsQueryIndex("l1=v2")); assertEquals(Arrays.asList("seriesQueryIndex1", "seriesQueryIndex2"), client.tsQueryIndex("l1=v1")); assertEquals(Arrays.asList("seriesQueryIndex2"), client.tsQueryIndex("l2=v22")); } @Test public void testInfo() { Map labels = new HashMap<>(); labels.put("l1", "v1"); labels.put("l2", "v2"); assertEquals("OK", client.tsCreate("source", TSCreateParams.createParams().retention(10000L).labels(labels))); assertEquals("OK", client.tsCreate("dest", TSCreateParams.createParams().retention(20000L))); assertEquals("OK", client.tsCreateRule("source", "dest", AggregationType.AVG, 100)); TSInfo info = client.tsInfo("source"); assertEquals((Long) 10000L, info.getProperty("retentionTime")); assertEquals((Long) 4096L, info.getProperty("chunkSize")); assertEquals("v1", info.getLabel("l1")); assertEquals("v2", info.getLabel("l2")); assertNull(info.getLabel("l3")); assertEquals(1, info.getRules().size()); TSInfo.Rule rule = info.getRule("dest"); assertEquals("dest", rule.getCompactionKey()); assertEquals(100L, rule.getBucketDuration()); assertEquals(AggregationType.AVG, rule.getAggregator()); try { client.tsInfo("none"); fail(); } catch (JedisDataException e) { // Error on info on none existing series } } @Test public void testInfoDebug() { assertEquals("OK", client.tsCreate("source", TSCreateParams.createParams())); TSInfo info = client.tsInfoDebug("source"); assertEquals((Long) 0L, info.getProperty("retentionTime")); assertEquals(0, info.getLabels().size()); assertEquals(0, info.getRules().size()); List> chunks = info.getChunks(); assertEquals(1, chunks.size()); Map chunk = chunks.get(0); assertEquals(0L, chunk.get("samples")); // Don't care what the values are as long as the values are parsed according to types assertTrue(chunk.get("size") instanceof Long); assertTrue(chunk.get("startTimestamp") instanceof Long); assertTrue(chunk.get("endTimestamp") instanceof Long); assertTrue(chunk.get("bytesPerSample") instanceof Double); try { client.tsInfoDebug("none"); fail(); } catch (JedisDataException e) { // Error on info on none existing series } } @Test public void testRevRange() { Map labels = new HashMap<>(); labels.put("l1", "v1"); labels.put("l2", "v2"); assertEquals("OK", client.tsCreate("seriesAdd", TSCreateParams.createParams().retention(10000L).labels(labels))); assertEquals(Collections.emptyList(), client.tsRevRange("seriesAdd", TSRangeParams.rangeParams())); assertEquals(1000L, client.tsAdd("seriesRevRange", 1000L, 1.1, TSCreateParams.createParams().retention(10000))); assertEquals(2000L, client.tsAdd("seriesRevRange", 2000L, 0.9, TSCreateParams.createParams().labels(null))); assertEquals(3200L, client.tsAdd("seriesRevRange", 3200L, 1.1, TSCreateParams.createParams().retention(10000))); assertEquals(4500L, client.tsAdd("seriesRevRange", 4500L, -1.1)); TSElement[] rawValues = new TSElement[]{ new TSElement(4500L, -1.1), new TSElement(3200L, 1.1), new TSElement(2000L, 0.9), new TSElement(1000L, 1.1) }; List values = client.tsRevRange("seriesRevRange", 800L, 3000L); assertEquals(2, values.size()); assertEquals(Arrays.asList(Arrays.copyOfRange(rawValues, 2, 4)), values); values = client.tsRevRange("seriesRevRange", 800L, 5000L); assertEquals(4, values.size()); assertEquals(Arrays.asList(rawValues), values); assertEquals(Arrays.asList(rawValues), client.tsRevRange("seriesRevRange", TSRangeParams.rangeParams())); List expectedCountValues = Arrays.asList( new TSElement(4500L, 1), new TSElement(3200L, 1), new TSElement(2000L, 1)); values = client.tsRevRange("seriesRevRange", TSRangeParams.rangeParams(1200L, 4600L) .aggregation(AggregationType.COUNT, 1)); assertEquals(3, values.size()); assertEquals(expectedCountValues, values); List expectedAvgValues = Arrays.asList( new TSElement(4000L, -1.1), new TSElement(2000L, 1), new TSElement(0L, 1.1)); values = client.tsRevRange("seriesRevRange", TSRangeParams.rangeParams(500L, 4600L) .aggregation(AggregationType.AVG, 2000L)); assertEquals(3, values.size()); assertEquals(expectedAvgValues, values); } @Test public void testMRevRange() { assertEquals(Collections.emptyMap(), client.tsMRevRange(TSMRangeParams.multiRangeParams().filter("l=v"))); Map labels1 = new HashMap<>(); labels1.put("l3", "v3"); labels1.put("l4", "v4"); assertEquals(1000L, client.tsAdd("seriesMRevRange1", 1000L, 1.1, TSCreateParams.createParams().retention(10000).labels(labels1))); assertEquals(2222L, client.tsAdd("seriesMRevRange1", 2222L, 3.1, TSCreateParams.createParams().retention(10000).labels(labels1))); Map ranges1 = client.tsMRevRange(TSMRangeParams.multiRangeParams(500L, 4600L) .aggregation(AggregationType.COUNT, 1).withLabels().filter("l4=v4")); assertEquals(1, ranges1.size()); ArrayList ranges1List = new ArrayList<>(ranges1.values()); assertEquals(labels1, ranges1List.get(0).getLabels()); assertEquals(Arrays.asList(new TSElement(2222L, 1.0), new TSElement(1000L, 1.0)), ranges1List.get(0).getValue()); Map labels2 = new HashMap<>(); labels2.put("l3", "v3"); labels2.put("l4", "v44"); assertEquals(1000L, client.tsAdd("seriesMRevRange2", 1000L, 8.88, TSCreateParams.createParams().retention(10000).labels(labels2))); assertEquals(1111L, client.tsAdd("seriesMRevRange2", 1111L, 99.99, TSCreateParams.createParams().retention(10000).labels(labels2))); Map ranges2 = client.tsMRevRange(500L, 4600L, "l3=v3"); assertEquals(2, ranges2.size()); ArrayList ranges2List = new ArrayList<>(ranges2.values()); assertEquals(Collections.emptyMap(), ranges2List.get(0).getLabels()); assertEquals(Arrays.asList(new TSElement(2222L, 3.1), new TSElement(1000L, 1.1)), ranges2List.get(0).getValue()); assertEquals(Collections.emptyMap(), ranges2List.get(0).getLabels()); assertEquals(Arrays.asList(new TSElement(1111L, 99.99), new TSElement(1000L, 8.88)), ranges2List.get(1).getValue()); Map labels3 = new HashMap<>(); labels3.put("l3", "v33"); labels3.put("l4", "v4"); assertEquals(2200L, client.tsAdd("seriesMRevRange3", 2200L, -1.1, TSCreateParams.createParams().labels(labels3))); assertEquals(2400L, client.tsAdd("seriesMRevRange3", 2400L, 1.1, TSCreateParams.createParams().labels(labels3))); assertEquals(3300L, client.tsAdd("seriesMRevRange3", 3300L, -33, TSCreateParams.createParams().labels(labels3))); Map ranges3 = client.tsMRevRange(TSMRangeParams.multiRangeParams(500L, 4600L) .aggregation(AggregationType.AVG, 500).withLabels().count(5).filter("l4=v4")); assertEquals(2, ranges3.size()); ArrayList ranges3List = new ArrayList<>(ranges3.values()); assertEquals(labels1, ranges3List.get(0).getLabels()); assertEquals(Arrays.asList(new TSElement(2000L, 3.1), new TSElement(1000L, 1.1)), ranges3List.get(0).getValue()); assertEquals(labels3, ranges3List.get(1).getLabels()); assertEquals(Arrays.asList(new TSElement(3000L, -33.0), new TSElement(2000L, 0.0)), ranges3List.get(1).getValue()); } @Test public void latest() { client.tsCreate("ts1"); client.tsCreate("ts2"); client.tsCreateRule("ts1", "ts2", AggregationType.SUM, 10); client.tsAdd("ts1", 1, 1); client.tsAdd("ts1", 2, 3); client.tsAdd("ts1", 11, 7); client.tsAdd("ts1", 13, 1); List range = client.tsRange("ts1", 0, 20); assertEquals(4, range.size()); final TSElement compact = new TSElement(0, 4); final TSElement latest = new TSElement(10, 8); // get assertEquals(compact, client.tsGet("ts2", TSGetParams.getParams())); assertEquals(latest, client.tsGet("ts2", TSGetParams.getParams().latest())); // range assertEquals(Arrays.asList(compact), client.tsRange("ts2", TSRangeParams.rangeParams(0, 10))); assertEquals(Arrays.asList(compact, latest), client.tsRange("ts2", TSRangeParams.rangeParams(0, 10).latest())); // revrange assertEquals(Arrays.asList(compact), client.tsRevRange("ts2", TSRangeParams.rangeParams(0, 10))); assertEquals(Arrays.asList(latest, compact), client.tsRevRange("ts2", TSRangeParams.rangeParams(0, 10).latest())); } @Test public void latestMulti() { client.tsCreate("ts1"); client.tsCreate("ts2", TSCreateParams.createParams().label("compact", "true")); client.tsCreateRule("ts1", "ts2", AggregationType.SUM, 10); client.tsAdd("ts1", 1, 1); client.tsAdd("ts1", 2, 3); client.tsAdd("ts1", 11, 7); client.tsAdd("ts1", 13, 1); List range = client.tsRange("ts1", 0, 20); assertEquals(4, range.size()); final TSElement compact = new TSElement(0, 4); final TSElement latest = new TSElement(10, 8); // mget assertEquals(makeSingletonMap(new TSMGetElement("ts2", null, compact)), client.tsMGet(TSMGetParams.multiGetParams(), "compact=true")); assertEquals(makeSingletonMap(new TSMGetElement("ts2", null, latest)), client.tsMGet(TSMGetParams.multiGetParams().latest(), "compact=true")); // mrange assertEquals(makeSingletonMap(new TSMRangeElements("ts2", null, Arrays.asList(compact))), client.tsMRange(TSMRangeParams.multiRangeParams().filter("compact=true"))); assertEquals(makeSingletonMap(new TSMRangeElements("ts2", null, Arrays.asList(compact, latest))), client.tsMRange(TSMRangeParams.multiRangeParams().latest().filter("compact=true"))); // mrevrange assertEquals(makeSingletonMap(new TSMRangeElements("ts2", null, Arrays.asList(compact))), client.tsMRevRange(TSMRangeParams.multiRangeParams().filter("compact=true"))); assertEquals(makeSingletonMap(new TSMRangeElements("ts2", null, Arrays.asList(latest, compact))), client.tsMRevRange(TSMRangeParams.multiRangeParams().latest().filter("compact=true"))); } private Map makeSingletonMap(TSMGetElement value) { return Collections.singletonMap(value.getKey(), value); } private Map makeSingletonMap(TSMRangeElements value) { return Collections.singletonMap(value.getKey(), value); } @Test public void empty() { client.tsCreate("ts", TSCreateParams.createParams().label("l", "v")); client.tsAdd("ts", 1, 1); client.tsAdd("ts", 2, 3); client.tsAdd("ts", 11, 7); client.tsAdd("ts", 13, 1); // range List range = client.tsRange("ts", TSRangeParams.rangeParams().aggregation(AggregationType.MAX, 5)); assertEquals(2, range.size()); range = client.tsRange("ts", TSRangeParams.rangeParams().aggregation(AggregationType.MAX, 5).empty()); assertEquals(3, range.size()); assertNotNull(range.get(1).getValue()); // any parsable value // revrange range = client.tsRevRange("ts", TSRangeParams.rangeParams().aggregation(AggregationType.MIN, 5)); assertEquals(2, range.size()); range = client.tsRevRange("ts", TSRangeParams.rangeParams().aggregation(AggregationType.MIN, 5).empty()); assertEquals(3, range.size()); assertNotNull(range.get(1).getValue()); // any parsable value // mrange Map mrange = client.tsMRange(TSMRangeParams.multiRangeParams() .aggregation(AggregationType.MIN, 5).filter("l=v")); assertEquals(1, mrange.size()); ArrayList mrangeList = new ArrayList<>(mrange.values()); assertEquals(2, mrangeList.get(0).getValue().size()); mrange = client.tsMRange(TSMRangeParams.multiRangeParams() .aggregation(AggregationType.MIN, 5).empty().filter("l=v")); assertEquals(1, mrange.size()); mrangeList = new ArrayList<>(mrange.values()); assertEquals(3, mrangeList.get(0).getValue().size()); assertNotNull(mrangeList.get(0).getValue().get(1).getValue()); // any parsable value // mrevrange mrange = client.tsMRevRange(TSMRangeParams.multiRangeParams() .aggregation(AggregationType.MAX, 5).filter("l=v")); assertEquals(1, mrange.size()); mrangeList = new ArrayList<>(mrange.values()); assertEquals(2, mrangeList.get(0).getValue().size()); mrange = client.tsMRevRange(TSMRangeParams.multiRangeParams() .aggregation(AggregationType.MAX, 5).empty().filter("l=v")); assertEquals(1, mrange.size()); mrangeList = new ArrayList<>(mrange.values()); assertEquals(3, mrangeList.get(0).getValue().size()); assertNotNull(mrangeList.get(0).getValue().get(1).getValue()); // any parsable value } @Test public void bucketTimestamp() { client.tsCreate("ts", TSCreateParams.createParams().label("l", "v")); client.tsAdd("ts", 1, 1); client.tsAdd("ts", 2, 3); // range / revrange assertEquals(0, client.tsRange("ts", TSRangeParams.rangeParams() .aggregation(AggregationType.FIRST, 10).bucketTimestampLow()).get(0).getTimestamp()); assertEquals(10, client.tsRange("ts", TSRangeParams.rangeParams() .aggregation(AggregationType.LAST, 10).bucketTimestampHigh()).get(0).getTimestamp()); assertEquals(5, client.tsRange("ts", TSRangeParams.rangeParams() .aggregation(AggregationType.RANGE, 10).bucketTimestampMid()).get(0).getTimestamp()); assertEquals(5, client.tsRevRange("ts", TSRangeParams.rangeParams() .aggregation(AggregationType.TWA, 10).bucketTimestampMid()).get(0).getTimestamp()); assertEquals(5, client.tsRevRange("ts", TSRangeParams.rangeParams() .aggregation(AggregationType.TWA, 10).bucketTimestamp("mid")).get(0).getTimestamp()); // mrange / mrevrange assertEquals(0, client.tsMRange(TSMRangeParams.multiRangeParams() .aggregation(AggregationType.STD_P, 10).bucketTimestampLow().filter("l=v")) .values().stream().findAny().get().getValue().get(0).getTimestamp()); assertEquals(10, client.tsMRange(TSMRangeParams.multiRangeParams() .aggregation(AggregationType.STD_S, 10).bucketTimestampHigh().filter("l=v")) .values().stream().findAny().get().getValue().get(0).getTimestamp()); assertEquals(5, client.tsMRange(TSMRangeParams.multiRangeParams() .aggregation(AggregationType.TWA, 10).bucketTimestampMid().filter("l=v")) .values().stream().findAny().get().getValue().get(0).getTimestamp()); assertEquals(5, client.tsMRange(TSMRangeParams.multiRangeParams() .aggregation(AggregationType.VAR_P, 10).bucketTimestampMid().filter("l=v")) .values().stream().findAny().get().getValue().get(0).getTimestamp()); assertEquals(5, client.tsMRange(TSMRangeParams.multiRangeParams() .aggregation(AggregationType.VAR_S, 10).bucketTimestamp("~").filter("l=v")) .values().stream().findAny().get().getValue().get(0).getTimestamp()); } @Test public void alignTimestamp() { client.tsCreate("ts1"); client.tsCreate("ts2"); client.tsCreate("ts3"); client.tsCreateRule("ts1", "ts2", AggregationType.COUNT, 10, 0); client.tsCreateRule("ts1", "ts3", AggregationType.COUNT, 10, 1); client.tsAdd("ts1", 1, 1); client.tsAdd("ts1", 10, 3); client.tsAdd("ts1", 21, 7); assertEquals(2, client.tsRange("ts2", TSRangeParams.rangeParams().aggregation(AggregationType.COUNT, 10)).size()); assertEquals(1, client.tsRange("ts3", TSRangeParams.rangeParams().aggregation(AggregationType.COUNT, 10)).size()); } /** * Test for COUNTNAN and COUNTALL aggregation types introduced in RedisTimeSeries 8.6.0. * COUNTNAN counts the number of NaN values in a bucket. * COUNTALL counts all values in a bucket, including NaN values. */ @Test @SinceRedisVersion("8.5.0") public void countNanAndCountAll() { // Create a time series with some regular values client.tsCreate("ts-countnan", TSCreateParams.createParams().label("type", "test")); client.tsAdd("ts-countnan", 1, 1.0); client.tsAdd("ts-countnan", 2, 2.0); client.tsAdd("ts-countnan", 3, Double.NaN); client.tsAdd("ts-countnan", 4, 4.0); client.tsAdd("ts-countnan", 5, Double.NaN); client.tsAdd("ts-countnan", 11, 11.0); client.tsAdd("ts-countnan", 12, Double.NaN); // Test COUNTNAN aggregation - counts NaN values in each bucket List countNanValues = client.tsRange("ts-countnan", TSRangeParams.rangeParams(0L, 20L).aggregation(AggregationType.COUNTNAN, 10)); assertEquals(2, countNanValues.size()); // First bucket (0-10): 2 NaN values (at timestamps 3 and 5) assertEquals(0L, countNanValues.get(0).getTimestamp()); assertEquals(2.0, countNanValues.get(0).getValue(), 0.001); // Second bucket (10-20): 1 NaN value (at timestamp 12) assertEquals(10L, countNanValues.get(1).getTimestamp()); assertEquals(1.0, countNanValues.get(1).getValue(), 0.001); // Test COUNTALL aggregation - counts all values including NaN List countAllValues = client.tsRange("ts-countnan", TSRangeParams.rangeParams(0L, 20L).aggregation(AggregationType.COUNTALL, 10)); assertEquals(2, countAllValues.size()); // First bucket (0-10): 5 total values assertEquals(0L, countAllValues.get(0).getTimestamp()); assertEquals(5.0, countAllValues.get(0).getValue(), 0.001); // Second bucket (10-20): 2 total values assertEquals(10L, countAllValues.get(1).getTimestamp()); assertEquals(2.0, countAllValues.get(1).getValue(), 0.001); // Compare with regular COUNT which excludes NaN values List countValues = client.tsRange("ts-countnan", TSRangeParams.rangeParams(0L, 20L).aggregation(AggregationType.COUNT, 10)); assertEquals(2, countValues.size()); // First bucket (0-10): 3 non-NaN values assertEquals(0L, countValues.get(0).getTimestamp()); assertEquals(3.0, countValues.get(0).getValue(), 0.001); // Second bucket (10-20): 1 non-NaN value assertEquals(10L, countValues.get(1).getTimestamp()); assertEquals(1.0, countValues.get(1).getValue(), 0.001); // Test with MRANGE Map mrangeCountNan = client.tsMRange( TSMRangeParams.multiRangeParams(0L, 20L) .aggregation(AggregationType.COUNTNAN, 10) .filter("type=test")); assertEquals(1, mrangeCountNan.size()); TSMRangeElements elements = mrangeCountNan.get("ts-countnan"); assertNotNull(elements); assertEquals(2, elements.getValue().size()); assertEquals(2.0, elements.getValue().get(0).getValue(), 0.001); Map mrangeCountAll = client.tsMRange( TSMRangeParams.multiRangeParams(0L, 20L) .aggregation(AggregationType.COUNTALL, 10) .filter("type=test")); assertEquals(1, mrangeCountAll.size()); elements = mrangeCountAll.get("ts-countnan"); assertNotNull(elements); assertEquals(2, elements.getValue().size()); assertEquals(5.0, elements.getValue().get(0).getValue(), 0.001); // Test with REVRANGE List revRangeCountNan = client.tsRevRange("ts-countnan", TSRangeParams.rangeParams(0L, 20L).aggregation(AggregationType.COUNTNAN, 10)); assertEquals(2, revRangeCountNan.size()); // Results should be in reverse order assertEquals(10L, revRangeCountNan.get(0).getTimestamp()); assertEquals(1.0, revRangeCountNan.get(0).getValue(), 0.001); assertEquals(0L, revRangeCountNan.get(1).getTimestamp()); assertEquals(2.0, revRangeCountNan.get(1).getValue(), 0.001); List revRangeCountAll = client.tsRevRange("ts-countnan", TSRangeParams.rangeParams(0L, 20L).aggregation(AggregationType.COUNTALL, 10)); assertEquals(2, revRangeCountAll.size()); assertEquals(10L, revRangeCountAll.get(0).getTimestamp()); assertEquals(2.0, revRangeCountAll.get(0).getValue(), 0.001); assertEquals(0L, revRangeCountAll.get(1).getTimestamp()); assertEquals(5.0, revRangeCountAll.get(1).getValue(), 0.001); // Test with MREVRANGE Map mrevrangeCountNan = client.tsMRevRange( TSMRangeParams.multiRangeParams(0L, 20L) .aggregation(AggregationType.COUNTNAN, 10) .filter("type=test")); assertEquals(1, mrevrangeCountNan.size()); elements = mrevrangeCountNan.get("ts-countnan"); assertNotNull(elements); assertEquals(2, elements.getValue().size()); // Results in reverse order assertEquals(1.0, elements.getValue().get(0).getValue(), 0.001); assertEquals(2.0, elements.getValue().get(1).getValue(), 0.001); } /** * Test COUNTNAN and COUNTALL with bucket timestamp options. */ @Test @SinceRedisVersion("8.5.0") public void countNanAndCountAllWithBucketTimestamp() { client.tsCreate("ts-countnan-bucket", TSCreateParams.createParams().label("l", "v")); client.tsAdd("ts-countnan-bucket", 1, 1.0); client.tsAdd("ts-countnan-bucket", 2, Double.NaN); client.tsAdd("ts-countnan-bucket", 3, 3.0); // Test COUNTNAN with different bucket timestamp options assertEquals(0, client.tsRange("ts-countnan-bucket", TSRangeParams.rangeParams() .aggregation(AggregationType.COUNTNAN, 10).bucketTimestampLow()).get(0).getTimestamp()); assertEquals(10, client.tsRange("ts-countnan-bucket", TSRangeParams.rangeParams() .aggregation(AggregationType.COUNTNAN, 10).bucketTimestampHigh()).get(0).getTimestamp()); assertEquals(5, client.tsRange("ts-countnan-bucket", TSRangeParams.rangeParams() .aggregation(AggregationType.COUNTNAN, 10).bucketTimestampMid()).get(0).getTimestamp()); // Test COUNTALL with different bucket timestamp options assertEquals(0, client.tsRange("ts-countnan-bucket", TSRangeParams.rangeParams() .aggregation(AggregationType.COUNTALL, 10).bucketTimestampLow()).get(0).getTimestamp()); assertEquals(10, client.tsRange("ts-countnan-bucket", TSRangeParams.rangeParams() .aggregation(AggregationType.COUNTALL, 10).bucketTimestampHigh()).get(0).getTimestamp()); assertEquals(5, client.tsRange("ts-countnan-bucket", TSRangeParams.rangeParams() .aggregation(AggregationType.COUNTALL, 10).bucketTimestampMid()).get(0).getTimestamp()); // Test with MRANGE assertEquals(0, client.tsMRange(TSMRangeParams.multiRangeParams() .aggregation(AggregationType.COUNTNAN, 10).bucketTimestampLow().filter("l=v")) .values().stream().findAny().get().getValue().get(0).getTimestamp()); assertEquals(10, client.tsMRange(TSMRangeParams.multiRangeParams() .aggregation(AggregationType.COUNTALL, 10).bucketTimestampHigh().filter("l=v")) .values().stream().findAny().get().getValue().get(0).getTimestamp()); } /** * Test that AggregationType.safeValueOf correctly parses COUNTNAN and COUNTALL. */ @Test @SinceRedisVersion("8.5.0") public void aggregationTypeSafeValueOf() { assertEquals(AggregationType.COUNTNAN, AggregationType.safeValueOf("COUNTNAN")); assertEquals(AggregationType.COUNTNAN, AggregationType.safeValueOf("countnan")); assertEquals(AggregationType.COUNTALL, AggregationType.safeValueOf("COUNTALL")); assertEquals(AggregationType.COUNTALL, AggregationType.safeValueOf("countall")); // Verify existing types still work assertEquals(AggregationType.COUNT, AggregationType.safeValueOf("COUNT")); assertEquals(AggregationType.AVG, AggregationType.safeValueOf("avg")); assertEquals(AggregationType.STD_P, AggregationType.safeValueOf("STD.P")); assertEquals(AggregationType.VAR_S, AggregationType.safeValueOf("var.s")); } } ================================================ FILE: src/test/java/redis/clients/jedis/params/BitPosParamsTest.java ================================================ package redis.clients.jedis.params; import org.junit.jupiter.api.Test; import static org.junit.jupiter.api.Assertions.*; public class BitPosParamsTest { @Test public void checkEqualsIdenticalParams() { BitPosParams firstParam = getDefaultValue(); BitPosParams secondParam = getDefaultValue(); assertTrue(firstParam.equals(secondParam)); } @Test public void checkHashCodeIdenticalParams() { BitPosParams firstParam = getDefaultValue(); BitPosParams secondParam = getDefaultValue(); assertEquals(firstParam.hashCode(), secondParam.hashCode()); } @Test public void checkEqualsVariousParams() { BitPosParams firstParam = getDefaultValue(); BitPosParams secondParam = getDefaultValue(); secondParam.end(15); assertFalse(firstParam.equals(secondParam)); } @Test public void checkHashCodeVariousParams() { BitPosParams firstParam = getDefaultValue(); BitPosParams secondParam = getDefaultValue(); secondParam.start(15); assertNotEquals(firstParam.hashCode(), secondParam.hashCode()); } @Test public void checkEqualsWithNull() { BitPosParams firstParam = getDefaultValue(); BitPosParams secondParam = null; assertFalse(firstParam.equals(secondParam)); } private BitPosParams getDefaultValue() { return new BitPosParams(); } } ================================================ FILE: src/test/java/redis/clients/jedis/params/ClientKillParamsTest.java ================================================ package redis.clients.jedis.params; import org.junit.jupiter.api.Test; import redis.clients.jedis.args.ClientType; import static org.junit.jupiter.api.Assertions.*; public class ClientKillParamsTest { @Test public void checkEqualsIdenticalParams() { ClientKillParams firstParam = getDefaultValue(); ClientKillParams secondParam = getDefaultValue(); assertTrue(firstParam.equals(secondParam)); } @Test public void checkHashCodeIdenticalParams() { ClientKillParams firstParam = getDefaultValue(); ClientKillParams secondParam = getDefaultValue(); assertEquals(firstParam.hashCode(), secondParam.hashCode()); } @Test public void checkEqualsVariousParams() { ClientKillParams firstParam = getDefaultValue(); firstParam.type(ClientType.NORMAL); ClientKillParams secondParam = getDefaultValue(); secondParam.skipMe(ClientKillParams.SkipMe.NO); assertFalse(firstParam.equals(secondParam)); } @Test public void checkHashCodeVariousParams() { ClientKillParams firstParam = getDefaultValue(); firstParam.type(ClientType.NORMAL); ClientKillParams secondParam = getDefaultValue(); secondParam.skipMe(ClientKillParams.SkipMe.NO); assertNotEquals(firstParam.hashCode(), secondParam.hashCode()); } @Test public void checkEqualsWithNull() { ClientKillParams firstParam = getDefaultValue(); ClientKillParams secondParam = null; assertFalse(firstParam.equals(secondParam)); } private ClientKillParams getDefaultValue() { return new ClientKillParams(); } } ================================================ FILE: src/test/java/redis/clients/jedis/params/CommandListFilterByParamsTest.java ================================================ package redis.clients.jedis.params; import org.junit.jupiter.api.Test; import static org.junit.jupiter.api.Assertions.*; public class CommandListFilterByParamsTest { @Test public void checkEqualsIdenticalParams() { CommandListFilterByParams firstParam = getDefaultValue(); CommandListFilterByParams secondParam = getDefaultValue(); assertTrue(firstParam.equals(secondParam)); } @Test public void checkHashCodeIdenticalParams() { CommandListFilterByParams firstParam = getDefaultValue(); CommandListFilterByParams secondParam = getDefaultValue(); assertEquals(firstParam.hashCode(), secondParam.hashCode()); } @Test public void checkEqualsVariousParams() { CommandListFilterByParams firstParam = getDefaultValue(); firstParam.filterByAclCat("admin"); CommandListFilterByParams secondParam = getDefaultValue(); secondParam.filterByModule("JSON"); assertFalse(firstParam.equals(secondParam)); } @Test public void checkHashCodeVariousParams() { CommandListFilterByParams firstParam = getDefaultValue(); firstParam.filterByAclCat("admin"); CommandListFilterByParams secondParam = getDefaultValue(); secondParam.filterByModule("JSON"); assertNotEquals(firstParam.hashCode(), secondParam.hashCode()); } @Test public void checkEqualsWithNull() { CommandListFilterByParams firstParam = getDefaultValue(); CommandListFilterByParams secondParam = null; assertFalse(firstParam.equals(secondParam)); } private CommandListFilterByParams getDefaultValue() { return new CommandListFilterByParams(); } } ================================================ FILE: src/test/java/redis/clients/jedis/params/FailoverParamsTest.java ================================================ package redis.clients.jedis.params; import org.junit.jupiter.api.Test; import static org.junit.jupiter.api.Assertions.*; public class FailoverParamsTest { @Test public void checkEqualsIdenticalParams() { FailoverParams firstParam = getDefaultValue(); FailoverParams secondParam = getDefaultValue(); assertTrue(firstParam.equals(secondParam)); } @Test public void checkHashCodeIdenticalParams() { FailoverParams firstParam = getDefaultValue(); FailoverParams secondParam = getDefaultValue(); assertEquals(firstParam.hashCode(), secondParam.hashCode()); } @Test public void checkEqualsVariousParams() { FailoverParams firstParam = getDefaultValue(); firstParam.timeout(15); FailoverParams secondParam = getDefaultValue(); secondParam.timeout(20); assertFalse(firstParam.equals(secondParam)); } @Test public void checkHashCodeVariousParams() { FailoverParams firstParam = getDefaultValue(); firstParam.timeout(15); FailoverParams secondParam = getDefaultValue(); secondParam.timeout(20); assertNotEquals(firstParam.hashCode(), secondParam.hashCode()); } @Test public void checkEqualsWithNull() { FailoverParams firstParam = getDefaultValue(); FailoverParams secondParam = null; assertFalse(firstParam.equals(secondParam)); } private FailoverParams getDefaultValue() { return new FailoverParams(); } } ================================================ FILE: src/test/java/redis/clients/jedis/params/GeoAddParamsTest.java ================================================ package redis.clients.jedis.params; import org.junit.jupiter.api.Test; import static org.junit.jupiter.api.Assertions.*; public class GeoAddParamsTest { @Test public void checkEqualsIdenticalParams() { GeoAddParams firstParam = getDefaultValue(); GeoAddParams secondParam = getDefaultValue(); assertTrue(firstParam.equals(secondParam)); } @Test public void checkHashCodeIdenticalParams() { GeoAddParams firstParam = getDefaultValue(); GeoAddParams secondParam = getDefaultValue(); assertEquals(firstParam.hashCode(), secondParam.hashCode()); } @Test public void checkEqualsVariousParams() { GeoAddParams firstParam = getDefaultValue(); firstParam.nx(); GeoAddParams secondParam = getDefaultValue(); secondParam.xx(); assertFalse(firstParam.equals(secondParam)); } @Test public void checkHashCodeVariousParams() { GeoAddParams firstParam = getDefaultValue(); firstParam.nx(); GeoAddParams secondParam = getDefaultValue(); secondParam.xx(); assertNotEquals(firstParam.hashCode(), secondParam.hashCode()); } @Test public void checkEqualsWithNull() { GeoAddParams firstParam = getDefaultValue(); GeoAddParams secondParam = null; assertFalse(firstParam.equals(secondParam)); } private GeoAddParams getDefaultValue() { return new GeoAddParams(); } } ================================================ FILE: src/test/java/redis/clients/jedis/params/GetExParamsTest.java ================================================ package redis.clients.jedis.params; import org.junit.jupiter.api.Test; import static org.junit.jupiter.api.Assertions.*; public class GetExParamsTest { @Test public void checkEqualsIdenticalParams() { GetExParams firstParam = getDefaultValue(); GetExParams secondParam = getDefaultValue(); assertTrue(firstParam.equals(secondParam)); } @Test public void checkHashCodeIdenticalParams() { GetExParams firstParam = getDefaultValue(); GetExParams secondParam = getDefaultValue(); assertEquals(firstParam.hashCode(), secondParam.hashCode()); } @Test public void checkEqualsVariousParams() { GetExParams firstParam = getDefaultValue(); firstParam.ex(15); GetExParams secondParam = getDefaultValue(); secondParam.px(20); assertFalse(firstParam.equals(secondParam)); } @Test public void checkHashCodeVariousParams() { GetExParams firstParam = getDefaultValue(); firstParam.ex(15); GetExParams secondParam = getDefaultValue(); secondParam.px(20); assertNotEquals(firstParam.hashCode(), secondParam.hashCode()); } @Test public void checkEqualsWithNull() { GetExParams firstParam = getDefaultValue(); GetExParams secondParam = null; assertFalse(firstParam.equals(secondParam)); } private GetExParams getDefaultValue() { return new GetExParams(); } } ================================================ FILE: src/test/java/redis/clients/jedis/params/HotkeysParamsTest.java ================================================ package redis.clients.jedis.params; import org.junit.jupiter.api.Nested; import org.junit.jupiter.api.Test; import redis.clients.jedis.CommandArguments; import redis.clients.jedis.Protocol; import redis.clients.jedis.args.HotkeysMetric; import static org.junit.jupiter.api.Assertions.*; public class HotkeysParamsTest { private HotkeysParams getDefaultValue() { return new HotkeysParams(); } @Nested class EqualityAndHashCodeTests { @Test public void equalsWithIdenticalParams() { HotkeysParams firstParam = getDefaultValue(); HotkeysParams secondParam = getDefaultValue(); assertEquals(firstParam, secondParam); } @Test public void hashCodeWithIdenticalParams() { HotkeysParams firstParam = getDefaultValue(); HotkeysParams secondParam = getDefaultValue(); assertEquals(firstParam.hashCode(), secondParam.hashCode()); } @Test public void equalsWithDifferentParams() { HotkeysParams firstParam = getDefaultValue(); firstParam.metrics(HotkeysMetric.CPU).count(10); HotkeysParams secondParam = getDefaultValue(); secondParam.metrics(HotkeysMetric.NET).count(20); assertNotEquals(firstParam, secondParam); } @Test public void hashCodeWithDifferentParams() { HotkeysParams firstParam = getDefaultValue(); firstParam.metrics(HotkeysMetric.CPU).count(10); HotkeysParams secondParam = getDefaultValue(); secondParam.metrics(HotkeysMetric.NET).count(20); assertNotEquals(firstParam.hashCode(), secondParam.hashCode()); } @Test public void equalsWithNull() { HotkeysParams firstParam = getDefaultValue(); HotkeysParams secondParam = null; assertFalse(firstParam.equals(secondParam)); } @Test public void equalsWithSameInstance() { HotkeysParams param = getDefaultValue(); assertTrue(param.equals(param)); } @Test public void equalsWithDifferentMetrics() { HotkeysParams firstParam = getDefaultValue(); firstParam.metrics(HotkeysMetric.CPU); HotkeysParams secondParam = getDefaultValue(); secondParam.metrics(HotkeysMetric.NET); assertNotEquals(firstParam, secondParam); } @Test public void equalsWithDifferentSlots() { HotkeysParams firstParam = getDefaultValue(); firstParam.metrics(HotkeysMetric.CPU).slots(1, 2, 3); HotkeysParams secondParam = getDefaultValue(); secondParam.metrics(HotkeysMetric.CPU).slots(4, 5, 6); assertNotEquals(firstParam, secondParam); } } @Nested class ValidationTests { @Test public void metricsNullThrowsException() { HotkeysParams params = getDefaultValue(); IllegalArgumentException exception = assertThrows(IllegalArgumentException.class, () -> { params.metrics(null); }); assertEquals("metrics must not be null", exception.getMessage()); } @Test public void metricsEmptyArrayThrowsException() { HotkeysParams params = getDefaultValue(); IllegalArgumentException exception = assertThrows(IllegalArgumentException.class, () -> { params.metrics(new HotkeysMetric[0]); }); assertEquals("at least one metric is required", exception.getMessage()); } @Test public void countTooLowThrowsException() { HotkeysParams params = getDefaultValue(); IllegalArgumentException exception = assertThrows(IllegalArgumentException.class, () -> { params.count(9); }); assertEquals("count must be between 1 and 64", exception.getMessage()); } @Test public void countTooHighThrowsException() { HotkeysParams params = getDefaultValue(); IllegalArgumentException exception = assertThrows(IllegalArgumentException.class, () -> { params.count(65); }); assertEquals("count must be between 1 and 64", exception.getMessage()); } @Test public void countBoundaryMinValid() { HotkeysParams params = getDefaultValue(); assertDoesNotThrow(() -> params.count(10)); } @Test public void countBoundaryMaxValid() { HotkeysParams params = getDefaultValue(); assertDoesNotThrow(() -> params.count(64)); } @Test public void durationNegativeThrowsException() { HotkeysParams params = getDefaultValue(); IllegalArgumentException exception = assertThrows(IllegalArgumentException.class, () -> { params.duration(-1); }); assertEquals("duration must be >= 0", exception.getMessage()); } @Test public void durationZeroValid() { HotkeysParams params = getDefaultValue(); assertDoesNotThrow(() -> params.duration(0)); } @Test public void sampleTooLowThrowsException() { HotkeysParams params = getDefaultValue(); IllegalArgumentException exception = assertThrows(IllegalArgumentException.class, () -> { params.sample(0); }); assertEquals("sample must be >= 1", exception.getMessage()); } @Test public void sampleOneValid() { HotkeysParams params = getDefaultValue(); assertDoesNotThrow(() -> params.sample(1)); } @Test public void slotTooLowThrowsException() { HotkeysParams params = getDefaultValue(); IllegalArgumentException exception = assertThrows(IllegalArgumentException.class, () -> { params.slots(-1); }); assertEquals("each slot must be between 0 and 16383", exception.getMessage()); } @Test public void slotTooHighThrowsException() { HotkeysParams params = getDefaultValue(); IllegalArgumentException exception = assertThrows(IllegalArgumentException.class, () -> { params.slots(16384); }); assertEquals("each slot must be between 0 and 16383", exception.getMessage()); } @Test public void slotBoundaryMinValid() { HotkeysParams params = getDefaultValue(); assertDoesNotThrow(() -> params.slots(0)); } @Test public void slotBoundaryMaxValid() { HotkeysParams params = getDefaultValue(); assertDoesNotThrow(() -> params.slots(16383)); } @Test public void slotsMultipleWithInvalidThrowsException() { HotkeysParams params = getDefaultValue(); assertThrows(IllegalArgumentException.class, () -> { params.slots(0, 100, 16384); }); } @Test public void slotsNullValid() { HotkeysParams params = getDefaultValue(); assertDoesNotThrow(() -> params.slots((int[]) null)); } } @Nested class BuilderTests { @Test public void staticFactoryMethodReturnsInstance() { HotkeysParams params = HotkeysParams.hotkeysParams(); assertNotNull(params); assertInstanceOf(HotkeysParams.class, params); } @Test public void methodChainingWorks() { HotkeysParams params = HotkeysParams.hotkeysParams() .metrics(HotkeysMetric.CPU, HotkeysMetric.NET).count(20).duration(60).sample(5) .slots(0, 100, 200); assertNotNull(params); } @Test public void builderPatternProducesCorrectState() { HotkeysParams firstParam = HotkeysParams.hotkeysParams() .metrics(HotkeysMetric.CPU, HotkeysMetric.NET).count(20).duration(60).sample(5) .slots(0, 100); HotkeysParams secondParam = HotkeysParams.hotkeysParams() .metrics(HotkeysMetric.CPU, HotkeysMetric.NET).count(20).duration(60).sample(5) .slots(0, 100); assertEquals(firstParam, secondParam); assertEquals(firstParam.hashCode(), secondParam.hashCode()); } } @Nested class EdgeCaseBehaviorTests { @Test public void slotsEmptyArrayNotAdded() { HotkeysParams params = getDefaultValue(); assertDoesNotThrow(() -> params.metrics(HotkeysMetric.CPU).slots(new int[0])); } @Test public void lastBuilderCallWins() { HotkeysParams firstParam = getDefaultValue(); firstParam.metrics(HotkeysMetric.CPU).count(10).count(20); HotkeysParams secondParam = getDefaultValue(); secondParam.metrics(HotkeysMetric.CPU).count(20); assertEquals(firstParam, secondParam); } @Test public void equalsWithSameMetricsDifferentOrder() { HotkeysParams firstParam = getDefaultValue(); firstParam.metrics(HotkeysMetric.CPU, HotkeysMetric.NET); HotkeysParams secondParam = getDefaultValue(); secondParam.metrics(HotkeysMetric.NET, HotkeysMetric.CPU); assertNotEquals(firstParam, secondParam); } } @Nested class AddParamsTests { @Test public void addParamsWithMinimalConfiguration() { HotkeysParams params = getDefaultValue(); params.metrics(HotkeysMetric.CPU); CommandArguments args = new CommandArguments(Protocol.Command.HOTKEYS); params.addParams(args); // Expected: HOTKEYS METRICS 1 CPU assertEquals(4, args.size()); assertEquals(Protocol.Keyword.METRICS, args.get(1)); assertEquals(HotkeysMetric.CPU, args.get(3)); } @Test public void addParamsWithAllOptions() { HotkeysParams params = getDefaultValue(); params.metrics(HotkeysMetric.CPU, HotkeysMetric.NET).count(20).duration(60).sample(5) .slots(100, 200, 300); CommandArguments args = new CommandArguments(Protocol.Command.HOTKEYS); params.addParams(args); // Expected: HOTKEYS METRICS 2 CPU NET COUNT 20 DURATION 60 SAMPLE 5 SLOTS 3 100 200 300 assertEquals(16, args.size()); assertEquals(Protocol.Keyword.METRICS, args.get(1)); assertEquals(HotkeysMetric.CPU, args.get(3)); assertEquals(HotkeysMetric.NET, args.get(4)); assertEquals(Protocol.Keyword.COUNT, args.get(5)); assertEquals(Protocol.Keyword.DURATION, args.get(7)); assertEquals(Protocol.Keyword.SAMPLE, args.get(9)); assertEquals(Protocol.Keyword.SLOTS, args.get(11)); } @Test public void addParamsWithEmptySlotsNotAdded() { HotkeysParams params = getDefaultValue(); params.metrics(HotkeysMetric.CPU).slots(new int[0]); CommandArguments args = new CommandArguments(Protocol.Command.HOTKEYS); params.addParams(args); // Expected: HOTKEYS METRICS 1 CPU (no SLOTS) assertEquals(4, args.size()); assertEquals(Protocol.Keyword.METRICS, args.get(1)); assertEquals(HotkeysMetric.CPU, args.get(3)); } @Test public void addParamsWithMultipleMetrics() { HotkeysParams params = getDefaultValue(); params.metrics(HotkeysMetric.CPU, HotkeysMetric.NET); CommandArguments args = new CommandArguments(Protocol.Command.HOTKEYS); params.addParams(args); // Expected: HOTKEYS METRICS 2 CPU NET assertEquals(5, args.size()); assertEquals(Protocol.Keyword.METRICS, args.get(1)); assertEquals(HotkeysMetric.CPU, args.get(3)); assertEquals(HotkeysMetric.NET, args.get(4)); } @Test public void addParamsWithOnlyCount() { HotkeysParams params = getDefaultValue(); params.metrics(HotkeysMetric.CPU).count(30); CommandArguments args = new CommandArguments(Protocol.Command.HOTKEYS); params.addParams(args); // Expected: HOTKEYS METRICS 1 CPU COUNT 30 assertEquals(6, args.size()); assertEquals(Protocol.Keyword.METRICS, args.get(1)); assertEquals(HotkeysMetric.CPU, args.get(3)); assertEquals(Protocol.Keyword.COUNT, args.get(4)); } @Test public void addParamsWithOnlyDuration() { HotkeysParams params = getDefaultValue(); params.metrics(HotkeysMetric.NET).duration(120); CommandArguments args = new CommandArguments(Protocol.Command.HOTKEYS); params.addParams(args); // Expected: HOTKEYS METRICS 1 NET DURATION 120 assertEquals(6, args.size()); assertEquals(Protocol.Keyword.METRICS, args.get(1)); assertEquals(HotkeysMetric.NET, args.get(3)); assertEquals(Protocol.Keyword.DURATION, args.get(4)); } @Test public void addParamsWithOnlySample() { HotkeysParams params = getDefaultValue(); params.metrics(HotkeysMetric.CPU).sample(10); CommandArguments args = new CommandArguments(Protocol.Command.HOTKEYS); params.addParams(args); // Expected: HOTKEYS METRICS 1 CPU SAMPLE 10 assertEquals(6, args.size()); assertEquals(Protocol.Keyword.METRICS, args.get(1)); assertEquals(HotkeysMetric.CPU, args.get(3)); assertEquals(Protocol.Keyword.SAMPLE, args.get(4)); } @Test public void addParamsWithoutMetricsThrowsException() { HotkeysParams params = getDefaultValue(); // Don't set metrics CommandArguments args = new CommandArguments(Protocol.Command.HOTKEYS); IllegalArgumentException exception = assertThrows(IllegalArgumentException.class, () -> params.addParams(args)); assertEquals("metrics must not be null", exception.getMessage()); } } } ================================================ FILE: src/test/java/redis/clients/jedis/params/LCSParamsTest.java ================================================ package redis.clients.jedis.params; import org.junit.jupiter.api.Test; import static org.junit.jupiter.api.Assertions.*; public class LCSParamsTest { @Test public void checkEqualsIdenticalParams() { LCSParams firstParam = getDefaultValue(); LCSParams secondParam = getDefaultValue(); assertTrue(firstParam.equals(secondParam)); } @Test public void checkHashCodeIdenticalParams() { LCSParams firstParam = getDefaultValue(); LCSParams secondParam = getDefaultValue(); assertEquals(firstParam.hashCode(), secondParam.hashCode()); } @Test public void checkEqualsVariousParams() { LCSParams firstParam = getDefaultValue(); firstParam.idx(); LCSParams secondParam = getDefaultValue(); secondParam.len(); assertFalse(firstParam.equals(secondParam)); } @Test public void checkHashCodeVariousParams() { LCSParams firstParam = getDefaultValue(); firstParam.idx(); LCSParams secondParam = getDefaultValue(); secondParam.len(); assertNotEquals(firstParam.hashCode(), secondParam.hashCode()); } @Test public void checkEqualsWithNull() { LCSParams firstParam = getDefaultValue(); LCSParams secondParam = null; assertFalse(firstParam.equals(secondParam)); } private LCSParams getDefaultValue() { return new LCSParams(); } } ================================================ FILE: src/test/java/redis/clients/jedis/params/LPosParamsTest.java ================================================ package redis.clients.jedis.params; import org.junit.jupiter.api.Test; import static org.junit.jupiter.api.Assertions.*; public class LPosParamsTest { @Test public void checkEqualsIdenticalParams() { LPosParams firstParam = getDefaultValue(); LPosParams secondParam = getDefaultValue(); assertTrue(firstParam.equals(secondParam)); } @Test public void checkHashCodeIdenticalParams() { LPosParams firstParam = getDefaultValue(); LPosParams secondParam = getDefaultValue(); assertEquals(firstParam.hashCode(), secondParam.hashCode()); } @Test public void checkEqualsVariousParams() { LPosParams firstParam = getDefaultValue(); firstParam.rank(1); LPosParams secondParam = getDefaultValue(); secondParam.rank(2); assertFalse(firstParam.equals(secondParam)); } @Test public void checkHashCodeVariousParams() { LPosParams firstParam = getDefaultValue(); firstParam.rank(1); LPosParams secondParam = getDefaultValue(); secondParam.rank(2); assertNotEquals(firstParam.hashCode(), secondParam.hashCode()); } @Test public void checkEqualsWithNull() { LPosParams firstParam = getDefaultValue(); LPosParams secondParam = null; assertFalse(firstParam.equals(secondParam)); } private LPosParams getDefaultValue() { return new LPosParams(); } } ================================================ FILE: src/test/java/redis/clients/jedis/params/LolwutParamsTest.java ================================================ package redis.clients.jedis.params; import org.junit.jupiter.api.Test; import static org.junit.jupiter.api.Assertions.*; public class LolwutParamsTest { @Test public void checkEqualsIdenticalParams() { LolwutParams firstParam = getDefaultValue(); LolwutParams secondParam = getDefaultValue(); assertTrue(firstParam.equals(secondParam)); } @Test public void checkHashCodeIdenticalParams() { LolwutParams firstParam = getDefaultValue(); LolwutParams secondParam = getDefaultValue(); assertEquals(firstParam.hashCode(), secondParam.hashCode()); } @Test public void checkEqualsVariousParams() { LolwutParams firstParam = getDefaultValue(); firstParam.version(1); LolwutParams secondParam = getDefaultValue(); secondParam.version(2); assertFalse(firstParam.equals(secondParam)); } @Test public void checkHashCodeVariousParams() { LolwutParams firstParam = getDefaultValue(); firstParam.version(1); LolwutParams secondParam = getDefaultValue(); secondParam.version(2); assertNotEquals(firstParam.hashCode(), secondParam.hashCode()); } @Test public void checkEqualsWithNull() { LolwutParams firstParam = getDefaultValue(); LolwutParams secondParam = null; assertFalse(firstParam.equals(secondParam)); } private LolwutParams getDefaultValue() { return new LolwutParams(); } } ================================================ FILE: src/test/java/redis/clients/jedis/params/MigrateParamsTest.java ================================================ package redis.clients.jedis.params; import org.junit.jupiter.api.Test; import static org.junit.jupiter.api.Assertions.*; public class MigrateParamsTest { @Test public void checkEqualsIdenticalParams() { MigrateParams firstParam = getDefaultValue(); MigrateParams secondParam = getDefaultValue(); assertTrue(firstParam.equals(secondParam)); } @Test public void checkHashCodeIdenticalParams() { MigrateParams firstParam = getDefaultValue(); MigrateParams secondParam = getDefaultValue(); assertEquals(firstParam.hashCode(), secondParam.hashCode()); } @Test public void checkEqualsVariousParams() { MigrateParams firstParam = getDefaultValue(); firstParam.auth("123"); MigrateParams secondParam = getDefaultValue(); secondParam.auth("234"); assertFalse(firstParam.equals(secondParam)); } @Test public void checkHashCodeVariousParams() { MigrateParams firstParam = getDefaultValue(); firstParam.auth("123"); MigrateParams secondParam = getDefaultValue(); secondParam.auth("234"); assertNotEquals(firstParam.hashCode(), secondParam.hashCode()); } @Test public void checkEqualsWithNull() { MigrateParams firstParam = getDefaultValue(); MigrateParams secondParam = null; assertFalse(firstParam.equals(secondParam)); } private MigrateParams getDefaultValue() { return new MigrateParams(); } } ================================================ FILE: src/test/java/redis/clients/jedis/params/ModuleLoadExParamsTest.java ================================================ package redis.clients.jedis.params; import org.junit.jupiter.api.Test; import static org.junit.jupiter.api.Assertions.*; public class ModuleLoadExParamsTest { @Test public void checkEqualsIdenticalParams() { ModuleLoadExParams firstParam = getDefaultValue(); ModuleLoadExParams secondParam = getDefaultValue(); assertTrue(firstParam.equals(secondParam)); } @Test public void checkHashCodeIdenticalParams() { ModuleLoadExParams firstParam = getDefaultValue(); ModuleLoadExParams secondParam = getDefaultValue(); assertEquals(firstParam.hashCode(), secondParam.hashCode()); } @Test public void checkEqualsVariousParams() { ModuleLoadExParams firstParam = getDefaultValue(); firstParam.arg("123"); ModuleLoadExParams secondParam = getDefaultValue(); secondParam.arg("234"); assertFalse(firstParam.equals(secondParam)); } @Test public void checkHashCodeVariousParams() { ModuleLoadExParams firstParam = getDefaultValue(); firstParam.arg("123"); ModuleLoadExParams secondParam = getDefaultValue(); secondParam.arg("234"); assertNotEquals(firstParam.hashCode(), secondParam.hashCode()); } @Test public void checkEqualsWithNull() { ModuleLoadExParams firstParam = getDefaultValue(); ModuleLoadExParams secondParam = null; assertFalse(firstParam.equals(secondParam)); } private ModuleLoadExParams getDefaultValue() { return new ModuleLoadExParams(); } } ================================================ FILE: src/test/java/redis/clients/jedis/params/RestoreParamsTest.java ================================================ package redis.clients.jedis.params; import org.junit.jupiter.api.Test; import static org.junit.jupiter.api.Assertions.*; public class RestoreParamsTest { @Test public void checkEqualsIdenticalParams() { RestoreParams firstParam = getDefaultValue(); RestoreParams secondParam = getDefaultValue(); assertTrue(firstParam.equals(secondParam)); } @Test public void checkHashCodeIdenticalParams() { RestoreParams firstParam = getDefaultValue(); RestoreParams secondParam = getDefaultValue(); assertEquals(firstParam.hashCode(), secondParam.hashCode()); } @Test public void checkEqualsVariousParams() { RestoreParams firstParam = getDefaultValue(); firstParam.idleTime(14); RestoreParams secondParam = getDefaultValue(); secondParam.idleTime(15); assertFalse(firstParam.equals(secondParam)); } @Test public void checkHashCodeVariousParams() { RestoreParams firstParam = getDefaultValue(); firstParam.idleTime(14); RestoreParams secondParam = getDefaultValue(); secondParam.idleTime(15); assertNotEquals(firstParam.hashCode(), secondParam.hashCode()); } @Test public void checkEqualsWithNull() { RestoreParams firstParam = getDefaultValue(); RestoreParams secondParam = null; assertFalse(firstParam.equals(secondParam)); } private RestoreParams getDefaultValue() { return new RestoreParams(); } } ================================================ FILE: src/test/java/redis/clients/jedis/params/ScanParamsTest.java ================================================ package redis.clients.jedis.params; import org.junit.jupiter.api.Test; import static org.junit.jupiter.api.Assertions.*; public class ScanParamsTest { @Test public void checkEqualsIdenticalParams() { ScanParams firstParam = getDefaultValue(); ScanParams secondParam = getDefaultValue(); assertTrue(firstParam.equals(secondParam)); } @Test public void checkHashCodeIdenticalParams() { ScanParams firstParam = getDefaultValue(); ScanParams secondParam = getDefaultValue(); assertEquals(firstParam.hashCode(), secondParam.hashCode()); } @Test public void checkEqualsVariousParams() { ScanParams firstParam = getDefaultValue(); firstParam.count(15); ScanParams secondParam = getDefaultValue(); secondParam.count(16); assertFalse(firstParam.equals(secondParam)); } @Test public void checkHashCodeVariousParams() { ScanParams firstParam = getDefaultValue(); firstParam.count(15); ScanParams secondParam = getDefaultValue(); secondParam.count(16); assertNotEquals(firstParam.hashCode(), secondParam.hashCode()); } @Test public void checkEqualsWithNull() { ScanParams firstParam = getDefaultValue(); ScanParams secondParam = null; assertFalse(firstParam.equals(secondParam)); } private ScanParams getDefaultValue() { return new ScanParams(); } } ================================================ FILE: src/test/java/redis/clients/jedis/params/SetParamsTest.java ================================================ package redis.clients.jedis.params; import org.junit.jupiter.api.Test; import static org.junit.jupiter.api.Assertions.*; public class SetParamsTest { @Test public void checkEqualsIdenticalParams() { SetParams firstParam = getDefaultValue(); SetParams secondParam = getDefaultValue(); assertTrue(firstParam.equals(secondParam)); } @Test public void checkHashCodeIdenticalParams() { SetParams firstParam = getDefaultValue(); SetParams secondParam = getDefaultValue(); assertEquals(firstParam.hashCode(), secondParam.hashCode()); } @Test public void checkEqualsVariousParams() { SetParams firstParam = getDefaultValue(); firstParam.nx(); SetParams secondParam = getDefaultValue(); secondParam.xx(); assertFalse(firstParam.equals(secondParam)); } @Test public void checkHashCodeVariousParams() { SetParams firstParam = getDefaultValue(); firstParam.nx(); SetParams secondParam = getDefaultValue(); secondParam.xx(); assertNotEquals(firstParam.hashCode(), secondParam.hashCode()); } @Test public void checkEqualsWithNull() { SetParams firstParam = getDefaultValue(); SetParams secondParam = null; assertFalse(firstParam.equals(secondParam)); } private SetParams getDefaultValue() { return new SetParams(); } } ================================================ FILE: src/test/java/redis/clients/jedis/params/ShutdownParamsTest.java ================================================ package redis.clients.jedis.params; import org.junit.jupiter.api.Test; import static org.junit.jupiter.api.Assertions.*; public class ShutdownParamsTest { @Test public void checkEqualsIdenticalParams() { ShutdownParams firstParam = getDefaultValue(); ShutdownParams secondParam = getDefaultValue(); assertTrue(firstParam.equals(secondParam)); } @Test public void checkHashCodeIdenticalParams() { ShutdownParams firstParam = getDefaultValue(); ShutdownParams secondParam = getDefaultValue(); assertEquals(firstParam.hashCode(), secondParam.hashCode()); } @Test public void checkEqualsVariousParams() { ShutdownParams firstParam = getDefaultValue(); firstParam.force(); ShutdownParams secondParam = getDefaultValue(); secondParam.nosave(); assertFalse(firstParam.equals(secondParam)); } @Test public void checkHashCodeVariousParams() { ShutdownParams firstParam = getDefaultValue(); firstParam.force(); ShutdownParams secondParam = getDefaultValue(); secondParam.nosave(); assertNotEquals(firstParam.hashCode(), secondParam.hashCode()); } @Test public void checkEqualsWithNull() { ShutdownParams firstParam = getDefaultValue(); ShutdownParams secondParam = null; assertFalse(firstParam.equals(secondParam)); } private ShutdownParams getDefaultValue() { return new ShutdownParams(); } } ================================================ FILE: src/test/java/redis/clients/jedis/params/SortingParamsTest.java ================================================ package redis.clients.jedis.params; import org.junit.jupiter.api.Test; import static org.junit.jupiter.api.Assertions.*; public class SortingParamsTest { @Test public void checkEqualsIdenticalParams() { SortingParams firstParam = getDefaultValue(); SortingParams secondParam = getDefaultValue(); assertTrue(firstParam.equals(secondParam)); } @Test public void checkHashCodeIdenticalParams() { SortingParams firstParam = getDefaultValue(); SortingParams secondParam = getDefaultValue(); assertEquals(firstParam.hashCode(), secondParam.hashCode()); } @Test public void checkEqualsVariousParams() { SortingParams firstParam = getDefaultValue(); firstParam.limit(15, 20); SortingParams secondParam = getDefaultValue(); secondParam.limit(10, 15); assertFalse(firstParam.equals(secondParam)); } @Test public void checkHashCodeVariousParams() { SortingParams firstParam = getDefaultValue(); firstParam.limit(15, 20); SortingParams secondParam = getDefaultValue(); secondParam.limit(10, 15); assertNotEquals(firstParam.hashCode(), secondParam.hashCode()); } @Test public void checkEqualsWithNull() { SortingParams firstParam = getDefaultValue(); SortingParams secondParam = null; assertFalse(firstParam.equals(secondParam)); } private SortingParams getDefaultValue() { return new SortingParams(); } } ================================================ FILE: src/test/java/redis/clients/jedis/params/XAddParamsTest.java ================================================ package redis.clients.jedis.params; import org.junit.jupiter.api.Test; import static org.junit.jupiter.api.Assertions.*; public class XAddParamsTest { @Test public void checkEqualsIdenticalParams() { XAddParams firstParam = getDefaultValue(); XAddParams secondParam = getDefaultValue(); assertTrue(firstParam.equals(secondParam)); } @Test public void checkHashCodeIdenticalParams() { XAddParams firstParam = getDefaultValue(); XAddParams secondParam = getDefaultValue(); assertEquals(firstParam.hashCode(), secondParam.hashCode()); } @Test public void checkEqualsVariousParams() { XAddParams firstParam = getDefaultValue(); firstParam.id(15); XAddParams secondParam = getDefaultValue(); secondParam.id(20); assertFalse(firstParam.equals(secondParam)); } @Test public void checkHashCodeVariousParams() { XAddParams firstParam = getDefaultValue(); firstParam.id(15); XAddParams secondParam = getDefaultValue(); secondParam.id(20); assertNotEquals(firstParam.hashCode(), secondParam.hashCode()); } @Test public void checkEqualsWithNull() { XAddParams firstParam = getDefaultValue(); XAddParams secondParam = null; assertFalse(firstParam.equals(secondParam)); } @Test public void checkEqualsIdmpAutoParams() { XAddParams firstParam = getDefaultValue(); firstParam.idmpAuto("producer1"); XAddParams secondParam = getDefaultValue(); secondParam.idmpAuto("producer1"); assertTrue(firstParam.equals(secondParam)); } @Test public void checkHashCodeIdmpAutoParams() { XAddParams firstParam = getDefaultValue(); firstParam.idmpAuto("producer1"); XAddParams secondParam = getDefaultValue(); secondParam.idmpAuto("producer1"); assertEquals(firstParam.hashCode(), secondParam.hashCode()); } @Test public void checkEqualsVariousIdmpAutoParams() { XAddParams firstParam = getDefaultValue(); firstParam.idmpAuto("producer1"); XAddParams secondParam = getDefaultValue(); secondParam.idmpAuto("producer2"); assertFalse(firstParam.equals(secondParam)); } @Test public void checkEqualsIdmpParams() { XAddParams firstParam = getDefaultValue(); firstParam.idmp("producer1", "iid1"); XAddParams secondParam = getDefaultValue(); secondParam.idmp("producer1", "iid1"); assertTrue(firstParam.equals(secondParam)); } @Test public void checkHashCodeIdmpParams() { XAddParams firstParam = getDefaultValue(); firstParam.idmp("producer1", "iid1"); XAddParams secondParam = getDefaultValue(); secondParam.idmp("producer1", "iid1"); assertEquals(firstParam.hashCode(), secondParam.hashCode()); } @Test public void checkEqualsVariousIdmpParams() { XAddParams firstParam = getDefaultValue(); firstParam.idmp("producer1", "iid1"); XAddParams secondParam = getDefaultValue(); secondParam.idmp("producer1", "iid2"); assertFalse(firstParam.equals(secondParam)); } @Test public void checkEqualsIdmpAutoVsIdmp() { XAddParams firstParam = getDefaultValue(); firstParam.idmpAuto("producer1"); XAddParams secondParam = getDefaultValue(); secondParam.idmp("producer1", "iid1"); assertFalse(firstParam.equals(secondParam)); } private XAddParams getDefaultValue() { return new XAddParams(); } } ================================================ FILE: src/test/java/redis/clients/jedis/params/XAutoClaimParamsTest.java ================================================ package redis.clients.jedis.params; import org.junit.jupiter.api.Test; import static org.junit.jupiter.api.Assertions.*; public class XAutoClaimParamsTest { @Test public void checkEqualsIdenticalParams() { XAutoClaimParams firstParam = getDefaultValue(); XAutoClaimParams secondParam = getDefaultValue(); assertTrue(firstParam.equals(secondParam)); } @Test public void checkHashCodeIdenticalParams() { XAutoClaimParams firstParam = getDefaultValue(); XAutoClaimParams secondParam = getDefaultValue(); assertEquals(firstParam.hashCode(), secondParam.hashCode()); } @Test public void checkEqualsVariousParams() { XAutoClaimParams firstParam = getDefaultValue(); firstParam.count(15); XAutoClaimParams secondParam = getDefaultValue(); secondParam.count(20); assertFalse(firstParam.equals(secondParam)); } @Test public void checkHashCodeVariousParams() { XAutoClaimParams firstParam = getDefaultValue(); firstParam.count(15); XAutoClaimParams secondParam = getDefaultValue(); secondParam.count(20); assertNotEquals(firstParam.hashCode(), secondParam.hashCode()); } @Test public void checkEqualsWithNull() { XAutoClaimParams firstParam = getDefaultValue(); XAutoClaimParams secondParam = null; assertFalse(firstParam.equals(secondParam)); } private XAutoClaimParams getDefaultValue() { return new XAutoClaimParams(); } } ================================================ FILE: src/test/java/redis/clients/jedis/params/XCfgSetParamsTest.java ================================================ package redis.clients.jedis.params; import org.junit.jupiter.api.Test; import static org.junit.jupiter.api.Assertions.*; public class XCfgSetParamsTest { @Test public void checkEqualsIdenticalParams() { XCfgSetParams firstParam = getDefaultValue(); XCfgSetParams secondParam = getDefaultValue(); assertTrue(firstParam.equals(secondParam)); } @Test public void checkHashCodeIdenticalParams() { XCfgSetParams firstParam = getDefaultValue(); XCfgSetParams secondParam = getDefaultValue(); assertEquals(firstParam.hashCode(), secondParam.hashCode()); } @Test public void checkEqualsVariousParams() { XCfgSetParams firstParam = getDefaultValue(); firstParam.idmpDuration(100); XCfgSetParams secondParam = getDefaultValue(); secondParam.idmpDuration(200); assertFalse(firstParam.equals(secondParam)); } @Test public void checkHashCodeVariousParams() { XCfgSetParams firstParam = getDefaultValue(); firstParam.idmpDuration(100); XCfgSetParams secondParam = getDefaultValue(); secondParam.idmpDuration(200); assertNotEquals(firstParam.hashCode(), secondParam.hashCode()); } @Test public void checkEqualsWithNull() { XCfgSetParams firstParam = getDefaultValue(); XCfgSetParams secondParam = null; assertFalse(firstParam.equals(secondParam)); } @Test public void testIdmpDurationValidRange() { XCfgSetParams params = new XCfgSetParams(); // Test minimum valid value assertDoesNotThrow(() -> params.idmpDuration(1)); // Test maximum valid value assertDoesNotThrow(() -> params.idmpDuration(86400)); // Test value in range assertDoesNotThrow(() -> params.idmpDuration(100)); } @Test public void testIdmpDurationBelowMinimum() { XCfgSetParams params = new XCfgSetParams(); IllegalArgumentException exception = assertThrows(IllegalArgumentException.class, () -> params.idmpDuration(0)); assertEquals("IDMP-DURATION must be between 1 and 86400 seconds", exception.getMessage()); exception = assertThrows(IllegalArgumentException.class, () -> params.idmpDuration(-1)); assertEquals("IDMP-DURATION must be between 1 and 86400 seconds", exception.getMessage()); } @Test public void testIdmpDurationAboveMaximum() { XCfgSetParams params = new XCfgSetParams(); IllegalArgumentException exception = assertThrows(IllegalArgumentException.class, () -> params.idmpDuration(86401)); assertEquals("IDMP-DURATION must be between 1 and 86400 seconds", exception.getMessage()); } @Test public void testIdmpMaxsizeValidRange() { XCfgSetParams params = new XCfgSetParams(); // Test minimum valid value assertDoesNotThrow(() -> params.idmpMaxsize(1)); // Test maximum valid value assertDoesNotThrow(() -> params.idmpMaxsize(10000)); // Test value in range assertDoesNotThrow(() -> params.idmpMaxsize(100)); } @Test public void testIdmpMaxsizeBelowMinimum() { XCfgSetParams params = new XCfgSetParams(); IllegalArgumentException exception = assertThrows(IllegalArgumentException.class, () -> params.idmpMaxsize(0)); assertEquals("IDMP-MAXSIZE must be between 1 and 10000", exception.getMessage()); exception = assertThrows(IllegalArgumentException.class, () -> params.idmpMaxsize(-1)); assertEquals("IDMP-MAXSIZE must be between 1 and 10000", exception.getMessage()); } @Test public void testIdmpMaxsizeAboveMaximum() { XCfgSetParams params = new XCfgSetParams(); IllegalArgumentException exception = assertThrows(IllegalArgumentException.class, () -> params.idmpMaxsize(10001)); assertEquals("IDMP-MAXSIZE must be between 1 and 10000", exception.getMessage()); } @Test public void testBothParametersValid() { XCfgSetParams params = new XCfgSetParams(); assertDoesNotThrow(() -> params.idmpDuration(1000).idmpMaxsize(500)); } private XCfgSetParams getDefaultValue() { return new XCfgSetParams(); } } ================================================ FILE: src/test/java/redis/clients/jedis/params/XClaimParamsTest.java ================================================ package redis.clients.jedis.params; import org.junit.jupiter.api.Test; import static org.junit.jupiter.api.Assertions.*; public class XClaimParamsTest { @Test public void checkEqualsIdenticalParams() { XClaimParams firstParam = getDefaultValue(); XClaimParams secondParam = getDefaultValue(); assertTrue(firstParam.equals(secondParam)); } @Test public void checkHashCodeIdenticalParams() { XClaimParams firstParam = getDefaultValue(); XClaimParams secondParam = getDefaultValue(); assertEquals(firstParam.hashCode(), secondParam.hashCode()); } @Test public void checkEqualsVariousParams() { XClaimParams firstParam = getDefaultValue(); firstParam.time(20); XClaimParams secondParam = getDefaultValue(); secondParam.time(21); assertFalse(firstParam.equals(secondParam)); } @Test public void checkHashCodeVariousParams() { XClaimParams firstParam = getDefaultValue(); firstParam.time(20); XClaimParams secondParam = getDefaultValue(); secondParam.time(21); assertNotEquals(firstParam.hashCode(), secondParam.hashCode()); } @Test public void checkEqualsWithNull() { XClaimParams firstParam = getDefaultValue(); XClaimParams secondParam = null; assertFalse(firstParam.equals(secondParam)); } private XClaimParams getDefaultValue() { return new XClaimParams(); } } ================================================ FILE: src/test/java/redis/clients/jedis/params/XPendingParamsTest.java ================================================ package redis.clients.jedis.params; import org.junit.jupiter.api.Test; import redis.clients.jedis.StreamEntryID; import static org.junit.jupiter.api.Assertions.*; public class XPendingParamsTest { @Test public void checkEqualsIdenticalParams() { XPendingParams firstParam = getDefaultValue(); XPendingParams secondParam = getDefaultValue(); assertTrue(firstParam.equals(secondParam)); } @Test public void checkHashCodeIdenticalParams() { XPendingParams firstParam = getDefaultValue(); XPendingParams secondParam = getDefaultValue(); assertEquals(firstParam.hashCode(), secondParam.hashCode()); } @Test public void checkEqualsVariousParams() { XPendingParams firstParam = getDefaultValue(); firstParam.start(StreamEntryID.XGROUP_LAST_ENTRY); XPendingParams secondParam = getDefaultValue(); secondParam.start(StreamEntryID.NEW_ENTRY); assertFalse(firstParam.equals(secondParam)); } @Test public void checkHashCodeVariousParams() { XPendingParams firstParam = getDefaultValue(); firstParam.start(StreamEntryID.XGROUP_LAST_ENTRY); XPendingParams secondParam = getDefaultValue(); secondParam.start(StreamEntryID.NEW_ENTRY); assertNotEquals(firstParam.hashCode(), secondParam.hashCode()); } @Test public void checkEqualsWithNull() { XPendingParams firstParam = getDefaultValue(); XPendingParams secondParam = null; assertFalse(firstParam.equals(secondParam)); } private XPendingParams getDefaultValue() { return new XPendingParams(); } } ================================================ FILE: src/test/java/redis/clients/jedis/params/XReadGroupParamsTest.java ================================================ package redis.clients.jedis.params; import org.junit.jupiter.api.Test; import static org.junit.jupiter.api.Assertions.*; public class XReadGroupParamsTest { @Test public void checkEqualsIdenticalParams() { XReadGroupParams firstParam = getDefaultValue(); XReadGroupParams secondParam = getDefaultValue(); assertTrue(firstParam.equals(secondParam)); } @Test public void checkHashCodeIdenticalParams() { XReadGroupParams firstParam = getDefaultValue(); XReadGroupParams secondParam = getDefaultValue(); assertEquals(firstParam.hashCode(), secondParam.hashCode()); } @Test public void checkEqualsVariousParams() { XReadGroupParams firstParam = getDefaultValue(); firstParam.block(14); XReadGroupParams secondParam = getDefaultValue(); secondParam.block(15); assertFalse(firstParam.equals(secondParam)); } @Test public void checkHashCodeVariousParams() { XReadGroupParams firstParam = getDefaultValue(); firstParam.block(14); XReadGroupParams secondParam = getDefaultValue(); secondParam.block(15); assertNotEquals(firstParam.hashCode(), secondParam.hashCode()); } @Test public void checkEqualsWithNull() { XReadGroupParams firstParam = getDefaultValue(); XReadGroupParams secondParam = null; assertFalse(firstParam.equals(secondParam)); } private XReadGroupParams getDefaultValue() { return new XReadGroupParams(); } } ================================================ FILE: src/test/java/redis/clients/jedis/params/XReadParamsTest.java ================================================ package redis.clients.jedis.params; import org.junit.jupiter.api.Test; import static org.junit.jupiter.api.Assertions.*; public class XReadParamsTest { @Test public void checkEqualsIdenticalParams() { XReadParams firstParam = getDefaultValue(); XReadParams secondParam = getDefaultValue(); assertTrue(firstParam.equals(secondParam)); } @Test public void checkHashCodeIdenticalParams() { XReadParams firstParam = getDefaultValue(); XReadParams secondParam = getDefaultValue(); assertEquals(firstParam.hashCode(), secondParam.hashCode()); } @Test public void checkEqualsVariousParams() { XReadParams firstParam = getDefaultValue(); firstParam.block(14); XReadParams secondParam = getDefaultValue(); secondParam.block(15); assertFalse(firstParam.equals(secondParam)); } @Test public void checkHashCodeVariousParams() { XReadParams firstParam = getDefaultValue(); firstParam.block(14); XReadParams secondParam = getDefaultValue(); secondParam.block(15); assertNotEquals(firstParam.hashCode(), secondParam.hashCode()); } @Test public void checkEqualsWithNull() { XReadParams firstParam = getDefaultValue(); XReadParams secondParam = null; assertFalse(firstParam.equals(secondParam)); } private XReadParams getDefaultValue() { return new XReadParams(); } } ================================================ FILE: src/test/java/redis/clients/jedis/params/XTrimParamsTest.java ================================================ package redis.clients.jedis.params; import org.junit.jupiter.api.Test; import static org.junit.jupiter.api.Assertions.*; public class XTrimParamsTest { @Test public void checkEqualsIdenticalParams() { XTrimParams firstParam = getDefaultValue(); XTrimParams secondParam = getDefaultValue(); assertTrue(firstParam.equals(secondParam)); } @Test public void checkHashCodeIdenticalParams() { XTrimParams firstParam = getDefaultValue(); XTrimParams secondParam = getDefaultValue(); assertEquals(firstParam.hashCode(), secondParam.hashCode()); } @Test public void checkEqualsVariousParams() { XTrimParams firstParam = getDefaultValue(); firstParam.maxLen(15); XTrimParams secondParam = getDefaultValue(); secondParam.maxLen(16); assertFalse(firstParam.equals(secondParam)); } @Test public void checkHashCodeVariousParams() { XTrimParams firstParam = getDefaultValue(); firstParam.maxLen(15); XTrimParams secondParam = getDefaultValue(); secondParam.maxLen(16); assertNotEquals(firstParam.hashCode(), secondParam.hashCode()); } @Test public void checkEqualsWithNull() { XTrimParams firstParam = getDefaultValue(); XTrimParams secondParam = null; assertFalse(firstParam.equals(secondParam)); } private XTrimParams getDefaultValue() { return new XTrimParams(); } } ================================================ FILE: src/test/java/redis/clients/jedis/params/ZAddParamsTest.java ================================================ package redis.clients.jedis.params; import org.junit.jupiter.api.Test; import static org.junit.jupiter.api.Assertions.*; public class ZAddParamsTest { @Test public void checkEqualsIdenticalParams() { ZAddParams firstParam = getDefaultValue(); ZAddParams secondParam = getDefaultValue(); assertTrue(firstParam.equals(secondParam)); } @Test public void checkHashCodeIdenticalParams() { ZAddParams firstParam = getDefaultValue(); ZAddParams secondParam = getDefaultValue(); assertEquals(firstParam.hashCode(), secondParam.hashCode()); } @Test public void checkEqualsVariousParams() { ZAddParams firstParam = getDefaultValue(); firstParam.nx(); ZAddParams secondParam = getDefaultValue(); secondParam.xx(); assertFalse(firstParam.equals(secondParam)); } @Test public void checkHashCodeVariousParams() { ZAddParams firstParam = getDefaultValue(); firstParam.nx(); ZAddParams secondParam = getDefaultValue(); secondParam.xx(); assertNotEquals(firstParam.hashCode(), secondParam.hashCode()); } @Test public void checkEqualsWithNull() { ZAddParams firstParam = getDefaultValue(); ZAddParams secondParam = null; assertFalse(firstParam.equals(secondParam)); } private ZAddParams getDefaultValue() { return new ZAddParams(); } } ================================================ FILE: src/test/java/redis/clients/jedis/params/ZIncrByParamsTest.java ================================================ package redis.clients.jedis.params; import org.junit.jupiter.api.Test; import static org.junit.jupiter.api.Assertions.*; public class ZIncrByParamsTest { @Test public void checkEqualsIdenticalParams() { ZIncrByParams firstParam = getDefaultValue(); ZIncrByParams secondParam = getDefaultValue(); assertTrue(firstParam.equals(secondParam)); } @Test public void checkHashCodeIdenticalParams() { ZIncrByParams firstParam = getDefaultValue(); ZIncrByParams secondParam = getDefaultValue(); assertEquals(firstParam.hashCode(), secondParam.hashCode()); } @Test public void checkEqualsVariousParams() { ZIncrByParams firstParam = getDefaultValue(); firstParam.nx(); ZIncrByParams secondParam = getDefaultValue(); secondParam.xx(); assertFalse(firstParam.equals(secondParam)); } @Test public void checkHashCodeVariousParams() { ZIncrByParams firstParam = getDefaultValue(); firstParam.nx(); ZIncrByParams secondParam = getDefaultValue(); secondParam.xx(); assertNotEquals(firstParam.hashCode(), secondParam.hashCode()); } @Test public void checkEqualsWithNull() { ZIncrByParams firstParam = getDefaultValue(); ZIncrByParams secondParam = null; assertFalse(firstParam.equals(secondParam)); } private ZIncrByParams getDefaultValue() { return new ZIncrByParams(); } } ================================================ FILE: src/test/java/redis/clients/jedis/params/ZParamsTest.java ================================================ package redis.clients.jedis.params; import org.junit.jupiter.api.Test; import static org.junit.jupiter.api.Assertions.*; public class ZParamsTest { @Test public void checkEqualsIdenticalParams() { ZParams firstParam = getDefaultValue(); ZParams secondParam = getDefaultValue(); assertTrue(firstParam.equals(secondParam)); } @Test public void checkHashCodeIdenticalParams() { ZParams firstParam = getDefaultValue(); ZParams secondParam = getDefaultValue(); assertEquals(firstParam.hashCode(), secondParam.hashCode()); } @Test public void checkEqualsVariousParams() { ZParams firstParam = getDefaultValue(); firstParam.aggregate(ZParams.Aggregate.MIN); ZParams secondParam = getDefaultValue(); secondParam.aggregate(ZParams.Aggregate.MAX); assertFalse(firstParam.equals(secondParam)); } @Test public void checkHashCodeVariousParams() { ZParams firstParam = getDefaultValue(); firstParam.aggregate(ZParams.Aggregate.MIN); ZParams secondParam = getDefaultValue(); secondParam.aggregate(ZParams.Aggregate.MAX); assertNotEquals(firstParam.hashCode(), secondParam.hashCode()); } @Test public void checkEqualsWithNull() { ZParams firstParam = getDefaultValue(); ZParams secondParam = null; assertFalse(firstParam.equals(secondParam)); } private ZParams getDefaultValue() { return new ZParams(); } } ================================================ FILE: src/test/java/redis/clients/jedis/params/ZRangeParamsTest.java ================================================ package redis.clients.jedis.params; import org.junit.jupiter.api.Nested; import org.junit.jupiter.api.Test; import redis.clients.jedis.CommandArguments; import redis.clients.jedis.Protocol; import redis.clients.jedis.args.Rawable; import redis.clients.jedis.args.RawableFactory; import redis.clients.jedis.util.CommandArgumentsMatchers; import redis.clients.jedis.util.ProtocolTestUtil; import java.io.IOException; import java.util.Iterator; import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.*; import static org.junit.jupiter.api.Assertions.*; import static redis.clients.jedis.util.CommandArgumentsMatchers.*; public class ZRangeParamsTest { @Test public void checkEqualsIdenticalParams() { ZRangeParams firstParam = getDefaultValue(); ZRangeParams secondParam = getDefaultValue(); assertTrue(firstParam.equals(secondParam)); } @Test public void checkHashCodeIdenticalParams() { ZRangeParams firstParam = getDefaultValue(); ZRangeParams secondParam = getDefaultValue(); assertEquals(firstParam.hashCode(), secondParam.hashCode()); } @Test public void checkEqualsVariousParams() { ZRangeParams firstParam = getDefaultValue(); firstParam.limit(15, 20); ZRangeParams secondParam = getDefaultValue(); secondParam.limit(16, 21); assertFalse(firstParam.equals(secondParam)); } @Test public void checkHashCodeVariousParams() { ZRangeParams firstParam = getDefaultValue(); firstParam.limit(15, 20); ZRangeParams secondParam = getDefaultValue(); secondParam.limit(16, 21); assertNotEquals(firstParam.hashCode(), secondParam.hashCode()); } @Test public void checkEqualsWithNull() { ZRangeParams firstParam = getDefaultValue(); ZRangeParams secondParam = null; assertFalse(firstParam.equals(secondParam)); } @Nested class BuilderTests { @Test public void testZrangeParamsIntMinMax() { int min = 0; int max = 1; ZRangeParams params = ZRangeParams.zrangeParams(min, max); CommandArguments args = new CommandArguments(Protocol.Command.ZRANGE); params.addParams(args); assertThat(args, hasArgumentCount(3)); assertThat(args, hasArguments( Protocol.Command.ZRANGE, RawableFactory.from(min), RawableFactory.from(max) )); } //Test that long values are serialized as long (not double) to avoid precision loss @Test public void testZrangeParamsLongMinMax() { long min = Integer.MAX_VALUE + 1L; long max = Integer.MAX_VALUE + 2L; ZRangeParams params = ZRangeParams.zrangeParams(min, max); CommandArguments args = new CommandArguments(Protocol.Command.ZRANGE); params.addParams(args); assertThat(args, hasArgumentCount(3)); assertThat(args, hasArguments( Protocol.Command.ZRANGE, RawableFactory.from(min), RawableFactory.from(max) )); } //Test that int factory method delegates to long constructor and produces the same Rawable encoding @Test public void testZrangeParamsIntMinMaxDelegatesToLong() { int min = 100; int max = 200; ZRangeParams intParams = ZRangeParams.zrangeParams(min, max); ZRangeParams longParams = ZRangeParams.zrangeParams((long) min, (long) max); CommandArguments intArgs = new CommandArguments(Protocol.Command.ZRANGE); intParams.addParams(intArgs); CommandArguments longArgs = new CommandArguments(Protocol.Command.ZRANGE); longParams.addParams(longArgs); // Both should produce identical arguments assertEquals(intArgs.size(), longArgs.size()); Iterator intIter = intArgs.iterator(); Iterator longIter = longArgs.iterator(); while (intIter.hasNext() && longIter.hasNext()) { assertEquals(intIter.next(), longIter.next()); } } // Test that zrangeParams(double, double) produces BYSCORE args @Test public void testZrangeParamsByScore() { double min = 0.1; double max = 1.1; ZRangeParams params = ZRangeParams.zrangeByScoreParams(min, max); CommandArguments args = new CommandArguments(Protocol.Command.ZRANGE); params.addParams(args); assertThat(args, hasArgumentCount(4)); assertThat(args, hasArguments( Protocol.Command.ZRANGE, RawableFactory.from(min), RawableFactory.from(max), Protocol.Keyword.BYSCORE )); } // Test the actual RESP protocol output @Test public void testProtocolOutputWithLongValues() throws IOException { long largeStart = 3_000_000_000L; long largeEnd = 3_000_000_099L; ZRangeParams params = ZRangeParams.zrangeParams(largeStart, largeEnd); CommandArguments args = new CommandArguments(Protocol.Command.ZRANGE); params.addParams(args); // Capture the RESP protocol output String respOutput = ProtocolTestUtil.captureCommandOutput(args); // RESP protocol uses CRLF (\r\n) line endings String expected = "*3\r\n" + "$6\r\n" + "ZRANGE\r\n" + "$10\r\n" + "3000000000\r\n" + "$10\r\n" + "3000000099\r\n"; assertEquals(expected, respOutput); } } private ZRangeParams getDefaultValue() { return new ZRangeParams(0, 0); } } ================================================ FILE: src/test/java/redis/clients/jedis/prefix/JedisPooledPrefixedKeysTest.java ================================================ package redis.clients.jedis.prefix; import redis.clients.jedis.EndpointConfig; import redis.clients.jedis.Endpoints; import redis.clients.jedis.RedisClient; import org.junit.jupiter.api.Tag; import org.junit.jupiter.api.BeforeAll; @Tag("integration") public class JedisPooledPrefixedKeysTest extends PrefixedKeysTest { private static EndpointConfig ENDPOINT; @BeforeAll public static void prepareEndpoint() { ENDPOINT = Endpoints.getRedisEndpoint("standalone1"); } @Override RedisClient nonPrefixingJedis() { return RedisClient.builder() .hostAndPort(ENDPOINT.getHostAndPort()) .clientConfig(ENDPOINT.getClientConfigBuilder().timeoutMillis(500).build()) .build(); } } ================================================ FILE: src/test/java/redis/clients/jedis/prefix/PrefixedKeysTest.java ================================================ package redis.clients.jedis.prefix; import io.redis.test.annotations.ConditionalOnEnv; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.RegisterExtension; import redis.clients.jedis.AbstractPipeline; import redis.clients.jedis.AbstractTransaction; import redis.clients.jedis.UnifiedJedis; import redis.clients.jedis.resps.Tuple; import redis.clients.jedis.util.EnvCondition; import redis.clients.jedis.util.PrefixedKeyArgumentPreProcessor; import redis.clients.jedis.util.SafeEncoder; import redis.clients.jedis.util.TestEnvUtil; import static org.junit.jupiter.api.Assertions.assertEquals; public abstract class PrefixedKeysTest { @RegisterExtension public static EnvCondition envCondition = new EnvCondition(); abstract T nonPrefixingJedis(); T prefixingJedis() { T jedis = nonPrefixingJedis(); jedis.setKeyArgumentPreProcessor(new PrefixedKeyArgumentPreProcessor("test-prefix:")); return jedis; } @AfterEach public void cleanUp() { try (UnifiedJedis jedis = prefixingJedis()) { jedis.flushAll(); } } @Test public void prefixesKeys() { try (UnifiedJedis jedis = prefixingJedis()) { jedis.set("foo1", "bar1"); jedis.set(SafeEncoder.encode("foo2"), SafeEncoder.encode("bar2")); AbstractPipeline pipeline = jedis.pipelined(); pipeline.incr("foo3"); pipeline.zadd("foo4", 1234, "bar4"); pipeline.sync(); } try (UnifiedJedis jedis = nonPrefixingJedis()) { assertEquals("bar1", jedis.get("test-prefix:foo1")); assertEquals("bar2", jedis.get("test-prefix:foo2")); assertEquals("1", jedis.get("test-prefix:foo3")); assertEquals(new Tuple("bar4", 1234d), jedis.zpopmax("test-prefix:foo4")); } } @Test @ConditionalOnEnv(value = TestEnvUtil.ENV_REDIS_ENTERPRISE, enabled = false) public void prefixesKeysInTransaction() { try (UnifiedJedis jedis = prefixingJedis()) { AbstractTransaction transaction = jedis.multi(); transaction.set("foo1", "bar1-from-transaction"); transaction.hset("foo2", "bar2-key", "bar2-value"); transaction.exec(); } try (UnifiedJedis jedis = nonPrefixingJedis()) { assertEquals("bar1-from-transaction", jedis.get("test-prefix:foo1")); assertEquals("bar2-value", jedis.hget("test-prefix:foo2", "bar2-key")); } } } ================================================ FILE: src/test/java/redis/clients/jedis/prefix/RedisClusterPrefixedKeysTest.java ================================================ package redis.clients.jedis.prefix; import java.util.HashSet; import java.util.Set; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Tag; import org.junit.jupiter.api.BeforeAll; import redis.clients.jedis.EndpointConfig; import redis.clients.jedis.Endpoints; import redis.clients.jedis.HostAndPort; import redis.clients.jedis.JedisClientConfig; import redis.clients.jedis.RedisClusterClient; import static org.junit.jupiter.api.Assertions.assertThrows; @Tag("integration") public class RedisClusterPrefixedKeysTest extends PrefixedKeysTest { private static EndpointConfig endpoint; private static JedisClientConfig CLIENT_CONFIG; private static Set NODES; @BeforeAll public static void prepareEndpoint() { endpoint = Endpoints.getRedisEndpoint("cluster-stable"); CLIENT_CONFIG = endpoint.getClientConfigBuilder().build(); NODES = new HashSet<>(endpoint.getHostsAndPorts()); } @Override RedisClusterClient nonPrefixingJedis() { return RedisClusterClient.builder() .nodes(NODES) .clientConfig(CLIENT_CONFIG) .build(); } @Override @Test public void prefixesKeysInTransaction() { assertThrows(UnsupportedOperationException.class, () -> super.prefixesKeysInTransaction()); } } ================================================ FILE: src/test/java/redis/clients/jedis/prefix/RedisSentinelClientPrefixedKeysTest.java ================================================ package redis.clients.jedis.prefix; import java.util.Arrays; import java.util.HashSet; import java.util.Set; import org.junit.jupiter.api.BeforeAll; import redis.clients.jedis.DefaultJedisClientConfig; import redis.clients.jedis.HostAndPort; import redis.clients.jedis.Endpoints; import redis.clients.jedis.JedisClientConfig; import redis.clients.jedis.RedisSentinelClient; import org.junit.jupiter.api.Tag; @Tag("integration") public class RedisSentinelClientPrefixedKeysTest extends PrefixedKeysTest { private static final String MASTER_NAME = "mymaster"; private static final JedisClientConfig MASTER_CLIENT_CONFIG = DefaultJedisClientConfig.builder().password("foobared").build(); private static Set SENTINEL_NODES; private static final JedisClientConfig SENTINEL_CLIENT_CONFIG = DefaultJedisClientConfig.builder().build(); @BeforeAll public static void prepareEndpoints() { SENTINEL_NODES = new HashSet<>( Arrays.asList( Endpoints.getRedisEndpoint("sentinel-standalone2-1").getHostAndPort(), Endpoints.getRedisEndpoint("sentinel-standalone2-3").getHostAndPort())); } @Override RedisSentinelClient nonPrefixingJedis() { return RedisSentinelClient.builder() .masterName(MASTER_NAME) .clientConfig(MASTER_CLIENT_CONFIG) .sentinels(SENTINEL_NODES) .sentinelClientConfig(SENTINEL_CLIENT_CONFIG) .build(); } } ================================================ FILE: src/test/java/redis/clients/jedis/providers/HealthStatusManagerTest.java ================================================ package redis.clients.jedis.providers; import static org.junit.jupiter.api.Assertions.*; import java.util.concurrent.CountDownLatch; import java.util.concurrent.TimeUnit; import org.junit.jupiter.api.Test; import redis.clients.jedis.HostAndPort; import redis.clients.jedis.mcf.HealthCheckStrategy; import redis.clients.jedis.mcf.HealthStatus; import redis.clients.jedis.mcf.HealthStatusListener; import redis.clients.jedis.mcf.HealthStatusManager; import redis.clients.jedis.mcf.TestHealthCheckStrategy; public class HealthStatusManagerTest { private final HostAndPort endpoint = new HostAndPort("localhost", 6379); @Test void manager_event_emission_order_basic() throws InterruptedException { HealthStatusManager manager = new HealthStatusManager(); CountDownLatch eventLatch = new CountDownLatch(1); HealthStatusListener listener = event -> eventLatch.countDown(); manager.registerListener(endpoint, listener); HealthCheckStrategy immediateHealthy = new TestHealthCheckStrategy( HealthCheckStrategy.Config.builder().interval(50).timeout(25).build(), e -> HealthStatus.HEALTHY); manager.add(endpoint, immediateHealthy); assertTrue(eventLatch.await(2, TimeUnit.SECONDS), "Should receive health status event"); manager.remove(endpoint); } @Test void manager_hasHealthCheck_add_remove() { HealthStatusManager manager = new HealthStatusManager(); assertFalse(manager.hasHealthCheck(endpoint)); HealthCheckStrategy strategy = new TestHealthCheckStrategy( HealthCheckStrategy.Config.builder().interval(50).timeout(25).build(), e -> HealthStatus.HEALTHY); manager.add(endpoint, strategy); assertTrue(manager.hasHealthCheck(endpoint)); manager.remove(endpoint); assertFalse(manager.hasHealthCheck(endpoint)); } } ================================================ FILE: src/test/java/redis/clients/jedis/providers/MultiClusterPooledConnectionProviderTest.java ================================================ ================================================ FILE: src/test/java/redis/clients/jedis/providers/MultiDbProviderHealthStatusChangeTest.java ================================================ package redis.clients.jedis.providers; import static org.junit.jupiter.api.Assertions.*; import static org.mockito.Mockito.*; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; import org.mockito.MockedConstruction; import org.mockito.junit.jupiter.MockitoExtension; import redis.clients.jedis.Connection; import redis.clients.jedis.ConnectionPool; import redis.clients.jedis.DefaultJedisClientConfig; import redis.clients.jedis.HostAndPort; import redis.clients.jedis.JedisClientConfig; import redis.clients.jedis.MultiDbConfig; import redis.clients.jedis.mcf.HealthStatus; import redis.clients.jedis.mcf.MultiDbConnectionProvider; import redis.clients.jedis.mcf.MultiDbConnectionProviderHelper; /** * Tests for MultiDbConnectionProvider event handling behavior during initialization and throughout * its lifecycle with HealthStatusChangeEvents. */ @ExtendWith(MockitoExtension.class) public class MultiDbProviderHealthStatusChangeTest { private HostAndPort endpoint1; private HostAndPort endpoint2; private HostAndPort endpoint3; private JedisClientConfig clientConfig; @BeforeEach void setUp() { endpoint1 = new HostAndPort("localhost", 6879); endpoint2 = new HostAndPort("localhost", 6880); endpoint3 = new HostAndPort("localhost", 6881); clientConfig = DefaultJedisClientConfig.builder().build(); } private MockedConstruction mockConnectionPool() { Connection mockConnection = mock(Connection.class); lenient().when(mockConnection.ping()).thenReturn(true); return mockConstruction(ConnectionPool.class, (mock, context) -> { when(mock.getResource()).thenReturn(mockConnection); doNothing().when(mock).close(); }); } @Test void postInit_unhealthy_active_sets_grace_and_fails_over() throws Exception { try (MockedConstruction mockedPool = mockConnectionPool()) { // Create databases without health checks MultiDbConfig.DatabaseConfig database1 = MultiDbConfig.DatabaseConfig .builder(endpoint1, clientConfig).weight(1.0f).healthCheckEnabled(false).build(); MultiDbConfig.DatabaseConfig database2 = MultiDbConfig.DatabaseConfig .builder(endpoint2, clientConfig).weight(0.5f).healthCheckEnabled(false).build(); MultiDbConfig config = new MultiDbConfig.Builder( new MultiDbConfig.DatabaseConfig[] { database1, database2 }).build(); try (MultiDbConnectionProvider provider = new MultiDbConnectionProvider(config)) { assertFalse(provider.getDatabase(endpoint1).isInGracePeriod()); assertEquals(provider.getDatabase(), provider.getDatabase(endpoint1)); // This should process immediately since initialization is complete assertDoesNotThrow(() -> { MultiDbConnectionProviderHelper.onHealthStatusChange(provider, endpoint1, HealthStatus.HEALTHY, HealthStatus.UNHEALTHY); }, "Post-initialization events should be processed immediately"); // Verify the database has changed according to the UNHEALTHY status assertTrue(provider.getDatabase(endpoint1).isInGracePeriod(), "UNHEALTHY status on active database should cause a grace period"); assertNotEquals(provider.getDatabase(), provider.getDatabase(endpoint1), "UNHEALTHY status on active database should cause a failover"); } } } @Test void postInit_nonActive_changes_do_not_switch_active() throws Exception { try (MockedConstruction mockedPool = mockConnectionPool()) { MultiDbConfig.DatabaseConfig database1 = MultiDbConfig.DatabaseConfig .builder(endpoint1, clientConfig).weight(1.0f).healthCheckEnabled(false).build(); MultiDbConfig.DatabaseConfig database2 = MultiDbConfig.DatabaseConfig .builder(endpoint2, clientConfig).weight(0.5f).healthCheckEnabled(false).build(); MultiDbConfig config = new MultiDbConfig.Builder( new MultiDbConfig.DatabaseConfig[] { database1, database2 }).build(); try (MultiDbConnectionProvider provider = new MultiDbConnectionProvider(config)) { // Verify initial state assertEquals(provider.getDatabase(endpoint1), provider.getDatabase(), "Should start with endpoint1 active"); // Simulate multiple rapid events for the same endpoint (post-init behavior) MultiDbConnectionProviderHelper.onHealthStatusChange(provider, endpoint1, HealthStatus.HEALTHY, HealthStatus.UNHEALTHY); // After first UNHEALTHY on active database: it enters grace period and provider fails over assertTrue(provider.getDatabase(endpoint1).isInGracePeriod(), "Active database should enter grace period"); assertEquals(provider.getDatabase(endpoint2), provider.getDatabase(), "Should fail over to endpoint2"); MultiDbConnectionProviderHelper.onHealthStatusChange(provider, endpoint1, HealthStatus.UNHEALTHY, HealthStatus.HEALTHY); // Healthy event for non-active database should not immediately revert active database assertEquals(provider.getDatabase(endpoint2), provider.getDatabase(), "Active database should remain endpoint2"); assertTrue(provider.getDatabase(endpoint1).isInGracePeriod(), "Grace period should still be in effect"); MultiDbConnectionProviderHelper.onHealthStatusChange(provider, endpoint1, HealthStatus.HEALTHY, HealthStatus.UNHEALTHY); // Further UNHEALTHY for non-active database is a no-op assertEquals(provider.getDatabase(endpoint2), provider.getDatabase(), "Active database unchanged"); assertTrue(provider.getDatabase(endpoint1).isInGracePeriod(), "Still in grace period"); } } } @Test void init_selects_highest_weight_healthy_when_checks_disabled() throws Exception { try (MockedConstruction mockedPool = mockConnectionPool()) { MultiDbConfig.DatabaseConfig database1 = MultiDbConfig.DatabaseConfig .builder(endpoint1, clientConfig).weight(1.0f).healthCheckEnabled(false).build(); MultiDbConfig.DatabaseConfig database2 = MultiDbConfig.DatabaseConfig .builder(endpoint2, clientConfig).weight(2.0f).healthCheckEnabled(false).build(); MultiDbConfig config = new MultiDbConfig.Builder( new MultiDbConfig.DatabaseConfig[] { database1, database2 }).build(); try (MultiDbConnectionProvider provider = new MultiDbConnectionProvider(config)) { // This test verifies that multiple endpoints are properly initialized // Verify both databases are initialized properly assertNotNull(provider.getDatabase(endpoint1), "Database 1 should be available"); assertNotNull(provider.getDatabase(endpoint2), "Database 2 should be available"); // Both should be healthy (no health checks = assumed healthy) assertTrue(provider.getDatabase(endpoint1).isHealthy(), "Database 1 should be healthy"); assertTrue(provider.getDatabase(endpoint2).isHealthy(), "Database 2 should be healthy"); } } } @Test void init_single_database_initializes_and_is_healthy() throws Exception { try (MockedConstruction mockedPool = mockConnectionPool()) { MultiDbConfig.DatabaseConfig database1 = MultiDbConfig.DatabaseConfig .builder(endpoint1, clientConfig).weight(1.0f).healthCheckEnabled(false).build(); MultiDbConfig config = new MultiDbConfig.Builder( new MultiDbConfig.DatabaseConfig[] { database1 }).build(); // This test verifies that the provider initializes correctly and doesn't lose events // In practice, with health checks disabled, no events should be generated during init try (MultiDbConnectionProvider provider = new MultiDbConnectionProvider(config)) { // Verify successful initialization assertNotNull(provider.getDatabase(), "Provider should have initialized successfully"); assertEquals(provider.getDatabase(endpoint1), provider.getDatabase(), "Should have selected the configured database"); assertTrue(provider.getDatabase().isHealthy(), "Database should be healthy (assumed healthy with no health checks)"); } } } // ========== POST-INITIALIZATION EVENT ORDERING TESTS ========== @Test void postInit_two_hop_failover_chain_respected() throws Exception { try (MockedConstruction mockedPool = mockConnectionPool()) { MultiDbConfig.DatabaseConfig database1 = MultiDbConfig.DatabaseConfig .builder(endpoint1, clientConfig).weight(1.0f).healthCheckEnabled(false).build(); MultiDbConfig.DatabaseConfig database2 = MultiDbConfig.DatabaseConfig .builder(endpoint2, clientConfig).weight(0.5f).healthCheckEnabled(false).build(); MultiDbConfig.DatabaseConfig database3 = MultiDbConfig.DatabaseConfig .builder(endpoint3, clientConfig).weight(0.2f).healthCheckEnabled(false).build(); MultiDbConfig config = new MultiDbConfig.Builder( new MultiDbConfig.DatabaseConfig[] { database1, database2, database3 }).build(); try (MultiDbConnectionProvider provider = new MultiDbConnectionProvider(config)) { // First event: endpoint1 (active) becomes UNHEALTHY -> failover to endpoint2, endpoint1 // enters grace MultiDbConnectionProviderHelper.onHealthStatusChange(provider, endpoint1, HealthStatus.HEALTHY, HealthStatus.UNHEALTHY); assertTrue(provider.getDatabase(endpoint1).isInGracePeriod(), "Endpoint1 should be in grace after unhealthy"); assertEquals(provider.getDatabase(endpoint2), provider.getDatabase(), "Should have failed over to endpoint2"); // Second event: endpoint2 (now active) becomes UNHEALTHY -> failover to endpoint3 MultiDbConnectionProviderHelper.onHealthStatusChange(provider, endpoint2, HealthStatus.HEALTHY, HealthStatus.UNHEALTHY); assertTrue(provider.getDatabase(endpoint2).isInGracePeriod(), "Endpoint2 should be in grace after unhealthy"); assertEquals(provider.getDatabase(endpoint3), provider.getDatabase(), "Should have failed over to endpoint3"); // Third event: endpoint1 becomes HEALTHY again -> no immediate switch due to grace period // behavior MultiDbConnectionProviderHelper.onHealthStatusChange(provider, endpoint1, HealthStatus.UNHEALTHY, HealthStatus.HEALTHY); assertEquals(provider.getDatabase(endpoint3), provider.getDatabase(), "Active database should remain endpoint3"); } } } @Test void postInit_rapid_events_respect_grace_and_keep_active_stable() throws Exception { try (MockedConstruction mockedPool = mockConnectionPool()) { MultiDbConfig.DatabaseConfig database1 = MultiDbConfig.DatabaseConfig .builder(endpoint1, clientConfig).weight(1.0f).healthCheckEnabled(false).build(); MultiDbConfig.DatabaseConfig database2 = MultiDbConfig.DatabaseConfig .builder(endpoint2, clientConfig).weight(0.5f).healthCheckEnabled(false).build(); MultiDbConfig config = new MultiDbConfig.Builder( new MultiDbConfig.DatabaseConfig[] { database1, database2 }).build(); try (MultiDbConnectionProvider provider = new MultiDbConnectionProvider(config)) { // Verify initial state assertEquals(HealthStatus.HEALTHY, provider.getDatabase(endpoint1).getHealthStatus(), "Should start as HEALTHY"); // Send rapid sequence of events post-init MultiDbConnectionProviderHelper.onHealthStatusChange(provider, endpoint1, HealthStatus.HEALTHY, HealthStatus.UNHEALTHY); // triggers failover and grace MultiDbConnectionProviderHelper.onHealthStatusChange(provider, endpoint1, HealthStatus.UNHEALTHY, HealthStatus.HEALTHY); // non-active database becomes healthy MultiDbConnectionProviderHelper.onHealthStatusChange(provider, endpoint1, HealthStatus.HEALTHY, HealthStatus.UNHEALTHY); // still non-active and in grace; no change // Final expectations: endpoint1 is in grace, provider remains on endpoint2 assertTrue(provider.getDatabase(endpoint1).isInGracePeriod(), "Endpoint1 should be in grace period"); assertEquals(provider.getDatabase(endpoint2), provider.getDatabase(), "Active database should remain endpoint2"); } } } } ================================================ FILE: src/test/java/redis/clients/jedis/providers/SentineledConnectionProviderReconnectionTest.java ================================================ package redis.clients.jedis.providers; import static org.junit.jupiter.api.Assertions.*; import static org.mockito.ArgumentMatchers.*; import static org.mockito.Mockito.*; import java.time.Duration; import java.util.Arrays; import java.util.HashSet; import java.util.Set; import java.util.concurrent.CopyOnWriteArrayList; import java.util.concurrent.CountDownLatch; import java.util.concurrent.TimeUnit; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Tag; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; import org.mockito.Mock; import org.mockito.junit.jupiter.MockitoExtension; import redis.clients.jedis.HostAndPort; import redis.clients.jedis.Jedis; import redis.clients.jedis.JedisClientConfig; import redis.clients.jedis.JedisPubSub; import redis.clients.jedis.exceptions.JedisConnectionException; import redis.clients.jedis.providers.SentineledConnectionProvider.SentinelConnectionFactory; import redis.clients.jedis.util.Delay; /** * Unit tests for SentineledConnectionProvider reconnection logic. Tests connection provider's * ability to reconnect to sentinel nodes with configured delay. */ @ExtendWith(MockitoExtension.class) @Tag("unit") public class SentineledConnectionProviderReconnectionTest { private static final String MASTER_NAME = "mymaster"; private static final HostAndPort SENTINEL_1 = new HostAndPort("localhost", 26379); private static final HostAndPort SENTINEL_2 = new HostAndPort("localhost", 26380); private static final HostAndPort MASTER = new HostAndPort("localhost", 6379); private Set sentinels; private JedisClientConfig masterConfig; private JedisClientConfig sentinelConfig; private SentineledConnectionProvider provider; @Mock private Jedis mockJedis; @Mock private SentinelConnectionFactory sentinelConnectionFactory; @Mock private Delay reconnectDelay; @BeforeEach void setUp() { sentinels = new HashSet<>(); sentinels.add(SENTINEL_1); sentinels.add(SENTINEL_2); masterConfig = mock(JedisClientConfig.class); sentinelConfig = mock(JedisClientConfig.class); } @AfterEach void tearDown() { if (provider != null) { provider.close(); } } @Test void testReconnectToSentinelWithConfiguredDelay() throws InterruptedException { // Capture delay values passed to sleeper CopyOnWriteArrayList capturedDelays = new CopyOnWriteArrayList<>(); // await for 3 reconnect attempts CountDownLatch reconnectAttempts = new CountDownLatch(3); // Mock dependencies SentineledConnectionProvider.Sleeper capturingSleeper = millis -> { capturedDelays.add(millis); reconnectAttempts.countDown(); }; // Simulate sentinel connection failures (disconnect scenario) long expectedDelay = 100L; when(mockJedis.sentinelGetMasterAddrByName(MASTER_NAME)) .thenReturn(Arrays.asList(MASTER.getHost(), String.valueOf(MASTER.getPort()))); Jedis failingJedis = mock(Jedis.class); doThrow(new JedisConnectionException("Connection lost")).when(failingJedis) .subscribe(any(JedisPubSub.class), anyString()); when(sentinelConnectionFactory.createConnection(any(), any())) .thenAnswer(invocation -> mockJedis).thenAnswer(invocation -> failingJedis); when(reconnectDelay.delay(anyLong())).thenReturn(Duration.ofMillis(expectedDelay)); // Create provider provider = new SentineledConnectionProvider(MASTER_NAME, masterConfig, null, null, sentinels, sentinelConfig, reconnectDelay, sentinelConnectionFactory, capturingSleeper); // Verify reconnection attempts happen with configured delay assertTrue(reconnectAttempts.await(100, TimeUnit.MILLISECONDS), "Should attempt to reconnect at least 3 times after disconnect"); // Assert all delays match the configured value assertTrue(capturedDelays.size() >= 3, "Should have captured at least 3 delay values"); for (Long delay : capturedDelays) { assertEquals(expectedDelay, delay, "Sleeper should be called with configured delay of " + expectedDelay + "ms"); } } } ================================================ FILE: src/test/java/redis/clients/jedis/resps/LibraryInfoTest.java ================================================ package redis.clients.jedis.resps; import org.junit.jupiter.api.Test; import redis.clients.jedis.Protocol; import redis.clients.jedis.util.RedisInputStream; import java.io.ByteArrayInputStream; import java.io.InputStream; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertNotNull; import static org.junit.jupiter.api.Assertions.assertNull; import static org.junit.jupiter.api.Assertions.assertTrue; public class LibraryInfoTest { private static Object parseRespResponse(String respResponse) { InputStream is = new ByteArrayInputStream(respResponse.getBytes()); return Protocol.read(new RedisInputStream(is)); } @Test public void buildLibraryInfoResp2Standard() { // Simulate standard RESP2 response for FUNCTION LIST (without WITHCODE) // Format: [library_name, , engine, , functions, ] String respResponse = "*6\r\n" + "$12\r\nlibrary_name\r\n" + "$5\r\nmylib\r\n" + "$6\r\nengine\r\n" + "$3\r\nLUA\r\n" + "$9\r\nfunctions\r\n" + "*1\r\n" + // functions // array with 1 // element "*6\r\n" + // function info with 3 key-value pairs "$4\r\nname\r\n" + "$6\r\nmyfunc\r\n" + "$11\r\ndescription\r\n" + "$-1\r\n" + // null // description "$5\r\nflags\r\n" + "*0\r\n"; // empty flags array Object data = parseRespResponse(respResponse); LibraryInfo result = LibraryInfo.LIBRARY_INFO.build(data); assertNotNull(result); assertEquals("mylib", result.getLibraryName()); assertEquals("LUA", result.getEngine()); assertNotNull(result.getFunctions()); assertEquals(1, result.getFunctions().size()); assertEquals("myfunc", result.getFunctions().get(0).get("name")); assertNull(result.getLibraryCode()); } @Test public void buildLibraryInfoResp2WithCode() { // Simulate RESP2 response for FUNCTION LIST WITHCODE // Format: [library_name, , engine, , functions, , library_code, ] String respResponse = "*8\r\n" + "$12\r\nlibrary_name\r\n" + "$5\r\nmylib\r\n" + "$6\r\nengine\r\n" + "$3\r\nLUA\r\n" + "$9\r\nfunctions\r\n" + "*1\r\n" + // functions // array with 1 // element "*6\r\n" + // function info with 3 key-value pairs "$4\r\nname\r\n" + "$6\r\nmyfunc\r\n" + "$11\r\ndescription\r\n" + "$-1\r\n" + // null // description "$5\r\nflags\r\n" + "*0\r\n" + // empty flags array "$12\r\nlibrary_code\r\n" + "$50\r\n#!LUA name=mylib\nredis.register_function('myfunc')\r\n"; Object data = parseRespResponse(respResponse); LibraryInfo result = LibraryInfo.LIBRARY_INFO.build(data); assertNotNull(result); assertEquals("mylib", result.getLibraryName()); assertEquals("LUA", result.getEngine()); assertNotNull(result.getFunctions()); assertEquals(1, result.getFunctions().size()); assertEquals("myfunc", result.getFunctions().get(0).get("name")); assertNotNull(result.getLibraryCode()); assertTrue(result.getLibraryCode().contains("#!LUA name=mylib")); } @Test public void buildLibraryInfoResp2WithExtraConsistentField() { // Simulate Redis Enterprise RESP2 response with extra "consistent" field // This is the bug scenario from CAE-2120 // Format: [library_name, , engine, , consistent, , functions, ] String respResponse = "*8\r\n" + "$12\r\nlibrary_name\r\n" + "$5\r\nmylib\r\n" + "$6\r\nengine\r\n" + "$3\r\nLUA\r\n" + "$10\r\nconsistent\r\n" + ":1\r\n" + // consistent // value // (integer) "$9\r\nfunctions\r\n" + "*1\r\n" + // functions array with 1 element "*6\r\n" + // function info with 3 key-value pairs "$4\r\nname\r\n" + "$6\r\nmyfunc\r\n" + "$11\r\ndescription\r\n" + "$-1\r\n" + // null // description "$5\r\nflags\r\n" + "*0\r\n"; // empty flags array Object data = parseRespResponse(respResponse); LibraryInfo result = LibraryInfo.LIBRARY_INFO.build(data); assertNotNull(result); assertEquals("mylib", result.getLibraryName()); assertEquals("LUA", result.getEngine()); assertNotNull(result.getFunctions()); assertEquals(1, result.getFunctions().size()); assertEquals("myfunc", result.getFunctions().get(0).get("name")); assertNull(result.getLibraryCode()); } @Test public void buildLibraryInfoResp2WithExtraConsistentFieldAndCode() { // Simulate Redis Enterprise RESP2 response with extra "consistent" field and WITHCODE // Format: [library_name, , engine, , consistent, , functions, , // library_code, ] String respResponse = "*10\r\n" + "$12\r\nlibrary_name\r\n" + "$5\r\nmylib\r\n" + "$6\r\nengine\r\n" + "$3\r\nLUA\r\n" + "$10\r\nconsistent\r\n" + ":1\r\n" + // consistent // value // (integer) "$9\r\nfunctions\r\n" + "*1\r\n" + // functions array with 1 element "*6\r\n" + // function info with 3 key-value pairs "$4\r\nname\r\n" + "$6\r\nmyfunc\r\n" + "$11\r\ndescription\r\n" + "$-1\r\n" + // null // description "$5\r\nflags\r\n" + "*0\r\n" + // empty flags array "$12\r\nlibrary_code\r\n" + "$50\r\n#!LUA name=mylib\nredis.register_function('myfunc')\r\n"; Object data = parseRespResponse(respResponse); LibraryInfo result = LibraryInfo.LIBRARY_INFO.build(data); assertNotNull(result); assertEquals("mylib", result.getLibraryName()); assertEquals("LUA", result.getEngine()); assertNotNull(result.getFunctions()); assertEquals(1, result.getFunctions().size()); assertEquals("myfunc", result.getFunctions().get(0).get("name")); assertNotNull(result.getLibraryCode()); assertTrue(result.getLibraryCode().contains("#!LUA name=mylib")); } } ================================================ FILE: src/test/java/redis/clients/jedis/resps/StreamEntryDeletionResultTest.java ================================================ package redis.clients.jedis.resps; import org.junit.jupiter.api.Test; import static org.junit.jupiter.api.Assertions.*; public class StreamEntryDeletionResultTest { @Test public void testFromCode() { assertEquals(StreamEntryDeletionResult.NOT_FOUND, StreamEntryDeletionResult.fromCode(-1)); assertEquals(StreamEntryDeletionResult.DELETED, StreamEntryDeletionResult.fromCode(1)); assertEquals(StreamEntryDeletionResult.NOT_DELETED_UNACKNOWLEDGED_OR_STILL_REFERENCED, StreamEntryDeletionResult.fromCode(2)); } @Test public void testFromCodeInvalid() { assertThrows(IllegalArgumentException.class, () -> StreamEntryDeletionResult.fromCode(0)); assertThrows(IllegalArgumentException.class, () -> StreamEntryDeletionResult.fromCode(3)); assertThrows(IllegalArgumentException.class, () -> StreamEntryDeletionResult.fromCode(-2)); } @Test public void testFromLong() { assertEquals(StreamEntryDeletionResult.NOT_FOUND, StreamEntryDeletionResult.fromLong(-1L)); assertEquals(StreamEntryDeletionResult.DELETED, StreamEntryDeletionResult.fromLong(1L)); assertEquals(StreamEntryDeletionResult.NOT_DELETED_UNACKNOWLEDGED_OR_STILL_REFERENCED, StreamEntryDeletionResult.fromLong(2L)); } @Test public void testFromLongNull() { assertThrows(IllegalArgumentException.class, () -> StreamEntryDeletionResult.fromLong(null)); } @Test public void testGetCode() { assertEquals(-1, StreamEntryDeletionResult.NOT_FOUND.getCode()); assertEquals(1, StreamEntryDeletionResult.DELETED.getCode()); assertEquals(2, StreamEntryDeletionResult.NOT_DELETED_UNACKNOWLEDGED_OR_STILL_REFERENCED.getCode()); } @Test public void testToString() { assertEquals("NOT_FOUND(-1)", StreamEntryDeletionResult.NOT_FOUND.toString()); assertEquals("DELETED(1)", StreamEntryDeletionResult.DELETED.toString()); assertEquals("NOT_DELETED_UNACKNOWLEDGED_OR_STILL_REFERENCED(2)", StreamEntryDeletionResult.NOT_DELETED_UNACKNOWLEDGED_OR_STILL_REFERENCED.toString()); } } ================================================ FILE: src/test/java/redis/clients/jedis/scenario/ActiveActiveFailoverIT.java ================================================ package redis.clients.jedis.scenario; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.Tag; import org.junit.jupiter.api.Tags; import org.junit.jupiter.api.Test; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import redis.clients.jedis.*; import redis.clients.jedis.MultiDbConfig.DatabaseConfig; import redis.clients.jedis.exceptions.JedisConnectionException; import redis.clients.jedis.mcf.DatabaseSwitchEvent; import redis.clients.jedis.mcf.MultiDbConnectionProvider; import redis.clients.jedis.util.ClientTestUtil; import java.io.IOException; import java.time.Duration; import java.time.Instant; import java.util.HashMap; import java.util.Map; import java.util.concurrent.atomic.AtomicLong; import java.util.concurrent.atomic.AtomicReference; import java.util.function.Consumer; import static org.awaitility.Awaitility.await; import static org.hamcrest.Matchers.greaterThanOrEqualTo; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertTrue; import static org.junit.jupiter.api.Assertions.fail; import static org.junit.jupiter.api.Assumptions.assumeTrue; import static org.hamcrest.MatcherAssert.assertThat; import static redis.clients.jedis.Protocol.DEFAULT_TIMEOUT; @Tags({ @Tag("failover"), @Tag("scenario") }) public class ActiveActiveFailoverIT { private static final Logger log = LoggerFactory.getLogger(ActiveActiveFailoverIT.class); private static final int NUM_OF_THREADS = 18; private static final int SOCKET_TIMEOUT_MS = DEFAULT_TIMEOUT; private static final int CONNECTION_TIMEOUT_MS = DEFAULT_TIMEOUT; private static final long NETWORK_FAILURE_INTERVAL = 15L; private static EndpointConfig endpoint; private final FaultInjectionClient faultClient = new FaultInjectionClient(); @BeforeAll public static void beforeClass() { try { ActiveActiveFailoverIT.endpoint = Endpoints.getRedisEndpoint("re-active-active"); } catch (IllegalArgumentException e) { log.warn("Skipping test because no Redis endpoint is configured"); assumeTrue(false); } } @Test public void testFailover() { JedisClientConfig config = endpoint.getClientConfigBuilder() .socketTimeoutMillis(SOCKET_TIMEOUT_MS).connectionTimeoutMillis(CONNECTION_TIMEOUT_MS) .build(); DatabaseConfig primary = DatabaseConfig.builder(endpoint.getHostAndPort(0), config) .connectionPoolConfig(RecommendedSettings.poolConfig).weight(1.0f).build(); DatabaseConfig secondary = DatabaseConfig.builder(endpoint.getHostAndPort(1), config) .connectionPoolConfig(RecommendedSettings.poolConfig).weight(0.5f).build(); MultiDbConfig multiConfig = MultiDbConfig.builder().database(primary).database(secondary) .failureDetector(MultiDbConfig.CircuitBreakerConfig.builder().slidingWindowSize(1) // SLIDING // WINDOW // SIZE // IN // SECONDS .failureRateThreshold(10.0f) // percentage of failures to trigger circuit breaker .build()) .failbackSupported(true).failbackCheckInterval(1000).gracePeriod(2000) .commandRetry(MultiDbConfig.RetryConfig.builder().waitDuration(10).maxAttempts(1) .exponentialBackoffMultiplier(1).build()) .fastFailover(true).retryOnFailover(false).build(); class FailoverReporter implements Consumer { String currentClusterName = "not set"; boolean failoverHappened = false; Instant failoverAt = null; boolean failbackHappened = false; Instant failbackAt = null; public String getCurrentClusterName() { return currentClusterName; } @Override public void accept(DatabaseSwitchEvent e) { this.currentClusterName = e.getDatabaseName(); log.info( "\n\n====FailoverEvent=== \nJedis failover to cluster: {}\n====FailoverEvent===\n\n", e.getDatabaseName()); if (failoverHappened) { failbackHappened = true; failbackAt = Instant.now(); } else { failoverHappened = true; failoverAt = Instant.now(); } } } FailoverReporter reporter = new FailoverReporter(); MultiDbClient client = MultiDbClient.builder().multiDbConfig(multiConfig) .databaseSwitchListener(reporter).build(); AtomicLong executedCommands = new AtomicLong(0); AtomicLong retryingThreadsCounter = new AtomicLong(0); AtomicLong failedCommandsAfterFailover = new AtomicLong(0); AtomicReference lastFailedCommandAt = new AtomicReference<>(); Endpoint primaryEndpoint = client.getActiveDatabaseEndpoint(); // Start thread that imitates an application that uses the client MultiThreadedFakeApp fakeApp = new MultiThreadedFakeApp(client, (UnifiedJedis c) -> { long threadId = Thread.currentThread().getId(); int attempt = 0; int maxTries = 500; int retryingDelay = 5; while (true) { boolean attemptToExecuteOnFailedCluster = true; try { Map executionInfo = new HashMap() { { put("threadId", String.valueOf(threadId)); put("cluster", reporter.getCurrentClusterName()); } }; attemptToExecuteOnFailedCluster = client.getActiveDatabaseEndpoint() == primaryEndpoint; client.xadd("execution_log", StreamEntryID.NEW_ENTRY, executionInfo); executedCommands.incrementAndGet(); if (attempt > 0) { log.info("Thread {} recovered after {} ms. Threads still not recovered: {}", threadId, attempt * retryingDelay, retryingThreadsCounter.decrementAndGet()); } break; } catch (JedisConnectionException e) { if (reporter.failoverHappened && !reporter.failbackHappened && attemptToExecuteOnFailedCluster) { failedCommandsAfterFailover.incrementAndGet(); lastFailedCommandAt.set(Instant.now()); } if (attempt == 0) { long failedThreads = retryingThreadsCounter.incrementAndGet(); log.warn("Thread {} failed to execute command. Failed threads: {}", threadId, failedThreads); } try { Thread.sleep(retryingDelay); } catch (InterruptedException ie) { throw new RuntimeException(ie); } if (++attempt == maxTries) throw e; } } return true; }, NUM_OF_THREADS); fakeApp.setKeepExecutingForSeconds(30); Thread t = new Thread(fakeApp); t.start(); while (executedCommands.get() == 0) { // Wait for fake app to start try { Thread.sleep(100); } catch (InterruptedException e) { throw new RuntimeException(e); } } log.info("Fake app started."); HashMap params = new HashMap<>(); params.put("bdb_id", endpoint.getBdbId()); params.put("delay", NETWORK_FAILURE_INTERVAL); FaultInjectionClient.TriggerActionResponse actionResponse = null; try { log.info("Triggering network_failure for ~{} seconds", NETWORK_FAILURE_INTERVAL); actionResponse = faultClient.triggerAction("network_failure", params); } catch (IOException e) { fail("Fault Injection Server error:" + e.getMessage()); } log.info("Action id: {}", actionResponse.getActionId()); fakeApp.setAction(actionResponse); try { t.join(); } catch (InterruptedException e) { throw new RuntimeException(e); } MultiDbConnectionProvider provider = ClientTestUtil.getConnectionProvider(client); ConnectionPool pool1 = provider.getDatabase(endpoint.getHostAndPort(0)).getConnectionPool(); ConnectionPool pool2 = provider.getDatabase(endpoint.getHostAndPort(1)).getConnectionPool(); await().atMost(Duration.ofSeconds(1)).until(() -> pool1.getNumActive() == 0); await().atMost(Duration.ofSeconds(1)).until(() -> pool2.getNumActive() == 0); log.info("Connection pool {}: active: {}, idle: {}", endpoint.getHostAndPort(0), pool1.getNumActive(), pool1.getNumIdle()); log.info("Connection pool {}: active: {}, idle: {}", endpoint.getHostAndPort(1), pool2.getNumActive(), pool2.getNumIdle()); log.info("Failover happened at: {}", reporter.failoverAt); log.info("Failback happened at: {}", reporter.failbackAt); log.info("Last failed command at: {}", lastFailedCommandAt.get()); log.info("Failed commands after failover: {}", failedCommandsAfterFailover.get()); if (lastFailedCommandAt.get() == null) { log.info("No failed commands after failover!"); } else { Duration fullFailoverTime = Duration.between(reporter.failoverAt, lastFailedCommandAt.get()); log.info("Full failover time: {} s", fullFailoverTime.getSeconds()); } assertEquals(0, pool1.getNumActive()); assertTrue(fakeApp.capturedExceptions().isEmpty()); assertTrue(reporter.failoverHappened); assertTrue(reporter.failbackHappened); assertThat(Duration.between(reporter.failoverAt, reporter.failbackAt).getSeconds(), greaterThanOrEqualTo(NETWORK_FAILURE_INTERVAL)); client.close(); } } ================================================ FILE: src/test/java/redis/clients/jedis/scenario/ClusterTopologyRefreshIT.java ================================================ package redis.clients.jedis.scenario; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.Tags; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Tag; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import redis.clients.jedis.*; import redis.clients.jedis.providers.ClusterConnectionProvider; import java.io.IOException; import java.util.HashMap; import java.util.HashSet; import java.util.Set; import java.util.concurrent.atomic.AtomicLong; import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.greaterThan; import static org.hamcrest.Matchers.is; import static org.hamcrest.Matchers.not; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertTrue; import static org.junit.jupiter.api.Assertions.fail; import static org.junit.jupiter.api.Assumptions.assumeTrue; import static org.mockito.Mockito.*; @Tags({ @Tag("scenario") }) public class ClusterTopologyRefreshIT { private static final Logger log = LoggerFactory.getLogger(ClusterTopologyRefreshIT.class); private static EndpointConfig endpoint; private final FaultInjectionClient faultClient = new FaultInjectionClient(); @BeforeAll public static void beforeClass() { try { ClusterTopologyRefreshIT.endpoint = Endpoints.getRedisEndpoint("re-single-shard-oss-cluster"); } catch (IllegalArgumentException e) { log.warn("Skipping test because no Redis endpoint is configured"); assumeTrue(false); } } @Test public void testWithPool() { Set jedisClusterNode = new HashSet<>(); jedisClusterNode.add(endpoint.getHostAndPort()); JedisClientConfig config = endpoint.getClientConfigBuilder() .socketTimeoutMillis(RecommendedSettings.DEFAULT_TIMEOUT_MS) .connectionTimeoutMillis(RecommendedSettings.DEFAULT_TIMEOUT_MS).build(); try (RedisClusterClient client = RedisClusterClient.builder().nodes(jedisClusterNode) .clientConfig(config).maxAttempts(RecommendedSettings.MAX_RETRIES) .maxTotalRetriesDuration(RecommendedSettings.MAX_TOTAL_RETRIES_DURATION).build()) { Set initialNodes = client.getClusterNodes().keySet(); assertEquals(1, initialNodes.size(), "Was this BDB used to run this test before?"); AtomicLong commandsExecuted = new AtomicLong(); // Start thread that imitates an application that uses the client FakeApp fakeApp = new FakeApp(client, (UnifiedJedis c) -> { long i = commandsExecuted.getAndIncrement(); client.set(String.valueOf(i), String.valueOf(i)); return true; }); Thread t = new Thread(fakeApp); t.start(); HashMap params = new HashMap<>(); params.put("bdb_id", endpoint.getBdbId()); params.put("actions", "[\"reshard\",\"failover\"]"); FaultInjectionClient.TriggerActionResponse actionResponse = null; try { log.info("Triggering Resharding and Failover"); actionResponse = faultClient.triggerAction("sequence_of_actions", params); } catch (IOException e) { fail("Fault Injection Server error:" + e.getMessage()); } log.info("Action id: {}", actionResponse.getActionId()); fakeApp.setAction(actionResponse); try { t.join(); } catch (InterruptedException e) { throw new RuntimeException(e); } assertTrue(fakeApp.capturedExceptions().isEmpty()); log.info("Commands executed: {}", commandsExecuted.get()); for (long i = 0; i < commandsExecuted.get(); i++) { assertTrue(client.exists(String.valueOf(i))); } Set afterReshardNodes = client.getClusterNodes().keySet(); assertThat("After set should have more nodes than initial set", afterReshardNodes.size(), greaterThan(initialNodes.size())); boolean hasNewNode = afterReshardNodes.stream().anyMatch(n -> !initialNodes.contains(n)); assertThat("After set should have a node not in initial set", hasNewNode, is(true)); } } } ================================================ FILE: src/test/java/redis/clients/jedis/scenario/ConnectionInterruptionIT.java ================================================ package redis.clients.jedis.scenario; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.Tag; import org.junit.jupiter.api.Tags; import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.ValueSource; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import redis.clients.jedis.*; import redis.clients.jedis.exceptions.JedisConnectionException; import redis.clients.jedis.providers.ConnectionProvider; import redis.clients.jedis.providers.PooledConnectionProvider; import redis.clients.jedis.util.SafeEncoder; import java.io.IOException; import java.util.HashMap; import java.util.concurrent.atomic.AtomicLong; import static org.junit.jupiter.api.Assertions.*; import static org.junit.jupiter.api.Assumptions.assumeTrue; @Tags({ @Tag("scenario") }) public class ConnectionInterruptionIT { private static final Logger log = LoggerFactory.getLogger(ConnectionInterruptionIT.class); private static EndpointConfig endpoint; private final FaultInjectionClient faultClient = new FaultInjectionClient(); @BeforeAll public static void beforeClass() { try { ConnectionInterruptionIT.endpoint = Endpoints.getRedisEndpoint("re-standalone"); } catch (IllegalArgumentException e) { log.warn("Skipping test because no Redis endpoint is configured"); assumeTrue(false); } } @ParameterizedTest @ValueSource(strings = { "dmc_restart", "network_failure" }) public void testWithPool(String triggerAction) { ConnectionProvider connectionProvider = new PooledConnectionProvider(endpoint.getHostAndPort(), endpoint.getClientConfigBuilder().build(), RecommendedSettings.poolConfig); UnifiedJedis client = new UnifiedJedis(connectionProvider, RecommendedSettings.MAX_RETRIES, RecommendedSettings.MAX_TOTAL_RETRIES_DURATION); String keyName = "counter"; client.set(keyName, "0"); assertEquals("0", client.get(keyName)); AtomicLong commandsExecuted = new AtomicLong(); // Start thread that imitates an application that uses the client FakeApp fakeApp = new FakeApp(client, (UnifiedJedis c) -> { assertTrue(client.incr(keyName) > 0); long currentCount = commandsExecuted.getAndIncrement(); log.info("Command executed {}", currentCount); return true; }); fakeApp.setKeepExecutingForSeconds(RecommendedSettings.DEFAULT_TIMEOUT_MS / 1000 * 2); Thread t = new Thread(fakeApp); t.start(); HashMap params = new HashMap<>(); params.put("bdb_id", endpoint.getBdbId()); FaultInjectionClient.TriggerActionResponse actionResponse = null; try { log.info("Triggering {}", triggerAction); actionResponse = faultClient.triggerAction(triggerAction, params); } catch (IOException e) { fail("Fault Injection Server error:" + e.getMessage()); } log.info("Action id: {}", actionResponse.getActionId()); fakeApp.setAction(actionResponse); try { t.join(); } catch (InterruptedException e) { throw new RuntimeException(e); } log.info("Commands executed: {}", commandsExecuted.get()); assertEquals(commandsExecuted.get(), Long.parseLong(client.get(keyName))); assertTrue(fakeApp.capturedExceptions().isEmpty()); client.close(); } @ParameterizedTest @ValueSource(strings = { "dmc_restart", "network_failure" }) public void testWithPubSub(String triggerAction) { ConnectionProvider connectionProvider = new PooledConnectionProvider(endpoint.getHostAndPort(), endpoint.getClientConfigBuilder().build(), RecommendedSettings.poolConfig); UnifiedJedis client = new UnifiedJedis(connectionProvider, RecommendedSettings.MAX_RETRIES, RecommendedSettings.MAX_TOTAL_RETRIES_DURATION); AtomicLong messagesSent = new AtomicLong(); AtomicLong messagesReceived = new AtomicLong(); final Thread subscriberThread = getSubscriberThread(messagesReceived, connectionProvider); // Start thread that imitates a publisher that uses the client FakeApp fakeApp = new FakeApp(client, (UnifiedJedis c) -> { log.info("Publishing message"); long consumed = client.publish("test", String.valueOf(messagesSent.getAndIncrement())); return consumed > 0; }); fakeApp.setKeepExecutingForSeconds(10); Thread t = new Thread(fakeApp); t.start(); HashMap params = new HashMap<>(); params.put("bdb_id", endpoint.getBdbId()); FaultInjectionClient.TriggerActionResponse actionResponse = null; try { log.info("Triggering {}", triggerAction); actionResponse = faultClient.triggerAction(triggerAction, params); } catch (IOException e) { fail("Fault Injection Server error:" + e.getMessage()); } log.info("Action id: {}", actionResponse.getActionId()); fakeApp.setAction(actionResponse); try { t.join(); } catch (InterruptedException e) { throw new RuntimeException(e); } if (subscriberThread.isAlive()) subscriberThread.interrupt(); assertEquals(messagesSent.get() - 1, messagesReceived.get()); assertTrue(fakeApp.capturedExceptions().isEmpty()); client.close(); } private static Thread getSubscriberThread(AtomicLong messagesReceived, ConnectionProvider connectionProvider) { final JedisPubSubBase pubSub = new JedisPubSubBase() { @Override public void onMessage(String channel, String message) { messagesReceived.incrementAndGet(); log.info("Received message: {}", message); } @Override protected String encode(byte[] raw) { return SafeEncoder.encode(raw); } }; final Thread subscriberThread = new Thread(() -> { try { pubSub.proceed(connectionProvider.getConnection(), "test"); fail("PubSub should have been interrupted"); } catch (JedisConnectionException e) { log.info("Expected exception in Subscriber: {}", e.getMessage()); assertTrue(e.getMessage().contains("Unexpected end of stream.")); } }); subscriberThread.start(); return subscriberThread; } } ================================================ FILE: src/test/java/redis/clients/jedis/scenario/FakeApp.java ================================================ package redis.clients.jedis.scenario; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import redis.clients.jedis.UnifiedJedis; import redis.clients.jedis.exceptions.JedisConnectionException; import redis.clients.jedis.exceptions.JedisException; import java.time.Duration; import java.util.ArrayList; import java.util.List; public class FakeApp implements Runnable { protected static final Logger log = LoggerFactory.getLogger(FakeApp.class); public void setKeepExecutingForSeconds(int keepExecutingForSeconds) { this.keepExecutingForSeconds = keepExecutingForSeconds; } protected int keepExecutingForSeconds = 60; protected FaultInjectionClient.TriggerActionResponse actionResponse = null; protected final UnifiedJedis client; protected final ExecutedAction action; protected List exceptions = new ArrayList<>(); @FunctionalInterface public interface ExecutedAction { boolean run(UnifiedJedis client); } public FakeApp(UnifiedJedis client, ExecutedAction action) { this.client = client; this.action = action; } public void setAction(FaultInjectionClient.TriggerActionResponse actionResponse) { this.actionResponse = actionResponse; } public List capturedExceptions() { return exceptions; } public void run() { log.info("Starting FakeApp"); int checkEachSeconds = 5; int timeoutSeconds = 120; while (actionResponse == null || !actionResponse.isCompleted( Duration.ofSeconds(checkEachSeconds), Duration.ofSeconds(keepExecutingForSeconds), Duration.ofSeconds(timeoutSeconds))) { try { boolean success = action.run(client); if (!success) break; } catch (JedisConnectionException e) { log.error("Error executing action", e); exceptions.add(e); } } } } ================================================ FILE: src/test/java/redis/clients/jedis/scenario/FaultInjectionClient.java ================================================ package redis.clients.jedis.scenario; import java.io.IOException; import java.time.Duration; import java.time.Instant; import java.util.HashMap; import java.util.concurrent.TimeUnit; import com.google.gson.FieldNamingPolicy; import com.google.gson.GsonBuilder; import com.google.gson.reflect.TypeToken; import com.google.gson.annotations.Expose; import org.apache.hc.client5.http.config.RequestConfig; import org.apache.hc.client5.http.fluent.Request; import com.google.gson.Gson; import org.apache.hc.client5.http.fluent.Response; import org.apache.hc.client5.http.impl.classic.CloseableHttpClient; import org.apache.hc.client5.http.impl.classic.HttpClientBuilder; import org.apache.hc.core5.http.ContentType; import org.slf4j.Logger; import org.slf4j.LoggerFactory; public class FaultInjectionClient { private static final String BASE_URL; static final int CONNECTION_REQUEST_TIMEOUT = 3000; static final int RESPONSE_TIMEOUT = 3000; static { BASE_URL = System.getenv().getOrDefault("FAULT_INJECTION_API_URL", "http://127.0.0.1:20324"); } private static final Logger log = LoggerFactory.getLogger(FaultInjectionClient.class); public static class TriggerActionResponse { @Expose private final String actionId; private Instant lastRequestTime = null; private Instant completedAt = null; private Instant firstRequestAt = null; public TriggerActionResponse(String actionId) { this.actionId = actionId; } public String getActionId() { return actionId; } public boolean isCompleted(Duration checkInterval, Duration delayAfter, Duration timeout) { if (completedAt != null) { return Duration.between(completedAt, Instant.now()).compareTo(delayAfter) >= 0; } if (firstRequestAt != null && Duration.between(firstRequestAt, Instant.now()) .compareTo(timeout) >= 0) { throw new RuntimeException("Timeout"); } if (lastRequestTime == null || Duration.between(lastRequestTime, Instant.now()) .compareTo(checkInterval) >= 0) { lastRequestTime = Instant.now(); if (firstRequestAt == null) { firstRequestAt = lastRequestTime; } CloseableHttpClient httpClient = getHttpClient(); Request request = Request.get(BASE_URL + "/action/" + actionId); try { Response response = request.execute(httpClient); String result = response.returnContent().asString(); log.info("Action status: {}", result); if (result.contains("success")) { completedAt = Instant.now(); return Duration.between(completedAt, Instant.now()).compareTo(delayAfter) >= 0; } } catch (IOException e) { throw new RuntimeException("Fault injection proxy error ", e); } } return false; } } private static CloseableHttpClient getHttpClient() { RequestConfig requestConfig = RequestConfig.custom() .setConnectionRequestTimeout(CONNECTION_REQUEST_TIMEOUT, TimeUnit.MILLISECONDS) .setResponseTimeout(RESPONSE_TIMEOUT, TimeUnit.MILLISECONDS).build(); return HttpClientBuilder.create() .setDefaultRequestConfig(requestConfig).build(); } public TriggerActionResponse triggerAction(String actionType, HashMap parameters) throws IOException { Gson gson = new GsonBuilder() .setFieldNamingPolicy(FieldNamingPolicy.LOWER_CASE_WITH_UNDERSCORES) .excludeFieldsWithoutExposeAnnotation() .create(); HashMap payload = new HashMap<>(); payload.put("type", actionType); payload.put("parameters", parameters); String jsonString = gson.toJson(payload); CloseableHttpClient httpClient = getHttpClient(); Request request = Request.post(BASE_URL + "/action"); request.bodyString(jsonString, ContentType.APPLICATION_JSON); try { String result = request.execute(httpClient).returnContent().asString(); return gson.fromJson(result, new TypeToken() { }.getType()); } catch (IOException e) { e.printStackTrace(); throw e; } } } ================================================ FILE: src/test/java/redis/clients/jedis/scenario/LagAwareStrategySslIT.java ================================================ package redis.clients.jedis.scenario; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Tag; import org.junit.jupiter.api.Tags; import org.junit.jupiter.api.Test; import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.CsvSource; import org.junit.jupiter.params.provider.EnumSource; import redis.clients.jedis.DefaultRedisCredentials; import redis.clients.jedis.Endpoint; import redis.clients.jedis.EndpointConfig; import redis.clients.jedis.Endpoints; import redis.clients.jedis.RedisCredentials; import redis.clients.jedis.SslOptions; import redis.clients.jedis.SslVerifyMode; import redis.clients.jedis.exceptions.JedisException; import redis.clients.jedis.mcf.HealthStatus; import redis.clients.jedis.mcf.LagAwareStrategy; import redis.clients.jedis.util.TlsUtil; import javax.net.ssl.HttpsURLConnection; import javax.net.ssl.SSLContext; import javax.net.ssl.SSLSocket; import javax.net.ssl.SSLSocketFactory; import javax.net.ssl.TrustManager; import javax.net.ssl.X509TrustManager; import java.io.FileOutputStream; import java.nio.file.Path; import java.nio.file.Paths; import java.security.KeyStore; import java.security.cert.Certificate; import java.security.cert.X509Certificate; import java.util.function.Supplier; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertThrows; import static org.junit.jupiter.api.Assertions.fail; /** * Test class demonstrating SSL configuration for LagAwareStrategy */ @Tags({ @Tag("scenario") }) public class LagAwareStrategySslIT { private static EndpointConfig crdb; private static Endpoint restEndpoint; private static Supplier credentialsSupplier; private static final String certificateAlias = "redis-enterprise"; private static final char[] trustStorePassword = "changeit".toCharArray(); // truststore with certificate from REST API endpoint private static final String trustStorePfx = LagAwareStrategySslIT.class.getSimpleName(); private static final Path crdbTrustStore = Paths.get(trustStorePfx + "-crdb.jks"); // empty truststore to test untrusted cert private static final Path dummyTrustStore = Paths.get(trustStorePfx + "-dummy.jks"); private SSLSocketFactory origSslSocketFactory; @BeforeAll public static void beforeClass() { crdb = Endpoints.getRedisEndpoint("re-active-active"); restEndpoint = RestEndpointUtil.getRestAPIEndpoint(crdb); credentialsSupplier = () -> new DefaultRedisCredentials("test@redis.com", "test123"); try { TlsUtil.createEmptyTruststore(dummyTrustStore, trustStorePassword); createTrustedTruststore(crdbTrustStore, restEndpoint.getHost(), restEndpoint.getPort(), certificateAlias, trustStorePassword); } catch (Exception e) { fail("Failed to create test truststore", e); } } @BeforeEach public void enforceEmptyDefaultTrustStore() { TlsUtil.setCustomTrustStore(dummyTrustStore, new String(trustStorePassword)); origSslSocketFactory = HttpsURLConnection.getDefaultSSLSocketFactory(); } @AfterEach public void restoreDefaultTrustStore() { TlsUtil.restoreOriginalTrustStore(); HttpsURLConnection.setDefaultSSLSocketFactory(origSslSocketFactory); } @ParameterizedTest(name = "SSL mode {0} should be HEALTHY") @EnumSource(value = SslVerifyMode.class, names = { "FULL", "CA", "INSECURE" }) public void healthyWhenUsingCustomTruststore(SslVerifyMode sslVerifyMode) throws Exception { // Create SSL options with custom truststore SslOptions sslOptions = SslOptions.builder() .truststore(crdbTrustStore.toFile(), trustStorePassword).sslVerifyMode(sslVerifyMode) .build(); // Create LagAwareStrategy config with SSL support using builder LagAwareStrategy.Config config = LagAwareStrategy.Config .builder(restEndpoint, credentialsSupplier).sslOptions(sslOptions).build(); try (LagAwareStrategy lagAwareStrategy = new LagAwareStrategy(config)) { assertEquals(HealthStatus.HEALTHY, lagAwareStrategy.doHealthCheck(crdb.getHostAndPort())); } } @Test void usingDefaultTruststoreWithUntrustedCertificateInsecure() { // Create SSL options without specifying truststore SslOptions sslOptions = SslOptions.builder().sslVerifyMode(SslVerifyMode.INSECURE).build(); // Create LagAwareStrategy config with SSL support using builder LagAwareStrategy.Config config = LagAwareStrategy.Config .builder(restEndpoint, credentialsSupplier).sslOptions(sslOptions).build(); try (LagAwareStrategy lagAwareStrategy = new LagAwareStrategy(config)) { assertEquals(HealthStatus.HEALTHY, lagAwareStrategy.doHealthCheck(crdb.getHostAndPort())); } } @ParameterizedTest(name = "SSL mode {0} should result in {1}") @CsvSource({ "FULL, UNHEALTHY", "CA, UNHEALTHY" }) void usingDefaultTruststoreWithUntrustedCertificateThrowsException(SslVerifyMode sslVerifyMode, HealthStatus expected) { // Create SSL options without specifying truststore SslOptions sslOptions = SslOptions.builder().sslVerifyMode(sslVerifyMode).build(); // Create LagAwareStrategy config with SSL support using builder LagAwareStrategy.Config config = LagAwareStrategy.Config .builder(restEndpoint, credentialsSupplier).sslOptions(sslOptions).build(); try (LagAwareStrategy lagAwareStrategy = new LagAwareStrategy(config)) { JedisException ex = assertThrows(JedisException.class, () -> { lagAwareStrategy.doHealthCheck(crdb.getHostAndPort()); }); } } @ParameterizedTest(name = "SSL mode {0} should result in {1}") @CsvSource({ "FULL, HEALTHY", "CA, HEALTHY", "INSECURE, HEALTHY" }) void healthyWhenUsingDefaultTruststoreWithTrustedCertificate(SslVerifyMode sslVerifyMode, HealthStatus expected) { // Create SSL options without specifying truststore SslOptions sslOptions = SslOptions.builder().sslVerifyMode(sslVerifyMode).build(); // Create LagAwareStrategy config with SSL support using builder LagAwareStrategy.Config config = LagAwareStrategy.Config .builder(restEndpoint, credentialsSupplier).sslOptions(sslOptions).build(); // Verify configuration - trusted cert should pass // Default SSL context is used if no user defined trust store is configured // Configure default SSL context to trust our custom CA // and reinitialize default SSL context for HttpsURLConnection TlsUtil.setCustomTrustStore(crdbTrustStore, new String(trustStorePassword)); SSLSocketFactory orig = HttpsURLConnection.getDefaultSSLSocketFactory(); try (LagAwareStrategy lagAwareStrategy = new LagAwareStrategy(config)) { HttpsURLConnection .setDefaultSSLSocketFactory((SSLSocketFactory) SSLSocketFactory.getDefault()); assertEquals(expected, lagAwareStrategy.doHealthCheck(crdb.getHostAndPort())); } finally { TlsUtil.restoreOriginalTrustStore(); HttpsURLConnection.setDefaultSSLSocketFactory(orig); } } @Test public void fallbackToDefaultSslContextWhenSslOptionsAreNull() { // Test that SSL options can be null // If SSL options are null, we should fallback to default SSL context LagAwareStrategy.Config config = LagAwareStrategy.Config .builder(restEndpoint, credentialsSupplier).build(); // Verify configuration - untrusted cert should fail TlsUtil.setCustomTrustStore(dummyTrustStore, new String(trustStorePassword)); SSLSocketFactory orig = HttpsURLConnection.getDefaultSSLSocketFactory(); try (LagAwareStrategy lagAwareStrategy = new LagAwareStrategy(config)) { assertThrows(JedisException.class, () -> { lagAwareStrategy.doHealthCheck(crdb.getHostAndPort()); }); } finally { TlsUtil.restoreOriginalTrustStore(); HttpsURLConnection.setDefaultSSLSocketFactory(orig); } // Verify configuration - trusted cert should pass // Default SSL context is used if no SSL options are provided // Configure default SSL context to trust our custom CA // and reinitialize default SSL context for HttpsURLConnection TlsUtil.setCustomTrustStore(crdbTrustStore, new String(trustStorePassword)); orig = HttpsURLConnection.getDefaultSSLSocketFactory(); try (LagAwareStrategy lagAwareStrategy = new LagAwareStrategy(config)) { HttpsURLConnection .setDefaultSSLSocketFactory((SSLSocketFactory) SSLSocketFactory.getDefault()); assertEquals(HealthStatus.HEALTHY, lagAwareStrategy.doHealthCheck(crdb.getHostAndPort())); } finally { TlsUtil.restoreOriginalTrustStore(); HttpsURLConnection.setDefaultSSLSocketFactory(orig); } } /** * Downloads the server certificate from a host and stores it as a JKS file. * @param jks path where the JKS file will be created * @param host host to connect to (e.g., redis.example.com) * @param port TLS port (e.g., 9443) * @param alias alias to store the certificate under * @param password password for the JKS */ static void createTrustedTruststore(Path jks, String host, int port, String alias, char[] password) throws Exception { // Use SSL socket to fetch server cert // Disable certificate verification TrustManager[] trustAll = new TrustManager[] { new X509TrustManager() { public void checkClientTrusted(X509Certificate[] chain, String authType) { } public void checkServerTrusted(X509Certificate[] chain, String authType) { } public X509Certificate[] getAcceptedIssuers() { return new X509Certificate[0]; } } }; SSLContext sslContext = SSLContext.getInstance("TLS"); sslContext.init(null, trustAll, new java.security.SecureRandom()); SSLSocketFactory factory = sslContext.getSocketFactory(); try (SSLSocket socket = (SSLSocket) factory.createSocket(host, port)) { socket.startHandshake(); Certificate[] serverCerts = socket.getSession().getPeerCertificates(); // Create an empty KeyStore KeyStore ks = KeyStore.getInstance("JKS"); ks.load(null, password); // Add server certificate (first in chain) ks.setCertificateEntry(alias, serverCerts[0]); // Write KeyStore to disk try (FileOutputStream fos = new FileOutputStream(jks.toFile())) { ks.store(fos, password); } } } } ================================================ FILE: src/test/java/redis/clients/jedis/scenario/MultiThreadedFakeApp.java ================================================ package redis.clients.jedis.scenario; import io.github.resilience4j.ratelimiter.RateLimiter; import io.github.resilience4j.ratelimiter.RateLimiterConfig; import io.github.resilience4j.ratelimiter.RateLimiterRegistry; import redis.clients.jedis.UnifiedJedis; import redis.clients.jedis.exceptions.JedisConnectionException; import java.time.Duration; import java.util.concurrent.ExecutorService; import java.util.concurrent.LinkedBlockingQueue; import java.util.concurrent.ThreadPoolExecutor; import java.util.concurrent.TimeUnit; public class MultiThreadedFakeApp extends FakeApp { static final int QUEUE_CAPACITY = 100000; private final ExecutorService executorService; private final RateLimiter rateLimiter; public MultiThreadedFakeApp(UnifiedJedis client, FakeApp.ExecutedAction action, int numThreads) { this(client, action, numThreads, null); } public MultiThreadedFakeApp(UnifiedJedis client, FakeApp.ExecutedAction action, int numThreads, RateLimiterConfig config) { super(client, action); this.executorService = new ThreadPoolExecutor( numThreads, numThreads, 0L, TimeUnit.MILLISECONDS, new LinkedBlockingQueue(QUEUE_CAPACITY), new ThreadPoolExecutor.CallerRunsPolicy() ); if (config != null) { this.rateLimiter = RateLimiterRegistry.of(config).rateLimiter("fakeAppLimiter"); } else { this.rateLimiter = null; } } @Override public void run() { log.info("Starting FakeApp"); int checkEachSeconds = 5; int timeoutSeconds = 120; while (actionResponse == null || !actionResponse.isCompleted( Duration.ofSeconds(checkEachSeconds), Duration.ofSeconds(keepExecutingForSeconds), Duration.ofSeconds(timeoutSeconds))) { try { if (rateLimiter != null) { RateLimiter.waitForPermission(rateLimiter); } executorService.submit(() -> action.run(client)); } catch (JedisConnectionException e) { log.error("Error executing action", e); exceptions.add(e); } } executorService.shutdown(); try { if (!executorService.awaitTermination(keepExecutingForSeconds, TimeUnit.SECONDS)) { executorService.shutdownNow(); } } catch (InterruptedException e) { log.error("Error waiting for executor service to terminate", e); } } } ================================================ FILE: src/test/java/redis/clients/jedis/scenario/RecommendedSettings.java ================================================ package redis.clients.jedis.scenario; import redis.clients.jedis.ConnectionPoolConfig; import java.time.Duration; public class RecommendedSettings { public static ConnectionPoolConfig poolConfig; static { poolConfig = new ConnectionPoolConfig(); ConnectionPoolConfig poolConfig = new ConnectionPoolConfig(); poolConfig.setMaxTotal(8); poolConfig.setMaxIdle(8); poolConfig.setMinIdle(0); poolConfig.setBlockWhenExhausted(true); poolConfig.setMaxWait(Duration.ofSeconds(1)); poolConfig.setTestWhileIdle(true); poolConfig.setTimeBetweenEvictionRuns(Duration.ofSeconds(1)); } public static int MAX_RETRIES = 5; public static Duration MAX_TOTAL_RETRIES_DURATION = Duration.ofSeconds(10); public static int DEFAULT_TIMEOUT_MS = 5000; } ================================================ FILE: src/test/java/redis/clients/jedis/scenario/RestEndpointUtil.java ================================================ package redis.clients.jedis.scenario; import redis.clients.jedis.Endpoint; import redis.clients.jedis.EndpointConfig; public class RestEndpointUtil { public static Endpoint getRestAPIEndpoint(EndpointConfig config) { return new Endpoint() { @Override public String getHost() { // convert this to Redis FQDN by removing the node prefix // "dns":"redis-10232.c1.taki-active-active-test-c114170a.cto.redislabs.com" String host = config.getHost(); // trim until the first dot return host.substring(host.indexOf('.') + 1); } @Override public int getPort() { // default port for REST API return 9443; } }; } } ================================================ FILE: src/test/java/redis/clients/jedis/search/hybrid/FTHybridPostProcessingParamsTest.java ================================================ package redis.clients.jedis.search.hybrid; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Nested; import org.junit.jupiter.api.Test; import redis.clients.jedis.CommandArguments; import redis.clients.jedis.args.Rawable; import redis.clients.jedis.args.RawableFactory; import redis.clients.jedis.search.Apply; import redis.clients.jedis.search.Filter; import redis.clients.jedis.search.Limit; import redis.clients.jedis.search.SearchProtocol; import redis.clients.jedis.search.aggr.Group; import redis.clients.jedis.search.aggr.Reducers; import redis.clients.jedis.search.aggr.SortedField; import java.util.Iterator; import static org.junit.jupiter.api.Assertions.*; import static redis.clients.jedis.search.SearchProtocol.SearchKeyword.LOAD; public class FTHybridPostProcessingParamsTest { private FTHybridPostProcessingParams.Builder builder; @BeforeEach void setUp() { builder = FTHybridPostProcessingParams.builder(); } @Nested class EqualityAndHashCodeTests { @Test public void equalsWithIdenticalParams() { FTHybridPostProcessingParams firstParam = builder.load("field1", "field2").build(); FTHybridPostProcessingParams secondParam = FTHybridPostProcessingParams.builder() .load("field1", "field2").build(); assertEquals(firstParam, secondParam); } @Test public void hashCodeWithIdenticalParams() { FTHybridPostProcessingParams firstParam = builder.load("field1", "field2").build(); FTHybridPostProcessingParams secondParam = FTHybridPostProcessingParams.builder() .load("field1", "field2").build(); assertEquals(firstParam.hashCode(), secondParam.hashCode()); } @Test public void equalsWithDifferentLoadFields() { FTHybridPostProcessingParams firstParam = builder.load("field1").build(); FTHybridPostProcessingParams secondParam = FTHybridPostProcessingParams.builder() .load("field2").build(); assertNotEquals(firstParam, secondParam); } @Test public void hashCodeWithDifferentLoadFields() { FTHybridPostProcessingParams firstParam = builder.load("field1").build(); FTHybridPostProcessingParams secondParam = FTHybridPostProcessingParams.builder() .load("field2").build(); assertNotEquals(firstParam.hashCode(), secondParam.hashCode()); } @Test public void equalsWithNull() { FTHybridPostProcessingParams firstParam = builder.load("field1").build(); FTHybridPostProcessingParams secondParam = null; assertFalse(firstParam.equals(secondParam)); } @Test public void equalsWithSameInstance() { FTHybridPostProcessingParams param = builder.load("field1").build(); assertTrue(param.equals(param)); } @Test public void equalsLoadAllVsLoadFields() { FTHybridPostProcessingParams firstParam = builder.loadAll().build(); FTHybridPostProcessingParams secondParam = FTHybridPostProcessingParams.builder() .load("field1").build(); assertNotEquals(firstParam, secondParam); } @Test public void equalsWithDifferentLimit() { FTHybridPostProcessingParams firstParam = builder.load("field1").limit(Limit.of(0, 10)) .build(); FTHybridPostProcessingParams secondParam = FTHybridPostProcessingParams.builder() .load("field1").limit(Limit.of(0, 20)).build(); assertNotEquals(firstParam, secondParam); } } @Nested class LoadValidationTests { @Test public void loadNullThrowsException() { IllegalArgumentException exception = assertThrows(IllegalArgumentException.class, () -> builder.load((String[]) null)); assertEquals("Fields must not be null", exception.getMessage()); } @Test public void loadEmptyArrayThrowsException() { IllegalArgumentException exception = assertThrows(IllegalArgumentException.class, () -> builder.load(new String[0])); assertEquals("At least one field is required", exception.getMessage()); } @Test public void loadWithWildcardThrowsException() { IllegalArgumentException exception = assertThrows(IllegalArgumentException.class, () -> builder.load("*")); assertEquals("Cannot use '*' in load(). Use loadAll() instead to load all fields.", exception.getMessage()); } @Test public void loadWithWildcardMixedWithFieldsThrowsException() { IllegalArgumentException exception = assertThrows(IllegalArgumentException.class, () -> builder.load("field1", "*", "field2")); assertEquals("Cannot use '*' in load(). Use loadAll() instead to load all fields.", exception.getMessage()); } @Test public void loadWithNullFieldThrowsException() { IllegalArgumentException exception = assertThrows(IllegalArgumentException.class, () -> builder.load("field1", null, "field2")); assertEquals("Field names cannot be null", exception.getMessage()); } @Test public void loadAllDoesNotThrow() { assertDoesNotThrow(() -> builder.loadAll()); } @Test public void loadWithValidFieldsDoesNotThrow() { assertDoesNotThrow(() -> builder.load("field1", "field2", "field3")); } } @Nested class BuilderTests { @Test public void lastLoadCallWins() { FTHybridPostProcessingParams firstParam = builder.load("field1").load("field2").build(); FTHybridPostProcessingParams secondParam = FTHybridPostProcessingParams.builder() .load("field2").build(); assertEquals(firstParam, secondParam); } @Test public void loadAllOverridesLoad() { FTHybridPostProcessingParams firstParam = builder.load("field1", "field2").loadAll().build(); FTHybridPostProcessingParams secondParam = FTHybridPostProcessingParams.builder().loadAll() .build(); assertEquals(firstParam, secondParam); } @Test public void loadOverridesLoadAll() { FTHybridPostProcessingParams firstParam = builder.loadAll().load("field1").build(); FTHybridPostProcessingParams secondParam = FTHybridPostProcessingParams.builder() .load("field1").build(); assertEquals(firstParam, secondParam); } @Test public void equalsWithSameFieldsDifferentOrder() { FTHybridPostProcessingParams firstParam = builder.load("field1", "field2").build(); FTHybridPostProcessingParams secondParam = FTHybridPostProcessingParams.builder() .load("field2", "field1").build(); assertNotEquals(firstParam, secondParam); } @Test public void loadWithAtPrefixPreserved() { FTHybridPostProcessingParams params = builder.load("@field1", "field2").build(); CommandArguments args = new CommandArguments(SearchProtocol.SearchCommand.HYBRID); params.addParams(args); // Both should have @ prefix in the output Iterator iter = args.iterator(); assertEquals(SearchProtocol.SearchCommand.HYBRID, iter.next()); assertEquals(LOAD, iter.next()); assertEquals(RawableFactory.from(2), iter.next()); assertEquals(RawableFactory.from("@field1"), iter.next()); assertEquals(RawableFactory.from("@field2"), iter.next()); } } @Nested class SortByTests { @Test public void sortByWithSingleField() { FTHybridPostProcessingParams params = builder.sortBy(SortedField.asc("@price")).build(); CommandArguments args = new CommandArguments(SearchProtocol.SearchCommand.HYBRID); params.addParams(args); // Expected: FT.HYBRID SORTBY 2 @price ASC Iterator iter = args.iterator(); assertEquals(SearchProtocol.SearchCommand.HYBRID, iter.next()); assertEquals(SearchProtocol.SearchKeyword.SORTBY, iter.next()); assertEquals(RawableFactory.from(2), iter.next()); // 1 field * 2 = 2 assertEquals(RawableFactory.from("@price"), iter.next()); assertEquals(RawableFactory.from("ASC"), iter.next()); } @Test public void sortByWithMultipleFields() { FTHybridPostProcessingParams params = builder .sortBy(SortedField.asc("@price"), SortedField.desc("@rating"), SortedField.asc("@brand")) .build(); CommandArguments args = new CommandArguments(SearchProtocol.SearchCommand.HYBRID); params.addParams(args); // Expected: FT.HYBRID SORTBY 6 @price ASC @rating DESC @brand ASC Iterator iter = args.iterator(); assertEquals(SearchProtocol.SearchCommand.HYBRID, iter.next()); assertEquals(SearchProtocol.SearchKeyword.SORTBY, iter.next()); assertEquals(RawableFactory.from(6), iter.next()); // 3 fields * 2 = 6 assertEquals(RawableFactory.from("@price"), iter.next()); assertEquals(RawableFactory.from("ASC"), iter.next()); assertEquals(RawableFactory.from("@rating"), iter.next()); assertEquals(RawableFactory.from("DESC"), iter.next()); assertEquals(RawableFactory.from("@brand"), iter.next()); assertEquals(RawableFactory.from("ASC"), iter.next()); } @Test public void sortByWithLoadAndLimit() { FTHybridPostProcessingParams params = builder.load("price", "rating") .sortBy(SortedField.desc("@price")).limit(Limit.of(0, 10)).build(); CommandArguments args = new CommandArguments(SearchProtocol.SearchCommand.HYBRID); params.addParams(args); // Expected: FT.HYBRID LOAD 2 @price @rating SORTBY 2 @price DESC LIMIT 0 10 Iterator iter = args.iterator(); assertEquals(SearchProtocol.SearchCommand.HYBRID, iter.next()); assertEquals(LOAD, iter.next()); assertEquals(RawableFactory.from(2), iter.next()); assertEquals(RawableFactory.from("@price"), iter.next()); assertEquals(RawableFactory.from("@rating"), iter.next()); assertEquals(SearchProtocol.SearchKeyword.SORTBY, iter.next()); assertEquals(RawableFactory.from(2), iter.next()); assertEquals(RawableFactory.from("@price"), iter.next()); assertEquals(RawableFactory.from("DESC"), iter.next()); assertEquals(SearchProtocol.SearchKeyword.LIMIT, iter.next()); assertEquals(RawableFactory.from(0), iter.next()); assertEquals(RawableFactory.from(10), iter.next()); } @Test public void lastSortByCallWins() { // When sortBy is called multiple times, the last call should win FTHybridPostProcessingParams params = builder.sortBy(SortedField.asc("@price")) .sortBy(SortedField.desc("@rating")).build(); CommandArguments args = new CommandArguments(SearchProtocol.SearchCommand.HYBRID); params.addParams(args); // Expected: FT.HYBRID SORTBY 2 @rating DESC (only the last sortBy) Iterator iter = args.iterator(); assertEquals(SearchProtocol.SearchCommand.HYBRID, iter.next()); assertEquals(SearchProtocol.SearchKeyword.SORTBY, iter.next()); assertEquals(RawableFactory.from(2), iter.next()); assertEquals(RawableFactory.from("@rating"), iter.next()); assertEquals(RawableFactory.from("DESC"), iter.next()); } @Test public void lastSortByNoSortCallWins() { // When both sortBy and noSort are set, noSort should take precedence FTHybridPostProcessingParams params = builder.sortBy(SortedField.asc("@price")).noSort() .build(); CommandArguments args = new CommandArguments(SearchProtocol.SearchCommand.HYBRID); params.addParams(args); // Expected: FT.HYBRID NOSORT (sortBy should be ignored) Iterator iter = args.iterator(); assertEquals(SearchProtocol.SearchCommand.HYBRID, iter.next()); assertEquals(SearchProtocol.SearchKeyword.NOSORT, iter.next()); assertFalse(iter.hasNext()); } @Test public void lastNoSortSortByCallWins() { // When both sortBy and noSort are set, noSort should take precedence FTHybridPostProcessingParams params = builder.noSort().sortBy(SortedField.asc("@price")) .build(); CommandArguments args = new CommandArguments(SearchProtocol.SearchCommand.HYBRID); params.addParams(args); // Expected: FT.HYBRID NOSORT (sortBy should be ignored) Iterator iter = args.iterator(); assertEquals(SearchProtocol.SearchCommand.HYBRID, iter.next()); assertEquals(SearchProtocol.SearchKeyword.SORTBY, iter.next()); assertEquals(RawableFactory.from(2), iter.next()); assertEquals(RawableFactory.from("@price"), iter.next()); assertEquals(RawableFactory.from("ASC"), iter.next()); assertFalse(iter.hasNext()); } @Test public void sortByWithEmptyArrayThrowsException() { IllegalArgumentException exception = assertThrows(IllegalArgumentException.class, () -> { builder.sortBy((SortedField[]) null); }); assertEquals("Sort by fields must not be null", exception.getMessage()); } } @Nested class AddParamsTests { @Test public void addParamsWithLoadSpecificFields() { FTHybridPostProcessingParams params = builder.load("field1", "field2").build(); CommandArguments args = new CommandArguments(SearchProtocol.SearchCommand.HYBRID); params.addParams(args); // Expected: FT.HYBRID LOAD 2 @field1 @field2 Iterator iter = args.iterator(); assertEquals(SearchProtocol.SearchCommand.HYBRID, iter.next()); assertEquals(LOAD, iter.next()); assertEquals(RawableFactory.from(2), iter.next()); assertEquals(RawableFactory.from("@field1"), iter.next()); assertEquals(RawableFactory.from("@field2"), iter.next()); } @Test public void addParamsWithLoadAll() { FTHybridPostProcessingParams params = builder.loadAll().build(); CommandArguments args = new CommandArguments(SearchProtocol.SearchCommand.HYBRID); params.addParams(args); // Expected: FT.HYBRID LOAD * Iterator iter = args.iterator(); assertEquals(SearchProtocol.SearchCommand.HYBRID, iter.next()); assertEquals(LOAD, iter.next()); assertEquals(RawableFactory.from("*"), iter.next()); } @Test public void addParamsWithNoLoad() { FTHybridPostProcessingParams params = builder.limit(Limit.of(0, 10)).build(); CommandArguments args = new CommandArguments(SearchProtocol.SearchCommand.HYBRID); params.addParams(args); // Expected: FT.HYBRID LIMIT 0 10 (no LOAD) Iterator iter = args.iterator(); assertEquals(SearchProtocol.SearchCommand.HYBRID, iter.next()); assertEquals(SearchProtocol.SearchKeyword.LIMIT, iter.next()); assertEquals(RawableFactory.from(0), iter.next()); assertEquals(RawableFactory.from(10), iter.next()); } @Test public void addParamsWithLoadAndGroupBy() { FTHybridPostProcessingParams params = builder.load("brand", "price") .groupBy(new Group("@brand").reduce(Reducers.count().as("count"))).build(); CommandArguments args = new CommandArguments(SearchProtocol.SearchCommand.HYBRID); params.addParams(args); // Expected: FT.HYBRID LOAD 2 @brand @price GROUPBY 1 @brand REDUCE COUNT 0 AS count Iterator iter = args.iterator(); assertEquals(SearchProtocol.SearchCommand.HYBRID, iter.next()); assertEquals(LOAD, iter.next()); assertEquals(RawableFactory.from(2), iter.next()); assertEquals(RawableFactory.from("@brand"), iter.next()); assertEquals(RawableFactory.from("@price"), iter.next()); assertEquals(SearchProtocol.SearchKeyword.GROUPBY, iter.next()); // Continue with GROUPBY assertions... } @Test public void addParamsWithLoadAndApply() { FTHybridPostProcessingParams params = builder.load("price") .apply(Apply.of("@price * 0.9", "discounted")).build(); CommandArguments args = new CommandArguments(SearchProtocol.SearchCommand.HYBRID); params.addParams(args); // Expected: FT.HYBRID LOAD 1 @price APPLY @price * 0.9 AS discounted Iterator iter = args.iterator(); assertEquals(SearchProtocol.SearchCommand.HYBRID, iter.next()); assertEquals(LOAD, iter.next()); assertEquals(RawableFactory.from(1), iter.next()); assertEquals(RawableFactory.from("@price"), iter.next()); assertEquals(SearchProtocol.SearchKeyword.APPLY, iter.next()); // Continue with APPLY assertions... } @Test public void addParamsWithLoadAndSortBy() { FTHybridPostProcessingParams params = builder.load("price", "rating") .sortBy(SortedField.asc("@price")).build(); CommandArguments args = new CommandArguments(SearchProtocol.SearchCommand.HYBRID); params.addParams(args); // Expected: FT.HYBRID LOAD 2 @price @rating SORTBY 2 @price ASC Iterator iter = args.iterator(); assertEquals(SearchProtocol.SearchCommand.HYBRID, iter.next()); assertEquals(LOAD, iter.next()); assertEquals(RawableFactory.from(2), iter.next()); assertEquals(RawableFactory.from("@price"), iter.next()); assertEquals(RawableFactory.from("@rating"), iter.next()); assertEquals(SearchProtocol.SearchKeyword.SORTBY, iter.next()); // Continue with SORTBY assertions... } @Test public void addParamsWithLoadAndNoSort() { FTHybridPostProcessingParams params = builder.load("field1").noSort().build(); CommandArguments args = new CommandArguments(SearchProtocol.SearchCommand.HYBRID); params.addParams(args); // Expected: FT.HYBRID LOAD 1 @field1 NOSORT Iterator iter = args.iterator(); assertEquals(SearchProtocol.SearchCommand.HYBRID, iter.next()); assertEquals(LOAD, iter.next()); assertEquals(RawableFactory.from(1), iter.next()); assertEquals(RawableFactory.from("@field1"), iter.next()); assertEquals(SearchProtocol.SearchKeyword.NOSORT, iter.next()); } @Test public void addParamsWithLoadAndFilter() { FTHybridPostProcessingParams params = builder.load("price").filter(Filter.of("@price > 100")) .build(); CommandArguments args = new CommandArguments(SearchProtocol.SearchCommand.HYBRID); params.addParams(args); // Expected: FT.HYBRID LOAD 1 @price FILTER @price > 100 Iterator iter = args.iterator(); assertEquals(SearchProtocol.SearchCommand.HYBRID, iter.next()); assertEquals(LOAD, iter.next()); assertEquals(RawableFactory.from(1), iter.next()); assertEquals(RawableFactory.from("@price"), iter.next()); assertEquals(SearchProtocol.SearchKeyword.FILTER, iter.next()); // Continue with FILTER assertions... } @Test public void addParamsWithLoadAndLimit() { FTHybridPostProcessingParams params = builder.load("field1", "field2").limit(Limit.of(10, 20)) .build(); CommandArguments args = new CommandArguments(SearchProtocol.SearchCommand.HYBRID); params.addParams(args); // Expected: FT.HYBRID LOAD 2 @field1 @field2 LIMIT 10 20 Iterator iter = args.iterator(); assertEquals(SearchProtocol.SearchCommand.HYBRID, iter.next()); assertEquals(LOAD, iter.next()); assertEquals(RawableFactory.from(2), iter.next()); assertEquals(RawableFactory.from("@field1"), iter.next()); assertEquals(RawableFactory.from("@field2"), iter.next()); assertEquals(SearchProtocol.SearchKeyword.LIMIT, iter.next()); assertEquals(RawableFactory.from(10), iter.next()); assertEquals(RawableFactory.from(20), iter.next()); } @Test public void addParamsFieldWithoutAtPrefixGetsPrefix() { FTHybridPostProcessingParams params = builder.load("field1").build(); CommandArguments args = new CommandArguments(SearchProtocol.SearchCommand.HYBRID); params.addParams(args); // Field without @ should get @ prefix Iterator iter = args.iterator(); assertEquals(SearchProtocol.SearchCommand.HYBRID, iter.next()); assertEquals(LOAD, iter.next()); assertEquals(RawableFactory.from(1), iter.next()); assertEquals(RawableFactory.from("@field1"), iter.next()); } @Test public void addParamsFieldWithAtPrefixNotDuplicated() { FTHybridPostProcessingParams params = builder.load("@field1").build(); CommandArguments args = new CommandArguments(SearchProtocol.SearchCommand.HYBRID); params.addParams(args); // Field with @ should not get another @ prefix Iterator iter = args.iterator(); assertEquals(SearchProtocol.SearchCommand.HYBRID, iter.next()); assertEquals(LOAD, iter.next()); assertEquals(RawableFactory.from(1), iter.next()); assertEquals(RawableFactory.from("@field1"), iter.next()); } } } ================================================ FILE: src/test/java/redis/clients/jedis/tls/ACLJedisIT.java ================================================ package redis.clients.jedis.tls; import static org.junit.jupiter.api.Assertions.assertEquals; import org.junit.jupiter.api.Test; import redis.clients.jedis.DefaultJedisClientConfig; import redis.clients.jedis.Jedis; /** * SSL/TLS tests for {@link Jedis} with ACL authentication (username + password). *

* This test class focuses on testing the ssl(true) flag approach (using system truststore) with ACL * credentials. */ public class ACLJedisIT extends JedisTlsTestBase { /** * Tests SSL connection with explicit ACL credentials (username + password). */ @Test public void connectWithSsl() { try (Jedis jedis = new Jedis(aclEndpoint.getHost(), aclEndpoint.getPort(), true)) { jedis.auth(aclEndpoint.getUsername(), aclEndpoint.getPassword()); assertEquals("PONG", jedis.ping()); } } /** * Tests SSL connection using DefaultJedisClientConfig with ACL credentials. */ @Test public void connectWithConfig() { try (Jedis jedis = new Jedis(aclEndpoint.getHostAndPort(), DefaultJedisClientConfig.builder().ssl(true).build())) { jedis.auth(aclEndpoint.getUsername(), aclEndpoint.getPassword()); assertEquals("PONG", jedis.ping()); } } /** * Tests SSL connection using URL with credentials. */ @Test public void connectWithUrl() { // The "rediss" scheme instructs jedis to open a SSL/TLS connection. // Test with default user endpoint try ( Jedis jedis = new Jedis(endpoint.getURIBuilder().defaultCredentials().build().toString())) { assertEquals("PONG", jedis.ping()); } // Test with ACL user endpoint try (Jedis jedis = new Jedis( aclEndpoint.getURIBuilder().defaultCredentials().build().toString())) { assertEquals("PONG", jedis.ping()); } } /** * Tests SSL connection using URI with credentials. */ @Test public void connectWithUri() { // The "rediss" scheme instructs jedis to open a SSL/TLS connection. // Test with default user endpoint try (Jedis jedis = new Jedis(endpoint.getURIBuilder().defaultCredentials().build())) { assertEquals("PONG", jedis.ping()); } // Test with ACL user endpoint try (Jedis jedis = new Jedis(aclEndpoint.getURIBuilder().defaultCredentials().build())) { assertEquals("PONG", jedis.ping()); } } } ================================================ FILE: src/test/java/redis/clients/jedis/tls/ACLRedisClientIT.java ================================================ package redis.clients.jedis.tls; import static org.junit.jupiter.api.Assertions.assertEquals; import org.junit.jupiter.api.Test; import redis.clients.jedis.DefaultJedisClientConfig; import redis.clients.jedis.RedisClient; /** * SSL/TLS tests for {@link RedisClient} with ACL authentication (username + password). *

* This test class focuses on testing the ssl(true) flag approach (using system truststore) with ACL * credentials. */ public class ACLRedisClientIT extends RedisClientTlsTestBase { /** * Tests SSL connection with explicit ACL credentials (username + password). */ @Test public void connectWithSsl() { try ( RedisClient client = RedisClient.builder() .hostAndPort(aclEndpoint.getHost(), aclEndpoint.getPort()) .clientConfig(DefaultJedisClientConfig.builder().ssl(true) .user(aclEndpoint.getUsername()).password(aclEndpoint.getPassword()).build()) .build()) { assertEquals("PONG", client.ping()); } } /** * Tests SSL connection using endpoint's client config builder (credentials from endpoint). */ @Test public void connectWithConfig() { try ( RedisClient client = RedisClient.builder().hostAndPort(aclEndpoint.getHostAndPort()) .clientConfig(DefaultJedisClientConfig.builder().ssl(true) .user(aclEndpoint.getUsername()).password(aclEndpoint.getPassword()).build()) .build()) { assertEquals("PONG", client.ping()); } } /** * Tests SSL connection using URL with credentials. */ @Test public void connectWithUrl() { // The "rediss" scheme instructs jedis to open a SSL/TLS connection. // Test with default user endpoint try (RedisClient client = RedisClient .create(endpoint.getURIBuilder().defaultCredentials().build().toString())) { assertEquals("PONG", client.ping()); } // Test with ACL user endpoint try (RedisClient client = RedisClient .create(aclEndpoint.getURIBuilder().defaultCredentials().build().toString())) { assertEquals("PONG", client.ping()); } } /** * Tests SSL connection using URI with credentials. */ @Test public void connectWithUri() { // The "rediss" scheme instructs jedis to open a SSL/TLS connection. // Test with default user endpoint try (RedisClient client = RedisClient .create(endpoint.getURIBuilder().defaultCredentials().build())) { assertEquals("PONG", client.ping()); } // Test with ACL user endpoint try (RedisClient client = RedisClient .create(aclEndpoint.getURIBuilder().defaultCredentials().build())) { assertEquals("PONG", client.ping()); } } } ================================================ FILE: src/test/java/redis/clients/jedis/tls/ACLRedisSentinelClientIT.java ================================================ package redis.clients.jedis.tls; import static org.junit.jupiter.api.Assertions.assertEquals; import io.redis.test.annotations.ConditionalOnEnv; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.RegisterExtension; import redis.clients.jedis.DefaultJedisClientConfig; import redis.clients.jedis.EndpointConfig; import redis.clients.jedis.Endpoints; import redis.clients.jedis.RedisSentinelClient; import redis.clients.jedis.util.EnvCondition; import redis.clients.jedis.util.RedisVersionCondition; import redis.clients.jedis.util.TestEnvUtil; /** * SSL/TLS tests for {@link RedisSentinelClient} with ACL authentication (username + password). *

* This test class focuses on testing the ssl(true) flag approach (using system truststore) rather * than explicit SslOptions configuration. */ @ConditionalOnEnv(value = TestEnvUtil.ENV_OSS_SOURCE, enabled = false) public class ACLRedisSentinelClientIT extends RedisSentinelTlsTestBase { // Endpoint for master with ACL authentication private static EndpointConfig aclEndpoint; @RegisterExtension public static EnvCondition envCondition = new EnvCondition(); @RegisterExtension public static RedisVersionCondition versionCondition = new RedisVersionCondition( () -> Endpoints.getRedisEndpoint("standalone0-acl-tls")); @BeforeAll public static void setUp() { aclEndpoint = Endpoints.getRedisEndpoint("standalone0-acl-tls"); } /** * Tests SSL connection with explicit ACL credentials (username + password). */ @Test public void connectWithSsl() { DefaultJedisClientConfig masterConfig = DefaultJedisClientConfig.builder() .clientName("master-client").ssl(true).user(aclEndpoint.getUsername()) .password(aclEndpoint.getPassword()).hostAndPortMapper(PRIMARY_SSL_PORT_MAPPER).build(); DefaultJedisClientConfig sentinelConfig = Endpoints.getRedisEndpoint("sentinel-standalone0-tls") .getClientConfigBuilder().clientName("sentinel-client").ssl(true) .hostAndPortMapper(SENTINEL_SSL_PORT_MAPPER).build(); try (RedisSentinelClient client = RedisSentinelClient.builder().masterName(MASTER_NAME) .sentinels(sentinels).clientConfig(masterConfig).sentinelClientConfig(sentinelConfig) .build()) { assertEquals("PONG", client.ping()); } } /** * Tests SSL connection using endpoint's client config builder (credentials from endpoint). */ @Test public void connectWithConfig() { DefaultJedisClientConfig masterConfig = aclEndpoint.getClientConfigBuilder() .clientName("master-client").ssl(true).hostAndPortMapper(PRIMARY_SSL_PORT_MAPPER).build(); DefaultJedisClientConfig sentinelConfig = Endpoints.getRedisEndpoint("sentinel-standalone0-tls") .getClientConfigBuilder().clientName("sentinel-client").ssl(true) .hostAndPortMapper(SENTINEL_SSL_PORT_MAPPER).build(); try (RedisSentinelClient client = RedisSentinelClient.builder().masterName(MASTER_NAME) .sentinels(sentinels).clientConfig(masterConfig).sentinelClientConfig(sentinelConfig) .build()) { assertEquals("PONG", client.ping()); } } /** * Tests SSL connection using SslOptions with truststore configuration. */ @Test public void connectWithSslOptions() { DefaultJedisClientConfig masterConfig = aclEndpoint.getClientConfigBuilder() .clientName("master-client").sslOptions(sslOptions) .hostAndPortMapper(PRIMARY_SSL_PORT_MAPPER).build(); DefaultJedisClientConfig sentinelConfig = createSentinelConfigWithSsl(sslOptions); try (RedisSentinelClient client = RedisSentinelClient.builder().masterName(MASTER_NAME) .sentinels(sentinels).clientConfig(masterConfig).sentinelClientConfig(sentinelConfig) .build()) { assertEquals("PONG", client.ping()); } } } ================================================ FILE: src/test/java/redis/clients/jedis/tls/ClientAuthIT.java ================================================ package redis.clients.jedis.tls; import static org.junit.jupiter.api.Assertions.assertEquals; import org.junit.jupiter.api.Test; import redis.clients.jedis.BuilderFactory; import redis.clients.jedis.CommandArguments; import redis.clients.jedis.CommandObject; import redis.clients.jedis.Protocol; import redis.clients.jedis.SslOptions; import redis.clients.jedis.UnifiedJedis; /** * Abstract integration test class for mTLS (mutual TLS) certificate-based authentication. *

* Defines common test methods that verify client certificate authentication works correctly across * different Redis deployment types (standalone, cluster). *

* Subclasses must implement factory methods to create environment-specific clients and execute * commands. */ public abstract class ClientAuthIT extends ClientAuthTestBase { /** * Creates a client with the specified SSL options. *

* Subclasses provide environment-specific implementations (e.g., RedisClient for standalone, * RedisClusterClient for cluster). * @param sslOptions SSL configuration for mTLS * @return UnifiedJedis client configured with mTLS */ protected abstract UnifiedJedis createClient(SslOptions sslOptions); /** * Executes ACL WHOAMI command and returns the authenticated username. *

* @param client the connected client * @return the authenticated username */ private String aclWhoAmI(UnifiedJedis client) { return client.executeCommand(new CommandObject<>( new CommandArguments(Protocol.Command.ACL).add("WHOAMI"), BuilderFactory.STRING)); } /** * Tests mTLS connection with mtls-user1 certificate. *

* Verifies that ACL WHOAMI returns the expected username based on Redis version. */ @Test public void connectWithMtlsUser1() { SslOptions sslOptions = createMtlsSslOptionsUser1(); try (UnifiedJedis client = createClient(sslOptions)) { assertEquals("PONG", client.ping()); assertExpectedUsername(client, aclWhoAmI(client), MTLS_USER_1); } } /** * Tests mTLS connection with mtls-user2 certificate. *

* Verifies that a different certificate authenticates as a different user. */ @Test public void connectWithMtlsUser2() { SslOptions sslOptions = createMtlsSslOptionsUser2(); try (UnifiedJedis client = createClient(sslOptions)) { assertEquals("PONG", client.ping()); assertExpectedUsername(client, aclWhoAmI(client), MTLS_USER_2); } } /** * Tests mTLS connection with mtls-user-without-acl certificate. *

* Verifies that when using a certificate for a user without a corresponding ACL user configured * in Redis, the connection succeeds and ACL WHOAMI returns "default". */ @Test public void connectWithMtlsUserWithoutAcl() { SslOptions sslOptions = createMtlsSslOptionsUserWithoutAcl(); try (UnifiedJedis client = createClient(sslOptions)) { assertEquals("PONG", client.ping()); assertEquals("default", aclWhoAmI(client)); } } /** * Tests that mTLS authenticated users can perform basic Redis operations. */ @Test public void performBasicOperationsWithMtls() { SslOptions sslOptions = createMtlsSslOptionsUser1(); try (UnifiedJedis client = createClient(sslOptions)) { String key = "mtls-test-key"; String value = "mtls-test-value"; assertEquals("OK", client.set(key, value)); assertEquals(value, client.get(key)); assertEquals(1, client.del(key)); } } } ================================================ FILE: src/test/java/redis/clients/jedis/tls/ClientAuthJedisIT.java ================================================ package redis.clients.jedis.tls; import static org.junit.jupiter.api.Assertions.assertEquals; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.Test; import redis.clients.jedis.DefaultJedisClientConfig; import redis.clients.jedis.Endpoints; import redis.clients.jedis.Jedis; import redis.clients.jedis.SslOptions; /** * Integration tests for mTLS (mutual TLS) certificate-based authentication with Jedis. *

* These tests verify that: - Client certificate authentication works correctly with Jedis - The * authenticated user matches the certificate CN (Redis 8.6+) or is "default" (older versions) - * Different client certificates authenticate as different users */ public class ClientAuthJedisIT extends ClientAuthTestBase { @BeforeAll public static void setUpStandaloneMtlsStores() { endpoint = Endpoints.getRedisEndpoint("standalone-mtls"); setUpMtlsStoresForEndpoint(endpoint, ClientAuthJedisIT.class.getSimpleName()); } /** * Tests mTLS connection with mtls-user1 certificate using Jedis. Verifies that ACL WHOAMI returns * the expected username based on Redis version. */ @Test public void connectWithMtlsUser1() { SslOptions sslOptions = createMtlsSslOptionsUser1(); try (Jedis jedis = new Jedis(endpoint.getHostAndPort(), DefaultJedisClientConfig.builder().sslOptions(sslOptions).build())) { assertEquals("PONG", jedis.ping()); // Verify username based on Redis version assertExpectedUsername(jedis, jedis.aclWhoAmI(), MTLS_USER_1); } } /** * Tests mTLS connection with mtls-user2 certificate using Jedis. Verifies that a different * certificate authenticates as a different user. */ @Test public void connectWithMtlsUser2() { SslOptions sslOptions = createMtlsSslOptionsUser2(); try (Jedis jedis = new Jedis(endpoint.getHostAndPort(), DefaultJedisClientConfig.builder().sslOptions(sslOptions).build())) { assertEquals("PONG", jedis.ping()); // Verify username based on Redis version assertExpectedUsername(jedis, jedis.aclWhoAmI(), MTLS_USER_2); } } /** * Tests mTLS connection with mtls-user-without-acl certificate using Jedis. Verifies that when * using a certificate for a user without a corresponding ACL user configured in Redis, the * connection succeeds and ACL WHOAMI returns "default". */ @Test public void connectWithMtlsUserWithoutAcl() { SslOptions sslOptions = createMtlsSslOptionsUserWithoutAcl(); try (Jedis jedis = new Jedis(endpoint.getHostAndPort(), DefaultJedisClientConfig.builder().sslOptions(sslOptions).build())) { assertEquals("PONG", jedis.ping()); assertEquals("default", jedis.aclWhoAmI()); } } /** * Tests mTLS connection using host and port separately. */ @Test public void connectWithHostAndPort() { SslOptions sslOptions = createMtlsSslOptionsUser1(); try (Jedis jedis = new Jedis(endpoint.getHost(), endpoint.getPort(), DefaultJedisClientConfig.builder().sslOptions(sslOptions).build())) { assertEquals("PONG", jedis.ping()); assertExpectedUsername(jedis, jedis.aclWhoAmI(), MTLS_USER_1); } } /** * Tests that mTLS authenticated users can perform basic Redis operations with Jedis. */ @Test public void performBasicOperationsWithMtls() { SslOptions sslOptions = createMtlsSslOptionsUser1(); try (Jedis jedis = new Jedis(endpoint.getHostAndPort(), DefaultJedisClientConfig.builder().sslOptions(sslOptions).build())) { // Test basic operations String key = "mtls-jedis-test-key"; String value = "mtls-jedis-test-value"; assertEquals("OK", jedis.set(key, value)); assertEquals(value, jedis.get(key)); assertEquals(1, jedis.del(key)); } } } ================================================ FILE: src/test/java/redis/clients/jedis/tls/ClientAuthRedisClientIT.java ================================================ package redis.clients.jedis.tls; import org.junit.jupiter.api.BeforeAll; import redis.clients.jedis.*; /** * Integration tests for mTLS (mutual TLS) certificate-based authentication with standalone Redis. *

* Extends {@link ClientAuthIT} to provide standalone-specific client creation and command * execution. */ public class ClientAuthRedisClientIT extends ClientAuthIT { @BeforeAll public static void setUpStandaloneMtlsStores() { endpoint = Endpoints.getRedisEndpoint("standalone-mtls"); setUpMtlsStoresForEndpoint(endpoint, ClientAuthRedisClientIT.class.getSimpleName()); } @Override protected UnifiedJedis createClient(SslOptions sslOptions) { return RedisClient.builder().hostAndPort(endpoint.getHostAndPort()) .clientConfig(DefaultJedisClientConfig.builder().sslOptions(sslOptions).build()).build(); } } ================================================ FILE: src/test/java/redis/clients/jedis/tls/ClientAuthRedisClusterClientIT.java ================================================ package redis.clients.jedis.tls; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertTrue; import java.util.Collections; import java.util.HashSet; import java.util.Map; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.Test; import redis.clients.jedis.*; /** * Integration tests for mTLS (mutual TLS) certificate-based authentication with Redis Cluster. *

* Extends {@link ClientAuthIT} to provide cluster-specific client creation and command execution. * Also includes cluster-specific tests like node discovery. */ public class ClientAuthRedisClusterClientIT extends ClientAuthIT { private static final int DEFAULT_REDIRECTIONS = 5; private static final ConnectionPoolConfig DEFAULT_POOL_CONFIG = new ConnectionPoolConfig(); @BeforeAll public static void setUpClusterMtlsStores() { endpoint = Endpoints.getRedisEndpoint("cluster-mtls"); setUpMtlsStoresForEndpoint(endpoint, ClientAuthRedisClusterClientIT.class.getSimpleName()); } @Override protected UnifiedJedis createClient(SslOptions sslOptions) { return RedisClusterClient.builder().nodes(new HashSet<>(endpoint.getHostsAndPorts())) .clientConfig(DefaultJedisClientConfig.builder().sslOptions(sslOptions).build()) .maxAttempts(DEFAULT_REDIRECTIONS).poolConfig(DEFAULT_POOL_CONFIG).build(); } /** * Cluster-specific test: Verifies that cluster node discovery works with mTLS. */ @Test public void discoverClusterNodesWithMtls() { SslOptions sslOptions = createMtlsSslOptionsUser1(); try (RedisClusterClient cluster = RedisClusterClient.builder() .nodes(Collections.singleton(endpoint.getHostAndPort())) .clientConfig(DefaultJedisClientConfig.builder().sslOptions(sslOptions).build()) .maxAttempts(DEFAULT_REDIRECTIONS).poolConfig(DEFAULT_POOL_CONFIG).build()) { Map clusterNodes = cluster.getClusterNodes(); // Should discover all 3 cluster nodes assertEquals(3, clusterNodes.size()); assertTrue(clusterNodes.containsKey(endpoint.getHostAndPort(0).toString())); assertTrue(clusterNodes.containsKey(endpoint.getHostAndPort(1).toString())); assertTrue(clusterNodes.containsKey(endpoint.getHostAndPort(2).toString())); } } } ================================================ FILE: src/test/java/redis/clients/jedis/tls/ClientAuthTestBase.java ================================================ package redis.clients.jedis.tls; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertTrue; import java.nio.file.Path; import java.util.Arrays; import java.util.Collections; import java.util.List; import io.redis.test.annotations.ConditionalOnEnv; import io.redis.test.utils.RedisVersion; import org.junit.jupiter.api.AfterAll; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.extension.RegisterExtension; import redis.clients.jedis.EndpointConfig; import redis.clients.jedis.Endpoints; import redis.clients.jedis.SslOptions; import redis.clients.jedis.SslVerifyMode; import redis.clients.jedis.UnifiedJedis; import redis.clients.jedis.util.EnvCondition; import redis.clients.jedis.util.RedisVersionUtil; import redis.clients.jedis.util.TestEnvUtil; import redis.clients.jedis.util.TlsUtil; /** * Abstract base class for mTLS (mutual TLS) authentication tests. *

* This class provides common setup for tests that verify certificate-based client authentication. * It configures both truststore (for server verification) and keystore (for client authentication). *

* The mTLS setup requires: - A truststore containing the CA certificate to verify the Redis server * - A keystore containing the client certificate and private key for client authentication */ @ConditionalOnEnv(value = TestEnvUtil.ENV_OSS_SOURCE, enabled = false) public abstract class ClientAuthTestBase { private static final String TRUSTSTORE_PASSWORD = "changeit"; private static final String KEYSTORE_PASSWORD = "changeit"; /** Default mTLS user for testing */ protected static final String MTLS_USER_1 = "mtls-user1"; protected static final String MTLS_USER_2 = "mtls-user2"; protected static final String MTLS_USER_WITHOUT_ACL = "mtls-user-without-acl"; @RegisterExtension public static EnvCondition envCondition = new EnvCondition(); protected static EndpointConfig endpoint; protected static Path trustStorePath; protected static Path keyStorePath1; protected static Path keyStorePath2; protected static Path keyStorePathUserWithoutAcl; /** * Sets up mTLS stores for a specific targetEndpioint. Should be called by subclasses in * their @BeforeAll method. * @param targetEndpioint the targetEndpioint to configure mTLS for * @param testClassName the test class name for truststore naming */ protected static void setUpMtlsStoresForEndpoint(EndpointConfig targetEndpioint, String testClassName) { // Create truststore with CA certificate for server verification List trustedCertLocation = Collections .singletonList(targetEndpioint.getCertificatesLocation()); trustStorePath = TlsUtil.createAndSaveTestTruststore(testClassName, trustedCertLocation, TRUSTSTORE_PASSWORD); TlsUtil.setCustomTrustStore(trustStorePath, TRUSTSTORE_PASSWORD); // Use pre-generated PKCS12 keystores from Docker container // The container generates .p12 files with password "changeit" for each TLS_CLIENT_CNS entry Path certLocation = targetEndpioint.getCertificatesLocation(); keyStorePath1 = TlsUtil.clientKeystorePath(certLocation, MTLS_USER_1); keyStorePath2 = TlsUtil.clientKeystorePath(certLocation, MTLS_USER_2); keyStorePathUserWithoutAcl = TlsUtil.clientKeystorePath(certLocation, MTLS_USER_WITHOUT_ACL); } @AfterAll public static void tearDownMtlsStores() { TlsUtil.restoreOriginalTrustStore(); } /** * Creates SslOptions configured for mTLS with the specified client keystore. * @param keystorePath path to the client keystore * @return SslOptions configured for mTLS */ protected static SslOptions createMtlsSslOptions(Path keystorePath) { return SslOptions.builder().truststore(trustStorePath.toFile()).trustStoreType("jceks") .keystore(keystorePath.toFile(), KEYSTORE_PASSWORD.toCharArray()).keyStoreType("PKCS12") .sslVerifyMode(SslVerifyMode.FULL).build(); } /** * Creates SslOptions for mtls-user1. */ protected static SslOptions createMtlsSslOptionsUser1() { return createMtlsSslOptions(keyStorePath1); } /** * Creates SslOptions for mtls-user2. */ protected static SslOptions createMtlsSslOptionsUser2() { return createMtlsSslOptions(keyStorePath2); } /** * Creates SslOptions for mtls-user-without-acl. */ protected static SslOptions createMtlsSslOptionsUserWithoutAcl() { return createMtlsSslOptions(keyStorePathUserWithoutAcl); } /** * Asserts the expected username based on Redis version. *

* Redis 8.6+ supports automatic certificate-based authentication via tls-auth-clients-user CN, * where the username is extracted from the client certificate's Common Name. For Redis versions * below 8.6, the user will be "default" since cert-based auth is not supported. * @param jedis the connected Jedis client to check version * @param actualUsername the actual username from ACL WHOAMI * @param expectedCertUser the expected username from client certificate CN */ protected static void assertExpectedUsername(redis.clients.jedis.Jedis jedis, String actualUsername, String expectedCertUser) { RedisVersion version = RedisVersionUtil.getRedisVersion(jedis); assertUsernameForVersion(version, actualUsername, expectedCertUser); } /** * Asserts the expected username based on Redis version for UnifiedJedis clients (RedisClient, * RedisClusterClient, etc.). * @param jedis the connected UnifiedJedis client to check version * @param actualUsername the actual username from ACL WHOAMI * @param expectedCertUser the expected username from client certificate CN */ protected static void assertExpectedUsername(UnifiedJedis jedis, String actualUsername, String expectedCertUser) { RedisVersion version = RedisVersionUtil.getRedisVersion(jedis); assertUsernameForVersion(version, actualUsername, expectedCertUser); } private static void assertUsernameForVersion(RedisVersion version, String actualUsername, String expectedCertUser) { if (version.isGreaterThanOrEqualTo(RedisVersion.V8_6_0)) { assertEquals(expectedCertUser, actualUsername, "Redis " + version + " supports cert-based auth, expected username from certificate CN"); } else { List allowedUsers = Arrays.asList("default", expectedCertUser); assertTrue(allowedUsers.contains(actualUsername), "Redis " + version + " does not support cert-based auth, expected 'default' or '" + expectedCertUser + "' but was '" + actualUsername + "'"); } } } ================================================ FILE: src/test/java/redis/clients/jedis/tls/JedisIT.java ================================================ package redis.clients.jedis.tls; import static org.junit.jupiter.api.Assertions.assertEquals; import org.junit.jupiter.api.Test; import redis.clients.jedis.DefaultJedisClientConfig; import redis.clients.jedis.Jedis; import redis.clients.jedis.JedisClientConfig; /** * SSL/TLS tests for {@link Jedis} with basic authentication (password-only, no ACL). *

* Uses the system truststore (ssl=true flag) for SSL connections. */ public class JedisIT extends JedisTlsTestBase { @Test public void connectWithSsl() { try (Jedis jedis = new Jedis(endpoint.getHost(), endpoint.getPort(), true)) { jedis.auth(endpoint.getPassword()); assertEquals("PONG", jedis.ping()); } } @Test public void connectWithConfig() { try (Jedis jedis = new Jedis(endpoint.getHostAndPort(), DefaultJedisClientConfig.builder().ssl(true).build())) { jedis.auth(endpoint.getPassword()); assertEquals("PONG", jedis.ping()); } } @Test public void connectWithConfigInterface() { try (Jedis jedis = new Jedis(endpoint.getHostAndPort(), new JedisClientConfig() { @Override public boolean isSsl() { return true; } })) { jedis.auth(endpoint.getPassword()); assertEquals("PONG", jedis.ping()); } } /** * Tests opening a default SSL/TLS connection to redis using "rediss://" scheme url. */ @Test public void connectWithUrl() { // The "rediss" scheme instructs jedis to open a SSL/TLS connection. try (Jedis jedis = new Jedis(endpoint.getURI().toString())) { jedis.auth(endpoint.getPassword()); assertEquals("PONG", jedis.ping()); } } /** * Tests opening a default SSL/TLS connection to redis. */ @Test public void connectWithUri() { // The "rediss" scheme instructs jedis to open a SSL/TLS connection. try (Jedis jedis = new Jedis(endpoint.getURI())) { jedis.auth(endpoint.getPassword()); assertEquals("PONG", jedis.ping()); } } } ================================================ FILE: src/test/java/redis/clients/jedis/tls/JedisSentinelPoolIT.java ================================================ package redis.clients.jedis.tls; import org.apache.commons.pool2.impl.GenericObjectPoolConfig; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.Test; import redis.clients.jedis.DefaultJedisClientConfig; import redis.clients.jedis.EndpointConfig; import redis.clients.jedis.Endpoints; import redis.clients.jedis.Jedis; import redis.clients.jedis.JedisSentinelPool; /** * SSL/TLS tests for {@link JedisSentinelPool} using system truststore (ssl=true flag). *

* Tests various combinations of SSL on master and sentinel connections: *

    *
  • Sentinel without SSL, Redis master with SSL
  • *
  • Sentinel with SSL, Redis master without SSL
  • *
  • Both sentinel and Redis master with SSL
  • *
*/ public class JedisSentinelPoolIT extends RedisSentinelTlsTestBase { private static final GenericObjectPoolConfig POOL_CONFIG = new GenericObjectPoolConfig<>(); // Endpoints for different SSL configurations private static EndpointConfig aclTlsEndpoint; private static EndpointConfig aclEndpoint; private static EndpointConfig sentinelTlsEndpoint; @BeforeAll public static void setUp() { aclTlsEndpoint = Endpoints.getRedisEndpoint("standalone0-acl-tls"); aclEndpoint = Endpoints.getRedisEndpoint("standalone0-acl"); sentinelTlsEndpoint = Endpoints.getRedisEndpoint("sentinel-standalone0-tls"); } /** * Tests sentinel without SSL connecting to Redis master with SSL. */ @Test public void sentinelWithoutSslConnectsToRedisWithSsl() { DefaultJedisClientConfig masterConfig = aclTlsEndpoint.getClientConfigBuilder() .clientName("master-client").hostAndPortMapper(PRIMARY_SSL_PORT_MAPPER).build(); DefaultJedisClientConfig sentinelConfig = createSentinelConfigWithoutSsl(); try (JedisSentinelPool pool = new JedisSentinelPool(MASTER_NAME, sentinels, masterConfig, sentinelConfig)) { pool.getResource().close(); } try (JedisSentinelPool pool = new JedisSentinelPool(MASTER_NAME, sentinels, POOL_CONFIG, masterConfig, sentinelConfig)) { pool.getResource().close(); } } /** * Tests sentinel with SSL connecting to Redis master without SSL. */ @Test public void sentinelWithSslConnectsToRedisWithoutSsl() { DefaultJedisClientConfig masterConfig = aclEndpoint.getClientConfigBuilder() .clientName("master-client").build(); DefaultJedisClientConfig sentinelConfig = sentinelTlsEndpoint.getClientConfigBuilder() .clientName("sentinel-client").hostAndPortMapper(SENTINEL_SSL_PORT_MAPPER).build(); try (JedisSentinelPool pool = new JedisSentinelPool(MASTER_NAME, sentinels, masterConfig, sentinelConfig)) { pool.getResource().close(); } try (JedisSentinelPool pool = new JedisSentinelPool(MASTER_NAME, sentinels, POOL_CONFIG, masterConfig, sentinelConfig)) { pool.getResource().close(); } } /** * Tests both sentinel and Redis master with SSL. */ @Test public void sentinelWithSslConnectsToRedisWithSsl() { DefaultJedisClientConfig masterConfig = aclTlsEndpoint.getClientConfigBuilder() .clientName("master-client").hostAndPortMapper(PRIMARY_SSL_PORT_MAPPER).build(); DefaultJedisClientConfig sentinelConfig = sentinelTlsEndpoint.getClientConfigBuilder() .clientName("sentinel-client").hostAndPortMapper(SENTINEL_SSL_PORT_MAPPER).build(); try (JedisSentinelPool pool = new JedisSentinelPool(MASTER_NAME, sentinels, masterConfig, sentinelConfig)) { pool.getResource().close(); } try (JedisSentinelPool pool = new JedisSentinelPool(MASTER_NAME, sentinels, POOL_CONFIG, masterConfig, sentinelConfig)) { pool.getResource().close(); } } } ================================================ FILE: src/test/java/redis/clients/jedis/tls/JedisTlsTestBase.java ================================================ package redis.clients.jedis.tls; import java.nio.file.Path; import java.util.Arrays; import java.util.List; import java.util.stream.Stream; import org.junit.jupiter.api.AfterAll; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.params.provider.Arguments; import redis.clients.jedis.EndpointConfig; import redis.clients.jedis.Endpoints; import redis.clients.jedis.SslOptions; import redis.clients.jedis.SslVerifyMode; import redis.clients.jedis.util.TlsUtil; /** * Abstract base class for SSL/TLS tests for {@link redis.clients.jedis.Jedis}. */ public abstract class JedisTlsTestBase { private static final String TRUSTSTORE_PASSWORD = "changeit"; protected static EndpointConfig endpoint; protected static EndpointConfig aclEndpoint; protected static Path trustStorePath; protected static SslOptions sslOptions; @BeforeAll public static void setUpTrustStore() { endpoint = Endpoints.getRedisEndpoint("standalone0-tls"); aclEndpoint = Endpoints.getRedisEndpoint("standalone0-acl-tls"); List trustedCertLocation = Arrays.asList(endpoint.getCertificatesLocation(), aclEndpoint.getCertificatesLocation()); trustStorePath = TlsUtil.createAndSaveTestTruststore(JedisTlsTestBase.class.getSimpleName(), trustedCertLocation, TRUSTSTORE_PASSWORD); TlsUtil.setCustomTrustStore(trustStorePath, TRUSTSTORE_PASSWORD); sslOptions = createTruststoreSslOptions(); } @AfterAll public static void tearDownTrustStore() { TlsUtil.restoreOriginalTrustStore(); } protected static SslOptions createTruststoreSslOptions() { return SslOptions.builder().truststore(trustStorePath.toFile()).trustStoreType("jceks") .sslVerifyMode(SslVerifyMode.CA).build(); } protected static Stream sslOptionsProvider() { return Stream.of(Arguments.of("truststore", createTruststoreSslOptions()), Arguments.of("insecure", SslOptions.builder().sslVerifyMode(SslVerifyMode.INSECURE).build()), Arguments.of("ssl-protocol", SslOptions.builder().sslProtocol("SSL").truststore(trustStorePath.toFile()) .trustStoreType("jceks").sslVerifyMode(SslVerifyMode.CA).build())); } } ================================================ FILE: src/test/java/redis/clients/jedis/tls/RedisClientIT.java ================================================ package redis.clients.jedis.tls; import static org.junit.jupiter.api.Assertions.assertEquals; import org.junit.jupiter.api.Test; import redis.clients.jedis.DefaultJedisClientConfig; import redis.clients.jedis.RedisClient; /** * SSL/TLS tests for {@link RedisClient} with basic authentication (password-only, no ACL). *

* Uses the system truststore (ssl=true flag) for SSL connections. */ public class RedisClientIT extends RedisClientTlsTestBase { @Test public void connectWithSsl() { try (RedisClient client = RedisClient.builder() .hostAndPort(endpoint.getHost(), endpoint.getPort()) .clientConfig( DefaultJedisClientConfig.builder().ssl(true).password(endpoint.getPassword()).build()) .build()) { assertEquals("PONG", client.ping()); } } @Test public void connectWithConfig() { try (RedisClient client = RedisClient.builder().hostAndPort(endpoint.getHostAndPort()) .clientConfig( DefaultJedisClientConfig.builder().ssl(true).password(endpoint.getPassword()).build()) .build()) { assertEquals("PONG", client.ping()); } } /** * Tests opening a default SSL/TLS connection to redis using "rediss://" scheme url. */ @Test public void connectWithUrl() { // The "rediss" scheme instructs jedis to open a SSL/TLS connection. // URI includes credentials via defaultCredentials() try (RedisClient client = RedisClient .create(endpoint.getURIBuilder().defaultCredentials().build().toString())) { assertEquals("PONG", client.ping()); } } /** * Tests opening a default SSL/TLS connection to redis. */ @Test public void connectWithUri() { // The "rediss" scheme instructs jedis to open a SSL/TLS connection. // URI includes credentials via defaultCredentials() try (RedisClient client = RedisClient .create(endpoint.getURIBuilder().defaultCredentials().build())) { assertEquals("PONG", client.ping()); } } } ================================================ FILE: src/test/java/redis/clients/jedis/tls/RedisClientTlsTestBase.java ================================================ package redis.clients.jedis.tls; import java.nio.file.Path; import java.util.Arrays; import java.util.List; import java.util.stream.Stream; import org.junit.jupiter.api.AfterAll; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.params.provider.Arguments; import redis.clients.jedis.EndpointConfig; import redis.clients.jedis.Endpoints; import redis.clients.jedis.SslOptions; import redis.clients.jedis.SslVerifyMode; import redis.clients.jedis.util.TlsUtil; /** * Abstract base class for SSL/TLS tests for {@link redis.clients.jedis.RedisClient}. */ public abstract class RedisClientTlsTestBase { private static final String TRUSTSTORE_PASSWORD = "changeit"; protected static EndpointConfig endpoint; protected static EndpointConfig aclEndpoint; protected static Path trustStorePath; protected static SslOptions sslOptions; @BeforeAll public static void setUpTrustStore() { endpoint = Endpoints.getRedisEndpoint("standalone0-tls"); aclEndpoint = Endpoints.getRedisEndpoint("standalone0-acl-tls"); List trustedCertLocation = Arrays.asList(endpoint.getCertificatesLocation(), aclEndpoint.getCertificatesLocation()); trustStorePath = TlsUtil.createAndSaveTestTruststore( RedisClientTlsTestBase.class.getSimpleName(), trustedCertLocation, TRUSTSTORE_PASSWORD); TlsUtil.setCustomTrustStore(trustStorePath, TRUSTSTORE_PASSWORD); sslOptions = createTruststoreSslOptions(); } @AfterAll public static void tearDownTrustStore() { TlsUtil.restoreOriginalTrustStore(); } protected static SslOptions createTruststoreSslOptions() { return SslOptions.builder().truststore(trustStorePath.toFile()).trustStoreType("jceks") .sslVerifyMode(SslVerifyMode.CA).build(); } protected static Stream sslOptionsProvider() { return Stream.of(Arguments.of("truststore", createTruststoreSslOptions()), Arguments.of("insecure", SslOptions.builder().sslVerifyMode(SslVerifyMode.INSECURE).build()), Arguments.of("ssl-protocol", SslOptions.builder().sslProtocol("SSL").truststore(trustStorePath.toFile()) .trustStoreType("jceks").sslVerifyMode(SslVerifyMode.CA).build())); } } ================================================ FILE: src/test/java/redis/clients/jedis/tls/RedisClusterClientIT.java ================================================ package redis.clients.jedis.tls; import static org.junit.jupiter.api.Assertions.*; import static redis.clients.jedis.util.TlsUtil.*; import java.util.Collections; import java.util.Map; import java.util.stream.Stream; import javax.net.ssl.HostnameVerifier; import javax.net.ssl.SSLParameters; import org.junit.jupiter.api.Test; import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.Arguments; import org.junit.jupiter.params.provider.MethodSource; import redis.clients.jedis.*; import redis.clients.jedis.util.TlsUtil; import redis.clients.jedis.exceptions.JedisClusterOperationException; public class RedisClusterClientIT extends RedisClusterTestBase { private static final int DEFAULT_REDIRECTIONS = 5; private static final ConnectionPoolConfig DEFAULT_POOL_CONFIG = new ConnectionPoolConfig(); /** * Provides different SslOptions configurations for parametrized tests. */ protected static Stream sslOptionsProvider() { return Stream.of(Arguments.of("truststore", createSslOptions()), Arguments.of("insecure", SslOptions.builder().sslVerifyMode(SslVerifyMode.INSECURE).build()), Arguments.of("ssl-protocol", SslOptions.builder().sslProtocol("SSL").truststore(trustStorePath.toFile()) .trustStoreType("jceks").sslVerifyMode(SslVerifyMode.CA).build())); } /** * Tests SSL discover nodes with various SSL configurations. */ @ParameterizedTest(name = "testSSLDiscoverNodesAutomatically_{0}") @MethodSource("sslOptionsProvider") void testSSLDiscoverNodesAutomatically(String testName, SslOptions ssl) { try (RedisClusterClient jc = RedisClusterClient.builder() .nodes(Collections.singleton(tlsEndpoint.getHostAndPort())) .clientConfig(DefaultJedisClientConfig.builder().password(tlsEndpoint.getPassword()) .sslOptions(ssl).build()) .maxAttempts(DEFAULT_REDIRECTIONS).poolConfig(DEFAULT_POOL_CONFIG).build()) { Map clusterNodes = jc.getClusterNodes(); assertTrue(clusterNodes.containsKey(tlsEndpoint.getHostAndPort(0).toString())); assertTrue(clusterNodes.containsKey(tlsEndpoint.getHostAndPort(1).toString())); assertTrue(clusterNodes.containsKey(tlsEndpoint.getHostAndPort(2).toString())); assertEquals("PONG", jc.ping()); } } /** * Tests that connecting with ssl=true flag (system truststore) works. */ @Test void connectWithSslFlag() { try (RedisClusterClient jc = RedisClusterClient.builder() .nodes(Collections.singleton(tlsEndpoint.getHostAndPort())) .clientConfig( DefaultJedisClientConfig.builder().password(tlsEndpoint.getPassword()).ssl(true).build()) .maxAttempts(DEFAULT_REDIRECTIONS).poolConfig(DEFAULT_POOL_CONFIG).build()) { assertEquals("PONG", jc.ping()); } } /** * Tests that connecting to nodes succeeds with SSL parameters and hostname verification. */ @ParameterizedTest(name = "connectToNodesSucceedsWithSSLParametersAndHostMapping_{0}") @MethodSource("sslOptionsProvider") void connectToNodesSucceedsWithSSLParametersAndHostMapping(String testName, SslOptions ssl) { final SSLParameters sslParameters = new SSLParameters(); sslParameters.setEndpointIdentificationAlgorithm("HTTPS"); try (RedisClusterClient jc = RedisClusterClient.builder() .nodes(Collections.singleton(tlsEndpoint.getHostAndPort())) .clientConfig(DefaultJedisClientConfig.builder().password(tlsEndpoint.getPassword()) .sslOptions(ssl).sslParameters(sslParameters).build()) .maxAttempts(DEFAULT_REDIRECTIONS).poolConfig(DEFAULT_POOL_CONFIG).build()) { assertEquals("PONG", jc.ping()); } } /** * Tests connecting with custom hostname verifier and SslOptions (from * SSLOptionsRedisClusterClientIT). */ @Test public void connectWithCustomHostNameVerifierAndSslOptions() { HostnameVerifier hostnameVerifier = new TlsUtil.BasicHostnameVerifier(); SslOptions sslOptions = SslOptions.builder().truststore(trustStorePath.toFile()) .trustStoreType("jceks").sslVerifyMode(SslVerifyMode.CA).build(); try (RedisClusterClient jc = RedisClusterClient.builder() .nodes(Collections.singleton(tlsEndpoint.getHostAndPort())) .clientConfig(DefaultJedisClientConfig.builder().password(tlsEndpoint.getPassword()) .sslOptions(sslOptions).hostnameVerifier(hostnameVerifier).build()) .maxAttempts(DEFAULT_REDIRECTIONS).poolConfig(DEFAULT_POOL_CONFIG).build()) { assertEquals("PONG", jc.ping()); } } /** * Tests connecting with custom SSL socket factory. */ @Test void connectWithCustomSocketFactory() { try (RedisClusterClient jc = RedisClusterClient.builder() .nodes(Collections.singleton(tlsEndpoint.getHostAndPort())) .clientConfig( DefaultJedisClientConfig.builder().password(tlsEndpoint.getPassword()).ssl(true) .sslSocketFactory(sslSocketFactoryForEnv(tlsEndpoint.getCertificatesLocation())) .build()) .maxAttempts(DEFAULT_REDIRECTIONS).poolConfig(DEFAULT_POOL_CONFIG).build()) { assertEquals("PONG", jc.ping()); } } /** * Tests that connecting with an empty trust store fails. */ @Test void connectWithEmptyTrustStore() throws Exception { try (RedisClusterClient jc = RedisClusterClient.builder() .nodes(Collections.singleton(tlsEndpoint.getHostAndPort())) .clientConfig(DefaultJedisClientConfig.builder().password(tlsEndpoint.getPassword()) .ssl(true).sslSocketFactory(createTrustNoOneSslSocketFactory()).build()) .maxAttempts(DEFAULT_REDIRECTIONS).poolConfig(DEFAULT_POOL_CONFIG).build()) { jc.get("foo"); fail("Should have thrown an exception"); } catch (JedisClusterOperationException e) { assertEquals("Could not initialize cluster slots cache.", e.getMessage()); } } } ================================================ FILE: src/test/java/redis/clients/jedis/tls/RedisClusterTestBase.java ================================================ package redis.clients.jedis.tls; import java.nio.file.Path; import java.util.Collections; import java.util.HashSet; import java.util.List; import io.redis.test.annotations.ConditionalOnEnv; import org.junit.jupiter.api.AfterAll; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.extension.RegisterExtension; import redis.clients.jedis.*; import redis.clients.jedis.util.*; /** * Abstract base class for SSL/TLS Redis cluster tests. *

* This class provides common setup and teardown for TLS-enabled Redis cluster tests, including * truststore initialization and cluster client configuration. *

* Uses the {@code cluster-stable-tls} endpoint for stable integration tests. *

* Note: The {@link RedisVersionCondition} and {@link EnabledOnCommandCondition} extensions use the * non-TLS {@code cluster-stable} endpoint for version/command checks because JUnit 5 extensions run * before {@code @BeforeAll} methods where the truststore is configured. */ @ConditionalOnEnv(value = TestEnvUtil.ENV_OSS_SOURCE, enabled = false) public abstract class RedisClusterTestBase { private static final String ENDPOINT_NAME = "cluster-stable-tls"; /** * Non-TLS endpoint used for version and command checks. Extensions run before @BeforeAll, so we * can't use TLS endpoints for these checks since the truststore isn't configured yet. */ private static final String VERSION_CHECK_ENDPOINT_NAME = "cluster-stable"; private static final String TRUSTSTORE_PASSWORD = "changeit"; @RegisterExtension public static EnvCondition envCondition = new EnvCondition(); @RegisterExtension public EnabledOnCommandCondition enabledOnCommandCondition = new EnabledOnCommandCondition( () -> Endpoints.getRedisEndpoint(VERSION_CHECK_ENDPOINT_NAME)); protected static EndpointConfig tlsEndpoint; protected static Path trustStorePath; protected RedisClusterClient cluster; /** * HostAndPortMapper that only maps localhost, leaving IP addresses unchanged. Useful for testing * hostname verification failures. */ protected final HostAndPortMapper portMap = (HostAndPort hostAndPort) -> { if ("localhost".equals(hostAndPort.getHost())) { return hostAndPort; } return new HostAndPort(hostAndPort.getHost(), hostAndPort.getPort()); }; @BeforeAll public static void prepareEndpointAndTrustStore() { tlsEndpoint = Endpoints.getRedisEndpoint(ENDPOINT_NAME); List trustedCertLocation = Collections .singletonList(tlsEndpoint.getCertificatesLocation()); trustStorePath = TlsUtil.createAndSaveTestTruststore(RedisClusterTestBase.class.getSimpleName(), trustedCertLocation, TRUSTSTORE_PASSWORD); TlsUtil.setCustomTrustStore(trustStorePath, TRUSTSTORE_PASSWORD); } @AfterAll public static void teardownTrustStore() { TlsUtil.restoreOriginalTrustStore(); } @BeforeEach public void setUp() { SslOptions sslOptions = SslOptions.builder().truststore(trustStorePath.toFile()) .trustStoreType("jceks").sslVerifyMode(SslVerifyMode.CA).build(); cluster = RedisClusterClient.builder().nodes(new HashSet<>(tlsEndpoint.getHostsAndPorts())) .clientConfig(DefaultJedisClientConfig.builder().password(tlsEndpoint.getPassword()) .sslOptions(sslOptions).build()) .build(); cluster.flushAll(); } @AfterEach public void tearDown() { if (cluster != null) { cluster.flushAll(); cluster.close(); } } protected static SslOptions createSslOptions() { return SslOptions.builder().truststore(trustStorePath.toFile()).trustStoreType("jceks").build(); } } ================================================ FILE: src/test/java/redis/clients/jedis/tls/RedisSentinelClientIT.java ================================================ package redis.clients.jedis.tls; import static org.junit.jupiter.api.Assertions.assertEquals; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.MethodSource; import redis.clients.jedis.DefaultJedisClientConfig; import redis.clients.jedis.EndpointConfig; import redis.clients.jedis.Endpoints; import redis.clients.jedis.RedisSentinelClient; import redis.clients.jedis.SslOptions; /** * SSL/TLS tests for {@link RedisSentinelClient} with basic authentication (password-only, no ACL). *

* Tests various SSL/TLS connection configurations including: *

    *
  • Basic SSL connection with truststore
  • *
  • Insecure SSL mode (no certificate verification)
  • *
  • Custom SSL protocol
  • *
*

* This test class uses the default user with password authentication instead of ACL user. The * sentinel connection does not use SSL, only the master connection uses SSL. */ public class RedisSentinelClientIT extends RedisSentinelTlsTestBase { // Endpoint for master with default user (password-only, no ACL) private static EndpointConfig masterEndpoint; @BeforeAll public static void setUp() { masterEndpoint = Endpoints.getRedisEndpoint("standalone0-tls"); } @ParameterizedTest(name = "connectWithSsl_{0}") @MethodSource("sslOptionsProvider") void connectWithSsl(String testName, SslOptions ssl) { DefaultJedisClientConfig masterConfig = DefaultJedisClientConfig.builder() .clientName("master-client").sslOptions(ssl).password(masterEndpoint.getPassword()) .hostAndPortMapper(PRIMARY_SSL_PORT_MAPPER).build(); // Sentinel requires authentication but does not use SSL DefaultJedisClientConfig sentinelConfig = createSentinelConfigWithoutSsl(); try (RedisSentinelClient client = RedisSentinelClient.builder().masterName(MASTER_NAME) .sentinels(sentinels).clientConfig(masterConfig).sentinelClientConfig(sentinelConfig) .build()) { assertEquals("PONG", client.ping()); } } } ================================================ FILE: src/test/java/redis/clients/jedis/tls/RedisSentinelTlsTestBase.java ================================================ package redis.clients.jedis.tls; import java.nio.file.Path; import java.nio.file.Paths; import java.util.Collections; import java.util.HashSet; import java.util.List; import java.util.Set; import java.util.stream.Stream; import org.junit.jupiter.api.AfterAll; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.params.provider.Arguments; import redis.clients.jedis.DefaultJedisClientConfig; import redis.clients.jedis.EndpointConfig; import redis.clients.jedis.Endpoints; import redis.clients.jedis.HostAndPort; import redis.clients.jedis.HostAndPortMapper; import redis.clients.jedis.SslOptions; import redis.clients.jedis.SslVerifyMode; import redis.clients.jedis.util.TlsUtil; /** * Abstract base class for Redis Sentinel TLS integration tests. */ public abstract class RedisSentinelTlsTestBase { protected static final String MASTER_NAME = "aclmaster"; private static final String TRUSTSTORE_PASSWORD = "changeit"; private static final String TRUSTSTORE_TYPE = "jceks"; protected static EndpointConfig sentinel; protected static Set sentinels = new HashSet<>(); protected static Path trustStorePath; protected static SslOptions sslOptions; protected static final HostAndPortMapper SENTINEL_SSL_PORT_MAPPER = ( HostAndPort hap) -> new HostAndPort(hap.getHost(), hap.getPort() + 10000); protected static final HostAndPortMapper PRIMARY_SSL_PORT_MAPPER = ( HostAndPort hap) -> new HostAndPort(hap.getHost(), hap.getPort() + 11); @BeforeAll public static void setupSentinelTls() { sentinel = Endpoints.getRedisEndpoint("sentinel-standalone0"); sentinels.add(sentinel.getHostAndPort()); List trustedCertLocation = Collections .singletonList(Paths.get("redis1-2-5-8-sentinel/work/tls")); trustStorePath = TlsUtil.createAndSaveTestTruststore( RedisSentinelTlsTestBase.class.getSimpleName(), trustedCertLocation, TRUSTSTORE_PASSWORD); sslOptions = createTruststoreSslOptions(); TlsUtil.setCustomTrustStore(trustStorePath, TRUSTSTORE_PASSWORD); } @AfterAll public static void teardownTrustStore() { TlsUtil.restoreOriginalTrustStore(); } protected static SslOptions createTruststoreSslOptions() { return SslOptions.builder().truststore(trustStorePath.toFile()).trustStoreType(TRUSTSTORE_TYPE) .sslVerifyMode(SslVerifyMode.CA).build(); } protected static DefaultJedisClientConfig createSentinelConfigWithSsl(SslOptions ssl) { return Endpoints.getRedisEndpoint("sentinel-standalone0-tls").getClientConfigBuilder() .clientName("sentinel-client").sslOptions(ssl).hostAndPortMapper(SENTINEL_SSL_PORT_MAPPER) .build(); } protected static DefaultJedisClientConfig createSentinelConfigWithoutSsl() { return sentinel.getClientConfigBuilder().clientName("sentinel-client").build(); } protected static Stream sslOptionsProvider() { return Stream.of(Arguments.of("truststore", createTruststoreSslOptions()), Arguments.of("insecure", SslOptions.builder().sslVerifyMode(SslVerifyMode.INSECURE).build()), Arguments.of("ssl-protocol", SslOptions.builder().sslProtocol("SSL").truststore(trustStorePath.toFile()) .trustStoreType(TRUSTSTORE_TYPE).sslVerifyMode(SslVerifyMode.CA).build())); } } ================================================ FILE: src/test/java/redis/clients/jedis/tls/SSLOptionsJedisIT.java ================================================ package redis.clients.jedis.tls; import static org.junit.jupiter.api.Assertions.assertEquals; import org.junit.jupiter.api.Test; import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.MethodSource; import redis.clients.jedis.DefaultJedisClientConfig; import redis.clients.jedis.Jedis; import redis.clients.jedis.SslOptions; /** * SSL/TLS tests for {@link Jedis} using SslOptions builder pattern. *

* Tests various SSL/TLS connection configurations including: *

    *
  • Basic SSL connection with truststore
  • *
  • Insecure SSL mode (no certificate verification)
  • *
  • Custom SSL protocol
  • *
  • ACL authentication over SSL
  • *
*/ public class SSLOptionsJedisIT extends JedisTlsTestBase { /** * Tests connecting to Redis with various SSL configurations using DefaultJedisClientConfig. */ @ParameterizedTest(name = "connectWithSsl_{0}") @MethodSource("sslOptionsProvider") void connectWithSsl(String testName, SslOptions ssl) { try (Jedis jedis = new Jedis(endpoint.getHostAndPort(), DefaultJedisClientConfig.builder().sslOptions(ssl).build())) { jedis.auth(endpoint.getPassword()); assertEquals("PONG", jedis.ping()); } } /** * Tests connecting to Redis with various SSL configurations using endpoint's client config. */ @ParameterizedTest(name = "connectWithClientConfig_{0}") @MethodSource("sslOptionsProvider") void connectWithClientConfig(String testName, SslOptions ssl) { try (Jedis jedis = new Jedis(endpoint.getHostAndPort(), endpoint.getClientConfigBuilder().sslOptions(ssl).build())) { assertEquals("PONG", jedis.ping()); } } /** * Tests ACL authentication over SSL. */ @Test public void connectWithAcl() { try (Jedis jedis = new Jedis(aclEndpoint.getHostAndPort(), aclEndpoint.getClientConfigBuilder().sslOptions(sslOptions).build())) { assertEquals("PONG", jedis.ping()); } } } ================================================ FILE: src/test/java/redis/clients/jedis/tls/SSLOptionsJedisSentinelPoolIT.java ================================================ package redis.clients.jedis.tls; import org.apache.commons.pool2.impl.GenericObjectPoolConfig; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.Test; import redis.clients.jedis.DefaultJedisClientConfig; import redis.clients.jedis.EndpointConfig; import redis.clients.jedis.Endpoints; import redis.clients.jedis.Jedis; import redis.clients.jedis.JedisSentinelPool; /** * SSL/TLS tests for {@link JedisSentinelPool} using SslOptions builder pattern. *

* Tests various combinations of SSL on master and sentinel connections: *

    *
  • Sentinel without SSL, Redis master with SSL (using SslOptions)
  • *
  • Sentinel with SSL (using SslOptions), Redis master without SSL
  • *
  • Both sentinel and Redis master with SSL (using SslOptions)
  • *
*/ public class SSLOptionsJedisSentinelPoolIT extends RedisSentinelTlsTestBase { private static final GenericObjectPoolConfig POOL_CONFIG = new GenericObjectPoolConfig<>(); // Endpoints for different SSL configurations private static EndpointConfig aclTlsEndpoint; private static EndpointConfig aclEndpoint; private static EndpointConfig sentinelTlsEndpoint; @BeforeAll public static void setUp() { aclTlsEndpoint = Endpoints.getRedisEndpoint("standalone0-acl-tls"); aclEndpoint = Endpoints.getRedisEndpoint("standalone0-acl"); sentinelTlsEndpoint = Endpoints.getRedisEndpoint("sentinel-standalone0-tls"); } /** * Tests sentinel without SSL connecting to Redis master with SSL using SslOptions. */ @Test public void sentinelWithoutSslConnectsToRedisWithSsl() { DefaultJedisClientConfig masterConfig = aclTlsEndpoint.getClientConfigBuilder() .clientName("master-client").sslOptions(sslOptions) .hostAndPortMapper(PRIMARY_SSL_PORT_MAPPER).build(); DefaultJedisClientConfig sentinelConfig = createSentinelConfigWithoutSsl(); try (JedisSentinelPool pool = new JedisSentinelPool(MASTER_NAME, sentinels, masterConfig, sentinelConfig)) { pool.getResource().close(); } try (JedisSentinelPool pool = new JedisSentinelPool(MASTER_NAME, sentinels, POOL_CONFIG, masterConfig, sentinelConfig)) { pool.getResource().close(); } } /** * Tests sentinel with SSL (using SslOptions) connecting to Redis master without SSL. */ @Test public void sentinelWithSslConnectsToRedisWithoutSsl() { DefaultJedisClientConfig masterConfig = aclEndpoint.getClientConfigBuilder() .clientName("master-client").build(); DefaultJedisClientConfig sentinelConfig = sentinelTlsEndpoint.getClientConfigBuilder() .sslOptions(sslOptions).hostAndPortMapper(SENTINEL_SSL_PORT_MAPPER).build(); try (JedisSentinelPool pool = new JedisSentinelPool(MASTER_NAME, sentinels, masterConfig, sentinelConfig)) { pool.getResource().close(); } try (JedisSentinelPool pool = new JedisSentinelPool(MASTER_NAME, sentinels, POOL_CONFIG, masterConfig, sentinelConfig)) { pool.getResource().close(); } } /** * Tests both sentinel and Redis master with SSL using SslOptions. */ @Test public void sentinelWithSslConnectsToRedisWithSsl() { DefaultJedisClientConfig masterConfig = aclTlsEndpoint.getClientConfigBuilder() .clientName("master-client").sslOptions(sslOptions) .hostAndPortMapper(PRIMARY_SSL_PORT_MAPPER).build(); DefaultJedisClientConfig sentinelConfig = createSentinelConfigWithSsl(sslOptions); try (JedisSentinelPool pool = new JedisSentinelPool(MASTER_NAME, sentinels, masterConfig, sentinelConfig)) { pool.getResource().close(); } try (JedisSentinelPool pool = new JedisSentinelPool(MASTER_NAME, sentinels, POOL_CONFIG, masterConfig, sentinelConfig)) { pool.getResource().close(); } } } ================================================ FILE: src/test/java/redis/clients/jedis/tls/SSLOptionsRedisClientIT.java ================================================ package redis.clients.jedis.tls; import static org.junit.jupiter.api.Assertions.assertEquals; import org.junit.jupiter.api.Test; import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.MethodSource; import redis.clients.jedis.RedisClient; import redis.clients.jedis.SslOptions; /** * SSL/TLS tests for {@link RedisClient} using SslOptions builder pattern. *

* Tests various SSL/TLS connection configurations including: *

    *
  • Basic SSL connection with truststore
  • *
  • Insecure SSL mode (no certificate verification)
  • *
  • Custom SSL protocol
  • *
  • ACL authentication over SSL
  • *
*/ public class SSLOptionsRedisClientIT extends RedisClientTlsTestBase { @ParameterizedTest(name = "connectWithSsl_{0}") @MethodSource("sslOptionsProvider") void connectWithSsl(String testName, SslOptions ssl) { try (RedisClient client = RedisClient.builder().hostAndPort(endpoint.getHostAndPort()) .clientConfig(endpoint.getClientConfigBuilder().sslOptions(ssl).build()).build()) { assertEquals("PONG", client.ping()); } } /** * Tests ACL authentication over SSL. */ @Test public void connectWithAcl() { try (RedisClient client = RedisClient.builder().hostAndPort(aclEndpoint.getHostAndPort()) .clientConfig(aclEndpoint.getClientConfigBuilder().sslOptions(sslOptions).build()) .build()) { assertEquals("PONG", client.ping()); } } } ================================================ FILE: src/test/java/redis/clients/jedis/tls/SSLOptionsRedisSentinelClientIT.java ================================================ package redis.clients.jedis.tls; import static org.junit.jupiter.api.Assertions.assertEquals; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.Test; import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.MethodSource; import redis.clients.jedis.DefaultJedisClientConfig; import redis.clients.jedis.EndpointConfig; import redis.clients.jedis.Endpoints; import redis.clients.jedis.RedisSentinelClient; import redis.clients.jedis.SslOptions; /** * SSL/TLS tests for RedisSentinelClient using SslOptions builder pattern. *

* Tests various SSL/TLS connection configurations including: *

    *
  • Basic SSL connection with truststore
  • *
  • Insecure SSL mode (no certificate verification)
  • *
  • Custom SSL protocol
  • *
  • ACL authentication over SSL
  • *
*

* Both master and sentinel connections use SSL in these tests. */ public class SSLOptionsRedisSentinelClientIT extends RedisSentinelTlsTestBase { // Endpoint for master with ACL authentication private static EndpointConfig aclEndpoint; @BeforeAll public static void setUp() { aclEndpoint = Endpoints.getRedisEndpoint("standalone0-acl-tls"); } /** * Tests connecting to Redis master and sentinel with various SSL configurations. Both master and * sentinel connections use SSL. */ @ParameterizedTest(name = "connectWithSsl_{0}") @MethodSource("sslOptionsProvider") void connectWithSsl(String testName, SslOptions ssl) { DefaultJedisClientConfig masterConfig = aclEndpoint.getClientConfigBuilder() .clientName("master-client").sslOptions(ssl).hostAndPortMapper(PRIMARY_SSL_PORT_MAPPER) .build(); DefaultJedisClientConfig sentinelConfig = createSentinelConfigWithSsl(ssl); try (RedisSentinelClient client = RedisSentinelClient.builder().masterName(MASTER_NAME) .sentinels(sentinels).clientConfig(masterConfig).sentinelClientConfig(sentinelConfig) .build()) { assertEquals("PONG", client.ping()); } } /** * Tests ACL authentication over SSL (same as truststore test but explicitly named). */ @Test public void connectWithAcl() { DefaultJedisClientConfig masterConfig = aclEndpoint.getClientConfigBuilder() .clientName("master-client").sslOptions(sslOptions) .hostAndPortMapper(PRIMARY_SSL_PORT_MAPPER).build(); DefaultJedisClientConfig sentinelConfig = createSentinelConfigWithSsl(sslOptions); try (RedisSentinelClient client = RedisSentinelClient.builder().masterName(MASTER_NAME) .sentinels(sentinels).clientConfig(masterConfig).sentinelClientConfig(sentinelConfig) .build()) { assertEquals("PONG", client.ping()); } } /** * Tests that sentinel without SSL can connect to Redis master with SSL. */ @Test public void sentinelWithoutSslConnectsToRedisWithSsl() { DefaultJedisClientConfig masterConfig = aclEndpoint.getClientConfigBuilder() .clientName("master-client").sslOptions(sslOptions) .hostAndPortMapper(PRIMARY_SSL_PORT_MAPPER).build(); DefaultJedisClientConfig sentinelConfig = createSentinelConfigWithoutSsl(); try (RedisSentinelClient client = RedisSentinelClient.builder().masterName(MASTER_NAME) .sentinels(sentinels).clientConfig(masterConfig).sentinelClientConfig(sentinelConfig) .build()) { assertEquals("PONG", client.ping()); } } } ================================================ FILE: src/test/java/redis/clients/jedis/util/ACLTestUtil.java ================================================ package redis.clients.jedis.util; import java.util.ArrayList; import java.util.List; import java.util.Map; import java.util.stream.Collectors; import redis.clients.jedis.BuilderFactory; import redis.clients.jedis.resps.AccessControlLogEntry; /** * Utility class for ACL testing operations. */ public final class ACLTestUtil { private ACLTestUtil() { throw new InstantiationError("Must not instantiate this class"); } /** * Filters a list of ACL log entries by client ID. * @param entries the list of ACL log entries to filter * @param clientId the client ID to filter by * @return a new list containing only entries matching the specified client ID */ public static List filterByClientId(List entries, long clientId) { String clientIdStr = String.valueOf(clientId); return entries.stream().filter(entry -> { Map clientInfo = entry.getClientInfo(); return clientInfo != null && clientIdStr.equals(clientInfo.get("id")); }).collect(Collectors.toList()); } /** * Filters a list of binary ACL log entries by client ID. *

* This method converts the binary ACL log entries to AccessControlLogEntry objects, filters them * by client ID, and returns the filtered binary entries. * @param binaryEntries the list of binary ACL log entries to filter (raw Redis response) * @param clientId the client ID to filter by * @return a new list containing only binary entries matching the specified client ID */ public static List filterBinaryByClientId(List binaryEntries, long clientId) { if (binaryEntries == null || binaryEntries.isEmpty()) { return new ArrayList<>(); } // Build the structured entries from binary data List entries = BuilderFactory.ACCESS_CONTROL_LOG_ENTRY_LIST .build(binaryEntries); if (entries == null || entries.isEmpty()) { return new ArrayList<>(); } // Filter by client ID String clientIdStr = String.valueOf(clientId); List matchingIndices = new ArrayList<>(); for (int i = 0; i < entries.size(); i++) { AccessControlLogEntry entry = entries.get(i); Map clientInfo = entry.getClientInfo(); if (clientInfo != null && clientIdStr.equals(clientInfo.get("id"))) { matchingIndices.add(i); } } // Return the corresponding binary entries List result = new ArrayList<>(); for (Integer index : matchingIndices) { result.add(binaryEntries.get(index)); } return result; } } ================================================ FILE: src/test/java/redis/clients/jedis/util/AssertUtil.java ================================================ package redis.clients.jedis.util; import static org.junit.jupiter.api.Assertions.assertArrayEquals; import static org.junit.jupiter.api.Assertions.assertEquals; import java.util.Arrays; import java.util.Collection; import java.util.Iterator; import java.util.List; import java.util.Objects; import java.util.Set; import org.opentest4j.AssertionFailedError; import redis.clients.jedis.RedisProtocol; public class AssertUtil { public static void assertOK(String str) { assertEquals("OK", str); } public static void assertEqualsByProtocol(RedisProtocol protocol, Object expectedResp2, Object expectedResp3, Object actual) { if (protocol != RedisProtocol.RESP3) { assertEquals(expectedResp2, actual); } else { assertEquals(expectedResp3, actual); } } public static boolean assertCollectionContains(Collection array, T expected) { for (T element : array) { if (Objects.equals(element, expected)) { return true; } } throw new AssertionFailedError("element is missing", Objects.toString(expected), array.toString()); } public static boolean assertByteArrayCollectionContains(Collection array, byte[] expected) { for (byte[] bytes : array) { if (Arrays.equals(bytes, expected)) { return true; } } throw new AssertionFailedError("element is missing", Arrays.toString(expected), array.toString()); } public static void assertByteArrayListEquals(List expected, List actual) { assertEquals(expected.size(), actual.size()); for (int n = 0; n < expected.size(); n++) { assertArrayEquals(expected.get(n), actual.get(n), n + "'th elements don't match"); } } public static void assertByteArraySetEquals(Set expected, Set actual) { assertEquals(expected.size(), actual.size()); Iterator e = expected.iterator(); while (e.hasNext()) { byte[] next = e.next(); boolean contained = false; for (byte[] element : actual) { if (Arrays.equals(next, element)) { contained = true; break; } } if (!contained) { throw new AssertionFailedError("element is missing", Arrays.toString(next), actual.toString()); } } } public static void assertCollectionContainsAll(Collection all, Collection few) { Iterator fi = few.iterator(); while (fi.hasNext()) { Object fo = fi.next(); boolean found = false; for (Object ao : all) { if (Objects.equals(fo, ao)) { found = true; break; } } if (!found) { throw new AssertionFailedError("element is missing", Objects.toString(fo), all.toString()); } } } public static void assertByteArrayCollectionContainsAll(Collection all, Collection few) { Iterator fi = few.iterator(); while (fi.hasNext()) { byte[] fo = fi.next(); boolean found = false; for (byte[] ao : all) { if (Arrays.equals(fo, ao)) { found = true; break; } } if (!found) { throw new AssertionFailedError("element is missing", Arrays.toString(fo), all.toString()); } } } public static void assertPipelineSyncAll(List expected, List actual) { assertEquals(expected.size(), actual.size()); for (int n = 0; n < expected.size(); n++) { Object expObj = expected.get(n); Object actObj = actual.get(n); if (expObj instanceof List) { if (!(actObj instanceof List)) { throw new AssertionFailedError(n + "'th element is not a list", expObj.getClass().toString(), actObj.getClass().toString()); } assertPipelineSyncAll((List) expObj, (List) actObj); } else if (expObj instanceof List) { if (!(actObj instanceof List)) { throw new AssertionFailedError(n + "'th element is not a list", expObj.getClass().toString(), actObj.getClass().toString()); } assertPipelineSyncAll((List) expObj, (List) actObj); } else if (expObj instanceof Set) { if (!(actObj instanceof Set)) { throw new AssertionFailedError(n + "'th element is not a set", expObj.getClass().toString(), actObj.getClass().toString()); } assertPipelineSyncAllSet((Set) expObj, (Set) actObj); } else if (expObj instanceof byte[]) { if (!(actObj instanceof byte[])) { throw new AssertionFailedError(n + "'th element is not byte array", expObj.getClass().toString(), actObj.getClass().toString()); } assertArrayEquals((byte[]) expObj, (byte[]) actObj); } else { assertEquals(expObj, actObj, n + "'th element mismatched"); } } } private static void assertPipelineSyncAllSet(Set expected, Set actual) { assertEquals(expected.size(), actual.size()); if (expected.iterator().next() instanceof byte[]) { assertByteArraySetEquals((Set) expected, (Set) actual); } else { assertEquals(expected, actual); } } } ================================================ FILE: src/test/java/redis/clients/jedis/util/ByteArrayComparatorTest.java ================================================ package redis.clients.jedis.util; import static org.junit.jupiter.api.Assertions.assertTrue; import org.junit.jupiter.api.Test; public class ByteArrayComparatorTest { @Test public void test() { byte[] foo = SafeEncoder.encode("foo"); byte[] foo2 = SafeEncoder.encode("foo"); byte[] bar = SafeEncoder.encode("bar"); assertTrue(ByteArrayComparator.compare(foo, foo2) == 0); assertTrue(ByteArrayComparator.compare(foo, bar) > 0); assertTrue(ByteArrayComparator.compare(bar, foo) < 0); } @Test public void testPrefix() { byte[] foo = SafeEncoder.encode("foo"); byte[] fooo = SafeEncoder.encode("fooo"); assertTrue(ByteArrayComparator.compare(foo, fooo) < 0); assertTrue(ByteArrayComparator.compare(fooo, foo) > 0); } } ================================================ FILE: src/test/java/redis/clients/jedis/util/ByteArrayMapMatcher.java ================================================ package redis.clients.jedis.util; import org.hamcrest.Description; import org.hamcrest.TypeSafeMatcher; import java.util.Map; import java.util.Arrays; public class ByteArrayMapMatcher extends TypeSafeMatcher> { private final Map expected; public ByteArrayMapMatcher(Map expected) { this.expected = expected; } @Override protected boolean matchesSafely(Map actual) { if (actual == null) { return expected == null; } if (actual.size() != expected.size()) return false; // For each expected key, find the matching key in actual and verify the value matches for (Map.Entry expectedEntry : expected.entrySet()) { byte[] expectedKey = expectedEntry.getKey(); byte[] expectedValue = expectedEntry.getValue(); // Find the actual entry with matching key boolean keyFound = false; for (Map.Entry actualEntry : actual.entrySet()) { if (Arrays.equals(expectedKey, actualEntry.getKey())) { keyFound = true; // Verify the value for this key matches if (!Arrays.equals(expectedValue, actualEntry.getValue())) { return false; // Key found but value doesn't match } break; } } if (!keyFound) { return false; // Expected key not found in actual } } return true; } @Override public void describeTo(Description description) { description.appendText("maps to be equal by byte[] content"); } public static ByteArrayMapMatcher contentEquals(Map expected) { return new ByteArrayMapMatcher(expected); } } ================================================ FILE: src/test/java/redis/clients/jedis/util/ByteArrayUtil.java ================================================ package redis.clients.jedis.util; import java.util.Arrays; import java.util.Collection; import java.util.Iterator; public class ByteArrayUtil { public static boolean byteArrayCollectionRemove(Collection all, byte[] element) { Iterator it = all.iterator(); while (it.hasNext()) { if (Arrays.equals(it.next(), element)) { it.remove(); return true; } } return false; } public static boolean byteArrayCollectionRemoveAll(Collection all, Collection few) { boolean modified = false; for (byte[] e : few) { modified |= byteArrayCollectionRemove(all, e); } return modified; } } ================================================ FILE: src/test/java/redis/clients/jedis/util/ClientKillerUtil.java ================================================ package redis.clients.jedis.util; import redis.clients.jedis.Jedis; public class ClientKillerUtil { public static void killClient(Jedis jedis, String clientName) { for (String clientInfo : jedis.clientList().split("\n")) { if (clientInfo.contains("name=" + clientName)) { // Ugly, but cmon, it's a test. String hostAndPortString = clientInfo.split(" ")[1].split("=")[1]; // It would be better if we kill the client by Id as it's safer but jedis doesn't implement // the command yet. jedis.clientKill(hostAndPortString); } } } public static void tagClient(Jedis j, String name) { j.clientSetname(name); } } ================================================ FILE: src/test/java/redis/clients/jedis/util/ClientTestUtil.java ================================================ package redis.clients.jedis.util; import redis.clients.jedis.UnifiedJedis; import redis.clients.jedis.providers.ConnectionProvider; public class ClientTestUtil { public static T getConnectionProvider(UnifiedJedis jedis) { return ReflectionTestUtil.getField(jedis, "provider"); } } ================================================ FILE: src/test/java/redis/clients/jedis/util/CommandArgumentsMatchers.java ================================================ package redis.clients.jedis.util; import org.hamcrest.Description; import org.hamcrest.Matcher; import org.hamcrest.TypeSafeMatcher; import org.mockito.ArgumentMatcher; import redis.clients.jedis.CommandArguments; import redis.clients.jedis.args.Rawable; import redis.clients.jedis.commands.ProtocolCommand; import java.util.ArrayList; import java.util.Arrays; import java.util.Iterator; import java.util.List; import java.util.stream.Collectors; /** * Utility class providing matchers for CommandArguments testing. *

* Provides both Mockito ArgumentMatchers (for mock verification) and Hamcrest Matchers (for * assertions). *

*

* Hamcrest matcher example usage: *

* *
 * {@code
 * assertThat(args, hasCommand(Protocol.Command.ZRANGE));
 * assertThat(args, hasArgumentCount(3));
 * assertThat(args, hasArgument(1, RawableFactory.from(100L)));
 * assertThat(args, hasArguments(
 *     Protocol.Command.ZRANGE,
 *     RawableFactory.from(0L),
 *     RawableFactory.from(100L)
 * ));
 * }
 * 
*

* Mockito matcher example usage: *

* *
 * {@code
 * verify(mock).someMethod(argThat(commandIs(Protocol.Command.GET)));
 * verify(mock).someMethod(argThat(commandWithArgs(Protocol.Command.SET, "key")));
 * }
 * 
*/ public final class CommandArgumentsMatchers { private CommandArgumentsMatchers() { throw new InstantiationError("Must not instantiate this class"); } // ========== Mockito ArgumentMatchers ========== /** * Mockito matcher for CommandArguments with specific ProtocolCommand. * @param command the expected protocol command * @return an ArgumentMatcher that checks if the CommandArguments has the specified command */ public static ArgumentMatcher commandIs(ProtocolCommand command) { return args -> { if (args == null || !(args instanceof CommandArguments)) { return false; } return command.equals(args.getCommand()); }; } /** * Mockito matcher for CommandArguments containing a specific argument (as String). * @param expectedArg the expected argument value (will be compared as String) * @return an ArgumentMatcher that checks if the CommandArguments contains the argument */ public static ArgumentMatcher hasArgument(String expectedArg) { return args -> { for (Rawable arg : args) { if (expectedArg.equals(SafeEncoder.encode(arg.getRaw()))) { return true; } } return false; }; } /** * Mockito matcher for CommandArguments with specific command and containing a specific argument. * @param command the expected protocol command * @param expectedArg the expected argument value (will be compared as String) * @return an ArgumentMatcher that checks both command and argument */ public static ArgumentMatcher commandWithArgs(ProtocolCommand command, String expectedArg) { return cmd -> commandIs(command).matches(cmd) && hasArgument(expectedArg).matches(cmd); } // ========== Hamcrest Matchers ========== /** * Matches CommandArguments with a specific command. * @param expectedCommand the expected protocol command * @return a matcher that checks if the CommandArguments has the specified command */ public static Matcher hasCommand(ProtocolCommand expectedCommand) { return new TypeSafeMatcher() { @Override protected boolean matchesSafely(CommandArguments args) { return expectedCommand.equals(args.getCommand()); } @Override public void describeTo(Description description) { description.appendText("CommandArguments with command ").appendValue(expectedCommand); } @Override protected void describeMismatchSafely(CommandArguments args, Description mismatchDescription) { mismatchDescription.appendText("was CommandArguments with command ") .appendValue(args.getCommand()); } }; } /** * Matches CommandArguments with a specific argument count. * @param expectedSize the expected number of arguments (including the command) * @return a matcher that checks if the CommandArguments has the specified size */ public static Matcher hasArgumentCount(int expectedSize) { return new TypeSafeMatcher() { @Override protected boolean matchesSafely(CommandArguments args) { return args.size() == expectedSize; } @Override public void describeTo(Description description) { description.appendText("CommandArguments with argument count ").appendValue(expectedSize); } @Override protected void describeMismatchSafely(CommandArguments args, Description mismatchDescription) { mismatchDescription.appendText("was CommandArguments with argument count ") .appendValue(args.size()); } }; } /** * Matches CommandArguments with a specific argument at a specific index. * @param index the index of the argument (0-based, where 0 is the command) * @param expectedArg the expected Rawable argument * @return a matcher that checks if the CommandArguments has the specified argument at the index */ public static Matcher hasArgument(int index, Rawable expectedArg) { return new TypeSafeMatcher() { @Override protected boolean matchesSafely(CommandArguments args) { if (index < 0 || index >= args.size()) { return false; } return expectedArg.equals(args.get(index)); } @Override public void describeTo(Description description) { description.appendText("CommandArguments with argument at index ").appendValue(index) .appendText(" equal to ").appendValue(expectedArg); } @Override protected void describeMismatchSafely(CommandArguments args, Description mismatchDescription) { if (index < 0 || index >= args.size()) { mismatchDescription.appendText("index ").appendValue(index) .appendText(" is out of bounds (size: ").appendValue(args.size()).appendText(")"); } else { mismatchDescription.appendText("argument at index ").appendValue(index) .appendText(" was ").appendValue(args.get(index)); } } }; } /** * Matches CommandArguments with a specific sequence of arguments. *

* The first argument should be the command, followed by the parameters. *

* @param expectedArgs the expected sequence of Rawable arguments (command + parameters) * @return a matcher that checks if the CommandArguments matches the sequence */ public static Matcher hasArguments(Rawable... expectedArgs) { return new TypeSafeMatcher() { @Override protected boolean matchesSafely(CommandArguments args) { if (args.size() != expectedArgs.length) { return false; } Iterator iter = args.iterator(); for (Rawable expected : expectedArgs) { if (!iter.hasNext() || !expected.equals(iter.next())) { return false; } } return true; } @Override public void describeTo(Description description) { List decodedExpectedArgs = Arrays.stream(expectedArgs).map(Rawable::getRaw) .map(SafeEncoder::encode).collect(Collectors.toList()); description.appendText("CommandArguments with arguments ").appendValue(decodedExpectedArgs); } @Override protected void describeMismatchSafely(CommandArguments args, Description mismatchDescription) { List actualArgs = new ArrayList<>(); args.forEach(actualArgs::add); List decodedActualArgs = actualArgs.stream().map(Rawable::getRaw) .map(SafeEncoder::encode).collect(Collectors.toList()); mismatchDescription.appendText("was CommandArguments with arguments ") .appendValue(decodedActualArgs); } }; } } ================================================ FILE: src/test/java/redis/clients/jedis/util/DelayTest.java ================================================ package redis.clients.jedis.util; import org.junit.jupiter.api.Test; import java.time.Duration; import static org.junit.jupiter.api.Assertions.*; public class DelayTest { @Test public void testConstantDelay() { Delay delay = Delay.constant(Duration.ofMillis(100)); // Constant delay should return the same value for all attempts assertEquals(100, delay.delay(0).toMillis()); assertEquals(100, delay.delay(1).toMillis()); assertEquals(100, delay.delay(5).toMillis()); assertEquals(100, delay.delay(100).toMillis()); } @Test public void testExponentialWithJitterBounds() { Duration lower = Duration.ofMillis(50); Duration upper = Duration.ofSeconds(10); Duration base = Duration.ofMillis(100); Delay delay = Delay.exponentialWithJitter(lower, upper, base); // Test multiple attempts to verify bounds for (int attempt = 0; attempt < 20; attempt++) { Duration result = delay.delay(attempt); long millis = result.toMillis(); // Verify lower bound assertTrue(millis >= lower.toMillis(), String.format( "Attempt %d: delay %d should be >= lower bound %d", attempt, millis, lower.toMillis())); // Verify upper bound assertTrue(millis <= upper.toMillis(), String.format( "Attempt %d: delay %d should be <= upper bound %d", attempt, millis, upper.toMillis())); } } @Test public void testExponentialWithJitterGrowth() { Duration lower = Duration.ofMillis(10); Duration upper = Duration.ofSeconds(60); Duration base = Duration.ofMillis(100); Delay delay = Delay.exponentialWithJitter(lower, upper, base); // Collect multiple samples for each attempt to verify growth trend int samples = 100; // Attempt 0: base * 2^0 = 100ms, range [50-100]ms long sum0 = 0; for (int i = 0; i < samples; i++) { sum0 += delay.delay(0).toMillis(); } long avg0 = sum0 / samples; // Attempt 1: base * 2^1 = 200ms, range [100-200]ms long sum1 = 0; for (int i = 0; i < samples; i++) { sum1 += delay.delay(1).toMillis(); } long avg1 = sum1 / samples; // Attempt 2: base * 2^2 = 400ms, range [200-400]ms long sum2 = 0; for (int i = 0; i < samples; i++) { sum2 += delay.delay(2).toMillis(); } long avg2 = sum2 / samples; // Verify exponential growth: avg1 should be roughly 2x avg0, avg2 should be roughly 2x avg1 assertTrue(avg1 > avg0, "Average delay should increase with attempts"); assertTrue(avg2 > avg1, "Average delay should continue to increase"); } @Test public void testExponentialWithJitterEqualJitterFormula() { Duration lower = Duration.ofMillis(0); Duration upper = Duration.ofSeconds(10); Duration base = Duration.ofMillis(100); Delay delay = Delay.exponentialWithJitter(lower, upper, base); // For attempt 0: temp = min(10000, 100 * 2^0) = 100 // Equal jitter: delay = temp/2 + random[0, temp/2] = 50 + random[0, 50] // Range should be [50, 100] for (int i = 0; i < 50; i++) { long millis = delay.delay(0).toMillis(); assertTrue(millis >= 50 && millis <= 100, String.format("Attempt 0: delay %d should be in range [50, 100]", millis)); } // For attempt 1: temp = min(10000, 100 * 2^1) = 200 // Equal jitter: delay = 100 + random[0, 100] // Range should be [100, 200] for (int i = 0; i < 50; i++) { long millis = delay.delay(1).toMillis(); assertTrue(millis >= 100 && millis <= 200, String.format("Attempt 1: delay %d should be in range [100, 200]", millis)); } } @Test public void testExponentialWithJitterUpperBoundCapping() { Duration lower = Duration.ofMillis(10); Duration upper = Duration.ofMillis(500); Duration base = Duration.ofMillis(100); Delay delay = Delay.exponentialWithJitter(lower, upper, base); // For high attempts, exponential should be capped at upper bound // Attempt 10: base * 2^10 = 102400ms, but capped at 500ms // Equal jitter: delay = 250 + random[0, 250] // Range should be [250, 500] for (int i = 0; i < 50; i++) { long millis = delay.delay(10).toMillis(); assertTrue(millis >= 250 && millis <= 500, String.format("Attempt 10: delay %d should be in range [250, 500] (capped)", millis)); } } @Test public void testExponentialWithJitterLowerBoundEnforcement() { Duration lower = Duration.ofMillis(200); Duration upper = Duration.ofSeconds(10); Duration base = Duration.ofMillis(100); Delay delay = Delay.exponentialWithJitter(lower, upper, base); // For attempt 0: temp = 100, equal jitter would give [50, 100] // But lower bound is 200, so all values should be >= 200 for (int i = 0; i < 50; i++) { long millis = delay.delay(0).toMillis(); assertTrue(millis >= 200, String.format("Attempt 0: delay %d should be >= lower bound 200", millis)); } } } ================================================ FILE: src/test/java/redis/clients/jedis/util/EnabledOnCommandCondition.java ================================================ package redis.clients.jedis.util; import io.redis.test.annotations.EnabledOnCommand; import org.junit.jupiter.api.extension.ConditionEvaluationResult; import org.junit.jupiter.api.extension.ExecutionCondition; import org.junit.jupiter.api.extension.ExtensionContext; import org.junit.platform.commons.util.AnnotationUtils; import redis.clients.jedis.*; import redis.clients.jedis.resps.CommandInfo; import java.util.Map; import java.util.Optional; import java.util.function.Supplier; public class EnabledOnCommandCondition implements ExecutionCondition { private final Supplier endpointSupplier; private HostAndPort hostPort; private JedisClientConfig config; public EnabledOnCommandCondition(HostAndPort hostPort, JedisClientConfig config) { this.endpointSupplier = null; this.hostPort = hostPort; this.config = config; } public EnabledOnCommandCondition(Supplier endpointSupplier) { this.endpointSupplier = endpointSupplier; this.hostPort = null; this.config = null; } private void ensureInitialized() { if (hostPort == null && endpointSupplier != null) { EndpointConfig endpoint = endpointSupplier.get(); this.hostPort = endpoint.getHostAndPort(); this.config = endpoint.getClientConfigBuilder().build(); } } @Override public ConditionEvaluationResult evaluateExecutionCondition(ExtensionContext context) { ensureInitialized(); try (Jedis jedisClient = new Jedis(hostPort, config)) { String[] command = getCommandFromAnnotations(context); if (command != null && !isCommandAvailable(jedisClient, command[0], command[1])) { return ConditionEvaluationResult.disabled( "Test requires Redis command '" + command[0] + " " + command[1] + "' to be available on " + hostPort + ", but it was not found."); } } catch (Exception e) { return ConditionEvaluationResult.disabled( "Failed to check Redis command on " + hostPort + ": " + e.getMessage()); } return ConditionEvaluationResult.enabled("Redis command is available"); } private String[] getCommandFromAnnotations(ExtensionContext context) { Optional methodAnnotation = AnnotationUtils.findAnnotation( context.getRequiredTestMethod(), EnabledOnCommand.class); if (methodAnnotation.isPresent()) { return new String[] { methodAnnotation.get().value(), methodAnnotation.get().subCommand() }; } Optional classAnnotation = AnnotationUtils.findAnnotation( context.getRequiredTestClass(), EnabledOnCommand.class); if (classAnnotation.isPresent()) { return new String[] { classAnnotation.get().value(), classAnnotation.get().subCommand() }; } return null; } private boolean isCommandAvailable(Jedis jedisClient, String command, String subCommand) { try { Map commandInfoMap = jedisClient.commandInfo(command); CommandInfo commandInfo = commandInfoMap.get(command.toLowerCase()); if (commandInfo != null) { if (subCommand != null && !subCommand.isEmpty()) { String replySubCommandName = command + '|' + subCommand; for (CommandInfo supportedSubCommand : commandInfo.getSubcommands().values()) { if (replySubCommandName.equalsIgnoreCase(supportedSubCommand.getName())) { return true; } } return false; } return true; } return false; } catch (Exception e) { throw new RuntimeException("Error found while EnableOnCommand for command '" + command + "'", e); } } } ================================================ FILE: src/test/java/redis/clients/jedis/util/EnvCondition.java ================================================ package redis.clients.jedis.util; import io.redis.test.annotations.ConditionalOnEnv; import org.junit.jupiter.api.extension.ConditionEvaluationResult; import org.junit.jupiter.api.extension.ExecutionCondition; import org.junit.jupiter.api.extension.ExtensionContext; import org.junit.platform.commons.util.AnnotationUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import java.util.Arrays; import java.util.Optional; /** * JUnit 5 execution condition that enables or disables tests based on the test environment. *

* This condition works with the {@link ConditionalOnEnv} annotation to conditionally execute tests * depending on the current test environment (e.g., Docker, Redis Enterprise). *

* The environment is determined using {@link TestEnvUtil}. *

* Example usage with JUnit 5: * *

 * @ExtendWith(EnvCondition.class)
 * @ConditionalOnEnv(value = TestEnvUtil.ENV_OSS_DOCKER, enabled = true)
 * public class DockerOnlyTest {
 *   // Tests in this class only run in Docker environment
 * }
 *
 * @ExtendWith(EnvCondition.class)
 * @ConditionalOnEnv(value = TestEnvUtil.ENV_REDIS_ENTERPRISE, enabled = false)
 * public class NotOnEnterpriseTest {
 *   // Tests in this class are skipped in Redis Enterprise environment
 * }
 * 
*/ public class EnvCondition implements ExecutionCondition { private static final Logger logger = LoggerFactory.getLogger(EnvCondition.class); @Override public ConditionEvaluationResult evaluateExecutionCondition(ExtensionContext context) { String currentEnv = TestEnvUtil.getTestEnvProvider(); ConditionalOnEnv annotation = getConditionalOnEnvAnnotation(context); if (annotation != null) { return evaluateConditionalOnEnv(annotation, currentEnv); } return ConditionEvaluationResult.enabled("No @ConditionalOnEnv annotation found"); } private ConditionEvaluationResult evaluateConditionalOnEnv(ConditionalOnEnv annotation, String currentEnv) { String[] envs = annotation.value(); boolean enabled = annotation.enabled(); boolean matches = Arrays.stream(envs).anyMatch(env -> env.equalsIgnoreCase(currentEnv)); if (enabled) { // enabled = true: test runs ONLY when environment matches if (matches) { logger.debug("Test enabled: current environment '{}' matches one of {}", currentEnv, Arrays.toString(envs)); return ConditionEvaluationResult .enabled("Current environment '" + currentEnv + "' is in the enabled list"); } String message = annotation.message(); String disabledReason = message.isEmpty() ? "Test requires environment " + Arrays.toString(envs) + ", but current environment is '" + currentEnv + "'" : message; logger.debug("Test disabled: {}", disabledReason); return ConditionEvaluationResult.disabled(disabledReason); } else { // enabled = false: test is SKIPPED when environment matches if (matches) { String message = annotation.message(); String disabledReason = message.isEmpty() ? "Test is disabled in environment '" + currentEnv + "'" : message; logger.debug("Test disabled: {}", disabledReason); return ConditionEvaluationResult.disabled(disabledReason); } logger.debug("Test enabled: current environment '{}' is not in disabled list {}", currentEnv, Arrays.toString(envs)); return ConditionEvaluationResult .enabled("Current environment '" + currentEnv + "' is not in the disabled list"); } } /** * Retrieves the {@link ConditionalOnEnv} annotation from the test method or class. Method-level * annotations take precedence over class-level annotations. * @param context the extension context * @return the annotation, or null if not present */ private ConditionalOnEnv getConditionalOnEnvAnnotation(ExtensionContext context) { Optional methodAnnotation = AnnotationUtils .findAnnotation(context.getTestMethod(), ConditionalOnEnv.class); if (methodAnnotation.isPresent()) { return methodAnnotation.get(); } Optional classAnnotation = AnnotationUtils .findAnnotation(context.getRequiredTestClass(), ConditionalOnEnv.class); return classAnnotation.orElse(null); } } ================================================ FILE: src/test/java/redis/clients/jedis/util/FragmentedByteArrayInputStream.java ================================================ package redis.clients.jedis.util; import java.io.ByteArrayInputStream; /** * Test class the fragment a byte array for testing purpose. */ public class FragmentedByteArrayInputStream extends ByteArrayInputStream { private int readMethodCallCount = 0; public FragmentedByteArrayInputStream(final byte[] buf) { super(buf); } @Override public synchronized int read(final byte[] b, final int off, final int len) { readMethodCallCount++; if (len <= 10) { // if the len <= 10, return as usual .. return super.read(b, off, len); } else { // else return the first half .. return super.read(b, off, len / 2); } } public int getReadMethodCallCount() { return readMethodCallCount; } } ================================================ FILE: src/test/java/redis/clients/jedis/util/GeoCoordinateMatcher.java ================================================ package redis.clients.jedis.util; import org.hamcrest.Description; import org.hamcrest.TypeSafeMatcher; import redis.clients.jedis.GeoCoordinate; public class GeoCoordinateMatcher extends TypeSafeMatcher { public static GeoCoordinateMatcher atCoordinates(double longitude, double latitude) { return atCoordinates(new GeoCoordinate(longitude, latitude)); } static GeoCoordinateMatcher atCoordinates(GeoCoordinate expected) { return new GeoCoordinateMatcher(expected); } private static final double EPSILON = 1e-5; private final GeoCoordinate expected; public GeoCoordinateMatcher(GeoCoordinate expected) { this.expected = expected; } @Override protected boolean matchesSafely(GeoCoordinate actual) { return Math.abs(actual.getLatitude() - expected.getLatitude()) < EPSILON && Math.abs(actual.getLongitude() - expected.getLongitude()) < EPSILON; } @Override public void describeTo(Description description) { description.appendText("a GeoCoordinate within ") .appendValue(EPSILON) .appendText(" of ") .appendValue(expected); } @Override protected void describeMismatchSafely(GeoCoordinate actual, Description mismatchDescription) { mismatchDescription.appendText("was ").appendValue(actual); } } ================================================ FILE: src/test/java/redis/clients/jedis/util/GeoRadiusResponseMatcher.java ================================================ package redis.clients.jedis.util; import java.util.Arrays; import org.hamcrest.Description; import org.hamcrest.TypeSafeMatcher; import redis.clients.jedis.resps.GeoRadiusResponse; public class GeoRadiusResponseMatcher extends TypeSafeMatcher { public static GeoRadiusResponseMatcher ofResponse(GeoRadiusResponse expected) { return new GeoRadiusResponseMatcher(expected); } private final GeoRadiusResponse expected; public GeoRadiusResponseMatcher(GeoRadiusResponse expected) { this.expected = expected; } @Override protected boolean matchesSafely(GeoRadiusResponse actual) { // Check if coordinates match within the tolerance if (!GeoCoordinateMatcher.atCoordinates(expected.getCoordinate()) .matches(actual.getCoordinate())) { return false; } // Check if other attributes match exactly if (Double.compare(expected.getDistance(), actual.getDistance()) != 0) { return false; } if (Long.compare(expected.getRawScore(), actual.getRawScore()) != 0) { return false; } return Arrays.equals(expected.getMember(), actual.getMember()); } @Override public void describeTo(Description description) { description.appendText("a GeoRadiusResponse with coordinate ") .appendValue(expected.getCoordinate()) .appendText(", distance ") .appendValue(expected.getDistance()) .appendText(", rawScore ") .appendValue(expected.getRawScore()) .appendText("and member ") .appendValue(expected.getMemberByString()); } @Override protected void describeMismatchSafely(GeoRadiusResponse actual, Description mismatchDescription) { mismatchDescription.appendText("was ").appendValue(actual); } } ================================================ FILE: src/test/java/redis/clients/jedis/util/JedisByteMapMatcher.java ================================================ package redis.clients.jedis.util; import org.hamcrest.Description; import org.hamcrest.TypeSafeMatcher; import java.util.Arrays; import java.util.Map; public class JedisByteMapMatcher extends TypeSafeMatcher> { private final Map expected; public JedisByteMapMatcher(Map expected) { this.expected = expected; } @Override protected boolean matchesSafely(Map actual) { if (actual == null) { return expected == null; } if (actual.size() != expected.size()) return false; // For each expected key, find the matching key in actual and verify the value matches for (Map.Entry expectedEntry : expected.entrySet()) { byte[] expectedKey = expectedEntry.getKey(); T expectedValue = expectedEntry.getValue(); // Find the actual entry with matching key boolean keyFound = false; for (Map.Entry actualEntry : actual.entrySet()) { if (Arrays.equals(expectedKey, actualEntry.getKey())) { keyFound = true; // Verify the value for this key matches if (!expectedValue.equals(actualEntry.getValue())) { return false; // Key found but value doesn't match } break; } } if (!keyFound) { return false; // Expected key not found in actual } } return true; } @Override public void describeTo(Description description) { description.appendText("maps to be equal by byte[] content"); } public static JedisByteMapMatcher contentEquals(Map expected) { return new JedisByteMapMatcher(expected); } } ================================================ FILE: src/test/java/redis/clients/jedis/util/JedisClusterCRC16Test.java ================================================ package redis.clients.jedis.util; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertNotEquals; import java.util.HashMap; import java.util.Map; import java.util.Map.Entry; import org.junit.jupiter.api.Test; public class JedisClusterCRC16Test { @Test public void testGetCRC16() { Map solutions = prepareSolutionSet(); for (Entry entry : solutions.entrySet()) { // string version assertEquals(entry.getValue().intValue(), JedisClusterCRC16.getCRC16(entry.getKey())); // byte array version assertEquals(entry.getValue().intValue(), JedisClusterCRC16.getCRC16(SafeEncoder.encode(entry.getKey()))); } } @Test public void testGetSlot() { assertEquals(7186, JedisClusterCRC16.getSlot("51")); } private Map prepareSolutionSet() { Map solutionMap = new HashMap(); solutionMap.put("", 0x0); solutionMap.put("123456789", 0x31C3); solutionMap.put("sfger132515", 0xA45C); solutionMap.put("hae9Napahngaikeethievubaibogiech", 0x58CE); solutionMap.put("AAAAAAAAAAAAAAAAAAAAAA", 0x92cd); solutionMap.put("Hello, World!", 0x4FD6); return solutionMap; } @Test public void testRedisHashtagGetSlot() { assertEquals(JedisClusterCRC16.getSlot("{bar"), JedisClusterCRC16.getSlot("foo{{bar}}zap")); assertEquals(JedisClusterCRC16.getSlot("{user1000}.following"), JedisClusterCRC16.getSlot("{user1000}.followers")); assertNotEquals(JedisClusterCRC16.getSlot("foo{}{bar}"), JedisClusterCRC16.getSlot("bar")); assertEquals(JedisClusterCRC16.getSlot("foo{bar}{zap}"), JedisClusterCRC16.getSlot("bar")); } @Test public void testBinaryHashtagGetSlot() { assertEquals(JedisClusterCRC16.getSlot("{bar".getBytes()), JedisClusterCRC16.getSlot("{bar".getBytes())); assertEquals(JedisClusterCRC16.getSlot("{user1000}.following".getBytes()), JedisClusterCRC16.getSlot("{user1000}.followers".getBytes())); assertNotEquals(JedisClusterCRC16.getSlot("foo{}{bar}".getBytes()), JedisClusterCRC16.getSlot("bar".getBytes())); assertEquals(JedisClusterCRC16.getSlot("foo{bar}{zap}".getBytes()), JedisClusterCRC16.getSlot("bar".getBytes())); } } ================================================ FILE: src/test/java/redis/clients/jedis/util/JedisClusterTestUtil.java ================================================ package redis.clients.jedis.util; import redis.clients.jedis.HostAndPort; import redis.clients.jedis.Jedis; import redis.clients.jedis.exceptions.JedisException; public class JedisClusterTestUtil { public static void waitForClusterReady(Jedis... nodes) throws InterruptedException { boolean clusterOk = false; while (!clusterOk) { boolean isOk = true; for (Jedis node : nodes) { if (!node.clusterInfo().split("\n")[0].contains("ok")) { isOk = false; break; } } if (isOk) { clusterOk = true; } Thread.sleep(50); } } public static String getNodeId(String infoOutput) { for (String infoLine : infoOutput.split("\n")) { if (infoLine.contains("myself")) { return infoLine.split(" ")[0]; } } return ""; } public static String getNodeId(String infoOutput, HostAndPort node) { for (String infoLine : infoOutput.split("\n")) { if (infoLine.contains(node.toString())) { return infoLine.split(" ")[0]; } } return ""; } public static void assertNodeIsKnown(Jedis node, String targetNodeId, int timeoutMs) { assertNodeRecognizedStatus(node, targetNodeId, true, timeoutMs); } public static void assertNodeIsUnknown(Jedis node, String targetNodeId, int timeoutMs) { assertNodeRecognizedStatus(node, targetNodeId, false, timeoutMs); } private static void assertNodeRecognizedStatus(Jedis node, String targetNodeId, boolean shouldRecognized, int timeoutMs) { int sleepInterval = 100; for (int sleepTime = 0; sleepTime <= timeoutMs; sleepTime += sleepInterval) { boolean known = isKnownNode(node, targetNodeId); if (shouldRecognized == known) return; try { Thread.sleep(sleepInterval); } catch (InterruptedException e) { } } throw new JedisException("Node recognize check error"); } private static boolean isKnownNode(Jedis node, String nodeId) { String infoOutput = node.clusterNodes(); for (String infoLine : infoOutput.split("\n")) { if (infoLine.contains(nodeId)) { return true; } } return false; } } ================================================ FILE: src/test/java/redis/clients/jedis/util/JedisSentinelTestUtil.java ================================================ package redis.clients.jedis.util; import java.util.concurrent.atomic.AtomicReference; import redis.clients.jedis.HostAndPort; import redis.clients.jedis.JedisPubSub; import redis.clients.jedis.Jedis; import redis.clients.jedis.exceptions.FailoverAbortedException; public class JedisSentinelTestUtil { public static HostAndPort waitForNewPromotedMaster(final String masterName, final Jedis sentinelJedis, final Jedis commandJedis) throws InterruptedException { final AtomicReference newmaster = new AtomicReference(""); sentinelJedis.psubscribe(new JedisPubSub() { @Override public void onPMessage(String pattern, String channel, String message) { if (channel.equals("+switch-master")) { newmaster.set(message); punsubscribe(); } else if (channel.startsWith("-failover-abort")) { punsubscribe(); throw new FailoverAbortedException("Unfortunately sentinel cannot failover..." + " reason(channel) : " + channel + " / message : " + message); } } @Override public void onPSubscribe(String pattern, int subscribedChannels) { commandJedis.sentinelFailover(masterName); } }, "*"); String[] chunks = newmaster.get().split(" "); HostAndPort newMaster = new HostAndPort(chunks[3], Integer.parseInt(chunks[4])); return newMaster; } } ================================================ FILE: src/test/java/redis/clients/jedis/util/JedisURIHelperTest.java ================================================ package redis.clients.jedis.util; import static org.junit.jupiter.api.Assertions.*; import static redis.clients.jedis.util.JedisURIHelper.*; import java.net.URI; import java.net.URISyntaxException; import org.junit.jupiter.api.Test; import redis.clients.jedis.RedisProtocol; import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.emptyString; public class JedisURIHelperTest { @Test public void shouldGetUserAndPasswordFromURIWithCredentials() throws URISyntaxException { URI uri = new URI("redis://user:password@host:9000/0"); assertEquals("user", JedisURIHelper.getUser(uri)); assertEquals("password", JedisURIHelper.getPassword(uri)); } @Test public void shouldGetNullUserFromURIWithCredentials() throws URISyntaxException { URI uri = new URI("redis://:password@host:9000/0"); assertNull(JedisURIHelper.getUser(uri)); assertEquals("password", JedisURIHelper.getPassword(uri)); } @Test public void shouldReturnNullIfURIDoesNotHaveCredentials() throws URISyntaxException { URI uri = new URI("redis://host:9000/0"); assertNull(JedisURIHelper.getUser(uri)); assertNull(JedisURIHelper.getPassword(uri)); } @Test public void shouldGetDbFromURIWithCredentials() throws URISyntaxException { URI uri = new URI("redis://user:password@host:9000/3"); assertEquals(3, JedisURIHelper.getDBIndex(uri)); } @Test public void shouldGetDbFromURIWithoutCredentials() throws URISyntaxException { URI uri = new URI("redis://host:9000/4"); assertEquals(4, JedisURIHelper.getDBIndex(uri)); } @Test public void shouldGetDefaultDbFromURIIfNoDbWasSpecified() throws URISyntaxException { URI uri = new URI("redis://host:9000"); assertEquals(0, JedisURIHelper.getDBIndex(uri)); } @Test public void hasDbIndex_shouldReturnFalseIfURIDoesNotHaveDbIndex() throws URISyntaxException { URI uri = new URI("redis://host:9000"); assertFalse(JedisURIHelper.hasDbIndex(uri)); } @Test public void hasDbIndex_shouldReturnTrueIfURIDoesHaveDbIndex() throws URISyntaxException { URI uri = new URI("redis://host:9000/3"); assertTrue(JedisURIHelper.hasDbIndex(uri)); } @Test public void shouldValidateInvalidURIs() throws URISyntaxException { assertFalse(JedisURIHelper.isValid(new URI("host:9000"))); assertFalse(JedisURIHelper.isValid(new URI("user:password@host:9000/0"))); assertFalse(JedisURIHelper.isValid(new URI("host:9000/0"))); assertFalse(JedisURIHelper.isValid(new URI("redis://host/0"))); } @Test public void shouldGetDefaultProtocolWhenNotDefined() { assertNull(getRedisProtocol(URI.create("redis://host:1234"))); assertNull(getRedisProtocol(URI.create("redis://host:1234/1"))); } @Test public void shouldGetProtocolFromDefinition() { assertEquals(RedisProtocol.RESP3, getRedisProtocol(URI.create("redis://host:1234?protocol=3"))); assertEquals(RedisProtocol.RESP3, getRedisProtocol(URI.create("redis://host:1234/?protocol=3"))); assertEquals(RedisProtocol.RESP3, getRedisProtocol(URI.create("redis://host:1234/1?protocol=3"))); assertEquals(RedisProtocol.RESP3, getRedisProtocol(URI.create("redis://host:1234/1/?protocol=3"))); } @Test public void emptyPassword() { // ensure we can provide an empty password for default user assertThat(JedisURIHelper.getPassword(URI.create("redis://:@host:9000/0")), emptyString()); // ensure we can provide an empty password for user assertEquals(JedisURIHelper.getUser(URI.create("redis://username:@host:9000/0")), "username"); assertThat(JedisURIHelper.getPassword(URI.create("redis://username:@host:9000/0")), emptyString()); } @Test public void shouldThrowIfNoPasswordInURI() throws URISyntaxException { // ensure we throw if user is provided but password is missing in URI URI uri = new URI("redis://user@host:9000/0"); IllegalArgumentException illegalArgumentException = assertThrows(IllegalArgumentException.class, () -> getPassword(uri)); assertEquals("Password not provided in uri.", illegalArgumentException.getMessage()); } } ================================================ FILE: src/test/java/redis/clients/jedis/util/JsonObjectMapperTestUtil.java ================================================ package redis.clients.jedis.util; import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.SerializationFeature; import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule; import com.google.gson.*; import redis.clients.jedis.exceptions.JedisException; import redis.clients.jedis.json.JsonObjectMapper; import java.lang.reflect.Type; import java.time.Instant; import java.time.format.DateTimeFormatter; import java.time.temporal.TemporalAccessor; public class JsonObjectMapperTestUtil { public static CustomJacksonObjectMapper getCustomJacksonObjectMapper() { ObjectMapper om = new ObjectMapper(); om.registerModule(new JavaTimeModule()); om.disable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS); return new CustomJacksonObjectMapper(om); } public static CustomGsonObjectMapper getCustomGsonObjectMapper() { final class InstantAdapter implements JsonSerializer, JsonDeserializer { DateTimeFormatter format = DateTimeFormatter.ISO_INSTANT; @Override public Instant deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) throws JsonParseException { JsonPrimitive primitive = json.getAsJsonPrimitive(); if (!primitive.isJsonNull()) { String asString = primitive.getAsString(); TemporalAccessor temporalAccessor = format.parse(asString); return Instant.from(temporalAccessor); } return null; } @Override public JsonElement serialize(Instant src, Type typeOfSrc, JsonSerializationContext context) { return new JsonPrimitive(format.format(src)); } } return new CustomGsonObjectMapper( new GsonBuilder().registerTypeAdapter(Instant.class, new InstantAdapter()).create()); } public static class CustomJacksonObjectMapper implements JsonObjectMapper { private final ObjectMapper om; CustomJacksonObjectMapper(ObjectMapper om) { this.om = om; } @Override public T fromJson(String value, Class valueType) { try { return om.readValue(value, valueType); } catch (JsonProcessingException e) { throw new JedisException(e); } } @Override public String toJson(Object value) { try { return om.writeValueAsString(value); } catch (JsonProcessingException e) { throw new JedisException(e); } } } public static class CustomGsonObjectMapper implements JsonObjectMapper { private final Gson gson; public CustomGsonObjectMapper(Gson gson) { this.gson = gson; } @Override public T fromJson(String value, Class valueType) { return gson.fromJson(value, valueType); } @Override public String toJson(Object value) { return gson.toJson(value); } } } ================================================ FILE: src/test/java/redis/clients/jedis/util/ProtocolTestUtil.java ================================================ package redis.clients.jedis.util; import java.io.ByteArrayOutputStream; import java.io.IOException; import redis.clients.jedis.CommandArguments; import redis.clients.jedis.Protocol; /** * Utility class for testing Redis protocol commands and serialization. *

* Provides helper methods to capture and inspect the RESP protocol output that would be sent to * Redis servers. *

*/ public final class ProtocolTestUtil { private ProtocolTestUtil() { throw new InstantiationError("Must not instantiate this class"); } /** * Captures the RESP protocol output that would be sent to Redis. *

* This method serializes the CommandArguments using the actual Protocol.sendCommand() method and * returns the raw RESP protocol string that would be sent over the wire. *

* @param args the command arguments to serialize * @return the RESP protocol string (e.g., "*3\r\n$6\r\nZRANGE\r\n$10\r\n3000000000\r\n...") * @throws AssertionError if an I/O error occurs during serialization (should never happen with * ByteArrayOutputStream) */ public static String captureCommandOutput(CommandArguments args) { ByteArrayOutputStream baos = new ByteArrayOutputStream(); RedisOutputStream ros = new RedisOutputStream(baos); try { Protocol.sendCommand(ros, args); ros.flush(); } catch (IOException e) { // This should never happen with ByteArrayOutputStream, but if it does, it's a test setup // error throw new AssertionError("Failed to serialize command arguments", e); } return baos.toString(); } } ================================================ FILE: src/test/java/redis/clients/jedis/util/RedisConditions.java ================================================ package redis.clients.jedis.util; import io.redis.test.utils.RedisVersion; import redis.clients.jedis.CommandArguments; import redis.clients.jedis.CommandObject; import redis.clients.jedis.Module; import redis.clients.jedis.UnifiedJedis; import redis.clients.jedis.resps.CommandInfo; import java.util.List; import java.util.Map; import java.util.stream.Collectors; import static redis.clients.jedis.BuilderFactory.MODULE_LIST; import static redis.clients.jedis.Protocol.Command.COMMAND; import static redis.clients.jedis.Protocol.Command.MODULE; import static redis.clients.jedis.Protocol.Keyword.LIST; public class RedisConditions { public enum ModuleVersion { SEARCH_MOD_VER_80M3("SEARCH", 79903), SEARCH_MOD_VER_84RC1("SEARCH", 80390); private final String moduleName; private final int version; ModuleVersion(String moduleName, int version) { this.moduleName = moduleName; this.version = version; } public String getModuleName() { return moduleName; } public int getVersion() { return version; } } private final RedisVersion version; private final Map modules; private final Map commands; private RedisConditions(RedisVersion version, Map commands, Map modules) { this.version = version; this.commands = commands; this.modules = modules; } public static RedisConditions of(UnifiedJedis jedis) { RedisVersion version = RedisVersionUtil.getRedisVersion(jedis); CommandObject> commandInfoCmd = new CommandObject<>( new CommandArguments(COMMAND), CommandInfo.COMMAND_INFO_RESPONSE); Map commands = jedis.executeCommand(commandInfoCmd); CommandObject> moduleListCmd = new CommandObject<>( new CommandArguments(MODULE).add(LIST), MODULE_LIST); Map modules = jedis.executeCommand(moduleListCmd).stream() .collect(Collectors.toMap((m) -> m.getName().toUpperCase(), Module::getVersion)); return new RedisConditions(version, commands, modules); } public RedisVersion getVersion() { return version; } /** * @param command * @return {@code true} if the command is present. */ public boolean hasCommand(String command) { return commands.containsKey(command.toUpperCase()); } /** * @param module * @return {@code true} if the module is present. */ public boolean hasModule(String module) { return modules.containsKey(module.toUpperCase()); } /** * @param module * @param version * @return {@code true} if the module with the requested minimum version is present. */ public boolean moduleVersionIsGreaterThanOrEqual(String module, int version) { Integer moduleVersion = modules.get(module.toUpperCase()); return moduleVersion != null && moduleVersion >= version; } /** * @param moduleVersion * @return {@code true} if the module version is greater than or equal to the specified version. */ public boolean moduleVersionIsGreaterThanOrEqual(ModuleVersion moduleVersion) { return moduleVersionIsGreaterThanOrEqual(moduleVersion.getModuleName(), moduleVersion.getVersion()); } } ================================================ FILE: src/test/java/redis/clients/jedis/util/RedisVersionCondition.java ================================================ package redis.clients.jedis.util; import io.redis.test.annotations.SinceRedisVersion; import io.redis.test.utils.RedisInfo; import io.redis.test.utils.RedisVersion; import org.junit.jupiter.api.extension.ConditionEvaluationResult; import org.junit.jupiter.api.extension.ExecutionCondition; import org.junit.jupiter.api.extension.ExtensionContext; import org.junit.platform.commons.util.AnnotationUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import redis.clients.jedis.EndpointConfig; import redis.clients.jedis.HostAndPort; import redis.clients.jedis.Jedis; import redis.clients.jedis.JedisClientConfig; import java.util.Optional; import java.util.function.Supplier; import static redis.clients.jedis.util.RedisVersionUtil.forcedVersion; public class RedisVersionCondition implements ExecutionCondition { private static final Logger logger = LoggerFactory.getLogger(RedisVersionCondition.class); private final Supplier endpointSupplier; private HostAndPort hostPort; private JedisClientConfig config; public RedisVersionCondition(Supplier endpointSupplier) { this.endpointSupplier = endpointSupplier; this.hostPort = null; this.config = null; } public RedisVersionCondition(HostAndPort hostPort, JedisClientConfig config) { this.endpointSupplier = null; this.hostPort = hostPort; this.config = config; } private void ensureInitialized() { if (hostPort == null && endpointSupplier != null) { EndpointConfig endpoint = endpointSupplier.get(); this.hostPort = endpoint.getHostAndPort(); this.config = endpoint.getClientConfigBuilder().build(); } } @Override public ConditionEvaluationResult evaluateExecutionCondition(ExtensionContext context) { ensureInitialized(); try (Jedis jedisClient = new Jedis(hostPort, config)) { SinceRedisVersion versionAnnotation = getAnnotation(context); if (versionAnnotation != null) { RedisVersion currentVersion; if (forcedVersion != null) { logger.info("Using forced Redis server version from environment variable: " + forcedVersion); currentVersion = forcedVersion; } else { RedisInfo info = RedisInfo.parseInfoServer(jedisClient.info("server")); currentVersion = RedisVersion.of(info.getRedisVersion()); } RedisVersion minRequiredVersion = RedisVersion.of(versionAnnotation.value()); if (currentVersion.isLessThan(minRequiredVersion)) { return ConditionEvaluationResult.disabled("Test requires Redis version " + minRequiredVersion + " or later, but found " + currentVersion); } } } catch (Exception e) { return ConditionEvaluationResult.disabled("Failed to check Redis version: " + e.getMessage()); } return ConditionEvaluationResult.enabled("Redis version is sufficient"); } private SinceRedisVersion getAnnotation(ExtensionContext context) { Optional methodAnnotation = AnnotationUtils.findAnnotation(context.getTestMethod(), SinceRedisVersion.class); if (methodAnnotation.isPresent()) { return methodAnnotation.get(); } Optional classAnnotation = AnnotationUtils.findAnnotation(context.getRequiredTestClass(), SinceRedisVersion.class); return classAnnotation.orElse(null); } } ================================================ FILE: src/test/java/redis/clients/jedis/util/RedisVersionUtil.java ================================================ package redis.clients.jedis.util; import io.redis.test.utils.RedisInfo; import io.redis.test.utils.RedisVersion; import redis.clients.jedis.*; public class RedisVersionUtil { static final String FORCE_REDIS_SERVER_VERSION_ENV = "forceRedisServerVersion"; static final RedisVersion forcedVersion = System.getenv(FORCE_REDIS_SERVER_VERSION_ENV) != null ? RedisVersion.of(System.getenv(FORCE_REDIS_SERVER_VERSION_ENV)) : null; public static RedisVersion getRedisVersion(Connection conn) { if (forcedVersion != null) { return forcedVersion; } try (Jedis jedis = new Jedis(conn)) { return getRedisVersion(jedis); } } public static RedisVersion getRedisVersion(UnifiedJedis jedis) { if (forcedVersion != null) { return forcedVersion; } Object response = SafeEncoder.encodeObject(jedis.sendCommand(Protocol.Command.INFO, "server")); RedisInfo info = RedisInfo.parseInfoServer(response.toString()); return RedisVersion.of(info.getRedisVersion()); } public static RedisVersion getRedisVersion(Jedis jedis) { if (forcedVersion != null) { return forcedVersion; } RedisInfo info = RedisInfo.parseInfoServer(jedis.info("server")); return RedisVersion.of(info.getRedisVersion()); } public static RedisVersion getRedisVersion(EndpointConfig endpoint) { if (forcedVersion != null) { return forcedVersion; } try (Jedis jedis = new Jedis(endpoint.getHostAndPort(), endpoint.getClientConfigBuilder().build())) { return getRedisVersion(jedis); } } } ================================================ FILE: src/test/java/redis/clients/jedis/util/ReflectionTestUtil.java ================================================ package redis.clients.jedis.util; import java.lang.reflect.Field; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; /** * Simple utility for accessing private fields in tests using reflection. *

* This utility is intended for testing purposes only to access internal state that is not exposed * through public APIs. *

*/ public class ReflectionTestUtil { /** * Gets the value of a private field from an object. * @param target the object containing the field * @param fieldName the name of the field to access * @param the expected type of the field value * @return the value of the field * @throws RuntimeException if the field cannot be accessed */ @SuppressWarnings("unchecked") public static T getField(Object target, String fieldName) { if (target == null) { throw new IllegalArgumentException("Target object cannot be null"); } if (fieldName == null || fieldName.isEmpty()) { throw new IllegalArgumentException("Field name cannot be null or empty"); } try { Field field = findField(target.getClass(), fieldName); field.setAccessible(true); return (T) field.get(target); } catch (NoSuchFieldException e) { throw new RuntimeException( "Field '" + fieldName + "' not found in class " + target.getClass().getName(), e); } catch (IllegalAccessException e) { throw new RuntimeException( "Cannot access field '" + fieldName + "' in class " + target.getClass().getName(), e); } } /** * Sets the value of a private field in an object. * @param target the object containing the field * @param fieldName the name of the field to set * @param value the value to set * @throws RuntimeException if the field cannot be accessed */ public static void setField(Object target, String fieldName, Object value) { if (target == null) { throw new IllegalArgumentException("Target object cannot be null"); } if (fieldName == null || fieldName.isEmpty()) { throw new IllegalArgumentException("Field name cannot be null or empty"); } try { Field field = findField(target.getClass(), fieldName); field.setAccessible(true); field.set(target, value); } catch (NoSuchFieldException e) { throw new RuntimeException( "Field '" + fieldName + "' not found in class " + target.getClass().getName(), e); } catch (IllegalAccessException e) { throw new RuntimeException( "Cannot access field '" + fieldName + "' in class " + target.getClass().getName(), e); } } /** * Finds a field in the class hierarchy. * @param clazz the class to search * @param fieldName the name of the field * @return the field * @throws NoSuchFieldException if the field is not found */ private static Field findField(Class clazz, String fieldName) throws NoSuchFieldException { Class current = clazz; while (current != null) { try { return current.getDeclaredField(fieldName); } catch (NoSuchFieldException e) { current = current.getSuperclass(); } } throw new NoSuchFieldException( "Field '" + fieldName + "' not found in class hierarchy of " + clazz.getName()); } /** * Invokes a private method on an object. * @param target the object on which to invoke the method * @param methodName the name of the method to invoke * @param parameterTypes the parameter types of the method * @param args the arguments to pass to the method * @param the expected return type * @return the result of the method invocation * @throws RuntimeException if the method cannot be invoked */ @SuppressWarnings("unchecked") public static T invokeMethod(Object target, String methodName, Class[] parameterTypes, Object... args) { if (target == null) { throw new IllegalArgumentException("Target object cannot be null"); } if (methodName == null || methodName.isEmpty()) { throw new IllegalArgumentException("Method name cannot be null or empty"); } try { Method method = findMethod(target.getClass(), methodName, parameterTypes); method.setAccessible(true); return (T) method.invoke(target, args); } catch (NoSuchMethodException e) { throw new RuntimeException( "Method '" + methodName + "' not found in class " + target.getClass().getName(), e); } catch (IllegalAccessException e) { throw new RuntimeException( "Cannot access method '" + methodName + "' in class " + target.getClass().getName(), e); } catch (InvocationTargetException e) { Throwable cause = e.getCause(); if (cause instanceof RuntimeException) { throw (RuntimeException) cause; } throw new RuntimeException( "Exception thrown by method '" + methodName + "' in class " + target.getClass().getName(), cause); } } /** * Finds a method in the class hierarchy. * @param clazz the class to search * @param methodName the name of the method * @param parameterTypes the parameter types of the method * @return the method * @throws NoSuchMethodException if the method is not found */ private static Method findMethod(Class clazz, String methodName, Class[] parameterTypes) throws NoSuchMethodException { Class current = clazz; while (current != null) { try { return current.getDeclaredMethod(methodName, parameterTypes); } catch (NoSuchMethodException e) { current = current.getSuperclass(); } } throw new NoSuchMethodException( "Method '" + methodName + "' not found in class hierarchy of " + clazz.getName()); } } ================================================ FILE: src/test/java/redis/clients/jedis/util/StreamEntryBinaryListMatcher.java ================================================ package redis.clients.jedis.util; import org.hamcrest.Description; import org.hamcrest.TypeSafeMatcher; import redis.clients.jedis.resps.StreamEntryBinary; import java.util.List; import java.util.Map; import java.util.Arrays; public class StreamEntryBinaryListMatcher extends TypeSafeMatcher> { private final List expected; public StreamEntryBinaryListMatcher(List expected) { this.expected = expected; } @Override protected boolean matchesSafely(List actual) { if (actual.size() != expected.size()) return false; for (int i = 0; i < expected.size(); i++) { StreamEntryBinary e = expected.get(i); StreamEntryBinary a = actual.get(i); if (!e.getID().equals(a.getID())) return false; if (!mapsEqual(e.getFields(), a.getFields())) return false; } return true; } private boolean mapsEqual(Map m1, Map m2) { if (m1.size() != m2.size()) return false; outer: for (Map.Entry e1 : m1.entrySet()) { for (Map.Entry e2 : m2.entrySet()) { if (Arrays.equals(e1.getKey(), e2.getKey()) && Arrays.equals(e1.getValue(), e2.getValue())) { continue outer; } } return false; } return true; } @Override public void describeTo(Description description) { description.appendText("StreamEntryBinary lists to match by ID and field content"); } public static StreamEntryBinaryListMatcher equalsStreamEntries(List expected) { return new StreamEntryBinaryListMatcher(expected); } } ================================================ FILE: src/test/java/redis/clients/jedis/util/TestDataUtil.java ================================================ package redis.clients.jedis.util; /** * Utility class for generating test data. */ public class TestDataUtil { private TestDataUtil() { throw new InstantiationError("Must not instantiate this class"); } /** * Generates a string of a specific size filled with a repeated character. * @param size The desired size in characters * @param fillChar The character to fill the string with * @return A string of the specified size */ public static String generateString(int size, char fillChar) { StringBuilder value = new StringBuilder(size); for (int i = 0; i < size; i++) { value.append(fillChar); } return value.toString(); } /** * Generates a string of a specific size filled with 'x' characters. * @param size The desired size in characters * @return A string of the specified size filled with 'x' */ public static String generateString(int size) { return generateString(size, 'x'); } } ================================================ FILE: src/test/java/redis/clients/jedis/util/TestEnvUtil.java ================================================ package redis.clients.jedis.util; import java.util.Optional; public class TestEnvUtil { // Redis servers running inside docker public static final String ENV_OSS_DOCKER = "oss-docker"; public static final String ENV_OSS_SOURCE = "oss-source"; public static final String ENV_REDIS_ENTERPRISE = "re"; private static final String TEST_ENV_PROVIDER = System.getenv().getOrDefault("TEST_ENV_PROVIDER", ENV_OSS_DOCKER); private static final String TESTMODULE_SO_PATH = Optional.ofNullable(System.getenv("TESTMODULE_SO")) .orElseGet(() -> isContainerEnv() ? "/redis/work/modules/testmodule.so" : "/tmp/testmodule.so"); private static final String ENDPOINTS_CONFIG_PATH = Optional.ofNullable(System.getenv("REDIS_ENDPOINTS_CONFIG_PATH")) .orElseGet(() -> TEST_ENV_PROVIDER.equals(ENV_OSS_SOURCE) ? "src/test/resources/endpoints_source.json" : "src/test/resources/endpoints.json"); public static boolean isContainerEnv() { return TEST_ENV_PROVIDER.equals(ENV_OSS_DOCKER); } public static String getTestEnvProvider() { return TEST_ENV_PROVIDER; } public static String testModuleSoPath() { return TESTMODULE_SO_PATH; } public static String getEndpointsConfigPath() { return ENDPOINTS_CONFIG_PATH; } } ================================================ FILE: src/test/java/redis/clients/jedis/util/TlsUtil.java ================================================ package redis.clients.jedis.util; import javax.net.ssl.*; import java.io.*; import java.nio.file.Path; import java.nio.file.Paths; import java.security.*; import java.security.cert.CertificateException; import java.security.cert.CertificateFactory; import java.security.cert.X509Certificate; import java.util.ArrayList; import java.util.List; import java.util.UUID; public class TlsUtil { private static final String TRUST_STORE_PROPERTY = "javax.net.ssl.trustStore"; private static final String TRUST_STORE_PASSWORD_PROPERTY = "javax.net.ssl.trustStorePassword"; private static final String TRUST_STORE_TYPE_PROPERTY = "javax.net.ssl.trustStoreType"; private static String originalTrustStore; private static String originalTrustStoreType; private static String originalTrustStorePassword; private static final String TRUST_STORE_TYPE = "JCEKS"; private static final String CERTIFICATE_TYPE = "X.509"; private static final String TEST_WORK_FOLDER = System.getenv().getOrDefault("TEST_WORK_FOLDER", "/tmp/redis-env-work"); private static final String TEST_TRUSTSTORE = System.getenv().getOrDefault("TEST_TRUSTSTORE", "truststore.jceks"); private static final String TEST_CA_CERT = System.getenv().getOrDefault("TEST_CA_CERT", "ca.crt"); private static final String TEST_SERVER_CERT = System.getenv().getOrDefault("TEST_SERVER_CERT", "redis.crt"); public static void setCustomTrustStore(Path customTrustStorePath, String customTrustStorePassword) { // Store original properties originalTrustStore = System.getProperty(TRUST_STORE_PROPERTY); originalTrustStorePassword = System.getProperty(TRUST_STORE_PASSWORD_PROPERTY); originalTrustStoreType = System.getProperty(TRUST_STORE_TYPE_PROPERTY); // Set new properties for the custom truststore System.setProperty(TRUST_STORE_PROPERTY, customTrustStorePath.toAbsolutePath().toString()); System.setProperty(TRUST_STORE_TYPE_PROPERTY, TRUST_STORE_TYPE); if (customTrustStorePassword != null) { System.setProperty(TRUST_STORE_PASSWORD_PROPERTY, customTrustStorePassword); } else { System.clearProperty(TRUST_STORE_PASSWORD_PROPERTY); } reinitializeDefaultSSLContext(); } public static void reinitializeDefaultSSLContext(){ String trustStorePath = System.getProperty(TRUST_STORE_PROPERTY); String trustStorePassword = System.getProperty(TRUST_STORE_PASSWORD_PROPERTY); String trustStoreType = System.getProperty(TRUST_STORE_TYPE_PROPERTY, KeyStore.getDefaultType()); // Load the new truststore KeyStore trustStore = null; try { trustStore = KeyStore.getInstance(trustStoreType); try (java.io.FileInputStream trustStoreStream = new java.io.FileInputStream(trustStorePath)) { trustStore.load(trustStoreStream, trustStorePassword.toCharArray()); } catch (CertificateException | IOException | NoSuchAlgorithmException e) { throw new RuntimeException(e); } TrustManagerFactory tmf = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm()); tmf.init(trustStore); SSLContext newSslContext = SSLContext.getInstance("TLS"); newSslContext.init(null, tmf.getTrustManagers(), null); SSLContext.setDefault(newSslContext); } catch (KeyStoreException | KeyManagementException | NoSuchAlgorithmException e) { throw new RuntimeException(e); } } public static void restoreOriginalTrustStore() { // Restore original properties if (originalTrustStore != null) { System.setProperty(TRUST_STORE_PROPERTY, originalTrustStore); } else { System.clearProperty(TRUST_STORE_PROPERTY); } if ( originalTrustStoreType != null) { System.setProperty(TRUST_STORE_TYPE_PROPERTY, originalTrustStoreType); } else { System.clearProperty(TRUST_STORE_TYPE_PROPERTY); } if (originalTrustStorePassword != null) { System.setProperty(TRUST_STORE_PASSWORD_PROPERTY, originalTrustStorePassword); } else { System.clearProperty(TRUST_STORE_PASSWORD_PROPERTY); } } private static Path envCa(Path certLocation) { if (certLocation.isAbsolute()) { return certLocation.resolve(TEST_CA_CERT); } return Paths.get(TEST_WORK_FOLDER, certLocation.toString(), TEST_CA_CERT); } private static Path envServerCert(Path certLocation) { if (certLocation.isAbsolute()) { return certLocation.resolve(TEST_SERVER_CERT); } return Paths.get(TEST_WORK_FOLDER, certLocation.toString(), TEST_SERVER_CERT); } /** * Resolves the path to a pre-generated PKCS12 keystore for mTLS client authentication. * The Docker container generates .p12 files for each TLS_CLIENT_CNS entry. * * @param certLocation the certificate location from EndpointConfig * @param clientName the name of the client certificate (without extension) * @return the absolute path to the .p12 keystore file */ public static Path clientKeystorePath(Path certLocation, String clientName) { if (certLocation.isAbsolute()) { return certLocation.resolve(clientName + ".p12"); } return Paths.get(TEST_WORK_FOLDER, certLocation.toString(), clientName + ".p12"); } public static Path testTruststorePath(String name) { return Paths.get(TEST_WORK_FOLDER, name + '-' + TEST_TRUSTSTORE); } public static Path createAndSaveTestTruststore(String trustStoreName, List certificateLocations, String truststorePassword) { List trustedCertPaths = new ArrayList<>(); // Traverse each location in certificateLocations and add both CA and Server certificates for (Path location : certificateLocations) { trustedCertPaths.add(envCa(location).toAbsolutePath()); trustedCertPaths.add(envServerCert(location).toAbsolutePath()); } Path trustStorePath = testTruststorePath(trustStoreName).toAbsolutePath(); return createAndSaveTruststore(trustedCertPaths, trustStorePath, truststorePassword); } /** * Creates an empty truststore. * * @return An empty KeyStore object. * @throws KeyStoreException If there's an error initializing the truststore. * @throws IOException If there's an error loading the truststore. * @throws NoSuchAlgorithmException If the algorithm used to check the integrity of the truststore cannot be found. * @throws CertificateException If any of the certificates in the truststore could not be loaded. */ private static KeyStore createTruststore() throws KeyStoreException, IOException, NoSuchAlgorithmException, CertificateException { KeyStore trustStore = KeyStore.getInstance(TRUST_STORE_TYPE); trustStore.load(null, null); return trustStore; } /** * Adds a trusted certificate to the given truststore. * * @param trustStore The KeyStore object. * @param alias Alias for the certificate. * @param certPath Path to the certificate file. * @throws Exception If there's an error adding the certificate. */ private static void addTrustedCertificate(KeyStore trustStore, String alias, Path certPath) throws Exception { X509Certificate cert = loadCertificate(certPath); trustStore.setCertificateEntry(alias, cert); } /** * Loads an X.509 certificate from the given file path. * * @param certPath Path to the certificate file. * @return An X509Certificate object. * @throws Exception If there's an error loading the certificate. */ private static X509Certificate loadCertificate(Path certPath) throws Exception { try (FileInputStream fis = new FileInputStream(certPath.toFile())) { CertificateFactory certFactory = CertificateFactory.getInstance("X.509"); return (X509Certificate) certFactory.generateCertificate(fis); } } /** * Creates a truststore, adds multiple trusted certificates, and saves it to the specified path. * * @param trustedCertPaths List of certificate file paths to add to the truststore. * @param truststorePath Path to save the generated truststore. * @param truststorePassword Password for the truststore. * @return Path to the saved truststore file. */ public static Path createAndSaveTruststore(List trustedCertPaths, Path truststorePath, String truststorePassword) { try { KeyStore trustStore = createTruststore(); for (Path certPath : trustedCertPaths) { addTrustedCertificate(trustStore, "trusted-cert-" + UUID.randomUUID(), certPath); } try (FileOutputStream fos = new FileOutputStream(truststorePath.toFile())) { trustStore.store(fos, truststorePassword.toCharArray()); } catch (IOException e) { throw new RuntimeException("Failed to save truststore to " + truststorePath + ": " + e.getMessage(), e); } } catch (Exception e) { throw new RuntimeException("Failed to create and save truststore: " + e.getMessage(), e); } return truststorePath; } /** * Creates an SSLSocketFactory that trusts all certificates in truststore.jceks. * for given test environment */ public static SSLSocketFactory sslSocketFactoryForEnv(Path certLocations){ return sslSocketFactory(envCa(certLocations)); } /** * Returns SSLSocketFactory configured with Truststore containing provided CA cert */ private static SSLSocketFactory sslSocketFactory(Path trustedCertPath) { KeyStore trustStore = null; try { trustStore = createTruststore(); addTrustedCertificate(trustStore, "trusted-cert-" + UUID.randomUUID(), trustedCertPath.toAbsolutePath()); TrustManagerFactory trustManagerFactory = TrustManagerFactory.getInstance("PKIX"); trustManagerFactory.init(trustStore); TrustManager[] trustManagers = trustManagerFactory.getTrustManagers(); SSLContext sslContext = SSLContext.getInstance("TLS"); sslContext.init(null, trustManagers, new SecureRandom()); return sslContext.getSocketFactory(); } catch (Exception e) { throw new RuntimeException("Failed to initialise SslSocketFactory for " + trustedCertPath , e); } } /** * Creates an SSLSocketFactory with a trust manager that does not trust any certificates. */ public static SSLSocketFactory createTrustNoOneSslSocketFactory() throws Exception { TrustManager[] unTrustManagers = new TrustManager[]{new X509TrustManager() { public X509Certificate[] getAcceptedIssuers() { return new X509Certificate[0]; } public void checkClientTrusted(X509Certificate[] chain, String authType) throws CertificateException { throw new CertificateException("Using a trust manager that does not trust any certificates for test purposes!",new InvalidAlgorithmParameterException()); } public void checkServerTrusted(X509Certificate[] chain, String authType) throws CertificateException { throw new CertificateException("Using a trust manager that does not trust any certificates for test purposes!", new InvalidAlgorithmParameterException()); } }}; SSLContext sslContext = SSLContext.getInstance("TLS"); sslContext.init(null, unTrustManagers, new SecureRandom()); return sslContext.getSocketFactory(); } public static void createEmptyTruststore(Path emptyTrustStore, char[] trustStorePassword) throws Exception { // Create empty truststore KeyStore ks = KeyStore.getInstance("JKS"); ks.load(null, trustStorePassword); // Save truststore try (FileOutputStream fos = new FileOutputStream(emptyTrustStore.toFile())) { ks.store(fos, trustStorePassword); } } /** * Very basic hostname verifier implementation for testing. NOT recommended for production. */ public static class BasicHostnameVerifier implements HostnameVerifier { private static final String COMMON_NAME_RDN_PREFIX = "CN="; @Override public boolean verify(String hostname, SSLSession session) { X509Certificate peerCertificate; try { peerCertificate = (X509Certificate) session.getPeerCertificates()[0]; } catch (SSLPeerUnverifiedException e) { throw new IllegalStateException("The session does not contain a peer X.509 certificate.", e); } String peerCertificateCN = getCommonName(peerCertificate); return hostname.equals(peerCertificateCN); } private String getCommonName(X509Certificate peerCertificate) { String subjectDN = peerCertificate.getSubjectDN().getName(); String[] dnComponents = subjectDN.split(","); for (String dnComponent : dnComponents) { dnComponent = dnComponent.trim(); if (dnComponent.startsWith(COMMON_NAME_RDN_PREFIX)) { return dnComponent.substring(COMMON_NAME_RDN_PREFIX.length()); } } throw new IllegalArgumentException("The certificate has no common name."); } } } ================================================ FILE: src/test/java/redis/clients/jedis/util/VectorTestUtils.java ================================================ package redis.clients.jedis.util; import java.nio.ByteBuffer; import java.nio.ByteOrder; import java.util.ArrayList; import java.util.List; /** * Utility class for vector-related test operations. Provides methods for converting between float * arrays and FP32 byte representations. */ public class VectorTestUtils { /** * Convert float array to FP32 byte blob (IEEE 754 format). Each float is converted to 4 bytes in * little-endian order. * @param floats the float array to convert * @return byte array containing FP32 representation */ public static byte[] floatArrayToFP32Bytes(float[] floats) { byte[] bytes = new byte[floats.length * 4]; // 4 bytes per float for (int i = 0; i < floats.length; i++) { int bits = Float.floatToIntBits(floats[i]); bytes[i * 4] = (byte) (bits & 0xFF); bytes[i * 4 + 1] = (byte) ((bits >> 8) & 0xFF); bytes[i * 4 + 2] = (byte) ((bits >> 16) & 0xFF); bytes[i * 4 + 3] = (byte) ((bits >> 24) & 0xFF); } return bytes; } /** * Convert FP32 byte blob back to float array (IEEE 754 format). Uses ByteBuffer for clean and * readable conversion from little-endian bytes. * @param fp32Bytes the FP32 byte array to convert * @return List of Float values reconstructed from the byte data */ public static List fp32BytesToFloatArray(byte[] fp32Bytes) { ByteBuffer buffer = ByteBuffer.wrap(fp32Bytes).order(ByteOrder.LITTLE_ENDIAN); List floats = new ArrayList<>(); while (buffer.remaining() >= 4) { floats.add(buffer.getFloat()); } return floats; } } ================================================ FILE: src/test/resources/cert.pem ================================================ -----BEGIN CERTIFICATE----- MIIFYTCCA0mgAwIBAgIJAKFpzRp2tUTDMA0GCSqGSIb3DQEBCwUAMEcxDjAMBgNVBAoMBWplZGlz MRMwEQYDVQQIDApTb21lLVN0YXRlMQswCQYDVQQGEwJBUjETMBEGA1UEAwwKamVkaXMtdGVzdDAe Fw0xNjAxMTExOTIyMzRaFw0yMTAxMTAxOTIyMzRaMEcxDjAMBgNVBAoMBWplZGlzMRMwEQYDVQQI DApTb21lLVN0YXRlMQswCQYDVQQGEwJBUjETMBEGA1UEAwwKamVkaXMtdGVzdDCCAiIwDQYJKoZI hvcNAQEBBQADggIPADCCAgoCggIBAMrnt94Om3LEhQd8AA8OaIst38px5JlOYDcUAiifh8ONlGrd OTDvgsNQpF1eB9gbEwsbxQEeF1C8zr+O2+xl/XLgC1aeVYX05+tIUZxuncrTNqdB5ke721myS+TE Q2j4Q0zNm2qOPHboTwBOSpCZfP62e0RjGh4s+ghf4rmY254/H1OMO0jZ0Vlova5NaiK7FLZu5xp4 TQEGTTtkHVmFIZK+5KJ49KItFfrSaqJddnUvP2tt6mhZjW1TvnvyOtyhYWStzsS15TTzppJyU/Fj i4ETokVhB4S7xln7D+/OK3Y7VUKyNsejI4F2N+hdmF33WLz9TyZEVR6PiUWytBWjSF0OqfCxWsHJ Ii3JVs4Z7Dx2xcARAgP4HhjotW2KiLD043dE3vzaPQRKFGO3qpXkOULqoMAxl5EJHRj27twPzhtC 1xDyZtoQv8gi1dXtt0CuR/HcwuGPFw+28JBcaaPeXmnTIMerrrq6de7/ITZbrWM3GOQNhflJ0Gve LANibReQv0Ms0dZ6FoOdcaQBuzN1X2lSIWkQ4ZLKGW5eNjK1KrSd9MGGhydGI7k+xyEyq6uo/nYA p/vZpevGx8bR9G+R9Tet0AIxnL4VsoVG/OciSxCgcMTJf58Q1pJS86LRK17iZ3X9mN+Q905jVbuj Ls4n2E/Hl6akJINRU8odt6KiBPEHAgMBAAGjUDBOMB0GA1UdDgQWBBTH4N06+osTbe7M5GQiVMis tQ990TAfBgNVHSMEGDAWgBTH4N06+osTbe7M5GQiVMistQ990TAMBgNVHRMEBTADAQH/MA0GCSqG SIb3DQEBCwUAA4ICAQA3n4GzRWXyNC5SF0Pp2OZeWRe2Ms44pSIvqM6PijP84dX1h2XH9Zk9L8gz GDS1KWhAaF5L5UMSUqHPLiMHxmNowU5zmXKDnmUaaQB1EX+yqV/U8GQ/xfTwM+Zu/K1fWiAQqx2g ATIze8rIOrZW5rPEJO7Www84WV5QNyH7nq1wRyqqDiy7e8tqrCWHGJ9jiF94lhUH2AcqUilZOA8F pdmYDTFceZ/Eavdy+SSkIUAlLTj7Ncqz3cT1FnZdwHGMidDqnnD85CDdfOvlQ1qeKION1GGsteG8 tGtGJPnVt6QEMVJXCHUl5mC+lgbg85+8KDzJ+jWLXhACm+yaKHKRESk0ycYHEtWoGnsqowor30FZ mm5BH6U7TQJanHWsu/X/ZPRyjcZrw+Feo+7YlQWXh94dZDj6p8kRcvT6Juk5MyLYCFNSlhD7bDwa W8Gb5tU1DWbBiMLeuyF8sqo2Cw1wksYXcHFzotPjupaWSt19R/l4YxIwGfe3vQXNiEdKrK3Bi70c e8mjTawsZvxNE3hAsUCCrAyD7We4wI1WmYw44Ss4awqjA6R0aJZyUrVr+r5Xdpq40ZLS9M3rxdWO G19j+IqqdQSRCLnVVjrKLfkAI42t3edNmAP3cGIU12F2Y5WKSH71kjZUbeIJRqd1wu8l2zYHLzJ6 N0Muc2vs4WZhBC7wSg== -----END CERTIFICATE----- ================================================ FILE: src/test/resources/endpoints.json ================================================ { "sentinel-standalone0": { "tls": false, "username": "sentinel", "password": "foobared", "endpoints": [ "redis://127.0.0.1:26379" ] }, "sentinel-standalone0-tls": { "tls": true, "username": "sentinel", "password": "foobared", "endpoints": [ "redis://127.0.0.1:36379" ] }, "sentinel-standalone2-1": { "tls": false, "endpoints": [ "redis://127.0.0.1:26380" ] }, "sentinel-failover": { "tls": false, "endpoints": [ "redis://127.0.0.1:26381" ] }, "sentinel-standalone2-3": { "tls": false, "endpoints": [ "redis://127.0.0.1:26382" ] }, "redis-failover-1": { "tls": false, "endpoints": [ "redis://127.0.0.1:29379" ] }, "redis-failover-2": { "tls": false, "endpoints": [ "redis://127.0.0.1:29380" ] }, "standalone0": { "password": "foobared", "tls": false, "endpoints": [ "redis://localhost:6379" ] }, "standalone0-tls": { "username": "default", "password": "foobared", "tls": true, "tls_cert_path": "redis1-2-5-8-sentinel/work/tls", "endpoints": [ "rediss://localhost:6390" ] }, "standalone0-acl": { "username": "acljedis", "password": "fizzbuzz", "tls": false, "endpoints": [ "redis://localhost:6379" ] }, "standalone0-acl-tls": { "username": "acljedis", "password": "fizzbuzz", "tls": true, "tls_cert_path": "redis1-2-5-8-sentinel/work/tls", "endpoints": [ "rediss://localhost:6390" ] }, "standalone1": { "username": "default", "password": "foobared", "tls": false, "endpoints": [ "redis://localhost:6380" ] }, "standalone2-primary": { "username": "default", "password": "foobared", "tls": false, "endpoints": [ "redis://127.0.0.1:6381" ] }, "standalone3-replica-of-standalone2": { "username": "default", "password": "foobared", "tls": false, "endpoints": [ "redis://localhost:6382" ] }, "standalone4-replica-of-standalone1": { "username": "default", "password": "foobared", "tls": false, "endpoints": [ "redis://localhost:6383" ] }, "standalone7-with-lfu-policy": { "username": "default", "password": "foobared", "tls": false, "endpoints": [ "redis://localhost:6386" ] }, "standalone9-failover": { "tls": false, "endpoints": [ "redis://localhost:6388" ] }, "standalone10-replica-of-standalone9": { "tls": false, "endpoints": [ "redis://localhost:6389" ] }, "modules-docker": { "tls": false, "endpoints": [ "redis://localhost:6479" ] }, "cluster-unbound": { "password": "cluster", "tls": false, "endpoints": [ "redis://127.0.0.1:7379", "redis://127.0.0.1:7380", "redis://127.0.0.1:7381", "redis://127.0.0.1:7382", "redis://127.0.0.1:7383", "redis://127.0.0.1:7384" ] }, "cluster-stable": { "password": "cluster", "tls": false, "endpoints": [ "redis://127.0.0.1:7479", "redis://127.0.0.1:7480", "redis://127.0.0.1:7481" ] }, "cluster-stable-tls": { "password": "cluster", "tls": true, "tls_cert_path": "cluster-stable/work/tls", "endpoints": [ "rediss://localhost:8479", "rediss://localhost:8480", "rediss://localhost:8481" ] }, "cluster-stack": { "password": "cluster", "tls": false, "endpoints": [ "redis://127.0.0.1:16479", "redis://127.0.0.1:16480", "redis://127.0.0.1:16481" ] }, "standalone-mtls": { "tls": true, "tls_cert_path": "standalone-mtls/work/tls", "endpoints": [ "rediss://localhost:6790" ] }, "cluster-mtls": { "tls": true, "tls_cert_path": "cluster-mtls/work/tls", "endpoints": [ "rediss://localhost:8579", "rediss://localhost:8580", "rediss://localhost:8581" ] } } ================================================ FILE: src/test/resources/endpoints_source.json ================================================ { "standalone0": { "password": "foobared", "tls": false, "endpoints": [ "redis://localhost:6379" ] } } ================================================ FILE: src/test/resources/env/cluster-unbound/config/node-7379-8379/redis.conf ================================================ bind 0.0.0.0 port 7379 requirepass cluster cluster-node-timeout 150 save "" appendonly no cluster-enabled yes ================================================ FILE: src/test/resources/env/cluster-unbound/config/node-7380-8380/redis.conf ================================================ bind 0.0.0.0 port 7380 requirepass cluster cluster-node-timeout 150 save "" appendonly no cluster-enabled yes ================================================ FILE: src/test/resources/env/cluster-unbound/config/node-7381-8381/redis.conf ================================================ bind 0.0.0.0 port 7381 requirepass cluster cluster-node-timeout 150 save "" appendonly no cluster-enabled yes ================================================ FILE: src/test/resources/env/cluster-unbound/config/node-7382-8382/redis.conf ================================================ bind 0.0.0.0 requirepass cluster port 7382 cluster-node-timeout 150 save "" appendonly no cluster-enabled yes ================================================ FILE: src/test/resources/env/cluster-unbound/config/node-7383-8383/redis.conf ================================================ bind 0.0.0.0 port 7383 requirepass cluster cluster-node-timeout 150 save "" appendonly no cluster-enabled yes ================================================ FILE: src/test/resources/env/config/redis6-7/node-sentinel-26381-36381/redis.conf ================================================ port 26380 tls-port 36380 tls-auth-clients no user deploy on allcommands allkeys >verify sentinel monitor mymaster 127.0.0.1 6381 1 sentinel auth-pass mymaster foobared sentinel down-after-milliseconds mymaster 2000 sentinel parallel-syncs mymaster 1 sentinel failover-timeout mymaster 120000 ================================================ FILE: src/test/resources/env/docker-compose.yml ================================================ x-client-libs-image: &client-libs-image image: "${CLIENT_LIBS_TEST_IMAGE}:${CLIENT_LIBS_TEST_IMAGE_TAG:-${REDIS_VERSION:-}}" x-client-libs-stack-image: &client-libs-stack-image image: "${CLIENT_LIBS_TEST_IMAGE}:${CLIENT_LIBS_TEST_IMAGE_TAG:-${REDIS_STACK_VERSION:-${REDIS_VERSION:-}}}" services: toxiproxy: image: ghcr.io/shopify/toxiproxy:2.8.0 ports: - "8474:8474" # Admin API - "29379:29379" # redis-failover-1 proxy - "29380:29380" # redis-failover-2 proxy redis-failover-1: <<: *client-libs-image container_name: redis-failover-1 environment: - PORT=9379 ports: - 9379:9379 redis-failover-2: <<: *client-libs-image container_name: redis-failover-2 environment: - PORT=9380 ports: - 9380:9380 redis1-2-5-8-sentinel: sysctls: - net.ipv6.conf.all.disable_ipv6=1 <<: *client-libs-image container_name: redis1-2-5-8-sentinel #network_mode: host environment: - REDIS_CLUSTER=no - REDIS_CLIENT_USER=deploy - REDIS_CLIENT_PASSWORD=verify - TLS_ENABLED=yes ports: - "6379:6379" - "6380:6380" - "6383:6383" - "6386:6386" - "6390:6390" - "6391:6391" - "26379:26379" # sentinel - "36379:36379" # sentinel tls command: ${ENABLE_MODULE_COMMAND_DIRECTIVE} volumes: - ${REDIS_ENV_CONF_DIR}/redis1-2-5-8-sentinel/config:/redis/config:r - ${REDIS_ENV_WORK_DIR}/redis1-2-5-8-sentinel/work:/redis/work:rw standalone2-sentinel: sysctls: - net.ipv6.conf.all.disable_ipv6=1 <<: *client-libs-image container_name: standalone2-sentinel #network_mode: host environment: - REDIS_CLUSTER=no - TLS_ENABLED=yes - REDIS_PASSWORD=foobared ports: - "6381:6381" - "16381:16381" - "6382:6382" - "16382:16382" - "26380:26380" # sentinel-standalone2-1 - "36380:36380" # sentinel tls - "26382:26382" # sentinel-standalone2-3 - "36382:36382" # sentinel tls volumes: - ${REDIS_ENV_CONF_DIR}/standalone2-sentinel/config:/redis/config:r - ${REDIS_ENV_WORK_DIR}/standalone2-sentinel/work:/redis/work:rw sentinel-failover: sysctls: - net.ipv6.conf.all.disable_ipv6=1 <<: *client-libs-image container_name: sentinel-standalone2-failover #network_mode: host environment: - REDIS_CLUSTER=no - REDIS_PASSWORD=foobared ports: - "6384:6384" - "6385:6385" - "26381:26381" # sentinel - "36381:36381" # sentinel tls volumes: - ${REDIS_ENV_CONF_DIR}/sentinel-standalone2-failover/config:/redis/config:r - ${REDIS_ENV_WORK_DIR}/sentinel-standalone2-failover/work:/redis/work:rw redis9-10: sysctls: - net.ipv6.conf.all.disable_ipv6=1 <<: *client-libs-image container_name: redis9-10 #network_mode: host environment: - REDIS_CLUSTER=no ports: - "6388:6388" - "6389:6389" volumes: - ${REDIS_ENV_CONF_DIR}/redis9-10/config:/redis/config:r - ${REDIS_ENV_WORK_DIR}/redis9-10/work:/redis/work:rw redis-unavailable: sysctls: - net.ipv6.conf.all.disable_ipv6=1 <<: *client-libs-image container_name: redis-unavailable-1 #network_mode: host environment: - REDIS_CLUSTER=no - PORT=6400 ports: - "6400:6400" volumes: - ${REDIS_ENV_WORK_DIR}/redis-unavailable/work:/redis/work:rw cluster-unbound: sysctls: - net.ipv6.conf.all.disable_ipv6=1 <<: *client-libs-image container_name: cluster-unbound-1 environment: - REDIS_PASSWORD=cluster ports: - "7379-7383:7379-7383" volumes: - ${REDIS_ENV_CONF_DIR}/cluster-unbound/config:/redis/config:r - ${REDIS_ENV_WORK_DIR}/cluster-unbound/work:/redis/work:rw #TLS endpoints of Cluster stable are used for TLS tests that do not require client authentication cluster-stable: sysctls: - net.ipv6.conf.all.disable_ipv6=1 <<: *client-libs-image container_name: cluster-stable-1 #network_mode: host command: --cluster-preferred-endpoint-type hostname --cluster-announce-hostname "localhost" --cluster-node-timeout 150 --tls-auth-clients no --save "" environment: - REDIS_CLUSTER=yes - REDIS_PASSWORD=cluster - PORT=7479 - TLS_PORT=8479 - NODES=6 - REPLICAS=1 - TLS_ENABLED=yes - TLS_AUTH_CLIENTS_USER=off ports: - "7479-7484:7479-7484" - "8479-8484:8479-8484" volumes: - ${REDIS_ENV_CONF_DIR}/cluster-stable/config:/redis/config:r - ${REDIS_ENV_WORK_DIR}/cluster-stable/work:/redis/work:rw jedis-stack: sysctls: - net.ipv6.conf.all.disable_ipv6=1 <<: *client-libs-stack-image container_name: jedis-stack command: --save "" environment: - REDIS_CLUSTER=no - PORT=6479 ports: - "6479:6479" volumes: - ${REDIS_ENV_WORK_DIR}/jedis-stack/work:/redis/work:rw #todo find a way to connect from mac os host to exposed unix socket in container # redis_uds: # <<: *client-libs-image # container_name: redis_uds # #network_mode: host # command: redis-server /etc/redis.conf # volumes: # - "./redis_uds/config/node-0/redis.conf:/etc/redis.conf" # - "./work/redis_uds/work:/tmp/docker/" # Standalone Redis instance with mTLS (mutual TLS) authentication # Uses TLS_CLIENT_CNS to generate client certificates for mTLS users # TLS_AUTH_CLIENTS_USER=CN (default) extracts username from client certificate CN # ACL users matching client certificate CNs are configured via --user directives standalone-mtls: sysctls: - net.ipv6.conf.all.disable_ipv6=1 <<: *client-libs-image container_name: standalone-mtls command: - "--save" - "" - "--user" - "mtls-user1" - "on" - "nopass" - "~*" - "&*" - "+@all" - "--user" - "mtls-user2" - "on" - "nopass" - "~*" - "&*" - "+@all" environment: - REDIS_CLUSTER=no - TLS_ENABLED=yes - TLS_PORT=6790 - PORT=6789 - TLS_CLIENT_CNS=mtls-user1 mtls-user2 mtls-user-without-acl ports: # We don't expose non-TLS port on purpose - "6790:6790" volumes: - ${REDIS_ENV_WORK_DIR}/standalone-mtls/work:/redis/work:rw # Redis cluster with mTLS (mutual TLS) authentication # 3 nodes without replicas to minimize memory footprint # TLS_AUTH_CLIENTS_USER=CN (default) extracts username from client certificate CN # ACL users matching client certificate CNs are configured via --user directives cluster-mtls: sysctls: - net.ipv6.conf.all.disable_ipv6=1 <<: *client-libs-image container_name: cluster-mtls command: - "--cluster-preferred-endpoint-type" - "hostname" - "--cluster-announce-hostname" - "localhost" - "--cluster-node-timeout" - "150" - "--save" - "" - "--user" - "mtls-user1" - "on" - "nopass" - "~*" - "&*" - "+@all" - "--user" - "mtls-user2" - "on" - "nopass" - "~*" - "&*" - "+@all" environment: - REDIS_CLUSTER=yes - TLS_ENABLED=yes - TLS_PORT=8579 - PORT=7579 - NODES=3 - REPLICAS=0 - TLS_CLIENT_CNS=mtls-user1 mtls-user2 mtls-user-without-acl ports: # We don't expose non-TLS ports on purpose - "8579-8581:8579-8581" volumes: - ${REDIS_ENV_WORK_DIR}/cluster-mtls/work:/redis/work:rw ================================================ FILE: src/test/resources/env/redis-uds/config/node-0/redis.conf ================================================ unixsocket /tmp/docker/redis.sock unixsocketperm 777 ================================================ FILE: src/test/resources/env/redis1-2-5-8-sentinel/config/node-6379-6390/redis.conf ================================================ port 6379 tls-port 6390 requirepass foobared user deploy on allcommands allkeys >verify user acljedis on allcommands allkeys >fizzbuzz save "" appendonly no tls-auth-clients no # Not supported on v6. provided as argument on node start # enable-module-command yes client-output-buffer-limit pubsub 256k 128k 5 ================================================ FILE: src/test/resources/env/redis1-2-5-8-sentinel/config/node-6380/redis.conf ================================================ protected-mode no port 6380 user deploy on allcommands allkeys >verify requirepass foobared pidfile /tmp/redis2.pid logfile /tmp/redis2.log save "" appendonly no ================================================ FILE: src/test/resources/env/redis1-2-5-8-sentinel/config/node-6383-6391/redis.conf ================================================ port 6383 tls-port 6391 user deploy on allcommands allkeys >verify requirepass foobared masterauth foobared tls-auth-clients no save "" appendonly no slaveof localhost 6379 ================================================ FILE: src/test/resources/env/redis1-2-5-8-sentinel/config/node-6386/redis.conf ================================================ protected-mode no port 6386 user deploy on allcommands allkeys >verify save "" appendonly no maxmemory-policy allkeys-lfu ================================================ FILE: src/test/resources/env/redis1-2-5-8-sentinel/config/node-sentinel-26379-36379/redis.conf ================================================ port 26379 tls-port 36379 tls-auth-clients no user default off user deploy on allcommands allkeys >verify user sentinel on allcommands allkeys allchannels >foobared sentinel monitor aclmaster 127.0.0.1 6379 1 sentinel auth-user aclmaster acljedis sentinel auth-pass aclmaster fizzbuzz sentinel down-after-milliseconds aclmaster 2000 sentinel failover-timeout aclmaster 120000 sentinel parallel-syncs aclmaster 1 ================================================ FILE: src/test/resources/env/redis9-10/config/node-6388/redis.conf ================================================ port 6388 save "" appendonly no ================================================ FILE: src/test/resources/env/redis9-10/config/node-6389/redis.conf ================================================ port 6389 save "" appendonly no replicaof localhost 6388 ================================================ FILE: src/test/resources/env/sentinel-standalone2-failover/config/node-6384/redis.conf ================================================ port 6384 requirepass foobared masterauth foobared save "" appendonly no ================================================ FILE: src/test/resources/env/sentinel-standalone2-failover/config/node-6385/redis.conf ================================================ port 6385 requirepass foobared masterauth foobared save "" appendonly no slaveof localhost 6384 ================================================ FILE: src/test/resources/env/sentinel-standalone2-failover/config/node-sentinel-26381-36381/redis.conf ================================================ port 26381 protected-mode no sentinel monitor mymasterfailover 127.0.0.1 6384 1 sentinel auth-pass mymasterfailover foobared sentinel down-after-milliseconds mymasterfailover 2000 sentinel failover-timeout mymasterfailover 120000 sentinel parallel-syncs mymasterfailover 1 ================================================ FILE: src/test/resources/env/standalone2-sentinel/config/node-6381-16381/redis.conf ================================================ port 6381 tls-port 16381 requirepass foobared masterauth foobared save "" appendonly no ================================================ FILE: src/test/resources/env/standalone2-sentinel/config/node-6382-16382/redis.conf ================================================ port 6382 tls-port 16382 requirepass foobared masterauth foobared save "" appendonly no slaveof localhost 6381 ================================================ FILE: src/test/resources/env/standalone2-sentinel/config/node-sentinel-26380-36380/redis.conf ================================================ port 26380 tls-port 36380 tls-auth-clients no sentinel monitor mymaster 127.0.0.1 6381 1 sentinel auth-pass mymaster foobared sentinel down-after-milliseconds mymaster 2000 sentinel parallel-syncs mymaster 1 sentinel failover-timeout mymaster 120000 ================================================ FILE: src/test/resources/env/standalone2-sentinel/config/node-sentinel-26382-36382/redis.conf ================================================ port 26382 tls-port 36382 tls-auth-clients no sentinel monitor mymaster 127.0.0.1 6381 1 sentinel auth-pass mymaster foobared sentinel down-after-milliseconds mymaster 2000 sentinel parallel-syncs mymaster 1 sentinel failover-timeout mymaster 120000 ================================================ FILE: src/test/resources/functions/keyspaceTriggers.js ================================================ #!js api_version=1.0 name=keyspaceTriggers redis.registerKeySpaceTrigger("consumer", "", function(client, data){ if (client.call("type", data.key) != "hash") { // key is not a hash, do not touch it. return; } // get the current time in ms var curr_time = client.call("time")[0]; // set '__last_updated__' with the current time value client.call('hset', data.key, '__last_updated__', curr_time); }); ================================================ FILE: src/test/resources/functions/pingpong.js ================================================ #!js api_version=1.0 name=pingpong function answer(client, data) { return client.call('ping'); } redis.registerFunction('playPingPong', answer, {description: 'You PING, we PONG'}); ================================================ FILE: src/test/resources/functions/streamTriggers.js ================================================ #!js api_version=1.0 name=streamTriggers redis.registerStreamTrigger( "consumer", // consumer name "stream", // streams prefix function(c, data) { // callback to run on each element added to the stream redis.log(JSON.stringify(data, (key, value) => typeof value === 'bigint' ? value.toString() : value // return everything else unchanged )); } ); ================================================ FILE: src/test/resources/functions/withConfig.js ================================================ #!js api_version=1.0 name=withConfig var last_modified_field_name = "__last_modified__" if (redis.config.last_modified_field_name !== undefined) { if (typeof redis.config.last_modified_field_name != 'string') { throw "last_modified_field_name must be a string"; } last_modified_field_name = redis.config.last_modified_field_name } redis.registerFunction("hset", function(client, key, field, val){ // get the current time in ms var curr_time = client.call("time")[0]; return client.call('hset', key, field, val, last_modified_field_name, curr_time); }); ================================================ FILE: src/test/resources/functions/withFlags.js ================================================ #!js api_version=1.0 name=withFlags redis.registerFunction("my_set", (c, key, val) => { return c.call("set", key, val); }, { flags: [redis.functionFlags.RAW_ARGUMENTS] } ); ================================================ FILE: src/test/resources/functions/workingWIthHashes.js ================================================ #!js api_version=1.0 name=hashitout redis.registerFunction('hashy', function(client, key_name){ if (client.call('type', key_name) == 'hash') { return client.call('hgetall', key_name); } throw "Oops, that wasn't a Hash!"; }); ================================================ FILE: src/test/resources/junit-platform.properties ================================================ # JUnit 5 Platform Configuration # Global timeout for all test methods (5 minutes = 300 seconds) # Any test method that exceeds this timeout will automatically fail junit.jupiter.execution.timeout.default = 300s ================================================ FILE: src/test/resources/logback-test.xml ================================================ %d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n target/jedis-test.log true %d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n ================================================ FILE: src/test/resources/redismodule.h ================================================ #ifndef REDISMODULE_H #define REDISMODULE_H #include #include #include /* ---------------- Defines common between core and modules --------------- */ /* Error status return values. */ #define REDISMODULE_OK 0 #define REDISMODULE_ERR 1 /* API versions. */ #define REDISMODULE_APIVER_1 1 /* API flags and constants */ #define REDISMODULE_READ (1<<0) #define REDISMODULE_WRITE (1<<1) #define REDISMODULE_LIST_HEAD 0 #define REDISMODULE_LIST_TAIL 1 /* Key types. */ #define REDISMODULE_KEYTYPE_EMPTY 0 #define REDISMODULE_KEYTYPE_STRING 1 #define REDISMODULE_KEYTYPE_LIST 2 #define REDISMODULE_KEYTYPE_HASH 3 #define REDISMODULE_KEYTYPE_SET 4 #define REDISMODULE_KEYTYPE_ZSET 5 /* Reply types. */ #define REDISMODULE_REPLY_UNKNOWN -1 #define REDISMODULE_REPLY_STRING 0 #define REDISMODULE_REPLY_ERROR 1 #define REDISMODULE_REPLY_INTEGER 2 #define REDISMODULE_REPLY_ARRAY 3 #define REDISMODULE_REPLY_NULL 4 /* Postponed array length. */ #define REDISMODULE_POSTPONED_ARRAY_LEN -1 /* Expire */ #define REDISMODULE_NO_EXPIRE -1 /* Sorted set API flags. */ #define REDISMODULE_ZADD_XX (1<<0) #define REDISMODULE_ZADD_NX (1<<1) #define REDISMODULE_ZADD_ADDED (1<<2) #define REDISMODULE_ZADD_UPDATED (1<<3) #define REDISMODULE_ZADD_NOP (1<<4) /* Hash API flags. */ #define REDISMODULE_HASH_NONE 0 #define REDISMODULE_HASH_NX (1<<0) #define REDISMODULE_HASH_XX (1<<1) #define REDISMODULE_HASH_CFIELDS (1<<2) #define REDISMODULE_HASH_EXISTS (1<<3) /* A special pointer that we can use between the core and the module to signal * field deletion, and that is impossible to be a valid pointer. */ #define REDISMODULE_HASH_DELETE ((RedisModuleString*)(long)1) /* Error messages. */ #define REDISMODULE_ERRORMSG_WRONGTYPE "WRONGTYPE Operation against a key holding the wrong kind of value" #define REDISMODULE_POSITIVE_INFINITE (1.0/0.0) #define REDISMODULE_NEGATIVE_INFINITE (-1.0/0.0) /* ------------------------- End of common defines ------------------------ */ #ifndef REDISMODULE_CORE typedef long long mstime_t; /* Incomplete structures for compiler checks but opaque access. */ typedef struct RedisModuleCtx RedisModuleCtx; typedef struct RedisModuleKey RedisModuleKey; typedef struct RedisModuleString RedisModuleString; typedef struct RedisModuleCallReply RedisModuleCallReply; typedef int (*RedisModuleCmdFunc) (RedisModuleCtx *ctx, RedisModuleString **argv, int argc); #define REDISMODULE_GET_API(name) \ RedisModule_GetApi("RedisModule_" #name, ((void **)&RedisModule_ ## name)) #define REDISMODULE_API_FUNC(x) (*x) int REDISMODULE_API_FUNC(RedisModule_GetApi)(const char *, void *); int REDISMODULE_API_FUNC(RedisModule_CreateCommand)(RedisModuleCtx *ctx, const char *name, RedisModuleCmdFunc cmdfunc, const char *strflags, int firstkey, int lastkey, int keystep); int REDISMODULE_API_FUNC(RedisModule_SetModuleAttribs)(RedisModuleCtx *ctx, const char *name, int ver, int apiver); int REDISMODULE_API_FUNC(RedisModule_WrongArity)(RedisModuleCtx *ctx); int REDISMODULE_API_FUNC(RedisModule_ReplyWithLongLong)(RedisModuleCtx *ctx, long long ll); int REDISMODULE_API_FUNC(RedisModule_GetSelectedDb)(RedisModuleCtx *ctx); int REDISMODULE_API_FUNC(RedisModule_SelectDb)(RedisModuleCtx *ctx, int newid); void *REDISMODULE_API_FUNC(RedisModule_OpenKey)(RedisModuleCtx *ctx, RedisModuleString *keyname, int mode); void REDISMODULE_API_FUNC(RedisModule_CloseKey)(RedisModuleKey *kp); int REDISMODULE_API_FUNC(RedisModule_KeyType)(RedisModuleKey *kp); size_t REDISMODULE_API_FUNC(RedisModule_ValueLength)(RedisModuleKey *kp); int REDISMODULE_API_FUNC(RedisModule_ListPush)(RedisModuleKey *kp, int where, RedisModuleString *ele); RedisModuleString *REDISMODULE_API_FUNC(RedisModule_ListPop)(RedisModuleKey *key, int where); RedisModuleCallReply *REDISMODULE_API_FUNC(RedisModule_Call)(RedisModuleCtx *ctx, const char *cmdname, const char *fmt, ...); const char *REDISMODULE_API_FUNC(RedisModule_CallReplyProto)(RedisModuleCallReply *reply, size_t *len); void REDISMODULE_API_FUNC(RedisModule_FreeCallReply)(RedisModuleCallReply *reply); int REDISMODULE_API_FUNC(RedisModule_CallReplyType)(RedisModuleCallReply *reply); long long REDISMODULE_API_FUNC(RedisModule_CallReplyInteger)(RedisModuleCallReply *reply); size_t REDISMODULE_API_FUNC(RedisModule_CallReplyLength)(RedisModuleCallReply *reply); RedisModuleCallReply *REDISMODULE_API_FUNC(RedisModule_CallReplyArrayElement)(RedisModuleCallReply *reply, size_t idx); RedisModuleString *REDISMODULE_API_FUNC(RedisModule_CreateString)(RedisModuleCtx *ctx, const char *ptr, size_t len); RedisModuleString *REDISMODULE_API_FUNC(RedisModule_CreateStringFromLongLong)(RedisModuleCtx *ctx, long long ll); void REDISMODULE_API_FUNC(RedisModule_FreeString)(RedisModuleCtx *ctx, RedisModuleString *str); const char *REDISMODULE_API_FUNC(RedisModule_StringPtrLen)(RedisModuleString *str, size_t *len); int REDISMODULE_API_FUNC(RedisModule_ReplyWithError)(RedisModuleCtx *ctx, const char *err); int REDISMODULE_API_FUNC(RedisModule_ReplyWithSimpleString)(RedisModuleCtx *ctx, const char *msg); int REDISMODULE_API_FUNC(RedisModule_ReplyWithArray)(RedisModuleCtx *ctx, long len); void REDISMODULE_API_FUNC(RedisModule_ReplySetArrayLength)(RedisModuleCtx *ctx, long len); int REDISMODULE_API_FUNC(RedisModule_ReplyWithStringBuffer)(RedisModuleCtx *ctx, const char *buf, size_t len); int REDISMODULE_API_FUNC(RedisModule_ReplyWithString)(RedisModuleCtx *ctx, RedisModuleString *str); int REDISMODULE_API_FUNC(RedisModule_ReplyWithNull)(RedisModuleCtx *ctx); int REDISMODULE_API_FUNC(RedisModule_ReplyWithDouble)(RedisModuleCtx *ctx, double d); int REDISMODULE_API_FUNC(RedisModule_ReplyWithCallReply)(RedisModuleCtx *ctx, RedisModuleCallReply *reply); int REDISMODULE_API_FUNC(RedisModule_StringToLongLong)(RedisModuleString *str, long long *ll); int REDISMODULE_API_FUNC(RedisModule_StringToDouble)(RedisModuleString *str, double *d); void REDISMODULE_API_FUNC(RedisModule_AutoMemory)(RedisModuleCtx *ctx); int REDISMODULE_API_FUNC(RedisModule_Replicate)(RedisModuleCtx *ctx, const char *cmdname, const char *fmt, ...); int REDISMODULE_API_FUNC(RedisModule_ReplicateVerbatim)(RedisModuleCtx *ctx); const char *REDISMODULE_API_FUNC(RedisModule_CallReplyStringPtr)(RedisModuleCallReply *reply, size_t *len); RedisModuleString *REDISMODULE_API_FUNC(RedisModule_CreateStringFromCallReply)(RedisModuleCallReply *reply); int REDISMODULE_API_FUNC(RedisModule_DeleteKey)(RedisModuleKey *key); int REDISMODULE_API_FUNC(RedisModule_StringSet)(RedisModuleKey *key, RedisModuleString *str); char *REDISMODULE_API_FUNC(RedisModule_StringDMA)(RedisModuleKey *key, size_t *len, int mode); int REDISMODULE_API_FUNC(RedisModule_StringTruncate)(RedisModuleKey *key, size_t newlen); mstime_t REDISMODULE_API_FUNC(RedisModule_GetExpire)(RedisModuleKey *key); int REDISMODULE_API_FUNC(RedisModule_SetExpire)(RedisModuleKey *key, mstime_t expire); int REDISMODULE_API_FUNC(RedisModule_ZsetAdd)(RedisModuleKey *key, double score, RedisModuleString *ele, int *flagsptr); int REDISMODULE_API_FUNC(RedisModule_ZsetIncrby)(RedisModuleKey *key, double score, RedisModuleString *ele, int *flagsptr, double *newscore); int REDISMODULE_API_FUNC(RedisModule_ZsetScore)(RedisModuleKey *key, RedisModuleString *ele, double *score); int REDISMODULE_API_FUNC(RedisModule_ZsetRem)(RedisModuleKey *key, RedisModuleString *ele, int *deleted); void REDISMODULE_API_FUNC(RedisModule_ZsetRangeStop)(RedisModuleKey *key); int REDISMODULE_API_FUNC(RedisModule_ZsetFirstInScoreRange)(RedisModuleKey *key, double min, double max, int minex, int maxex); int REDISMODULE_API_FUNC(RedisModule_ZsetLastInScoreRange)(RedisModuleKey *key, double min, double max, int minex, int maxex); int REDISMODULE_API_FUNC(RedisModule_ZsetFirstInLexRange)(RedisModuleKey *key, RedisModuleString *min, RedisModuleString *max); int REDISMODULE_API_FUNC(RedisModule_ZsetLastInLexRange)(RedisModuleKey *key, RedisModuleString *min, RedisModuleString *max); RedisModuleString *REDISMODULE_API_FUNC(RedisModule_ZsetRangeCurrentElement)(RedisModuleKey *key, double *score); int REDISMODULE_API_FUNC(RedisModule_ZsetRangeNext)(RedisModuleKey *key); int REDISMODULE_API_FUNC(RedisModule_ZsetRangePrev)(RedisModuleKey *key); int REDISMODULE_API_FUNC(RedisModule_ZsetRangeEndReached)(RedisModuleKey *key); int REDISMODULE_API_FUNC(RedisModule_HashSet)(RedisModuleKey *key, int flags, ...); int REDISMODULE_API_FUNC(RedisModule_HashGet)(RedisModuleKey *key, int flags, ...); int REDISMODULE_API_FUNC(RedisModule_IsKeysPositionRequest)(RedisModuleCtx *ctx); void REDISMODULE_API_FUNC(RedisModule_KeyAtPos)(RedisModuleCtx *ctx, int pos); unsigned long long REDISMODULE_API_FUNC(RedisModule_GetClientId)(RedisModuleCtx *ctx); /* This is included inline inside each Redis module. */ static int RedisModule_Init(RedisModuleCtx *ctx, const char *name, int ver, int apiver) { void *getapifuncptr = ((void**)ctx)[0]; RedisModule_GetApi = (int (*)(const char *, void *)) (unsigned long)getapifuncptr; REDISMODULE_GET_API(CreateCommand); REDISMODULE_GET_API(SetModuleAttribs); REDISMODULE_GET_API(WrongArity); REDISMODULE_GET_API(ReplyWithLongLong); REDISMODULE_GET_API(ReplyWithError); REDISMODULE_GET_API(ReplyWithSimpleString); REDISMODULE_GET_API(ReplyWithArray); REDISMODULE_GET_API(ReplySetArrayLength); REDISMODULE_GET_API(ReplyWithStringBuffer); REDISMODULE_GET_API(ReplyWithString); REDISMODULE_GET_API(ReplyWithNull); REDISMODULE_GET_API(ReplyWithCallReply); REDISMODULE_GET_API(ReplyWithDouble); REDISMODULE_GET_API(ReplySetArrayLength); REDISMODULE_GET_API(GetSelectedDb); REDISMODULE_GET_API(SelectDb); REDISMODULE_GET_API(OpenKey); REDISMODULE_GET_API(CloseKey); REDISMODULE_GET_API(KeyType); REDISMODULE_GET_API(ValueLength); REDISMODULE_GET_API(ListPush); REDISMODULE_GET_API(ListPop); REDISMODULE_GET_API(StringToLongLong); REDISMODULE_GET_API(StringToDouble); REDISMODULE_GET_API(Call); REDISMODULE_GET_API(CallReplyProto); REDISMODULE_GET_API(FreeCallReply); REDISMODULE_GET_API(CallReplyInteger); REDISMODULE_GET_API(CallReplyType); REDISMODULE_GET_API(CallReplyLength); REDISMODULE_GET_API(CallReplyArrayElement); REDISMODULE_GET_API(CallReplyStringPtr); REDISMODULE_GET_API(CreateStringFromCallReply); REDISMODULE_GET_API(CreateString); REDISMODULE_GET_API(CreateStringFromLongLong); REDISMODULE_GET_API(FreeString); REDISMODULE_GET_API(StringPtrLen); REDISMODULE_GET_API(AutoMemory); REDISMODULE_GET_API(Replicate); REDISMODULE_GET_API(ReplicateVerbatim); REDISMODULE_GET_API(DeleteKey); REDISMODULE_GET_API(StringSet); REDISMODULE_GET_API(StringDMA); REDISMODULE_GET_API(StringTruncate); REDISMODULE_GET_API(GetExpire); REDISMODULE_GET_API(SetExpire); REDISMODULE_GET_API(ZsetAdd); REDISMODULE_GET_API(ZsetIncrby); REDISMODULE_GET_API(ZsetScore); REDISMODULE_GET_API(ZsetRem); REDISMODULE_GET_API(ZsetRangeStop); REDISMODULE_GET_API(ZsetFirstInScoreRange); REDISMODULE_GET_API(ZsetLastInScoreRange); REDISMODULE_GET_API(ZsetFirstInLexRange); REDISMODULE_GET_API(ZsetLastInLexRange); REDISMODULE_GET_API(ZsetRangeCurrentElement); REDISMODULE_GET_API(ZsetRangeNext); REDISMODULE_GET_API(ZsetRangePrev); REDISMODULE_GET_API(ZsetRangeEndReached); REDISMODULE_GET_API(HashSet); REDISMODULE_GET_API(HashGet); REDISMODULE_GET_API(IsKeysPositionRequest); REDISMODULE_GET_API(KeyAtPos); REDISMODULE_GET_API(GetClientId); RedisModule_SetModuleAttribs(ctx,name,ver,apiver); return REDISMODULE_OK; } #else /* Things only defined for the modules core, not exported to modules * including this file. */ #define RedisModuleString robj #endif /* REDISMODULE_CORE */ #endif /* REDISMOUDLE_H */ ================================================ FILE: src/test/resources/testmodule.c ================================================ #include "redismodule.h" #include int HelloworldRand_RedisCommand(RedisModuleCtx *ctx, RedisModuleString **argv, int argc) { RedisModule_ReplyWithLongLong(ctx,rand()); return REDISMODULE_OK; } int RedisModule_OnLoad(RedisModuleCtx *ctx, RedisModuleString **argv, int argc) { if (RedisModule_Init(ctx,"testmodule",1,REDISMODULE_APIVER_1) == REDISMODULE_ERR) return REDISMODULE_ERR; if (RedisModule_CreateCommand(ctx,"testmodule.simple", HelloworldRand_RedisCommand, "readonly",0,0,0) == REDISMODULE_ERR) return REDISMODULE_ERR; }