diff --git a/lib/Database.php b/lib/Database.php
index 0e3ad9b3..45e42150 100644
--- a/lib/Database.php
+++ b/lib/Database.php
@@ -271,7 +271,7 @@ class Database {
         } else {
             return $this->db->prepare(
                 "WITH RECURSIVE folders(id) as (SELECT id from arsse_folders where owner is ? and parent is ? union select arsse_folders.id from arsse_folders join folders on arsse_folders.parent=folders.id) ".
-                "SELECT id,name,parent from arsse_folders where id in(SELECT id from folders) order by name",
+                "SELECT id,name,parent from arsse_folders where id in (SELECT id from folders) order by name",
             "str", "int")->run($user, $parent);
         }
     }
@@ -404,7 +404,7 @@ class Database {
                 url,favicon,source,folder,pinned,err_count,err_msg,order_type,added,
                 topmost.top as top_folder,
                 coalesce(arsse_subscriptions.title, arsse_feeds.title) as title,
-                (SELECT count(*) from arsse_articles where feed is arsse_subscriptions.feed) - (SELECT count(*) from arsse_marks join user on user is owner join arsse_articles on article = arsse_articles.id where feed is arsse_feeds.id and read is 1) as unread
+                (SELECT count(*) from arsse_articles where feed is arsse_subscriptions.feed) - (SELECT count(*) from arsse_marks where subscription is arsse_subscriptions.id and read is 1) as unread
              from arsse_subscriptions 
                 join user on user is owner 
                 join arsse_feeds on feed = arsse_feeds.id 
@@ -678,10 +678,10 @@ class Database {
                 edited as edited_date,
                 max(
                     modified, 
-                    coalesce((select modified from arsse_marks join user on user is owner where article is arsse_articles.id),'')
+                    coalesce((select modified from arsse_marks where article is arsse_articles.id and subscription in (select sub from subscribed_feeds)),'')
                 ) as modified_date,
-                NOT (select count(*) from arsse_marks join user on user is owner where article is arsse_articles.id and read is 1) as unread,
-                (select count(*) from arsse_marks join user on user is owner where article is arsse_articles.id and starred is 1) as starred,
+                NOT (select count(*) from arsse_marks where article is arsse_articles.id and read is 1 and subscription in (select sub from subscribed_feeds)) as unread,
+                (select count(*) from arsse_marks where article is arsse_articles.id and starred is 1 and subscription in (select sub from subscribed_feeds)) as starred,
                 (select max(id) from arsse_editions where article is arsse_articles.id) as edition,
                 subscribed_feeds.sub as subscription,
                 url_title_hash||':'||url_content_hash||':'||title_content_hash as fingerprint,
@@ -756,11 +756,11 @@ class Database {
                     starred = coalesce((select starred from target_values),starred),
                     modified = CURRENT_TIMESTAMP  
                 WHERE 
-                    owner is (select user from user) 
+                    subscription in (select sub from subscribed_feeds)
                     and article in (select id from target_articles where to_insert is 0 and (honour_read is 1 or honour_star is 1))",
-            "INSERT INTO arsse_marks(owner,article,read,starred)
+            "INSERT INTO arsse_marks(subscription,article,read,starred)
                 select 
-                    (select user from user),
+                    (select id from arsse_subscriptions join user on user is owner where arsse_subscriptions.feed is target_articles.feed),
                     id,
                     coalesce((select read from target_values) * honour_read,0),
                     coalesce((select starred from target_values),0)
@@ -787,26 +787,21 @@ class Database {
             $q = new Query(
                 "SELECT
                     arsse_articles.id as id,
+                    feed,
                     (select max(id) from arsse_editions where article is arsse_articles.id) as edition,
                     max(arsse_articles.modified,
-                        coalesce((select modified from arsse_marks join user on user is owner where article is arsse_articles.id),'')
+                        coalesce((select modified from arsse_marks where article is arsse_articles.id and subscription in (select sub from subscribed_feeds)),'')
                     ) as modified_date,
-                    (not exists(select id from arsse_marks join user on user is owner where article is arsse_articles.id)) as to_insert,
-                    ((select read from target_values) is not null and (select read from target_values) is not (coalesce((select read from arsse_marks join user on user is owner where article is arsse_articles.id),0)) and (not exists(select * from requested_articles) or (select max(id) from arsse_editions where article is arsse_articles.id) in (select edition from requested_articles))) as honour_read,
-                    ((select starred from target_values) is not null and (select starred from target_values) is not (coalesce((select starred from arsse_marks join user on user is owner where article is arsse_articles.id),0))) as honour_star
+                    (not exists(select article from arsse_marks where article is arsse_articles.id and subscription in (select sub from subscribed_feeds))) as to_insert,
+                    ((select read from target_values) is not null and (select read from target_values) is not (coalesce((select read from arsse_marks where article is arsse_articles.id and subscription in (select sub from subscribed_feeds)),0)) and (not exists(select * from requested_articles) or (select max(id) from arsse_editions where article is arsse_articles.id) in (select edition from requested_articles))) as honour_read,
+                    ((select starred from target_values) is not null and (select starred from target_values) is not (coalesce((select starred from arsse_marks where article is arsse_articles.id and subscription in (select sub from subscribed_feeds)),0))) as honour_star
                 FROM arsse_articles"
             );
             // common table expression for the affected user
             $q->setCTE("user(user)", "SELECT ?", "str", $user);
             // common table expression with the values to set
             $q->setCTE("target_values(read,starred)", "SELECT ?,?", ["bool","bool"], $values);
-            if($context->edition()) {
-                // if an edition is specified, filter for its previously identified article
-                $q->setWhere("arsse_articles.id is ?", "int", $edition['article']);
-            } else if($context->article()) {
-                // if an article is specified, filter for it (it has already been validated above)
-                $q->setWhere("arsse_articles.id is ?", "int", $context->article);
-            } else if($context->subscription()) {
+            if($context->subscription()) {
                 // if a subscription is specified, make sure it exists
                 $id = $this->subscriptionValidateId($user, $context->subscription)['feed'];
                 // add a basic CTE that will join in only the requested subscription
@@ -822,6 +817,13 @@ class Database {
                 // otherwise add a CTE for all the user's subscriptions
                 $q->setCTE("subscribed_feeds(id,sub)", "SELECT feed,id from arsse_subscriptions join user on user is owner", [], [], "join subscribed_feeds on feed is subscribed_feeds.id");
             }
+            if($context->edition()) {
+                // if an edition is specified, filter for its previously identified article
+                $q->setWhere("arsse_articles.id is ?", "int", $edition['article']);
+            } else if($context->article()) {
+                // if an article is specified, filter for it (it has already been validated above)
+                $q->setWhere("arsse_articles.id is ?", "int", $context->article);
+            }
             if($context->editions()) {
                 // if multiple specific editions have been requested, prepare a CTE to list them and their articles
                 if(!$context->editions) {
@@ -869,7 +871,7 @@ class Database {
                 $q->setWhere("modified_date <= ?", "datetime", $context->notModifiedSince);
             }
             // push the current query onto the CTE stack and execute the query we're actually interested in
-            $q->pushCTE("target_articles(id,edition,modified_date,to_insert,honour_read,honour_star)");
+            $q->pushCTE("target_articles(id,feed,edition,modified_date,to_insert,honour_read,honour_star)");
             $q->setBody($query);
             $out += $this->db->prepare($q->getQuery(), $q->getTypes())->run($q->getValues())->changes();
         }
@@ -878,21 +880,11 @@ class Database {
         return (bool) $out;
     }
 
-    public function articleStarredCount(string $user, array $context = []): int {
+    public function articleStarredCount(string $user): int {
         if(!Arsse::$user->authorize($user, __FUNCTION__)) {
             throw new User\ExceptionAuthz("notAuthorized", ["action" => __FUNCTION__, "user" => $user]);
         }
-        return $this->db->prepare(
-            "WITH RECURSIVE
-                user(user) as (SELECT ?),
-                subscribed_feeds(id,sub) as (SELECT feed,id from arsse_subscriptions join user on user is owner) ".
-            "SELECT count(*) from arsse_marks 
-                join user on user is owner 
-                join arsse_articles on arsse_marks.article is arsse_articles.id
-                join subscribed_feeds on arsse_articles.feed is subscribed_feeds.id
-            where starred is 1", 
-            "str"
-        )->run($user)->getValue();
+        return $this->db->prepare("SELECT count(*) from arsse_marks where starred is 1 and subscription in (select id from arsse_subscriptions where owner is ?)", "str")->run($user)->getValue();
     }
 
     protected function articleValidateId(string $user, int $id): array {
diff --git a/sql/SQLite3/0.sql b/sql/SQLite3/0.sql
index 5acf2484..9a725a77 100644
--- a/sql/SQLite3/0.sql
+++ b/sql/SQLite3/0.sql
@@ -94,13 +94,12 @@ create table arsse_enclosures(
 
 -- users' actions on newsfeed entries
 create table arsse_marks(
-    id integer primary key,
     article integer not null references arsse_articles(id) on delete cascade,
-    owner text not null references arsse_users(id) on delete cascade on update cascade,
+    subscription integer not null references arsse_subscriptions(id) on delete cascade on update cascade,
     read boolean not null default 0,
     starred boolean not null default 0,
     modified text not null default CURRENT_TIMESTAMP,    
-    unique(article,owner)
+    primary key(article,subscription)
 );
 
 -- IDs for specific editions of articles (required for at least NextCloud News)
diff --git a/tests/lib/Database/SeriesArticle.php b/tests/lib/Database/SeriesArticle.php
index cfbc6de9..41486371 100644
--- a/tests/lib/Database/SeriesArticle.php
+++ b/tests/lib/Database/SeriesArticle.php
@@ -80,7 +80,7 @@ trait SeriesArticle {
                 [4,"john.doe@example.com",4,6],
                 [5,"john.doe@example.com",10,5],
                 [6,"jane.doe@example.com",1,null],
-                [7,"jane.doe@example.com",9,null],
+                [7,"jane.doe@example.com",10,null],
                 [8,"john.doe@example.org",11,null],
                 [9,"john.doe@example.org",12,null],
                 [10,"john.doe@example.org",13,null],
@@ -106,7 +106,27 @@ trait SeriesArticle {
                 'title_content_hash' => "str",
                 'modified'           => "datetime",
             ],
-            'rows' => [ // lower IDs are filled by series setup
+            'rows' => [
+                [1,1,null,null,null,null,null,null,null,"","","","2000-01-01T00:00:00Z"],
+                [2,1,null,null,null,null,null,null,null,"","","","2010-01-01T00:00:00Z"],
+                [3,2,null,null,null,null,null,null,null,"","","","2000-01-01T00:00:00Z"],
+                [4,2,null,null,null,null,null,null,null,"","","","2010-01-01T00:00:00Z"],
+                [5,3,null,null,null,null,null,null,null,"","","","2000-01-01T00:00:00Z"],
+                [6,3,null,null,null,null,null,null,null,"","","","2010-01-01T00:00:00Z"],
+                [7,4,null,null,null,null,null,null,null,"","","","2000-01-01T00:00:00Z"],
+                [8,4,null,null,null,null,null,null,null,"","","","2010-01-01T00:00:00Z"],
+                [9,5,null,null,null,null,null,null,null,"","","","2000-01-01T00:00:00Z"],
+                [10,5,null,null,null,null,null,null,null,"","","","2010-01-01T00:00:00Z"],
+                [11,6,null,null,null,null,null,null,null,"","","","2000-01-01T00:00:00Z"],
+                [12,6,null,null,null,null,null,null,null,"","","","2010-01-01T00:00:00Z"],
+                [13,7,null,null,null,null,null,null,null,"","","","2000-01-01T00:00:00Z"],
+                [14,7,null,null,null,null,null,null,null,"","","","2010-01-01T00:00:00Z"],
+                [15,8,null,null,null,null,null,null,null,"","","","2000-01-01T00:00:00Z"],
+                [16,8,null,null,null,null,null,null,null,"","","","2010-01-01T00:00:00Z"],
+                [17,9,null,null,null,null,null,null,null,"","","","2000-01-01T00:00:00Z"],
+                [18,9,null,null,null,null,null,null,null,"","","","2010-01-01T00:00:00Z"],
+                [19,10,null,null,null,null,null,null,null,"","","","2000-01-01T00:00:00Z"],
+                [20,10,null,null,null,null,null,null,null,"","","","2010-01-01T00:00:00Z"],
                 [101,11,'http://example.com/1','Article title 1','','2000-01-01 00:00:00','2000-01-01 00:00:01','<p>Article content 1</p>','e433653cef2e572eee4215fa299a4a5af9137b2cefd6283c85bd69a32915beda','f5cb8bfc1c7396dc9816af212a3e2ac5221585c2a00bf7ccb6aabd95dcfcd6a6','fb0bc8f8cb08913dc5a497db700e327f1d34e4987402687d494a5891f24714d4','18fdd4fa93d693128c43b004399e5c9cea6c261ddfa002518d3669f55d8c2207','2000-01-01 01:00:00'],
                 [102,11,'http://example.com/2','Article title 2','','2000-01-02 00:00:00','2000-01-02 00:00:02','<p>Article content 2</p>','5be8a5a46ecd52ed132191c8d27fb1af6b3d4edc00234c5d9f8f0e10562ed3b7','0e86d2de822a174fe3c44a466953e63ca1f1a58a19cbf475fce0855d4e3d5153','13075894189c47ffcfafd1dfe7fbb539f7c74a69d35a399b3abf8518952714f9','2abd0a8cba83b8214a66c8f0293ba63e467d720540e29ff8ddcdab069d4f1c9e','2000-01-02 02:00:00'],
                 [103,12,'http://example.com/3','Article title 3','','2000-01-03 00:00:00','2000-01-03 00:00:03','<p>Article content 3</p>','31a6594500a48b59fcc8a075ce82b946c9c3c782460d088bd7b8ef3ede97ad92','f74b06b240bd08abf4d3fdfc20dba6a6f6eb8b4f1a00e9a617efd63a87180a4b','b278380e984cefe63f0e412b88ffc9cb0befdfa06fdc00bace1da99a8daff406','ad622b31e739cd3a3f3c788991082cf4d2f7a8773773008e75f0572e58cd373b','2000-01-03 03:00:00'],
@@ -133,7 +153,27 @@ trait SeriesArticle {
                 'id'       => "int",
                 'article'  => "int",
             ],
-            'rows' => [ // lower IDs are filled by series setup
+            'rows' => [
+                [1,1],
+                [2,2],
+                [3,3],
+                [4,4],
+                [5,5],
+                [6,6],
+                [7,7],
+                [8,8],
+                [9,9],
+                [10,10],
+                [11,11],
+                [12,12],
+                [13,13],
+                [14,14],
+                [15,15],
+                [16,16],
+                [17,17],
+                [18,18],
+                [19,19],
+                [20,20],
                 [101,101],
                 [102,102],
                 [103,103],
@@ -149,26 +189,25 @@ trait SeriesArticle {
         ],
         'arsse_marks' => [
             'columns' => [
-                'owner'    => "str",
-                'article'  => "int",
-                'read'     => "bool",
-                'starred'  => "bool",
-                'modified' => "datetime"
+                'subscription' => "int",
+                'article'      => "int",
+                'read'         => "bool",
+                'starred'      => "bool",
+                'modified'     => "datetime"
             ],
             'rows' => [
-                ["john.doe@example.com",  1,1,1,'2000-01-01 00:00:00'],
-                ["john.doe@example.com", 19,1,0,'2000-01-01 00:00:00'],
-                ["john.doe@example.com", 20,0,1,'2010-01-01 00:00:00'],
-                ["jane.doe@example.com", 20,1,0,'2010-01-01 00:00:00'],
-                ["john.doe@example.org",102,1,0,'2000-01-02 02:00:00'],
-                ["john.doe@example.org",103,0,1,'2000-01-03 03:00:00'],
-                ["john.doe@example.org",104,1,1,'2000-01-04 04:00:00'],
-                ["john.doe@example.org",105,0,0,'2000-01-05 05:00:00'],
-                ["john.doe@example.net", 19,0,0,'2017-01-01 00:00:00'],
-                ["john.doe@example.net", 20,1,0,'2017-01-01 00:00:00'],
-                ["john.doe@example.net",  3,0,1,'2017-01-01 00:00:00'],
-                ["john.doe@example.net",  4,1,1,'2017-01-01 00:00:00'],
-                ["john.doe@example.net", 12,0,1,'2017-01-01 00:00:00'], // user is no longer subscribed to this article's feed; the star should not be counted in articleStarredCount
+                [1,   1,1,1,'2000-01-01 00:00:00'],
+                [5,  19,1,0,'2000-01-01 00:00:00'],
+                [5,  20,0,1,'2010-01-01 00:00:00'],
+                [7,  20,1,0,'2010-01-01 00:00:00'],
+                [8, 102,1,0,'2000-01-02 02:00:00'],
+                [9, 103,0,1,'2000-01-03 03:00:00'],
+                [9, 104,1,1,'2000-01-04 04:00:00'],
+                [10,105,0,0,'2000-01-05 05:00:00'],
+                [11, 19,0,0,'2017-01-01 00:00:00'],
+                [11, 20,1,0,'2017-01-01 00:00:00'],
+                [12,  3,0,1,'2017-01-01 00:00:00'],
+                [12,  4,1,1,'2017-01-01 00:00:00'],
             ]
         ],
     ];
@@ -266,14 +305,7 @@ trait SeriesArticle {
     ];
 
     function setUpSeries() {
-        for($a = 0, $b = 1; $b <= 10; $b++) {
-            // add two generic articles per feed, and matching initial editions
-            $this->data['arsse_articles']['rows'][] = [++$a,$b,null,null,null,null,null,null,null,"","","","2000-01-01T00:00:00Z"];
-            $this->data['arsse_editions']['rows'][] = [$a,$a];
-            $this->data['arsse_articles']['rows'][] = [++$a,$b,null,null,null,null,null,null,null,"","","","2010-01-01T00:00:00Z"];
-            $this->data['arsse_editions']['rows'][] = [$a,$a];
-        }
-        $this->checkTables = ['arsse_marks' => ["owner","article","read","starred","modified"],];
+        $this->checkTables = ['arsse_marks' => ["subscription","article","read","starred","modified"],];
         $this->user = "john.doe@example.net";
     }
 
@@ -369,10 +401,10 @@ trait SeriesArticle {
         $state['arsse_marks']['rows'][8][4] = $now;
         $state['arsse_marks']['rows'][10][2] = 1;
         $state['arsse_marks']['rows'][10][4] = $now;
-        $state['arsse_marks']['rows'][] = [$this->user,5,1,0,$now];
-        $state['arsse_marks']['rows'][] = [$this->user,6,1,0,$now];
-        $state['arsse_marks']['rows'][] = [$this->user,7,1,0,$now];
-        $state['arsse_marks']['rows'][] = [$this->user,8,1,0,$now];
+        $state['arsse_marks']['rows'][] = [13,5,1,0,$now];
+        $state['arsse_marks']['rows'][] = [13,6,1,0,$now];
+        $state['arsse_marks']['rows'][] = [14,7,1,0,$now];
+        $state['arsse_marks']['rows'][] = [14,8,1,0,$now];
         $this->compareExpectations($state);
     }
 
@@ -395,10 +427,10 @@ trait SeriesArticle {
         $state['arsse_marks']['rows'][8][4] = $now;
         $state['arsse_marks']['rows'][9][3] = 1;
         $state['arsse_marks']['rows'][9][4] = $now;
-        $state['arsse_marks']['rows'][] = [$this->user,5,0,1,$now];
-        $state['arsse_marks']['rows'][] = [$this->user,6,0,1,$now];
-        $state['arsse_marks']['rows'][] = [$this->user,7,0,1,$now];
-        $state['arsse_marks']['rows'][] = [$this->user,8,0,1,$now];
+        $state['arsse_marks']['rows'][] = [13,5,0,1,$now];
+        $state['arsse_marks']['rows'][] = [13,6,0,1,$now];
+        $state['arsse_marks']['rows'][] = [14,7,0,1,$now];
+        $state['arsse_marks']['rows'][] = [14,8,0,1,$now];
         $this->compareExpectations($state);
     }
 
@@ -427,10 +459,10 @@ trait SeriesArticle {
         $state['arsse_marks']['rows'][9][4] = $now;
         $state['arsse_marks']['rows'][10][2] = 1;
         $state['arsse_marks']['rows'][10][4] = $now;
-        $state['arsse_marks']['rows'][] = [$this->user,5,1,1,$now];
-        $state['arsse_marks']['rows'][] = [$this->user,6,1,1,$now];
-        $state['arsse_marks']['rows'][] = [$this->user,7,1,1,$now];
-        $state['arsse_marks']['rows'][] = [$this->user,8,1,1,$now];
+        $state['arsse_marks']['rows'][] = [13,5,1,1,$now];
+        $state['arsse_marks']['rows'][] = [13,6,1,1,$now];
+        $state['arsse_marks']['rows'][] = [14,7,1,1,$now];
+        $state['arsse_marks']['rows'][] = [14,8,1,1,$now];
         $this->compareExpectations($state);
     }
 
@@ -445,10 +477,10 @@ trait SeriesArticle {
         $state['arsse_marks']['rows'][9][4] = $now;
         $state['arsse_marks']['rows'][11][2] = 0;
         $state['arsse_marks']['rows'][11][4] = $now;
-        $state['arsse_marks']['rows'][] = [$this->user,5,0,1,$now];
-        $state['arsse_marks']['rows'][] = [$this->user,6,0,1,$now];
-        $state['arsse_marks']['rows'][] = [$this->user,7,0,1,$now];
-        $state['arsse_marks']['rows'][] = [$this->user,8,0,1,$now];
+        $state['arsse_marks']['rows'][] = [13,5,0,1,$now];
+        $state['arsse_marks']['rows'][] = [13,6,0,1,$now];
+        $state['arsse_marks']['rows'][] = [14,7,0,1,$now];
+        $state['arsse_marks']['rows'][] = [14,8,0,1,$now];
         $this->compareExpectations($state);
     }
 
@@ -463,10 +495,10 @@ trait SeriesArticle {
         $state['arsse_marks']['rows'][10][4] = $now;
         $state['arsse_marks']['rows'][11][3] = 0;
         $state['arsse_marks']['rows'][11][4] = $now;
-        $state['arsse_marks']['rows'][] = [$this->user,5,1,0,$now];
-        $state['arsse_marks']['rows'][] = [$this->user,6,1,0,$now];
-        $state['arsse_marks']['rows'][] = [$this->user,7,1,0,$now];
-        $state['arsse_marks']['rows'][] = [$this->user,8,1,0,$now];
+        $state['arsse_marks']['rows'][] = [13,5,1,0,$now];
+        $state['arsse_marks']['rows'][] = [13,6,1,0,$now];
+        $state['arsse_marks']['rows'][] = [14,7,1,0,$now];
+        $state['arsse_marks']['rows'][] = [14,8,1,0,$now];
         $this->compareExpectations($state);
     }
 
@@ -474,10 +506,10 @@ trait SeriesArticle {
         Arsse::$db->articleMark($this->user, ['read'=>true], (new Context)->folder(7));
         $now = Date::transform(time(), "sql");
         $state = $this->primeExpectations($this->data, $this->checkTables);
-        $state['arsse_marks']['rows'][] = [$this->user,5,1,0,$now];
-        $state['arsse_marks']['rows'][] = [$this->user,6,1,0,$now];
-        $state['arsse_marks']['rows'][] = [$this->user,7,1,0,$now];
-        $state['arsse_marks']['rows'][] = [$this->user,8,1,0,$now];
+        $state['arsse_marks']['rows'][] = [13,5,1,0,$now];
+        $state['arsse_marks']['rows'][] = [13,6,1,0,$now];
+        $state['arsse_marks']['rows'][] = [14,7,1,0,$now];
+        $state['arsse_marks']['rows'][] = [14,8,1,0,$now];
         $this->compareExpectations($state);
     }
 
@@ -485,8 +517,8 @@ trait SeriesArticle {
         Arsse::$db->articleMark($this->user, ['read'=>true], (new Context)->folder(8));
         $now = Date::transform(time(), "sql");
         $state = $this->primeExpectations($this->data, $this->checkTables);
-        $state['arsse_marks']['rows'][] = [$this->user,5,1,0,$now];
-        $state['arsse_marks']['rows'][] = [$this->user,6,1,0,$now];
+        $state['arsse_marks']['rows'][] = [13,5,1,0,$now];
+        $state['arsse_marks']['rows'][] = [13,6,1,0,$now];
         $this->compareExpectations($state);
     }
 
@@ -499,8 +531,8 @@ trait SeriesArticle {
         Arsse::$db->articleMark($this->user, ['read'=>true], (new Context)->subscription(13));
         $now = Date::transform(time(), "sql");
         $state = $this->primeExpectations($this->data, $this->checkTables);
-        $state['arsse_marks']['rows'][] = [$this->user,5,1,0,$now];
-        $state['arsse_marks']['rows'][] = [$this->user,6,1,0,$now];
+        $state['arsse_marks']['rows'][] = [13,5,1,0,$now];
+        $state['arsse_marks']['rows'][] = [13,6,1,0,$now];
         $this->compareExpectations($state);
     }
 
@@ -524,7 +556,7 @@ trait SeriesArticle {
         $state = $this->primeExpectations($this->data, $this->checkTables);
         $state['arsse_marks']['rows'][9][3] = 1;
         $state['arsse_marks']['rows'][9][4] = $now;
-        $state['arsse_marks']['rows'][] = [$this->user,7,0,1,$now];
+        $state['arsse_marks']['rows'][] = [14,7,0,1,$now];
         $this->compareExpectations($state);
     }
 
@@ -537,7 +569,7 @@ trait SeriesArticle {
         $state['arsse_marks']['rows'][9][4] = $now;
         $state['arsse_marks']['rows'][11][2] = 0;
         $state['arsse_marks']['rows'][11][4] = $now;
-        $state['arsse_marks']['rows'][] = [$this->user,7,0,1,$now];
+        $state['arsse_marks']['rows'][] = [14,7,0,1,$now];
         $this->compareExpectations($state);
     }
 
@@ -571,7 +603,7 @@ trait SeriesArticle {
         $state = $this->primeExpectations($this->data, $this->checkTables);
         $state['arsse_marks']['rows'][9][3] = 1;
         $state['arsse_marks']['rows'][9][4] = $now;
-        $state['arsse_marks']['rows'][] = [$this->user,7,0,1,$now];
+        $state['arsse_marks']['rows'][] = [14,7,0,1,$now];
         $this->compareExpectations($state);
     }
 
@@ -603,7 +635,7 @@ trait SeriesArticle {
         $state['arsse_marks']['rows'][9][4] = $now;
         $state['arsse_marks']['rows'][11][2] = 0;
         $state['arsse_marks']['rows'][11][4] = $now;
-        $state['arsse_marks']['rows'][] = [$this->user,7,0,1,$now];
+        $state['arsse_marks']['rows'][] = [14,7,0,1,$now];
         $this->compareExpectations($state);
     }
 
@@ -669,10 +701,10 @@ trait SeriesArticle {
         $state = $this->primeExpectations($this->data, $this->checkTables);
         $state['arsse_marks']['rows'][8][3] = 1;
         $state['arsse_marks']['rows'][8][4] = $now;
-        $state['arsse_marks']['rows'][] = [$this->user,5,0,1,$now];
-        $state['arsse_marks']['rows'][] = [$this->user,6,0,1,$now];
-        $state['arsse_marks']['rows'][] = [$this->user,7,0,1,$now];
-        $state['arsse_marks']['rows'][] = [$this->user,8,0,1,$now];
+        $state['arsse_marks']['rows'][] = [13,5,0,1,$now];
+        $state['arsse_marks']['rows'][] = [13,6,0,1,$now];
+        $state['arsse_marks']['rows'][] = [14,7,0,1,$now];
+        $state['arsse_marks']['rows'][] = [14,8,0,1,$now];
         $this->compareExpectations($state);
     }
 
@@ -691,8 +723,8 @@ trait SeriesArticle {
         Arsse::$db->articleMark($this->user, ['starred'=>true], (new Context)->notModifiedSince('2000-01-01T00:00:00Z'));
         $now = Date::transform(time(), "sql");
         $state = $this->primeExpectations($this->data, $this->checkTables);
-        $state['arsse_marks']['rows'][] = [$this->user,5,0,1,$now];
-        $state['arsse_marks']['rows'][] = [$this->user,7,0,1,$now];
+        $state['arsse_marks']['rows'][] = [13,5,0,1,$now];
+        $state['arsse_marks']['rows'][] = [14,7,0,1,$now];
         $this->compareExpectations($state);
     }
 
diff --git a/tests/lib/Database/SeriesFeed.php b/tests/lib/Database/SeriesFeed.php
index ebe5795e..7132151c 100644
--- a/tests/lib/Database/SeriesFeed.php
+++ b/tests/lib/Database/SeriesFeed.php
@@ -75,18 +75,21 @@ trait SeriesFeed {
             ],
             'arsse_subscriptions' => [
                 'columns' => [
+                    'id'    => "int",
                     'owner' => "str",
-                    'feed' => "int",
+                    'feed'  => "int",
                 ],
                 'rows' => [
                     // the first five feeds need at least one subscription so they are not involved in the cleanup test
-                    ['john.doe@example.com',1],
-                    ['john.doe@example.com',2],
-                    ['john.doe@example.com',3],
-                    ['john.doe@example.com',4],
-                    ['john.doe@example.com',5],
+                    [1,'john.doe@example.com',1],
+                    [2,'john.doe@example.com',2],
+                    [3,'john.doe@example.com',3],
+                    [4,'john.doe@example.com',4],
+                    [5,'john.doe@example.com',5],
+                    // Jane also needs a subscription to the first feed, for marks
+                    [6,'jane.doe@example.com',1],
                     // one feed previously marked for deletion has a subscription again, and so should not be deleted
-                    ['jane.doe@example.com',6],
+                    [7,'jane.doe@example.com',6],
                 ]
             ],
             'arsse_articles' => [
@@ -131,22 +134,23 @@ trait SeriesFeed {
             ],
             'arsse_marks' => [
                 'columns' => [
-                    'id'      => "int",
-                    'article' => "int",
-                    'owner'   => "str",
-                    'read'    => "bool",
-                    'starred' => "bool",
-                    'modified' => "datetime",
+                    'article'      => "int",
+                    'subscription' => "int",
+                    'read'         => "bool",
+                    'starred'      => "bool",
+                    'modified'     => "datetime",
                 ],
                 'rows' => [
-                    [1,1,"jane.doe@example.com",1,0,$past],
-                    [2,2,"jane.doe@example.com",1,0,$past],
-                    [3,3,"jane.doe@example.com",1,1,$past],
-                    [4,4,"jane.doe@example.com",1,0,$past],
-                    [5,5,"jane.doe@example.com",1,1,$past],
-                    [9, 1,"john.doe@example.com",1,0,$past],
-                    [10,3,"john.doe@example.com",1,0,$past],
-                    [11,4,"john.doe@example.com",0,1,$past],
+                    // Jane's marks
+                    [1,6,1,0,$past],
+                    [2,6,1,0,$past],
+                    [3,6,1,1,$past],
+                    [4,6,1,0,$past],
+                    [5,6,1,1,$past],
+                    // John's marks
+                    [1,1,1,0,$past],
+                    [3,1,1,0,$past],
+                    [4,1,0,1,$past],
                 ]
             ],
             'arsse_enclosures' => [
@@ -193,7 +197,7 @@ trait SeriesFeed {
         $state = $this->primeExpectations($this->data, [
             'arsse_articles' => ["id", "feed","url","title","author","published","edited","content","guid","url_title_hash","url_content_hash","title_content_hash","modified"],
             'arsse_editions' => ["id","article","modified"],
-            'arsse_marks'    => ["id","article","read","starred","modified"],
+            'arsse_marks'    => ["subscription","article","read","starred","modified"],
         ]);
         $state['arsse_articles']['rows'][2] = [3,1,'http://example.com/3','Article title 3 (updated)','','2000-01-03 00:00:00','2000-01-03 00:00:00','<p>Article content 3</p>','31a6594500a48b59fcc8a075ce82b946c9c3c782460d088bd7b8ef3ede97ad92','6cc99be662ef3486fef35a890123f18d74c29a32d714802d743c5b4ef713315a','b278380e984cefe63f0e412b88ffc9cb0befdfa06fdc00bace1da99a8daff406','d5faccc13bf8267850a1e8e61f95950a0f34167df2c8c58011c0aaa6367026ac',$now];
         $state['arsse_articles']['rows'][3] = [4,1,'http://example.com/4','Article title 4','','2000-01-04 00:00:00','2000-01-04 00:00:01','<p>Article content 4</p>','804e517d623390e71497982c77cf6823180342ebcd2e7d5e32da1e55b09dd180','f3615c7f16336d3ea242d35cf3fc17dbc4ee3afb78376bf49da2dd7a5a25dec8','f11c2b4046f207579aeb9c69a8c20ca5461cef49756ccfa5ba5e2344266da3b3','ab2da63276acce431250b18d3d49b988b226a99c7faadf275c90b751aee05be9',$now];
@@ -203,9 +207,9 @@ trait SeriesFeed {
             [7,3,$now],
             [8,4,$now],
         ]);
-        $state['arsse_marks']['rows'][2] = [3,3,0,1,$now];
-        $state['arsse_marks']['rows'][3] = [4,4,0,0,$now];
-        $state['arsse_marks']['rows'][6] = [10,3,0,0,$now];
+        $state['arsse_marks']['rows'][2] = [6,3,0,1,$now];
+        $state['arsse_marks']['rows'][3] = [6,4,0,0,$now];
+        $state['arsse_marks']['rows'][6] = [1,3,0,0,$now];
         $this->compareExpectations($state);
         // update a valid feed which previously had an error
         Arsse::$db->feedUpdate(2);
diff --git a/tests/lib/Database/SeriesSubscription.php b/tests/lib/Database/SeriesSubscription.php
index 74f70f86..e51cc0ff 100644
--- a/tests/lib/Database/SeriesSubscription.php
+++ b/tests/lib/Database/SeriesSubscription.php
@@ -85,24 +85,20 @@ trait SeriesSubscription {
         ],
         'arsse_marks' => [
             'columns' => [
-                'id'      => "int",
-                'article' => "int",
-                'owner'   => "str",
-                'read'    => "bool",
-                'starred' => "bool",
+                'article'      => "int",
+                'subscription' => "int",
+                'read'         => "bool",
+                'starred'      => "bool",
             ],
             'rows' => [
-                [1,1,"jane.doe@example.com",1,0],
-                [2,2,"jane.doe@example.com",1,0],
-                [3,3,"jane.doe@example.com",1,0],
-                [4,4,"jane.doe@example.com",1,0],
-                [5,5,"jane.doe@example.com",1,0],
-                [6,6,"jane.doe@example.com",1,0],
-                [7,7,"jane.doe@example.com",1,0],
-                [8,8,"jane.doe@example.com",1,0],
-                [9, 1,"john.doe@example.com",1,0],
-                [10,7,"john.doe@example.com",1,0],
-                [11,8,"john.doe@example.com",0,0],
+                [1,2,1,0],
+                [2,2,1,0],
+                [3,2,1,0],
+                [4,2,1,0],
+                [5,2,1,0],
+                [1,1,1,0],
+                [7,3,1,0],
+                [8,3,0,0],
             ]
         ],
     ];