修改了缓存行为,当发现指定id的行缺失时也会触发被动获取

This commit is contained in:
Xu Chang 2023-09-01 14:40:22 +08:00
parent a3c0385b73
commit 88bbe7baa3
7 changed files with 174 additions and 77 deletions

View File

@ -77,6 +77,19 @@ export declare class Cache<ED extends EntityDict & BaseEntityDict, Cxt extends A
entity: keyof ED;
operation: ED[keyof ED]['Operation'];
}>): void;
fetchRows(missedRows: Array<{
entity: keyof ED;
selection: ED[keyof ED]['Selection'];
}>): void;
/**
* getById可以处理当本行不在缓存中的自动取
* @attention 访id不存在的行
* @param entity
* @param data
* @param id
* @param allowMiss
*/
getById<T extends keyof ED>(entity: T, data: ED[T]['Selection']['data'], id: string, allowMiss?: boolean): Partial<ED[T]['Schema']> | undefined;
private getInner;
get<T extends keyof ED>(entity: T, selection: ED[T]['Selection'], allowMiss?: boolean): Partial<ED[T]["Schema"]>[];
judgeRelation(entity: keyof ED, attr: string): string | 0 | 1 | string[] | 2;

View File

@ -15,7 +15,7 @@ var Cache = /** @class */ (function (_super) {
tslib_1.__extends(Cache, _super);
function Cache(storageSchema, aspectWrapper, frontendContextBuilder, checkers, getFullData, localStorage, savedEntities, keepFreshPeriod) {
var _this = _super.call(this) || this;
_this.refreshing = false;
_this.refreshing = 0;
_this.refreshRecords = {};
_this.aspectWrapper = aspectWrapper;
_this.syncEventsCallbacks = [];
@ -69,15 +69,16 @@ var Cache = /** @class */ (function (_super) {
switch (_b.label) {
case 0:
_b.trys.push([0, 2, , 3]);
this.refreshing++;
return [4 /*yield*/, this.aspectWrapper.exec(name, params)];
case 1:
_a = _b.sent(), result = _a.result, opRecords = _a.opRecords, message = _a.message;
this.refreshing = false;
if (opRecords) {
this.sync(opRecords);
}
this.refreshing--;
callback && callback(result, opRecords);
if (opRecords && !dontPublish) {
if (opRecords && opRecords.length > 0 && !dontPublish) {
this.publish();
}
return [2 /*return*/, {
@ -87,6 +88,7 @@ var Cache = /** @class */ (function (_super) {
case 2:
e_1 = _b.sent();
// 如果是数据不一致错误,这里可以让用户知道
this.refreshing--;
if (e_1 instanceof Exception_1.OakException) {
opRecord = e_1.opRecord;
if (opRecord) {
@ -317,7 +319,6 @@ var Cache = /** @class */ (function (_super) {
}
undoSetRefreshRecord = this.addRefreshRecord(entity, key, now);
}
this.refreshing = true;
_b.label = 1;
case 1:
_b.trys.push([1, 3, , 4]);
@ -350,7 +351,6 @@ var Cache = /** @class */ (function (_super) {
}];
case 3:
err_1 = _b.sent();
this.refreshing = false;
undoSetRefreshRecord && undoSetRefreshRecord();
throw err_1;
case 4: return [2 /*return*/];
@ -380,13 +380,11 @@ var Cache = /** @class */ (function (_super) {
var result;
return tslib_1.__generator(this, function (_a) {
switch (_a.label) {
case 0:
this.refreshing = true;
return [4 /*yield*/, this.exec('operate', {
entity: entity,
operation: operation,
option: option,
}, callback)];
case 0: return [4 /*yield*/, this.exec('operate', {
entity: entity,
operation: operation,
option: option,
}, callback)];
case 1:
result = _a.sent();
return [2 /*return*/, result];
@ -499,8 +497,78 @@ var Cache = /** @class */ (function (_super) {
});
return;
};
Cache.prototype.getInner = function (entity, selection, allowMiss) {
Cache.prototype.fetchRows = function (missedRows) {
var _this = this;
if (!this.refreshing) {
if (process.env.NODE_ENV === 'development') {
console.warn('缓存被动去获取数据,请查看页面行为并加以优化', missedRows);
}
this.exec('fetchRows', missedRows, function (result, opRecords) { return tslib_1.__awaiter(_this, void 0, void 0, function () {
var _a, _b, record, d, missedRows_1, missedRows_1_1, mr;
var e_5, _c, e_6, _d;
return tslib_1.__generator(this, function (_e) {
try {
// missedRows理论上一定要取到不能为空集。否则就是程序员有遗漏
for (_a = tslib_1.__values(opRecords), _b = _a.next(); !_b.done; _b = _a.next()) {
record = _b.value;
d = record.d;
(0, assert_1.default)(Object.keys(d).length > 0, '在通过fetchRow取不一致数据时返回了空数据请拿该程序员祭天。');
try {
for (missedRows_1 = (e_6 = void 0, tslib_1.__values(missedRows)), missedRows_1_1 = missedRows_1.next(); !missedRows_1_1.done; missedRows_1_1 = missedRows_1.next()) {
mr = missedRows_1_1.value;
(0, assert_1.default)(Object.keys(d[mr.entity]).length > 0, "\u5728\u901A\u8FC7fetchRow\u53D6\u4E0D\u4E00\u81F4\u6570\u636E\u65F6\u8FD4\u56DE\u4E86\u7A7A\u6570\u636E\uFF0C\u8BF7\u62FF\u8BE5\u7A0B\u5E8F\u5458\u796D\u5929\u3002entity\u662F".concat(mr.entity));
}
}
catch (e_6_1) { e_6 = { error: e_6_1 }; }
finally {
try {
if (missedRows_1_1 && !missedRows_1_1.done && (_d = missedRows_1.return)) _d.call(missedRows_1);
}
finally { if (e_6) throw e_6.error; }
}
}
}
catch (e_5_1) { e_5 = { error: e_5_1 }; }
finally {
try {
if (_b && !_b.done && (_c = _a.return)) _c.call(_a);
}
finally { if (e_5) throw e_5.error; }
}
return [2 /*return*/];
});
}); });
}
};
/**
* getById可以处理当本行不在缓存中的自动取
* @attention 这里如果访问了一个id不存在的行被删除可能会陷入无限循环如果遇到了再处理
* @param entity
* @param data
* @param id
* @param allowMiss
*/
Cache.prototype.getById = function (entity, data, id, allowMiss) {
var result = this.getInner(entity, {
data: data,
filter: {
id: id,
},
}, allowMiss);
if (result.length === 0 && !allowMiss) {
this.fetchRows([{
entity: entity,
selection: {
data: data,
filter: {
id: id,
},
}
}]);
}
return result[0];
};
Cache.prototype.getInner = function (entity, selection, allowMiss) {
var autoCommit = false;
if (!this.context) {
this.begin();
@ -521,44 +589,8 @@ var Cache = /** @class */ (function (_super) {
this.rollback();
}
if (err instanceof Exception_1.OakRowUnexistedException) {
if (!this.refreshing && !allowMiss) {
var missedRows_1 = err.getRows();
this.refreshing = true;
this.exec('fetchRows', missedRows_1, function (result, opRecords) { return tslib_1.__awaiter(_this, void 0, void 0, function () {
var _a, _b, record, d, missedRows_2, missedRows_2_1, mr;
var e_5, _c, e_6, _d;
return tslib_1.__generator(this, function (_e) {
try {
// missedRows理论上一定要取到不能为空集。否则就是程序员有遗漏
for (_a = tslib_1.__values(opRecords), _b = _a.next(); !_b.done; _b = _a.next()) {
record = _b.value;
d = record.d;
(0, assert_1.default)(Object.keys(d).length > 0, '在通过fetchRow取不一致数据时返回了空数据请拿该程序员祭天。');
try {
for (missedRows_2 = (e_6 = void 0, tslib_1.__values(missedRows_1)), missedRows_2_1 = missedRows_2.next(); !missedRows_2_1.done; missedRows_2_1 = missedRows_2.next()) {
mr = missedRows_2_1.value;
(0, assert_1.default)(Object.keys(d[mr.entity]).length > 0, "\u5728\u901A\u8FC7fetchRow\u53D6\u4E0D\u4E00\u81F4\u6570\u636E\u65F6\u8FD4\u56DE\u4E86\u7A7A\u6570\u636E\uFF0C\u8BF7\u62FF\u8BE5\u7A0B\u5E8F\u5458\u796D\u5929\u3002entity\u662F".concat(mr.entity));
}
}
catch (e_6_1) { e_6 = { error: e_6_1 }; }
finally {
try {
if (missedRows_2_1 && !missedRows_2_1.done && (_d = missedRows_2.return)) _d.call(missedRows_2);
}
finally { if (e_6) throw e_6.error; }
}
}
}
catch (e_5_1) { e_5 = { error: e_5_1 }; }
finally {
try {
if (_b && !_b.done && (_c = _a.return)) _c.call(_a);
}
finally { if (e_5) throw e_5.error; }
}
return [2 /*return*/];
});
}); });
if (!allowMiss) {
this.fetchRows(err.getRows());
}
return [];
}

View File

@ -180,6 +180,12 @@ var RelationAuth = /** @class */ (function (_super) {
}
catch (err) {
this.cache.rollback();
if (err instanceof types_1.OakRowUnexistedException) {
// 发现缓存中缺失项的话要协助获取
var missedRows = err.getRows();
this.cache.fetchRows(missedRows);
return false;
}
if (!(err instanceof types_1.OakUserException)) {
throw err;
}

View File

@ -249,7 +249,7 @@ var ListNode = /** @class */ (function (_super) {
},
filter: filter,
sorter: sorter,
});
}, true);
this.ids = result.map(function (ele) { return ele.id; });
}
}
@ -430,7 +430,7 @@ var ListNode = /** @class */ (function (_super) {
var result = this.cache.get(this.entity, {
data: data,
filter: filter2,
}, this.isLoading());
}, true);
var r2_1 = result.filter(function (ele) { var _a; return ele.$$createAt$$ === 1 || ((_a = _this.ids) === null || _a === void 0 ? void 0 : _a.includes(ele.id)); }).sort(function (ele1, ele2) {
if (ele1.$$createAt$$ === 1) {
return -1;
@ -1001,7 +1001,7 @@ var SingleNode = /** @class */ (function (_super) {
filter: {
id: id,
},
}, this.isLoading());
}, true);
if (this.aggr) {
(0, lodash_1.merge)(result[0], this.aggr);
}

View File

@ -32,7 +32,7 @@ export class Cache<
(opRecords: OpRecord<ED>[]) => void
>;
private contextBuilder?: () => FrontCxt;
private refreshing = false;
private refreshing = 0;
private savedEntities: (keyof ED)[];
private keepFreshPeriod: number;
private localStorage: LocalStorage;
@ -119,13 +119,14 @@ export class Cache<
dontPublish?: true,
) {
try {
this.refreshing ++;
const { result, opRecords, message } = await this.aspectWrapper.exec(name, params);
this.refreshing = false;
if (opRecords) {
this.sync(opRecords);
}
this.refreshing --;
callback && callback(result, opRecords);
if (opRecords && !dontPublish) {
if (opRecords && opRecords.length > 0 && !dontPublish) {
this.publish();
}
return {
@ -135,6 +136,7 @@ export class Cache<
}
catch (e) {
// 如果是数据不一致错误,这里可以让用户知道
this.refreshing --;
if (e instanceof OakException) {
const { opRecord } = e;
if (opRecord) {
@ -375,7 +377,6 @@ export class Cache<
undoSetRefreshRecord = this.addRefreshRecord(entity, key, now);
}
this.refreshing = true;
try {
const { result: { ids, count, aggr } } = await this.exec('select', {
entity,
@ -407,7 +408,6 @@ export class Cache<
};
}
catch(err) {
this.refreshing = false;
undoSetRefreshRecord && undoSetRefreshRecord();
throw err;
@ -434,7 +434,6 @@ export class Cache<
option?: OP,
callback?: (result: Awaited<ReturnType<AD['operate']>>) => void,
) {
this.refreshing = true;
const result = await this.exec('operate', {
entity,
operation,
@ -543,6 +542,58 @@ export class Cache<
return;
}
fetchRows(missedRows: Array<{ entity: keyof ED, selection: ED[keyof ED]['Selection']}>) {
if (!this.refreshing) {
if (process.env.NODE_ENV === 'development') {
console.warn('缓存被动去获取数据,请查看页面行为并加以优化', missedRows);
}
this.exec('fetchRows', missedRows, async (result, opRecords) => {
// missedRows理论上一定要取到不能为空集。否则就是程序员有遗漏
for (const record of opRecords!) {
const { d } = record as SelectOpResult<ED>;
assert(Object.keys(d).length > 0, '在通过fetchRow取不一致数据时返回了空数据请拿该程序员祭天。');
for (const mr of missedRows) {
assert(Object.keys(d![mr.entity]!).length > 0, `在通过fetchRow取不一致数据时返回了空数据请拿该程序员祭天。entity是${mr.entity as string}`);
}
}
})
}
}
/**
* getById可以处理当本行不在缓存中的自动取
* @attention 访id不存在的行
* @param entity
* @param data
* @param id
* @param allowMiss
*/
getById<T extends keyof ED>(
entity: T,
data: ED[T]['Selection']['data'],
id: string,
allowMiss?: boolean
): Partial<ED[T]['Schema']> | undefined {
const result = this.getInner(entity, {
data,
filter: {
id,
},
}, allowMiss);
if (result.length === 0 && !allowMiss) {
this.fetchRows([{
entity,
selection: {
data,
filter: {
id,
},
}
}]);
}
return result[0];
}
private getInner<T extends keyof ED>(
entity: T,
selection: ED[T]['Selection'],
@ -571,19 +622,8 @@ export class Cache<
this.rollback();
}
if (err instanceof OakRowUnexistedException) {
if (!this.refreshing && !allowMiss) {
const missedRows = err.getRows();
this.refreshing = true;
this.exec('fetchRows', missedRows, async (result, opRecords) => {
// missedRows理论上一定要取到不能为空集。否则就是程序员有遗漏
for (const record of opRecords!) {
const { d } = record as SelectOpResult<ED>;
assert(Object.keys(d).length > 0, '在通过fetchRow取不一致数据时返回了空数据请拿该程序员祭天。');
for (const mr of missedRows) {
assert(Object.keys(d![mr.entity]!).length > 0, `在通过fetchRow取不一致数据时返回了空数据请拿该程序员祭天。entity是${mr.entity}`);
}
}
})
if (!allowMiss) {
this.fetchRows(err.getRows());
}
return [];
} else {

View File

@ -1,4 +1,4 @@
import { EntityDict, OperateOption, SelectOption, OpRecord, AspectWrapper, CheckerType, Aspect, SelectOpResult, AuthCascadePath, AuthDeduceRelationMap, OakUserException } from 'oak-domain/lib/types';
import { EntityDict, OperateOption, SelectOption, OpRecord, AspectWrapper, CheckerType, Aspect, SelectOpResult, AuthCascadePath, AuthDeduceRelationMap, OakUserException, OakRowUnexistedException } from 'oak-domain/lib/types';
import { EntityDict as BaseEntityDict } from 'oak-domain/lib/base-app-domain';
import { CommonAspectDict } from 'oak-common-aspect';
import { Feature } from '../types/Feature';
@ -241,6 +241,12 @@ export class RelationAuth<
}
catch (err) {
this.cache.rollback();
if (err instanceof OakRowUnexistedException) {
// 发现缓存中缺失项的话要协助获取
const missedRows = err.getRows();
this.cache.fetchRows(missedRows);
return false;
}
if (!(err instanceof OakUserException)) {
throw err;
}

View File

@ -328,7 +328,7 @@ class ListNode<
},
filter,
sorter,
});
}, true);
this.ids = result.map((ele) => ele.id) as unknown as string[];
}
}
@ -562,7 +562,7 @@ class ListNode<
const result = this.cache.get(this.entity, {
data,
filter: filter2,
}, this.isLoading());
}, true);
const r2 = result.filter(
@ -1131,7 +1131,7 @@ class SingleNode<ED extends EntityDict & BaseEntityDict,
filter: {
id,
},
}, this.isLoading());
}, true);
if (this.aggr) {
merge(result[0], this.aggr);
}