craftcms / aws-s3

Amazon S3 volume type for Craft CMS.
https://plugins.craftcms.com/aws-s3
MIT License
61 stars 28 forks source link

Instances of craft\awss3\S3Client cannot be serialized #144

Closed jamesmacwhite closed 2 years ago

jamesmacwhite commented 2 years ago

Description

We are getting a transient issue with the AWS S3 plugin with the following error occurring on various entries.

Instances of craft\awss3\S3Client cannot be serialized

The error is not always thrown however. After loading the page that threw the error, reloading it then works.

The full stack trace from our staging environment is below. I know there is a similar issue #5395 although the stacktrace seems a bit different.

An Error occurred while handling another error:
RuntimeException: Instances of craft\awss3\S3Client cannot be serialized in /srv/app/nttmcoll-staging/htdocs/vendor/aws/aws-sdk-php/src/AwsClient.php:276
Stack trace:
#0 [internal function]: Aws\AwsClient->__sleep()
#1 /srv/app/nttmcoll-staging/htdocs/vendor/yiisoft/yii2/caching/Cache.php(249): serialize(Array)
#2 /srv/app/nttmcoll-staging/htdocs/vendor/yiisoft/yii2/caching/Cache.php(608): yii\caching\Cache->set('parsed_globals_...', Array, 30, Object(yii\caching\TagDependency))
#3 /srv/app/nttmcoll-staging/htdocs/vendor/nystudio107/craft-seomatic/src/services/MetaContainers.php(271): yii\caching\Cache->getOrSet('parsed_globals_...', Object(Closure), 30, Object(yii\caching\TagDependency))
#4 /srv/app/nttmcoll-staging/htdocs/vendor/nystudio107/craft-seomatic/src/services/MetaContainers.php(249): nystudio107\seomatic\services\MetaContainers->parseGlobalVars()
#5 /srv/app/nttmcoll-staging/htdocs/vendor/nystudio107/craft-seomatic/src/Seomatic.php(747): nystudio107\seomatic\services\MetaContainers->includeMetaContainers()
#6 [internal function]: nystudio107\seomatic\Seomatic->nystudio107\seomatic\{closure}(Object(yii\base\Event))
#7 /srv/app/nttmcoll-staging/htdocs/vendor/yiisoft/yii2/base/Event.php(312): call_user_func(Object(Closure), Object(yii\base\Event))
#8 /srv/app/nttmcoll-staging/htdocs/vendor/yiisoft/yii2/base/Component.php(642): yii\base\Event::trigger('craft\\web\\View', 'endPage', Object(yii\base\Event))
#9 /srv/app/nttmcoll-staging/htdocs/vendor/yiisoft/yii2/web/View.php(180): yii\base\Component->trigger('endPage')
#10 /srv/app/nttmcoll-staging/htdocs/vendor/craftcms/cms/src/web/View.php(1731): yii\web\View->endPage(false)
#11 /srv/app/nttmcoll-staging/htdocs/vendor/yiisoft/yii2/views/errorHandler/exception.php(544): craft\web\View->endPage()
#12 /srv/app/nttmcoll-staging/htdocs/vendor/yiisoft/yii2/base/View.php(347): require('/srv/app/nttmco...')
#13 /srv/app/nttmcoll-staging/htdocs/vendor/yiisoft/yii2/base/View.php(257): yii\base\View->renderPhpFile('/srv/app/nttmco...', Array)
#14 /srv/app/nttmcoll-staging/htdocs/vendor/yiisoft/yii2/web/ErrorHandler.php(270): yii\base\View->renderFile('/srv/app/nttmco...', Array, Object(craft\web\ErrorHandler))
#15 /srv/app/nttmcoll-staging/htdocs/vendor/yiisoft/yii2/web/ErrorHandler.php(127): yii\web\ErrorHandler->renderFile('@yii/views/erro...', Array)
#16 /srv/app/nttmcoll-staging/htdocs/vendor/craftcms/cms/src/web/ErrorHandler.php(192): yii\web\ErrorHandler->renderException(Object(RuntimeException))
#17 /srv/app/nttmcoll-staging/htdocs/vendor/yiisoft/yii2/base/ErrorHandler.php(135): craft\web\ErrorHandler->renderException(Object(RuntimeException))
#18 /srv/app/nttmcoll-staging/htdocs/vendor/craftcms/cms/src/web/ErrorHandler.php(71): yii\base\ErrorHandler->handleException(Object(RuntimeException))
#19 [internal function]: craft\web\ErrorHandler->handleException(Object(RuntimeException))
#20 {main}
Previous exception:
RuntimeException: Instances of craft\awss3\S3Client cannot be serialized in /srv/app/nttmcoll-staging/htdocs/vendor/aws/aws-sdk-php/src/AwsClient.php:276
Stack trace:
#0 [internal function]: Aws\AwsClient->__sleep()
#1 /srv/app/nttmcoll-staging/htdocs/vendor/yiisoft/yii2/caching/Cache.php(249): serialize(Array)
#2 /srv/app/nttmcoll-staging/htdocs/vendor/yiisoft/yii2/caching/Cache.php(608): yii\caching\Cache->set('parsed_globals_...', Array, 30, Object(yii\caching\TagDependency))
#3 /srv/app/nttmcoll-staging/htdocs/vendor/nystudio107/craft-seomatic/src/services/MetaContainers.php(271): yii\caching\Cache->getOrSet('parsed_globals_...', Object(Closure), 30, Object(yii\caching\TagDependency))
#4 /srv/app/nttmcoll-staging/htdocs/vendor/nystudio107/craft-seomatic/src/services/MetaContainers.php(249): nystudio107\seomatic\services\MetaContainers->parseGlobalVars()
#5 /srv/app/nttmcoll-staging/htdocs/vendor/nystudio107/craft-seomatic/src/Seomatic.php(747): nystudio107\seomatic\services\MetaContainers->includeMetaContainers()
#6 [internal function]: nystudio107\seomatic\Seomatic->nystudio107\seomatic\{closure}(Object(yii\base\Event))
#7 /srv/app/nttmcoll-staging/htdocs/vendor/yiisoft/yii2/base/Event.php(312): call_user_func(Object(Closure), Object(yii\base\Event))
#8 /srv/app/nttmcoll-staging/htdocs/vendor/yiisoft/yii2/base/Component.php(642): yii\base\Event::trigger('craft\\web\\View', 'endPage', Object(yii\base\Event))
#9 /srv/app/nttmcoll-staging/htdocs/vendor/yiisoft/yii2/web/View.php(180): yii\base\Component->trigger('endPage')
#10 /srv/app/nttmcoll-staging/htdocs/vendor/craftcms/cms/src/web/View.php(1731): yii\web\View->endPage(false)
#11 /srv/app/nttmcoll-staging/htdocs/vendor/craftcms/cms/src/web/View.php(462): craft\web\View->endPage()
#12 /srv/app/nttmcoll-staging/htdocs/vendor/craftcms/cms/src/web/Controller.php(201): craft\web\View->renderPageTemplate('_dispatcher', Array, 'site')
#13 /srv/app/nttmcoll-staging/htdocs/vendor/craftcms/cms/src/controllers/TemplatesController.php(102): craft\web\Controller->renderTemplate('_dispatcher', Array)
#14 [internal function]: craft\controllers\TemplatesController->actionRender('_dispatcher', Array)
#15 /srv/app/nttmcoll-staging/htdocs/vendor/yiisoft/yii2/base/InlineAction.php(57): call_user_func_array(Array, Array)
#16 /srv/app/nttmcoll-staging/htdocs/vendor/yiisoft/yii2/base/Controller.php(178): yii\base\InlineAction->runWithParams(Array)
#17 /srv/app/nttmcoll-staging/htdocs/vendor/yiisoft/yii2/base/Module.php(552): yii\base\Controller->runAction('render', Array)
#18 /srv/app/nttmcoll-staging/htdocs/vendor/craftcms/cms/src/web/Application.php(293): yii\base\Module->runAction('templates/rende...', Array)
#19 /srv/app/nttmcoll-staging/htdocs/vendor/yiisoft/yii2/web/Application.php(103): craft\web\Application->runAction('templates/rende...', Array)
#20 /srv/app/nttmcoll-staging/htdocs/vendor/craftcms/cms/src/web/Application.php(278): yii\web\Application->handleRequest(Object(craft\web\Request))
#21 /srv/app/nttmcoll-staging/htdocs/vendor/yiisoft/yii2/base/Application.php(384): craft\web\Application->handleRequest(Object(craft\web\Request))
#22 /srv/app/nttmcoll-staging/htdocs/web/index.php(12): yii\base\Application->run()
#23 {main}

