Framework: Redis behaves differently homestead vs vapor/aws...

Created on 10 Sep 2020  路  13Comments  路  Source: laravel/framework

  • Laravel Version: 7.28.0
  • Laravel Homestead: 11.1.2
  • PHP Version: 7.4.8
  • Database Driver & Version: whatever's on Homestead 11.1.2

Description:

I use redis for my application (with predis/predis 1.1.4). When using Redis::scan(), I always get an array back. The cursor, when I check, is a string, so I cast it to an int and move forward.

Once deployed to production (vapor), I just noticed that I get an error: "Trying to access array offset on value of type bool". This is because Redis::scan() returns false and I'm treating it as an array.

So...I've updated my code to first check if it's false before moving forward. No problem here.

The issue for me, though, is that the same code on homestead has Redis::scan() returning an array with the cursor equal to a string containing 0. In production, when the cursor is an integer equal to 0 and the result set is empty, then false will be returned. So, it seems the cursor is a string in homestead and an integer in vapor/aws redis.

The code in Redis::scan() clearly looks at the cursor to see if it's an integer equal to zero, but always (from what I've seen) returns a string "equal to" zero in my instance of homestead. Here's the code:

    // Illuminate\Redis\Connections\PhpRedisConnection

    public function scan($cursor, $options = [])
    {
        $result = $this->client->scan($cursor,
            $options['match'] ?? '*',
            $options['count'] ?? 10
        );

        if ($result === false) {
            $result = [];
        }

        return $cursor === 0 && empty($result) ? false : [$cursor, $result];
    }

Notice the last line.

So, I guess I wonder how this might be fixed to make both environments identical. I also worry about other places where this type of thing might occur.

Steps To Reproduce:

Install homestead with redis
Use predis/predis
Run code that calls Redis::scan(), paying attention to what is returned....the cursor will be a string containing the character for zero.
Do the previous steps on vapor with redis, paying attention to what is returned...the boolean value, false.

I think that's it. Let me know how I can help and whether or not this should be posted elsewhere (laravel/homestead, predis/predis, or wherever).

Thx. 馃

needs more info

All 13 comments

@tillkruss do you know what's going on here?

@telkins: Can you check what Cache::connection(); returns on Homestead, as well as Vapor?

Are you sure you're using Predis on Vapor? I thought we automatically configured your application to use phpredis which may have some slight difference in how scan is working?

@tillkruss I can do this on homestead and get back to you, but I'm not sure how to do it on vapor without putting some sort of log in and deploying....which I'll do, if necessary.

