fox-wamp
Version:
Web Application Message Router/Server WAMP/MQTT
191 lines (165 loc) • 5.72 kB
text/typescript
import { Actor, BaseRealm, BaseEngine, makeDataSerializable, unSerializeData } from '../realm'
import Router from '../router'
import { HyperClient } from '../hyper/client'
import { MemKeyValueStorage } from '../mono/memkv'
import { AdvanceHistoryEvent, AdvanceOffsetId, INTRA_REALM_NAME } from './netengine.h'
export class HistorySegment {
private content: Map<number,Actor> = new Map()
private advanceSegment: string
private generator: number = 0
constructor (advanceSegment: string) {
this.advanceSegment = advanceSegment
}
addActor (actor: Actor): AdvanceOffsetId {
this.generator++
this.content.set(this.generator, actor)
return { segment: this.advanceSegment, offset: this.generator }
}
fetchActor (advanceId: AdvanceOffsetId): Actor | undefined {
if (advanceId.segment !== this.advanceSegment) {
throw Error("advance is not identical "+advanceId.segment+" "+this.advanceSegment)
}
let actor = this.content.get(advanceId.offset)
if (actor) {
this.content.delete(advanceId.offset)
}
return actor
}
getAdvanceSegment(): string {
return this.advanceSegment
}
}
export class NetEngine extends BaseEngine {
private netEngineMill: NetEngineMill
constructor (netEngineMill: NetEngineMill) {
super()
this.netEngineMill = netEngineMill
}
// @return promise
doPush (actor: any) {
return this.netEngineMill.saveHistory(actor, this.getRealmName())
}
getHistoryAfter (after: string, uri: string, cbEmitRow: any): Promise<any> {
return Promise.resolve()
// return History.getEventHistory(
// getMainDb(),
// engine.getRealmName(),
// { fromId: after, uri },
// (event) => {
// cbEmitRow({
// qid: event.id,
// uri: event.uri,
// data: unSerializeData(event.body)
// })
// }
// )
}
}
export class NetEngineMill {
private curSegment: HistorySegment | null = null
private advanceSegmentGen: number = 0
private segments = new Map()
private router: Router
private sysRealm: BaseRealm
private sysApi: HyperClient
constructor (router: any) {
this.router = router
this.sysRealm = new BaseRealm(router, new BaseEngine())
this.router.initRealm(INTRA_REALM_NAME, this.sysRealm)
this.sysRealm.registerKeyValueEngine(['#'], new MemKeyValueStorage())
this.sysApi = this.sysRealm.buildApi()
this.sysApi.subscribe('trim-advance-segment', (data: any, opt: any) => {
// to do voute for complete
if (!data.advanceSegment) {
console.error('ERROR: no advanceSegment in package')
}
if (this.curSegment) {
if (data.advanceSegment == this.curSegment.getAdvanceSegment()) {
// it will be required to create new segment at the next inbound message
this.curSegment = null
console.log('advance-segment-over =>', data.advanceSegment)
this.sysApi.publish('advance-segment-over', {
advanceSegment: data.advanceSegment
})
} else {
console.warn('warn: new segment is not accepted, cur:', this.curSegment.getAdvanceSegment(), 'inbound:', data.advanceSegment)
}
}
})
this.sysApi.subscribe('advance-segment-resolved', (data: any, opt: any) => {
this.advanceSegmentResolved(opt.headers)
})
this.sysApi.subscribe('dispatchEvent', (data: any, opt: any) => {
console.log('=> dispatchEvent', opt.headers.qid)
this.dispatchEvent(opt.headers)
})
}
getSegment () {
if (this.curSegment) {
return this.curSegment
}
this.advanceSegmentGen++
let curAdvanceSegment = '' + this.router.getId() + '-' + this.advanceSegmentGen
this.curSegment = new HistorySegment(curAdvanceSegment)
this.segments.set(curAdvanceSegment, this.curSegment)
// todo: sent all open advance segments, in case of sharding that keeps order
this.sysApi.publish('begin-advance-segment', {
advanceSegment: curAdvanceSegment
})
return this.curSegment
}
findSegment (advanceSegment: string) {
return this.segments.get(advanceSegment)
}
deleteSegment (advanceSegment: string) {
return this.segments.delete(advanceSegment)
}
advanceSegmentResolved (syncMessage: any) {
let segment = this.findSegment(syncMessage.advanceSegment)
if (!segment) {
return
}
console.log('advance-segment-resolved', syncMessage.advanceSegment, syncMessage.pkg)
for (let event of syncMessage.pkg) {
let actor = segment.fetchActor(event.advanceId)
if (actor) {
actor.setEventId(event.qid)
actor.confirm()
} else {
console.log("actor not found by advanceId", event.advanceId)
}
}
if (syncMessage.final) {
this.deleteSegment(syncMessage.advanceSegment)
if (segment.size() > 0) {
console.log("removing not empty segment")
}
}
}
// @return promise
saveHistory (actor: any, realmName: string) {
let segment = this.getSegment()
let advanceId = segment.addActor(actor)
const event: AdvanceHistoryEvent = {
advanceId: advanceId,
realm: realmName,
data: makeDataSerializable(actor.getData()),
uri: actor.getUri(),
opt: actor.getOpt(),
sid: actor.getSid()
}
return this.sysApi.publish('keep-advance-history', null, { headers: event})
}
dispatchEvent (eventData: any) {
const realm = this.router.findRealm(eventData.realm)
if (realm) {
realm.getEngine().disperseToSubs({
qid: eventData.qid,
uri: eventData.uri,
data: unSerializeData(eventData.data),
opt: eventData.opt,
sid: eventData.sid
})
}
}
}