phpspreadsheet with redis increases run time - redis

when setting the cache method to redis it seems to increase runtime for loading a spreadsheet and reading etc.
before i use any part of PHPSpreadsheet classes i run the following
if(CACHE_ON){
$client = new \Redis();
$client->connect(CACHE_HOST, 6379);
$pool = new \Cache\Adapter\Redis\RedisCachePool($client);
$simpleCache = new \Cache\Bridge\SimpleCache\SimpleCacheBridge($pool);
\PhpOffice\PhpSpreadsheet\Settings::setCache($simpleCache);
}
//...
try {
$fileType = IOFactory::identify($uploadFile);
} catch(\PhpOffice\PhpSpreadsheet\Reader\Exception $e) {
$message = 'Input file is not an Excel Workbook. Please save as an Excel Workbook and try again.';
$this->log->error('Error loading file: '.$e->getMessage() . mime_content_type($uploadFile) . $message);
return ['error'=>true,'message'=>$message];
}
I don't understand why it increases time by 1/2 seconds. is this normal behaviour or do i need to update anything in redis?

Maybe you can use redis connection pool, don't use redis when need, you can prepare in advance.

Related

Redis StackExchange LuaScripts with parameters

I'm trying to use the following Lua script using C# StackExchange library:
private const string LuaScriptToExecute = #"
local current
current = redis.call(""incr"", KEYS[1])
if current == 1 then
redis.call(""expire"", KEYS[1], KEYS[2])
return 1
else
return current
end
Whenever i'm evaluating the script "as a string", it works properly:
var incrementValue = await Database.ScriptEvaluateAsync(LuaScriptToExecute,
new RedisKey[] { key, ttlInSeconds });
If I understand correctly, each time I invoke the ScriptEvaluateAsync method, the script is transmitted to the redis server which is not very effective.
To overcome this, I tried using the "prepared script" approach, by running:
_setCounterWithExpiryScript = LuaScript.Prepare(LuaScriptToExecute);
...
...
var incrementValue = await Database.ScriptEvaluateAsync(_setCounterWithExpiryScript,
new[] { key, ttlInSeconds });
Whenever I try to use this approach, I receive the following error:
ERR Error running script (call to f_7c891a96328dfc3aca83aa6fb9340674b54c4442): #user_script:3: #user_script: 3: Lua redis() command arguments must be strings or integers
What am I doing wrong?
What is the right approach in using "prepared" LuaScripts that receive dynamic parameters?
If I look in the documentation: no idea.
If I look in the unit test on github it looks really easy.
(by the way, is your ttlInSeconds really RedisKey and not RedisValue? You are accessing it thru KEYS[2] - shouldnt that be ARGV[1]? Anyway...)
It looks like you should rewrite your script to use named parameters and not arguments:
private const string LuaScriptToExecute = #"
local current
current = redis.call(""incr"", #myKey)
if current == 1 then
redis.call(""expire"", #myKey, #ttl)
return 1
else
return current
end";
// We should load scripts to whole redis cluster. Even when we dont have any.
// In that case, there will be only one EndPoint, one iteration etc...
_myScripts = _redisMultiplexer.GetEndPoints()
.Select(endpoint => _redisMultiplexer.GetServer(endpoint))
.Where(server => server != null)
.Select(server => lua.Load(server))
.ToArray();
Then just execute it with anonymous class as parameter:
for(var setCounterWithExpiryScript in _myScripts)
{
var incrementValue = await Database.ScriptEvaluateAsync(
setCounterWithExpiryScript,
new {
myKey: (RedisKey)key, // or new RedisKey(key) or idk
ttl: (RedisKey)ttlInSeconds
}
)// .ConfigureAwait(false); // ? ;-)
// when ttlInSeconds is value and not key, just dont cast it to RedisKey
/*
var incrementValue = await
Database.ScriptEvaluateAsync(
setCounterWithExpiryScript,
new {
myKey: (RedisKey)key,
ttl: ttlInSeconds
}
).ConfigureAwait(false);*/
}
Warning:
Please note that Redis is in full-stop mode when executing scripts. Your script looks super-easy (you sometimes save one trip to redis (when current != 1) so i have a feeling that this script will be counter productive in greater-then-trivial scale. Just do one or two calls from c# and dont bother with this script.
First of all, Jan's comment above is correct.
The script line that updated the key's TTL should be redis.call(""expire"", KEYS[1], ARGV[1]).
Regarding the issue itself, after searching for similar issues in RedisStackExchange's Github, I found that Lua scripts do not work really well in cluster mode.
Fortunately, it seems that "loading the scripts" isn't really necessary.
The ScriptEvaluateAsync method works properly in cluster mode and is sufficient (caching-wise).
More details can be found in the following Github issue.
So at the end, using ScriptEvaluateAsync without "preparing the script" did the job.
As a side note about Jan's comment above that this script isn't needed and can be replaced with two C# calls, it is actually quite important since this operation should be atomic as it is a "Rate limiter" pattern.

Redlock.net can't aquire lock

Using:
StackExchange.Redis v1.1.608.0
RedLock.net v1.7.4.0
This code always returns false after 250-600ms:
var eps = new [] { new DnsEndPoint("localhost", 6379) };
var lf = new RedisLockFactory(eps);
var resource = "the-thing-we-are-locking-on";
var expiry = TimeSpan.FromSeconds(30);
using (var redisLock = lf.Create(resource, expiry))
{
Response.Write("Lock acquired: " + redisLock.IsAcquired);
}
I'm struggling to work out why, as I'm able to cache things in Redis just fine with StackExchange.Redis connection string localhost,allowAdmin=true.
In the Redis console I can see a client is being connected, but that's as far as it gets.
I've added a firewall rule for port 6379 but nothing changed.
Any ideas on my the lock can never be acquired?
Found the cause of the issue. I'm using MSOpenTech Redis server v3.2.100:
https://github.com/MSOpenTech/redis/releases
Rolling back to v3.0.500 appears to fix the issue. Not ideal, but in testing environment should be OK for now.

Transaction not working after unsuccessful connection to redis using StackExchange.Redis

I'm not sure, whether it is a bug or I'm doing something wrong. Here is the code:
using StackExchange.Redis;
ConnectionMultiplexer conn = null;
while (conn == null);
{
try
{
conn = ConnectionMultiplexer.Connect("localhost:6379");
}
catch (Exception)
{
conn = null;
Thread.Sleep(TimeSpan.FromSeconds(5));
}
}
var db = conn.GetDatabase();
var transaction = db.CreateTransaction();
var tasks = new List<Task>();
tasks.Add(transaction.HashSetAsync("key", "field", "value"));
if (transaction.Execute())
{
Task.WaitAll(tasks.ToArray());
}
When I run it with started redis (windows version - 2.6, 2.8.17, 2.8.19), everything works fine. If I start redis after few loops of the cycle, either it doesn't jump into if-statement or it jumps and get blocked on WaitAll(). If I try to check values in redis, they are stored.
This situation happens when we start server and forget to start redis. After postpone start of redis it gets stuck. The same problem appears when using batch instead of transaction.
Am I doing connection to multiplexer wrong or is it bug? (I found few that looked similar but I'm not sure)
It was a bug in older versions of StackExchange.Redis - 1.0.481, 1.0.488 (didn't test any older ones). With new version 1.1.553 it works fine (https://github.com/StackExchange/StackExchange.Redis/issues/200).

How to backup and restore sessions on Fiddler startup and shutdown?

What I want is backup all sessions at Fiddler shutdown and when I turn it on again it needs to load that sessions again.
I managed to change the FiddlerScript by creating a save action and dump all sessions with this:
case "save":
FiddlerObject.UI.actSelectAll();
FiddlerObject.UI.actSaveSessionsToZip(CONFIG.GetPath("Captures") + "saved.saz");
FiddlerObject.StatusText = "Saved in " + CONFIG.GetPath("Captures") + "saved.saz";
break;
It works fine and all currently loaded sessions are saved.
I tried to create a action to restore them but it does nothing (I loaded the session but don't know how to get back in the grid):
case "restore":
//I don't know what I need to do with this
Utilities.ReadSessionArchive(CONFIG.GetPath("Captures") + "saved.saz", true);
break;
After that I want to do something similar do execute them with ExecAction at startup and shutdown but this is another part of my puzzle.
TL;DR
How to restore a previously saved dump using FiddlerScript on startup?
Rules > Customize Rules.
Update the OnBoot and OnShutdown functions thusly:
static function OnBoot() {
FiddlerApplication.UI.actLoadSessionArchive("_stored.saz");
}
static function OnShutdown() {
FiddlerApplication.UI.actSelectAll();
var sFilename = (CONFIG.GetPath("Captures") + "_stored.saz");
FiddlerApplication.UI.actSaveSessionsToZip(sFilename);
}

Redis on Appharbor - Booksleeve GetString exception

i am trying to setup Redis on appharbor. I have followed their instructions and again i have an issue with the Booksleeve API. Here is the code i am using to make it work initially:
var connectionUri = new Uri(url);
using (var redis = new RedisConnection(connectionUri.Host, connectionUri.Port, password: connectionUri.UserInfo.Split(new[] { ':' }, 2)[1]))
{
redis.Strings.Set(1, "greeting", "welcome to remember your stuff!");
try
{
var task = redis.Strings.GetString(1, "greeting");
redis.Wait(task);
ViewBag.Message = task.Result;
}
catch (Exception)
{
// It throws an exception trying to wait for the task?
}
}
However, the issue is that it sets the string correctly, but when trying to retrieve the same string from the key value store, it throws a timeout exception waiting for the task to eexecute. However, this code works on my local redis server connection.
Am i using the API in a wrong way? or is this something related to Appharbor?
Thanks
Like a SqlConnection, you need to call Open() (otherwise your messages are queued for delivery).
Unlike SqlConnection, you should not fire up a RedisConnection each time you need it - it is intended to be used as a shared, thread-safe, multiplexer - i.e. a single connection is held somewhere and used by lots and lots of unrelated callers. Unless of course you only need to do one thing!