Creatures are now cached in memory for computation
Also removed dependency group calculation because the optimisation isn't as useful to reduce DB calls if the creature is in memory anyway
This commit is contained in:
@@ -82,14 +82,6 @@ const DenormalisedOnlyCreaturePropertySchema = new SimpleSchema({
|
|||||||
index: 1,
|
index: 1,
|
||||||
removeBeforeCompute: true,
|
removeBeforeCompute: true,
|
||||||
},
|
},
|
||||||
// Dependency tree, the ID of the lowest ordered doc connected to this doc
|
|
||||||
// via dependencies
|
|
||||||
depGroupId: {
|
|
||||||
type: String,
|
|
||||||
regEx: SimpleSchema.RegEx.Id,
|
|
||||||
index: 1,
|
|
||||||
removeBeforeCompute: true,
|
|
||||||
},
|
|
||||||
});
|
});
|
||||||
|
|
||||||
CreaturePropertySchema.extend(DenormalisedOnlyCreaturePropertySchema);
|
CreaturePropertySchema.extend(DenormalisedOnlyCreaturePropertySchema);
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ import SimpleSchema from 'simpl-schema';
|
|||||||
import CreatureProperties from '/imports/api/creature/creatureProperties/CreatureProperties.js';
|
import CreatureProperties from '/imports/api/creature/creatureProperties/CreatureProperties.js';
|
||||||
import getRootCreatureAncestor from '/imports/api/creature/creatureProperties/getRootCreatureAncestor.js';
|
import getRootCreatureAncestor from '/imports/api/creature/creatureProperties/getRootCreatureAncestor.js';
|
||||||
import { assertEditPermission } from '/imports/api/sharing/sharingPermissions.js';
|
import { assertEditPermission } from '/imports/api/sharing/sharingPermissions.js';
|
||||||
import computeCreature, { computeCreatureDependencyGroup } from '/imports/api/engine/computeCreature.js';
|
import computeCreature from '/imports/api/engine/computeCreature.js';
|
||||||
|
|
||||||
const damageProperty = new ValidatedMethod({
|
const damageProperty = new ValidatedMethod({
|
||||||
name: 'creatureProperties.damage',
|
name: 'creatureProperties.damage',
|
||||||
@@ -38,12 +38,7 @@ const damageProperty = new ValidatedMethod({
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
let result = damagePropertyWork({ property, operation, value });
|
let result = damagePropertyWork({ property, operation, value });
|
||||||
if (property.depGroupId) {
|
computeCreature(rootCreature._id);
|
||||||
// Dependencies can't be changed through damage, only recompute deps
|
|
||||||
computeCreatureDependencyGroup([property.depGroupId], rootCreature._id);
|
|
||||||
} else {
|
|
||||||
computeCreature(rootCreature._id);
|
|
||||||
}
|
|
||||||
return result;
|
return result;
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ import { nodeArrayToTree } from '/imports/api/parenting/nodesToTree.js';
|
|||||||
import CreatureProperties,
|
import CreatureProperties,
|
||||||
{ DenormalisedOnlyCreaturePropertySchema as denormSchema }
|
{ DenormalisedOnlyCreaturePropertySchema as denormSchema }
|
||||||
from '/imports/api/creature/creatureProperties/CreatureProperties.js';
|
from '/imports/api/creature/creatureProperties/CreatureProperties.js';
|
||||||
|
import { loadedCreatures } from '../loadCreatures.js';
|
||||||
import Creatures from '/imports/api/creature/creatures/Creatures.js';
|
import Creatures from '/imports/api/creature/creatures/Creatures.js';
|
||||||
import computedOnlySchemas from '/imports/api/properties/computedOnlyPropertySchemasIndex.js';
|
import computedOnlySchemas from '/imports/api/properties/computedOnlyPropertySchemasIndex.js';
|
||||||
import computedSchemas from '/imports/api/properties/computedPropertySchemasIndex.js';
|
import computedSchemas from '/imports/api/properties/computedPropertySchemasIndex.js';
|
||||||
@@ -15,7 +16,6 @@ import linkTypeDependencies from './buildComputation/linkTypeDependencies.js';
|
|||||||
import computeSlotQuantityFilled from './buildComputation/computeSlotQuantityFilled.js';
|
import computeSlotQuantityFilled from './buildComputation/computeSlotQuantityFilled.js';
|
||||||
import CreatureComputation from './CreatureComputation.js';
|
import CreatureComputation from './CreatureComputation.js';
|
||||||
import removeSchemaFields from './buildComputation/removeSchemaFields.js';
|
import removeSchemaFields from './buildComputation/removeSchemaFields.js';
|
||||||
import assignDependencyGroups from '/imports/api/engine/computation/utility/assignDependencyGroups.js';
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Store index of properties
|
* Store index of properties
|
||||||
@@ -38,35 +38,22 @@ export default function buildCreatureComputation(creatureId){
|
|||||||
return computation;
|
return computation;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function buildDependencyGroupComputation(depGroupIds, creatureId) {
|
|
||||||
const creature = getCreature(creatureId);
|
|
||||||
const properties = getGroupProperties(depGroupIds);
|
|
||||||
const computation = buildComputationFromProps(properties, creature);
|
|
||||||
return computation;
|
|
||||||
}
|
|
||||||
|
|
||||||
function getProperties(creatureId) {
|
function getProperties(creatureId) {
|
||||||
return CreatureProperties.find({
|
if (loadedCreatures.has(creatureId)) {
|
||||||
|
const creature = loadedCreatures.get(creatureId);
|
||||||
|
const props = Array.from(creature.properties.values());
|
||||||
|
return props;
|
||||||
|
}
|
||||||
|
console.time(`fetching from db: ${creatureId}`)
|
||||||
|
const props = CreatureProperties.find({
|
||||||
'ancestors.id': creatureId,
|
'ancestors.id': creatureId,
|
||||||
'removed': {$ne: true},
|
'removed': {$ne: true},
|
||||||
}, {
|
}, {
|
||||||
sort: { order: 1 },
|
sort: { order: 1 },
|
||||||
fields: { icon: 0 },
|
fields: { icon: 0 },
|
||||||
}).fetch();
|
}).fetch();
|
||||||
}
|
console.timeEnd(`fetching from db: ${creatureId}`);
|
||||||
|
return props;
|
||||||
function getGroupProperties(depGroupIds) {
|
|
||||||
console.log({ depGroupIds });
|
|
||||||
if (!depGroupIds || depGroupIds.includes(undefined)) {
|
|
||||||
throw `Expected array full of ids, got ${depGroupIds}`
|
|
||||||
}
|
|
||||||
return CreatureProperties.find({
|
|
||||||
depGroupId: { $in: depGroupIds },
|
|
||||||
'removed': { $ne: true },
|
|
||||||
}, {
|
|
||||||
sort: { order: 1 },
|
|
||||||
fields: { icon: 0 },
|
|
||||||
}).fetch();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function getCreature(creatureId){
|
function getCreature(creatureId){
|
||||||
@@ -138,8 +125,5 @@ export function buildComputationFromProps(properties, creature){
|
|||||||
linkCalculationDependencies(dependencyGraph, prop, computation);
|
linkCalculationDependencies(dependencyGraph, prop, computation);
|
||||||
});
|
});
|
||||||
|
|
||||||
// Store the connected groups of the dependency graph
|
|
||||||
assignDependencyGroups(dependencyGraph);
|
|
||||||
|
|
||||||
return computation;
|
return computation;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -20,7 +20,6 @@ export default function writeAlteredProperties(computation){
|
|||||||
'deactivatedBySelf',
|
'deactivatedBySelf',
|
||||||
'deactivatedByAncestor',
|
'deactivatedByAncestor',
|
||||||
'deactivatedByToggle',
|
'deactivatedByToggle',
|
||||||
'depGroupId',
|
|
||||||
'damage',
|
'damage',
|
||||||
...schema.objectKeys(),
|
...schema.objectKeys(),
|
||||||
];
|
];
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import buildCreatureComputation, { buildDependencyGroupComputation } from './computation/buildCreatureComputation.js';
|
import buildCreatureComputation from './computation/buildCreatureComputation.js';
|
||||||
import computeCreatureComputation from './computation/computeCreatureComputation.js';
|
import computeCreatureComputation from './computation/computeCreatureComputation.js';
|
||||||
import writeAlteredProperties from './computation/writeComputation/writeAlteredProperties.js';
|
import writeAlteredProperties from './computation/writeComputation/writeAlteredProperties.js';
|
||||||
import writeScope from './computation/writeComputation/writeScope.js';
|
import writeScope from './computation/writeComputation/writeScope.js';
|
||||||
@@ -6,14 +6,10 @@ import writeErrors from './computation/writeComputation/writeErrors.js';
|
|||||||
|
|
||||||
export default function computeCreature(creatureId){
|
export default function computeCreature(creatureId){
|
||||||
if (Meteor.isClient) return;
|
if (Meteor.isClient) return;
|
||||||
|
console.time('Compute');
|
||||||
const computation = buildCreatureComputation(creatureId);
|
const computation = buildCreatureComputation(creatureId);
|
||||||
computeComputation(computation, creatureId);
|
computeComputation(computation, creatureId);
|
||||||
}
|
console.timeEnd('Compute');
|
||||||
|
|
||||||
// Recompute only some groups of the dependency tree
|
|
||||||
export function computeCreatureDependencyGroup(depGroupIds, creatureId) {
|
|
||||||
const computation = buildDependencyGroupComputation(depGroupIds, creatureId);
|
|
||||||
computeComputation(computation, creatureId);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function computeComputation(computation, creatureId) {
|
function computeComputation(computation, creatureId) {
|
||||||
|
|||||||
117
app/imports/api/engine/loadCreatures.js
Normal file
117
app/imports/api/engine/loadCreatures.js
Normal file
@@ -0,0 +1,117 @@
|
|||||||
|
import CreatureProperties from '/imports/api/creature/creatureProperties/CreatureProperties.js';
|
||||||
|
import Creatures from '/imports/api/creature/creatures/Creatures.js';
|
||||||
|
|
||||||
|
export const loadedCreatures = new Map(); // creatureId => {creature, properties, etc.}
|
||||||
|
|
||||||
|
export function loadCreature(creatureId, subscription) {
|
||||||
|
if (!creatureId) throw 'creatureId is required';
|
||||||
|
let creature = loadedCreatures.get(creatureId);
|
||||||
|
if (loadedCreatures.has(creatureId)) {
|
||||||
|
creature.subs.add(subscription);
|
||||||
|
} else {
|
||||||
|
console.time(`loading to memory ${creatureId}`);
|
||||||
|
creature = new LoadedCreature(subscription, creatureId);
|
||||||
|
loadedCreatures.set(creatureId, creature);
|
||||||
|
console.timeEnd(`loading to memory ${creatureId}`);
|
||||||
|
console.log('Creatures in memory: ', loadedCreatures.size);
|
||||||
|
}
|
||||||
|
subscription.onStop(() => {
|
||||||
|
unloadCreature(creatureId, subscription);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function unloadCreature(creatureId, subscription) {
|
||||||
|
if (!creatureId) throw 'creatureId is required';
|
||||||
|
const creature = loadedCreatures.get(creatureId);
|
||||||
|
if (!creature) return;
|
||||||
|
creature.subs.delete(subscription);
|
||||||
|
if (creature.subs.size === 0) {
|
||||||
|
creature.stop();
|
||||||
|
loadedCreatures.delete(creatureId);
|
||||||
|
}
|
||||||
|
console.log('Creatures in memory: ', loadedCreatures.size);
|
||||||
|
}
|
||||||
|
|
||||||
|
class LoadedCreature {
|
||||||
|
constructor(sub, creatureId) {
|
||||||
|
// This may be called from a subscription, but we don't want the observers
|
||||||
|
// to be destroyed with it, so use a non-reactive context to observe
|
||||||
|
// the required documents
|
||||||
|
const self = this;
|
||||||
|
Tracker.nonreactive(() => {
|
||||||
|
|
||||||
|
self.subs = new Set([sub]);
|
||||||
|
|
||||||
|
self.properties = new Map();
|
||||||
|
// Observe all creature properties which are needed for computation
|
||||||
|
self.propertyObserver = CreatureProperties.find({
|
||||||
|
'ancestors.id': creatureId,
|
||||||
|
removed: { $ne: true },
|
||||||
|
}, {
|
||||||
|
// sort: { order: 1 },
|
||||||
|
fields: { icon: 0 },
|
||||||
|
}).observeChanges({
|
||||||
|
added(id, fields) {
|
||||||
|
fields._id = id;
|
||||||
|
return self.addProperty(fields)
|
||||||
|
},
|
||||||
|
changed(id, fields) {
|
||||||
|
return self.changeProperty(id, fields);
|
||||||
|
},
|
||||||
|
removed(id) {
|
||||||
|
return self.removeProperty(id);
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
self.creatures = new Map();
|
||||||
|
// Observe the creature itself
|
||||||
|
self.creatureObserver = Creatures.find({
|
||||||
|
_id: creatureId,
|
||||||
|
}).observeChanges({
|
||||||
|
added(id, fields) {
|
||||||
|
fields._id = id;
|
||||||
|
self.addCreature(fields)
|
||||||
|
},
|
||||||
|
changed(id, fields) {
|
||||||
|
return self.changeCreature(id, fields);
|
||||||
|
},
|
||||||
|
removed(id) {
|
||||||
|
return self.removeCreature(id);
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
});
|
||||||
|
}
|
||||||
|
stop() {
|
||||||
|
this.creatureObserver.stop();
|
||||||
|
this.propertyObserver.stop();
|
||||||
|
}
|
||||||
|
addProperty(prop) {
|
||||||
|
this.properties.set(prop._id, prop);
|
||||||
|
}
|
||||||
|
addCreature(creature) {
|
||||||
|
this.creatures.set(creature._id, creature);
|
||||||
|
}
|
||||||
|
changeProperty(id, fields) {
|
||||||
|
this.changeMap(id, fields, this.properties);
|
||||||
|
}
|
||||||
|
changeCreature(id, fields) {
|
||||||
|
this.changeMap(id, fields, this.creatures);
|
||||||
|
}
|
||||||
|
changeMap(id, fields, map) {
|
||||||
|
const doc = map.get(id);
|
||||||
|
for (let key in fields) {
|
||||||
|
if (key === undefined) {
|
||||||
|
delete doc[key];
|
||||||
|
} else {
|
||||||
|
doc[key] = fields[key];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
removeProperty(id) {
|
||||||
|
this.properties.delete(id)
|
||||||
|
}
|
||||||
|
removeCreature(id) {
|
||||||
|
this.creatures.delete(id)
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,38 +0,0 @@
|
|||||||
<template lang="html">
|
|
||||||
<v-text-field
|
|
||||||
ref="input"
|
|
||||||
v-bind="$attrs"
|
|
||||||
class="dc-text-field"
|
|
||||||
:loading="loading"
|
|
||||||
:error-messages="errors"
|
|
||||||
:value="safeValue"
|
|
||||||
:disabled="isDisabled"
|
|
||||||
:outlined="!regular"
|
|
||||||
@input="input"
|
|
||||||
@focus="focused = true"
|
|
||||||
@blur="focused = false"
|
|
||||||
@keyup="e => $emit('keyup', e)"
|
|
||||||
>
|
|
||||||
<template #append>
|
|
||||||
<slot name="value" />
|
|
||||||
</template>
|
|
||||||
</v-text-field>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<script lang="js">
|
|
||||||
import SmartInput from '/imports/ui/components/global/SmartInputMixin.js';
|
|
||||||
|
|
||||||
export default {
|
|
||||||
mixins: [SmartInput],
|
|
||||||
props: {
|
|
||||||
regular: Boolean,
|
|
||||||
},
|
|
||||||
};
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<style lang="css">
|
|
||||||
.dc-text-field .v-input__append-inner{
|
|
||||||
font-size: 12px;
|
|
||||||
margin-top: 36px;
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
@@ -5,6 +5,7 @@ import CreatureLogs from '/imports/api/creature/log/CreatureLogs.js';
|
|||||||
import { assertViewPermission } from '/imports/api/creature/creatures/creaturePermissions.js';
|
import { assertViewPermission } from '/imports/api/creature/creatures/creaturePermissions.js';
|
||||||
import computeCreature from '/imports/api/engine/computeCreature.js';
|
import computeCreature from '/imports/api/engine/computeCreature.js';
|
||||||
import VERSION from '/imports/constants/VERSION.js';
|
import VERSION from '/imports/constants/VERSION.js';
|
||||||
|
import { loadCreature } from '/imports/api/engine/loadCreatures.js';
|
||||||
|
|
||||||
let schema = new SimpleSchema({
|
let schema = new SimpleSchema({
|
||||||
creatureId: {
|
creatureId: {
|
||||||
@@ -13,7 +14,8 @@ let schema = new SimpleSchema({
|
|||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
Meteor.publish('singleCharacter', function(creatureId){
|
Meteor.publish('singleCharacter', function (creatureId) {
|
||||||
|
const self = this;
|
||||||
try {
|
try {
|
||||||
schema.validate({ creatureId });
|
schema.validate({ creatureId });
|
||||||
} catch (e){
|
} catch (e){
|
||||||
@@ -21,21 +23,24 @@ Meteor.publish('singleCharacter', function(creatureId){
|
|||||||
}
|
}
|
||||||
this.autorun(function (computation){
|
this.autorun(function (computation){
|
||||||
let userId = this.userId;
|
let userId = this.userId;
|
||||||
let creatureCursor
|
let permissionCreature = Creatures.findOne({
|
||||||
creatureCursor = Creatures.find({
|
|
||||||
_id: creatureId,
|
_id: creatureId,
|
||||||
|
}, {
|
||||||
|
fields: { owner: 1, readers: 1, writers: 1, public: 1, computeVersion: 1 }
|
||||||
});
|
});
|
||||||
let creature = creatureCursor.fetch()[0];
|
try { assertViewPermission(permissionCreature, userId) }
|
||||||
try { assertViewPermission(creature, userId) }
|
catch (e) { return [] }
|
||||||
catch(e){ return [] }
|
loadCreature(creatureId, self);
|
||||||
if (creature.computeVersion !== VERSION && computation.firstRun){
|
if (permissionCreature.computeVersion !== VERSION && computation.firstRun){
|
||||||
try {
|
try {
|
||||||
computeCreature(creatureId)
|
computeCreature(creatureId)
|
||||||
}
|
}
|
||||||
catch(e){ console.error(e) }
|
catch(e){ console.error(e) }
|
||||||
}
|
}
|
||||||
return [
|
return [
|
||||||
creatureCursor,
|
Creatures.find({
|
||||||
|
_id: creatureId,
|
||||||
|
}),
|
||||||
CreatureProperties.find({
|
CreatureProperties.find({
|
||||||
'ancestors.id': creatureId,
|
'ancestors.id': creatureId,
|
||||||
}),
|
}),
|
||||||
|
|||||||
Reference in New Issue
Block a user