diff --git a/core/core.services.yml b/core/core.services.yml
index 43c61040aa39fd76b12897872576cdf25ed2c53a..51ce4c4ca2714837fe82006565d014ed9beb33d6 100644
--- a/core/core.services.yml
+++ b/core/core.services.yml
@@ -284,7 +284,7 @@ services:
       - [setRequest, ['@?request=']]
   router.route_provider:
     class: Drupal\Core\Routing\RouteProvider
-    arguments: ['@database', '@router.builder']
+    arguments: ['@database', '@router.builder', '@state']
     tags:
       - { name: event_subscriber }
   router.route_preloader:
@@ -334,7 +334,7 @@ services:
     arguments: ['@database']
   router.dumper:
     class: Drupal\Core\Routing\MatcherDumper
-    arguments: ['@database']
+    arguments: ['@database', '@state']
   router.builder:
     class: Drupal\Core\Routing\RouteBuilder
     arguments: ['@router.dumper', '@lock', '@event_dispatcher', '@module_handler', '@controller_resolver', '@state']
diff --git a/core/lib/Drupal/Core/Routing/MatcherDumper.php b/core/lib/Drupal/Core/Routing/MatcherDumper.php
index 889b6918e617029ce779f54da407cbded847779b..aeed49ca6c6c96c891e8ceada96812239795a2ef 100644
--- a/core/lib/Drupal/Core/Routing/MatcherDumper.php
+++ b/core/lib/Drupal/Core/Routing/MatcherDumper.php
@@ -7,6 +7,7 @@
 
 namespace Drupal\Core\Routing;
 
+use Drupal\Core\KeyValueStore\StateInterface;
 use Symfony\Component\Routing\RouteCollection;
 
 use Drupal\Core\Database\Connection;
