Как указано в комментариях к вашему ответу, написание собственного модуля будет вариантом, который может очень хорошо соответствовать вашим требованиям.
Такой модуль был бы написан на C. Следовательно, необходима десятичная библиотека, которая соответствует математическим требованиям финансовых приложений.
Здесь я использую библиотеку decNumber C, библиотеку, изначально написанную IBM. Я использовал для своего теста следующие ссылки:
Демо
Перед просмотром кода здесь небольшая демонстрация:
![balance decrement demo](https://i.stack.imgur.com/XJsH1.png)
Как видите, он работает с произвольной точностью.
Команда, подобная balance.decrement mykey myfield "0.1"
, уменьшает mykey myfield
на значение, переданное в качестве последнего строкового параметра. Новое значение сохраняется в mykey myfield
и выводится как результат команды. Если результат будет меньше 0, он не уменьшается. Затем выводится NOP
. Операция атомная.
Модуль источника
#include "../redismodule.h"
#include "../rmutil/util.h"
#include "../rmutil/strings.h"
#include "../rmutil/test_util.h"
#define DECNUMDIGITS 34
#include "decNumber.h"
int decrementCommand(RedisModuleCtx *ctx, RedisModuleString **argv, int argc) {
if (argc != 4) {
return RedisModule_WrongArity(ctx);
}
RedisModule_AutoMemory(ctx);
RedisModuleKey *key = RedisModule_OpenKey(ctx, argv[1], REDISMODULE_READ | REDISMODULE_WRITE);
if (RedisModule_KeyType(key) != REDISMODULE_KEYTYPE_HASH &&
RedisModule_KeyType(key) != REDISMODULE_KEYTYPE_EMPTY) {
return RedisModule_ReplyWithError(ctx, REDISMODULE_ERRORMSG_WRONGTYPE);
}
RedisModuleCallReply *currentValueReply = RedisModule_Call(ctx, "HGET", "ss", argv[1], argv[2]);
RMUTIL_ASSERT_NOERROR(ctx, currentValueReply);
RedisModuleString *currentValueRedisString = RedisModule_CreateStringFromCallReply(currentValueReply);
if (!currentValueRedisString) {
return 0;
}
const char *currentValueString = RedisModule_StringPtrLen(currentValueRedisString, NULL);
const char *decrementValueString = RedisModule_StringPtrLen(argv[3], NULL);
decNumber currentNum, decrementNum;
decContext set;
char resultStr[DECNUMDIGITS + 14];
decContextDefault(&set, DEC_INIT_BASE);
set.traps = 0;
set.digits = DECNUMDIGITS;
decNumberFromString(¤tNum, currentValueString, &set);
decNumberFromString(&decrementNum, decrementValueString, &set);
decNumber resultNum;
decNumberSubtract(&resultNum, ¤tNum, &decrementNum, &set);
if (!decNumberIsNegative(&resultNum)) {
decNumberToString(&resultNum, resultStr);
RedisModuleCallReply *srep = RedisModule_Call(ctx, "HSET", "ssc", argv[1], argv[2], resultStr);
RMUTIL_ASSERT_NOERROR(ctx, srep);
RedisModule_ReplyWithStringBuffer(ctx, resultStr, strlen(resultStr));
return REDISMODULE_OK;
}
if (RedisModule_CallReplyType(currentValueReply) == REDISMODULE_REPLY_NULL) {
RedisModule_ReplyWithNull(ctx);
return REDISMODULE_OK;
}
RedisModule_ReplyWithSimpleString(ctx, "NOP");
return REDISMODULE_OK;
}
int RedisModule_OnLoad(RedisModuleCtx *ctx) {
if (RedisModule_Init(ctx, "balance", 1, REDISMODULE_APIVER_1) == REDISMODULE_ERR) {
return REDISMODULE_ERR;
}
RMUtil_RegisterWriteCmd(ctx, "balance.decrement", decrementCommand);
return REDISMODULE_OK;
}
Как собрать и запустить
Я бы порекомендовал клонировать https://github.com/RedisLabs/RedisModulesSDK. Есть пример папки Замените module.c указанным выше кодом модуля. Скопируйте следующие файлы из библиотеки decNumber C в папку примера:
- decContext.c
- decNumber.h
- decNumber.c
- decNumberLocal.h
Измените Makefile в папке примера, чтобы строка, начинающаяся с module.so, выглядела так:
module.so: module.o decNumber.o decContext.o
$(LD) -o $@ module.o decNumber.o decContext.o $(SHOBJ_LDFLAGS) $(LIBS) -L$(RMUTIL_LIBDIR) -lrmutil -lc
Введите эти команды в базовом каталоге:
make clean
make
Вы можете проверить это тогда:
redis-server --loadmodule ./module.so
Это то, что вы ищете?