diff --git a/core/lib/Drupal/Core/Cache/CacheChain.php b/core/lib/Drupal/Core/Cache/CacheChain.php
new file mode 100644
index 0000000..3c0721d
--- /dev/null
+++ b/core/lib/Drupal/Core/Cache/CacheChain.php
@@ -0,0 +1,291 @@
+<?php
+
+namespace Drupal\Core\Cache;
+
+/**
+ * Cache chain interface is usefull if you need a better read performance than
+ * normal cache, in a context where you supposedly will get more than once the
+ * same item. For example, you could chain an in-memory implementation (static
+ * cache) and a database implementation behind. Once the cache entry fetched,
+ * every new hit would give you statically cached result instead of querying
+ * back again the database.
+ *
+ * This proxy pattern is also clean, because using it you won't have to handle
+ * any static cache in your code anymore, and it makes it being pluggable.
+ *
+ * This object behavior is a chain a of command for writes (every modification
+ * will be repercuted on every backend in the chain) while it's a chain of
+ * responsability for all read operations (the first backend that returns a
+ * result wins, and the other are left alone). It makes it slower for writes,
+ * but a lot faster for read operations.
+ */
+class CacheChain implements CacheBackendInterface {
+
+  /**
+   * Ordered list of CacheBackendInterface instances.
+   *
+   * @var array
+   */
+  protected $backends = array();
+
+  /**
+   * Cache bin name.
+   *
+   * @var string
+   */
+  protected $bin;
+
+  /**
+   * Does this instance must propagate get.
+   *
+   * @var bool
+   */
+  protected $doPropagateGet = TRUE;
+
+  /**
+   * Set the propagate mode on or off.
+   *
+   * Propagation is a specific mecanism that will, if the first backends skips
+   * the key on which we are doing a get operation, will call the set method
+   * upon them if a lower backend has a value.
+   *
+   * As of now, this is mostly used for unit testing.
+   *
+   * @param bool $toggle
+   *   TRUE will enable get propagation, FALSE will disable it.
+   *
+   * @return Drupal\Core\Cache\CacheChain
+   *   Self reference for chaining.
+   */
+  public function toggleGetPropagation($toggle) {
+    $this->doPropagateGet = $toggle;
+
+    return $this;
+  }
+
+  /**
+   * Append a cache backend to this chain.
+   *
+   * @param CacheBackendInterface $backend
+   *   Backend to prepend.
+   *
+   * @return Drupal\Core\Cache\CacheChain
+   *   Self reference for chaining.
+   */
+  public function appendBackend(CacheBackendInterface $backend) {
+    $this->backends[] = $backend;
+
+    return $this;
+  }
+
+  /**
+   * Prepend a cache backend to this chain.
+   *
+   * @param CacheBackendInterface $backend
+   *   Backend to prepend.
+   *
+   * @return Drupal\Core\Cache\CacheChain
+   *   Self reference for chaining.
+   */
+  public function prependBackend(CacheBackendInterface $backend) {
+    array_unshift($this->backends, $backend);
+
+    return $this;
+  }
+
+  /**
+   * Implements Drupal\Core\Cache\CacheBackendInterface::get().
+   *
+   * Because the constructor is into the CacheBackendInterface, we cannot
+   * implement our own that would allow cache backends to be set directly.
+   */
+  public function __construct($bin) {
+    $this->bin = $bin;
+  }
+
+  /**
+   * CacheBackendInterface::get() method alias that will propagate fetched
+   * values over previous backends that missed the value.
+   */
+  protected function propagatedGet($cid) {
+
+    foreach ($this->backends as $index => $backend) {
+      if (FALSE !== ($ret = $backend->get($cid))) {
+        // We found a result, propagate it over all missed backends.
+        if (0 < $index) {
+          for ($i = 0; $i < $index; ++$i) {
+            // @todo The cache item tags property never has been specified and
+            // it will be different depending on the backend implementation, we
+            // cannot propagate tags.
+            $this->backends[$i]->set($cid, $ret->data, $ret->expire /*, $ret->tags */);
+          }
+        }
+
+        return $ret;
+      }
+    }
+
+    return FALSE;
+  }
+
+  /**
+   * Implements Drupal\Core\Cache\CacheBackendInterface::get().
+   */
+  public function get($cid) {
+    if ($this->doPropagateGet) {
+      return $this->propagatedGet($cid);
+    }
+
+    foreach ($this->backends as $backend) {
+      if (FALSE !== ($ret = $backend->get($cid))) {
+        return $ret;
+      }
+    } 
+
+    return FALSE;
+  }
+
+  /**
+   * CacheBackendInterface::getMultiple() method alias that will propagate
+   * fetched values over previous backends that missed the value.
+   *
+   * @todo Another optimization here, as well in the propagatedGet() method
+   * would be to propagate only in the first backend to raise its cache hit,
+   * but not in the others in the middle. Doing this would loose the generic
+   * API signature thought.
+   */
+  protected function propagatedGetMultiple(&$cids) {
+    $ret = array();
+
+    foreach ($this->backends as $index => $backend) {
+      $ret += $backend->getMultiple($cids);
+
+      // Do an on-the-fly cache set to missed backends, for this we need
+      // to have the missed track. Hopefully the CacheChain object signature
+      // doesn't allow to mess up with internal array indexes.
+      if (0 < $index) {
+        for ($i = 0; $i < $index; ++$i) {
+          foreach ($ret as $cached) {
+            // @todo The cache item tags property never has been specified and
+            // it will be different depending on the backend implementation, we
+            // cannot propagate tags.
+            $this->backends[$i]->set($cached->cid, $cached->data, $cached->expire /*, $ret->tags */);
+          }
+        }
+      }
+
+      if (empty($cids)) {
+        // No need to go further if we don't have any cid to fetch left.
+        break;
+      }
+    }
+
+    return $ret;
+  }
+
+  /**
+   * Implements Drupal\Core\Cache\CacheBackendInterface::getMultiple().
+   */
+  function getMultiple(&$cids) {
+    if ($this->doPropagateGet) {
+      return $this->propagatedGetMultiple($cids);
+    }
+
+    $ret = array();
+
+    foreach ($this->backends as $backend) {
+      $ret += $backend->getMultiple($cids);
+
+      if (empty($cids)) {
+        // No need to go further if we don't have any cid to fetch left.
+        break;
+      }
+    }
+
+    return $ret;
+  }
+
+  /**
+   * Implements Drupal\Core\Cache\CacheBackendInterface::set().
+   */
+  function set($cid, $data, $expire = CacheBackendInterface::CACHE_PERMANENT, array $tags = array()) {
+    foreach ($this->backends as $backend) {
+      $backend->set($cid, $data, $expire, $tags);
+    }
+  }
+
+  /**
+   * Implements Drupal\Core\Cache\CacheBackendInterface::delete().
+   */
+  function delete($cid) {
+    foreach ($this->backends as $backend) {
+      $backend->delete($cid);
+    }
+  }
+
+  /**
+   * Implements Drupal\Core\Cache\CacheBackendInterface::deleteMultiple().
+   */
+  function deleteMultiple(array $cids) {
+    foreach ($this->backends as $backend) {
+      $backend->deleteMultiple($cids);
+    }
+  }
+
+  /**
+   * Implements Drupal\Core\Cache\CacheBackendInterface::deletePrefix().
+   */
+  function deletePrefix($prefix) {
+    foreach ($this->backends as $backend) {
+      $backend->deletePrefix($prefix);
+    }
+  }
+
+  /**
+   * Implements Drupal\Core\Cache\CacheBackendInterface::flush().
+   */
+  public function flush() {
+    foreach ($this->backends as $backend) {
+      $backend->flush();
+    }
+  }
+
+  /**
+   * Implements Drupal\Core\Cache\CacheBackendInterface::expire().
+   */
+  public function expire() {
+    foreach ($this->backends as $backend) {
+      $backend->expire();
+    }
+  }
+
+  /**
+   * Implements Drupal\Core\Cache\CacheBackendInterface::invalidateTags().
+   */
+  public function invalidateTags(array $tags) {
+    foreach ($this->backends as $backend) {
+      $backend->invalidateTags($tags);
+    }
+  }
+
+  /**
+   * Implements Drupal\Core\Cache\CacheBackendInterface::garbageCollection().
+   */
+  public function garbageCollection() {
+    foreach ($this->backends as $backend) {
+      $backend->garbageCollection();
+    }
+  }
+
+  /**
+   * Implements Drupal\Core\Cache\CacheBackendInterface::isEmpty().
+   */
+  public function isEmpty() {
+    foreach ($this->backends as $backend) {
+      if (!$backend->isEmpty()) {
+        return FALSE;
+      }
+    }
+    return TRUE;
+  }
+}
diff --git a/core/modules/system/lib/Drupal/system/Tests/Cache/CacheChainUnitTest.php b/core/modules/system/lib/Drupal/system/Tests/Cache/CacheChainUnitTest.php
new file mode 100644
index 0000000..285ae17
--- /dev/null
+++ b/core/modules/system/lib/Drupal/system/Tests/Cache/CacheChainUnitTest.php
@@ -0,0 +1,279 @@
+<?php
+
+namespace Drupal\system\Tests\Cache;
+
+use Drupal\Core\Cache\CacheBackendInterface;
+use Drupal\Core\Cache\CacheChain;
+use Drupal\Core\Cache\DatabaseBackend;
+use Drupal\simpletest\UnitTestBase;
+
+/**
+ * Tests cache clearing methods.
+ */
+class CacheChainUnitTest extends UnitTestBase {  
+
+  public static function getInfo() {
+    return array(
+      'name' => 'Cache chain implementation',
+      'description' => 'Test that cache chain implementation is fully working.',
+      'group' => 'Cache'
+    );
+  }
+
+  /**
+   * Chain that will be heavily tested.
+   *
+   * @var Drupal\Core\Cache\CacheChain
+   */
+  protected $chain;
+
+  /**
+   * First in chain.
+   *
+   * @var Drupal\Core\Cache\CacheBackendInterface
+   */
+  protected $firstBackend;
+
+  /**
+   * Second in chain.
+   *
+   * @var Drupal\Core\Cache\CacheBackendInterface
+   */
+  protected $secondBackend;
+
+  /**
+   * Third in chain.
+   *
+   * @var Drupal\Core\Cache\CacheBackendInterface
+   */ 
+  protected $thirdBackend;
+
+  public function setUp() {
+    parent::setUp();
+
+    drupal_install_schema('system');
+
+    // Randomly taking three existing tables.
+    // FIXME: When the ArrayBackend will be commited, use it instead for
+    // test performance reasons, we are not testing the backends, but the
+    // chain.
+    $this->firstBackend  = new DatabaseBackend('cache');
+    $this->secondBackend = new DatabaseBackend('page');
+    $this->thirdBackend  = new DatabaseBackend('menu');
+
+    // Set an initial fixed dataset for all testing. The next three data
+    // collections will test two edge cases (last backend has the data, and
+    // first backend has the data) and will test a normal use case (middle
+    // backend has the data). We should have a complete unit test with those.
+    // Note that in all cases, when the same key is set on more than one
+    // backend, the values are voluntarely different, this ensures in which
+    // backend we actually fetched the key when doing get calls. This is
+    // important, do not change!
+
+    // Set a key present on three of them (for delete).
+    $this->firstBackend->set('t123', 1231);
+    $this->secondBackend->set('t123', 1232);
+    $this->thirdBackend->set('t123', 1233);
+
+    // Set a key present on the second and the third (for get), those two will
+    // be different, this will ensure from where we get the key.
+    $this->secondBackend->set('t23', 232);
+    $this->thirdBackend->set('t23', 233);
+
+    // Set a key on only the third, we will ensure propagation using this one.
+    $this->thirdBackend->set('t3', 33);
+
+    // Create the chain.
+    $this->chain = new CacheChain('cache_form');
+
+    $this
+      ->chain
+      ->toggleGetPropagation(FALSE)
+      ->appendBackend($this->firstBackend)
+      ->appendBackend($this->secondBackend)
+      ->appendBackend($this->thirdBackend);
+  }
+
+  public function tearDown() {
+    drupal_uninstall_schema('system');
+
+    parent::tearDown();
+  }
+
+  /**
+   * Test the get feature.
+   * Propagation is disabled for this test.
+   */
+  public function testGet() {
+    $cached = $this->chain->get('t123');
+    $this->assertNotIdentical(FALSE, $cached, "Got key that is on all backends");
+    $this->assertIdentical(1231, $cached->data, "Got the key from the first backend");
+
+    $cached = $this->chain->get('t23');
+    $this->assertNotIdentical(FALSE, $cached, "Got key that is on 2 and 3 backends");
+    $this->assertIdentical(232, $cached->data, "Got the key from the 2 backend");
+
+    $cached = $this->chain->get('t3');
+    $this->assertNotIdentical(FALSE, $cached, "Got key that is on the third backend");
+    $this->assertIdentical(33, $cached->data, "Got the key from the third backend");
+
+    // Exact same with propagation, but test only one key.
+    $this->chain->toggleGetPropagation(TRUE);
+
+    $cached = $this->chain->get('t23');
+    $this->assertNotIdentical(FALSE, $cached, "Got key that is on 2 and 3 backends");
+    $this->assertIdentical(232, $cached->data, "Got the key from the 2 backend");
+  }
+
+  /**
+   * Test the get multiple feature.
+   * Propagation is disabled for this test.
+   */
+  public function testGetMultiple() {
+    $cids = array('t123', 't23', 't3', 't4');
+
+    $ret = $this->chain->getMultiple($cids);
+    $this->assertIdentical($ret['t123']->data, 1231, "Got key 123 and value is from the first backend");
+    $this->assertIdentical($ret['t23']->data, 232, "Got key 23 and value is from the second backend");
+    $this->assertIdentical($ret['t3']->data, 33, "Got key 3 and value is from the third backend");
+    $this->assertFalse(array_key_exists('t4', $ret), "Didn't got the non existing key");
+
+    $this->assertFalse(in_array('t123', $cids), "Existing key 123 has been removed from &\$cids");
+    $this->assertFalse(in_array('t23', $cids), "Existing key 23 has been removed from &\$cids");
+    $this->assertFalse(in_array('t3', $cids), "Existing key 3 has been removed from &\$cids");
+    $this->assert(in_array('t4', $cids), "Non existing key 4 is still in &\$cids");
+
+    // Exact same test with propagation, but only get 2 keys.
+    $this->chain->toggleGetPropagation(TRUE);
+    $cids = array('t23', 't4');
+
+    $ret = $this->chain->getMultiple($cids);
+    $this->assertIdentical($ret['t23']->data, 232, "Got key 23 and value is from the second backend");
+    $this->assertFalse(array_key_exists('t4', $ret), "Didn't got the non existing key");
+
+    $this->assertFalse(in_array('t23', $cids), "Existing key 23 has been removed from &\$cids");
+    $this->assert(in_array('t4', $cids), "Non existing key 4 is still in &\$cids");
+  }
+
+  /**
+   * Test that set will propagate.
+   */
+  public function testSet() {
+    $this->chain->set('test', 123);
+
+    $cached = $this->firstBackend->get('test');
+    $this->assertNotIdentical(FALSE, $cached, "Test key is in the first backend");
+    $this->assertIdentical(123, $cached->data, "Test key has the right value");
+
+    $cached = $this->secondBackend->get('test');
+    $this->assertNotIdentical(FALSE, $cached, "Test key is in the second backend");
+    $this->assertIdentical(123, $cached->data, "Test key has the right value");
+
+    $cached = $this->thirdBackend->get('test');
+    $this->assertNotIdentical(FALSE, $cached, "Test key is in the third backend");
+    $this->assertIdentical(123, $cached->data, "Test key has the right value");
+  }
+
+  /**
+   * Test that delete will propagate.
+   */
+  public function testDelete() {
+    $this->chain->set('test', 5);
+    $this->chain->set('prefixtest', 7);
+
+    $this->chain->deletePrefix('prefix');
+
+    $cached = $this->firstBackend->get('prefixtest');
+    $this->assertIdentical(FALSE, $cached, "Prefixed test key is removed from the first backend");
+    $cached = $this->secondBackend->get('prefixtest');
+    $this->assertIdentical(FALSE, $cached, "Prefixed test key is removed from the second backend");
+    $cached = $this->thirdBackend->get('prefixtest');
+    $this->assertIdentical(FALSE, $cached, "Prefixed test key is removed from the third backend");
+
+    // This tests the prefix deletion didn't messed up with other keys.
+    $cached = $this->firstBackend->get('test');
+    $this->assertNotIdentical(FALSE, $cached, "Test key is still in the first backend");
+    $cached = $this->secondBackend->get('test');
+    $this->assertNotIdentical(FALSE, $cached, "Test key is still in the second backend");
+    $cached = $this->thirdBackend->get('test');
+    $this->assertNotIdentical(FALSE, $cached, "Test key is still in the third backend");
+
+    $this->chain->delete('test');
+
+    $cached = $this->firstBackend->get('test');
+    $this->assertIdentical(FALSE, $cached, "Test key is removed from the first backend");
+    $cached = $this->secondBackend->get('test');
+    $this->assertIdentical(FALSE, $cached, "Test key is removed from the second backend");
+    $cached = $this->thirdBackend->get('test');
+    $this->assertIdentical(FALSE, $cached, "Test key is removed from the third backend");
+  }
+
+  /**
+   * Ensure get values propagation to previous backends.
+   */
+  public function testGetHasPropagated() {
+    $this->chain->toggleGetPropagation(TRUE);
+
+    $this->chain->get('t23');
+    $cached = $this->firstBackend->get('t23');
+    $this->assertNotIdentical(FALSE, $cached, "Test 2 has been propagated to the first backend");
+
+    $this->chain->get('t3');
+    $cached = $this->firstBackend->get('t3');
+    $this->assertNotIdentical(FALSE, $cached, "Test 3 has been propagated to the first backend");
+    $cached = $this->secondBackend->get('t3');
+    $this->assertNotIdentical(FALSE, $cached, "Test 3 has been propagated to the second backend");
+  }
+
+  /**
+   * Ensure get multiple values propagation to previous backends.
+   */
+  public function testGetMultipleHasPropagated() {
+    $this->chain->toggleGetPropagation(TRUE);
+
+    $cids = array('t3', 't23');
+    $this->chain->getMultiple($cids);
+
+    $cached = $this->firstBackend->get('t3');
+    $this->assertNotIdentical(FALSE, $cached, "Test 3 has been propagated to the first backend");
+    $this->assertIdentical(33, $cached->data, "And value has been kept");
+    $cached = $this->secondBackend->get('t3');
+    $this->assertNotIdentical(FALSE, $cached, "Test 3 has been propagated to the second backend");
+    $this->assertIdentical(33, $cached->data, "And value has been kept");
+
+    $cached = $this->firstBackend->get('t23');
+    $this->assertNotIdentical(FALSE, $cached, "Test 2 has been propagated to the first backend");
+    $this->assertIdentical(232, $cached->data, "And value has been kept");
+  }
+
+  public function testNotEmptyIfOneBackendHasTheKey() {
+    $this->assertFalse($this->chain->isEmpty(), "Chain is not empty");
+
+    // This is the only test that needs to start with an empty chain.
+    $this->chain->flush();
+    $this->assert($this->chain->isEmpty(), "Chain have been emptied by the flush() call");
+
+    $this->secondBackend->set('test', 5);
+    $this->assertFalse($this->chain->isEmpty(), "Chain is not empty anymore now that the second backend has something");
+  }
+
+  public function testTagInvalidation() {
+    /*
+     * @todo: Restore those tests as soon as cache entries tag property has
+     * been specified and implemented.
+     *
+    $this->chain->toggleGetPropagation(TRUE);
+
+    $this->thirdBackend->set('t123', 1231, array('node' => array(1)));
+
+    $cached = $this->chain->get('t123');
+    $this->assertNotIdentical(FALSE, $cached, "Cache entry was propagated to all backends");
+
+    $this->chain->invalidateTags(array('node' => array(1)));
+    $cached = $this->firstBackend->get('t123');
+    $this->assertIdentical(FALSE, $cached, "Cache entry was invalidated in first backend");
+    $cached = $this->thirdBackend->get('t123');
+    $this->assertIdentical(FALSE, $cached, "Cache entry was invalidated in third backend");
+     */
+  }
+}
