@entity-access/entity-access 1.0.111 → 1.0.113
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/dist/common/ObjectPool.d.ts +6 -2
- package/dist/common/ObjectPool.d.ts.map +1 -1
- package/dist/common/ObjectPool.js +33 -27
- package/dist/common/ObjectPool.js.map +1 -1
- package/dist/common/sleep.d.ts.map +1 -1
- package/dist/common/sleep.js +23 -2
- package/dist/common/sleep.js.map +1 -1
- package/dist/decorators/IColumn.d.ts +1 -0
- package/dist/decorators/IColumn.d.ts.map +1 -1
- package/dist/decorators/Relate.d.ts +16 -10
- package/dist/decorators/Relate.d.ts.map +1 -1
- package/dist/decorators/Relate.js +58 -44
- package/dist/decorators/Relate.js.map +1 -1
- package/dist/entity-query/EntityType.d.ts.map +1 -1
- package/dist/entity-query/EntityType.js +5 -0
- package/dist/entity-query/EntityType.js.map +1 -1
- package/dist/tests/model/ShoppingContext.d.ts +4 -0
- package/dist/tests/model/ShoppingContext.d.ts.map +1 -1
- package/dist/tests/model/ShoppingContext.js +14 -1
- package/dist/tests/model/ShoppingContext.js.map +1 -1
- package/dist/tests/model/UseFile.d.ts +8 -0
- package/dist/tests/model/UseFile.d.ts.map +1 -0
- package/dist/tests/model/UseFile.js +36 -0
- package/dist/tests/model/UseFile.js.map +1 -0
- package/dist/tests/pool/pool-test.d.ts.map +1 -1
- package/dist/tests/pool/pool-test.js +55 -47
- package/dist/tests/pool/pool-test.js.map +1 -1
- package/dist/tsconfig.tsbuildinfo +1 -1
- package/package.json +1 -1
- package/src/common/ObjectPool.ts +44 -28
- package/src/common/sleep.ts +23 -2
- package/src/decorators/IColumn.ts +2 -0
- package/src/decorators/Relate.ts +97 -95
- package/src/entity-query/EntityType.ts +5 -0
- package/src/tests/model/ShoppingContext.ts +14 -2
- package/src/tests/model/UseFile.ts +24 -0
- package/src/tests/pool/pool-test.ts +58 -51
package/src/common/ObjectPool.ts
CHANGED
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
import EntityAccessError from "./EntityAccessError.js";
|
|
3
3
|
import TimedCache from "./cache/TimedCache.js";
|
|
4
4
|
import sleep from "./sleep.js";
|
|
5
|
+
import "./IDisposable.js";
|
|
5
6
|
|
|
6
7
|
interface IObjectPool<T> {
|
|
7
8
|
factory?: () => T;
|
|
@@ -22,12 +23,16 @@ interface IObjectPool<T> {
|
|
|
22
23
|
|
|
23
24
|
/**
|
|
24
25
|
* Max wait in milliseconds before creating
|
|
25
|
-
* new object, default is
|
|
26
|
+
* new object, default is 5 seconds
|
|
26
27
|
*/
|
|
27
28
|
maxWait?: number;
|
|
29
|
+
|
|
30
|
+
logger?: (text: string) => void;
|
|
28
31
|
}
|
|
29
32
|
|
|
30
|
-
export type IPooledObject<T> = T & {
|
|
33
|
+
export type IPooledObject<T> = T & {
|
|
34
|
+
[Symbol.asyncDisposable](): Promise<any>;
|
|
35
|
+
};
|
|
31
36
|
|
|
32
37
|
/**
|
|
33
38
|
* Most pool implementations are poor and are having too many errors.
|
|
@@ -63,10 +68,13 @@ export default class ObjectPool<T> {
|
|
|
63
68
|
|
|
64
69
|
private awaited: Set<AbortController> = new Set();
|
|
65
70
|
|
|
71
|
+
private logger: (text: string) => void;
|
|
72
|
+
|
|
66
73
|
constructor({
|
|
67
74
|
maxSize = 40,
|
|
68
75
|
maxWait = 5000,
|
|
69
76
|
poolSize = 20,
|
|
77
|
+
logger,
|
|
70
78
|
asyncFactory,
|
|
71
79
|
factory,
|
|
72
80
|
destroy,
|
|
@@ -79,6 +87,7 @@ export default class ObjectPool<T> {
|
|
|
79
87
|
this.factory = factory;
|
|
80
88
|
this.destroy = destroy;
|
|
81
89
|
this.subscribeForRemoval = subscribeForRemoval;
|
|
90
|
+
this.logger = logger;
|
|
82
91
|
}
|
|
83
92
|
|
|
84
93
|
public async dispose() {
|
|
@@ -94,51 +103,58 @@ export default class ObjectPool<T> {
|
|
|
94
103
|
}
|
|
95
104
|
|
|
96
105
|
public async acquire(): Promise<IPooledObject<T>> {
|
|
97
|
-
let
|
|
98
|
-
if(!
|
|
106
|
+
let existing = this.free.pop();
|
|
107
|
+
if(!existing) {
|
|
99
108
|
if (this.total >= this.poolSize) {
|
|
100
109
|
const a = new AbortController();
|
|
101
110
|
this.awaited.add(a);
|
|
102
111
|
await sleep(this.maxWait, a.signal);
|
|
103
112
|
this.awaited.delete(a);
|
|
104
|
-
|
|
113
|
+
existing = this.free.pop();
|
|
105
114
|
}
|
|
106
115
|
}
|
|
107
|
-
if(
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
throw new EntityAccessError(`Maximum size of pool reached. Retry after sometime.`);
|
|
111
|
-
}
|
|
112
|
-
this.total++;
|
|
116
|
+
if(existing) {
|
|
117
|
+
return existing as IPooledObject<T>;
|
|
118
|
+
}
|
|
113
119
|
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
item = this.factory();
|
|
117
|
-
} else {
|
|
118
|
-
item = await this.asyncFactory();
|
|
119
|
-
}
|
|
120
|
-
this.subscribeForRemoval(item as unknown as any, () => {
|
|
121
|
-
const index = this.free.indexOf(item);
|
|
122
|
-
this.free.splice(index, 1);
|
|
123
|
-
});
|
|
120
|
+
if (this.total >= this.maxSize) {
|
|
121
|
+
throw new EntityAccessError(`Maximum size of pool reached. Retry after sometime.`);
|
|
124
122
|
}
|
|
123
|
+
this.total++;
|
|
124
|
+
|
|
125
|
+
// create new..
|
|
126
|
+
const item = this.factory?.() ?? (await this.asyncFactory());
|
|
127
|
+
this.subscribeForRemoval(item as unknown as any, () => {
|
|
128
|
+
const index = this.free.indexOf(item);
|
|
129
|
+
this.free.splice(index, 1);
|
|
130
|
+
});
|
|
131
|
+
|
|
132
|
+
return this.setupItem(item);
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
|
|
136
|
+
private setupItem(item: T) {
|
|
125
137
|
const pooledItem = item as IPooledObject<T>;
|
|
126
138
|
pooledItem[Symbol.asyncDisposable] = async () => {
|
|
139
|
+
delete this[Symbol.asyncDisposable];
|
|
127
140
|
if (this.free.length < this.poolSize) {
|
|
128
|
-
this.
|
|
129
|
-
|
|
141
|
+
this.logger?.(`Pooled item ${pooledItem} freed.`);
|
|
142
|
+
this.free.push(pooledItem);
|
|
143
|
+
for (const [iterator] of this.awaited.entries()) {
|
|
130
144
|
this.awaited.delete(iterator);
|
|
131
145
|
iterator.abort();
|
|
132
|
-
|
|
146
|
+
return;
|
|
133
147
|
}
|
|
134
|
-
|
|
135
|
-
await this.destroy(item);
|
|
136
|
-
this.total--;
|
|
148
|
+
return;
|
|
137
149
|
}
|
|
150
|
+
this.total--;
|
|
151
|
+
this.logger?.(`Pooled item ${pooledItem} destoryed.`);
|
|
152
|
+
void this.destroy(pooledItem)?.catch(console.error);
|
|
138
153
|
};
|
|
154
|
+
this.logger?.(`Pooled item ${pooledItem} acquired.`);
|
|
155
|
+
this.logger?.(`Item ${pooledItem} has disposable ${typeof pooledItem[Symbol.asyncDisposable]}`);
|
|
139
156
|
return item as IPooledObject<T>;
|
|
140
157
|
}
|
|
141
|
-
|
|
142
158
|
}
|
|
143
159
|
|
|
144
160
|
export class NamedObjectPool<T> {
|
package/src/common/sleep.ts
CHANGED
|
@@ -3,14 +3,35 @@ export default async function sleep(n: number, signal?: AbortSignal, throwOnAbor
|
|
|
3
3
|
return;
|
|
4
4
|
}
|
|
5
5
|
return new Promise<void>((resolve, reject) => {
|
|
6
|
+
if (!signal) {
|
|
7
|
+
setTimeout(resolve, n);
|
|
8
|
+
return;
|
|
9
|
+
}
|
|
10
|
+
let resolved = false;
|
|
11
|
+
const old = resolve;
|
|
12
|
+
resolve = () => {
|
|
13
|
+
if (resolved) {
|
|
14
|
+
return;
|
|
15
|
+
}
|
|
16
|
+
resolved = true;
|
|
17
|
+
old();
|
|
18
|
+
};
|
|
19
|
+
const oldReject = reject;
|
|
20
|
+
reject = (r) => {
|
|
21
|
+
if (resolved) {
|
|
22
|
+
return;
|
|
23
|
+
}
|
|
24
|
+
resolved = true;
|
|
25
|
+
oldReject(r);
|
|
26
|
+
};
|
|
6
27
|
const id = setTimeout(resolve, n);
|
|
7
|
-
signal
|
|
28
|
+
signal.onabort = () => {
|
|
8
29
|
clearTimeout(id);
|
|
9
30
|
if (throwOnAbort) {
|
|
10
31
|
reject("cancelled");
|
|
11
32
|
return;
|
|
12
33
|
}
|
|
13
34
|
resolve();
|
|
14
|
-
}
|
|
35
|
+
};
|
|
15
36
|
});
|
|
16
37
|
}
|
package/src/decorators/Relate.ts
CHANGED
|
@@ -2,95 +2,62 @@ import type { IClassOf } from "./IClassOf.js";
|
|
|
2
2
|
import SchemaRegistry from "./SchemaRegistry.js";
|
|
3
3
|
import NameParser from "./parser/NameParser.js";
|
|
4
4
|
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
}: {
|
|
11
|
-
|
|
12
|
-
foreignKey: (item: T) => any;
|
|
13
|
-
|
|
14
|
-
inverseProperty: (item: TRelated) => T[];
|
|
15
|
-
|
|
16
|
-
inverseKey?: (item: TRelated) => any;
|
|
17
|
-
dotNotCreateIndex?: boolean;
|
|
18
|
-
}
|
|
19
|
-
|
|
20
|
-
) {
|
|
21
|
-
return (target: T, key: string): any => {
|
|
22
|
-
|
|
23
|
-
const cn = target.constructor ?? target;
|
|
24
|
-
const type = SchemaRegistry.model(cn);
|
|
25
|
-
|
|
26
|
-
type.addRelation({
|
|
27
|
-
type,
|
|
28
|
-
name: key,
|
|
29
|
-
foreignKey: NameParser.parseMember(name),
|
|
30
|
-
relatedTypeClass: c,
|
|
31
|
-
relatedName: NameParser.parseMember(inv),
|
|
32
|
-
relatedKey: invKey ? NameParser.parseMember(invKey) : void 0,
|
|
33
|
-
dotNotCreateIndex
|
|
34
|
-
});
|
|
35
|
-
|
|
36
|
-
};
|
|
5
|
+
export interface IRelatedType<T, TRelated> {
|
|
6
|
+
property: (item: T) => TRelated;
|
|
7
|
+
inverseProperty: (item: TRelated) => T[];
|
|
8
|
+
inverseKey?: (item: TRelated) => any;
|
|
9
|
+
dotNotCreateIndex?: boolean;
|
|
37
10
|
}
|
|
38
11
|
|
|
39
|
-
export
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
foreignKey: (item: T) => any;
|
|
45
|
-
|
|
46
|
-
inverseProperty: (item: TRelated) => T;
|
|
47
|
-
|
|
48
|
-
inverseKey?: (item: TRelated) => any;
|
|
49
|
-
dotNotCreateIndex?: boolean;
|
|
50
|
-
}
|
|
51
|
-
|
|
52
|
-
) {
|
|
53
|
-
return (target: T, key: string): any => {
|
|
54
|
-
|
|
55
|
-
const cn = target.constructor ?? target;
|
|
56
|
-
const type = SchemaRegistry.model(cn);
|
|
57
|
-
|
|
58
|
-
const r = type.addRelation({
|
|
59
|
-
type,
|
|
60
|
-
name: key,
|
|
61
|
-
foreignKey: NameParser.parseMember(name),
|
|
62
|
-
relatedTypeClass: c,
|
|
63
|
-
relatedName: NameParser.parseMember(inv),
|
|
64
|
-
relatedKey: invKey ? NameParser.parseMember(invKey) : void 0,
|
|
65
|
-
dotNotCreateIndex
|
|
66
|
-
});
|
|
67
|
-
r.relatedRelation.isCollection = false;
|
|
68
|
-
};
|
|
12
|
+
export interface IRelatedTypeOne<T, TRelated> {
|
|
13
|
+
property: (item: T) => TRelated;
|
|
14
|
+
inverseProperty: (item: TRelated) => T;
|
|
15
|
+
inverseKey?: (item: TRelated) => any;
|
|
16
|
+
dotNotCreateIndex?: boolean;
|
|
69
17
|
}
|
|
70
18
|
|
|
71
|
-
export
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
19
|
+
export interface IRelatedTypeWithType<T, TRelated> {
|
|
20
|
+
type: () => IClassOf<TRelated>,
|
|
21
|
+
property: (item: T) => TRelated;
|
|
22
|
+
inverseProperty: (item: TRelated) => T[];
|
|
23
|
+
inverseKey?: (item: TRelated) => any;
|
|
24
|
+
dotNotCreateIndex?: boolean;
|
|
25
|
+
}
|
|
76
26
|
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
27
|
+
export interface IRelatedTypeOneWithType<T, TRelated> {
|
|
28
|
+
type: () => IClassOf<TRelated>,
|
|
29
|
+
property: (item: T) => TRelated;
|
|
30
|
+
inverseProperty: (item: TRelated) => T;
|
|
31
|
+
inverseKey?: (item: TRelated) => any;
|
|
32
|
+
dotNotCreateIndex?: boolean;
|
|
33
|
+
}
|
|
34
|
+
export function RelateTo<T, TRelated>(p: IRelatedTypeWithType<T, TRelated>): (target: T, key: string) => any;
|
|
35
|
+
export function RelateTo<T, TRelated>(c: IClassOf<TRelated>, p: IRelatedType<T, TRelated>): (target: T, key: string) => any;
|
|
36
|
+
export function RelateTo(c, p?): any {
|
|
37
|
+
|
|
38
|
+
if (p === void 0) {
|
|
39
|
+
p = c;
|
|
40
|
+
// c = p.type?.();
|
|
41
|
+
if (p.type) {
|
|
42
|
+
c = void 0;
|
|
43
|
+
}
|
|
81
44
|
}
|
|
82
45
|
|
|
83
|
-
|
|
84
|
-
|
|
46
|
+
const { property, inverseKey: invKey, inverseProperty, dotNotCreateIndex } = p;
|
|
47
|
+
|
|
48
|
+
return (target: any, foreignKey: string): any => {
|
|
85
49
|
|
|
86
50
|
const cn = target.constructor ?? target;
|
|
87
|
-
const
|
|
51
|
+
const entityType = SchemaRegistry.model(cn);
|
|
52
|
+
|
|
53
|
+
const name = NameParser.parseMember(property);
|
|
88
54
|
|
|
89
|
-
|
|
90
|
-
type,
|
|
91
|
-
name
|
|
55
|
+
entityType.addRelation({
|
|
56
|
+
type: entityType,
|
|
57
|
+
name,
|
|
92
58
|
foreignKey,
|
|
93
59
|
relatedTypeClass: c,
|
|
60
|
+
relatedTypeClassFactory: p.type,
|
|
94
61
|
relatedName: NameParser.parseMember(inverseProperty),
|
|
95
62
|
relatedKey: invKey ? NameParser.parseMember(invKey) : void 0,
|
|
96
63
|
dotNotCreateIndex
|
|
@@ -99,34 +66,69 @@ export function RelateTo<T, TRelated>(c: IClassOf<TRelated>,
|
|
|
99
66
|
};
|
|
100
67
|
}
|
|
101
68
|
|
|
69
|
+
export function RelateToOne<T, TRelated>(p: IRelatedTypeOneWithType<T, TRelated>): (target: T, key: string) => any;
|
|
70
|
+
export function RelateToOne<T, TRelated>(c: IClassOf<TRelated>, p: IRelatedTypeOne<T, TRelated>): (target: T, key: string) => any;
|
|
71
|
+
export function RelateToOne(c, p?): any {
|
|
102
72
|
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
inverseProperty: (item: TRelated) => T;
|
|
110
|
-
|
|
111
|
-
inverseKey?: (item: TRelated) => any;
|
|
112
|
-
dotNotCreateIndex?: boolean;
|
|
73
|
+
if (p === void 0) {
|
|
74
|
+
p = c;
|
|
75
|
+
// c = p.type?.();
|
|
76
|
+
if (p.type) {
|
|
77
|
+
c = void 0;
|
|
78
|
+
}
|
|
113
79
|
}
|
|
114
80
|
|
|
115
|
-
|
|
116
|
-
|
|
81
|
+
const { property, inverseKey: invKey, inverseProperty, dotNotCreateIndex } = p;
|
|
82
|
+
|
|
83
|
+
return (target: any, foreignKey: string): any => {
|
|
117
84
|
|
|
118
85
|
const cn = target.constructor ?? target;
|
|
119
|
-
const
|
|
86
|
+
const entityType = SchemaRegistry.model(cn);
|
|
120
87
|
|
|
121
|
-
const
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
88
|
+
const name = NameParser.parseMember(property);
|
|
89
|
+
|
|
90
|
+
entityType.addRelation({
|
|
91
|
+
type: entityType,
|
|
92
|
+
name,
|
|
93
|
+
foreignKey,
|
|
125
94
|
relatedTypeClass: c,
|
|
126
|
-
|
|
95
|
+
relatedTypeClassFactory: p.type,
|
|
96
|
+
relatedName: NameParser.parseMember(inverseProperty),
|
|
127
97
|
relatedKey: invKey ? NameParser.parseMember(invKey) : void 0,
|
|
128
98
|
dotNotCreateIndex,
|
|
129
99
|
singleInverseRelation: true
|
|
130
100
|
});
|
|
101
|
+
|
|
131
102
|
};
|
|
132
103
|
}
|
|
104
|
+
|
|
105
|
+
// export function RelateToOne<T, TRelated>(c: IClassOf<TRelated>,
|
|
106
|
+
// {
|
|
107
|
+
// property: name, inverseProperty: inv, inverseKey: invKey, dotNotCreateIndex
|
|
108
|
+
// }: {
|
|
109
|
+
|
|
110
|
+
// property: (item: T) => TRelated;
|
|
111
|
+
// inverseProperty: (item: TRelated) => T;
|
|
112
|
+
|
|
113
|
+
// inverseKey?: (item: TRelated) => any;
|
|
114
|
+
// dotNotCreateIndex?: boolean;
|
|
115
|
+
// }
|
|
116
|
+
|
|
117
|
+
// ) {
|
|
118
|
+
// return (target: T, key: string): any => {
|
|
119
|
+
|
|
120
|
+
// const cn = target.constructor ?? target;
|
|
121
|
+
// const type = SchemaRegistry.model(cn);
|
|
122
|
+
|
|
123
|
+
// const r = type.addRelation({
|
|
124
|
+
// type,
|
|
125
|
+
// name: NameParser.parseMember(name),
|
|
126
|
+
// foreignKey: key,
|
|
127
|
+
// relatedTypeClass: c,
|
|
128
|
+
// relatedName: NameParser.parseMember(inv),
|
|
129
|
+
// relatedKey: invKey ? NameParser.parseMember(invKey) : void 0,
|
|
130
|
+
// dotNotCreateIndex,
|
|
131
|
+
// singleInverseRelation: true
|
|
132
|
+
// });
|
|
133
|
+
// };
|
|
134
|
+
// }
|
|
@@ -106,6 +106,11 @@ export default class EntityType {
|
|
|
106
106
|
// we will also set fk to the corresponding column
|
|
107
107
|
this.relations.push(relation);
|
|
108
108
|
this.relationMap.set(relation.name, relation);
|
|
109
|
+
if (getInverseModel) {
|
|
110
|
+
if (!relation.relatedTypeClass) {
|
|
111
|
+
relation.relatedTypeClass = relation.relatedTypeClassFactory();
|
|
112
|
+
}
|
|
113
|
+
}
|
|
109
114
|
|
|
110
115
|
// find fk...
|
|
111
116
|
let fkColumn = this.fieldMap.get(relation.foreignKey);
|
|
@@ -1,9 +1,10 @@
|
|
|
1
1
|
import EntityContext from "../../model/EntityContext.js";
|
|
2
2
|
import Column from "../../decorators/Column.js";
|
|
3
|
-
import
|
|
3
|
+
import { RelateTo, RelateToOne } from "../../decorators/Relate.js";
|
|
4
4
|
import Table from "../../decorators/Table.js";
|
|
5
5
|
import Index from "../../decorators/Index.js";
|
|
6
6
|
import DateTime from "../../types/DateTime.js";
|
|
7
|
+
import { UserFile } from "./UseFile.js";
|
|
7
8
|
|
|
8
9
|
export const statusPublished = "published";
|
|
9
10
|
|
|
@@ -47,6 +48,13 @@ export class User {
|
|
|
47
48
|
@Column({ dataType: "Char", length: 200 })
|
|
48
49
|
public userName: string;
|
|
49
50
|
|
|
51
|
+
@RelateTo({
|
|
52
|
+
type: () => UserFile,
|
|
53
|
+
property: (u) => u.photo,
|
|
54
|
+
inverseProperty: (uf) => uf.photoUsers
|
|
55
|
+
})
|
|
56
|
+
public photoID: number;
|
|
57
|
+
|
|
50
58
|
public profile: UserProfile;
|
|
51
59
|
|
|
52
60
|
public ownedProducts: Product[];
|
|
@@ -55,6 +63,9 @@ export class User {
|
|
|
55
63
|
|
|
56
64
|
public categories: UserCategory[];
|
|
57
65
|
|
|
66
|
+
public files: UserFile[];
|
|
67
|
+
|
|
68
|
+
public photo: UserFile;
|
|
58
69
|
}
|
|
59
70
|
|
|
60
71
|
@Table("Categories")
|
|
@@ -263,7 +274,8 @@ export class OrderItem {
|
|
|
263
274
|
public orderItemID: number;
|
|
264
275
|
|
|
265
276
|
@Column()
|
|
266
|
-
@RelateTo(
|
|
277
|
+
@RelateTo({
|
|
278
|
+
type: () => Order,
|
|
267
279
|
property: (orderItem) => orderItem.order,
|
|
268
280
|
inverseProperty: (order) => order.orderItems
|
|
269
281
|
})
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import Column from "../../decorators/Column.js";
|
|
2
|
+
import { RelateTo } from "../../decorators/Relate.js";
|
|
3
|
+
import Table from "../../decorators/Table.js";
|
|
4
|
+
import { User } from "./ShoppingContext.js";
|
|
5
|
+
|
|
6
|
+
@Table("UserFiles")
|
|
7
|
+
export class UserFile {
|
|
8
|
+
|
|
9
|
+
@Column({ dataType: "BigInt", key: true, autoGenerate: true })
|
|
10
|
+
public fileID: number;
|
|
11
|
+
|
|
12
|
+
@Column({ dataType: "BigInt"})
|
|
13
|
+
@RelateTo({
|
|
14
|
+
type: () => User,
|
|
15
|
+
property: (uf) => uf.user,
|
|
16
|
+
inverseProperty: (u) => u.files
|
|
17
|
+
})
|
|
18
|
+
public userID: number;
|
|
19
|
+
|
|
20
|
+
public user: User;
|
|
21
|
+
|
|
22
|
+
public photoUsers: User[];
|
|
23
|
+
|
|
24
|
+
}
|
|
@@ -3,70 +3,77 @@ import assert from "assert";
|
|
|
3
3
|
import ObjectPool from "../../common/ObjectPool.js";
|
|
4
4
|
import sleep from "../../common/sleep.js";
|
|
5
5
|
|
|
6
|
-
export default async function () {
|
|
7
|
-
const pool = new ObjectPool({
|
|
8
|
-
asyncFactory: async () => {
|
|
9
|
-
await sleep(10);
|
|
10
|
-
return Promise.resolve({});
|
|
11
|
-
},
|
|
12
|
-
subscribeForRemoval: (po, clear) => void 0,
|
|
13
|
-
destroy(item) {
|
|
14
|
-
return sleep(10);
|
|
15
|
-
},
|
|
16
|
-
maxSize: 5,
|
|
17
|
-
poolSize: 2,
|
|
18
|
-
maxWait: 100
|
|
19
|
-
});
|
|
20
6
|
|
|
21
|
-
|
|
22
|
-
|
|
7
|
+
export default async function () {
|
|
8
|
+
let id = 1;
|
|
9
|
+
const logs = [] as string[];
|
|
10
|
+
try {
|
|
11
|
+
const pool = new ObjectPool({
|
|
12
|
+
asyncFactory: async () => {
|
|
13
|
+
await sleep(1);
|
|
14
|
+
return Promise.resolve({ id: id++, toString() { return `item-${this.id}`; } });
|
|
15
|
+
},
|
|
16
|
+
subscribeForRemoval: (po, clear) => void 0,
|
|
17
|
+
destroy(item) {
|
|
18
|
+
return Promise.resolve();
|
|
19
|
+
},
|
|
20
|
+
maxSize: 5,
|
|
21
|
+
poolSize: 2,
|
|
22
|
+
maxWait: 5000,
|
|
23
|
+
logger: (t) => logs.push(t)
|
|
24
|
+
});
|
|
23
25
|
|
|
24
|
-
|
|
26
|
+
const c1 = await pool.acquire();
|
|
27
|
+
const c2 = await pool.acquire();
|
|
25
28
|
|
|
26
|
-
|
|
29
|
+
assert.equal(2, c2.id);
|
|
27
30
|
|
|
28
|
-
|
|
29
|
-
assert.equal(c1, c3);
|
|
31
|
+
assert.notStrictEqual(c1, c2);
|
|
30
32
|
|
|
31
|
-
|
|
33
|
+
await c1[Symbol.asyncDisposable]();
|
|
32
34
|
|
|
33
|
-
|
|
34
|
-
assert.notEqual(c4, c1);
|
|
35
|
-
assert.notEqual(c4, c2);
|
|
35
|
+
assert.equal(pool.freeSize, 1);
|
|
36
36
|
|
|
37
|
-
|
|
37
|
+
const c3 = await pool.acquire();
|
|
38
|
+
assert.strictEqual(c1, c3);
|
|
39
|
+
assert.notStrictEqual(c2, c3);
|
|
40
|
+
assert.strictEqual(pool.freeSize, 0);
|
|
38
41
|
|
|
39
|
-
|
|
42
|
+
const c4 = await pool.acquire();
|
|
43
|
+
assert.notStrictEqual(c4, c1);
|
|
44
|
+
assert.notStrictEqual(c4, c2);
|
|
40
45
|
|
|
41
|
-
|
|
46
|
+
assert.strictEqual(pool.currentSize, 3);
|
|
42
47
|
|
|
43
|
-
|
|
44
|
-
|
|
48
|
+
await c4[Symbol.asyncDisposable]();
|
|
49
|
+
await c2[Symbol.asyncDisposable]();
|
|
45
50
|
|
|
46
|
-
|
|
51
|
+
assert.equal(pool.currentSize, 3);
|
|
47
52
|
|
|
48
|
-
|
|
53
|
+
assert.equal(pool.freeSize, 2);
|
|
49
54
|
|
|
50
|
-
await pool.acquire();
|
|
51
|
-
await pool.acquire();
|
|
52
|
-
await pool.acquire();
|
|
53
|
-
await pool.acquire();
|
|
54
|
-
const last = await pool.acquire();
|
|
55
|
-
let lastError;
|
|
56
|
-
try {
|
|
57
55
|
await pool.acquire();
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
56
|
+
await pool.acquire();
|
|
57
|
+
await pool.acquire();
|
|
58
|
+
const last = await pool.acquire();
|
|
59
|
+
let lastError;
|
|
60
|
+
try {
|
|
61
|
+
await pool.acquire();
|
|
62
|
+
} catch (error) {
|
|
63
|
+
lastError = error;
|
|
64
|
+
}
|
|
65
|
+
if (!lastError) {
|
|
66
|
+
assert.fail("Failed");
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
// free last after few milliseconds
|
|
70
|
+
setTimeout(() => {
|
|
71
|
+
last[Symbol.asyncDisposable]().catch(console.error);
|
|
72
|
+
}, 10);
|
|
73
|
+
|
|
74
|
+
// this should not fail
|
|
75
|
+
await pool.acquire();
|
|
76
|
+
} finally {
|
|
77
|
+
console.log(logs.join("\n"));
|
|
63
78
|
}
|
|
64
|
-
|
|
65
|
-
// free last after few milliseconds
|
|
66
|
-
setTimeout(() => {
|
|
67
|
-
last[Symbol.asyncDisposable]().catch(console.error);
|
|
68
|
-
}, 10);
|
|
69
|
-
|
|
70
|
-
// this should not fail
|
|
71
|
-
await pool.acquire();
|
|
72
79
|
}
|