oak-backend-base 3.3.3 → 3.3.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/lib/AppLoader.d.ts +43 -43
- package/lib/AppLoader.js +444 -437
- package/lib/ClusterAppLoader.d.ts +17 -17
- package/lib/DataSubscriber.d.ts +18 -18
- package/lib/DataSubscriber.js +158 -158
- package/lib/DbStore.d.ts +20 -20
- package/lib/Synchronizer.d.ts +46 -36
- package/lib/Synchronizer.js +530 -471
- package/lib/cluster/DataSubscriber.d.ts +23 -23
- package/lib/cluster/DataSubscriber.js +83 -83
- package/lib/types/Sync.d.ts +16 -16
- package/lib/types/Sync.js +5 -5
- package/package.json +42 -42
|
@@ -1,17 +1,17 @@
|
|
|
1
|
-
import { EntityDict as BaseEntityDict } from 'oak-domain/lib/base-app-domain';
|
|
2
|
-
import { EntityDict, OperationResult, Trigger } from 'oak-domain/lib/types';
|
|
3
|
-
import { BackendRuntimeContext } from 'oak-frontend-base/lib/context/BackendRuntimeContext';
|
|
4
|
-
import { AppLoader } from './AppLoader';
|
|
5
|
-
import { DbStore } from './DbStore';
|
|
6
|
-
import { Namespace } from 'socket.io';
|
|
7
|
-
import { Socket } from 'socket.io-client';
|
|
8
|
-
export declare class ClusterAppLoader<ED extends EntityDict & BaseEntityDict, Cxt extends BackendRuntimeContext<ED>> extends AppLoader<ED, Cxt> {
|
|
9
|
-
protected socket: Socket;
|
|
10
|
-
private csTriggers;
|
|
11
|
-
private connect;
|
|
12
|
-
private sub;
|
|
13
|
-
constructor(path: string, contextBuilder: (scene?: string) => (store: DbStore<ED, Cxt>) => Promise<Cxt>, nsDs: Namespace, nsServer: Namespace, socketPath: string);
|
|
14
|
-
protected registerTrigger(trigger: Trigger<ED, keyof ED, Cxt>): void;
|
|
15
|
-
protected operateInWatcher<T extends keyof ED>(entity: T, operation: ED[T]['Update'], context: Cxt): Promise<OperationResult<ED>>;
|
|
16
|
-
protected selectInWatcher<T extends keyof ED>(entity: T, selection: ED[T]['Selection'], context: Cxt): Promise<Partial<ED[T]['Schema']>[]>;
|
|
17
|
-
}
|
|
1
|
+
import { EntityDict as BaseEntityDict } from 'oak-domain/lib/base-app-domain';
|
|
2
|
+
import { EntityDict, OperationResult, Trigger } from 'oak-domain/lib/types';
|
|
3
|
+
import { BackendRuntimeContext } from 'oak-frontend-base/lib/context/BackendRuntimeContext';
|
|
4
|
+
import { AppLoader } from './AppLoader';
|
|
5
|
+
import { DbStore } from './DbStore';
|
|
6
|
+
import { Namespace } from 'socket.io';
|
|
7
|
+
import { Socket } from 'socket.io-client';
|
|
8
|
+
export declare class ClusterAppLoader<ED extends EntityDict & BaseEntityDict, Cxt extends BackendRuntimeContext<ED>> extends AppLoader<ED, Cxt> {
|
|
9
|
+
protected socket: Socket;
|
|
10
|
+
private csTriggers;
|
|
11
|
+
private connect;
|
|
12
|
+
private sub;
|
|
13
|
+
constructor(path: string, contextBuilder: (scene?: string) => (store: DbStore<ED, Cxt>) => Promise<Cxt>, nsDs: Namespace, nsServer: Namespace, socketPath: string);
|
|
14
|
+
protected registerTrigger(trigger: Trigger<ED, keyof ED, Cxt>): void;
|
|
15
|
+
protected operateInWatcher<T extends keyof ED>(entity: T, operation: ED[T]['Update'], context: Cxt): Promise<OperationResult<ED>>;
|
|
16
|
+
protected selectInWatcher<T extends keyof ED>(entity: T, selection: ED[T]['Selection'], context: Cxt): Promise<Partial<ED[T]['Schema']>[]>;
|
|
17
|
+
}
|
package/lib/DataSubscriber.d.ts
CHANGED
|
@@ -1,18 +1,18 @@
|
|
|
1
|
-
import { EntityDict } from 'oak-domain/lib/types';
|
|
2
|
-
import { EntityDict as BaseEntityDict } from 'oak-domain/lib/base-app-domain';
|
|
3
|
-
import { BackendRuntimeContext } from 'oak-frontend-base';
|
|
4
|
-
import { Namespace } from 'socket.io';
|
|
5
|
-
export default class DataSubscriber<ED extends EntityDict & BaseEntityDict, Context extends BackendRuntimeContext<ED>> {
|
|
6
|
-
private ns;
|
|
7
|
-
private contextBuilder;
|
|
8
|
-
private filterMap;
|
|
9
|
-
private idEntityMap;
|
|
10
|
-
constructor(ns: Namespace, contextBuilder: (scene?: string) => Promise<Context>);
|
|
11
|
-
private formCreateRoomRoutine;
|
|
12
|
-
/**
|
|
13
|
-
* 来自外部的socket连接,监听数据变化
|
|
14
|
-
*/
|
|
15
|
-
private startup;
|
|
16
|
-
private sendRecord;
|
|
17
|
-
onDataCommited(context: Context): void;
|
|
18
|
-
}
|
|
1
|
+
import { EntityDict } from 'oak-domain/lib/types';
|
|
2
|
+
import { EntityDict as BaseEntityDict } from 'oak-domain/lib/base-app-domain';
|
|
3
|
+
import { BackendRuntimeContext } from 'oak-frontend-base';
|
|
4
|
+
import { Namespace } from 'socket.io';
|
|
5
|
+
export default class DataSubscriber<ED extends EntityDict & BaseEntityDict, Context extends BackendRuntimeContext<ED>> {
|
|
6
|
+
private ns;
|
|
7
|
+
private contextBuilder;
|
|
8
|
+
private filterMap;
|
|
9
|
+
private idEntityMap;
|
|
10
|
+
constructor(ns: Namespace, contextBuilder: (scene?: string) => Promise<Context>);
|
|
11
|
+
private formCreateRoomRoutine;
|
|
12
|
+
/**
|
|
13
|
+
* 来自外部的socket连接,监听数据变化
|
|
14
|
+
*/
|
|
15
|
+
private startup;
|
|
16
|
+
private sendRecord;
|
|
17
|
+
onDataCommited(context: Context): void;
|
|
18
|
+
}
|
package/lib/DataSubscriber.js
CHANGED
|
@@ -1,158 +1,158 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
const lodash_1 = require("oak-domain/lib/utils/lodash");
|
|
4
|
-
const oak_domain_1 = require("oak-domain");
|
|
5
|
-
class DataSubscriber {
|
|
6
|
-
ns;
|
|
7
|
-
contextBuilder;
|
|
8
|
-
filterMap;
|
|
9
|
-
idEntityMap;
|
|
10
|
-
constructor(ns, contextBuilder) {
|
|
11
|
-
this.ns = ns;
|
|
12
|
-
this.contextBuilder = contextBuilder;
|
|
13
|
-
this.startup();
|
|
14
|
-
this.filterMap = {};
|
|
15
|
-
this.idEntityMap = {};
|
|
16
|
-
}
|
|
17
|
-
formCreateRoomRoutine(def) {
|
|
18
|
-
const { id, entity, filter } = def;
|
|
19
|
-
return (room) => {
|
|
20
|
-
if (room === id) {
|
|
21
|
-
console.log('instance:', process.env.NODE_APP_INSTANCE, 'add filter', room);
|
|
22
|
-
// 本房间不存在,说明这个filter是新出现的
|
|
23
|
-
if (this.filterMap[entity]) {
|
|
24
|
-
// id的唯一性由前台保证,重复则无视
|
|
25
|
-
Object.assign(this.filterMap[entity], {
|
|
26
|
-
[id]: filter,
|
|
27
|
-
});
|
|
28
|
-
}
|
|
29
|
-
else {
|
|
30
|
-
Object.assign(this.filterMap, {
|
|
31
|
-
[entity]: {
|
|
32
|
-
[id]: filter,
|
|
33
|
-
}
|
|
34
|
-
});
|
|
35
|
-
}
|
|
36
|
-
this.idEntityMap[id] = entity;
|
|
37
|
-
}
|
|
38
|
-
};
|
|
39
|
-
}
|
|
40
|
-
/**
|
|
41
|
-
* 来自外部的socket连接,监听数据变化
|
|
42
|
-
*/
|
|
43
|
-
startup() {
|
|
44
|
-
this.ns.on('connection', async (socket) => {
|
|
45
|
-
try {
|
|
46
|
-
const { 'oak-cxt': cxtStr } = socket.handshake.headers;
|
|
47
|
-
const context = await this.contextBuilder(cxtStr);
|
|
48
|
-
socket.userId = context.getCurrentUserId();
|
|
49
|
-
socket.context = context;
|
|
50
|
-
socket.idMap = {};
|
|
51
|
-
socket.on('sub', async (data) => {
|
|
52
|
-
try {
|
|
53
|
-
console.log('instance:', process.env.NODE_APP_INSTANCE, 'on sub', JSON.stringify(data));
|
|
54
|
-
await Promise.all(data.map(async (ele) => {
|
|
55
|
-
const { id, entity, filter } = ele;
|
|
56
|
-
// 尝试select此filter,如果失败说明权限越界
|
|
57
|
-
await context.select(entity, {
|
|
58
|
-
data: {
|
|
59
|
-
id: 1,
|
|
60
|
-
},
|
|
61
|
-
filter,
|
|
62
|
-
}, {});
|
|
63
|
-
}));
|
|
64
|
-
}
|
|
65
|
-
catch (err) {
|
|
66
|
-
socket.emit('error', err.toString());
|
|
67
|
-
return;
|
|
68
|
-
}
|
|
69
|
-
data.forEach((ele) => {
|
|
70
|
-
const createRoomRoutine = this.formCreateRoomRoutine(ele);
|
|
71
|
-
this.ns.adapter.on('create-room', createRoomRoutine);
|
|
72
|
-
socket.join(ele.id);
|
|
73
|
-
this.ns.adapter.off('create-room', createRoomRoutine);
|
|
74
|
-
});
|
|
75
|
-
});
|
|
76
|
-
socket.on('unsub', (ids) => {
|
|
77
|
-
// console.log('instance:', process.env.NODE_APP_INSTANCE, 'on unsub', JSON.stringify(ids));
|
|
78
|
-
ids.forEach((id) => {
|
|
79
|
-
socket.leave(id);
|
|
80
|
-
});
|
|
81
|
-
});
|
|
82
|
-
}
|
|
83
|
-
catch (err) {
|
|
84
|
-
socket.emit('error', err.toString());
|
|
85
|
-
}
|
|
86
|
-
});
|
|
87
|
-
this.ns.adapter.on('delete-room', (room) => {
|
|
88
|
-
const entity = this.idEntityMap[room];
|
|
89
|
-
if (entity) {
|
|
90
|
-
// console.log('instance:', process.env.NODE_APP_INSTANCE, 'remove filter', room);
|
|
91
|
-
(0, lodash_1.unset)(this.filterMap[entity], room);
|
|
92
|
-
(0, lodash_1.unset)(this.idEntityMap, room);
|
|
93
|
-
}
|
|
94
|
-
});
|
|
95
|
-
this.ns.on('sendRecord', (entity, filter, record, isCreate) => {
|
|
96
|
-
console.log('instance:', process.env.NODE_APP_INSTANCE, 'get record from another', JSON.stringify(entity));
|
|
97
|
-
});
|
|
98
|
-
}
|
|
99
|
-
sendRecord(entity, filter, record, sid, isCreate) {
|
|
100
|
-
if (entity === 'spContractApplyment') {
|
|
101
|
-
console.log('instance:', process.env.NODE_APP_INSTANCE, 'sendRecord', JSON.stringify(entity));
|
|
102
|
-
}
|
|
103
|
-
this.ns.serverSideEmit('sendRecord', entity, filter, record, isCreate);
|
|
104
|
-
if (this.filterMap[entity]) {
|
|
105
|
-
Object.keys(this.filterMap[entity]).forEach(async (room) => {
|
|
106
|
-
const context = await this.contextBuilder();
|
|
107
|
-
const filter2 = this.filterMap[entity][room];
|
|
108
|
-
let needSend = false;
|
|
109
|
-
if (isCreate) {
|
|
110
|
-
// 如果是插入数据肯定是单行,使用相容性检测
|
|
111
|
-
const contained = await (0, oak_domain_1.checkFilterContains)(entity, context, filter2, filter, true);
|
|
112
|
-
needSend = contained;
|
|
113
|
-
}
|
|
114
|
-
else {
|
|
115
|
-
const repeled = await (0, oak_domain_1.checkFilterRepel)(entity, context, filter, filter2, true);
|
|
116
|
-
needSend = !repeled;
|
|
117
|
-
}
|
|
118
|
-
if (needSend) {
|
|
119
|
-
// console.log('instance:', process.env.NODE_APP_INSTANCE, 'needSend', JSON.stringify(room));
|
|
120
|
-
if (sid) {
|
|
121
|
-
this.ns.to(room).except(sid).emit('data', [record], [room]);
|
|
122
|
-
}
|
|
123
|
-
else {
|
|
124
|
-
this.ns.to(room).emit('data', [record], [room]);
|
|
125
|
-
}
|
|
126
|
-
}
|
|
127
|
-
});
|
|
128
|
-
}
|
|
129
|
-
}
|
|
130
|
-
onDataCommited(context) {
|
|
131
|
-
const sid = context.getSubscriberId();
|
|
132
|
-
const { opRecords } = context;
|
|
133
|
-
opRecords.forEach((record) => {
|
|
134
|
-
const { a } = record;
|
|
135
|
-
switch (a) {
|
|
136
|
-
case 'c': {
|
|
137
|
-
const { e, d } = record;
|
|
138
|
-
this.sendRecord(e, d, record, sid, true);
|
|
139
|
-
break;
|
|
140
|
-
}
|
|
141
|
-
case 'u': {
|
|
142
|
-
const { e, d, f } = record;
|
|
143
|
-
this.sendRecord(e, f, record, sid);
|
|
144
|
-
break;
|
|
145
|
-
}
|
|
146
|
-
case 'r': {
|
|
147
|
-
const { e, f } = record;
|
|
148
|
-
this.sendRecord(e, f, record, sid);
|
|
149
|
-
break;
|
|
150
|
-
}
|
|
151
|
-
default: {
|
|
152
|
-
break;
|
|
153
|
-
}
|
|
154
|
-
}
|
|
155
|
-
});
|
|
156
|
-
}
|
|
157
|
-
}
|
|
158
|
-
exports.default = DataSubscriber;
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
const lodash_1 = require("oak-domain/lib/utils/lodash");
|
|
4
|
+
const oak_domain_1 = require("oak-domain");
|
|
5
|
+
class DataSubscriber {
|
|
6
|
+
ns;
|
|
7
|
+
contextBuilder;
|
|
8
|
+
filterMap;
|
|
9
|
+
idEntityMap;
|
|
10
|
+
constructor(ns, contextBuilder) {
|
|
11
|
+
this.ns = ns;
|
|
12
|
+
this.contextBuilder = contextBuilder;
|
|
13
|
+
this.startup();
|
|
14
|
+
this.filterMap = {};
|
|
15
|
+
this.idEntityMap = {};
|
|
16
|
+
}
|
|
17
|
+
formCreateRoomRoutine(def) {
|
|
18
|
+
const { id, entity, filter } = def;
|
|
19
|
+
return (room) => {
|
|
20
|
+
if (room === id) {
|
|
21
|
+
console.log('instance:', process.env.NODE_APP_INSTANCE, 'add filter', room);
|
|
22
|
+
// 本房间不存在,说明这个filter是新出现的
|
|
23
|
+
if (this.filterMap[entity]) {
|
|
24
|
+
// id的唯一性由前台保证,重复则无视
|
|
25
|
+
Object.assign(this.filterMap[entity], {
|
|
26
|
+
[id]: filter,
|
|
27
|
+
});
|
|
28
|
+
}
|
|
29
|
+
else {
|
|
30
|
+
Object.assign(this.filterMap, {
|
|
31
|
+
[entity]: {
|
|
32
|
+
[id]: filter,
|
|
33
|
+
}
|
|
34
|
+
});
|
|
35
|
+
}
|
|
36
|
+
this.idEntityMap[id] = entity;
|
|
37
|
+
}
|
|
38
|
+
};
|
|
39
|
+
}
|
|
40
|
+
/**
|
|
41
|
+
* 来自外部的socket连接,监听数据变化
|
|
42
|
+
*/
|
|
43
|
+
startup() {
|
|
44
|
+
this.ns.on('connection', async (socket) => {
|
|
45
|
+
try {
|
|
46
|
+
const { 'oak-cxt': cxtStr } = socket.handshake.headers;
|
|
47
|
+
const context = await this.contextBuilder(cxtStr);
|
|
48
|
+
socket.userId = context.getCurrentUserId();
|
|
49
|
+
socket.context = context;
|
|
50
|
+
socket.idMap = {};
|
|
51
|
+
socket.on('sub', async (data) => {
|
|
52
|
+
try {
|
|
53
|
+
console.log('instance:', process.env.NODE_APP_INSTANCE, 'on sub', JSON.stringify(data));
|
|
54
|
+
await Promise.all(data.map(async (ele) => {
|
|
55
|
+
const { id, entity, filter } = ele;
|
|
56
|
+
// 尝试select此filter,如果失败说明权限越界
|
|
57
|
+
await context.select(entity, {
|
|
58
|
+
data: {
|
|
59
|
+
id: 1,
|
|
60
|
+
},
|
|
61
|
+
filter,
|
|
62
|
+
}, {});
|
|
63
|
+
}));
|
|
64
|
+
}
|
|
65
|
+
catch (err) {
|
|
66
|
+
socket.emit('error', err.toString());
|
|
67
|
+
return;
|
|
68
|
+
}
|
|
69
|
+
data.forEach((ele) => {
|
|
70
|
+
const createRoomRoutine = this.formCreateRoomRoutine(ele);
|
|
71
|
+
this.ns.adapter.on('create-room', createRoomRoutine);
|
|
72
|
+
socket.join(ele.id);
|
|
73
|
+
this.ns.adapter.off('create-room', createRoomRoutine);
|
|
74
|
+
});
|
|
75
|
+
});
|
|
76
|
+
socket.on('unsub', (ids) => {
|
|
77
|
+
// console.log('instance:', process.env.NODE_APP_INSTANCE, 'on unsub', JSON.stringify(ids));
|
|
78
|
+
ids.forEach((id) => {
|
|
79
|
+
socket.leave(id);
|
|
80
|
+
});
|
|
81
|
+
});
|
|
82
|
+
}
|
|
83
|
+
catch (err) {
|
|
84
|
+
socket.emit('error', err.toString());
|
|
85
|
+
}
|
|
86
|
+
});
|
|
87
|
+
this.ns.adapter.on('delete-room', (room) => {
|
|
88
|
+
const entity = this.idEntityMap[room];
|
|
89
|
+
if (entity) {
|
|
90
|
+
// console.log('instance:', process.env.NODE_APP_INSTANCE, 'remove filter', room);
|
|
91
|
+
(0, lodash_1.unset)(this.filterMap[entity], room);
|
|
92
|
+
(0, lodash_1.unset)(this.idEntityMap, room);
|
|
93
|
+
}
|
|
94
|
+
});
|
|
95
|
+
this.ns.on('sendRecord', (entity, filter, record, isCreate) => {
|
|
96
|
+
console.log('instance:', process.env.NODE_APP_INSTANCE, 'get record from another', JSON.stringify(entity));
|
|
97
|
+
});
|
|
98
|
+
}
|
|
99
|
+
sendRecord(entity, filter, record, sid, isCreate) {
|
|
100
|
+
if (entity === 'spContractApplyment') {
|
|
101
|
+
console.log('instance:', process.env.NODE_APP_INSTANCE, 'sendRecord', JSON.stringify(entity));
|
|
102
|
+
}
|
|
103
|
+
this.ns.serverSideEmit('sendRecord', entity, filter, record, isCreate);
|
|
104
|
+
if (this.filterMap[entity]) {
|
|
105
|
+
Object.keys(this.filterMap[entity]).forEach(async (room) => {
|
|
106
|
+
const context = await this.contextBuilder();
|
|
107
|
+
const filter2 = this.filterMap[entity][room];
|
|
108
|
+
let needSend = false;
|
|
109
|
+
if (isCreate) {
|
|
110
|
+
// 如果是插入数据肯定是单行,使用相容性检测
|
|
111
|
+
const contained = await (0, oak_domain_1.checkFilterContains)(entity, context, filter2, filter, true);
|
|
112
|
+
needSend = contained;
|
|
113
|
+
}
|
|
114
|
+
else {
|
|
115
|
+
const repeled = await (0, oak_domain_1.checkFilterRepel)(entity, context, filter, filter2, true);
|
|
116
|
+
needSend = !repeled;
|
|
117
|
+
}
|
|
118
|
+
if (needSend) {
|
|
119
|
+
// console.log('instance:', process.env.NODE_APP_INSTANCE, 'needSend', JSON.stringify(room));
|
|
120
|
+
if (sid) {
|
|
121
|
+
this.ns.to(room).except(sid).emit('data', [record], [room]);
|
|
122
|
+
}
|
|
123
|
+
else {
|
|
124
|
+
this.ns.to(room).emit('data', [record], [room]);
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
});
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
onDataCommited(context) {
|
|
131
|
+
const sid = context.getSubscriberId();
|
|
132
|
+
const { opRecords } = context;
|
|
133
|
+
opRecords.forEach((record) => {
|
|
134
|
+
const { a } = record;
|
|
135
|
+
switch (a) {
|
|
136
|
+
case 'c': {
|
|
137
|
+
const { e, d } = record;
|
|
138
|
+
this.sendRecord(e, d, record, sid, true);
|
|
139
|
+
break;
|
|
140
|
+
}
|
|
141
|
+
case 'u': {
|
|
142
|
+
const { e, d, f } = record;
|
|
143
|
+
this.sendRecord(e, f, record, sid);
|
|
144
|
+
break;
|
|
145
|
+
}
|
|
146
|
+
case 'r': {
|
|
147
|
+
const { e, f } = record;
|
|
148
|
+
this.sendRecord(e, f, record, sid);
|
|
149
|
+
break;
|
|
150
|
+
}
|
|
151
|
+
default: {
|
|
152
|
+
break;
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
});
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
exports.default = DataSubscriber;
|
package/lib/DbStore.d.ts
CHANGED
|
@@ -1,20 +1,20 @@
|
|
|
1
|
-
import { MysqlStore, MySqlSelectOption, MysqlOperateOption } from 'oak-db';
|
|
2
|
-
import { EntityDict, StorageSchema, Trigger, Checker, SelectOption, SelectFreeEntities, UpdateFreeDict, AuthDeduceRelationMap, VolatileTrigger, OperateOption } from 'oak-domain/lib/types';
|
|
3
|
-
import { EntityDict as BaseEntityDict } from 'oak-domain/lib/base-app-domain';
|
|
4
|
-
import { MySQLConfiguration } from 'oak-db/lib/MySQL/types/Configuration';
|
|
5
|
-
import { BackendRuntimeContext } from 'oak-frontend-base/lib/context/BackendRuntimeContext';
|
|
6
|
-
import { AsyncContext, AsyncRowStore } from 'oak-domain/lib/store/AsyncRowStore';
|
|
7
|
-
export declare class DbStore<ED extends EntityDict & BaseEntityDict, Cxt extends BackendRuntimeContext<ED>> extends MysqlStore<ED, Cxt> implements AsyncRowStore<ED, Cxt> {
|
|
8
|
-
private executor;
|
|
9
|
-
private relationAuth;
|
|
10
|
-
constructor(storageSchema: StorageSchema<ED>, contextBuilder: (scene?: string) => Promise<Cxt>, mysqlConfiguration: MySQLConfiguration, authDeduceRelationMap: AuthDeduceRelationMap<ED>, selectFreeEntities?: SelectFreeEntities<ED>, updateFreeDict?: UpdateFreeDict<ED>, onVolatileTrigger?: <T extends keyof ED>(entity: T, trigger: VolatileTrigger<ED, T, Cxt>, ids: string[], cxtStr: string, option: OperateOption) => Promise<void>);
|
|
11
|
-
protected cascadeUpdateAsync<T extends keyof ED>(entity: T, operation: ED[T]['Operation'], context: AsyncContext<ED>, option: MysqlOperateOption): Promise<import("oak-domain/lib/types").OperationResult<ED>>;
|
|
12
|
-
operate<T extends keyof ED>(entity: T, operation: ED[T]['Operation'], context: Cxt, option: MysqlOperateOption): Promise<import("oak-domain/lib/types").OperationResult<ED>>;
|
|
13
|
-
select<T extends keyof ED>(entity: T, selection: ED[T]['Selection'], context: Cxt, option: MySqlSelectOption): Promise<Partial<ED[T]["Schema"]>[]>;
|
|
14
|
-
count<T extends keyof ED>(entity: T, selection: Pick<ED[T]['Selection'], 'filter' | 'count'>, context: Cxt, option: SelectOption): Promise<number>;
|
|
15
|
-
registerTrigger<T extends keyof ED>(trigger: Trigger<ED, T, Cxt>): void;
|
|
16
|
-
registerChecker<T extends keyof ED>(checker: Checker<ED, T, Cxt>): void;
|
|
17
|
-
setOnVolatileTrigger(onVolatileTrigger: <T extends keyof ED>(entity: T, trigger: VolatileTrigger<ED, T, Cxt>, ids: string[], cxtStr: string, option: OperateOption) => Promise<void>): void;
|
|
18
|
-
execVolatileTrigger<T extends keyof ED>(entity: T, name: string, ids: string[], context: Cxt, option: OperateOption): Promise<void>;
|
|
19
|
-
checkpoint(ts: number): Promise<number>;
|
|
20
|
-
}
|
|
1
|
+
import { MysqlStore, MySqlSelectOption, MysqlOperateOption } from 'oak-db';
|
|
2
|
+
import { EntityDict, StorageSchema, Trigger, Checker, SelectOption, SelectFreeEntities, UpdateFreeDict, AuthDeduceRelationMap, VolatileTrigger, OperateOption } from 'oak-domain/lib/types';
|
|
3
|
+
import { EntityDict as BaseEntityDict } from 'oak-domain/lib/base-app-domain';
|
|
4
|
+
import { MySQLConfiguration } from 'oak-db/lib/MySQL/types/Configuration';
|
|
5
|
+
import { BackendRuntimeContext } from 'oak-frontend-base/lib/context/BackendRuntimeContext';
|
|
6
|
+
import { AsyncContext, AsyncRowStore } from 'oak-domain/lib/store/AsyncRowStore';
|
|
7
|
+
export declare class DbStore<ED extends EntityDict & BaseEntityDict, Cxt extends BackendRuntimeContext<ED>> extends MysqlStore<ED, Cxt> implements AsyncRowStore<ED, Cxt> {
|
|
8
|
+
private executor;
|
|
9
|
+
private relationAuth;
|
|
10
|
+
constructor(storageSchema: StorageSchema<ED>, contextBuilder: (scene?: string) => Promise<Cxt>, mysqlConfiguration: MySQLConfiguration, authDeduceRelationMap: AuthDeduceRelationMap<ED>, selectFreeEntities?: SelectFreeEntities<ED>, updateFreeDict?: UpdateFreeDict<ED>, onVolatileTrigger?: <T extends keyof ED>(entity: T, trigger: VolatileTrigger<ED, T, Cxt>, ids: string[], cxtStr: string, option: OperateOption) => Promise<void>);
|
|
11
|
+
protected cascadeUpdateAsync<T extends keyof ED>(entity: T, operation: ED[T]['Operation'], context: AsyncContext<ED>, option: MysqlOperateOption): Promise<import("oak-domain/lib/types").OperationResult<ED>>;
|
|
12
|
+
operate<T extends keyof ED>(entity: T, operation: ED[T]['Operation'], context: Cxt, option: MysqlOperateOption): Promise<import("oak-domain/lib/types").OperationResult<ED>>;
|
|
13
|
+
select<T extends keyof ED>(entity: T, selection: ED[T]['Selection'], context: Cxt, option: MySqlSelectOption): Promise<Partial<ED[T]["Schema"]>[]>;
|
|
14
|
+
count<T extends keyof ED>(entity: T, selection: Pick<ED[T]['Selection'], 'filter' | 'count'>, context: Cxt, option: SelectOption): Promise<number>;
|
|
15
|
+
registerTrigger<T extends keyof ED>(trigger: Trigger<ED, T, Cxt>): void;
|
|
16
|
+
registerChecker<T extends keyof ED>(checker: Checker<ED, T, Cxt>): void;
|
|
17
|
+
setOnVolatileTrigger(onVolatileTrigger: <T extends keyof ED>(entity: T, trigger: VolatileTrigger<ED, T, Cxt>, ids: string[], cxtStr: string, option: OperateOption) => Promise<void>): void;
|
|
18
|
+
execVolatileTrigger<T extends keyof ED>(entity: T, name: string, ids: string[], context: Cxt, option: OperateOption): Promise<void>;
|
|
19
|
+
checkpoint(ts: number): Promise<number>;
|
|
20
|
+
}
|
package/lib/Synchronizer.d.ts
CHANGED
|
@@ -1,36 +1,46 @@
|
|
|
1
|
-
import { EntityDict, StorageSchema, EndpointItem, SyncConfig } from 'oak-domain/lib/types';
|
|
2
|
-
import { VolatileTrigger } from 'oak-domain/lib/types/Trigger';
|
|
3
|
-
import { EntityDict as BaseEntityDict } from 'oak-domain/lib/base-app-domain';
|
|
4
|
-
import { BackendRuntimeContext } from 'oak-frontend-base/lib/context/BackendRuntimeContext';
|
|
5
|
-
export default class Synchronizer<ED extends EntityDict & BaseEntityDict, Cxt extends BackendRuntimeContext<ED>> {
|
|
6
|
-
private config;
|
|
7
|
-
private schema;
|
|
8
|
-
private
|
|
9
|
-
private
|
|
10
|
-
private
|
|
11
|
-
private
|
|
12
|
-
/**
|
|
13
|
-
* 向某一个远端对象push opers。根据幂等性,这里如果失败了必须反复推送
|
|
14
|
-
* @param channel
|
|
15
|
-
* @param retry
|
|
16
|
-
*/
|
|
17
|
-
private
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
*
|
|
21
|
-
*
|
|
22
|
-
*
|
|
23
|
-
*
|
|
24
|
-
*
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
private
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
*
|
|
32
|
-
* @
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
1
|
+
import { EntityDict, StorageSchema, EndpointItem, SyncConfig, Watcher } from 'oak-domain/lib/types';
|
|
2
|
+
import { VolatileTrigger } from 'oak-domain/lib/types/Trigger';
|
|
3
|
+
import { EntityDict as BaseEntityDict } from 'oak-domain/lib/base-app-domain';
|
|
4
|
+
import { BackendRuntimeContext } from 'oak-frontend-base/lib/context/BackendRuntimeContext';
|
|
5
|
+
export default class Synchronizer<ED extends EntityDict & BaseEntityDict, Cxt extends BackendRuntimeContext<ED>> {
|
|
6
|
+
private config;
|
|
7
|
+
private schema;
|
|
8
|
+
private remotePullInfoMap;
|
|
9
|
+
private pullMaxBornAtMap;
|
|
10
|
+
private remotePushChannel;
|
|
11
|
+
private pushAccessMap;
|
|
12
|
+
/**
|
|
13
|
+
* 向某一个远端对象push opers。根据幂等性,这里如果失败了必须反复推送
|
|
14
|
+
* @param channel
|
|
15
|
+
* @param retry
|
|
16
|
+
*/
|
|
17
|
+
private startChannel;
|
|
18
|
+
private joinChannel;
|
|
19
|
+
/**
|
|
20
|
+
* 推向远端Node的oper,需要严格保证按产生的时间序推送。根据幂等原理,这里必须要推送成功
|
|
21
|
+
* 因此在这里要实现两点:
|
|
22
|
+
* 1)oper如果推送失败了,必须留存在queue中,以保证在后面产生的oper之前推送
|
|
23
|
+
* 2)当对queue中增加oper时,要检查是否有重(有重说明之前失败过),如果无重则将之放置在队列尾
|
|
24
|
+
*
|
|
25
|
+
* 其实这里还无法严格保证先产生的oper一定先到达被推送,因为volatile trigger是在事务提交后再发生的,但这种情况在目前应该跑不出来,在实际执行oper的时候assert掉先。by Xc 20240226
|
|
26
|
+
*/
|
|
27
|
+
private pushOper;
|
|
28
|
+
/**
|
|
29
|
+
* 因为应用可能是多租户,得提前确定context下的selfEncryptInfo
|
|
30
|
+
* 由于checkpoint时无法区别不同上下文之间的未完成oper数据,所以接口只能这样设计
|
|
31
|
+
* @param id
|
|
32
|
+
* @param context
|
|
33
|
+
* @param selfEncryptInfo
|
|
34
|
+
* @returns
|
|
35
|
+
*/
|
|
36
|
+
private synchronizeOpersToRemote;
|
|
37
|
+
private makeCreateOperTrigger;
|
|
38
|
+
constructor(config: SyncConfig<ED, Cxt>, schema: StorageSchema<ED>);
|
|
39
|
+
/**
|
|
40
|
+
* 根据sync的定义,生成对应的 commit triggers
|
|
41
|
+
* @returns
|
|
42
|
+
*/
|
|
43
|
+
getSyncTriggers(): VolatileTrigger<ED, keyof ED, Cxt>[];
|
|
44
|
+
getSyncRoutine(): Watcher<ED, keyof ED, Cxt>;
|
|
45
|
+
getSelfEndpoint(): EndpointItem<ED, Cxt>;
|
|
46
|
+
}
|