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