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