silverstripe / silverstripe-framework

Silverstripe Framework, the MVC framework that powers Silverstripe CMS
https://www.silverstripe.org
BSD 3-Clause "New" or "Revised" License
723 stars 821 forks source link

Race condition detecting ENV variables #7478

Closed zanderwar closed 6 years ago

zanderwar commented 6 years ago

PRs:

Replicate:

  1. Install SS4 (framework only or full doesn't matter happens on both)
  2. Ensure you have your .env configured correctly and mod_env enabled
  3. Navigate to the homepage
  4. (Optional) Remove install.php
  5. Spam refresh as fast as possible letting the page load each time.

Inevitable Error:

If you've removed install.php

SilverStripe Framework requires a $databaseConfig defined.

If you havn't removed it

Redirected to install.php

Affecting: Everyone

image

zanderwar commented 6 years ago

@colintucker also replicated this issue by spamming clicks in the site tree within the CMS to inevitably receive "Server Error" in the X-Status

colintucker commented 6 years ago

Can confirm I have encountered this issue in dev. As @zanderwar mentioned I can generate server errors by spamming site tree clicks as well.

tractorcow commented 6 years ago

See https://gist.github.com/progmars/1e545d96dd48676a2aa7

Issue is due to putenv acting async.

tractorcow commented 6 years ago

A better example https://github.com/laravel/framework/issues/7354

tractorcow commented 6 years ago

It looks as a result of this issue is that we'll need to replace getenv with another thread-safe equivalent. Will be another beta change at least. =(

tractorcow commented 6 years ago

phpdotenv is made for development environments, and generally should not be used in production. In production, the actual environment variables should be set so that there is no overhead of loading the .env file on each request. This can be achieved via an automated deployment process with tools like Vagrant, chef, or Puppet, or can be set manually with cloud hosts like Pagodabox and Heroku.

https://github.com/vlucas/phpdotenv#usage-notes

Should have paid more attention!

sminnee commented 6 years ago

Can we confirm that no other popular framework uses getenv? If that’s not the case then I’d dispute the diagnosis.

zanderwar commented 6 years ago

@sminnee I believe laravel canned it for their own methodology. I researched everywhere regarding this issue and recall that being the case somewhere. However there is hard evidence everywhere (and in #ss4 of slack this morning) proving that this is an issue for all operating systems (except OSX so far) - especially Windows.

If this is something that gets swept under the rug because other frameworks couldn't be bothered because other popular frameworks couldn't be bothered than I personally (among others) am stuck on lesser versions than 4.0 <3

tractorcow commented 6 years ago

It's not simply the usage of getenv(); If you set your environment vars on your platform (e.g. ssp), it's safe, but loading these in the same request obviously has a race condition. However, we give the impression that .env files are safe to use in production when they obviously aren't.

sminnee commented 6 years ago

Should we introduce an env() getter that works the same way as Laravel’s, and only gets defined if it doesn’t already exist?

sminnee commented 6 years ago

Or do we recommend that people avoid using .env in prod?

zanderwar commented 6 years ago

~SetEnv has also proven unreliable. At least from .htaccess~

I've settled for DB::setConfig() until this tides over into being reliable, in my scenario I don't need any additional env vars,

~Ergo, this issue remains even in prod & without .env~

zanderwar commented 6 years ago

fml

Was a little quick on the refresh in this GIF but note that moment where the array is empty before sending me to install.php

Note: This GIF is from using .env

sminnee commented 6 years ago

If SetEnv is unreliable something seriously FUBAR is going on. Are you certain about that? Perhaps do a reinstall of apache?

zanderwar commented 6 years ago

Sorry, can confirm it's working in .htaccess

sminnee commented 6 years ago

If we use symfony/dotenv instead of vlucas/phpdotenv do we get any better results?

sminnee commented 6 years ago

It looks like Symfony has a wrapper around getenv for the same reason: https://github.com/nicolas-grekas/symfony/blob/f76e420e0959b52e73f1486a7b99771d6553ff01/src/Symfony/Component/DependencyInjection/Container.php#L436

Lesson: getenv() on its own is a bad API :-(

dhensby commented 6 years ago

Hmm - looks like we'll need to define our own getenv to mitigate this, then?

we knew that dotenv library wasn't meant for production use but at the same time we do have to accept that some people will use it in prod because of limitations outside of their control. We should aim to get it working as much as we can IMO whilst still saying it shouldn't be used on prod.

NightJar commented 6 years ago

Incoming PR.

Important questions first though, I have to do the hardest thing in programming: Will SilverStripe\Core\Environment::shimFromFile and SilverStripe\Core\Environment::env do?

I thought ::get was a bit... on the nose, leading to Confusion++ since there is alerady SilverStripe\Core\Environment::getVariables which... doesn't get environment variables :( (certainly caught me out when investigating :/ )

c.f. preliminary ideas at: https://github.com/silverstripe/silverstripe-framework/commit/e614edec61f396c7e8f29bd5ee3340f56cf0b1a6

tractorcow commented 6 years ago

I've done a bit of work after looking at @NightJar 's initial fix. Please check my PR at https://github.com/silverstripe/silverstripe-framework/pull/7484.

NightJar commented 6 years ago

Yeah, looks all good to me :)