summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorMaxence Lange <maxence@artificial-owl.com>2023-06-13 21:31:37 -0100
committerMaxence Lange <maxence@artificial-owl.com>2023-06-13 21:31:45 -0100
commit0f2c5816e00f1c774191472487df7b006c417f29 (patch)
tree1fd0c273df2351905d615eb85785fcfe47b58d2f
parent18790fb91484d77599285ee5510e2d7d50d140c9 (diff)
avoid race condition on stream action
Signed-off-by: Maxence Lange <maxence@artificial-owl.com>
-rw-r--r--lib/Db/CoreRequestBuilder.php4
-rw-r--r--lib/Db/SocialCrossQueryBuilder.php8
-rw-r--r--lib/Db/SocialLimitsQueryBuilder.php10
-rw-r--r--lib/Db/StreamActionsRequest.php35
-rw-r--r--lib/Db/StreamActionsRequestBuilder.php5
-rw-r--r--lib/Db/StreamRequestBuilder.php4
-rw-r--r--lib/Model/ActivityPub/Stream.php8
-rw-r--r--lib/Model/StreamAction.php29
8 files changed, 66 insertions, 37 deletions
diff --git a/lib/Db/CoreRequestBuilder.php b/lib/Db/CoreRequestBuilder.php
index da152dcc..f29f2fc7 100644
--- a/lib/Db/CoreRequestBuilder.php
+++ b/lib/Db/CoreRequestBuilder.php
@@ -1002,7 +1002,9 @@ class CoreRequestBuilder {
$qb->selectAlias('sa.id', 'streamaction_id')
->selectAlias('sa.actor_id', 'streamaction_actor_id')
->selectAlias('sa.stream_id', 'streamaction_stream_id')
- ->selectAlias('sa.values', 'streamaction_values');
+ ->selectAlias('sa.liked', 'streamaction_liked')
+ ->selectAlias('sa.boosted', 'streamaction_boosted')
+ ->selectAlias('sa.replied', 'streamaction_replied');
$orX = $expr->orX();
$orX->add($expr->eq('sa.stream_id_prim', $pf . '.id_prim'));
diff --git a/lib/Db/SocialCrossQueryBuilder.php b/lib/Db/SocialCrossQueryBuilder.php
index 41e33fb9..a90fb7b0 100644
--- a/lib/Db/SocialCrossQueryBuilder.php
+++ b/lib/Db/SocialCrossQueryBuilder.php
@@ -374,7 +374,9 @@ class SocialCrossQueryBuilder extends SocialCoreQueryBuilder {
$this->selectAlias('sa.id', 'streamaction_id')
->selectAlias('sa.actor_id', 'streamaction_actor_id')
->selectAlias('sa.stream_id', 'streamaction_stream_id')
- ->selectAlias('sa.values', 'streamaction_values');
+ ->selectAlias('sa.liked', 'streamaction_liked')
+ ->selectAlias('sa.boosted', 'streamaction_boosted')
+ ->selectAlias('sa.replied', 'streamaction_replied');
}
@@ -392,7 +394,9 @@ class SocialCrossQueryBuilder extends SocialCoreQueryBuilder {
$this->selectAlias($alias . '.id', 'streamaction_id')
->selectAlias($alias . '.actor_id', 'streamaction_actor_id')
->selectAlias($alias . '.stream_id', 'streamaction_stream_id')
- ->selectAlias($alias . '.values', 'streamaction_values');
+ ->selectAlias($alias . '.liked', 'streamaction_liked')
+ ->selectAlias($alias . '.boosted', 'streamaction_boosted')
+ ->selectAlias($alias . '.replied', 'streamaction_replied');
$orX = $expr->orX();
$orX->add($expr->eq($alias . '.stream_id_prim', $pf . '.id_prim'));
diff --git a/lib/Db/SocialLimitsQueryBuilder.php b/lib/Db/SocialLimitsQueryBuilder.php
index 283fffbb..f543294a 100644
--- a/lib/Db/SocialLimitsQueryBuilder.php
+++ b/lib/Db/SocialLimitsQueryBuilder.php
@@ -187,6 +187,16 @@ class SocialLimitsQueryBuilder extends SocialCrossQueryBuilder {
/**
+ * @param string $streamId
+ * @param string $alias
+ *
+ * @return void
+ */
+ public function limitToStreamIdPrim(string $streamId, string $alias = '') {
+ $this->limitToDBField('stream_id_prim', $streamId, false, $alias);
+ }
+
+ /**
* Limit the request to the FollowId
*
* @param string $followId
diff --git a/lib/Db/StreamActionsRequest.php b/lib/Db/StreamActionsRequest.php
index f0a31fef..fa77bd25 100644
--- a/lib/Db/StreamActionsRequest.php
+++ b/lib/Db/StreamActionsRequest.php
@@ -55,11 +55,6 @@ class StreamActionsRequest extends StreamActionsRequestBuilder {
->setValue('actor_id_prim', $qb->createNamedParameter($qb->prim($action->getActorId())))
->setValue('stream_id', $qb->createNamedParameter($action->getStreamId()))
->setValue('stream_id_prim', $qb->createNamedParameter($qb->prim($action->getStreamId())))
- ->setValue(
- 'values', $qb->createNamedParameter(
- json_encode($values, JSON_UNESCAPED_SLASHES)
- )
- )
->setValue('liked', $qb->createNamedParameter(($liked) ? 1 : 0))
->setValue('boosted', $qb->createNamedParameter(($boosted) ? 1 : 0))
->setValue('replied', $qb->createNamedParameter(($replied) ? 1 : 0));
@@ -68,24 +63,26 @@ class StreamActionsRequest extends StreamActionsRequestBuilder {
}
- /**
- * Create a new Queue in the database.
- */
public function update(StreamAction $action): int {
$qb = $this->getStreamActionUpdateSql();
- $values = $action->getValues();
- $liked = $this->getBool(StreamAction::LIKED, $values, false);
- $boosted = $this->getBool(StreamAction::BOOSTED, $values, false);
- $replied = $this->getBool(StreamAction::REPLIED, $values, false);
-
- $qb->set('values', $qb->createNamedParameter(json_encode($values, JSON_UNESCAPED_SLASHES)))
- ->set('liked', $qb->createNamedParameter(($liked) ? 1 : 0))
- ->set('boosted', $qb->createNamedParameter(($boosted) ? 1 : 0))
- ->set('replied', $qb->createNamedParameter(($replied) ? 1 : 0));
+ // update entry/field in database, based only on affected action
+ // to avoid race condition on 2 different actions
+ foreach($action->getAffected() as $entry) {
+ $field = match ($entry) {
+ StreamAction::LIKED => 'liked',
+ StreamAction::BOOSTED => 'boosted',
+ StreamAction::REPLIED => 'replied',
+ default => ''
+ };
+
+ if ($field !== '') {
+ $qb->set($field, $qb->createNamedParameter(($action->getValueBool($entry)) ? 1 : 0));
+ }
+ }
- $this->limitToActorId($qb, $action->getActorId());
- $this->limitToStreamId($qb, $action->getStreamId());
+ $qb->limitToActorIdPrim($qb->prim($action->getActorId()));
+ $qb->limitToStreamIdPrim($qb->prim($action->getStreamId()));
return $qb->executeStatement();
}
diff --git a/lib/Db/StreamActionsRequestBuilder.php b/lib/Db/StreamActionsRequestBuilder.php
index dc7159a0..f76a51f3 100644
--- a/lib/Db/StreamActionsRequestBuilder.php
+++ b/lib/Db/StreamActionsRequestBuilder.php
@@ -77,7 +77,10 @@ class StreamActionsRequestBuilder extends CoreRequestBuilder {
$qb = $this->getQueryBuilder();
/** @noinspection PhpMethodParametersCountMismatchInspection */
- $qb->select('sa.id', 'sa.actor_id', 'sa.stream_id', 'sa.values')
+ $qb->select(
+ 'sa.id', 'sa.actor_id', 'sa.stream_id',
+ 'sa.boosted', 'sa.liked', 'sa.replied'
+ )
->from(self::TABLE_STREAM_ACTIONS, 'sa');
$this->defaultSelectAlias = 'sa';
diff --git a/lib/Db/StreamRequestBuilder.php b/lib/Db/StreamRequestBuilder.php
index 620c07c8..32d47f4a 100644
--- a/lib/Db/StreamRequestBuilder.php
+++ b/lib/Db/StreamRequestBuilder.php
@@ -231,6 +231,8 @@ class StreamRequestBuilder extends CoreRequestBuilder {
}
$action = $this->parseStreamActionsLeftJoin($data);
+ $item->setAction($action);
+
if ($item->hasCache()) {
$cache = $item->getCache();
try {
@@ -243,7 +245,7 @@ class StreamRequestBuilder extends CoreRequestBuilder {
}
}
- $item->setAction($action);
+
if ($item->getType() === Announce::TYPE) {
$item->setAttributedTo($this->get('following_actor_id', $data, ''));
}
diff --git a/lib/Model/ActivityPub/Stream.php b/lib/Model/ActivityPub/Stream.php
index d4df8866..fbcaf200 100644
--- a/lib/Model/ActivityPub/Stream.php
+++ b/lib/Model/ActivityPub/Stream.php
@@ -529,12 +529,8 @@ class Stream extends ACore implements IQueryRow, JsonSerializable {
$this->setLanguage($this->get('language', $data));
$action = new StreamAction();
- $action->setValues(
- [
- StreamAction::LIKED => $this->getBool('favourited', $data),
- StreamAction::BOOSTED => $this->getBool('reblogged', $data)
- ]
- );
+ $action->updateValueBool(StreamAction::LIKED, $this->getBool('favourited', $data));
+ $action->updateValueBool(StreamAction::BOOSTED, $this->getBool('reblogged', $data));
$this->setAction($action);
try {
diff --git a/lib/Model/StreamAction.php b/lib/Model/StreamAction.php
index 0018c74d..57a26449 100644
--- a/lib/Model/StreamAction.php
+++ b/lib/Model/StreamAction.php
@@ -44,7 +44,6 @@ class StreamAction implements JsonSerializable {
use TArrayTools;
use TStringTools;
-
public const LIKED = 'liked';
public const BOOSTED = 'boosted';
public const REPLIED = 'replied';
@@ -53,7 +52,12 @@ class StreamAction implements JsonSerializable {
private string $actorId = '';
private string $streamId = '';
private array $values = [];
-
+ private array $affected = [];
+ private array $accepted = [
+ self::LIKED,
+ self::BOOSTED,
+ self::REPLIED
+ ];
/**
* StreamAction constructor.
@@ -95,14 +99,23 @@ class StreamAction implements JsonSerializable {
public function updateValue(string $key, string $value): void {
$this->values[$key] = $value;
+ if (in_array($key, $this->accepted) && !in_array($key, $this->affected)) {
+ $this->affected[] = $key;
+ }
}
public function updateValueInt(string $key, int $value): void {
$this->values[$key] = $value;
+ if (in_array($key, $this->accepted) && !in_array($key, $this->affected)) {
+ $this->affected[] = $key;
+ }
}
public function updateValueBool(string $key, bool $value): void {
$this->values[$key] = $value;
+ if (in_array($key, $this->accepted) && !in_array($key, $this->affected)) {
+ $this->affected[] = $key;
+ }
}
public function hasValue(string $key): bool {
@@ -125,10 +138,8 @@ class StreamAction implements JsonSerializable {
return $this->values;
}
- public function setValues(array $values): StreamAction {
- $this->values = $values;
-
- return $this;
+ public function getAffected(): array {
+ return $this->affected;
}
public function setDefaultValues(array $default): StreamAction {
@@ -146,7 +157,11 @@ class StreamAction implements JsonSerializable {
$this->setId($this->getInt('id', $data, 0));
$this->setActorId($this->get('actor_id', $data, ''));
$this->setStreamId($this->get('stream_id', $data, ''));
- $this->setValues($this->getArray('values', $data, []));
+ $this->values = [
+ self::LIKED => $this->getBool('liked', $data),
+ self::BOOSTED => $this->getBool('boosted', $data),
+ self::REPLIED => $this->getBool('replied', $data)
+ ];
}
public function jsonSerialize(): array {