There are two ways to configure how Magento send order related emails (order confirmation, invoice, shipment and credit memo emails):
Either immediately when an action is performed (for example, right after you place an order).
Or cron-based (asynchronous).
Magento itself recommends to configure this to be asynchronous for performance reasons, so the email dispatch process doesn't block nor delay what the customer is doing (for example, less time consumption when the customer is placing an order), or to avoid long cron runs (or total crons block) when an ERP is updating orders status in bulk.
Enabling the “Asynchronous email notifications” setting moves processes that handle checkout and order processing email notifications to the background.
For my personal experience, it is also recommended to configure emails sending to be asynchronous to avoid having unsent emails.
When this is configured as immediately (not async), if an email fails to be dispatched because of any reason (like a server timeout) then Magento won’t try to send that email again in the future and the customer won’t be ever notified of his/her action.
Basically, there are a lot of good reasons to have this to be async.
Before setting this up
If you are going live for the first time on a new site there's not much to worry about, but if you are changing this setting from not-async to cron-based on an already live site then there's something to consider first.
The way this works when it's configured to be async is that Magento will rely on a cron to pick up from the database (from those tables related to Orders, Invoices, Shipments and Credit Memos) all those entities where the email was not dispatched, and it will send it.
Problem is that Magento doesn't care much about the date of those entities.
For example, if there’s an Order from 2019 whose order confirmation email was not sent because of any reason (for example, because there was a PHP error that stopped the execution), the cron will now pick this Order up and Magento will send the email related to that order.
To avoid this behaviour we should mark all pending emails from Orders, Invoices, Shipments, and Credit Memos as if they doesn't need to be dispatched, which can be done by executing the following SQL statements before enabling the asynchronous sending.
UPDATE sales_order SET send_email = 0;
UPDATE sales_invoice SET send_email = 0;
UPDATE sales_shipment SET send_email = 0;
UPDATE sales_creditmemo SET send_email = 0;
What we are doing here is ignoring the sending of emails for old Orders (and Invoices, Shipments and Credit Memos) so when we change this to be async the cron will only cares for future stuff.
Changing the configuration
Enable the “Asynchronous sending” option under “Stores → Settings → All Stores → Sales → Sales Email → General Settings”.
"What now?" issues every now and then with Magento are the norm, and I got this one a few days ago:
SQLSTATE[40001]: Serialization failure: 1213 Deadlock found when trying to get lock; try restarting transaction, query was: INSERT INTO `catalog_product_index_eav_temp` SELECT DISTINCT [...]
Apparently the server went down for a second, or MySQL went down... or something happened and a reindex process was interrupted (in my case the one for the "Product EAV" index), and there was no way to make it work again.
Even the usual bin/magento indexer:reset and then a manual reindex with bin/magento indexer:reindex didn't do the trick and the only difference was that instead of the previous error I was getting the classic one.
Product EAV index is locked by another reindex process. Skipping.
Lucky me, I find the following on Twitter.
Magento 2 cron uses `SELECT GET_LOCK()`, which sets a lock for the current DB session. If you lose the session, you loose control over the lock, leaving you unable to unlock it.
Solution: change the default lock-prefix in env.php.
Basically, when you have no way to "unlock" an index, and after you already tried the reset and manual reindex, just change that prefix in your app/etc/env.php file to something new, then reset and finally try to reindex again.
Depending on the Magento version you have, or the Elasticsearch version you want, the process of getting this work locally can be easy or a nightmare I'll try to simplify here in this post.
If you already know what version to install, skip the following and jump straight to the installation instructions.
What Elasticsearch version do you need?
If we dive into the official Magento documentation and take a quick look at the different changes made to it across time, we can learn a few things.
Magento 2.3.1 adds support for Elasticsearch 6.x and it is enabled by default. Magento still provides connectivity for Elasticsearch 2.x and 5.x but these must be enabled in order to use these versions.
You must install and configure Elasticsearch 7.6.x before upgrading to Magento 2.4.0.
Magento does not support Elasticsearch 2.x, 5.x, and 6.x.
Or, to summarize:
For Magento 2.3.1 to Magento 2.3.4 you need to install Elasticsearch 6.x.
For Magento 2.3.5 to Magento 2.3.n (anything before 2.4.0) you'll need to install Elasticsearch 7.x.x. It works also with 6.8.x, but version 7.x.x is easy to install.
For Magento 2.4.0 and up you need to install Elasticsearch 7.6.x. No support for previous versions.
If you are working with multiple Magento projects on different versions, I would suggest you to install Elasticsearch 6.x to support from Magento 2.3.1 to Magento 2.3.5, and everything before Magento 2.4.
If you have all projects on Magento 2.3.5 and up then install Elasticsearch 7.6.x.
Install Elasticsearch 6.x on macOS
You need Homebrew first, so please install it by doing the following:
You can check the official Homebrew page for more information about it and alternative installations options.
Then, we need to add a third-party repo to Homebrew and a few packages, including Elasticsearch itself.
brew tap elastic/tap; brew cask install homebrew/cask-versions/adoptopenjdk8; brew install elasticsearch@6.8;
When you do that you'll get a lot of output on your terminal, but you need to only care for the installation path of the last elasticsearch@6.8 package.
➜ brew install elasticsearch@6.8;
==> Downloading https://homebrew.bintray.com/bottles/elasticsearch%406.8-6.8.8.catalina.bottle.tar.gz
Already downloaded: /Users/nahuelsanchez/Library/Caches/Homebrew/downloads/01d0782011cbecdb3b1125469ee2ed60ef07926a8f36752c64d1b8c2b763736d--elasticsearch@6.8-6.8.8.catalina.bottle.tar.gz
==> Pouring elasticsearch@6.8-6.8.8.catalina.bottle.tar.gz
==> /usr/local/Cellar/elasticsearch@6.8/6.8.8/bin/elasticsearch-keystore create
==> Caveats
Data: /usr/local/var/lib/elasticsearch/
Logs: /usr/local/var/log/elasticsearch/elasticsearch_nahuelsanchez.log
Plugins: /usr/local/var/elasticsearch/plugins/
Config: /usr/local/etc/elasticsearch/
elasticsearch@6.8 is keg-only, which means it was not symlinked into /usr/local,
because this is an alternate version of another formula.
If you need to have elasticsearch@6.8 first in your PATH run:
echo 'export PATH="/usr/local/opt/elasticsearch@6.8/bin:$PATH"' >> ~/.zshrc
To have launchd start elasticsearch@6.8 now and restart at login:
brew services start elasticsearch@6.8
Or, if you don't want/need a background service you can just run:
elasticsearch
==> Summary
🍺 /usr/local/Cellar/elasticsearch@6.8/6.8.8: 136 files, 103.4MB
The path below "Summary" is the one we need to mind, where mine is /usr/local/Cellar/elasticsearch@6.8/6.8.8.
Go to that folder and install a few Elasticsearch plugins.
That host and port on that cURL command (which by default is localhost:9200) is the host and port you need to use for configuring Elasticsearch in Magento. If it doesn't work, try the same cURL call but with 127.0.0.1:9200.
Remember to brew services stop elasticsearch@6.8 when you are done working so it's not running forever in your computer.
Install Elasticsearch 7.x on macOS
You need Homebrew first, so please install it by doing the following:
You can check the official Homebrew page for more information about it and alternative installations options.
Then, we need to add a third-party repo to Homebrew and the Elasticsearch package itself.
brew tap elastic/tap; brew install elastic/tap/elasticsearch-full;
When you do that you'll get a lot of output on your terminal, but you need to only care for the installation path of the elastic/tap/elasticsearch-full package, which is located just at the end below "Summary".
For example, mine says /usr/local/Cellar/elasticsearch-full/7.8.0.
Go to that folder and install a few Elasticsearch plugins.
Start Elasticsearch by doing brew services start elastic/tap/elasticsearch-full.
To check if everything is good, you can do a cURL to the Elasticsearch instance by running curl "http://localhost:9200/_nodes/settings?pretty=true" which will output a big JSON.
That host and port on that cURL command (which by default is localhost:9200) is the host and port you need to use for configuring Elasticsearch in Magento. If it doesn't work, try the same cURL call but with 127.0.0.1:9200.
Remember to brew services stop elastic/tap/elasticsearch-full when you are done working so it's not running forever in your computer.
Can I have both installed at the same time?
No... well, I didn't try it but I think you can't. I would avoid such experiment.
What you can do for sure is uninstall one and install the other on demand.
If you do that you need to uninstall the Elasticsearch plugins you install it and install them again so you ended up with the right plugins version compatible with the Elasticsearch version currently installed.