Skip to content

Commit 0352057

Browse files
committed
refactor(realtime): extract "update dirty note" to new job
Signed-off-by: BoHong Li <[email protected]>
1 parent 702fc48 commit 0352057

File tree

6 files changed

+211
-124
lines changed

6 files changed

+211
-124
lines changed

lib/realtime.js

Lines changed: 30 additions & 50 deletions
Original file line numberDiff line numberDiff line change
@@ -1,30 +1,31 @@
11
'use strict'
22
// realtime
33
// external modules
4-
var cookie = require('cookie')
5-
var cookieParser = require('cookie-parser')
6-
var url = require('url')
7-
var async = require('async')
8-
var randomcolor = require('randomcolor')
9-
var Chance = require('chance')
10-
var chance = new Chance()
11-
var moment = require('moment')
4+
const cookie = require('cookie')
5+
const cookieParser = require('cookie-parser')
6+
const url = require('url')
7+
const async = require('async')
8+
const randomcolor = require('randomcolor')
9+
const Chance = require('chance')
10+
const chance = new Chance()
11+
const moment = require('moment')
1212

1313
const get = require('lodash/get')
1414

1515
// core
16-
var config = require('./config')
17-
var logger = require('./logger')
18-
var history = require('./history')
19-
var models = require('./models')
16+
const config = require('./config')
17+
const logger = require('./logger')
18+
const history = require('./history')
19+
const models = require('./models')
2020

2121
// ot
22-
var ot = require('./ot')
22+
const ot = require('./ot')
2323

24-
const {RealtimeClientConnection} = require('./realtimeClientConnection')
24+
const { RealtimeClientConnection } = require('./realtimeClientConnection')
25+
const { UpdateDirtyNoteJob } = require('./realtimeUpdateDirtyNoteJob')
2526

