diff --git a/lib/classes/StudipCachedArray.php b/lib/classes/StudipCachedArray.php
index 221b76c3aafd1ac0b18c7efb23f6138d447b54d1..830128dde4d4a23fbedb22b7c9915b1887d74201 100644
--- a/lib/classes/StudipCachedArray.php
+++ b/lib/classes/StudipCachedArray.php
@@ -15,21 +15,24 @@ class StudipCachedArray implements ArrayAccess
 
     protected $data = [];
 
+    protected $hash;
+
     /**
      * Constructs the cached array
      *
-     * @param string $key    Cache key where the array is/should be stored
-     *                       an int which will be length of the substring
-     *                       of the given chache offset or a callable which
-     *                       will return the partition key.
-     * @param int $duration  Duration in seconds for which the item shall be
-     *                       stored
+     * @param string $key      Cache key where the array is/should be stored
+     *                         an int which will be length of the substring
+     *                         of the given chache offset or a callable which
+     *                         will return the partition key.
+     * @param int    $duration Duration in seconds for which the item shall be
+     *                         stored
      */
     public function __construct(string $key, int $duration = StudipCache::DEFAULT_EXPIRATION)
     {
-        $this->key      = self::class . "/{$key}";
-        $this->cache    = StudipCacheFactory::getCache();
+        $this->key = self::class . "/{$key}";
+        $this->cache = StudipCacheFactory::getCache();
         $this->duration = $duration;
+        $this->hash = $this->getHash();
 
         $this->reset();
     }
@@ -42,10 +45,20 @@ class StudipCachedArray implements ArrayAccess
         $this->data = [];
     }
 
+    /**
+     * Removes all values from the cache.
+     */
+    public function expire(): void
+    {
+        $this->hash = $this->getHash(true);
+        $this->reset();
+    }
+
     /**
      * Determines whether an offset exists in the array.
      *
      * @param string $offset Offset
+     *
      * @return bool
      */
     public function offsetExists($offset): bool
@@ -58,6 +71,7 @@ class StudipCachedArray implements ArrayAccess
      * Returns the value at given offset or null if it doesn't exist.
      *
      * @param string $offset Offset
+     *
      * @return mixed
      */
     public function offsetGet($offset)
@@ -75,7 +89,7 @@ class StudipCachedArray implements ArrayAccess
     public function offsetSet($offset, $value): void
     {
         if ($offset === null) {
-            throw new Exception('Cannot push to cached array, use StudipCachedArray instead');
+            throw new Exception('Cannot push to cached array, use correct offset instead');
         }
 
         if (!isset($this->data[$offset]) || $this->data[$offset] !== $value) {
@@ -120,9 +134,11 @@ class StudipCachedArray implements ArrayAccess
      */
     protected function storeData(string $offset): void
     {
+        $data = $this->swapNullAndFalse($this->data[$offset]);
+
         $this->cache->write(
             $this->getCacheKey($offset),
-            $this->swapNullAndFalse($this->data[$offset]),
+            $data,
             $this->duration
         );
     }
@@ -131,11 +147,18 @@ class StudipCachedArray implements ArrayAccess
      * Returns the cache key for a specific offset.
      *
      * @param string $offset Offset of the cached item
+     *
      * @return string
      */
     private function getCacheKey(string $offset): string
     {
-        return rtrim($this->key, '/') . "/{$offset}";
+        $key = rtrim($this->key, '/');
+        if ($this->hash) {
+            $key .= "/{$this->hash}";
+        }
+        $key .= "/{$offset}";
+
+        return $key;
     }
 
     /**
@@ -158,4 +181,21 @@ class StudipCachedArray implements ArrayAccess
 
         return $value;
     }
+
+    /**
+     * Loads or creates and stores a hash for this cached array.
+     *
+     * @return string
+     */
+    private function getHash(bool $recreate = false): string
+    {
+        if (!$recreate) {
+            $hash = $this->cache->read($this->key);
+            return $hash === false ? '' : $hash;
+        }
+
+        $hash = md5(uniqid(__CLASS__, true));
+        $this->cache->write($this->key, $hash);
+        return $hash;
+    }
 }
diff --git a/lib/plugins/db/RolePersistence.class.php b/lib/plugins/db/RolePersistence.class.php
index 1b03a0132c91558980bfa3da350e274f2f422309..ff17a94d0bd3b2f5779a8638fdc06df81dcce480 100644
--- a/lib/plugins/db/RolePersistence.class.php
+++ b/lib/plugins/db/RolePersistence.class.php
@@ -129,9 +129,10 @@ class RolePersistence
 
         // sweep roles cache
         self::expireRolesCache();
+        self::expireUserCache();
 
         foreach ($statement as $plugin_id) {
-            unset(self::getPluginRolesCache()[$plugin_id]);
+            self::expirePluginCache($plugin_id);
         }
 
         NotificationCenter::postNotification('RoleDidDelete', $id, $name);
@@ -338,7 +339,7 @@ class RolePersistence
             $statement->execute();
         }
 