Seeing SEOMatic in the stacktrace this might be related to our custom SEOImage logic based on the entry type. Our main application template has the following logic, we are using the imager plugin as we use Imgix.

{% switch entry.section.handle %}
     {% case 'entryHandle' %}
          {% set seoImage = craft.imager.transformImage(featuredImage, imageTransform) %}
    {% case 'anotherHandle' %}
         {% set seoImage = craft.imager.transformImage(anotherImageField, imageTransform) %}
   {% endswitch %}
...

{% if seoImage %}
    {% do seomatic.meta.setAttributes({
        seoImage: seoImage,
        ogImage: seoImage,
        ogImageWidth: seoImage.width,
        ogImageHeight: seoImage.height,
        twitterImage: seoImage,
        twitterImageWidth: seoImage.width,
        twitterImageHeight: seoImage.height
   }) %}
{% endif %}

I don't know if this is triggering an issue. The fact that the error occurs but then doesn't on reloading, suggests something isn't present on the first load, but then is on the second, but I'm not fully understanding the error itself.

This was captured on our staging environment, this runs with caching disabled completely, but I can see there's some SEOMatic related cache properties in the stack trace, so I'm a bit confused

Any further insights would be helpful. It is one of those weird errors that's transient so harder to solve!

Additional info

jamesmacwhite commented 2 years ago