2627
// public
27-
var realtime = {
28+
const realtime = {
2829
io: null,
2930
onAuthorizeSuccess: onAuthorizeSuccess,
3031
onAuthorizeFail: onAuthorizeFail,
@@ -83,44 +84,20 @@ function emitCheck (note) {
8384
// actions
8485
var users = {}
8586
var notes = {}
86-
// update when the note is dirty
87-
setInterval(function () {
88-
async.each(Object.keys(notes), function (key, callback) {
89-
var note = notes[key]
90-
if (note.server.isDirty) {
91-
if (config.debug) logger.info('updater found dirty note: ' + key)
92-
note.server.isDirty = false
93-
exports.updateNote(note, function (err, _note) {
94-
// handle when note already been clean up
95-
if (!notes[key] || !notes[key].server) return callback(null, null)
96-
if (!_note) {
97-
realtime.io.to(note.id).emit('info', {
98-
code: 404
99-
})
100-
logger.error('note not found: ', note.id)
101-
}
102-
if (err || !_note) {
103-
for (var i = 0, l = note.socks.length; i < l; i++) {
104-
var sock = note.socks[i]
105-
if (typeof sock !== 'undefined' && sock) {
106-
setTimeout(function () {
107-
sock.disconnect(true)
108-
}, 0)
109-
}
110-
}
111-
return callback(err, null)
112-
}
113-
note.updatetime = moment(_note.lastchangeAt).valueOf()
114-
emitCheck(note)
115-
return callback(null, null)
87+
88+
const updateDirtyNoteJob = new UpdateDirtyNoteJob(realtime)
89+
updateDirtyNoteJob.start(realtime)
90+
91+
function disconnectSocketOnNote (note) {
92+
note.socks.forEach((sock) => {
93+
if (sock) {
94+
sock.emit('delete')
95+
setImmediate(() => {
96+
sock.disconnect(true)
11697
})
117-
} else {
118-
return callback(null, null)
11998
}
120-
}, function (err) {
121-
if (err) return logger.error('updater error', err)
12299
})
123-
}, 1000)
100+
}
124101

125102
function updateNote (note, callback) {
126103
models.Note.findOne({
@@ -858,6 +835,9 @@ exports.checkViewPermission = checkViewPermission
858835
exports.getNoteFromNotePool = getNoteFromNotePool
859836
exports.getUserFromUserPool = getUserFromUserPool
860837
exports.buildUserOutData = buildUserOutData
838+
exports.getNotePool = getNotePool
839+
exports.emitCheck = emitCheck
840+
exports.disconnectSocketOnNote = disconnectSocketOnNote
861841
exports.notes = notes
862842
exports.users = users
863843
exports.disconnectSocketQueue = disconnectSocketQueue

lib/realtimeClientConnection.js

Lines changed: 1 addition & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -57,17 +57,6 @@ class RealtimeClientConnection {
5757
return config.allowAnonymous || config.allowAnonymousEdits
5858
}
5959

60-
disconnectSocketOnNote (note) {
61-
note.socks.forEach((sock) => {
62-
if (sock) {
63-
sock.emit('delete')
64-
setImmediate(() => {
65-
sock.disconnect(true)
66-
})
67-
}
68-
})
69-
}
70-
7160
getCurrentUser () {
7261
if (!this.socket.id) return
7362
return this.realtime.getUserFromUserPool(this.socket.id)
@@ -206,7 +195,7 @@ class RealtimeClientConnection {
206195
this.destroyNote(note.id)
207196
.then((successRows) => {
208197
if (!successRows) return
209-
this.disconnectSocketOnNote(note)
198+
this.realtime.disconnectSocketOnNote(note)
210199
})
211200
.catch(function (err) {
212201
return logger.error('delete note failed: ' + err)

lib/realtimeUpdateDirtyNoteJob.js

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
'use strict'
2+
3+
const async = require('async')
4+
const config = require('./config')
5+
const logger = require('./logger')
6+
const moment = require('moment')
7+
8+
class UpdateDirtyNoteJob {
9+
constructor (realtime) {
10+
this.realtime = realtime
11+
}
12+
13+
start () {
14+
setInterval(this.updateDirtyNote.bind(this), 1000)
15+
}
16+
17+
updateDirtyNote () {
18+
const notes = this.realtime.getNotePool()
19+
async.each(Object.keys(notes), (key, callback) => {
20+
const note = notes[key]
21+
if (!note.server.isDirty) return callback(null, null)
22+
23+
if (config.debug) logger.info('updater found dirty note: ' + key)
24+
note.server.isDirty = false
25+
this.realtime.updateNote(note, (err, _note) => {
26+
// handle when note already been clean up
27+
if (!notes[key] || !notes[key].server) return callback(null, null)
28+
if (!_note) {
29+
this.realtime.io.to(note.id).emit('info', {
30+
code: 404
31+
})
32+
logger.error('note not found: ', note.id)
33+
}
34+
35+
if (err || !_note) {
36+
this.realtime.disconnectSocketOnNote(note)
37+
return callback(err, null)
38+
}
39+
40+
note.updatetime = moment(_note.lastchangeAt).valueOf()
41+
this.realtime.emitCheck(note)
42+
return callback(null, null)
43+
})
44+
}, (err) => {
45+
if (err) return logger.error('updater error', err)
46+
})
47+
}
48+
}
49+
50+
exports.UpdateDirtyNoteJob = UpdateDirtyNoteJob

test/realtime/dirtyNoteUpdate.test.js

Lines changed: 130 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,130 @@
1+
/* eslint-env node, mocha */
2+
'use strict'
3+
4+
const assert = require('assert')
5+
const mock = require('mock-require')
6+
const sinon = require('sinon')
7+
const { removeModuleFromRequireCache, makeMockSocket } = require('./utils')
8+
9+
describe('realtime#update note is dirty timer', function () {
10+
let realtime
11+
let clock
12+
13+
beforeEach(() => {
14+
clock = sinon.useFakeTimers({
15+
toFake: ['setInterval']
16+
})
17+
mock('../../lib/logger', {
18+
error: () => {
19+
}
20+
})
21+
mock('../../lib/history', {})
22+
mock('../../lib/models', {
23+
Revision: {
24+
saveAllNotesRevision: () => {
25+
}
26+
}
27+
})
28+
mock('../../lib/config', {})
29+
realtime = require('../../lib/realtime')
30+
31+
realtime.io = {
32+
to: sinon.stub().callsFake(function () {
33+
return {
34+
emit: sinon.fake()
35+
}
36+
})
37+
}
38+
})
39+
40+
afterEach(() => {
41+
removeModuleFromRequireCache('../../lib/realtimeUpdateDirtyNoteJob')
42+
removeModuleFromRequireCache('../../lib/realtime')
43+
mock.stopAll()
44+
clock.restore()
45+
})
46+
47+
it('should update note when note is dirty', (done) => {
48+
sinon.stub(realtime, 'updateNote').callsFake(function (note, callback) {
49+
callback(null, note)
50+
})
51+
52+
realtime.notes['note1'] = {
53+
server: {
54+
isDirty: false
55+
},
56+
socks: []
57+
}
58+
59+
let note2 = {
60+
server: {
61+
isDirty: true
62+
},
63+
socks: []
64+
}
65+
66+
realtime.notes['note2'] = note2
67+
68+
clock.tick(1000)
69+
70+
setTimeout(() => {
71+
assert(note2.server.isDirty === false)
72+
done()
73+
}, 5)
74+
})
75+
76+
it('should not do anything when note missing', function (done) {
77+
sinon.stub(realtime, 'updateNote').callsFake(function (note, callback) {
78+
delete realtime.notes['note']
79+
callback(null, note)
80+
})
81+
82+
let note = {
83+
server: {
84+
isDirty: true
85+
},
86+
socks: [makeMockSocket()]
87+
}
88+
realtime.notes['note'] = note
89+
90+
clock.tick(1000)
91+
92+
setTimeout(() => {
93+
assert(note.server.isDirty === false)
94+
assert(note.socks[0].disconnect.called === false)
95+
done()
96+
}, 50)
97+
})
98+
99+
it('should disconnect all clients when update note error', function (done) {
100+
sinon.stub(realtime, 'updateNote').callsFake(function (note, callback) {
101+
callback(new Error('some error'), null)
102+
})
103+
104+
realtime.io = {
105+
to: sinon.stub().callsFake(function () {
106+
return {
107+
emit: sinon.fake()
108+
}
109+
})
110+
}
111+
112+
let note = {
113+
server: {
114+
isDirty: true
115+
},
116+
socks: [makeMockSocket(), undefined, makeMockSocket()]
117+
}
118+
realtime.notes['note'] = note
119+
120+
clock.tick(1000)
121+
122+
setTimeout(() => {
123+
assert(note.server.isDirty === false)
124+
assert(note.socks[0].disconnect.called)
125+
assert(note.socks[2].disconnect.called)
126+
done()
127+
}, 50)
128+
})
129+
130+
})

test/realtime/realtime.test.js

Lines changed: 0 additions & 61 deletions
Original file line numberDiff line numberDiff line change
@@ -37,67 +37,6 @@ function removeModuleFromRequireCache (modulePath) {
3737
}
3838

3939
describe('realtime', function () {
40-
41-
describe('update note is dirty timer', function () {
42-
let realtime
43-
beforeEach(() => {
44-
mock('../../lib/logger', {
45-
error: () => {
46-
}
47-
})
48-
mock('../../lib/history', {})
49-
mock('../../lib/models', {
50-
Revision: {
51-
saveAllNotesRevision: () => {
52-
}
53-
}
54-
})
55-
mock('../../lib/config', {})
56-
})
57-
58-
afterEach(() => {
59-
removeModuleFromRequireCache('../../lib/realtime')
60-
mock.stopAll()
61-
})
62-
63-
it('should update note when note is dirty', (done) => {
64-
const clock = sinon.useFakeTimers()
65-
realtime = require('../../lib/realtime')
66-
sinon.stub(realtime, 'updateNote').callsFake(function (note, callback) {
67-
callback(null, null)
68-
})
69-
const socketIoEmitFake = sinon.fake()
70-
realtime.io = {
71-
to: sinon.stub().callsFake(function () {
72-
return {
73-
emit: socketIoEmitFake
74-
}
75-
})
76-
}
77-
realtime.notes['note1'] = {
78-
server: {
79-
isDirty: false
80-
},
81-
socks: []
82-
}
83-
let note2 = {
84-
server: {
85-
isDirty: true
86-
},
87-
socks: []
88-
}
89-
realtime.notes['note2'] = note2
90-
91-
clock.tick(1000)
92-
clock.restore()
93-
94-
setTimeout(() => {
95-
assert(note2.server.isDirty === false)
96-
done()
97-
}, 50)
98-
})
99-
})
100-
10140
describe('updateNote', function () {
10241
let realtime, fakeNote
10342
beforeEach(() => {

test/realtime/socket-events.test.js

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -537,6 +537,5 @@ describe('realtime#socket event', function () {
537537
done()
538538
}, 5)
539539
})
540-
541540
})
542541
})

0 commit comments

Comments
 (0)