-        unset(self::getPluginRolesCache()[$plugin_id]);
+        self::expirePluginCache($plugin_id);
 
         foreach ($role_ids as $role_id) {
             NotificationCenter::postNotification(
@@ -370,7 +371,7 @@ class RolePersistence
             $statement->execute();
         }
 
-        unset(self::getPluginRolesCache()[$plugin_id]);
+        self::expirePluginCache($plugin_id);
 
         foreach ($role_ids as $role_id) {
             NotificationCenter::postNotification(
@@ -488,7 +489,7 @@ class RolePersistence
     private static $user_roles_cache = null;
     private static $plugin_roles_cache = null;
 
-    private static function getUserRolesCache()
+    private static function getUserRolesCache(): StudipCachedArray
     {
         if (self::$user_roles_cache === null) {
             self::$user_roles_cache = new StudipCachedArray(self::USER_ROLES_CACHE_KEY);
@@ -496,7 +497,7 @@ class RolePersistence
         return self::$user_roles_cache;
     }
 
-    private static function getPluginRolesCache()
+    private static function getPluginRolesCache(): StudipCachedArray
     {
         if (self::$plugin_roles_cache === null) {
             self::$plugin_roles_cache = new StudipCachedArray(self::PLUGIN_ROLES_CACHE_KEY);
@@ -504,13 +505,52 @@ class RolePersistence
         return self::$plugin_roles_cache;
     }
 
+    /**
+     * Expires all cached roles.
+     */
     public static function expireRolesCache()
     {
         StudipCacheFactory::getCache()->expire(self::ROLES_CACHE_KEY);
     }
 
-    public static function expireUserCache($user_id)
+    /**
+     * Expires all cached user role assignments.
+     *
+     * @param string|null $user_id Optional user id to expire the cache for.
+     *                             If none is given, the whole cache is cleared.
+     */
+    public static function expireUserCache($user_id = null)
     {
-        unset(self::getUserRolesCache()[$user_id]);
+        if ($user_id === null) {
+            self::getUserRolesCache()->expire();
+        } else {
+            unset(self::getUserRolesCache()[$user_id]);
+        }
+    }
+
+    /**
+     * Expires all cached plugin role assignments.
+     *
+     * @param string|int|null $plugin_id Optional plugin id to expire the cache
+     *                                   for. If none is given, the whole cache
+     *                                   is cleared.
+     */
+    public static function expirePluginCache($plugin_id = null)
+    {
+        if ($plugin_id === null) {
+            self::getPluginRolesCache()->expire();
+        } else {
+            unset(self::getPluginRolesCache()[$plugin_id]);
+        }
+    }
+
+    /**
+     * Expires all caches
+     */
+    public static function expireCaches(): void
+    {
+        self::expireRolesCache();
+        self::expireUserCache();
+        self::expirePluginCache();
     }
 }
diff --git a/tests/unit/lib/classes/StudipCachedArrayTest.php b/tests/unit/lib/classes/StudipCachedArrayTest.php
index e473dc631439a19a549f0c34f5834cb7fa069e6d..c98c1bd6b5c151bc9f34656047edec3e4b989e74 100644
--- a/tests/unit/lib/classes/StudipCachedArrayTest.php
+++ b/tests/unit/lib/classes/StudipCachedArrayTest.php
@@ -48,6 +48,21 @@ class StudipCachedArrayTest extends \Codeception\Test\Unit
         $this->assertFalse(isset($cache[$key]));
     }
 
+    /**
+     * @depends testStorage
+     * @dataProvider StorageProvider
+     */
+    public function testExpiration($key, $value)
+    {
+        $cache = $this->getCachedArray();
+
+        $cache[$key] = $value;
+
+        $cache->expire();
+
+        $this->assertFalse(isset($cache[$key]));
+    }
+
     public function StorageProvider(): array
     {
         return [