diff --git a/menu_position.services.yml b/menu_position.services.yml
index 7f5bb9d7e7ff259276d22dff252e6328036fce26..ae3bc04a07ad18923fd218bcfc2334fa0880dc4c 100644
--- a/menu_position.services.yml
+++ b/menu_position.services.yml
@@ -2,3 +2,12 @@ services:
   plugin.manager.menu_position_condition_plugin.processor:
     class: Drupal\menu_position\Plugin\MenuPositionConditionPluginManager
     parent: default_plugin_manager
+
+  menu_position.menu.active_trail:
+    class: Drupal\menu_position\Menu\MenuPositionActiveTrail
+    public: false
+    decorates: menu.active_trail
+    decoration_priority: 9
+    arguments: [ '@menu_position.menu.active_trail.inner', '@plugin.manager.menu.link', '@current_route_match', '@cache.menu', '@lock', '@entity_type.manager', '@config.factory' ]
+    tags:
+      - { name: needs_destruction }
diff --git a/src/Menu/MenuPositionActiveTrail.php b/src/Menu/MenuPositionActiveTrail.php
index a7344ed09d708da5ae429412c65fa18c32c8a846..6353f7160017be602d8517cfe5168ac000ecd831 100644
--- a/src/Menu/MenuPositionActiveTrail.php
+++ b/src/Menu/MenuPositionActiveTrail.php
@@ -3,59 +3,156 @@
 namespace Drupal\menu_position\Menu;
 
 use Drupal\Core\Cache\CacheBackendInterface;
+use Drupal\Core\Cache\CacheCollector;
 use Drupal\Core\Config\ConfigFactoryInterface;
+use Drupal\Core\Config\ImmutableConfig;
+use Drupal\Core\DependencyInjection\DependencySerializationTrait;
 use Drupal\Core\Entity\EntityTypeManagerInterface;
 use Drupal\Core\Lock\LockBackendInterface;
-use Drupal\Core\Menu\MenuActiveTrail;
+use Drupal\Core\Menu\MenuActiveTrailInterface;
 use Drupal\Core\Menu\MenuLinkManagerInterface;
 use Drupal\Core\Routing\RouteMatchInterface;
 
 /**
  * Menu Position active trail.
+ *
+ * Decorates the MenuActiveTrail class.
  */
