[mod] add caching

This commit is contained in:
Fenris Wolf 2024-10-10 23:51:58 +02:00
parent d3b61b620d
commit ed822041f3
9 changed files with 535 additions and 110 deletions

View file

@ -2381,6 +2381,99 @@ declare namespace lib_plankton.storage.sql_table_common {
}[]>;
}
}
declare namespace lib_plankton.cache {
/**
*/
type type_cache<type_value> = {
init: (() => Promise<void>);
clear: (() => Promise<void>);
query: ((key: string, retrieve: (() => Promise<type_value>)) => Promise<{
retrieved: boolean;
value: type_value;
}>);
};
}
declare namespace lib_plankton.cache {
/**
*/
function get<type_value>(cache: type_cache<type_value>, key: string, retrieve: (() => Promise<type_value>)): Promise<type_value>;
/**
*/
function get_complex<type_input, type_value>(cache: type_cache<type_value>, group: string, input: type_input, retrieve: ((input: type_input) => Promise<type_value>), options?: {
encode_input?: ((input: type_input) => string);
}): Promise<type_value>;
}
declare namespace lib_plankton.cache.never {
/**
* @author fenris
*/
type type_subject<type_value> = {};
/**
* @author fenris
*/
function make<type_value>(): type_subject<type_value>;
/**
*/
function implementation<type_value>(subject: type_subject<type_value>): type_cache<type_value>;
}
declare namespace lib_plankton.cache.always {
/**
* @author fenris
*/
type type_subject<type_value> = {
value: lib_plankton.pod.type_pod<type_value>;
};
/**
* @author fenris
*/
function make<type_value>(value: lib_plankton.pod.type_pod<type_value>): type_subject<type_value>;
/**
* @author fenris
*/
function clear<type_value>(subject: type_subject<type_value>): Promise<void>;
/**
* @author fenris
*/
function query<type_value>(subject: type_subject<type_value>, key: string, retrieve: (() => Promise<type_value>)): Promise<{
retrieved: boolean;
value: type_value;
}>;
/**
*/
function implementation<type_value>(subject: type_subject<type_value>): type_cache<type_value>;
}
declare namespace lib_plankton.cache.chest {
/**
* @author fenris
*/
type type_subject<type_value> = {
chest: lib_plankton.storage.type_chest<string, type_value, void, any, any>;
};
/**
* @author fenris
*/
function make<type_value>(options?: {
chest?: lib_plankton.storage.type_chest<string, type_value, void, any, any>;
}): type_subject<type_value>;
/**
* @author fenris
*/
function init<type_value>(subject: type_subject<type_value>): Promise<void>;
/**
* @author fenris
*/
function clear<type_value>(subject: type_subject<type_value>): Promise<void>;
/**
* @author fenris
*/
function query<type_value>(subject: type_subject<type_value>, key: string, retrieve: (() => Promise<type_value>)): Promise<{
retrieved: boolean;
value: type_value;
}>;
/**
*/
function implementation<type_value>(subject: type_subject<type_value>): type_cache<type_value>;
}
declare namespace lib_plankton.shape {
/**
* @todo

View file

@ -7423,6 +7423,289 @@ var lib_plankton;
})(storage = lib_plankton.storage || (lib_plankton.storage = {}));
})(lib_plankton || (lib_plankton = {}));
/*
This file is part of »bacterio-plankton:cache«.
Copyright 2016-2024 'Christian Fraß, Christian Neubauer, Martin Springwald GbR'
<info@greenscale.de>
»bacterio-plankton:cache« is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
»bacterio-plankton:cache« is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with »bacterio-plankton:cache«. If not, see <http://www.gnu.org/licenses/>.
*/
/*
This file is part of »bacterio-plankton:cache«.
Copyright 2016-2024 'Christian Fraß, Christian Neubauer, Martin Springwald GbR'
<info@greenscale.de>
»bacterio-plankton:cache« is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
»bacterio-plankton:cache« is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with »bacterio-plankton:cache«. If not, see <http://www.gnu.org/licenses/>.
*/
var lib_plankton;
(function (lib_plankton) {
var cache;
(function (cache_1) {
/**
*/
function get(cache, key, retrieve) {
return cache.query(key, retrieve).then(result => result.value);
}
cache_1.get = get;
/**
*/
function get_complex(cache, group, input, retrieve, options = {}) {
options = Object.assign({
"encode_input": input => JSON.stringify(input),
}, options);
return get(cache, (group + "." + options.encode_input(input)), () => retrieve(input));
}
cache_1.get_complex = get_complex;
})(cache = lib_plankton.cache || (lib_plankton.cache = {}));
})(lib_plankton || (lib_plankton = {}));
/*
This file is part of »bacterio-plankton:cache«.
Copyright 2016-2024 'Christian Fraß, Christian Neubauer, Martin Springwald GbR'
<info@greenscale.de>
»bacterio-plankton:cache« is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
»bacterio-plankton:cache« is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with »bacterio-plankton:cache«. If not, see <http://www.gnu.org/licenses/>.
*/
var lib_plankton;
(function (lib_plankton) {
var cache;
(function (cache) {
var never;
(function (never) {
/**
* @author fenris
*/
function make() {
return {};
}
never.make = make;
/**
* @author fenris
*/
function clear(subject) {
return Promise.resolve(undefined);
}
/**
* @author fenris
*/
async function query(subject, key, retrieve) {
return {
"retrieved": true,
"value": (await retrieve()),
};
}
/**
*/
function implementation(subject) {
return {
"init": () => Promise.resolve(undefined),
"clear": () => clear(subject),
"query": (key, retrieve) => query(subject, key, retrieve),
};
}
never.implementation = implementation;
})(never = cache.never || (cache.never = {}));
})(cache = lib_plankton.cache || (lib_plankton.cache = {}));
})(lib_plankton || (lib_plankton = {}));
/*
This file is part of »bacterio-plankton:cache«.
Copyright 2016-2024 'Christian Fraß, Christian Neubauer, Martin Springwald GbR'
<info@greenscale.de>
»bacterio-plankton:cache« is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
»bacterio-plankton:cache« is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with »bacterio-plankton:cache«. If not, see <http://www.gnu.org/licenses/>.
*/
var lib_plankton;
(function (lib_plankton) {
var cache;
(function (cache) {
var always;
(function (always) {
/**
* @author fenris
*/
function make(value) {
return {
"value": value,
};
}
always.make = make;
/**
* @author fenris
*/
function clear(subject) {
return Promise.resolve(undefined);
}
always.clear = clear;
/**
* @author fenris
*/
function query(subject, key, retrieve) {
if (lib_plankton.pod.is_filled(subject.value)) {
return Promise.resolve({
"retrieved": false,
"value": lib_plankton.pod.cull(subject.value),
});
}
else {
return Promise.reject();
}
}
always.query = query;
/**
*/
function implementation(subject) {
return {
"init": () => Promise.resolve(undefined),
"clear": () => clear(subject),
"query": (key, retrieve) => query(subject, key, retrieve),
};
}
always.implementation = implementation;
})(always = cache.always || (cache.always = {}));
})(cache = lib_plankton.cache || (lib_plankton.cache = {}));
})(lib_plankton || (lib_plankton = {}));
/*
This file is part of »bacterio-plankton:cache«.
Copyright 2016-2024 'Christian Fraß, Christian Neubauer, Martin Springwald GbR'
<info@greenscale.de>
»bacterio-plankton:cache« is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
»bacterio-plankton:cache« is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with »bacterio-plankton:cache«. If not, see <http://www.gnu.org/licenses/>.
*/
var lib_plankton;
(function (lib_plankton) {
var cache;
(function (cache) {
var chest;
(function (chest) {
/**
* @author fenris
*/
function make(options = {}) {
options = Object.assign({
"chest": lib_plankton.storage.memory.implementation_chest({}),
}, options);
return {
"chest": options.chest,
};
}
chest.make = make;
/**
* @author fenris
*/
function init(subject) {
return subject.chest.setup(undefined);
}
chest.init = init;
/**
* @author fenris
*/
function clear(subject) {
return subject.chest.clear();
}
chest.clear = clear;
/**
* @author fenris
*/
function query(subject, key, retrieve) {
return (subject.chest.read(key)
.then(value => (lib_plankton.log.info("cache.chest_hashed.known", {
"key": key,
})
,
Promise.resolve({
"retrieved": false,
"value": value,
})))
.catch(() => (lib_plankton.log.info("cache.chest_hashed.unknown", {
"key": key,
})
,
(retrieve()
.then((value) => (subject.chest.write(key, value)
.then(() => Promise.resolve({
"retrieved": true,
"value": value,
}))))
/*
.catch(
(reason) => Promise.reject<{retrieved : boolean; value : type_value}>(reason)
)
*/
))));
}
chest.query = query;
/**
*/
function implementation(subject) {
return {
"init": () => init(subject),
"clear": () => clear(subject),
"query": (key, retrieve) => query(subject, key, retrieve),
};
}
chest.implementation = implementation;
})(chest = cache.chest || (cache.chest = {}));
})(cache = lib_plankton.cache || (lib_plankton.cache = {}));
})(lib_plankton || (lib_plankton = {}));
/*
This file is part of »bacterio-plankton:shape«.
Copyright 2016-2024 'Christian Fraß, Christian Neubauer, Martin Springwald GbR'

8
source/base.ts Normal file
View file

@ -0,0 +1,8 @@
namespace _zeitbild
{
/**
*/
export var cache : lib_plankton.cache.type_cache<any>;
}

