-
-
Notifications
You must be signed in to change notification settings - Fork 14
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
What's next: improve warmup before deploying #35
Comments
Thank you for opening this issue. I will do some work on the Runtime thing. Im not 100% how it will work myself. =) I'll take time the next few days to properly write an answer to this issue. |
Short update: Here is some docs and example application for the Runtime: |
Hi, runtime discussions apart, I think the main topic to solve right now is the cache dir/build dir. If I understand correctly, what this package could do is automatically redirect the cache to (assuming this bridge targets Symfony 5.2 and up) |
@Nyholm awesome ! I'll give this a try soon 🙂 @mnapoli it is already redirecting the cache to For me there are some downsides (more details cliking on the arrows): 1. we will NOT be able to deploy an app without a prewarmed cacheThe main downside to me is that we loose the ability to NOT deploy the warmed cache whereas the current approach is allowing that because everything (cache + build) will be in If we use the buildDir with the default Symfony path (which would be This is intended but might be disturbing for new comers and it kinda "breaks" the actual behavior. 2. it's apparently not working when deploying from MacOS@chekalsky reported on brefphp/bref#874 that the currently documented approach doesn't work when deploying from MacOS because I suggested a workaround on Slack, not sure what the best solution is. 3. some files are generated on warmup in the cacheDir so they would be ignoredI noticed the cache warmup does generate router files ( I'm not sure how much this could have an impact on big apps with lots of routes as this apprently a slow process. Does that make sense ? So there are downsides but 1 should be okay with proper docs; 2 could work with the proposed workaround (not tested myself); 3 not sure if it has a significant impact. |
Oh I think I'm realizing something: can the cache (not build dir) be prewarmed? For the sake of the discussion, let's use these fixed terms:
I'm sorry but this whole topic is very complex. Every time I get back on that topic, every other week, I have already forgotten everything we already discussed because over many months we've discussed so many possibilities + Symfony is very complex + I have many other topics to follow. So, sorry about that, I'm really struggling 😅 Because of that, I will try to make the discussion simpler. Goals:
In a Symfony 5.2 project I should:
and Symfony should run on Lambda. Requirements on deployment should be minimal. For example, ideally we shouldn't require using Docker to deploy. We need to start with something that works, even if it's not perfect, and iterate. Just a few specific notes:
I was under the understanding that build dir was completely meant to be read-only at runtime. That was the reason that directory was created. If it's not the case, then we have a problem indeed. But I'm pretty sure it's OK that it's read-only. Regarding any other cache (i.e. not build dir), it should go into a writable directory and it's fine IMO if it's not pre-generated in v1. We can always improve that a bit later. Let me know if that makes sense and if I missed something |
I believe that this is already true.
You are correct. |
👍 but the current version of the bridge doesn't take the build dir into account. In that section of the README we encourage to warm the cache, and at the time it was indeed important because the build dir wasn't already pregenerated. Now we can probably:
This section is also maybe obsolete now? |
But those are good points, let's try to clarify even further the goals then:
Then a secondary goal:
(that secondary goal could include copying a prewarmed cache, etc. and can involve extra steps) WDYT? |
Well
IMHO the first part is not but the second would be obsolete if using the buildDir and not doing the copy/symlink thing. |
OK the 1st section ( OK and for the second I'm not familiar enough with Symfony caches to know if it still makes sense. If it doesn't, it could be removed indeed. |
Yes, using buildDir would make this required
The first part just says you should not write to filesystem for application cache and rely instead on a service (Redis, RDS, etc) The second part is specific to the current implementation (see https://github.com/brefphp/symfony-bridge/blob/master/src/BrefKernel.php#L59) |
OK so to be sure I understand, now all cache (not build) will be moved to Which is a win I think |
Caching with the filesystem adapter ( But this is already the case for the current implementation. And IMHO, in both case it's a bad idea because it would not be shared across different Lambdas. But in some way, it might be great because users relying on the filesystem adpater would have a working app out of the box. |
Yeah I don't think it's that bad of an idea in itself. There are plenty of use cases where using I'd prefer it to work out of the box, and in the README we can mention that the filesystem isn't shared between lambdas. Then users make their decision. But that's great, I think we have a clear vision now? |
FYI guys I've experimented today a lot of errors like this one after migrating from symfony-bridge to simplified
My app is a Symfony 5.2.5 app with ApiPlatform 1.6. cache pools are plenty of Api platform generated files (about 3Mib). So as cache pools were the only thing I loose in the migration I had the intuition that it would be cool to mirror them into writeable cache as it was previously done in symfony-bridge. And... yes looks like it was that. It take about 60ms on λ cold start to mirror the 3Mib cache pools with Symfony Filesystem component. Here's what I've changed to my Symfony Kernel: class Kernel {
/**
* {@inheritdoc}
*/
public function boot(): void
{
// When on the lambda, copy the cache dir over to /tmp where it is writable
if (isset($_SERVER['LAMBDA_TASK_ROOT']) && !is_dir($this->getCacheDir())) {
$this->warmupCachePools(parent::getCacheDir(), $this->getCacheDir());
}
parent::boot();
}
private function warmupCachePools(string $readOnlyDir, string $writeDir): void
{
if (!is_dir($readOnlyDir)) {
return;
}
$startTime = microtime(true);
$filesystem = new Filesystem();
$filesystem->mkdir($writeDir.'/pools');
$filesystem->mirror($readOnlyDir.'/pools', $writeDir.'/pools');
$message = sprintf(
'Symfony cache pools directory prepared in %s ms.',
number_format((microtime(true) - $startTime) * 1000, 2)
);
file_put_contents('php://stderr', date('[c] ').$message.PHP_EOL, FILE_APPEND);
}
} |
EDIT: it as improved the situation but not so much in fact, still experience some errors. I rollback from now to be sure that's related to described changes. |
EDIT2: I have no 500 since rollback. So I guess FPM sigsegv were related to some write attemps in read only Symfony buildDir 😭 |
That's something I noticed too but not that much of an impact on apps I tested but some others might be relying more on cache than others (like API Platform in your case). Your solution make senses and would be a nice addition. The idea could be to do the following in the BrefKernel :
This solves a number of "problems" :
This seems like the best of both worlds to me. What do you think ? PS : I'm currently working on this bridge along with a sample application based on the Symfony demo to have a better view on what is required (compared with a skeleton app) while being open-source. |
@t-richard you can't keep cache dir in var/cache as Symfony apps should be able to write into cacheDir at runtime, even if you've warmed it up. To me, no other choice than copying var/cache to /tmp/cache when running λ with the kind of trick in the Kernel boot I've mentioned above. But it seems it's not enough in some cases and still very risky, there are probably some write attempts in buildDir. |
@shouze well this is the approach taken by this bridge at the moment. The var/cache directory is deployed warm then its content is copied/symlinked to /tmp/cache at runtime during the kernel boot. So we could just take the extra step of separating buildDir and cacheDir to distinct directories to avoid unnecessary processing of builded files. To test this approach, just install this bridge and extend from the BrefKernel then add a If I'm not overseeing something, this should work in all cases. If you still get arrors then it would mean some libraries are trying to write to the buildDir which is not allowed and should not happen. |
@t-richard yes we're agree that buildDir should not be written but I suspect this is the case regarding my issue. Will check that on friday but I want to reproduce localy because it's too much energy and not so easy to debug once deployed to lambda. I'll reproduce same conditions (read only /var/task) inside a docker container and them will be able to find out why I'm having those fpm SIGSEGV happening. |
@t-richard +1 with your suggestion. I'm not sure I understand how it solves the MacOS problem and the @shouze I have no idea how to help you further, except maybe to try to use the signal technique to debug your timeouts? In #895 we can timeout before Lambda times out with a clear exception and stack trace: I would copy that bit of code in your project to find out what is causing the time out? (soon this feature should be built-in bref so that will be simpler to use) |
@shouze not sure its linked to your issue but while trying this approach myself I stumbled upon a similar issue and fixed it by adding
This would solve MacOS issues because we would rely on the current copy/symlink behaviour which implies that the container contains the default Symfony will only know about And to generate the correct container we had to use the variable Those two points were clearly not nice from a dev experience point of view. I've just opened #36 so that you can get an idea of what it really means and I'll continue to work from that. |
One thing that could be interesting to do it's to create a Symfony Flex recipe that prepare the application to be deploy. Here is a list of things that could be done by the recipe
wdyt ? |
Personally I use terraform so I will dislike 👎 this serverless.yml file.
Yes why not
|
Already thought about adding a Flex recipe in the original issue
But I'm not sure a recipe would be accepted as they are meant to configure Symfony bundles and we are not one ATM.
Also I don't think modifying the Kernel would be possible. Because we could use But I would be 👍 to generate Serverless Framework related files and config. @shouze I totally understand that you would not be happy with this but apart from personal preferences :
For these reasons it seems legitimate to generate these files for Serverless Framework and if users want to use an alternative approach (Terraform, SAM, plain Cloudformation, etc) then they can just remove those files (and if they don't, it will not hurt anyway). TL;DR; 👍 but not sure it would be accepted as it's not a bundle. |
I'm 👍 for the 3 changes suggested. Bref targets serverless.yml, I think this is an acceptable compromise as the recipe's changes would be easy to revert. This is also how the Laravel bridge works.
Good point, noted. I personally won't have time to implement this, but if someone wants to try feel free. |
I can give a try for the recipe but I will open an issue first on the recipes-contrib repository mainly to ask if we are allowed to create it as the library is not a bundle. |
Hi everyone ! I've been thinking over and over again about Symfony cache on lambda and how to make it work with the ephemeral filesystems. This seems like an deep issue with Symfony and I don't feel like it is a simple fix in Symfony. I've tried workarounds in #36 but to no avail and I have no idea how to proceed. BUT, yesterday I read that Lambda was compatible with EFS (simply put, it's shared filesystem) and was wondering if this is worth considering as an acceptable solution for the cache. Using EFS would mean being able to write to a single location on the first Lambda invocation (not of a single Lambda instance like the cold start, but the first invocation of a newly created Lambda) then reading from it on next requests (like it would on a classic hosting). I'm not sure if that's THE solution but it could be a (temporary ?) solution to accomodate the limitations that could be documented/added to the recipe. Hope I was clear enough, let me know what you think. NB : I haven't tested it yet and I'm unsure about potential limitations with this (added latency, costs, real performance benefits, etc) |
Hey @t-richard! I did test EFS, and honestly I'm not sure it would work here:
|
Yeah, I tested this out but you basically got it all. A VPC is a no go for simple apps (not needing a Database or private services that need to be in a VPC) and imply a lot of complexity. And NAT Gateways are super expensiv for small apps. It's slower on the first request which has to write the cache to the EFS (~8s on a symfony/skeleton app) but other requests seemed to be efficient enough because they only read from it and a cache folder is relatively small compared to a vendor directory. It would certainly imply more logic to clean the EFS on each deploy so that the new cache is generated. Costs should be OK because you pay mostly for storage and network usage but again, the cache being relatively slow means it could be cheap enough. Also the config needs to be repeated on each Lambda function so that could be confusing for new comers. In the end it's a lot of work, added complexity but it does bring cold starts to basically only Lambda init duration + function execution time which is super cool. Another dead end I suppose 😅 PS : if you want to see what this could look like in serverless.yml, here is a simple skeleton project deploying 2 functions whith one using this bridge and the other using EFS and the default symfony Kernel (just modified to read cache from the EFS). |
Hey everyone, posting some updates/summary of discussions:
Goals:
Conclusion: we could probably document 2 options:
Additionally, we can probably remove these lines now as most of the "read-only" cache is now in the build dir: symfony-bridge/src/BrefKernel.php Lines 104 to 109 in 4485405
|
There were recent discussions with @mnapoli regarding this bridge.
The idea was to start by documenting some missing parts to run Symfony apps with Bref then we would have "a clear path" to improve this bridge.
I was willing to contribute to this bridge (and still am!) but I have no idea how this could be achieved to provide a smooth experience. I'll try to share my thoughts here.
The current approach is to deploy a warmed
var/cache
directory then copy/symlink directories to/tmp/cache
It has the advantage of working regarding of the content of
var/cache
because if the cache is empty/incomplete, files will be generated by Symfony at runtime in a writable directory.On the other hand, the approach that was recently merged in the docs is nice because it is based on a feature offered by Symfony (it's less "hacky") and is faster because it doesn't imply copying. But it means the cache MUST be deployed along the source code and I found some examples of files generated on
cache:warmup
that are generated incacheDir
and notbuildDir
(these files would be copied using the current bridge approach but ignored using the documented approach).I also recently saw the Runtime component added to Symfony 5.3 which @Nyholm said on the Symfony Slack could be used to provide a Bref Runtime but I'm not sure how this would work.
I'm also wondering how/if this repository could benefit from Flex or being a bundle to automate even more things than cache.
Let me know what are your thoughts/ideas about this
The text was updated successfully, but these errors were encountered: