davidalger / capistrano-magento2

Magento 2 specific tasks for Capistrano 3
https://rubygems.org/gems/capistrano-magento2
Open Software License 3.0
228 stars 75 forks source link

Cached data from current release causes new release to fail with redis caching #151

Open PascalBrouwers opened 4 years ago

PascalBrouwers commented 4 years ago

Similar issue to: https://github.com/davidalger/capistrano-magento2/issues/138

When running magento:maintenance:enable the redis cache is not correct. You fixed it by flushing before app:config:import, but it should have done a cache flush after the magento:maintenance:enable in the release_path

issue with cache:

16:47:12       
16:47:12       Maintenance mode usage will be enforced per :magento_deploy_maintenance (setting is true)
16:47:12 07:05 magento:maintenance:enable
16:47:12       01 /usr/bin/php72 -f bin/magento -- maintenance:enable
16:47:13       01 Enabled maintenance mode
16:47:13     āœ” 01 xxx@xxx 0.865s
16:47:13       01 
16:47:13       01 In ClassReader.php line 19:
16:47:13       01                                                     
16:47:13       01   Class Vertex\Tax\Model\Cache\Type does not exist  
16:47:13       01                                                     
16:47:13       01 
16:47:13       01 maintenance:enable [--ip IP] [--magento-init-params MAGENTO-INIT-PARAMS]

To fix it I added the following to override it:

Rake::Task["magento:maintenance:enable"].clear

namespace :magento do
  namespace :maintenance do
    task :enable do
      on release_roles :all do
        within release_path do
          execute :magento, 'cache:flush'
          execute :magento, 'maintenance:enable'
          execute :magento, 'cache:flush'
        end
      end
    end
  end
end

The issue is that a cache:flush needs to be done after the maintenance:enable, because then it does a maintenance:enable in the current_path (https://github.com/davidalger/capistrano-magento2/blob/3d32c7d654bd9221f67e20c27fc4e62503bdd73d/lib/capistrano/tasks/deploy.rake#L74)

zetxek commented 4 years ago

We have also experienced this. But we have seen that capistrano-magento2 in the end just invokes magento:setup:di:compile, so it didn't look like the element to blame:

https://github.com/davidalger/capistrano-magento2/blob/3d32c7d654bd9221f67e20c27fc4e62503bdd73d/lib/capistrano/tasks/deploy.rake#L57

We still have some issues in our production environment during production releases - where classes are not generated. But seeing your issue @PascalBrouwers made us thought of it to tackle it better than manually copying the generated files from the staging server to production šŸ˜…

Maybe capistrano-magento2 could force Magento to ignore the Redis cache while preparing the 2nd release?

PascalBrouwers commented 4 years ago

That's not possible, but I should make sure that running commands between release_path and current_path the cache should be flushed.

PascalBrouwers commented 4 years ago

Small update: you also need to fix the autoloader because that is cached too. So run this too: invoke!('magento:composer:install') if fetch(:magento_deploy_composer)

PascalBrouwers commented 3 years ago

Trying to get a fail-safe method for this but it feels like there isn't one.

As soon as you flush the redis cache there is the possibility that a visitor is on the site and trigger the redis cache for the current release, while you were trying to fill it with the release_path. There is the option to set the maintenance flag, but that would mean a long downtime.

PascalBrouwers commented 3 years ago

Also an issue when running php 7.2 / 7.3 in your current release and the latest release need php 7.4 Running composer install in both directories isn't going to work with the same php version.

PascalBrouwers commented 3 years ago

Decided to go with using a different cache prefix for each deployment. This way generated files and other caches don't interfere with each other during a deploy for a site that has allot of visitors. Because this runs before composer, if also fixes the issue with the classmap cache.

after "deploy:symlink:linked_files", "set_cache_prefix"
task :set_cache_prefix do
  on release_roles :all do
    within release_path do
      unless test %Q[#{SSHKit.config.command_map[:php]} -r '
          $cfg = include "#{shared_path}/app/etc/env.php";
          exit((int)!isset($cfg["cache"]["frontend"]["default"]["id_prefix"]));
        ']
        # generate a new prefix
        version = '100_'
      else
        # get the current prefix
        version = capture %Q[#{SSHKit.config.command_map[:php]} -r '
            $cfg = include "#{shared_path}/app/etc/env.php";
            echo(($cfg["cache"]["frontend"]["default"]["id_prefix"]));
        ']
      end

      # convert prefix to a number and increment it
      version = version.gsub("_", "")
      versionNumber = version.to_i
      newVersionNumber = versionNumber + 1
      set :newVersionNumber, newVersionNumber

      # copy the file as a normal file in the next release
      execute :rm, "-rf #{release_path}/app/etc/env.php"
      execute :cp, "-rf #{shared_path}/app/etc/env.php #{release_path}/app/etc/env.php"

      execute %Q[#{SSHKit.config.command_map[:php]} -r '
        $cfg = include "#{release_path}/app/etc/env.php";
        $cfg["cache"]["frontend"]["default"]["id_prefix"] = "#{fetch(:newVersionNumber)}_";
        $out = "<?php return " . var_export($cfg, true) . ";"
        file_put_contents("#{release_path}/app/etc/env.php", $out);
        ']

      # save new env file to shared dir
      execute :rm, "-rf #{shared_path}/app/etc/env.php"
      execute :cp, "-rf #{release_path}/app/etc/env.php #{shared_path}/app/etc/env.php"
    end
  end
end

Works when env.php does not have a cache prefix yet. Also Magento fixes the array( syntax with a short syntax [ during deployment by itself. @davidalger is this an idea to include it?