View file

@ -260,6 +260,14 @@ async function main(
)
)
);
_zeitbild.cache = lib_plankton.cache.chest.implementation<any>(
lib_plankton.cache.chest.make<any>(
{
"chest": lib_plankton.storage.memory.implementation_chest<any>({}),
}
)
);
await _zeitbild.cache.init();
// exec
if (args["help"]) {

View file

@ -251,6 +251,7 @@ namespace _zeitbild.repository.calendar
{"level": access_attributed_row["level"]}
);
}
await _zeitbild.cache.clear();
return Promise.resolve<_zeitbild.type_calendar_id>(calendar_id);
}
@ -312,6 +313,7 @@ namespace _zeitbild.repository.calendar
);
}
}
await _zeitbild.cache.clear();
return Promise.resolve<void>(undefined);
}
@ -323,6 +325,7 @@ namespace _zeitbild.repository.calendar
calendar_id : _zeitbild.type_calendar_id
) : Promise<void>
{
await _zeitbild.cache.clear();
const core_store = get_core_store();
const access_attributed_chest = get_access_attributed_chest();
// attributed access
@ -371,70 +374,77 @@ namespace _zeitbild.repository.calendar
>
>
{
return (
lib_plankton.file.read("sql/calendar_overview.sql")
.then(
(template) => _zeitbild.database.get_implementation().query_free_get(
{
"template": template,
"arguments": {
"user_id": user_id,
return lib_plankton.cache.get_complex<any, Array<type_overview_entry>>(
_zeitbild.cache,
"calendar_overview",
{
"user_id": user_id,
},
() => (
lib_plankton.file.read("sql/calendar_overview.sql")
.then(
(template) => _zeitbild.database.get_implementation().query_free_get(
{
"template": template,
"arguments": {
"user_id": user_id,
}
}
}
)
)
)
.then(
(rows) => Promise.resolve(
lib_plankton.call.convey(
rows,
[
(x : Array<Record<string, any>>) => x.map(
(row : Record<string, any>) => ({
"id": row["id"],
"name": row["name"],
/**
* @todo unite with _zeitbild.service.calendar.get_access_level
*/
"access_level": decode_access_level(
Math.max(
(row["access_public"] ? 1 : 0),
(
(user_id === null)
?
0
:
(row["access_level_attributed"] ?? row["access_level_default"])
.then(
(rows) => Promise.resolve(
lib_plankton.call.convey(
rows,
[
(x : Array<Record<string, any>>) => x.map(
(row : Record<string, any>) => ({
"id": row["id"],
"name": row["name"],
/**
* @todo unite with _zeitbild.service.calendar.get_access_level
*/
"access_level": decode_access_level(
Math.max(
(row["access_public"] ? 1 : 0),
(
(user_id === null)
?
0
:
(row["access_level_attributed"] ?? row["access_level_default"])
)
)
),
})
),
(x : Array<type_overview_entry>) => x.filter(
(row) => (
! _zeitbild.value_object.access_level.order(
row.access_level,
_zeitbild.enum_access_level.none
)
),
})
),
(x : Array<type_overview_entry>) => x.filter(
(row) => (
! _zeitbild.value_object.access_level.order(
row.access_level,
_zeitbild.enum_access_level.none
)
)
),
(x : Array<type_overview_entry>) => lib_plankton.list.sorted<type_overview_entry>(
x,
{
"compare_element": lib_plankton.order.order_lexicographic_pair_wrapped<type_overview_entry, _zeitbild.enum_access_level, int>(
row => row.access_level,
row => row.id,
{
"order_first": _zeitbild.value_object.access_level.order,
"order_second": (a, b) => (a <= b)
}
),
}
),
]
),
(x : Array<type_overview_entry>) => lib_plankton.list.sorted<type_overview_entry>(
x,
{
"compare_element": lib_plankton.order.order_lexicographic_pair_wrapped<type_overview_entry, _zeitbild.enum_access_level, int>(
row => row.access_level,
row => row.id,
{
"order_first": (a, b) => _zeitbild.value_object.access_level.order(b, a),
"order_second": (a, b) => (a <= b)
}
),
}
),
]
)
)
)
)
)
);
}
}

View file

@ -398,6 +398,7 @@ namespace _zeitbild.repository.resource
"sub_id": local_resource_id,
}
);
await _zeitbild.cache.clear();
return Promise.resolve<_zeitbild.type_resource_id>(resource_id);
break;
}
@ -414,6 +415,7 @@ namespace _zeitbild.repository.resource
"sub_id": caldav_resource_id,
}
);
await _zeitbild.cache.clear();
return Promise.resolve<_zeitbild.type_resource_id>(resource_id);
break;
}
@ -493,6 +495,7 @@ namespace _zeitbild.repository.resource
"read_only": resource_object.data.read_only,
}
);
await _zeitbild.cache.clear();
break;
}
default: {
@ -546,7 +549,7 @@ namespace _zeitbild.repository.resource
throw (new Error("not a local resource"));
}
else {
return get_local_resource_event_store().create(
const local_resource_event_id: _zeitbild.type_local_resource_event_id = await get_local_resource_event_store().create(
encode_local_resource_event(
{
"local_resource_id": dataset_core["sub_id"],
@ -554,6 +557,8 @@ namespace _zeitbild.repository.resource
}
)
);
await _zeitbild.cache.clear();
return Promise.resolve(local_resource_event_id);
}
}
@ -571,7 +576,7 @@ namespace _zeitbild.repository.resource
throw (new Error("not a local resource"));
}
else {
return get_local_resource_event_store().update(
await get_local_resource_event_store().update(
event_id,
encode_local_resource_event(
{
@ -580,6 +585,8 @@ namespace _zeitbild.repository.resource
}
)
);
await _zeitbild.cache.clear();
return Promise.resolve<void>(undefined);
}
}
@ -591,6 +598,7 @@ namespace _zeitbild.repository.resource
local_resource_event_id : _zeitbild.type_local_resource_event_id
) : Promise<void>
{
await _zeitbild.cache.clear();
const dataset_core : Record<string, any> = await get_resource_core_store().read(resource_id);
if (! (dataset_core.kind === "local")) {
throw (new Error("not a local resource"));

View file

@ -425,6 +425,19 @@ namespace _zeitbild.service.calendar
}
/**
*/
type type_gather_events_result = Array<
{
calendar_id : _zeitbild.type_calendar_id;
calendar_name : string;
access_level : _zeitbild.enum_access_level;
event_id : (null | _zeitbild.type_local_resource_event_id);
event_object : _zeitbild.type_event_object;
}
>;
/**
*/
export async function gather_events(
@ -432,17 +445,7 @@ namespace _zeitbild.service.calendar
from_pit : lib_plankton.pit.type_pit,
to_pit : lib_plankton.pit.type_pit,
user_id : (null | _zeitbild.type_user_id)
) : Promise<
Array<
{
calendar_id : _zeitbild.type_calendar_id;
calendar_name : string;
access_level : _zeitbild.enum_access_level;
event_id : (null | _zeitbild.type_local_resource_event_id);
event_object : _zeitbild.type_event_object;
}
>
>
) : Promise<type_gather_events_result>
{
const calendar_ids_allowed : Array<_zeitbild.type_calendar_id> = (
(await overview(user_id))
@ -464,48 +467,58 @@ namespace _zeitbild.service.calendar
)
);
calendar_ids.sort();
return (
Promise.all(
calendar_ids
.map(
async (calendar_id) => {
const calendar_object : _zeitbild.type_calendar_object = await _zeitbild.repository.calendar.read(
calendar_id
);
const access_level : _zeitbild.enum_access_level = get_access_level(
calendar_object,
user_id
);
const events : Array<
{
id : (null | _zeitbild.type_local_resource_event_id);
object : _zeitbild.type_event_object;
}
> = await get_events(
calendar_id,
from_pit,
to_pit,
user_id
);
return Promise.resolve(
events
.map(
(event_entry) => ({
"calendar_id": calendar_id,
"calendar_name": calendar_object.name,
"access_level": access_level,
"event_id": event_entry.id,
"event_object": event_entry.object,
})
)
);
}
return lib_plankton.cache.get_complex<any, type_gather_events_result>(
_zeitbild.cache,
"gather_events",
{
"user_id": user_id,
"from_pit": from_pit,
"to_pit": to_pit,
"calendar_ids": calendar_ids,
},
() => (
Promise.all(
calendar_ids
.map(
async (calendar_id) => {
const calendar_object : _zeitbild.type_calendar_object = await _zeitbild.repository.calendar.read(
calendar_id
);
const access_level : _zeitbild.enum_access_level = get_access_level(
calendar_object,
user_id
);
const events : Array<
{
id : (null | _zeitbild.type_local_resource_event_id);
object : _zeitbild.type_event_object;
}
> = await get_events(
calendar_id,
from_pit,
to_pit,
user_id
);
return Promise.resolve(
events
.map(
(event_entry) => ({
"calendar_id": calendar_id,
"calendar_name": calendar_object.name,
"access_level": access_level,
"event_id": event_entry.id,
"event_object": event_entry.object,
})
)
);
}
)
)
)
.then(
(sub_results) => sub_results.reduce(
(x, y) => x.concat(y),
[]
.then(
(sub_results) => sub_results.reduce(
(x, y) => x.concat(y),
[]
)
)
)
);

View file

@ -35,6 +35,7 @@ ${dir_temp}/conf.ts: \
${dir_temp}/zeitbild-unlinked.js: \
${dir_lib}/plankton/plankton.d.ts \
${dir_source}/base.ts \
${dir_source}/helpers.ts \
${dir_source}/conf.ts \
${dir_source}/database.ts \

View file

@ -10,6 +10,7 @@ modules="${modules} call"
modules="${modules} log"
modules="${modules} conf"
modules="${modules} storage"
modules="${modules} cache"
modules="${modules} database"
modules="${modules} session"
modules="${modules} file"