From 1e7cf0ec65253cab5ae6a6fb8c05bad7e1a1f480 Mon Sep 17 00:00:00 2001 From: Saviio Date: Thu, 23 Mar 2017 11:17:46 +0800 Subject: [PATCH 1/4] fix: fix a series of bug in new version --- src/shared/Logger.ts | 2 +- src/storage/Database.ts | 72 +++++++++++++++++--------------- src/storage/helper/definition.ts | 6 ++- 3 files changed, 45 insertions(+), 35 deletions(-) diff --git a/src/shared/Logger.ts b/src/shared/Logger.ts index 7dc2c7d..b0ed238 100644 --- a/src/shared/Logger.ts +++ b/src/shared/Logger.ts @@ -76,7 +76,7 @@ export class Logger { if (!logger) { const ctxLogger = new ContextLogger(name, level || Logger.defaultLevel, formatter) Logger.contextMap.set(name, ctxLogger) - logger.destroy = () => Logger.contextMap.delete(name) + ctxLogger.destroy = () => Logger.contextMap.delete(name) return ctxLogger } diff --git a/src/storage/Database.ts b/src/storage/Database.ts index b828a3a..3c1abcb 100644 --- a/src/storage/Database.ts +++ b/src/storage/Database.ts @@ -137,7 +137,7 @@ export class Database { }) const { contextIds, queries } = Mutation.aggregate(db, muts, []) - return this.executor(queries) + return this.executor(db, queries) .do(() => contextIds.forEach(id => this.storedIds.add(id))) }) } @@ -191,7 +191,7 @@ export class Database { } }) - return this.executor([query]) + return this.executor(db, [query]) }) } @@ -212,7 +212,7 @@ export class Database { return Observable.fromPromise(prefetch.exec()) .concatMap((scopedIds) => { const query = predicatableQuery(db, table, provider.getPredicate(), StatementType.Delete) - return this.executor([query]) + return this.executor(db, [query]) .do(() => scopedIds.forEach((entity: any) => this.storedIds.delete(fieldIdentifier(tableName, entity[pk])))) }) @@ -233,7 +233,7 @@ export class Database { this.traverseCompound(db, tableName, clone(raw), insert, update, sharing) const { contextIds, queries } = Mutation.aggregate(db, insert, update) - return this.executor(queries) + return this.executor(db, queries) .do(() => { contextIds.forEach(id => this.storedIds.add(id)) }) @@ -265,9 +265,9 @@ export class Database { if (disposeHandler) { const scope = this.createScopedHandler(db, queries, removedIds) return disposeHandler(rootEntities, scope) - .concatMap(() => this.executor(queries)) + .concatMap(() => this.executor(db, queries)) } else { - return this.executor(queries) + return this.executor(db, queries) } }) .do(() => { @@ -284,15 +284,16 @@ export class Database { const cleanup = this.database$.map(db => db.getSchema().tables().map(t => db.delete().from(t))) - return cleanup.concatMap(queries => this.executor(queries)) - .concatMapTo(this.database$) - .do(db => { - db.close() - this.schemas.clear() - this.storedIds.clear() - this.schemaBuilder = null - this.subscription.unsubscribe() - }) + return this.database$.concatMap(db => { + return cleanup.concatMap(queries => this.executor(db, queries)) + .do(() => { + db.close() + this.schemas.clear() + this.storedIds.clear() + this.schemaBuilder = null + this.subscription.unsubscribe() + }) + }) } private buildTables() { @@ -492,8 +493,8 @@ export class Database { navigators.push(nav) }) - const onlyNavigator = fieldsValue.size === navigators.length && - navigators.every((nav) => fieldsValue.has(nav)) + const onlyNavigator = Array.from(fieldsValue.keys()) + .every(key => contains(key, navigators)) assert(!onlyNavigator, Exception.InvalidQuery()) if (!hasKey) { @@ -575,6 +576,7 @@ export class Database { const ret = this.traverseQueryFields(db, assocaiation.name, new Set(fields), containKey, glob, path.slice(0), context) handleAdvanced(ret, ctx.key, assocaiation) + ctx.skip() break } }) @@ -620,6 +622,11 @@ export class Database { const isColumn = schema.columns.has(key) const mapper = (isColumn && schema.mapper.get(key)) || null + if (!(isColumn || isNavigator || ctx.isRoot)) { + // 若当前节点非 有效节点、叶子节点或者根节点中任意一种时,直接停止子节点的迭代 + ctx.skip() + } + return (ctx.isRoot || (!isColumn && !isNavigator)) ? false : { mapper, visited, @@ -628,8 +635,9 @@ export class Database { }) traversable.forEach((ctx, node) => { + // 考虑到叶节点可能存在`Object` type, 所以无论分支节点还是叶节点,其后的结构都不迭代 + ctx.skip() if (ctx.isNavigatorLeaf) { - ctx.skip() const ref = schema.associations.get(ctx.key).name return this.traverseCompound(db, ref, node, insertMutList, updateMutList, sharing) } @@ -707,22 +715,20 @@ export class Database { } } - private executor(queries: lf.query.Builder[]) { - return this.database$.concatMap(db => { - const tx = db.createTransaction() - const handler = { - error: () => warn(`Execute failed, transaction is already marked for rollback.`) - } + private executor(db: lf.Database, queries: lf.query.Builder[]) { + const tx = db.createTransaction() + const handler = { + error: () => warn(`Execute failed, transaction is already marked for rollback.`) + } - return Observable.fromPromise(tx.exec(queries)) - .do(handler) - .map((ret) => { - return { - result: true, - ...mergeTransactionResult(queries, ret) - } - }) - }) + return Observable.fromPromise(tx.exec(queries)) + .do(handler) + .map((ret) => { + return { + result: true, + ...mergeTransactionResult(queries, ret) + } + }) } } diff --git a/src/storage/helper/definition.ts b/src/storage/helper/definition.ts index 177a472..8c29d83 100644 --- a/src/storage/helper/definition.ts +++ b/src/storage/helper/definition.ts @@ -7,7 +7,11 @@ import * as Exception from '../../exception' export function revise(relation: Relationship, def: Object) { switch (relation) { case Relationship.oneToOne: - forEach(def, (value) => value.id = value.id ? false : value.id) + forEach(def, (value) => { + if (value.id) { + value.id = false + } + }) break case Relationship.oneToMany: def = [def] From 10b4d0aca752f60a268217410091f764c94269a8 Mon Sep 17 00:00:00 2001 From: Saviio Date: Thu, 23 Mar 2017 11:22:42 +0800 Subject: [PATCH 2/4] utils: add a `hash` function --- src/utils/hash.ts | 8 ++++++++ src/utils/index.ts | 1 + test/specs/utils/utils.spec.ts | 12 +++++++++++- 3 files changed, 20 insertions(+), 1 deletion(-) create mode 100644 src/utils/hash.ts diff --git a/src/utils/hash.ts b/src/utils/hash.ts new file mode 100644 index 0000000..132c6b7 --- /dev/null +++ b/src/utils/hash.ts @@ -0,0 +1,8 @@ +export const hash = (str: string) => { + let ret = 0 + for (let i = 0; i < str.length; i++) { + ret = ((ret << 5) - ret) + str.charCodeAt(i) + ret = ret & ret + } + return ret +} diff --git a/src/utils/index.ts b/src/utils/index.ts index cc901f1..60cfed7 100644 --- a/src/utils/index.ts +++ b/src/utils/index.ts @@ -1,3 +1,4 @@ +export * from './hash' export * from './clone' export * from './valid' export * from './option' diff --git a/test/specs/utils/utils.spec.ts b/test/specs/utils/utils.spec.ts index 66b7743..079f525 100644 --- a/test/specs/utils/utils.spec.ts +++ b/test/specs/utils/utils.spec.ts @@ -1,4 +1,4 @@ -import { forEach, clone, getType, assert } from '../../index' +import { forEach, clone, getType, assert, hash } from '../../index' import { describe, it } from 'tman' import { expect } from 'chai' @@ -397,4 +397,14 @@ export default describe('Utils Testcase: ', () => { }) + describe('Func: hash', () => { + + it('should be able to convert string to hash', () => { + expect(hash('')).to.equal(0) + expect(hash(' ')).to.equal(32) + expect(hash(' ')).to.equal(1024) + }) + + }) + }) From a77b4f850f515f7097e4236128977b429c09e47c Mon Sep 17 00:00:00 2001 From: Saviio Date: Fri, 24 Mar 2017 11:41:15 +0800 Subject: [PATCH 3/4] fix: correct the lifecycle of cached-Id --- src/storage/Database.ts | 41 +++++++++++++++++++++++++++++------------ 1 file changed, 29 insertions(+), 12 deletions(-) diff --git a/src/storage/Database.ts b/src/storage/Database.ts index 3c1abcb..b04fee7 100644 --- a/src/storage/Database.ts +++ b/src/storage/Database.ts @@ -29,6 +29,7 @@ export class Database { private schemas = new Map() private schemaBuilder: lf.schema.Builder private connected = false + // note thin cache will be unreliable in some eage case private storedIds = new Set() private subscription: Subscription @@ -89,11 +90,20 @@ export class Database { load(data: any) { assert(!this.connected, Exception.DatabaseIsNotEmpty()) - return this.database$.concatMap(db => db.import(data)) - .do(() => { + + return this.database$ + .concatMap(db => { forEach(data.tables, (entities: any[], name: string) => { const schema = this.findSchema(name) - entities.forEach(entity => this.storedIds.add(entity[schema.pk])) + entities.forEach((entity: any) => + this.storedIds.add(fieldIdentifier(name, entity[schema.pk]))) + }) + return db.import(data).catch(() => { + forEach(data.tables, (entities: any[], name: string) => { + const schema = this.findSchema(name) + entities.forEach((entity: any) => + this.storedIds.delete(fieldIdentifier(name, entity[schema.pk]))) + }) }) }) } @@ -137,8 +147,9 @@ export class Database { }) const { contextIds, queries } = Mutation.aggregate(db, muts, []) + contextIds.forEach(id => this.storedIds.add(id)) return this.executor(db, queries) - .do(() => contextIds.forEach(id => this.storedIds.add(id))) + .do({ error: () => contextIds.forEach(id => this.storedIds.delete(id)) }) }) } @@ -212,9 +223,14 @@ export class Database { return Observable.fromPromise(prefetch.exec()) .concatMap((scopedIds) => { const query = predicatableQuery(db, table, provider.getPredicate(), StatementType.Delete) - return this.executor(db, [query]) - .do(() => scopedIds.forEach((entity: any) => - this.storedIds.delete(fieldIdentifier(tableName, entity[pk])))) + + scopedIds.forEach((entity: any) => + this.storedIds.delete(fieldIdentifier(tableName, entity[pk]))) + + return this.executor(db, [query]).do({ error: () => { + scopedIds.forEach((entity: any) => + this.storedIds.add(fieldIdentifier(tableName, entity[pk]))) + }}) }) }) } @@ -233,10 +249,9 @@ export class Database { this.traverseCompound(db, tableName, clone(raw), insert, update, sharing) const { contextIds, queries } = Mutation.aggregate(db, insert, update) + contextIds.forEach(id => this.storedIds.add(id)) return this.executor(db, queries) - .do(() => { - contextIds.forEach(id => this.storedIds.add(id)) - }) + .do({ error: () => contextIds.forEach(id => this.storedIds.delete(id)) }) }) } @@ -265,13 +280,15 @@ export class Database { if (disposeHandler) { const scope = this.createScopedHandler(db, queries, removedIds) return disposeHandler(rootEntities, scope) + .do(() => removedIds.forEach((id: string) => this.storedIds.delete(id))) .concatMap(() => this.executor(db, queries)) } else { + removedIds.forEach((id: string) => this.storedIds.delete(id)) return this.executor(db, queries) } }) - .do(() => { - removedIds.forEach((id: string) => this.storedIds.delete(id)) + .do({ error: () => + removedIds.forEach((id: string) => this.storedIds.add(id)) }) }) } From 620dfad090b7889c576a1ac8fedba03e3f159122 Mon Sep 17 00:00:00 2001 From: Saviio Date: Fri, 24 Mar 2017 15:28:59 +0800 Subject: [PATCH 4/4] chore: lock node-version in ci config --- circle.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/circle.yml b/circle.yml index 4a69636..bbe9226 100644 --- a/circle.yml +++ b/circle.yml @@ -1,6 +1,6 @@ machine: node: - version: 6 + version: 6.10.0 test: override: @@ -10,4 +10,4 @@ test: notify: webhooks: - - url: http://teambition.vvlyn.com/api/circle \ No newline at end of file + - url: http://teambition.vvlyn.com/api/circle