]> git.immae.eu Git - github/Chocobozzz/PeerTube.git/blame - server/models/pod/pod.ts
Implement video transcoding on server side
[github/Chocobozzz/PeerTube.git] / server / models / pod / pod.ts
CommitLineData
65fcc311 1import { map } from 'lodash'
e02643f3 2import * as Sequelize from 'sequelize'
9f10b292 3
74889a71
C
4import { FRIEND_SCORE, PODS_SCORE } from '../../initializers'
5import { logger, isHostValid } from '../../helpers'
9f10b292 6
74889a71 7import { addMethodsToModel } from '../utils'
e02643f3 8import {
e02643f3
C
9 PodInstance,
10 PodAttributes,
11
12 PodMethods
13} from './pod-interface'
14
15let Pod: Sequelize.Model<PodInstance, PodAttributes>
0aef76c4 16let toFormattedJSON: PodMethods.ToFormattedJSON
e02643f3
C
17let countAll: PodMethods.CountAll
18let incrementScores: PodMethods.IncrementScores
19let list: PodMethods.List
20let listAllIds: PodMethods.ListAllIds
21let listRandomPodIdsWithRequest: PodMethods.ListRandomPodIdsWithRequest
22let listBadPods: PodMethods.ListBadPods
23let load: PodMethods.Load
24let loadByHost: PodMethods.LoadByHost
25let removeAll: PodMethods.RemoveAll
26let updatePodsScore: PodMethods.UpdatePodsScore
27
127944aa
C
28export default function (sequelize: Sequelize.Sequelize, DataTypes: Sequelize.DataTypes) {
29 Pod = sequelize.define<PodInstance, PodAttributes>('Pod',
feb4bdfd
C
30 {
31 host: {
67bf9b96
C
32 type: DataTypes.STRING,
33 allowNull: false,
34 validate: {
075f16ca 35 isHost: value => {
65fcc311 36 const res = isHostValid(value)
67bf9b96
C
37 if (res === false) throw new Error('Host not valid.')
38 }
39 }
feb4bdfd
C
40 },
41 publicKey: {
67bf9b96
C
42 type: DataTypes.STRING(5000),
43 allowNull: false
feb4bdfd
C
44 },
45 score: {
46 type: DataTypes.INTEGER,
65fcc311 47 defaultValue: FRIEND_SCORE.BASE,
67bf9b96
C
48 allowNull: false,
49 validate: {
50 isInt: true,
65fcc311 51 max: FRIEND_SCORE.MAX
67bf9b96 52 }
4793c343
C
53 },
54 email: {
55 type: DataTypes.STRING(400),
ad4a8a1c
C
56 allowNull: false,
57 validate: {
58 isEmail: true
59 }
feb4bdfd 60 }
feb4bdfd
C
61 },
62 {
319d072e
C
63 indexes: [
64 {
5d67f289
C
65 fields: [ 'host' ],
66 unique: true
319d072e
C
67 },
68 {
69 fields: [ 'score' ]
70 }
e02643f3 71 ]
feb4bdfd
C
72 }
73 )
74
e02643f3
C
75 const classMethods = [
76 associate,
77
78 countAll,
79 incrementScores,
80 list,
81 listAllIds,
82 listRandomPodIdsWithRequest,
83 listBadPods,
84 load,
85 loadByHost,
86 updatePodsScore,
87 removeAll
88 ]
0aef76c4 89 const instanceMethods = [ toFormattedJSON ]
e02643f3
C
90 addMethodsToModel(Pod, classMethods, instanceMethods)
91
feb4bdfd 92 return Pod
53572423
C
93}
94
53572423
C
95// ------------------------------ METHODS ------------------------------
96
0aef76c4 97toFormattedJSON = function (this: PodInstance) {
53572423 98 const json = {
feb4bdfd 99 id: this.id,
49abbbbe 100 host: this.host,
4793c343 101 email: this.email,
70c065d6 102 score: this.score as number,
feb4bdfd 103 createdAt: this.createdAt
53572423
C
104 }
105
106 return json
107}
108
a3ee6fa2
C
109// ------------------------------ Statics ------------------------------
110
feb4bdfd 111function associate (models) {
e02643f3 112 Pod.belongsToMany(models.Request, {
feb4bdfd
C
113 foreignKey: 'podId',
114 through: models.RequestToPod,
7920c273 115 onDelete: 'cascade'
feb4bdfd
C
116 })
117}
118
6fcd19ba
C
119countAll = function () {
120 return Pod.count()
9f10b292 121}
45239549 122
6fcd19ba 123incrementScores = function (ids: number[], value: number) {
feb4bdfd 124 const update = {
e02643f3 125 score: Sequelize.literal('score +' + value)
feb4bdfd
C
126 }
127
67bf9b96 128 const options = {
feb4bdfd
C
129 where: {
130 id: {
131 $in: ids
132 }
67bf9b96
C
133 },
134 // In this case score is a literal and not an integer so we do not validate it
135 validate: false
feb4bdfd
C
136 }
137
6fcd19ba 138 return Pod.update(update, options)
9f10b292 139}
45239549 140
6fcd19ba
C
141list = function () {
142 return Pod.findAll()
9f10b292 143}
8c308c2b 144
6fcd19ba 145listAllIds = function (transaction: Sequelize.Transaction) {
556ddc31 146 const query = {
6fcd19ba
C
147 attributes: [ 'id' ],
148 transaction
feb4bdfd
C
149 }
150
6fcd19ba
C
151 return Pod.findAll(query).then(pods => {
152 return map(pods, 'id')
00057e85 153 })
528a9efa
C
154}
155
6fcd19ba
C
156listRandomPodIdsWithRequest = function (limit: number, tableWithPods: string, tableWithPodsJoins: string) {
157 return Pod.count().then(count => {
bd14d16a 158 // Optimization...
6fcd19ba 159 if (count === 0) return []
bd14d16a
C
160
161 let start = Math.floor(Math.random() * count) - limit
162 if (start < 0) start = 0
163
164 const query = {
165 attributes: [ 'id' ],
166 order: [
167 [ 'id', 'ASC' ]
168 ],
169 offset: start,
170 limit: limit,
171 where: {
172 id: {
556ddc31 173 $in: Sequelize.literal(`(SELECT DISTINCT "${tableWithPods}"."podId" FROM "${tableWithPods}" ${tableWithPodsJoins})`)
bd14d16a
C
174 }
175 }
176 }
177
6fcd19ba
C
178 return Pod.findAll(query).then(pods => {
179 return map(pods, 'id')
bd14d16a
C
180 })
181 })
182}
183
6fcd19ba 184listBadPods = function () {
feb4bdfd
C
185 const query = {
186 where: {
187 score: { $lte: 0 }
188 }
189 }
190
6fcd19ba 191 return Pod.findAll(query)
9f10b292 192}
8c308c2b 193
6fcd19ba
C
194load = function (id: number) {
195 return Pod.findById(id)
9f10b292 196}
c45f7f84 197
6fcd19ba 198loadByHost = function (host: string) {
feb4bdfd
C
199 const query = {
200 where: {
201 host: host
202 }
203 }
204
6fcd19ba 205 return Pod.findOne(query)
9f10b292 206}
c45f7f84 207
6fcd19ba
C
208removeAll = function () {
209 return Pod.destroy()
a3ee6fa2 210}
9e167724 211
69818c93 212updatePodsScore = function (goodPods: number[], badPods: number[]) {
9e167724
C
213 logger.info('Updating %d good pods and %d bad pods scores.', goodPods.length, badPods.length)
214
215 if (goodPods.length !== 0) {
6fcd19ba 216 incrementScores(goodPods, PODS_SCORE.BONUS).catch(err => {
ad0997ad 217 logger.error('Cannot increment scores of good pods.', err)
9e167724
C
218 })
219 }
220
221 if (badPods.length !== 0) {
40298b02 222 incrementScores(badPods, PODS_SCORE.PENALTY)
6fcd19ba
C
223 .then(() => removeBadPods())
224 .catch(err => {
ad0997ad 225 if (err) logger.error('Cannot decrement scores of bad pods.', err)
6fcd19ba 226 })
9e167724
C
227 }
228}
229
230// ---------------------------------------------------------------------------
231
232// Remove pods with a score of 0 (too many requests where they were unreachable)
233function removeBadPods () {
6fcd19ba
C
234 return listBadPods()
235 .then(pods => {
236 const podsRemovePromises = pods.map(pod => pod.destroy())
237 return Promise.all(podsRemovePromises).then(() => pods.length)
238 })
239 .then(numberOfPodsRemoved => {
240 if (numberOfPodsRemoved) {
241 logger.info('Removed %d pods.', numberOfPodsRemoved)
242 } else {
243 logger.info('No need to remove bad pods.')
244 }
245 })
246 .catch(err => {
ad0997ad 247 logger.error('Cannot remove bad pods.', err)
6fcd19ba 248 })
9e167724 249}