Module set/get config API#14051
Conversation
🎉 Snyk checks have passed. No issues have been found so far.✅ security/snyk check is complete. No issues have been found. (View Details) ✅ license/snyk check is complete. No issues have been found. (View Details) |
src/config.c
Outdated
There was a problem hiding this comment.
Is this considered a breaking change?
There was a problem hiding this comment.
@sundb @oshadmi I don't think so, as negative values were previously only handled in numericParseString - i.e if the config is not percent config and the value is negative it directly returned error.
Since now I needed to use the numericBoundaryCheck directly I needed to add that check which wouldn't break existing code.
Setting a numeric config through a module though would fail if one passes a negative value and the config is not a PERCENT_CONFIG as it should.
That said, I'm still worried if passing a negative value for percenta configs is confusing:
/* Set the value of a numeric config.
* If the value passed is meant to be a percentage, it should be passed as a
* negative value.
*
* See RedisModule_SetStringConfig for return value. */
int RM_SetNumericConfig(RedisModuleCtx *ctx, const char *name, long long value, RedisModuleString **err);
There was a problem hiding this comment.
we can consider it as a bug instead of a break change.
when ll is negative, we will convert it to unsigned, which might cause us to load an invalid negative config.
Although after checking the code, it seems this case wouldn’t actually happen.
There was a problem hiding this comment.
i just stumbled across this.
i think it's a breaking change.
if someone would have set an insane maxmemory, it the past it would have worked (memory being practically unlimited), and now it'll error.
i.e. the value is negative when we get to this function, but the caller intended to pass a positive value and it'll just be casted to this function as negative.
that is, unless the RESP or config parser would have blocked such a huge number (since they think they're parsing signed values)?
There was a problem hiding this comment.
i didn't carefully read the discussion.
@minchopaskal so you're saying that before this PR there was no way for this function to get a negative value for ULONGLONG config? not from config file and not from config command?
There was a problem hiding this comment.
Actually, yes - for the maxmemory example numericParseString->memtoull would have prevented it. But now with the new module API what you described can happen so needs a fix.
There was a problem hiding this comment.
in the PR you're saying it was a breaking change. but if it didn't break config file parsing and the CONFIG SET command, what did it break? the module APIs didn't yet exist so it can't break them.
am i missing something?
There was a problem hiding this comment.
You are not, it's my bad.
EDIT: It indeed was a breaking change.. I wasn't thinking of such cases (thanks @sundb to pointing this out)
Output from unstable
$ ./src/redis-server --maxmemory 9999999999999999999999999
*** FATAL CONFIG FILE ERROR (Redis 255.255.255) ***
Reading the configuration file, at line 2
>>> 'maxmemory "9999999999999999999999999"'
argument must be greater or equal to 0
oranagra
left a comment
There was a problem hiding this comment.
i only reviewed the API and their docs, not the code.
please consider adding more details in the PR description about the considerations we took, limitations and plans.
e.g. did we have any plans to later add some API to allow atomically set several configs at one (or apply them together)
CE Performance Automation : step 1 of 2 (build) DONE.This comment was automatically generated given a benchmark was triggered.
You can check a comparison in detail via the grafana link |
CE Performance Automation : step 2 of 2 (benchmark) FINISHED.This comment was automatically generated given a benchmark was triggered. Started benchmark suite at 2025-06-24 14:21:18.487824 and took 12565.242281 seconds to finish. In total will run 176 benchmarks. |
Automated performance analysis summaryThis comment was automatically generated given there is performance data available. Using platform named: intel64-ubuntu22.04-redis-gnr1 to do the comparison. In summary:
You can check a comparison in detail via the grafana link Comparison between unstable and minchopaskal:module-config-cmd-api.Time Period from 5 months ago. (environment used: oss-standalone) By GROUP change csv:command_group,min_change,max_change By COMMAND change csv:command,min_change,max_change
Regressions test regexp names: memtier_benchmark-1key-zrank-10M-elements-pipeline-1 Improvements Table
Improvements test regexp names: memtier_benchmark-1Mkeys-load-zset-listpack-with-100-elements-double-score Full Results table:WARNING: There were 333 benchmarks with NO datapoints for both baseline and comparison. NO DATAPOINTS test regexp names: latency-rate-limited-10000_qps-memtier_benchmark-100Kkeys-hash-hgetall-50-fields-100B-values|latency-rate-limited-10000_qps-memtier_benchmark-100Kkeys-load-hash-50-fields-with-1000B-values|latency-rate-limited-10000_qps-memtier_benchmark-100Kkeys-load-hash-50-fields-with-100B-values|latency-rate-limited-10000_qps-memtier_benchmark-100Kkeys-load-hash-50-fields-with-10B-values|latency-rate-limited-10000_qps-memtier_benchmark-10Mkeys-load-hash-5-fields-with-100B-values|latency-rate-limited-10000_qps-memtier_benchmark-10Mkeys-load-hash-5-fields-with-100B-values-pipeline-10|latency-rate-limited-10000_qps-memtier_benchmark-10Mkeys-load-hash-5-fields-with-10B-values|latency-rate-limited-10000_qps-memtier_benchmark-10Mkeys-load-hash-5-fields-with-10B-values-pipeline-10|latency-rate-limited-10000_qps-memtier_benchmark-1Mkeys-100B-expire-use-case|latency-rate-limited-10000_qps-memtier_benchmark-1Mkeys-10B-expire-use-case|latency-rate-limited-10000_qps-memtier_benchmark-1Mkeys-1KiB-expire-use-case|latency-rate-limited-10000_qps-memtier_benchmark-1Mkeys-4KiB-expire-use-case|latency-rate-limited-10000_qps-memtier_benchmark-1Mkeys-bitmap-getbit-pipeline-10|latency-rate-limited-10000_qps-memtier_benchmark-1Mkeys-generic-exists-pipeline-10|latency-rate-limited-10000_qps-memtier_benchmark-1Mkeys-generic-expire-pipeline-10|latency-rate-limited-10000_qps-memtier_benchmark-1Mkeys-generic-expireat-pipeline-10|latency-rate-limited-10000_qps-memtier_benchmark-1Mkeys-generic-pexpire-pipeline-10|latency-rate-limited-10000_qps-memtier_benchmark-1Mkeys-generic-scan-pipeline-10|latency-rate-limited-10000_qps-memtier_benchmark-1Mkeys-generic-touch-pipeline-10|latency-rate-limited-10000_qps-memtier_benchmark-1Mkeys-generic-ttl-pipeline-10|latency-rate-limited-10000_qps-memtier_benchmark-1Mkeys-hash-hexists|latency-rate-limited-10000_qps-memtier_benchmark-1Mkeys-hash-hget-hgetall-hkeys-hvals-with-100B-values|latency-rate-limited-10000_qps-memtier_benchmark-1Mkeys-hash-hgetall-50-fields-10B-values|latency-rate-limited-10000_qps-memtier_benchmark-1Mkeys-hash-hincrby|latency-rate-limited-10000_qps-memtier_benchmark-1Mkeys-hash-hmget-5-fields-with-100B-values-pipeline-10|latency-rate-limited-10000_qps-memtier_benchmark-1Mkeys-hash-transactions-multi-exec-pipeline-20|latency-rate-limited-10000_qps-memtier_benchmark-1Mkeys-list-lpop-rpop-with-100B-values|latency-rate-limited-10000_qps-memtier_benchmark-1Mkeys-list-lpop-rpop-with-10B-values|latency-rate-limited-10000_qps-memtier_benchmark-1Mkeys-list-lpop-rpop-with-1KiB-values|latency-rate-limited-10000_qps-memtier_benchmark-1Mkeys-load-hash-5-fields-with-1000B-values|latency-rate-limited-10000_qps-memtier_benchmark-1Mkeys-load-hash-5-fields-with-1000B-values-pipeline-10|latency-rate-limited-10000_qps-memtier_benchmark-1Mkeys-load-hash-hmset-5-fields-with-1000B-values|latency-rate-limited-10000_qps-memtier_benchmark-1Mkeys-load-list-with-100B-values|latency-rate-limited-10000_qps-memtier_benchmark-1Mkeys-load-list-with-10B-values|latency-rate-limited-10000_qps-memtier_benchmark-1Mkeys-load-list-with-1KiB-values|latency-rate-limited-10000_qps-memtier_benchmark-1Mkeys-load-set-intset-with-100-elements|latency-rate-limited-10000_qps-memtier_benchmark-1Mkeys-load-set-intset-with-100-elements-pipeline-10|latency-rate-limited-10000_qps-memtier_benchmark-1Mkeys-load-stream-1-fields-with-100B-values|latency-rate-limited-10000_qps-memtier_benchmark-1Mkeys-load-stream-1-fields-with-100B-values-pipeline-10|latency-rate-limited-10000_qps-memtier_benchmark-1Mkeys-load-stream-5-fields-with-100B-values|latency-rate-limited-10000_qps-memtier_benchmark-1Mkeys-load-stream-5-fields-with-100B-values-pipeline-10|latency-rate-limited-10000_qps-memtier_benchmark-1Mkeys-load-string-with-100B-values|latency-rate-limited-10000_qps-memtier_benchmark-1Mkeys-load-string-with-100B-values-pipeline-10|latency-rate-limited-10000_qps-memtier_benchmark-1Mkeys-load-string-with-10B-values|latency-rate-limited-10000_qps-memtier_benchmark-1Mkeys-load-string-with-10B-values-pipeline-10|latency-rate-limited-10000_qps-memtier_benchmark-1Mkeys-load-string-with-1KiB-values|latency-rate-limited-10000_qps-memtier_benchmark-1Mkeys-load-string-with-20KiB-values|latency-rate-limited-10000_qps-memtier_benchmark-1Mkeys-load-zset-with-10-elements-double-score|latency-rate-limited-10000_qps-memtier_benchmark-1Mkeys-load-zset-with-10-elements-int-score|latency-rate-limited-10000_qps-memtier_benchmark-1Mkeys-string-append-1-100B|latency-rate-limited-10000_qps-memtier_benchmark-1Mkeys-string-append-1-100B-pipeline-10|latency-rate-limited-10000_qps-memtier_benchmark-1Mkeys-string-decr|latency-rate-limited-10000_qps-memtier_benchmark-1Mkeys-string-get-100B|latency-rate-limited-10000_qps-memtier_benchmark-1Mkeys-string-get-100B-pipeline-10|latency-rate-limited-10000_qps-memtier_benchmark-1Mkeys-string-get-10B|latency-rate-limited-10000_qps-memtier_benchmark-1Mkeys-string-get-10B-pipeline-10|latency-rate-limited-10000_qps-memtier_benchmark-1Mkeys-string-get-1KiB|latency-rate-limited-10000_qps-memtier_benchmark-1Mkeys-string-get-1KiB-pipeline-10|latency-rate-limited-10000_qps-memtier_benchmark-1Mkeys-string-get-20KiB|latency-rate-limited-10000_qps-memtier_benchmark-1Mkeys-string-incrby|latency-rate-limited-10000_qps-memtier_benchmark-1Mkeys-string-incrby-pipeline-10|latency-rate-limited-10000_qps-memtier_benchmark-1Mkeys-string-incrbyfloat|latency-rate-limited-10000_qps-memtier_benchmark-1Mkeys-string-incrbyfloat-pipeline-10|latency-rate-limited-10000_qps-memtier_benchmark-1Mkeys-string-mget-1KiB|latency-rate-limited-10000_qps-memtier_benchmark-1Mkeys-string-setex-100B-pipeline-10|latency-rate-limited-10000_qps-memtier_benchmark-1Mkeys-string-setrange-100B|latency-rate-limited-10000_qps-memtier_benchmark-1Mkeys-string-setrange-100B-pipeline-10|latency-rate-limited-10000_qps-memtier_benchmark-1key-geo-2-elements-geopos|latency-rate-limited-10000_qps-memtier_benchmark-1key-geo-2-elements-geosearch-fromlonlat-withcoord|latency-rate-limited-10000_qps-memtier_benchmark-1key-geo-60M-elements-geodist|latency-rate-limited-10000_qps-memtier_benchmark-1key-geo-60M-elements-geodist-pipeline-10|latency-rate-limited-10000_qps-memtier_benchmark-1key-geo-60M-elements-geohash|latency-rate-limited-10000_qps-memtier_benchmark-1key-geo-60M-elements-geohash-pipeline-10|latency-rate-limited-10000_qps-memtier_benchmark-1key-geo-60M-elements-geopos|latency-rate-limited-10000_qps-memtier_benchmark-1key-geo-60M-elements-geopos-pipeline-10|latency-rate-limited-10000_qps-memtier_benchmark-1key-geo-60M-elements-geosearch-fromlonlat|latency-rate-limited-10000_qps-memtier_benchmark-1key-geo-60M-elements-geosearch-fromlonlat-bybox|latency-rate-limited-10000_qps-memtier_benchmark-1key-geo-60M-elements-geosearch-fromlonlat-pipeline-10|latency-rate-limited-10000_qps-memtier_benchmark-1key-hash-hscan-50-fields-10B-values|latency-rate-limited-10000_qps-memtier_benchmark-1key-list-10-elements-lrange-all-elements|latency-rate-limited-10000_qps-memtier_benchmark-1key-list-10-elements-lrange-all-elements-pipeline-10|latency-rate-limited-10000_qps-memtier_benchmark-1key-list-100-elements-lrange-all-elements|latency-rate-limited-10000_qps-memtier_benchmark-1key-list-100-elements-lrange-all-elements-pipeline-10|latency-rate-limited-10000_qps-memtier_benchmark-1key-list-10K-elements-lindex-integer|latency-rate-limited-10000_qps-memtier_benchmark-1key-list-10K-elements-lindex-string|latency-rate-limited-10000_qps-memtier_benchmark-1key-list-1K-elements-lrange-all-elements|latency-rate-limited-10000_qps-memtier_benchmark-1key-list-1K-elements-lrange-all-elements-pipeline-10|latency-rate-limited-10000_qps-memtier_benchmark-1key-pfadd-4KB-values-pipeline-10|latency-rate-limited-10000_qps-memtier_benchmark-1key-set-10-elements-smembers|latency-rate-limited-10000_qps-memtier_benchmark-1key-set-10-elements-smembers-pipeline-10|latency-rate-limited-10000_qps-memtier_benchmark-1key-set-10-elements-smismember|latency-rate-limited-10000_qps-memtier_benchmark-1key-set-100-elements-sismember-is-a-member|latency-rate-limited-10000_qps-memtier_benchmark-1key-set-100-elements-sismember-not-a-member|latency-rate-limited-10000_qps-memtier_benchmark-1key-set-100-elements-smembers|latency-rate-limited-10000_qps-memtier_benchmark-1key-set-100-elements-smismember|latency-rate-limited-10000_qps-memtier_benchmark-1key-set-100-elements-sscan|latency-rate-limited-10000_qps-memtier_benchmark-1key-set-10M-elements-sismember-50pct-chance|latency-rate-limited-10000_qps-memtier_benchmark-1key-set-1K-elements-smembers|latency-rate-limited-10000_qps-memtier_benchmark-1key-set-1M-elements-sismember-50pct-chance|latency-rate-limited-10000_qps-memtier_benchmark-1key-set-200K-elements-sadd-constant|latency-rate-limited-10000_qps-memtier_benchmark-1key-set-2M-elements-sadd-increasing|latency-rate-limited-10000_qps-memtier_benchmark-1key-zincrby-1M-elements-pipeline-1|latency-rate-limited-10000_qps-memtier_benchmark-1key-zrank-1M-elements-pipeline-1|latency-rate-limited-10000_qps-memtier_benchmark-1key-zrem-5M-elements-pipeline-1|latency-rate-limited-10000_qps-memtier_benchmark-1key-zrevrangebyscore-256K-elements-pipeline-1|latency-rate-limited-10000_qps-memtier_benchmark-1key-zrevrank-1M-elements-pipeline-1|latency-rate-limited-10000_qps-memtier_benchmark-1key-zset-10-elements-zrange-all-elements|latency-rate-limited-10000_qps-memtier_benchmark-1key-zset-10-elements-zrange-all-elements-long-scores|latency-rate-limited-10000_qps-memtier_benchmark-1key-zset-100-elements-zrange-all-elements|latency-rate-limited-10000_qps-memtier_benchmark-1key-zset-100-elements-zrangebyscore-all-elements|latency-rate-limited-10000_qps-memtier_benchmark-1key-zset-100-elements-zrangebyscore-all-elements-long-scores|latency-rate-limited-10000_qps-memtier_benchmark-1key-zset-100-elements-zscan|latency-rate-limited-10000_qps-memtier_benchmark-1key-zset-1M-elements-zcard-pipeline-10|latency-rate-limited-10000_qps-memtier_benchmark-1key-zset-1M-elements-zrevrange-5-elements|latency-rate-limited-10000_qps-memtier_benchmark-1key-zset-1M-elements-zscore-pipeline-10|latency-rate-limited-10000_qps-memtier_benchmark-2keys-lua-eval-hset-expire|latency-rate-limited-10000_qps-memtier_benchmark-2keys-lua-evalsha-hset-expire|latency-rate-limited-10000_qps-memtier_benchmark-2keys-set-10-100-elements-sdiff|latency-rate-limited-10000_qps-memtier_benchmark-2keys-set-10-100-elements-sinter|latency-rate-limited-10000_qps-memtier_benchmark-2keys-set-10-100-elements-sunion|latency-rate-limited-10000_qps-memtier_benchmark-2keys-stream-5-entries-xread-all-entries|latency-rate-limited-10000_qps-memtier_benchmark-2keys-stream-5-entries-xread-all-entries-pipeline-10|latency-rate-limited-10000_qps-memtier_benchmark-3Mkeys-load-string-with-512B-values|latency-rate-limited-10000_qps-memtier_benchmark-connection-hello|latency-rate-limited-1000_qps-memtier_benchmark-10Kkeys-load-hash-50-fields-with-10000B-values|latency-rate-limited-1000_qps-memtier_benchmark-1Mkeys-load-zset-listpack-with-100-elements-double-score|latency-rate-limited-1000_qps-memtier_benchmark-1key-100M-bits-bitmap-bitcount|latency-rate-limited-1000_qps-memtier_benchmark-1key-list-10K-elements-linsert-lrem-integer|latency-rate-limited-1000_qps-memtier_benchmark-1key-list-10K-elements-linsert-lrem-string|latency-rate-limited-1000_qps-memtier_benchmark-1key-list-10K-elements-lpos-integer|latency-rate-limited-1000_qps-memtier_benchmark-1key-list-10K-elements-lpos-string|latency-rate-limited-1000_qps-memtier_benchmark-1key-list-2K-elements-quicklist-lrange-all-elements-longs|latency-rate-limited-1000_qps-memtier_benchmark-1key-zset-1K-elements-zrange-all-elements|latency-rate-limited-1000_qps-memtier_benchmark-2keys-zset-300-elements-skiplist-encoded-zunion|latency-rate-limited-1000_qps-memtier_benchmark-2keys-zset-300-elements-skiplist-encoded-zunionstore|latency-rate-limited-100_qps-memtier_benchmark-1key-1Billion-bits-bitmap-bitcount|memtier_benchmark-100Kkeys-hash-hgetall-50-fields-100B-values|memtier_benchmark-100Kkeys-load-hash-20-fields-with-1B-values-pipeline-30|memtier_benchmark-100Kkeys-load-hash-50-fields-with-1000B-values|memtier_benchmark-100Kkeys-load-hash-50-fields-with-100B-values|memtier_benchmark-100Kkeys-load-hash-50-fields-with-10B-values|memtier_benchmark-10Kkeys-load-hash-50-fields-with-10000B-values|memtier_benchmark-10Kkeys-load-list-with-10B-values-pipeline-50|memtier_benchmark-10Mkeys-load-hash-5-fields-with-1000B-values|memtier_benchmark-10Mkeys-load-hash-5-fields-with-1000B-values-pipeline-10|memtier_benchmark-10Mkeys-load-hash-5-fields-with-100B-values-pipeline-10|memtier_benchmark-10Mkeys-load-hash-5-fields-with-10B-values|memtier_benchmark-10Mkeys-load-hash-5-fields-with-10B-values-pipeline-10|memtier_benchmark-10Mkeys-string-get-10B-pipeline-100-nokeyprefix|memtier_benchmark-1Mkeys-100B-expire-use-case|memtier_benchmark-1Mkeys-10B-expire-use-case|memtier_benchmark-1Mkeys-10B-psetex-expire-use-case|memtier_benchmark-1Mkeys-10B-setex-expire-use-case|memtier_benchmark-1Mkeys-1KiB-expire-use-case|memtier_benchmark-1Mkeys-4KiB-expire-use-case|memtier_benchmark-1Mkeys-bitmap-getbit-pipeline-10|memtier_benchmark-1Mkeys-generic-exists-pipeline-10|memtier_benchmark-1Mkeys-generic-expireat-pipeline-10|memtier_benchmark-1Mkeys-generic-pexpire-pipeline-10|memtier_benchmark-1Mkeys-generic-scan-count-500-pipeline-10|memtier_benchmark-1Mkeys-generic-scan-cursor-count-500-pipeline-10|memtier_benchmark-1Mkeys-generic-scan-cursor-count-5000-pipeline-10|memtier_benchmark-1Mkeys-generic-scan-cursor-pipeline-10|memtier_benchmark-1Mkeys-generic-scan-pipeline-10|memtier_benchmark-1Mkeys-generic-scan-type-pipeline-10|memtier_benchmark-1Mkeys-generic-ttl-pipeline-10|memtier_benchmark-1Mkeys-hash-hexists|memtier_benchmark-1Mkeys-hash-hget-hgetall-hkeys-hvals-with-100B-values|memtier_benchmark-1Mkeys-hash-hgetall-50-fields-10B-values|memtier_benchmark-1Mkeys-hash-hincrby|memtier_benchmark-1Mkeys-hash-hincrbyfloat|memtier_benchmark-1Mkeys-hash-hmget-5-fields-with-100B-values-pipeline-10|memtier_benchmark-1Mkeys-hash-transactions-multi-exec-pipeline-20|memtier_benchmark-1Mkeys-lhash-hexists|memtier_benchmark-1Mkeys-lhash-hincbry|memtier_benchmark-1Mkeys-list-lpop-rpop-with-100B-values|memtier_benchmark-1Mkeys-list-lpop-rpop-with-10B-values|memtier_benchmark-1Mkeys-list-lpop-rpop-with-1KiB-values|memtier_benchmark-1Mkeys-list-rpoplpush-with-10B-values|memtier_benchmark-1Mkeys-load-hash-5-fields-with-1000B-values|memtier_benchmark-1Mkeys-load-hash-5-fields-with-1000B-values-pipeline-10|memtier_benchmark-1Mkeys-load-list-rpush-with-10B-values|memtier_benchmark-1Mkeys-load-list-with-100B-values|memtier_benchmark-1Mkeys-load-list-with-10B-values|memtier_benchmark-1Mkeys-load-list-with-10B-values-pipeline-10|memtier_benchmark-1Mkeys-load-list-with-1KiB-values|memtier_benchmark-1Mkeys-load-set-intset-with-100-elements|memtier_benchmark-1Mkeys-load-set-intset-with-100-elements-pipeline-10|memtier_benchmark-1Mkeys-load-stream-1-fields-with-100B-values|memtier_benchmark-1Mkeys-load-stream-1-fields-with-100B-values-pipeline-10|memtier_benchmark-1Mkeys-load-stream-5-fields-with-100B-values|memtier_benchmark-1Mkeys-load-stream-5-fields-with-100B-values-pipeline-10|memtier_benchmark-1Mkeys-load-string-with-100B-values|memtier_benchmark-1Mkeys-load-string-with-100B-values-pipeline-10|memtier_benchmark-1Mkeys-load-string-with-10B-values|memtier_benchmark-1Mkeys-load-string-with-10B-values-pipeline-10|memtier_benchmark-1Mkeys-load-string-with-10B-values-pipeline-100|memtier_benchmark-1Mkeys-load-string-with-10B-values-pipeline-100-nokeyprefix|memtier_benchmark-1Mkeys-load-string-with-10B-values-pipeline-50|memtier_benchmark-1Mkeys-load-string-with-10B-values-pipeline-500|memtier_benchmark-1Mkeys-load-string-with-1KiB-values|memtier_benchmark-1Mkeys-load-string-with-200KiB-values|memtier_benchmark-1Mkeys-load-string-with-20KiB-values|memtier_benchmark-1Mkeys-load-string-with-2MB-values|memtier_benchmark-1Mkeys-load-zset-with-10-elements-double-score|memtier_benchmark-1Mkeys-load-zset-with-10-elements-int-score|memtier_benchmark-1Mkeys-string-append-1-100B|memtier_benchmark-1Mkeys-string-append-1-100B-pipeline-10|memtier_benchmark-1Mkeys-string-decr|memtier_benchmark-1Mkeys-string-get-100B|memtier_benchmark-1Mkeys-string-get-100B-pipeline-10|memtier_benchmark-1Mkeys-string-get-10B-pipeline-10|memtier_benchmark-1Mkeys-string-get-10B-pipeline-100-nokeyprefix|memtier_benchmark-1Mkeys-string-get-10B-pipeline-500|memtier_benchmark-1Mkeys-string-get-1KiB|memtier_benchmark-1Mkeys-string-get-1KiB-pipeline-10|memtier_benchmark-1Mkeys-string-get-200KiB|memtier_benchmark-1Mkeys-string-get-20KiB|memtier_benchmark-1Mkeys-string-get-2MB|memtier_benchmark-1Mkeys-string-get-32B|memtier_benchmark-1Mkeys-string-get-32B-pipeline-10|memtier_benchmark-1Mkeys-string-incr-pipeline-10|memtier_benchmark-1Mkeys-string-incrby|memtier_benchmark-1Mkeys-string-incrby-pipeline-10|memtier_benchmark-1Mkeys-string-incrbyfloat|memtier_benchmark-1Mkeys-string-int-encoding-strlen-pipeline-10|memtier_benchmark-1Mkeys-string-mget-1KiB|memtier_benchmark-1Mkeys-string-mixed-50-50-set-get-100B|memtier_benchmark-1Mkeys-string-mixed-50-50-set-get-100B-pipeline-10|memtier_benchmark-1Mkeys-string-mixed-50-50-set-get-1KB|memtier_benchmark-1Mkeys-string-mixed-50-50-set-get-1KB-pipeline-10|memtier_benchmark-1Mkeys-string-mixed-50-50-set-get-32B|memtier_benchmark-1Mkeys-string-mixed-50-50-set-get-32B-pipeline-10|memtier_benchmark-1Mkeys-string-mixed-50-50-set-get-512B|memtier_benchmark-1Mkeys-string-mixed-50-50-set-get-512B-pipeline-10|memtier_benchmark-1Mkeys-string-mixed-50-50-set-get-with-expiration-240B-400_conns|memtier_benchmark-1Mkeys-string-set-with-ex-100B-pipeline-10|memtier_benchmark-1Mkeys-string-setex-100B-pipeline-10|memtier_benchmark-1Mkeys-string-setrange-100B-pipeline-10|memtier_benchmark-1key-1Billion-bits-bitmap-bitcount|memtier_benchmark-1key-geo-2-elements-geopos|memtier_benchmark-1key-geo-2-elements-geosearch-fromlonlat-withcoord|memtier_benchmark-1key-geo-60M-elements-geodist|memtier_benchmark-1key-geo-60M-elements-geodist-pipeline-10|memtier_benchmark-1key-geo-60M-elements-geohash|memtier_benchmark-1key-geo-60M-elements-geohash-pipeline-10|memtier_benchmark-1key-geo-60M-elements-geopos|memtier_benchmark-1key-geo-60M-elements-geopos-pipeline-10|memtier_benchmark-1key-geo-60M-elements-geosearch-fromlonlat|memtier_benchmark-1key-geo-60M-elements-geosearch-fromlonlat-bybox|memtier_benchmark-1key-geo-60M-elements-geosearch-fromlonlat-pipeline-10|memtier_benchmark-1key-hash-1K-fields-hgetall|memtier_benchmark-1key-hash-1K-fields-hgetall-pipeline-10|memtier_benchmark-1key-hash-hscan-50-fields-10B-values|memtier_benchmark-1key-list-10-elements-lrange-all-elements|memtier_benchmark-1key-list-10-elements-lrange-all-elements-pipeline-10|memtier_benchmark-1key-list-100-elements-int-7bit-uint-lrange-all-elements-pipeline-10|memtier_benchmark-1key-list-100-elements-int-lrange-all-elements-pipeline-10|memtier_benchmark-1key-list-100-elements-llen-pipeline-10|memtier_benchmark-1key-list-100-elements-lrange-all-elements|memtier_benchmark-1key-list-10K-elements-lindex-integer|memtier_benchmark-1key-list-10K-elements-lindex-string|memtier_benchmark-1key-list-10K-elements-lindex-string-pipeline-10|memtier_benchmark-1key-list-10K-elements-linsert-lrem-integer|memtier_benchmark-1key-list-10K-elements-linsert-lrem-string|memtier_benchmark-1key-list-10K-elements-lpos-integer|memtier_benchmark-1key-list-10K-elements-lpos-string|memtier_benchmark-1key-list-1K-elements-lrange-all-elements|memtier_benchmark-1key-list-1K-elements-lrange-all-elements-pipeline-10|memtier_benchmark-1key-list-2K-elements-quicklist-lrange-all-elements-longs|memtier_benchmark-1key-load-hash-1K-fields-with-5B-values|memtier_benchmark-1key-load-zset-with-5-elements-parsing-float-score|memtier_benchmark-1key-load-zset-with-5-elements-parsing-hexa-score|memtier_benchmark-1key-pfadd-4KB-values-pipeline-10|memtier_benchmark-1key-set-10-elements-smembers|memtier_benchmark-1key-set-10-elements-smembers-pipeline-10|memtier_benchmark-1key-set-10-elements-smismember|memtier_benchmark-1key-set-100-elements-sismember-is-a-member|memtier_benchmark-1key-set-100-elements-sismember-not-a-member|memtier_benchmark-1key-set-100-elements-smembers|memtier_benchmark-1key-set-100-elements-smismember|memtier_benchmark-1key-set-100-elements-sscan|memtier_benchmark-1key-set-10M-elements-sismember-50pct-chance|memtier_benchmark-1key-set-10M-elements-srem-50pct-chance|memtier_benchmark-1key-set-1K-elements-smembers|memtier_benchmark-1key-set-1M-elements-sismember-50pct-chance|memtier_benchmark-1key-set-200K-elements-sadd-constant|memtier_benchmark-1key-set-2M-elements-sadd-increasing|memtier_benchmark-1key-zincrby-1M-elements-pipeline-1|memtier_benchmark-1key-zrank-100K-elements-pipeline-1|memtier_benchmark-1key-zrank-1M-elements-pipeline-1|memtier_benchmark-1key-zrem-5M-elements-pipeline-1|memtier_benchmark-1key-zrevrangebyscore-256K-elements-pipeline-1|memtier_benchmark-1key-zrevrank-1M-elements-pipeline-1|memtier_benchmark-1key-zset-10-elements-zrange-all-elements|memtier_benchmark-1key-zset-10-elements-zrange-all-elements-long-scores|memtier_benchmark-1key-zset-100-elements-zrange-all-elements|memtier_benchmark-1key-zset-100-elements-zrangebyscore-all-elements|memtier_benchmark-1key-zset-100-elements-zrangebyscore-all-elements-long-scores|memtier_benchmark-1key-zset-100-elements-zscan|memtier_benchmark-1key-zset-1K-elements-zrange-all-elements|memtier_benchmark-1key-zset-1M-elements-zcard-pipeline-10|memtier_benchmark-1key-zset-1M-elements-zremrangebyscore-pipeline-10|memtier_benchmark-1key-zset-1M-elements-zrevrange-withscores-5-elements-pipeline-10|memtier_benchmark-1key-zset-1M-elements-zscore-pipeline-10|memtier_benchmark-1key-zset-600K-elements-zrangestore-1K-elements|memtier_benchmark-1key-zset-600K-elements-zrangestore-300K-elements|memtier_benchmark-1key-zset-listpack-zrank-100-elements-pipeline-1|memtier_benchmark-2keys-lua-eval-hset-expire|memtier_benchmark-2keys-lua-evalsha-hset-expire|memtier_benchmark-2keys-set-10-100-elements-sdiff|memtier_benchmark-2keys-set-10-100-elements-sinter|memtier_benchmark-2keys-set-10-100-elements-sunion|memtier_benchmark-2keys-stream-5-entries-xread-all-entries|memtier_benchmark-2keys-stream-5-entries-xread-all-entries-pipeline-10|memtier_benchmark-3Mkeys-load-string-with-512B-values|memtier_benchmark-3Mkeys-load-string-with-512B-values-pipeline-10|memtier_benchmark-3Mkeys-string-get-with-1KiB-values-pipeline-10-2000_conns|memtier_benchmark-3Mkeys-string-get-with-1KiB-values-pipeline-10-400_conns|memtier_benchmark-3Mkeys-string-get-with-1KiB-values-pipeline-10-40_conns|memtier_benchmark-3Mkeys-string-mixed-20-80-with-512B-values-pipeline-10-2000_conns|memtier_benchmark-3Mkeys-string-mixed-20-80-with-512B-values-pipeline-10-400_conns|memtier_benchmark-3Mkeys-string-mixed-20-80-with-512B-values-pipeline-10-5200_conns|memtier_benchmark-connection-hello|memtier_benchmark-connection-hello-pipeline-10|memtier_benchmark-nokeys-connection-ping-pipeline-10|memtier_benchmark-nokeys-pubsub-publish-1K-channels-10B-no-subscribers|memtier_benchmark-nokeys-server-time-pipeline-10|redis-benchmark-full-suite-1Mkeys-100B|redis-benchmark-full-suite-1Mkeys-100B-pipeline-10|redis-benchmark-full-suite-1Mkeys-1KiB|redis-benchmark-full-suite-1Mkeys-1KiB-pipeline-10|vector_db_benchmark_test |
oranagra
left a comment
There was a problem hiding this comment.
interface changes LGTM.
one thought i had is that maybe the APIs should have a uniform prefix rather than a uniform suffix.
i.e. all start with RM_Config. WDYT?
32b66e1 to
40bdd78
Compare
There was a problem hiding this comment.
Pull Request Overview
This PR introduces a set/get configuration API for modules, allowing modules to access and modify various Redis configurations even when native commands are renamed or restricted. Key changes include:
- New RedisModule API functions for config iteration, type retrieval, and get/set operations.
- Refactoring of the internal configuration apply and backup restoration logic.
- Updates to tests and module build files to cover the new APIs.
Reviewed Changes
Copilot reviewed 8 out of 8 changed files in this pull request and generated 1 comment.
Show a summary per file
| File | Description |
|---|---|
| tests/unit/moduleapi/configaccess.tcl | Added tests for getting and setting various config types using the new module API. |
| tests/modules/configaccess.c | Introduced new test commands implementing the module config API functions. |
| tests/modules/Makefile | Updated to build the new configaccess module. |
| src/server.h | Added declarations for new API functions used by modules to access config values. |
| src/redismodule.h | Added new API function pointers and extended enum for config types. |
| src/module.c | Extended auto-memory handling and introduced REDISMODULE_AM_CONFIG for config iterators. |
| src/config.c | Refactored backup restoration and apply logic for both module and standard configs. |
| runtest-moduleapi | Included the new configaccess unit tests in the test runner. |
Comments suppressed due to low confidence (1)
src/config.c:3607
- [nitpick] Passing the address of a single config and a single sds value to a function that expects arrays can be confusing; consider adding a dedicated helper for restoring a single config to enhance clarity.
restoreBackupConfig(&config, &old_value, 1);
Remove AI slop from test cases
… and renamed Get/SetStringConfig to Get/SetConfig Improved comments, added tests
Co-authored-by: Oran Agra <[email protected]>
40bdd78 to
7fef8c0
Compare
| # Run a dummy server on used_port so we know we can't configure redis to | ||
| # use it. It's ok for this to fail because that means used_port is invalid | ||
| # anyway | ||
| catch {set sockfd [socket -server dummy_accept -myaddr 127.0.0.1 $used_port]} e |
# Problem Some redis modules need to call `CONFIG GET/SET` commands. Server may be ran with `rename-command CONFIG ""`(or something similar) which leads to the module being unable to access the config. # Solution Added new API functions for use by modules ``` RedisModuleConfigIterator* RedisModule_GetConfigIterator(RedisModuleCtx *ctx, const char *pattern); void RedisModule_ReleaseConfigIterator(RedisModuleCtx *ctx, RedisModuleConfigIterator *iter); const char *RedisModule_ConfigIteratorNext(RedisModuleConfigIterator *iter); int RedisModule_GetConfigType(const char *name, RedisModuleConfigType *res); int RedisModule_GetBoolConfig(RedisModuleCtx *ctx, const char *name, int *res); int RedisModule_GetConfig(RedisModuleCtx *ctx, const char *name, RedisModuleString **res); int RedisModule_GetEnumConfig(RedisModuleCtx *ctx, const char *name, RedisModuleString **res); int RedisModule_GetNumericConfig(RedisModuleCtx *ctx, const char *name, long long *res); int RedisModule_SetBoolConfig(RedisModuleCtx *ctx, const char *name, int value, RedisModuleString **err); int RedisModule_SetConfig(RedisModuleCtx *ctx, const char *name, RedisModuleString *value, RedisModuleString **err); int RedisModule_SetEnumConfig(RedisModuleCtx *ctx, const char *name, RedisModuleString *value, RedisModuleString **err); int RedisModule_SetNumericConfig(RedisModuleCtx *ctx, const char *name, long long value, RedisModuleString **err); ``` ## Implementation The work is mostly done inside `config.c` as I didn't want to expose the config dict outside of it. That means each of these module functions has a corresponding method in `config.c` that actually does the job. F.e `RedisModule_SetEnumConfig` calls `moduleSetEnumConfig` which is implemented in `config.c` ## Notes Also, refactored `configSetCommand` and `restoreBackupConfig` functions for the following reasons: - code and logic is now way more clear in `configSetCommand`. Only caveat here is removal of an optimization that skipped running apply functions that already have ran in favour of code clarity. - Both functions needlessly separated logic for module configs and normal configs whereas no such separation is needed. This also had the side effect of removing some allocations. - `restoreBackupConfig` now has clearer interface and can be reused with ease. One of the places I reused it is for the individual `moduleSet*Config` functions, each of which needs the restoration functionality but for a single config only. ## Future Additionally, a couple considerations were made for potentially extending the API in the future - if need be an API for atomically setting multiple config values can be added - `RedisModule_SetConfigsTranscationStart/End` or similar that can be put around `RedisModule_Set*Config` calls. - if performance is an issue an API `RedisModule_GetConfigIteratorNextWithTypehint` or similar may be added in order not to incur the additional cost of calling `RedisModule_GetConfigType`. --------- Co-authored-by: Oran Agra <[email protected]>
This is the General Availability release of Redis Open Source 8.2. ### Major changes compared to 8.0 - Streams - new commands: `XDELEX` and `XACKDEL`; extension to `XADD` and `XTRIM` - Bitmap - `BITOP`: new operators: `DIFF`, `DIFF1`, `ANDOR`, and `ONE` - Query Engine - new SVS-VAMANA vector index type which supports vector compression - More than 15 performance and resource utilization improvements - New metrics: per-slot usage metrics, key size distributions for basic data types, and more ### Binary distributions - Alpine and Debian Docker images - https://hub.docker.com/_/redis - Install using snap - see https://github.com/redis/redis-snap - Install using brew - see https://github.com/redis/homebrew-redis - Install using RPM - see https://github.com/redis/redis-rpm - Install using Debian APT - see https://github.com/redis/redis-debian ### Operating systems we test Redis 8.2 on - Ubuntu 22.04 (Jammy Jellyfish), 24.04 (Noble Numbat) - Rocky Linux 8.10, 9.5 - AlmaLinux 8.10, 9.5 - Debian 12 (Bookworm) - macOS 13 (Ventura), 14 (Sonoma), 15 (Sequoia) ### Security fixes (compared to 8.2-RC1) - (CVE-2025-32023) Fix out-of-bounds write in `HyperLogLog` commands - (CVE-2025-48367) Retry accepting other connections even if the accepted connection reports an error ### New Features (compared to 8.2-RC1) - #14141 Keyspace notifications - new event types: - `OVERWRITTEN` - the value of a key is completely overwritten - `TYPE_CHANGED` - key type change ### Bug fixes (compared to 8.2-RC1) - #14162 Crash when using evport with I/O threads - #14163 `EVAL` crash when error table is empty - #14144 Vector sets - RDB format is not compatible with big endian machines - #14165 Endless client blocking for blocking commands - #14164 Prevent `CLIENT UNBLOCK` from unblocking `CLIENT PAUSE` - #14216 TTL was not removed by the `SET` command - #14224 `HINCRBYFLOAT` removes field expiration on replica ### Performance and resource utilization improvements (compared to 8.2-RC1) - #14200 Store iterators on stack instead of on heap - #14144 Vector set - improve RDB loading / RESTORE speed by storing the worst link info - #Q6430 More compression variants for the SVS-VAMANA vector index - #Q6535 `SHARD_K_RATIO` parameter - favor network latency over accuracy for KNN vector query in a Redis cluster (unstable feature) (MOD-10359) ### Modules API - #14051 `RedisModule_Get*`, `RedisModule_Set*` - allow modules to access Redis configurations - #14114 `RM_UnsubscribeFromKeyspaceEvents` - unregister a module from specific keyspace notifications
Revert a breaking change introduced in #14051 described in this comment #14051 (comment) The non-negative check inside `checkNumericBoundaries` was ignoring that passing a big unsigned long long value (> 2^63-1) will be passed as negative value and will never reach the lower/upper boundary check. The check is removed, reverting the breaking change. This allows for RedisModule_ConfigSetNumeric to pass big unsigned number, albeit via `long long` parameter. Added comments about this behaviour. Added tests for #14051 (comment)
Problem
Some redis modules need to call
CONFIG GET/SETcommands. Server may be ran withrename-command CONFIG ""(or something similar) which leads to the module being unable to access the config.Solution
Added new API functions for use by modules
Implementation
The work is mostly done inside
config.cas I didn't want to expose the config dict outside of it. That means each of these module functions has a corresponding method inconfig.cthat actually does the job. F.eRedisModule_SetEnumConfigcallsmoduleSetEnumConfigwhich is implemented inconfig.cNotes
Also, refactored
configSetCommandandrestoreBackupConfigfunctions for the following reasons:configSetCommand. Only caveat here is removal of an optimization that skipped running apply functions that already have ran in favour of code clarity.restoreBackupConfignow has clearer interface and can be reused with ease. One of the places I reused it is for the individualmoduleSet*Configfunctions, each of which needs the restoration functionality but for a single config only.Future
Additionally, a couple considerations were made for potentially extending the API in the future
RedisModule_SetConfigsTranscationStart/Endor similar that can be put aroundRedisModule_Set*Configcalls.RedisModule_GetConfigIteratorNextWithTypehintor similar may be added in order not to incur the additional cost of callingRedisModule_GetConfigType.