aboutsummaryrefslogtreecommitdiffhomepage
path: root/server/models
diff options
context:
space:
mode:
authorChocobozzz <me@florianbigard.com>2018-01-11 09:35:50 +0100
committerChocobozzz <me@florianbigard.com>2018-01-11 09:35:50 +0100
commit60650c77c8a2a98e92d869b237ae4900f369a8fc (patch)
tree7304a7591b5b23b99d219c4d06c6bd5c4c58c1c2 /server/models
parent7ae71355c40e9065f83d3fc77b6750d1929ac201 (diff)
downloadPeerTube-60650c77c8a2a98e92d869b237ae4900f369a8fc.tar.gz
PeerTube-60650c77c8a2a98e92d869b237ae4900f369a8fc.tar.zst
PeerTube-60650c77c8a2a98e92d869b237ae4900f369a8fc.zip
Add scores to follows and remove bad ones
Diffstat (limited to 'server/models')
-rw-r--r--server/models/account/account.ts1
-rw-r--r--server/models/activitypub/actor-follow.ts79
-rw-r--r--server/models/activitypub/actor.ts13
-rw-r--r--server/models/server/server.ts85
-rw-r--r--server/models/video/video-channel.ts2
5 files changed, 84 insertions, 96 deletions
diff --git a/server/models/account/account.ts b/server/models/account/account.ts
index 47336d1e0..f81c50180 100644
--- a/server/models/account/account.ts
+++ b/server/models/account/account.ts
@@ -179,7 +179,6 @@ export class AccountModel extends Model<AccountModel> {
179 const actor = this.Actor.toFormattedJSON() 179 const actor = this.Actor.toFormattedJSON()
180 const account = { 180 const account = {
181 id: this.id, 181 id: this.id,
182 name: this.Actor.preferredUsername,
183 displayName: this.name, 182 displayName: this.name,
184 createdAt: this.createdAt, 183 createdAt: this.createdAt,
185 updatedAt: this.updatedAt 184 updatedAt: this.updatedAt
diff --git a/server/models/activitypub/actor-follow.ts b/server/models/activitypub/actor-follow.ts
index 5fcc3449d..78a65a0ff 100644
--- a/server/models/activitypub/actor-follow.ts
+++ b/server/models/activitypub/actor-follow.ts
@@ -1,8 +1,14 @@
1import * as Bluebird from 'bluebird' 1import * as Bluebird from 'bluebird'
2import { values } from 'lodash' 2import { values } from 'lodash'
3import * as Sequelize from 'sequelize' 3import * as Sequelize from 'sequelize'
4import { AllowNull, BelongsTo, Column, CreatedAt, DataType, ForeignKey, Model, Table, UpdatedAt } from 'sequelize-typescript' 4import {
5 AllowNull, BelongsTo, Column, CreatedAt, DataType, Default, ForeignKey, IsInt, Max, Model, Table,
6 UpdatedAt
7} from 'sequelize-typescript'
5import { FollowState } from '../../../shared/models/actors' 8import { FollowState } from '../../../shared/models/actors'
9import { AccountFollow } from '../../../shared/models/actors/follow.model'
10import { logger } from '../../helpers/logger'
11import { ACTOR_FOLLOW_SCORE } from '../../initializers'
6import { FOLLOW_STATES } from '../../initializers/constants' 12import { FOLLOW_STATES } from '../../initializers/constants'
7import { ServerModel } from '../server/server' 13import { ServerModel } from '../server/server'
8import { getSort } from '../utils' 14import { getSort } from '../utils'
@@ -20,6 +26,9 @@ import { ActorModel } from './actor'
20 { 26 {
21 fields: [ 'actorId', 'targetActorId' ], 27 fields: [ 'actorId', 'targetActorId' ],
22 unique: true 28 unique: true
29 },
30 {
31 fields: [ 'score' ]
23 } 32 }
24 ] 33 ]
25}) 34})
@@ -29,6 +38,13 @@ export class ActorFollowModel extends Model<ActorFollowModel> {
29 @Column(DataType.ENUM(values(FOLLOW_STATES))) 38 @Column(DataType.ENUM(values(FOLLOW_STATES)))
30 state: FollowState 39 state: FollowState
31 40
41 @AllowNull(false)
42 @Default(ACTOR_FOLLOW_SCORE.BASE)
43 @IsInt
44 @Max(ACTOR_FOLLOW_SCORE.MAX)
45 @Column
46 score: number
47
32 @CreatedAt 48 @CreatedAt
33 createdAt: Date 49 createdAt: Date
34 50
@@ -63,6 +79,34 @@ export class ActorFollowModel extends Model<ActorFollowModel> {
63 }) 79 })
64 ActorFollowing: ActorModel 80 ActorFollowing: ActorModel
65 81
82 // Remove actor follows with a score of 0 (too many requests where they were unreachable)
83 static async removeBadActorFollows () {
84 const actorFollows = await ActorFollowModel.listBadActorFollows()
85
86 const actorFollowsRemovePromises = actorFollows.map(actorFollow => actorFollow.destroy())
87 await Promise.all(actorFollowsRemovePromises)
88
89 const numberOfActorFollowsRemoved = actorFollows.length
90
91 if (numberOfActorFollowsRemoved) logger.info('Removed bad %d actor follows.', numberOfActorFollowsRemoved)
92 }
93
94 static updateActorFollowsScoreAndRemoveBadOnes (goodInboxes: string[], badInboxes: string[], t: Sequelize.Transaction) {
95 if (goodInboxes.length === 0 && badInboxes.length === 0) return
96
97 logger.info('Updating %d good actor follows and %d bad actor follows scores.', goodInboxes.length, badInboxes.length)
98
99 if (goodInboxes.length !== 0) {
100 ActorFollowModel.incrementScores(goodInboxes, ACTOR_FOLLOW_SCORE.BONUS, t)
101 .catch(err => logger.error('Cannot increment scores of good actor follows.', err))
102 }
103
104 if (badInboxes.length !== 0) {
105 ActorFollowModel.incrementScores(badInboxes, ACTOR_FOLLOW_SCORE.PENALTY, t)
106 .catch(err => logger.error('Cannot decrement scores of bad actor follows.', err))
107 }
108 }
109
66 static loadByActorAndTarget (actorId: number, targetActorId: number, t?: Sequelize.Transaction) { 110 static loadByActorAndTarget (actorId: number, targetActorId: number, t?: Sequelize.Transaction) {
67 const query = { 111 const query = {
68 where: { 112 where: {
@@ -260,7 +304,37 @@ export class ActorFollowModel extends Model<ActorFollowModel> {
260 } 304 }
261 } 305 }
262 306
263 toFormattedJSON () { 307 private static incrementScores (inboxUrls: string[], value: number, t: Sequelize.Transaction) {
308 const inboxUrlsString = inboxUrls.map(url => `'${url}'`).join(',')
309
310 const query = 'UPDATE "actorFollow" SET "score" = "score" +' + value + ' ' +
311 'WHERE id IN (' +
312 'SELECT "actorFollow"."id" FROM "actorFollow" ' +
313 'INNER JOIN "actor" ON "actor"."id" = "actorFollow"."actorId" ' +
314 'WHERE "actor"."inboxUrl" IN (' + inboxUrlsString + ') OR "actor"."sharedInboxUrl" IN (' + inboxUrlsString + ')' +
315 ')'
316
317 const options = {
318 type: Sequelize.QueryTypes.BULKUPDATE,
319 transaction: t
320 }
321
322 return ActorFollowModel.sequelize.query(query, options)
323 }
324
325 private static listBadActorFollows () {
326 const query = {
327 where: {
328 score: {
329 [Sequelize.Op.lte]: 0
330 }
331 }
332 }
333
334 return ActorFollowModel.findAll(query)
335 }
336
337 toFormattedJSON (): AccountFollow {
264 const follower = this.ActorFollower.toFormattedJSON() 338 const follower = this.ActorFollower.toFormattedJSON()
265 const following = this.ActorFollowing.toFormattedJSON() 339 const following = this.ActorFollowing.toFormattedJSON()
266 340
@@ -268,6 +342,7 @@ export class ActorFollowModel extends Model<ActorFollowModel> {
268 id: this.id, 342 id: this.id,
269 follower, 343 follower,
270 following, 344 following,
345 score: this.score,
271 state: this.state, 346 state: this.state,
272 createdAt: this.createdAt, 347 createdAt: this.createdAt,
273 updatedAt: this.updatedAt 348 updatedAt: this.updatedAt
diff --git a/server/models/activitypub/actor.ts b/server/models/activitypub/actor.ts
index b88e06b41..912d8d748 100644
--- a/server/models/activitypub/actor.ts
+++ b/server/models/activitypub/actor.ts
@@ -204,7 +204,7 @@ export class ActorModel extends Model<ActorModel> {
204 VideoChannel: VideoChannelModel 204 VideoChannel: VideoChannelModel
205 205
206 static load (id: number) { 206 static load (id: number) {
207 return ActorModel.scope(ScopeNames.FULL).findById(id) 207 return ActorModel.unscoped().findById(id)
208 } 208 }
209 209
210 static listByFollowersUrls (followersUrls: string[], transaction?: Sequelize.Transaction) { 210 static listByFollowersUrls (followersUrls: string[], transaction?: Sequelize.Transaction) {
@@ -267,20 +267,17 @@ export class ActorModel extends Model<ActorModel> {
267 avatar = this.Avatar.toFormattedJSON() 267 avatar = this.Avatar.toFormattedJSON()
268 } 268 }
269 269
270 let score: number
271 if (this.Server) {
272 score = this.Server.score
273 }
274
275 return { 270 return {
276 id: this.id, 271 id: this.id,
277 url: this.url, 272 url: this.url,
278 uuid: this.uuid, 273 uuid: this.uuid,
274 name: this.preferredUsername,
279 host: this.getHost(), 275 host: this.getHost(),
280 score,
281 followingCount: this.followingCount, 276 followingCount: this.followingCount,
282 followersCount: this.followersCount, 277 followersCount: this.followersCount,
283 avatar 278 avatar,
279 createdAt: this.createdAt,
280 updatedAt: this.updatedAt
284 } 281 }
285 } 282 }
286 283
diff --git a/server/models/server/server.ts b/server/models/server/server.ts
index d35aa0ca4..c43146156 100644
--- a/server/models/server/server.ts
+++ b/server/models/server/server.ts
@@ -1,8 +1,5 @@
1import * as Sequelize from 'sequelize' 1import { AllowNull, Column, CreatedAt, Is, Model, Table, UpdatedAt } from 'sequelize-typescript'
2import { AllowNull, Column, CreatedAt, Default, Is, IsInt, Max, Model, Table, UpdatedAt } from 'sequelize-typescript'
3import { isHostValid } from '../../helpers/custom-validators/servers' 2import { isHostValid } from '../../helpers/custom-validators/servers'
4import { logger } from '../../helpers/logger'
5import { SERVERS_SCORE } from '../../initializers'
6import { throwIfNotValid } from '../utils' 3import { throwIfNotValid } from '../utils'
7 4
8@Table({ 5@Table({
@@ -11,9 +8,6 @@ import { throwIfNotValid } from '../utils'
11 { 8 {
12 fields: [ 'host' ], 9 fields: [ 'host' ],
13 unique: true 10 unique: true
14 },
15 {
16 fields: [ 'score' ]
17 } 11 }
18 ] 12 ]
19}) 13})
@@ -24,86 +18,9 @@ export class ServerModel extends Model<ServerModel> {
24 @Column 18 @Column
25 host: string 19 host: string
26 20
27 @AllowNull(false)
28 @Default(SERVERS_SCORE.BASE)
29 @IsInt
30 @Max(SERVERS_SCORE.MAX)
31 @Column
32 score: number
33
34 @CreatedAt 21 @CreatedAt
35 createdAt: Date 22 createdAt: Date
36 23
37 @UpdatedAt 24 @UpdatedAt
38 updatedAt: Date 25 updatedAt: Date
39
40 static updateServersScoreAndRemoveBadOnes (goodServers: number[], badServers: number[]) {
41 logger.info('Updating %d good servers and %d bad servers scores.', goodServers.length, badServers.length)
42
43 if (goodServers.length !== 0) {
44 ServerModel.incrementScores(goodServers, SERVERS_SCORE.BONUS)
45 .catch(err => {
46 logger.error('Cannot increment scores of good servers.', err)
47 })
48 }
49
50 if (badServers.length !== 0) {
51 ServerModel.incrementScores(badServers, SERVERS_SCORE.PENALTY)
52 .then(() => ServerModel.removeBadServers())
53 .catch(err => {
54 if (err) logger.error('Cannot decrement scores of bad servers.', err)
55 })
56
57 }
58 }
59
60 // Remove servers with a score of 0 (too many requests where they were unreachable)
61 private static async removeBadServers () {
62 try {
63 const servers = await ServerModel.listBadServers()
64
65 const serversRemovePromises = servers.map(server => server.destroy())
66 await Promise.all(serversRemovePromises)
67
68 const numberOfServersRemoved = servers.length
69
70 if (numberOfServersRemoved) {
71 logger.info('Removed %d servers.', numberOfServersRemoved)
72 } else {
73 logger.info('No need to remove bad servers.')
74 }
75 } catch (err) {
76 logger.error('Cannot remove bad servers.', err)
77 }
78 }
79
80 private static incrementScores (ids: number[], value: number) {
81 const update = {
82 score: Sequelize.literal('score +' + value)
83 }
84
85 const options = {
86 where: {
87 id: {
88 [Sequelize.Op.in]: ids
89 }
90 },
91 // In this case score is a literal and not an integer so we do not validate it
92 validate: false
93 }
94
95 return ServerModel.update(update, options)
96 }
97
98 private static listBadServers () {
99 const query = {
100 where: {
101 score: {
102 [Sequelize.Op.lte]: 0
103 }
104 }
105 }
106
107 return ServerModel.findAll(query)
108 }
109} 26}
diff --git a/server/models/video/video-channel.ts b/server/models/video/video-channel.ts
index acc2486b3..e2cbf0422 100644
--- a/server/models/video/video-channel.ts
+++ b/server/models/video/video-channel.ts
@@ -228,7 +228,7 @@ export class VideoChannelModel extends Model<VideoChannelModel> {
228 const actor = this.Actor.toFormattedJSON() 228 const actor = this.Actor.toFormattedJSON()
229 const account = { 229 const account = {
230 id: this.id, 230 id: this.id,
231 name: this.name, 231 displayName: this.name,
232 description: this.description, 232 description: this.description,
233 isLocal: this.Actor.isOwned(), 233 isLocal: this.Actor.isOwned(),
234 createdAt: this.createdAt, 234 createdAt: this.createdAt,