Merge pull request #1780 from nextcloud/fix/noid/steam-action-fix-1

avoid race condition on stream action
pull/1783/head
Maxence Lange 2023-06-14 08:40:28 -01:00 zatwierdzone przez GitHub
commit 086d3c3924
Nie znaleziono w bazie danych klucza dla tego podpisu
ID klucza GPG: 4AEE18F83AFDEB23
8 zmienionych plików z 65 dodań i 36 usunięć

Wyświetl plik

@ -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'));

Wyświetl plik

@ -385,7 +385,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');
}
@ -403,7 +405,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'));

Wyświetl plik

@ -186,6 +186,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
*

Wyświetl plik

@ -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);
// 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 => ''
};
$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));
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();
}

Wyświetl plik

@ -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';

Wyświetl plik

@ -232,6 +232,8 @@ class StreamRequestBuilder extends CoreRequestBuilder {
}
$action = $this->parseStreamActionsLeftJoin($data);
$item->setAction($action);
if ($item->hasCache()) {
$cache = $item->getCache();
try {
@ -244,7 +246,7 @@ class StreamRequestBuilder extends CoreRequestBuilder {
}
}
$item->setAction($action);
if ($item->getType() === Announce::TYPE) {
$item->setAttributedTo($this->get('following_actor_id', $data, ''));
}

Wyświetl plik

@ -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 {

Wyświetl plik

@ -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 {