@khalwat Hi Andrew, just wanted to highlight if you had any ideas on this given SEOMatic is in the stacktrace, but I'm not sure if it's a specific issue with SEOMatic, given it's a error exception from the S3 plugin itself.

khalwat commented 2 years ago

SEOmatic does cache the rendered result of the meta information for each page, which is why it's in the backtrace. It's unclear to me why or how a craft\awss3\S3Client class would be part of that serialization, however.

I'm assuming craft.imager.transformImage() just returns a URL? Can you double-check that it's not returning an craft\awss3\S3Client object instead?

jamesmacwhite commented 2 years ago

Thanks for your reply Andrew. The SEO image being transformed by Imager X will be an asset from an S3 bucket which is why there's an S3 client in there I believe, as the volume the asset is stored on will be S3.

As far as I know craft.imager.transformImage() is designed to take an asset and performing whatever sizing options provided and output a new URL, it this case a URL from our Imgix domain.

Looking at the meta values for images, they are all just Imgix URLs as strings.

jamesmacwhite commented 2 years ago

Looking at the Imager X doc the pattern seems to suggest that I should be doing .url after the transform, which I wasn't when assigning the SEO image with seomatic.meta.setAttributes , it was already returning a URL value anyway, but I'll be explicit and see if that helps with this error.

khalwat commented 2 years ago

Looking at the Imager X doc the pattern seems to suggest that I should be doing .url after the transform, which I wasn't when assigning the SEO image with seomatic.meta.setAttributes , it was already returning a URL value anyway, but I'll be explicit and see if that helps with this error.

So probably what was happening was it was returning an craft\awss3\S3Client object, or some other kind of object, which the Yii2 caching method dutifully tried to serialize and cache for you.

The reason it "worked" in the templates is likely because there's a __tostring method in the object that by default returns the URL. ref: https://www.php.net/manual/en/language.oop5.magic.php#object.tostring

I'm suspecting correcting your templating code by adding .url will cause it to now serialize the actual string rather than the object it was returning, and will fix the issue.

jamesmacwhite commented 2 years ago

Thanks Andrew, based on Sentry reporting, this does appear to have resolved it as I haven't seen the error return since deploying this fix.

Thank you for the further clarification, that certainly makes sense. I'll note to make sure that I'm calling .url on all transformed images.