@@ -30,6 +31,13 @@ class MatcherDumper implements MatcherDumperInterface {
    */
   protected $routes;
 
+  /**
+   * The state.
+   *
+   * @var \Drupal\Core\KeyValueStore\StateInterface
+   */
+  protected $state;
+
   /**
    * The name of the SQL table to which to dump the routes.
    *
@@ -43,11 +51,14 @@ class MatcherDumper implements MatcherDumperInterface {
    * @param \Drupal\Core\Database\Connection $connection
    *   The database connection which will be used to store the route
    *   information.
+   * @param \Drupal\Core\KeyValueStore\StateInterface $state
+   *   The state.
    * @param string $table
    *   (optional) The table to store the route info in. Defaults to 'router'.
    */
-  public function __construct(Connection $connection, $table = 'router') {
+  public function __construct(Connection $connection, StateInterface $state, $table = 'router') {
     $this->connection = $connection;
+    $this->state = $state;
 
     $this->tableName = $table;
   }
@@ -79,6 +90,7 @@ public function dump(array $options = array()) {
     $options += array(
       'provider' => '',
     );
+
     // If there are no new routes, just delete any previously existing of this
     // provider.
     if (empty($this->routes) || !count($this->routes)) {
@@ -88,6 +100,8 @@ public function dump(array $options = array()) {
     }
     // Convert all of the routes into database records.
     else {
+      // Accumulate the menu masks on top of any we found before.
+      $masks = array_flip($this->state->get('routing.menu_masks.' . $this->tableName, array()));
       $insert = $this->connection->insert($this->tableName)->fields(array(
         'name',
         'provider',
@@ -101,6 +115,11 @@ public function dump(array $options = array()) {
       foreach ($this->routes as $name => $route) {
         $route->setOption('compiler_class', '\Drupal\Core\Routing\RouteCompiler');
         $compiled = $route->compile();
+        // The fit value is a binary number which has 1 at every fixed path
+        // position and 0 where there is a wildcard. We keep track of all such
+        // patterns that exist so that we can minimize the the number of path
+        // patterns we need to check in the RouteProvider.
+        $masks[$compiled->getFit()] = 1;
         $names[] = $name;
         $values = array(
           'name' => $name,
@@ -136,6 +155,10 @@ public function dump(array $options = array()) {
         watchdog_exception('Routing', $e);
         throw $e;
       }
+      // Sort the masks so they are in order of descending fit.
+      $masks = array_keys($masks);
+      rsort($masks);
+      $this->state->set('routing.menu_masks.' . $this->tableName, $masks);
     }
     // The dumper is reused for multiple providers, so reset the queued routes.
     $this->routes = NULL;
diff --git a/core/lib/Drupal/Core/Routing/RouteCompiler.php b/core/lib/Drupal/Core/Routing/RouteCompiler.php
index 966f9d48d1eaa8a8c62991749549b6c7f5daaa16..6ae58e18f083ccec9323ecfb501126bf71266376 100644
--- a/core/lib/Drupal/Core/Routing/RouteCompiler.php
+++ b/core/lib/Drupal/Core/Routing/RouteCompiler.php
@@ -93,7 +93,10 @@ public static function getFit($path) {
     // We store the highest index of parts here to save some work in the fit
     // calculation loop.
     $slashes = $number_parts - 1;
-
+    // The fit value is a binary number which has 1 at every fixed path
+    // position and 0 where there is a wildcard. We keep track of all such
+    // patterns that exist so that we can minimize the the number of path
+    // patterns we need to check in the RouteProvider.
     $fit = 0;
     foreach ($parts as $k => $part) {
       if (strpos($part, '{') === FALSE) {
diff --git a/core/lib/Drupal/Core/Routing/RouteProvider.php b/core/lib/Drupal/Core/Routing/RouteProvider.php
index 20b0ec138706e110810e52610a113fbb64bcb278..3dfe65e8f273d60372cbdae86d6155fa6f2e0f9d 100644
--- a/core/lib/Drupal/Core/Routing/RouteProvider.php
+++ b/core/lib/Drupal/Core/Routing/RouteProvider.php
@@ -8,6 +8,7 @@
 namespace Drupal\Core\Routing;
 
 use Drupal\Component\Utility\String;
+use Drupal\Core\KeyValueStore\StateInterface;
 use Symfony\Component\EventDispatcher\EventSubscriberInterface;
 use Symfony\Component\HttpFoundation\Request;
 use Symfony\Component\Routing\Exception\RouteNotFoundException;
@@ -42,6 +43,13 @@ class RouteProvider implements RouteProviderInterface, EventSubscriberInterface
    */
   protected $routeBuilder;
 
+  /**
+   * The state.
+   *
+   * @var \Drupal\Core\KeyValueStore\StateInterface
+   */
+  protected $state;
+
   /**
    * A cache of already-loaded routes, keyed by route name.
    *
@@ -56,12 +64,15 @@ class RouteProvider implements RouteProviderInterface, EventSubscriberInterface
    *   A database connection object.
    * @param \Drupal\Core\Routing\RouteBuilderInterface $route_builder
    *   The route builder.
+   * @param \Drupal\Core\KeyValueStore\StateInterface $state
+   *   The state.
    * @param string $table
    *   The table in the database to use for matching.
    */
-  public function __construct(Connection $connection, RouteBuilderInterface $route_builder, $table = 'router') {
+  public function __construct(Connection $connection, RouteBuilderInterface $route_builder, StateInterface $state, $table = 'router') {
     $this->connection = $connection;
     $this->routeBuilder = $route_builder;
+    $this->state = $state;
     $this->tableName = $table;
   }
 
@@ -115,10 +126,6 @@ public function getRouteCollectionForRequest(Request $request) {
       $collection = $this->getRoutesByPath($path);
     }
 
-    if (!$collection->count()) {
-      throw new ResourceNotFoundException(String::format("The route for '@path' could not be found", array('@path' => $path)));
-    }
-
     return $collection;
   }
 
@@ -202,7 +209,24 @@ public function getCandidateOutlines(array $parts) {
 
     // The highest possible mask is a 1 bit for every part of the path. We will
     // check every value down from there to generate a possible outline.
-    $masks = range($end, 0);
+    if ($number_parts == 1) {
+      $masks = array(1);
+    }
+    elseif ($number_parts <= 3) {
+      // Optimization - don't query the state system for short paths. This also
+      // insulates against the state entry for masks going missing for common
+      // user-facing paths since we generate all values without checking state.
+      $masks = range($end, 1);
+    }
+    elseif ($number_parts <= 0) {
+      // No path can match, short-circuit the process.
+      $masks = array();
+    }
+    else {
+      // Get the actual patterns that exist out of state.
+      $masks = (array) $this->state->get('routing.menu_masks.' . $this->tableName, array());
+    }
+
 
     // Only examine patterns that actually exist as router items (the masks).
     foreach ($masks as $i) {
@@ -260,14 +284,18 @@ protected function getRoutesByPath($path) {
       return $value !== NULL && $value !== '';
     }));
 
+    $collection = new RouteCollection();
+
     $ancestors = $this->getCandidateOutlines($parts);
+    if (empty($ancestors)) {
+      return $collection;
+    }
 
     $routes = $this->connection->query("SELECT name, route FROM {" . $this->connection->escapeTable($this->tableName) . "} WHERE pattern_outline IN (:patterns) ORDER BY fit DESC, name ASC", array(
       ':patterns' => $ancestors,
     ))
       ->fetchAllKeyed();
 
-    $collection = new RouteCollection();
     foreach ($routes as $name => $route) {
       $route = unserialize($route);
       if (preg_match($route->compile()->getRegex(), $path, $matches)) {
diff --git a/core/modules/system/lib/Drupal/system/Tests/Routing/MatcherDumperTest.php b/core/modules/system/lib/Drupal/system/Tests/Routing/MatcherDumperTest.php
index 8514b3f5b8ca2accfe79efaac266121566383f28..1b2fd8f1194fb9cd7b3c6033ed61391f5f388823 100644
--- a/core/modules/system/lib/Drupal/system/Tests/Routing/MatcherDumperTest.php
+++ b/core/modules/system/lib/Drupal/system/Tests/Routing/MatcherDumperTest.php
@@ -2,11 +2,13 @@
 
 /**
  * @file
- * Definition of Drupal\system\Tests\Routing\UrlMatcherDumperTest.
+ * Contains \Drupal\system\Tests\Routing\MatcherDumperTest.
  */
 
 namespace Drupal\system\Tests\Routing;
 
+use Drupal\Core\KeyValueStore\KeyValueMemoryFactory;
+use Drupal\Core\KeyValueStore\State;
 use Symfony\Component\Routing\Route;
 use Symfony\Component\Routing\RouteCollection;
 
@@ -27,6 +29,13 @@ class MatcherDumperTest extends UnitTestBase {
    */
   protected $fixtures;
 
+  /**
+   * The state.
+   *
+   * @var \Drupal\Core\KeyValueStore\StateInterface
+   */
+  protected $state;
+
   public static function getInfo() {
     return array(
       'name' => 'Dumper tests',
@@ -39,6 +48,7 @@ function __construct($test_id = NULL) {
     parent::__construct($test_id);
 
     $this->fixtures = new RoutingFixtures();
+    $this->state = new State(new KeyValueMemoryFactory());
   }
 
   function setUp() {
@@ -50,7 +60,7 @@ function setUp() {
    */
   function testCreate() {
     $connection = Database::getConnection();
-    $dumper= new MatcherDumper($connection);
+    $dumper= new MatcherDumper($connection, $this->state);
 
     $class_name = 'Drupal\Core\Routing\MatcherDumper';
     $this->assertTrue($dumper instanceof $class_name, 'Dumper created successfully');
@@ -61,7 +71,7 @@ function testCreate() {
    */
   function testAddRoutes() {
     $connection = Database::getConnection();
-    $dumper= new MatcherDumper($connection);
+    $dumper= new MatcherDumper($connection, $this->state);
 
     $route = new Route('test');
     $collection = new RouteCollection();
@@ -82,7 +92,7 @@ function testAddRoutes() {
    */
   function testAddAdditionalRoutes() {
     $connection = Database::getConnection();
-    $dumper= new MatcherDumper($connection);
+    $dumper= new MatcherDumper($connection, $this->state);
 
     $route = new Route('test');
     $collection = new RouteCollection();
@@ -118,7 +128,7 @@ function testAddAdditionalRoutes() {
    */
   public function testDump() {
     $connection = Database::getConnection();
-    $dumper= new MatcherDumper($connection, 'test_routes');
+    $dumper = new MatcherDumper($connection, $this->state, 'test_routes');
 
     $route = new Route('/test/{my}/path');
     $route->setOption('compiler_class', 'Drupal\Core\Routing\RouteCompiler');
@@ -142,12 +152,41 @@ public function testDump() {
     $this->assertTrue($loaded_route instanceof Route, 'Route object retrieved successfully.');
   }
 
+  /**
+   * Tests the determination of the masks generation.
+   */
+  public function testMenuMasksGeneration() {
+    $connection = Database::getConnection();
+    $dumper = new MatcherDumper($connection, $this->state, 'test_routes');
+
+    $collection = new RouteCollection();
+    $collection->add('test_route_1', new Route('/test-length-3/{my}/path'));
+    $collection->add('test_route_2', new Route('/test-length-3/hello/path'));
+    $collection->add('test_route_3', new Route('/test-length-5/{my}/path/marvin/magrathea'));
+    $collection->add('test_route_4', new Route('/test-length-7/{my}/path/marvin/magrathea/earth/ursa-minor'));
+
+    $dumper->addRoutes($collection);
+
+    $this->fixtures->createTables($connection);
+
+    $dumper->dump(array('provider' => 'test'));
+    // Using binary for readability, we expect a 0 at any wildcard slug. They
+    // should be ordered from longest to shortest.
+    $expected = array(
+      bindec('1011111'),
+      bindec('10111'),
+      bindec('111'),
+      bindec('101'),
+    );
+    $this->assertEqual($this->state->get('routing.menu_masks.test_routes'), $expected);
+  }
+
   /**
    * Tests that changing the provider of a route updates the dumped value.
    */
   public function testDumpRouteProviderRename() {
     $connection = Database::getConnection();
-    $dumper = new MatcherDumper($connection, 'test_routes');
+    $dumper = new MatcherDumper($connection, $this->state, 'test_routes');
     $this->fixtures->createTables($connection);
 
     $route = new Route('/test');
diff --git a/core/modules/system/lib/Drupal/system/Tests/Routing/RouteProviderTest.php b/core/modules/system/lib/Drupal/system/Tests/Routing/RouteProviderTest.php
index b1437978c7ade60374eaaa525e02a06ce1887fc1..05d1131221430302a0454238c0ffd86cc527e3ef 100644
--- a/core/modules/system/lib/Drupal/system/Tests/Routing/RouteProviderTest.php
+++ b/core/modules/system/lib/Drupal/system/Tests/Routing/RouteProviderTest.php
@@ -7,6 +7,8 @@
 
 namespace Drupal\system\Tests\Routing;
 
+use Drupal\Core\KeyValueStore\KeyValueMemoryFactory;
+use Drupal\Core\KeyValueStore\State;
 use Symfony\Component\HttpFoundation\Request;
 use Symfony\Component\Routing\Exception\RouteNotFoundException;
 use Symfony\Component\Routing\Route;
@@ -39,6 +41,13 @@ class RouteProviderTest extends UnitTestBase {
    */
   protected $routeBuilder;
 
+  /**
+   * The state.
+   *
+   * @var \Drupal\Core\KeyValueStore\StateInterface
+   */
+  protected $state;
+
   public static function getInfo() {
     return array(
       'name' => 'Route Provider tests',
@@ -47,11 +56,10 @@ public static function getInfo() {
     );
   }
 
-  function __construct($test_id = NULL) {
-    parent::__construct($test_id);
-
+  public function setUp() {
     $this->fixtures = new RoutingFixtures();
     $this->routeBuilder = new NullRouteBuilder();
+    $this->state = new State(new KeyValueMemoryFactory());
   }
 
   public function tearDown() {
@@ -66,7 +74,7 @@ public function tearDown() {
   public function testCandidateOutlines() {
 
     $connection = Database::getConnection();
-    $provider = new RouteProvider($connection, $this->routeBuilder);
+    $provider = new RouteProvider($connection, $this->routeBuilder, $this->state, 'test_routes');
 
     $parts = array('node', '5', 'edit');
 
@@ -74,7 +82,7 @@ public function testCandidateOutlines() {
 
     $candidates = array_flip($candidates);
 
-    $this->assertTrue(count($candidates) == 8, 'Correct number of candidates found');
+    $this->assertTrue(count($candidates) == 7, 'Correct number of candidates found');
     $this->assertTrue(array_key_exists('/node/5/edit', $candidates), 'First candidate found.');
     $this->assertTrue(array_key_exists('/node/5/%', $candidates), 'Second candidate found.');
     $this->assertTrue(array_key_exists('/node/%/edit', $candidates), 'Third candidate found.');
@@ -82,7 +90,6 @@ public function testCandidateOutlines() {
     $this->assertTrue(array_key_exists('/node/5', $candidates), 'Fifth candidate found.');
     $this->assertTrue(array_key_exists('/node/%', $candidates), 'Sixth candidate found.');
     $this->assertTrue(array_key_exists('/node', $candidates), 'Seventh candidate found.');
-    $this->assertTrue(array_key_exists('/', $candidates), 'Eighth candidate found.');
   }
 
   /**
@@ -90,11 +97,11 @@ public function testCandidateOutlines() {
    */
   function testExactPathMatch() {
     $connection = Database::getConnection();
-    $provider = new RouteProvider($connection, $this->routeBuilder, 'test_routes');
+    $provider = new RouteProvider($connection, $this->routeBuilder, $this->state, 'test_routes');
 
     $this->fixtures->createTables($connection);
 
-    $dumper = new MatcherDumper($connection, 'test_routes');
+    $dumper = new MatcherDumper($connection, $this->state, 'test_routes');
     $dumper->addRoutes($this->fixtures->sampleRouteCollection());
     $dumper->dump();
 
@@ -114,11 +121,11 @@ function testExactPathMatch() {
    */
   function testOutlinePathMatch() {
     $connection = Database::getConnection();
-    $provider = new RouteProvider($connection, $this->routeBuilder, 'test_routes');
+    $provider = new RouteProvider($connection, $this->routeBuilder, $this->state, 'test_routes');
 
     $this->fixtures->createTables($connection);
 
-    $dumper = new MatcherDumper($connection, 'test_routes');
+    $dumper = new MatcherDumper($connection, $this->state, 'test_routes');
     $dumper->addRoutes($this->fixtures->complexRouteCollection());
     $dumper->dump();
 
@@ -143,11 +150,11 @@ function testOutlinePathMatch() {
    */
   function testOutlinePathMatchTrailingSlash() {
     $connection = Database::getConnection();
-    $provider = new RouteProvider($connection, $this->routeBuilder, 'test_routes');
+    $provider = new RouteProvider($connection, $this->routeBuilder, $this->state, 'test_routes');
 
     $this->fixtures->createTables($connection);
 
-    $dumper = new MatcherDumper($connection, 'test_routes');
+    $dumper = new MatcherDumper($connection, $this->state, 'test_routes');
     $dumper->addRoutes($this->fixtures->complexRouteCollection());
     $dumper->dump();
 
@@ -172,7 +179,7 @@ function testOutlinePathMatchTrailingSlash() {
    */
   function testOutlinePathMatchDefaults() {
     $connection = Database::getConnection();
-    $provider = new RouteProvider($connection, $this->routeBuilder, 'test_routes');
+    $provider = new RouteProvider($connection, $this->routeBuilder, $this->state, 'test_routes');
 
     $this->fixtures->createTables($connection);
 
@@ -181,7 +188,7 @@ function testOutlinePathMatchDefaults() {
       'value' => 'poink',
     )));
 
-    $dumper = new MatcherDumper($connection, 'test_routes');
+    $dumper = new MatcherDumper($connection, $this->state, 'test_routes');
     $dumper->addRoutes($collection);
     $dumper->dump();
 
@@ -210,7 +217,7 @@ function testOutlinePathMatchDefaults() {
    */
   function testOutlinePathMatchDefaultsCollision() {
     $connection = Database::getConnection();
-    $provider = new RouteProvider($connection, $this->routeBuilder, 'test_routes');
+    $provider = new RouteProvider($connection, $this->routeBuilder, $this->state, 'test_routes');
 
     $this->fixtures->createTables($connection);
 
@@ -220,7 +227,7 @@ function testOutlinePathMatchDefaultsCollision() {
     )));
     $collection->add('narf', new Route('/some/path/here'));
 
-    $dumper = new MatcherDumper($connection, 'test_routes');
+    $dumper = new MatcherDumper($connection, $this->state, 'test_routes');
     $dumper->addRoutes($collection);
     $dumper->dump();
 
@@ -249,7 +256,7 @@ function testOutlinePathMatchDefaultsCollision() {
    */
   function testOutlinePathMatchDefaultsCollision2() {
     $connection = Database::getConnection();
-    $provider = new RouteProvider($connection, $this->routeBuilder, 'test_routes');
+    $provider = new RouteProvider($connection, $this->routeBuilder, $this->state, 'test_routes');
 
     $this->fixtures->createTables($connection);
 
@@ -260,7 +267,7 @@ function testOutlinePathMatchDefaultsCollision2() {
     $collection->add('narf', new Route('/some/path/here'));
     $collection->add('eep', new Route('/something/completely/different'));
 
-    $dumper = new MatcherDumper($connection, 'test_routes');
+    $dumper = new MatcherDumper($connection, $this->state, 'test_routes');
     $dumper->addRoutes($collection);
     $dumper->dump();
 
@@ -288,14 +295,14 @@ function testOutlinePathMatchDefaultsCollision2() {
    */
   public function testOutlinePathMatchZero() {
     $connection = Database::getConnection();
-    $provider = new RouteProvider($connection, $this->routeBuilder, 'test_routes');
+    $provider = new RouteProvider($connection, $this->routeBuilder, $this->state, 'test_routes');
 
     $this->fixtures->createTables($connection);
 
     $collection = new RouteCollection();
     $collection->add('poink', new Route('/some/path/{value}'));
 
-    $dumper = new MatcherDumper($connection, 'test_routes');
+    $dumper = new MatcherDumper($connection, $this->state, 'test_routes');
     $dumper->addRoutes($collection);
     $dumper->dump();
 
@@ -323,11 +330,11 @@ public function testOutlinePathMatchZero() {
    */
   function testOutlinePathNoMatch() {
     $connection = Database::getConnection();
-    $provider = new RouteProvider($connection, $this->routeBuilder, 'test_routes');
+    $provider = new RouteProvider($connection, $this->routeBuilder, $this->state, 'test_routes');
 
     $this->fixtures->createTables($connection);
 
-    $dumper = new MatcherDumper($connection, 'test_routes');
+    $dumper = new MatcherDumper($connection, $this->state, 'test_routes');
     $dumper->addRoutes($this->fixtures->complexRouteCollection());
     $dumper->dump();
 
@@ -335,16 +342,12 @@ function testOutlinePathNoMatch() {
 
     $request = Request::create($path, 'GET');
 
-    try {
-      $routes = $provider->getRoutesByPattern($path);
-      $this->assertFalse(count($routes), 'No path found with this pattern.');
 
-      $provider->getRouteCollectionForRequest($request);
-      $this->fail(t('No exception was thrown.'));
-    }
-    catch (\Exception $e) {
-      $this->assertTrue($e instanceof ResourceNotFoundException, 'The correct exception was thrown.');
-    }
+    $routes = $provider->getRoutesByPattern($path);
+    $this->assertFalse(count($routes), 'No path found with this pattern.');
+
+    $collection = $provider->getRouteCollectionForRequest($request);
+    $this->assertTrue(count($collection) == 0, 'Empty route collection found with this pattern.');
   }
 
   /**
@@ -352,11 +355,11 @@ function testOutlinePathNoMatch() {
    */
   function testSystemPathMatch() {
     $connection = Database::getConnection();
-    $provider = new RouteProvider($connection, $this->routeBuilder, 'test_routes');
+    $provider = new RouteProvider($connection, $this->routeBuilder, $this->state, 'test_routes');
 
     $this->fixtures->createTables($connection);
 
-    $dumper = new MatcherDumper($connection, 'test_routes');
+    $dumper = new MatcherDumper($connection, $this->state, 'test_routes');
     $dumper->addRoutes($this->fixtures->sampleRouteCollection());
     $dumper->dump();
 
@@ -377,11 +380,11 @@ function testSystemPathMatch() {
    */
   protected function testRouteByName() {
     $connection = Database::getConnection();
-    $provider = new RouteProvider($connection, $this->routeBuilder, 'test_routes');
+    $provider = new RouteProvider($connection, $this->routeBuilder, $this->state, 'test_routes');
 
     $this->fixtures->createTables($connection);
 
-    $dumper = new MatcherDumper($connection, 'test_routes');
+    $dumper = new MatcherDumper($connection, $this->state, 'test_routes');
     $dumper->addRoutes($this->fixtures->sampleRouteCollection());
     $dumper->dump();
 
@@ -412,21 +415,57 @@ protected function testRouteByName() {
    */
   public function testGetRoutesByPatternWithLongPatterns() {
     $connection = Database::getConnection();
-    $provider = new RouteProvider($connection, $this->routeBuilder, 'test_routes');
+    $provider = new RouteProvider($connection, $this->routeBuilder, $this->state, 'test_routes');
 
     $this->fixtures->createTables($connection);
-
-    $dumper = new MatcherDumper($connection, 'test_routes');
+    // This pattern has only 3 parts, so we will get candidates, but no routes,
+    // even though we have not dumped the routes yet.
+    $shortest = '/test/1/test2';
+    $result = $provider->getRoutesByPattern($shortest);
+    $this->assertEqual($result->count(), 0);
+    $candidates = $provider->getCandidateOutlines(explode('/', trim($shortest, '/')));
+    $this->assertEqual(count($candidates), 7);
+    // A longer patten is not found and returns no candidates
+    $path_to_test = '/test/1/test2/2/test3/3/4/5/6/test4';
+    $result = $provider->getRoutesByPattern($path_to_test);
+    $this->assertEqual($result->count(), 0);
+    $candidates = $provider->getCandidateOutlines(explode('/', trim($path_to_test, '/')));
+    $this->assertEqual(count($candidates), 0);
+
+    // Add a matching route and dump it.
+    $dumper = new MatcherDumper($connection, $this->state, 'test_routes');
     $collection = new RouteCollection();
     $collection->add('long_pattern', new Route('/test/{v1}/test2/{v2}/test3/{v3}/{v4}/{v5}/{v6}/test4'));
     $dumper->addRoutes($collection);
     $dumper->dump();
 
-    $result = $provider->getRoutesByPattern('/test/1/test2/2/test3/3/4/5/6/test4');
+    $result = $provider->getRoutesByPattern($path_to_test);
     $this->assertEqual($result->count(), 1);
     // We can't compare the values of the routes directly, nor use
     // spl_object_hash() because they are separate instances.
     $this->assertEqual(serialize($result->get('long_pattern')), serialize($collection->get('long_pattern')), 'The right route was found.');
+    // We now have a single candidate outline.
+    $candidates = $provider->getCandidateOutlines(explode('/', trim($path_to_test, '/')));
+    $this->assertEqual(count($candidates), 1);
+    // Longer and shorter patterns are not found. Both are longer than 3, so
+    // we should not have any candidates either. The fact that we do not
+    // get any candidates for a longer path is a security feature.
+    $longer = '/test/1/test2/2/test3/3/4/5/6/test4/trailing/more/parts';
+    $result = $provider->getRoutesByPattern($longer);
+    $this->assertEqual($result->count(), 0);
+    $candidates = $provider->getCandidateOutlines(explode('/', trim($longer, '/')));
+    $this->assertEqual(count($candidates), 1);
+    $shorter = '/test/1/test2/2/test3';
+    $result = $provider->getRoutesByPattern($shorter);
+    $this->assertEqual($result->count(), 0);
+    $candidates = $provider->getCandidateOutlines(explode('/', trim($shorter, '/')));
+    $this->assertEqual(count($candidates), 0);
+    // This pattern has only 3 parts, so we will get candidates, but no routes.
+    // This result is unchanged by running the dumper.
+    $result = $provider->getRoutesByPattern($shortest);
+    $this->assertEqual($result->count(), 0);
+    $candidates = $provider->getCandidateOutlines(explode('/', trim($shortest, '/')));
+    $this->assertEqual(count($candidates), 7);
   }
 
 }
diff --git a/core/modules/system/system.install b/core/modules/system/system.install
index 21bfb283dfc63000cda5d26ef915d9eae69b8e1b..ca5d12806f59bff08653c6e944506c47120a8ed2 100644
--- a/core/modules/system/system.install
+++ b/core/modules/system/system.install
@@ -812,7 +812,8 @@ function system_schema() {
       ),
       'route' => array(
         'description' => 'A serialized Route object',
-        'type' => 'text',
+        'type' => 'blob',
+        'size' => 'big',
       ),
       'number_parts' => array(
         'description' => 'Number of parts in this router path.',