Skip to content
This repository was archived by the owner on Dec 14, 2018. It is now read-only.

Frequent IOExceptions using MVC with Core CLR on Linux #3066

Closed
dougbu opened this issue Aug 30, 2015 · 5 comments
Closed

Frequent IOExceptions using MVC with Core CLR on Linux #3066

dougbu opened this issue Aug 30, 2015 · 5 comments
Assignees
Milestone

Comments

@dougbu
Copy link
Contributor

dougbu commented Aug 30, 2015

Functional tests on Linux often fail due to IOExceptions. I currently see 97 test failures in Test/Microsoft.AspNet.Mvc.FunctionalTests and 78 IOExceptions in the output.

The root cause may be an external issue but should start with confirming we're using PhysicalFilesWatchers efficiently (given the number of files in the repo). Or perhaps this is a known system issue we should work around when testing on Linux (similar to setting MONO_THREADS_PER_CPU).

Example stack traces (think this covers all 78 IOExceptions):

      System.IO.IOException : No space left on device
      Stack Trace:
           at Interop.CheckIo(Int64 result, String path, Boolean isDirectory, Func`2 errorRewriter)
           at System.IO.FileSystemWatcher.RunningInstance.SysCall[TArg1,TArg2](Func`4 sysCall, TArg1 arg1, TArg2 arg2)
           at System.IO.FileSystemWatcher.RunningInstance.AddDirectoryWatchUnlocked(WatchedDirectory parent, String directoryName)
           at System.IO.FileSystemWatcher.RunningInstance.AddDirectoryWatchUnlocked(WatchedDirectory parent, String directoryName)
...
           at System.IO.FileSystemWatcher.RunningInstance..ctor(FileSystemWatcher watcher, SafeFileHandle inotifyHandle, String directoryPath, Boolean includeSubdirectories, NotifyEvents notifyFilters, CancellationToken cancellationToken)
           at System.IO.FileSystemWatcher.StartRaisingEvents()
           at Microsoft.AspNet.FileProviders.PhysicalFilesWatcher.CreateFileChangeTrigger(String filter)
           at Microsoft.AspNet.Mvc.Razor.Compilation.CompilerCache.GetOrAdd(String relativePath, Func`2 compile)
           at Microsoft.AspNet.Mvc.Razor.VirtualPathRazorPageFactory.CreateInstance(String relativePath)
      System.IO.IOException : No space left on device
      Stack Trace:
           at Interop.CheckIo(Int64 result, String path, Boolean isDirectory, Func`2 errorRewriter)
           at System.IO.FileSystemWatcher.RunningInstance.SysCall[TArg1,TArg2](Func`4 sysCall, TArg1 arg1, TArg2 arg2)
           at System.IO.FileSystemWatcher.RunningInstance.AddDirectoryWatchUnlocked(WatchedDirectory parent, String directoryName)
           at System.IO.FileSystemWatcher.RunningInstance.AddDirectoryWatchUnlocked(WatchedDirectory parent, String directoryName)
...
           at System.IO.FileSystemWatcher.RunningInstance..ctor(FileSystemWatcher watcher, SafeFileHandle inotifyHandle, String directoryPath, Boolean includeSubdirectories, NotifyEvents notifyFilters, CancellationToken cancellationToken)
           at System.IO.FileSystemWatcher.StartRaisingEvents()
           at Microsoft.AspNet.FileProviders.PhysicalFilesWatcher.CreateFileChangeTrigger(String filter)
           at Microsoft.AspNet.Mvc.Razor.Directives.DefaultChunkTreeCache.GetOrAdd(String pagePath, Func`2 getChunkTree)
           at Microsoft.AspNet.Mvc.Razor.Directives.ChunkInheritanceUtility.GetInheritedChunkTrees(String pagePath)
      System.IO.IOException : Too many open files
      Stack Trace:
           at Interop.CheckIo(Int64 result, String path, Boolean isDirectory, Func`2 errorRewriter)
           at System.IO.FileSystemWatcher.StartRaisingEvents()
           at Microsoft.AspNet.FileProviders.PhysicalFilesWatcher.CreateFileChangeTrigger(String filter)
           at Microsoft.AspNet.Mvc.Razor.Compilation.CompilerCache.GetOrAdd(String relativePath, Func`2 compile)
           at Microsoft.AspNet.Mvc.Razor.VirtualPathRazorPageFactory.CreateInstance(String relativePath)
      System.IO.IOException : Too many open files
      Stack Trace:
           at Interop.CheckIo(Int64 result, String path, Boolean isDirectory, Func`2 errorRewriter)
           at System.IO.FileSystemWatcher.StartRaisingEvents()
           at Microsoft.AspNet.FileProviders.PhysicalFilesWatcher.CreateFileChangeTrigger(String filter)
           at Microsoft.AspNet.Mvc.Razor.Directives.DefaultChunkTreeCache.GetOrAdd(String pagePath, Func`2 getChunkTree)
           at Microsoft.AspNet.Mvc.Razor.Directives.ChunkInheritanceUtility.GetInheritedChunkTrees(String pagePath)

