Since Redis 6.2.7 (and Redis 7) an existing Lua script stopped working with the error message:
"ERR user_script:6: Attempt to modify a readonly table script: 2f405679dab26da46ec86d29bded48f66a99ff64, on #user_script:6."
The script is working fine with Redis 6.2.6. I did not find any breaking changes in the last Redis release notes.
Any clue ? Thanks !!
Here's the script:
-- returns valid task ID if successfull, nil if no tasks
local tenantId = unpack(ARGV)
local activeTenantsSet, activeTenantsList, tenantQueue = unpack(KEYS)
-- next task lua based function - return nil or taskId
function next ()
local task = redis.call('ZPOPMAX', tenantQueue)
if table.getn(task) == 0 then
redis.call('SREM', activeTenantsSet, tenantId)
redis.call('LREM', activeTenantsList, 0, tenantId)
return nil
end
redis.call('SADD', activeTenantsSet, tenantId)
redis.call('RPUSH', activeTenantsList, tenantId)
return task[1]
end
-------------
return next()
try adding 'local' in front of 'function next'
local function next ()
...
There's a fix for this in a new version of Sentry (the patch also applies cleanly to the lua scripts going back to some older versions):
https://github.com/getsentry/sentry/pull/34416/commits/7c57fe7b17f613fecc47a56c22fff3a20a958496
Related
I wrote and register this Redis Function:
local function stincr(KEYS, ARGS)
local value = redis.pcall('INCR', KEYS[1])
if value == 1 or value % 100 == 0 then
redis.call('ZADD', KEYS[2],'GT', tostring(value), KEYS[1])
end
return value;
end
redis.register_function('stincr', stincr)
Redis Functions are introduced in Redis 7. How can I call it with StackExchange.Redis?
As of right now StackExchange.Redis doesn't have any higher-level API wrapping up the functions API, however, you can just use the ad-hoc command API pretty easily. I modified your script to add the shebang in the beginning called for by redis and added it to script.lua:
#!lua name=mylib
local function stincr(KEYS, ARGS)
local value = redis.pcall('INCR', KEYS[1])
if value == 1 or value % 100 == 0 then
redis.call('ZADD', KEYS[2],'GT', tostring(value), KEYS[1])
end
return value;
end
redis.register_function('stincr', stincr)
Then loading/calling the function was pretty straight forward:
var script = File.ReadAllText("script.lua");
var muxer = ConnectionMultiplexer.Connect("localhost");
var db = muxer.GetDatabase();
db.Execute("FUNCTION", "LOAD", script);
var res = db.Execute("FCALL", "stincr", 2, "myNum", "myzset");
I have a Lua script which deletes all keys matching a pattern. The script is the following:
EVAL "return redis.call('del', 'defaultKey', unpack(redis.call('keys', ARGV[1])))" 0 ad:*
This works fine within redis-cli, but I want to execute this within a .NET app using StachExchange.Redis.
I tried the following:
await db.ExecuteAsync("EVAL", "\"return redis.call('del', 'defaultKey', unpack(redis.call('keys', ARGV[1])))\" 0 ad:*");
but I get the following error
| Errormessage: ERR wrong number of arguments for 'eval' command
Found the answer
var script = "return redis.call('del', 'defaultKey', unpack(redis.call('keys', #keypattern)))";
var prepared = LuaScript.Prepare(script);
var noOfDeletedKeys = db.ScriptEvaluate(prepared, new { keypattern = (RedisKey)"ad:*" });
template.setEnableTransactionSupport(true);
template.multi();
template.opsForValue().set("mykey", "Hello World");
List<String> dataList = template.opsForList().range("mylist", 0, -1);
template.exec();
Hi guys.
I have a list called "mylist' in my redis and its size is 50.
But when I run this code, I can't get what I want.
The field "dataList" is null, however, "mykey" with the value "Hello World" have persisted in my redis.
So how can I get my list data in a spring-data-redis transaction? many thanks.
The transaction suppport in SD-Redis helps participating in ongoing transactions and allows automatic commit (exec) / rollback (discard), so it helps wrapping commands into thread bound multi exec blocks using the same connection.
More generally redis transactions and the commands within a transaction get queued on server side and return a list of results on exec.
template.multi();
// queue set command
template.opsForValue().set("mykey", "Hello World");
// queue range command
List<String> dataList = template.opsForList().range("mylist", 0, -1);
// execute queued commands
// result[0] = OK
// result[1] = {"item-1", "item-2", "item-", ...}
List<Object> result = template.exec();
1) I setup a private ethereum network using the following command
$geth --genesis <genesis json file path> --datadir <some path to an empty
folder> --networkid 123 --nodiscover --maxpeers 0 console
2) Created an account
3) Then, started the miner using miner.start() command.
After a while ethers were getting added automatically to my account, but I don’t have any pending transaction in my private network. So from where could my miners are getting the ethers?
Even though, I didn’t instantiate any transactions in my network, I could see the some transaction being recorded in the logs, once I start the miner.
The log is as follows:
I0118 11:59:11.696523 9427 backend.go:584] Automatic pregeneration of ethash
DAG ON (ethash dir: /Users/minisha/.ethash)
I0118 11:59:11.696590 9427 backend.go:591] checking DAG (ethash dir:
/Users/minisha/.ethash)
I0118 11:59:11.696728 9427 miner.go:119] Starting mining operation (CPU=4
TOT=5)
true
> I0118 11:59:11.703907 9427 worker.go:570] commit new work on block 1 with 0
txs & 0 uncles. Took 7.109111ms
I0118 11:59:11.704083 9427 ethash.go:220] Generating DAG for epoch 0 (size
1073739904) (0000000000000000000000000000000000000000000000000000000000000000)
I0118 11:59:12.698679 9427 ethash.go:237] Done generating DAG for epoch 0, it
took 994.61107ms
I0118 11:59:15.163864 9427 worker.go:349]
And my genesis block code is as follows:
{
“nonce”: “0xdeadbeefdeadbeef”,
“timestamp”: “0x0”,
“parentHash”:
“0x0000000000000000000000000000000000000000000000000000000000000000”,
“extraData”: “0x0”,
“gasLimit”: “0x8000000”,
“difficulty”: “0x400”,
“mixhash”:
“0x0000000000000000000000000000000000000000000000000000000000000000”,
“coinbase”: “0x3333333333333333333333333333333333333333”,
“alloc”: {
}
}
Since my network is isolated and have only one node (no peers), I am quite confused with this behaviour. Any insights would be greatly appreciated.
Your client is mining empty blocks (containing no transactions) and getting rewards for mined blocks what is 5 ETH per block.
If you want to prevent empty block in your private blockchain you should consider using eth client (C++ implementation).
In case of geth client you can use a JavaScript script that will modify client's behavior. Any script can be loaded with js command: geth js script.js.
var mining_threads = 1
function checkWork() {
if (eth.getBlock("pending").transactions.length > 0) {
if (eth.mining) return;
console.log("== Pending transactions! Mining...");
miner.start(mining_threads);
} else {
miner.stop(0); // This param means nothing
console.log("== No transactions! Mining stopped.");
}
}
eth.filter("latest", function(err, block) { checkWork(); });
eth.filter("pending", function(err, block) { checkWork(); });
checkWork();
You can try EmbarkJS which can run geth client with mineWhenNeeded option on private network. It will only mine when new transactions come in.
I have same code on two servers.
I have job for adding batch job to the queue
static void Job_ScheduleBatch2(Args _args)
{
BatchHeader batHeader;
BatchInfo batInfo;
RunBaseBatch rbbTask;
str sParmCaption = "My Demonstration (b351) 2014-22-09T04:09";
SysRecurrenceData sysRecurrenceData = SysRecurrence::defaultRecurrence();
;
sysRecurrenceData = SysRecurrence::setRecurrenceStartDateTime(sysRecurrenceData, DateTimeUtil::utcNow());
sysRecurrenceData = SysRecurrence::setRecurrenceUnit(sysRecurrenceData, SysRecurrenceUnit::Minute,1);
rbbTask = new Batch4DemoClass();
batInfo = rbbTask .batchInfo();
batInfo .parmCaption(sParmCaption);
batInfo .parmGroupId(""); // The "Empty batch group".
batHeader = BatchHeader ::construct();
batHeader .addTask(rbbTask);
batHeader.parmRecurrenceData(sysRecurrenceData);
//batHeader.parmAlerts(NoYes::Yes,NoYes::Yes,NoYes::Yes,NoYes::Yes,NoYes::Yes);
batHeader.addUserAlerts(curUserId(),NoYes::No,NoYes::No,NoYes::No,NoYes::Yes,NoYes::No);
batHeader .save();
info(strFmt("'%1' batch has been scheduled.", sParmCaption));
}
and I have a batch job
class Batch4DemoClass extends RunBaseBatch
{
int methodVariable1;
int metodVariable2;
#define.CurrentVersion(1)
#localmacro.CurrentList
methodVariable1,
metodVariable2
endmacro
}
public container pack()
{
return [#CurrentVersion,#CurrentList];
}
public void run()
{
// The purpose of your job.
info(strFmt("epeating batch job Hello from Batch4DemoClass .run at %1"
,DateTimeUtil ::toStr(
DateTimeUtil ::utcNow())
));
}
public boolean unpack(container _packedClass)
{
Version version = RunBase::getVersion(_packedClass);
switch (version)
{
case #CurrentVersion:
[version,#CurrentList] = _packedClass;
break;
default:
return false;
}
return true;
}
On one server it do runs and in batch history I can see it is saving messages to the batch log and on the other server it does nothing. One server is R2 (running) and R3 (not running).
Both codes are translated to CIL. Both servers are Batch server. I can see the batch jobs in USMF/System administration/Area page inquries. Only difference I can find is that jobs on R2 has filled AOS instance name in genereal and in R3 it is empty. In R2 I can see that info messages in log and batch history but there is nothing in R3.
Any idea how to make batch jobs running?
See how to Configure an AOS instance as a batch server.
There are 3 preconditions:
Marked as a batch server
Time interval set correctly
Batch group set correctly
Update:
Deleting the user of a batch job may make the batch job never complete. Once the execute queue has filled, no further progress will happen.
Deleting the offending batch jobs is problematic, as only the owner can do so! Then consider changing BatchJob.aosValidateDelete.
Yes, worth a try to clear the tables, also if you could provide a screenshot of the already running batchjobs and their status, that might be of help further.