Skip to content

Commit d1f2217

Browse files
committed
wip prototyping queue, tasks and timer
1 parent 3c1b8c8 commit d1f2217

13 files changed

Lines changed: 1248 additions & 10 deletions

File tree

benches/gitgc.ts

Lines changed: 10 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ import path from 'path';
22
import b from 'benny';
33
import { suiteCommon } from './utils';
44

5-
async function main () {
5+
async function main() {
66
let map = new Map();
77
let obj = {};
88
let arr: any = [];
@@ -18,10 +18,10 @@ async function main () {
1818
for (let i = 0; i < 1000; i++) {
1919
map.delete(i);
2020
}
21-
for (const i of map) {
21+
for (const _i of map) {
2222
// NOOP
2323
}
24-
}
24+
};
2525
}),
2626
b.add('obj', async () => {
2727
obj = {};
@@ -32,26 +32,26 @@ async function main () {
3232
for (let i = 0; i < 1000; i++) {
3333
delete obj[i];
3434
}
35-
for (const i in obj) {
35+
for (const _i in obj) {
3636
// NOOP
3737
}
3838
};
3939
}),
4040
b.add('arr', async () => {
41-
// you first have to count the number of objects
41+
// You first have to count the number of objects
4242
arr = [];
4343
return async () => {
44-
// you have to iterate for each object
44+
// You have to iterate for each object
4545
// then for each value in length
4646
for (let i = 0; i < 1000; i++) {
4747
if (i === arr.length) {
48-
// double the vector
48+
// Double the vector
4949
arr.length = arr.length * 2 || 2;
5050
}
5151
arr[i] = { id: i, mark: false };
52-
// arr.push({ id: i, mark: false});
52+
// Arr.push({ id: i, mark: false});
5353
}
54-
// this has to iterate the length of the array
54+
// This has to iterate the length of the array
5555
// but stop as soon as it reaches the end
5656
// it gets complicate, but for 5x improvement
5757
// it could be interesting
@@ -74,7 +74,7 @@ async function main () {
7474
for (let i = 0; i < 1000; i++) {
7575
set.delete(i);
7676
}
77-
for (const i of set) {
77+
for (const _i of set) {
7878
// NOOP
7979
}
8080
};

src/tasks/Queue.ts

Lines changed: 165 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,165 @@
1+
import type { DB, DBTransaction, LevelPath } from '@matrixai/db';
2+
import type { TaskIdString } from './types';
3+
import type { PolykeyWorkerManagerInterface } from '../workers/types';
4+
import type { PromiseDeconstructed } from '../types';
5+
import Logger from '@matrixai/logger';
6+
import {
7+
CreateDestroyStartStop,
8+
ready,
9+
} from '@matrixai/async-init/dist/CreateDestroyStartStop';
10+
import * as tasksUtils from './utils';
11+
import * as tasksErrors from './errors';
12+
13+
interface Queue extends CreateDestroyStartStop {}
14+
@CreateDestroyStartStop(
15+
new tasksErrors.ErrorQueueRunning(),
16+
new tasksErrors.ErrorQueueDestroyed(),
17+
)
18+
class Queue {
19+
20+
public static async createQueue({
21+
db,
22+
logger = new Logger(this.name),
23+
fresh = false,
24+
}: {
25+
db: DB;
26+
logger?: Logger;
27+
fresh?: boolean;
28+
}) {
29+
logger.info(`Creating ${this.name}`);
30+
const queue = new this({ db, logger });
31+
await queue.start({ fresh });
32+
logger.info(`Created ${this.name}`);
33+
return queue;
34+
}
35+
36+
protected logger: Logger;
37+
protected db: DB;
38+
protected workerManager?: PolykeyWorkerManagerInterface;
39+
protected queueDbPath: LevelPath = [this.constructor.name];
40+
41+
// when the queue to execute the tasks
42+
// the task id is generated outside
43+
// you don't get a task id here
44+
// you just "push" tasks there to be executed
45+
// this is the "shared" set of promises to be maintained
46+
protected promises: Map<TaskIdString, PromiseDeconstructed<any>> = new Map();
47+
48+
// /**
49+
// * Listeners for task execution
50+
// * When a task is executed, these listeners are synchronously executed
51+
// * The listeners are intended for resolving or rejecting task promises
52+
// */
53+
// protected listeners: Map<TaskIdString, Array<TaskListener>> = new Map();
54+
55+
public constructor({
56+
db,
57+
logger
58+
}: {
59+
db: DB;
60+
logger: Logger
61+
}) {
62+
this.logger = logger;
63+
this.db = db;
64+
}
65+
66+
public setWorkerManager(workerManager: PolykeyWorkerManagerInterface) {
67+
this.workerManager = workerManager;
68+
}
69+
70+
public unsetWorkerManager() {
71+
delete this.workerManager;
72+
}
73+
74+
public async start({
75+
fresh = false,
76+
}: {
77+
fresh?: boolean;
78+
} = {}): Promise<void> {
79+
this.logger.info(`Starting ${this.constructor.name}`);
80+
if (fresh) {
81+
await this.db.clear(this.queueDbPath);
82+
}
83+
this.logger.info(`Started ${this.constructor.name}`);
84+
}
85+
86+
public async stop(): Promise<void> {
87+
this.logger.info(`Stopping ${this.constructor.name}`);
88+
this.logger.info(`Stopped ${this.constructor.name}`);
89+
}
90+
91+
public async destroy() {
92+
this.logger.info(`Destroying ${this.constructor.name}`);
93+
await this.db.clear(this.queueDbPath);
94+
this.logger.info(`Destroyed ${this.constructor.name}`);
95+
}
96+
97+
// promises are "connected" to events
98+
99+
/**
100+
* IF a handler does not exist
101+
* if the task is executed
102+
* then an exception is thrown
103+
* if listener exists, the exception is passed into this listener function
104+
* if it doesn't exist, then it's just a reference exception in general, this can be logged
105+
* There's nothing else to do
106+
*/
107+
@ready(new tasksErrors.ErrorSchedulerNotRunning())
108+
protected registerListener(
109+
taskId: TaskId,
110+
taskListener: TaskListener
111+
): void {
112+
const taskIdString = taskId.toString() as TaskIdString;
113+
const taskListeners = this.listeners.get(taskIdString);
114+
if (taskListeners != null) {
115+
taskListeners.push(taskListener);
116+
} else {
117+
this.listeners.set(taskIdString, [taskListener]);
118+
}
119+
}
120+
121+
@ready(new tasksErrors.ErrorSchedulerNotRunning())
122+
protected deregisterListener(
123+
taskId: TaskId,
124+
taskListener: TaskListener
125+
): void {
126+
const taskIdString = taskId.toString() as TaskIdString;
127+
const taskListeners = this.listeners.get(taskIdString);
128+
if (taskListeners == null || taskListeners.length < 1) return;
129+
const index = taskListeners.indexOf(taskListener);
130+
if (index !== -1) {
131+
taskListeners.splice(index, 1);
132+
}
133+
}
134+
135+
}
136+
137+
export default Queue;
138+
139+
140+
// epic queue
141+
// need to do a couple things:
142+
// 1. integrate fast-check
143+
// 2. integrate span checks
144+
// 3. might also consider span logs?
145+
// 4. open tracing observability
146+
// 5. structured logging
147+
// 6. async hooks to get traced promises to understand the situation
148+
// 7. do we also get fantasy land promises? and async cancellable stuff?
149+
// 8. task abstractions?
150+
// need to use the db for this
151+
// 9. priority structure
152+
// 10. timers
153+
// abort controller
154+
155+
// kinetic data structure
156+
// the priority grows as a function of time
157+
// order by priority <- this thing has a static value
158+
// in a key value DB, you can maintain sorted index of values
159+
// IDs can be lexicographically sortable
160+
161+
// this is a persistent queue
162+
// of tasks that should be EXECUTED right now
163+
// the scheduler is a persistent scheduler of scheduled tasks
164+
// tasks get pushed from the scheduler into the queue
165+
// the queue connects to the WorkerManager

0 commit comments

Comments
 (0)