FYI I reconfigured the system to bump up the ulimit -n number from 1024 to 10240 (see this answer). This had minimal impact.

@dougbu
Copy link
Contributor Author

dougbu commented Sep 1, 2015

Likely we could work around this issue by caching HostingEnvironment or PhysicalFileProvider instances in the functional test's TestHelper class. HostingEnvironment.WebRootFileProvider is the PhysicalFileProvider instance experiencing the above issues.

These problems should not occur in a real application because the HostingEnvironment is a singleton and maps 1:1 down to a FileSystemWatcher instance. We don't create FileSystemWatcher instances per trigger. (We also reuse triggers per pattern and therefore, as MVC uses the WebRootFileProvider, per *.cshtml file.)


Separately I'm trying to see what resource we're actually exhausting...

@rynowak
Copy link
Member

rynowak commented Sep 1, 2015

We also tend to create a new 'server' per testcase. We could probably speed up these tests a few seconds by not doing that.

dougbu added a commit that referenced this issue Sep 2, 2015
- #3066
- cache `HostingEnvironment` instances per application root
  - avoid creating a system-limited `FileSystemWatcher` per test
@dougbu
Copy link
Contributor Author

dougbu commented Sep 2, 2015

If users do functional testing of MVC behaviours, they may benefit from the more-specific error messages @stephentoub added in dotnet/corefx#3060.

Testing a PR to avoid inotify limits in our functional tests...

dougbu added a commit that referenced this issue Sep 3, 2015
…al tests

- #3066
- cache `TestServer` instances per site / configuration delegate
  - works around `inotify` limits with Core CLR on Linux
  - `PhysicalFileProvider` instances created in `IHostingEnvironment.Initialize()`,
    `RazorViewEngineOptionsSetup.ConfigureRazor()`, `RazorPreCompileModule.BeforeCompile()`, and a
    `MvcRazorHost` constructor that is called only from tooling
  - `IHostingEnvironment.Initialize()` called in `TestHelper.AddTestServices()` and `WebHostBuilder.Build()`
    i.e. the `TestServer` constructor
dougbu added a commit that referenced this issue Sep 4, 2015
…al tests

- #3066
- cache `TestServer` instances per site / configuration delegate
  - works around `inotify` limits with Core CLR on Linux
  - `PhysicalFileProvider` instances are created in `IHostingEnvironment.Initialize()`,
    `RazorViewEngineOptionsSetup.ConfigureRazor()`, `RazorPreCompileModule.BeforeCompile()`, and a
    `MvcRazorHost` constructor that is called only from tooling
  - `IHostingEnvironment.Initialize()` called in `TestHelper.AddTestServices()` and `WebHostBuilder.Build()`
    i.e. the `TestServer` constructor
- now create ~70 `TestServers` for the 51 sites we test
  - side benefit: xUnit reports functional tests execute for ~14.5s; was ~31.4s before this change
dougbu added a commit that referenced this issue Sep 4, 2015
…al tests

- #3066
- cache `TestServer` instances per site / configuration delegate
  - works around `inotify` limits with Core CLR on Linux
  - `PhysicalFileProvider` instances are created in `IHostingEnvironment.Initialize()`,
    `RazorViewEngineOptionsSetup.ConfigureRazor()`, `RazorPreCompileModule.BeforeCompile()`, and a
    `MvcRazorHost` constructor that is called only from tooling
  - `IHostingEnvironment.Initialize()` called in `TestHelper.AddTestServices()` and `WebHostBuilder.Build()`
    i.e. the `TestServer` constructor
