1 import { map } from 'lodash'
2 import * as Sequelize from 'sequelize'
4 import { FRIEND_SCORE, SERVERS_SCORE } from '../../initializers'
5 import { logger, isHostValid } from '../../helpers'
7 import { addMethodsToModel, getSort } from '../utils'
13 } from './server-interface'
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
28 export default function (sequelize: Sequelize.Sequelize, DataTypes: Sequelize.DataTypes) {
29 Server = sequelize.define<ServerInstance, ServerAttributes>('Server',
32 type: DataTypes.STRING,
36 const res = isHostValid(value)
37 if (res === false) throw new Error('Host not valid.')
42 type: DataTypes.INTEGER,
43 defaultValue: FRIEND_SCORE.BASE,
64 const classMethods = [
70 listRandomServerIdsWithRequest,
77 addMethodsToModel(Server, classMethods)
82 // ------------------------------ Statics ------------------------------
84 countAll = function () {
88 incrementScores = function (ids: number[], value: number) {
90 score: Sequelize.literal('score +' + value)
96 [Sequelize.Op.in]: ids
99 // In this case score is a literal and not an integer so we do not validate it
103 return Server.update(update, options)
107 return Server.findAll()
110 listForApi = function (start: number, count: number, sort: string) {
114 order: [ getSort(sort) ]
117 return Server.findAndCountAll(query).then(({ rows, count }) => {
125 listAllIds = function (transaction: Sequelize.Transaction) {
127 attributes: [ 'id' ],
131 return Server.findAll(query).then(servers => {
132 return map(servers, 'id')
136 listRandomServerIdsWithRequest = function (limit: number, tableWithServers: string, tableWithServersJoins: string) {
137 return Server.count().then(count => {
139 if (count === 0) return []
141 let start = Math.floor(Math.random() * count) - limit
142 if (start < 0) start = 0
144 const subQuery = `(SELECT DISTINCT "${tableWithServers}"."serverId" FROM "${tableWithServers}" ${tableWithServersJoins})`
146 attributes: [ 'id' ],
154 [Sequelize.Op.in]: Sequelize.literal(subQuery)
159 return Server.findAll(query).then(servers => {
160 return map(servers, 'id')
165 listBadServers = function () {
169 [Sequelize.Op.lte]: 0
174 return Server.findAll(query)
177 load = function (id: number) {
178 return Server.findById(id)
181 loadByHost = function (host: string) {
188 return Server.findOne(query)
191 removeAll = function () {
192 return Server.destroy()
195 updateServersScore = function (goodServers: number[], badServers: number[]) {
196 logger.info('Updating %d good servers and %d bad servers scores.', goodServers.length, badServers.length)
198 if (goodServers.length !== 0) {
199 incrementScores(goodServers, SERVERS_SCORE.BONUS).catch(err => {
200 logger.error('Cannot increment scores of good servers.', err)
204 if (badServers.length !== 0) {
205 incrementScores(badServers, SERVERS_SCORE.PENALTY)
206 .then(() => removeBadServers())
208 if (err) logger.error('Cannot decrement scores of bad servers.', err)
213 // ---------------------------------------------------------------------------
215 // Remove servers with a score of 0 (too many requests where they were unreachable)
216 async function removeBadServers () {
218 const servers = await listBadServers()
220 const serversRemovePromises = servers.map(server => server.destroy())
221 await Promise.all(serversRemovePromises)
223 const numberOfServersRemoved = servers.length
225 if (numberOfServersRemoved) {
226 logger.info('Removed %d servers.', numberOfServersRemoved)
228 logger.info('No need to remove bad servers.')
231 logger.error('Cannot remove bad servers.', err)