@taylorotwell I think I have been coming to this realization or suspicion in the last hour or so as I work through another redis-related problem. (Changing the prefix on the fly....can't get it to work.)

So...I've switched to predis locally (homestead), but now I've got another set of problems. Calls to zscan, for example, are different using phpredis than predis. Not a big deal, but I've got to get them nailed down.

Anyway, it's likely that it's either a difference between predis and phpredis or it's a difference between a single connection and a cluster. With predis, there's a difference and there may be one with phpredis as well. I thought that might be related somehow, but it seems more likely that it's just the difference between the two "libraries"

So...let me get things working with phpredis and then I'll get the Cache::connection() info logged out for @tillkruss locally and on vapor. I may not get to it until tomorrow...as it's pushing midnight here. 馃槴

Thanks, everyone. 馃

@tillkruss Now that I have you here... 馃槣 ...perhaps you can help me with a related issue...?

I'm using spatie's laravel-tenancy package and trying to change the caching prefix "on the fly".

I update the database.redis.options.prefix configuration setting, which is what's used when the instance is created. But, I'm not sure what to do next?

When switching tenants and preparing the cache, this is what's done:

        config()->set('cache.prefix', $prefix);

        app('cache')->forgetDriver($this->storeName);

So...the configuration setting is changed and the cache driver's "forgotten"...so the next time it's needed, it's rebuilt using the updated configuration setting.

How can I do this -- or something like it -- with my redis prefix...? (Preferably where it doesn't matter if I'm using predis or phpredis or if I'm on homestead or on vapor.)

I'm struggling...discourse posts and tweets have gone unanswered and I've spent hours and hours and hours. The Interwebz have not been terribly helpful today. 馃槥

If you don't know, any advice or guesses or whatever might be helpful.

Thanks again. 馃

@telkins: Let's do one thing at a time. You should be able to run vapor tinker --code="dump(Cache::connection())"

vapor tinker --code="dump(Cache::connection())"

@tillkruss Here's the vapor output...from vapor tinker --code="dump(Cache::connection())" production:

==> Executing Function...

Status Code: 0

Output:

Illuminate\Redis\Connections\PhpRedisClusterConnection {#4809
  #connector: null
  #config: []
  #client: RedisCluster {#4811
    _masters: array:1 [
      0 => array:2 [
        0 => "10.0.2.75"
        1 => 6379
      ]
    ]
    _redir: null
    mode: ATOMIC
    lastError: null
    options: {
      SLAVE_FAILOVER: NONE
      TCP_KEEPALIVE: 0
      READ_TIMEOUT: 0.0
      COMPRESSION: NONE
      SERIALIZER: NONE
      PREFIX: "r4nkt_database_"
      SCAN: NORETRY
    }
  }
  #name: "cache"
  #events: null
}

@tillkruss And here's from homestead (tinker):

>>> dump(Cache::connection())
Illuminate\Redis\Connections\PhpRedisConnection^ {#4824
  #connector: Closure()^ {#4823
    class: "Illuminate\Redis\Connectors\PhpRedisConnector"
    this: Illuminate\Redis\Connectors\PhpRedisConnector {#4829 鈥
    use: {
      $config: array:4 [
        "host" => "127.0.0.1"
        "password" => null
        "port" => "6379"
        "database" => "1"
      ]
      $options: array:2 [
        "cluster" => "redis"
        "prefix" => "r4nkt_database_"
      ]
    }
    file: "./vendor/laravel/framework/src/Illuminate/Redis/Connectors/PhpRedisConnector.php"
    line: "26 to 30"
  }
  #config: array:4 [
    "host" => "127.0.0.1"
    "password" => null
    "port" => "6379"
    "database" => "1"
  ]
  #client: Redis {#4825
    isConnected: true
    host: "127.0.0.1"
    port: 6379
    auth: null
    mode: ATOMIC
    dbNum: 1
    timeout: 0.0
    lastError: null
    persistentId: null
    options: {
      TCP_KEEPALIVE: 0
      READ_TIMEOUT: 0.0
      COMPRESSION: NONE
      SERIALIZER: NONE
      PREFIX: "r4nkt_database_"
      SCAN: NORETRY
    }
  }
  #name: "cache"
  #events: null
}
=> Illuminate\Redis\Connections\PhpRedisConnection {#4824}

But, as I said earlier, I have been in the process of switching from predis to phpredis since posting this issue. So, it does seem like the difference is likely phpredis vs predis...?

Looks like your Vapor environment is using a RedisCluster and Homestead is using a single instance Redis connection.

Could you try spinning up a non-cluster in AWS and see if that resolves the issue?

I assume PhpRedis' RedisCluster is handling SCAN slightly differently...

Looks like your Vapor environment is using a RedisCluster and Homestead is using a single instance Redis connection.

Could you try spinning up a non-cluster in AWS and see if that resolves the issue?

I assume PhpRedis' RedisCluster is handling SCAN slightly differently...

@tillkruss Thx for the feedback. It could be the difference between RedisCluster and Redis, as you suggest....or it could be the difference between predis and phpredis.

Since vapor "forces" phpredis, I'm refactoring my code to use and accommodate phpredis. It's a little more difficult to debug and I've finally had a minor breakthrough with changing the redis prefix on the fly. (I'm resorting to calling forgetInstance('redis') on the service container after updating the redis prefix configuration setting and then explicitly getting redis from the service container later rather than via the facade...cuz there seems to be a difference for some reason. This feels a little icky and if there's a better way, please let me know.)

Anyway, I'm hoping to be able to get everything running and passing tests locally and then I'll deploy and let you know how it goes. Again, if you have any guidance regarding my "secondary issue" (https://github.com/laravel/framework/issues/34253#issuecomment-690737743), I'd love to hear it. This is my current solution:

        config()->set('database.redis.options.prefix', $prefix);

        app()->forgetInstance('redis');

Then, I explicitly call app('redis') to get the redis manager. If I call the Redis facade, I get what seems to be a cached version...because the prefix that it uses is the "old" one.

Alright...I'll try to get the changes into production tonight or tomorrow and let you know how it goes.

Thx again...! 馃

I'd either:

  • Call setPrefix() on your cache's RedisStore
  • Manage the prefix for all keys yourself (maybe a little helper)
  • Manage multiple Redis or Cache connections with different prefixes

I'd either:

  • Call setPrefix() on your cache's RedisStore
  • Manage the prefix for all keys yourself (maybe a little helper)
  • Manage multiple Redis or Cache connections with different prefixes

@tillkruss Thx. I'll give one or more of these a try. It will likely be the first, as it's meant to occur when switching from one tenant to another in a multi-tenant application. The second might work, but would be more prone to error, I imagine. Whatever the case, I'll also get back with my progress/results. Thx again. 馃

@tillkruss FWIW, I finally got things sorted in my move from predis to phpredis (I hope), got nearly all of my tests to pass (had to skip one because I can't mock the Redis facade...well, I can, but since I'm not using it, then it doesn't help), deployed to production, and successfully ran a test script. Everything seems to be OK.

I will continue to keep my eye on things, of course, and open another ticket if there seem to be problems. I do think that the issue here was simply running predis locally and (unknowingly) running phpredis on vapor...and, of course, the differences between the two.

I do believe you're right that my "Vapor environment is using a RedisCluster and Homestead is using a single instance Redis connection"....but it appears that I'm not seeing any issues on either environment at the moment. Let's hope it stays that way. 馃

Alright...I'll close this ticket out. Thx again for your help...! 馃

Was this page helpful?
0 / 5 - 0 ratings

Related issues

YannPl picture YannPl  路  3Comments

kerbylav picture kerbylav  路  3Comments

iivanov2 picture iivanov2  路  3Comments

JamborJan picture JamborJan  路  3Comments

Anahkiasen picture Anahkiasen  路  3Comments