- now create ~70 `TestServers` for the 51 sites we test
  - side benefit: xUnit reports functional tests execute for ~14.5s; was ~31.4s before this change
dougbu added a commit that referenced this issue Sep 5, 2015
…al tests

- #3066
- cache `TestServer` instances per site / configuration delegate
  - add new `TestHelper.GetOrCreateServer()` methods that use new cache
  - switch tests from `CreateServer()` to `GetOrCreateServer()` except in a few special cases
    - special cases likely test issues though `WebApiCompatShimBasicTest` ones may indicate an underlying bug
  - work around `inotify` limits with Core CLR on Linux
- FYI `PhysicalFileProvider` instances are created in `IHostingEnvironment.Initialize()`,
    `RazorViewEngineOptionsSetup.ConfigureRazor()`, `RazorPreCompileModule.BeforeCompile()`, and a
    `MvcRazorHost` constructor that is called only from tooling
  - `IHostingEnvironment.Initialize()` called in `TestHelper.AddTestServices()` and `WebHostBuilder.Build()`
    i.e. the `TestServer` constructor
- now create ~75 `TestServers` for the 51 sites we test
  - side benefit: xUnit reports functional tests execute for ~14.5s; was ~31.4s before this change
dougbu added a commit that referenced this issue Sep 5, 2015
…al tests

- #3066
- cache `TestServer` instances per site / configuration delegate
  - add new `TestHelper.GetOrCreateServer()` methods that use new cache
  - switch tests from `CreateServer()` to `GetOrCreateServer()` except in a few special cases
    - special cases likely test issues though `WebApiCompatShimBasicTest` ones may indicate an underlying bug
  - work around `inotify` limits with Core CLR on Linux
- FYI `PhysicalFileProvider` instances are created in `IHostingEnvironment.Initialize()`,
    `RazorViewEngineOptionsSetup.ConfigureRazor()`, `RazorPreCompileModule.BeforeCompile()`, and a
    `MvcRazorHost` constructor that is called only from tooling
  - `IHostingEnvironment.Initialize()` called in `TestHelper.AddTestServices()` and `WebHostBuilder.Build()`
    i.e. the `TestServer` constructor
- now create ~75 `TestServers` for the 51 sites we test
  - side benefit: xUnit reports functional tests execute for ~14.5s; was ~31.4s before this change
dougbu added a commit that referenced this issue Sep 7, 2015
…al tests

- #3066
- cache `TestServer` instances per site / configuration delegate
  - add new `TestHelper.GetOrCreateServer()` methods that use new cache
  - switch tests from `CreateServer()` to `GetOrCreateServer()` except in a few special cases
    - special cases likely test issues though `WebApiCompatShimBasicTest` ones may indicate an underlying bug
  - work around `inotify` limits with Core CLR on Linux
- FYI `PhysicalFileProvider` instances are created in `IHostingEnvironment.Initialize()`,
    `RazorViewEngineOptionsSetup.ConfigureRazor()`, `RazorPreCompileModule.BeforeCompile()`, and a
    `MvcRazorHost` constructor that is called only from tooling
  - `IHostingEnvironment.Initialize()` called in `TestHelper.AddTestServices()` and `WebHostBuilder.Build()`
    i.e. the `TestServer` constructor
- now create ~75 `TestServers` for the 51 sites we test
  - side benefit: xUnit reports functional tests execute for ~14.5s; was ~31.4s before this change
dougbu added a commit that referenced this issue Sep 7, 2015
…al tests

- #3066
- cache `TestServer` instances per site / configuration delegate
  - add new `TestHelper.GetOrCreateServer()` methods that use new cache
  - switch tests from `CreateServer()` to `GetOrCreateServer()` except in a few special cases
    - special cases likely test issues though `WebApiCompatShimBasicTest` ones may indicate an underlying bug
  - work around `inotify` limits with Core CLR on Linux