-class MenuPositionActiveTrail extends MenuActiveTrail {
+class MenuPositionActiveTrail extends CacheCollector implements MenuActiveTrailInterface {
+
+  use DependencySerializationTrait;
 
   /**
    * The entity type manager.
    *
-   * @var \Drupal\Core\Entity\EntityTypeManagerInterface
+   * @var EntityTypeManagerInterface
    */
   protected $entityTypeManager;
 
   /**
    * Menu position settings.
    *
-   * @var \Drupal\Core\Config\ImmutableConfig
+   * @var ImmutableConfig
    */
   protected $settings;
 
+  /**
+   * The menu link plugin manager.
+   *
+   * @var MenuLinkManagerInterface
+   */
+  protected $menuLinkManager;
+
+  /**
+   * The route match object for the current page.
+   *
+   * @var RouteMatchInterface
+   */
+  protected $routeMatch;
+
+  /**
+   * The decorated MenuActiveTrail service.
+   *
+   * @var MenuActiveTrailInterface
+   */
+  protected $inner;
+
   /**
    * Constructs a \Drupal\Core\Menu\MenuActiveTrail object.
    *
-   * @param \Drupal\Core\Menu\MenuLinkManagerInterface $menu_link_manager
+   * @param MenuActiveTrailInterface $menu_active_trail
+   *   The menu link plugin manager.
+   * @param MenuLinkManagerInterface $menu_link_manager
    *   The menu link plugin manager.
-   * @param \Drupal\Core\Routing\RouteMatchInterface $route_match
+   * @param RouteMatchInterface $route_match
    *   A route match object for finding the active link.
-   * @param \Drupal\Core\Cache\CacheBackendInterface $cache
+   * @param CacheBackendInterface $cache
    *   The cache backend service.
-   * @param \Drupal\Core\Lock\LockBackendInterface $lock
+   * @param LockBackendInterface $lock
    *   The lock backend service.
-   * @param \Drupal\Core\Entity\EntityTypeManagerInterface $entity_type_manager
+   * @param EntityTypeManagerInterface $entity_type_manager
    *   The entity type manager.
-   * @param \Drupal\Core\Config\ConfigFactoryInterface $config_factory
+   * @param ConfigFactoryInterface $config_factory
    *   The config factory service.
    */
   public function __construct(
+    MenuActiveTrailInterface $menu_active_trail,
     MenuLinkManagerInterface $menu_link_manager,
     RouteMatchInterface $route_match,
     CacheBackendInterface $cache,
     LockBackendInterface $lock,
     EntityTypeManagerInterface $entity_type_manager,
     ConfigFactoryInterface $config_factory) {
-
-    parent::__construct($menu_link_manager, $route_match, $cache, $lock);
+    $this->inner = $menu_active_trail;
+    $this->menuLinkManager = $menu_link_manager;
+    $this->routeMatch = $route_match;
     $this->entityTypeManager = $entity_type_manager;
     $this->settings = $config_factory->get('menu_position.settings');
+    parent::__construct(NULL, $cache, $lock);
+  }
+
+  /**
+   * {@inheritdoc}
+   *
+   * @see ::getActiveTrailIds()
+   */
+  protected function getCid() {
+    if (!isset($this->cid)) {
+      $route_parameters = $this->routeMatch->getRawParameters()->all();
+      ksort($route_parameters);
+      $this->cid = 'active-trail:route:' . $this->routeMatch->getRouteName() . ':route_parameters:' . serialize($route_parameters);
+    }
+
+    return $this->cid;
+  }
+
+  /**
+   * {@inheritdoc}
+   *
+   * @see ::getActiveTrailIds()
+   */
+  protected function resolveCacheMiss($menu_name) {
+    $this->storage[$menu_name] = $this->doGetActiveTrailIds($menu_name);
+    $this->tags[] = 'config:system.menu.' . $menu_name;
+    $this->persist($menu_name);
+
+    return $this->storage[$menu_name];
+  }
+
+  /**
+   * {@inheritdoc}
+   *
+   * This implementation caches all active trail IDs per route match for *all*
+   * menus whose active trails are calculated on that page. This ensures 1 cache
+   * get for all active trails per page load, rather than N.
+   *
+   * It uses the cache collector pattern to do this.
+   *
+   * @see ::get()
+   * @see \Drupal\Core\Cache\CacheCollectorInterface
+   * @see CacheCollector
+   */
+  public function getActiveTrailIds($menu_name) {
+    return $this->get($menu_name);
+  }
+
+  /**
+   * Helper method for ::getActiveTrailIds().
+   */
+  protected function doGetActiveTrailIds($menu_name) {
+    // Parent ids; used both as key and value to ensure uniqueness.
+    // We always want all the top-level links with parent == ''.
+    $active_trail = ['' => ''];
+
+    // If a link in the given menu indeed matches the route, then use it to
+    // complete the active trail.
+    if ($active_link = $this->getActiveLink($menu_name)) {
+      if ($parents = $this->menuLinkManager->getParentIds($active_link->getPluginId())) {
+        $active_trail = $parents + $active_trail;
+      }
+    }
+
+    return $active_trail;
   }
 
   /**
@@ -104,7 +201,6 @@ class MenuPositionActiveTrail extends MenuActiveTrail {
     }
 
     // Default implementation takes here.
-    return parent::getActiveLink($menu_name);
+    return $this->inner->getActiveLink($menu_name);
   }
-
 }
diff --git a/src/MenuPositionServiceProvider.php b/src/MenuPositionServiceProvider.php
deleted file mode 100644
index 2ecebb28adce83be857d4bfa0188d4c89bf7d79d..0000000000000000000000000000000000000000
--- a/src/MenuPositionServiceProvider.php
+++ /dev/null
@@ -1,25 +0,0 @@
-<?php
-
-namespace Drupal\menu_position;
-
-use Symfony\Component\DependencyInjection\Reference;
-use Drupal\Core\DependencyInjection\ContainerBuilder;
-use Drupal\Core\DependencyInjection\ServiceProviderBase;
-
-/**
- * Menu Position service provider.
- */
-class MenuPositionServiceProvider extends ServiceProviderBase {
-
-  /**
-   * {@inheritdoc}
-   */
-  public function alter(ContainerBuilder $container) {
-    // Override the menu active trail with a new class.
-    $definition = $container->getDefinition('menu.active_trail');
-    $definition->setClass('Drupal\menu_position\Menu\MenuPositionActiveTrail');
-    $definition->addArgument(new Reference('entity_type.manager'));
-    $definition->addArgument(new Reference('config.factory'));
-  }
-
-}