- FYI `PhysicalFileProvider` instances are created in `IHostingEnvironment.Initialize()`,
    `RazorViewEngineOptionsSetup.ConfigureRazor()`, `RazorPreCompileModule.BeforeCompile()`, and a
    `MvcRazorHost` constructor that is called only from tooling
  - `IHostingEnvironment.Initialize()` called in `TestHelper.AddTestServices()` and `WebHostBuilder.Build()`
    i.e. the `TestServer` constructor
- now create ~75 `TestServers` for the 51 sites we test
  - side benefit: xUnit reports functional tests execute for ~14.5s; was ~31.4s before this change
@dougbu
Copy link
Contributor Author

dougbu commented Sep 8, 2015

Details on the inotify limits

Creating lots of FileSystemWatcher instances hits limits stored in /proc files with the following defaults:

> cat /proc/sys/fs/inotify/max_user_instances
128
> cat /proc/sys/fs/inotify/max_user_watches
8192

These can be increased temporarily e.g.

sudo sh -c "echo 2048 > /proc/sys/fs/inotify/max_user_instances"

or permanently e.g. adding the following to /etc/security/limits.conf.

fs.inotify.max_user_instances=2048
fs.inotify.max_user_watches=131072

dougbu added a commit that referenced this issue Sep 9, 2015
…al tests

- #3066
- cache `TestServer` instances per site / configuration delegate
  - work around `inotify` limits with Core CLR on Linux
  - add new `TestHelper.GetOrCreateServer()` methods that use new cache
  - switch tests from `CreateServer()` to `GetOrCreateServer()` except in a few special cases
    - e.g. where context services are referenced in tests
- FYI `PhysicalFileProvider` instances are created in `IHostingEnvironment.Initialize()`,
    `RazorViewEngineOptionsSetup.ConfigureRazor()`, `RazorPreCompileModule.BeforeCompile()`, and a
    `MvcRazorHost` constructor that is called only from tooling
  - `IHostingEnvironment.Initialize()` called in `TestHelper.AddTestServices()` and `WebHostBuilder.Build()`
    i.e. the `TestServer` constructor
- now create ~70 `TestServers` for the 51 sites we test
  - side benefit: xUnit reports functional tests execute for ~14.5s; was ~31.4s before this change
dougbu added a commit that referenced this issue Sep 15, 2015
- #3066
  - reduce `TestServer` -> `PhysicalFileProvider` -> `FileSystemWatcher` count enough to run with Core CLR on Linux
- remove use of `HttpClient.DefaultRequestHeaders`; any client change affects other tests
- remove use of `RequestBuilder` class; creates a per-test `HttpClient` and requires the `TestServer`
- updated a few expectations because `CommonTestEncoder` does JavaScript a bit differently
  - "JavaScriptEncode[[...]]" -> "JavaScriptStringEncode[[...]]"
- side benefit: xUnit reports functional tests execute for only ~12.4s; was >30s before this change

Infrastructure: Enhance `MvcTestFixture`
- handle `ConfigureServices()` methods that are not `void`
- handle `Configure(IApplicationBuilder, ILoggerFactory)`
- ensure server is initialized with consistent `CurrentCulture` and `CurrentUICulture`
- add `FilteredDefaultAssemblyProviderFixture<TStartup>` and `MvcEncodedTestFixture<TStartup>`
  - add `MvcTextFixture.AddAdditionalServices()` extension point supporting these

- do not expose the `TestServer`; an anti-pattern for tests to manipulate the server
- update class names to match containing files
- use existing `TestApplicationEnvironment`
  - apply some `MvcTestFixture` improvements to the shared `TestApplicationEnvironment` class
- remove unused methods from `TestHelper`

nits:
- touched-up some leftover `_app` &c declarations to be more explicit and minimize `using`s
- moved statements into correct sections of methods in `RoutingTests`
- removed `TestLoggerFactory` and related classes from `TagHelperSampleTest`
@dougbu
Copy link
Contributor Author

dougbu commented Sep 15, 2015

d03a851

@dougbu dougbu closed this as completed Sep 15, 2015
@danroth27 danroth27 added task and removed bug labels Sep 15, 2015
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Projects
None yet
Development

No branches or pull requests

3 participants