keeping track of peers from whom deltas are received
This commit is contained in:
parent
518bc4eb44
commit
e24be625d1
|
@ -0,0 +1,13 @@
|
||||||
|
import {PeerAddress} from '../src/types';
|
||||||
|
|
||||||
|
describe('PeerAddress', () => {
|
||||||
|
it('toString()', () => {
|
||||||
|
const addr = new PeerAddress('localhost', 1000);
|
||||||
|
expect(addr.toString()).toBe("localhost:1000");
|
||||||
|
});
|
||||||
|
it('fromString()', () => {
|
||||||
|
const addr = PeerAddress.fromString("localhost:1000");
|
||||||
|
expect(addr.addr).toBe("localhost");
|
||||||
|
expect(addr.port).toBe(1000);
|
||||||
|
});
|
||||||
|
});
|
|
@ -4,4 +4,9 @@ import tseslint from 'typescript-eslint';
|
||||||
export default tseslint.config(
|
export default tseslint.config(
|
||||||
eslint.configs.recommended,
|
eslint.configs.recommended,
|
||||||
tseslint.configs.recommended,
|
tseslint.configs.recommended,
|
||||||
|
{
|
||||||
|
ignores: [
|
||||||
|
"dist/",
|
||||||
|
],
|
||||||
|
}
|
||||||
);
|
);
|
File diff suppressed because it is too large
Load Diff
|
@ -5,7 +5,11 @@
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"start": "node --experimental-strip-types --experimental-transform-types src/main.ts",
|
"start": "node --experimental-strip-types --experimental-transform-types src/main.ts",
|
||||||
"lint": "eslint",
|
"lint": "eslint",
|
||||||
"test": "echo \"Error: no test specified\" && exit 1"
|
"test": "jest"
|
||||||
|
},
|
||||||
|
"jest": {
|
||||||
|
"testEnvironment": "node",
|
||||||
|
"preset": "ts-jest"
|
||||||
},
|
},
|
||||||
"author": "",
|
"author": "",
|
||||||
"license": "Unlicense",
|
"license": "Unlicense",
|
||||||
|
@ -20,9 +24,12 @@
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@eslint/js": "^9.17.0",
|
"@eslint/js": "^9.17.0",
|
||||||
"@types/express": "^5.0.0",
|
"@types/express": "^5.0.0",
|
||||||
|
"@types/jest": "^29.5.14",
|
||||||
"@types/node": "^22.10.2",
|
"@types/node": "^22.10.2",
|
||||||
"eslint": "^9.17.0",
|
"eslint": "^9.17.0",
|
||||||
"eslint-config-airbnb-base-typescript": "^1.1.0",
|
"eslint-config-airbnb-base-typescript": "^1.1.0",
|
||||||
|
"jest": "^29.7.0",
|
||||||
|
"ts-jest": "^29.2.5",
|
||||||
"typescript": "^5.7.2",
|
"typescript": "^5.7.2",
|
||||||
"typescript-eslint": "^8.18.0"
|
"typescript-eslint": "^8.18.0"
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,9 +2,8 @@
|
||||||
// It should enable operations like removing a property removes the value from the entities in the collection
|
// It should enable operations like removing a property removes the value from the entities in the collection
|
||||||
// It could then be further extended with e.g. table semantics like filter, sort, join
|
// It could then be further extended with e.g. table semantics like filter, sort, join
|
||||||
|
|
||||||
|
|
||||||
import EventEmitter from "node:events";
|
import EventEmitter from "node:events";
|
||||||
import { publishDelta, subscribeDeltas } from "./deltas";
|
import { deltasAccepted, publishDelta, subscribeDeltas } from "./deltas";
|
||||||
import { Entity, EntityProperties, EntityPropertiesDeltaBuilder } from "./object-layer";
|
import { Entity, EntityProperties, EntityPropertiesDeltaBuilder } from "./object-layer";
|
||||||
import { Delta } from "./types";
|
import { Delta } from "./types";
|
||||||
import { randomUUID } from "node:crypto";
|
import { randomUUID } from "node:crypto";
|
||||||
|
@ -45,10 +44,8 @@ export class Collection {
|
||||||
entities = new Map<string, Entity>();
|
entities = new Map<string, Entity>();
|
||||||
eventStream = new EventEmitter();
|
eventStream = new EventEmitter();
|
||||||
constructor() {
|
constructor() {
|
||||||
console.log('COLLECTION SUBSCRIBING TO DELTA STREAM');
|
|
||||||
subscribeDeltas((delta: Delta) => {
|
subscribeDeltas((delta: Delta) => {
|
||||||
// TODO: Make sure this is the kind of delta we're looking for
|
// TODO: Make sure this is the kind of delta we're looking for
|
||||||
console.log('COLLECTION RECEIVED DELTA');
|
|
||||||
this.applyDelta(delta);
|
this.applyDelta(delta);
|
||||||
});
|
});
|
||||||
this.eventStream.on('create', (entity: Entity) => {
|
this.eventStream.on('create', (entity: Entity) => {
|
||||||
|
@ -69,6 +66,7 @@ export class Collection {
|
||||||
eventType = 'create';
|
eventType = 'create';
|
||||||
}
|
}
|
||||||
const deltaBulider = new EntityPropertiesDeltaBuilder(entityId);
|
const deltaBulider = new EntityPropertiesDeltaBuilder(entityId);
|
||||||
|
console.log('deltaBulider -->', deltaBulider.delta);
|
||||||
|
|
||||||
if (!properties) {
|
if (!properties) {
|
||||||
// Let's interpret this as entity deletion
|
// Let's interpret this as entity deletion
|
||||||
|
@ -157,6 +155,7 @@ export class Collection {
|
||||||
const deltas: Delta[] = [];
|
const deltas: Delta[] = [];
|
||||||
const entity = this.updateEntity(entityId, properties, true, deltas);
|
const entity = this.updateEntity(entityId, properties, true, deltas);
|
||||||
deltas.forEach(async (delta: Delta) => {
|
deltas.forEach(async (delta: Delta) => {
|
||||||
|
deltasAccepted.push(delta);
|
||||||
await publishDelta(delta);
|
await publishDelta(delta);
|
||||||
});
|
});
|
||||||
return entity;
|
return entity;
|
||||||
|
@ -165,6 +164,7 @@ export class Collection {
|
||||||
const deltas: Delta[] = [];
|
const deltas: Delta[] = [];
|
||||||
this.updateEntity(entityId, undefined, true, deltas);
|
this.updateEntity(entityId, undefined, true, deltas);
|
||||||
deltas.forEach(async (delta: Delta) => {
|
deltas.forEach(async (delta: Delta) => {
|
||||||
|
deltasAccepted.push(delta);
|
||||||
await publishDelta(delta);
|
await publishDelta(delta);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,17 +1,19 @@
|
||||||
|
import {randomUUID} from "crypto";
|
||||||
|
import {PeerAddress} from "./types";
|
||||||
|
|
||||||
export const LEVEL_DB_DIR = process.env.RHIZOME_LEVEL_DB_DIR ?? './data';
|
export const LEVEL_DB_DIR = process.env.RHIZOME_LEVEL_DB_DIR ?? './data';
|
||||||
export const CREATOR = process.env.USER!;
|
export const CREATOR = process.env.USER!;
|
||||||
export const HOST = process.env.HOST!;
|
export const HOST_ID = process.env.RHIZOME_PEER_ID || randomUUID();
|
||||||
export const ADDRESS = process.env.ADDRESS ?? '127.0.0.1';
|
export const ADDRESS = process.env.RHIZOME_ADDRESS ?? '127.0.0.1';
|
||||||
export const REQUEST_BIND_PORT = parseInt(process.env.REQUEST_BIND_PORT || '4000');
|
export const REQUEST_BIND_PORT = parseInt(process.env.RHIZOME_REQUEST_BIND_PORT || '4000');
|
||||||
export const PUBLISH_BIND_PORT = parseInt(process.env.PUBLISH_BIND_PORT || '4001');
|
export const REQUEST_BIND_ADDR = process.env.RHIZOME_REQUEST_BIND_ADDR || ADDRESS || '127.0.0.1';
|
||||||
export const REQUEST_BIND_ADDR = process.env.ADDRESS || '127.0.0.1';
|
export const REQUEST_BIND_HOST = process.env.RHIZOME_REQUEST_BIND_HOST || REQUEST_BIND_ADDR || '127.0.0.1';
|
||||||
export const PUBLISH_BIND_ADDR = process.env.ADDRESS || '127.0.0.1';
|
export const PUBLISH_BIND_PORT = parseInt(process.env.RHIZOME_PUBLISH_BIND_PORT || '4001');
|
||||||
export const HTTP_API_PORT = parseInt(process.env.HTTP_API_PORT || '3000');
|
export const PUBLISH_BIND_ADDR = process.env.RHIZOME_PUBLISH_BIND_ADDR || ADDRESS || '127.0.0.1';
|
||||||
export const HTTP_API_ADDR = process.env.ADDRESS || '127.0.0.1';
|
export const PUBLISH_BIND_HOST = process.env.RHIZOME_PUBLISH_BIND_HOST || PUBLISH_BIND_ADDR || '127.0.0.1';
|
||||||
export const ENABLE_HTTP_API = process.env.ENABLE_HTTP_API === 'true';
|
export const HTTP_API_PORT = parseInt(process.env.RHIZOME_HTTP_API_PORT || '3000');
|
||||||
export const SEED_PEERS = (process.env.SEED_PEERS || '').split(',')
|
export const HTTP_API_ADDR = process.env.RHIZOME_HTTP_API_ADDR || ADDRESS || '127.0.0.1';
|
||||||
|
export const HTTP_API_ENABLE = process.env.RHIZOME_HTTP_API_ENABLE === 'true';
|
||||||
|
export const SEED_PEERS: PeerAddress[] = (process.env.RHIZOME_SEED_PEERS || '').split(',')
|
||||||
.filter(x => !!x)
|
.filter(x => !!x)
|
||||||
.map((peer: string) => {
|
.map((peer: string) => PeerAddress.fromString(peer));
|
||||||
const [addr, port] = peer.trim().split(':');
|
|
||||||
return {addr, port: parseInt(port)};
|
|
||||||
});
|
|
||||||
|
|
|
@ -1,6 +1,8 @@
|
||||||
import EventEmitter from 'node:events';
|
import EventEmitter from 'node:events';
|
||||||
import { Delta, Decision } from './types';
|
import {REQUEST_BIND_HOST, REQUEST_BIND_PORT} from './config';
|
||||||
import { publishSock, subscribeSock } from './pub-sub';
|
import {publishSock, subscribeSock} from './pub-sub';
|
||||||
|
import {Decision, Delta, PeerAddress} from './types';
|
||||||
|
import {myRequestAddr} from './peers';
|
||||||
|
|
||||||
export const deltaStream = new EventEmitter();
|
export const deltaStream = new EventEmitter();
|
||||||
|
|
||||||
|
@ -22,7 +24,7 @@ export function ingestDelta(delta: Delta) {
|
||||||
switch (decision) {
|
switch (decision) {
|
||||||
case Decision.Accept:
|
case Decision.Accept:
|
||||||
deltasAccepted.push(delta);
|
deltasAccepted.push(delta);
|
||||||
deltaStream.emit('delta', { delta });
|
deltaStream.emit('delta', {delta});
|
||||||
break;
|
break;
|
||||||
case Decision.Reject:
|
case Decision.Reject:
|
||||||
deltasRejected.push(delta);
|
deltasRejected.push(delta);
|
||||||
|
@ -67,7 +69,7 @@ export function subscribeDeltas(fn: (delta: Delta) => void) {
|
||||||
|
|
||||||
export async function publishDelta(delta: Delta) {
|
export async function publishDelta(delta: Delta) {
|
||||||
console.log(`Publishing delta: ${JSON.stringify(delta)}`);
|
console.log(`Publishing delta: ${JSON.stringify(delta)}`);
|
||||||
await publishSock.send(["deltas", serializeDelta(delta)])
|
await publishSock.send(["deltas", myRequestAddr.toAddrString(), serializeDelta(delta)]);
|
||||||
}
|
}
|
||||||
|
|
||||||
function serializeDelta(delta: Delta) {
|
function serializeDelta(delta: Delta) {
|
||||||
|
@ -79,11 +81,12 @@ function deserializeDelta(input: string) {
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function runDeltas() {
|
export async function runDeltas() {
|
||||||
for await (const [topic, msg] of subscribeSock) {
|
for await (const [topic, sender, msg] of subscribeSock) {
|
||||||
if (topic.toString() !== "deltas") {
|
if (topic.toString() !== "deltas") {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
const delta = deserializeDelta(msg.toString());
|
const delta = deserializeDelta(msg.toString());
|
||||||
|
delta.receivedFrom = PeerAddress.fromString(sender.toString());
|
||||||
console.log(`Received delta: ${JSON.stringify(delta)}`);
|
console.log(`Received delta: ${JSON.stringify(delta)}`);
|
||||||
ingestDelta(delta);
|
ingestDelta(delta);
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,14 +1,14 @@
|
||||||
// We can start to use deltas to express relational data in a given context
|
// We can start to use deltas to express relational data in a given context
|
||||||
|
|
||||||
import express from "express";
|
import express from "express";
|
||||||
import { bindPublish, } from "./pub-sub";
|
import {Collection} from "./collection-layer";
|
||||||
import { deltasAccepted, deltasProposed, runDeltas } from "./deltas";
|
import {HTTP_API_ENABLE, HTTP_API_ADDR, HTTP_API_PORT, SEED_PEERS} from "./config";
|
||||||
import { Entity } from "./object-layer";
|
import {deltasAccepted, deltasProposed, runDeltas} from "./deltas";
|
||||||
import { Collection } from "./collection-layer";
|
import {Entity} from "./object-layer";
|
||||||
import { bindReply, runRequestHandlers } from "./request-reply";
|
import {askAllPeersForDeltas, peers, subscribeToSeeds} from "./peers";
|
||||||
import { askAllPeersForDeltas, subscribeToSeeds } from "./peers";
|
import {bindPublish, } from "./pub-sub";
|
||||||
import { ENABLE_HTTP_API, HTTP_API_ADDR, HTTP_API_PORT } from "./config";
|
import {bindReply, runRequestHandlers} from "./request-reply";
|
||||||
|
import {Delta, PeerAddress} from "./types";
|
||||||
|
|
||||||
// As an app we want to be able to write and read data.
|
// As an app we want to be able to write and read data.
|
||||||
// The data is whatever shape we define it to be in a given context.
|
// The data is whatever shape we define it to be in a given context.
|
||||||
|
@ -16,7 +16,6 @@ import { ENABLE_HTTP_API, HTTP_API_ADDR, HTTP_API_PORT } from "./config";
|
||||||
// e.g. entities and their properties.
|
// e.g. entities and their properties.
|
||||||
|
|
||||||
// This implies at least one layer on top of the underlying primitive deltas.
|
// This implies at least one layer on top of the underlying primitive deltas.
|
||||||
|
|
||||||
type UserProperties = {
|
type UserProperties = {
|
||||||
id?: string;
|
id?: string;
|
||||||
name: string;
|
name: string;
|
||||||
|
@ -48,11 +47,13 @@ class Users {
|
||||||
}
|
}
|
||||||
|
|
||||||
(async () => {
|
(async () => {
|
||||||
|
console.log('1');
|
||||||
const users = new Users();
|
const users = new Users();
|
||||||
|
console.log('2');
|
||||||
|
|
||||||
const app = express()
|
const app = express()
|
||||||
app.get("/ids", (req: express.Request, res: express.Response) => {
|
app.get("/ids", (req: express.Request, res: express.Response) => {
|
||||||
res.json({ ids: users.getIds()});
|
res.json({ids: users.getIds()});
|
||||||
});
|
});
|
||||||
|
|
||||||
app.get("/deltas", (req: express.Request, res: express.Response) => {
|
app.get("/deltas", (req: express.Request, res: express.Response) => {
|
||||||
|
@ -65,14 +66,39 @@ class Users {
|
||||||
res.json(deltasAccepted.length);
|
res.json(deltasAccepted.length);
|
||||||
});
|
});
|
||||||
|
|
||||||
if (ENABLE_HTTP_API) {
|
app.get("/peers", (req: express.Request, res: express.Response) => {
|
||||||
|
res.json(peers.map(({reqAddr, publishAddr}) => {
|
||||||
|
const isSeedPeer = !!SEED_PEERS.find(({addr, port}) =>
|
||||||
|
addr === reqAddr.addr && port === reqAddr.port);
|
||||||
|
const deltasAcceptedCount = deltasAccepted
|
||||||
|
.filter((delta: Delta) => {
|
||||||
|
return delta.receivedFrom?.addr == reqAddr.addr &&
|
||||||
|
delta.receivedFrom?.port == reqAddr.port;
|
||||||
|
})
|
||||||
|
.length;
|
||||||
|
const peerInfo = {
|
||||||
|
reqAddr: reqAddr.toAddrString(),
|
||||||
|
publishAddr: publishAddr?.toAddrString(),
|
||||||
|
isSeedPeer,
|
||||||
|
deltaCount: {
|
||||||
|
accepted: deltasAcceptedCount
|
||||||
|
}
|
||||||
|
};
|
||||||
|
return peerInfo;
|
||||||
|
}));
|
||||||
|
});
|
||||||
|
|
||||||
|
if (HTTP_API_ENABLE) {
|
||||||
app.listen(HTTP_API_PORT, HTTP_API_ADDR, () => {
|
app.listen(HTTP_API_PORT, HTTP_API_ADDR, () => {
|
||||||
console.log(`HTTP API bound to http://${HTTP_API_ADDR}:${HTTP_API_PORT}`);
|
console.log(`HTTP API bound to http://${HTTP_API_ADDR}:${HTTP_API_PORT}`);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
console.log('3');
|
||||||
await bindPublish();
|
await bindPublish();
|
||||||
|
console.log('3a');
|
||||||
await bindReply();
|
await bindReply();
|
||||||
|
console.log('3b');
|
||||||
runDeltas();
|
runDeltas();
|
||||||
runRequestHandlers();
|
runRequestHandlers();
|
||||||
await new Promise((resolve) => setTimeout(resolve, 500));
|
await new Promise((resolve) => setTimeout(resolve, 500));
|
||||||
|
@ -81,11 +107,7 @@ class Users {
|
||||||
askAllPeersForDeltas();
|
askAllPeersForDeltas();
|
||||||
await new Promise((resolve) => setTimeout(resolve, 1000));
|
await new Promise((resolve) => setTimeout(resolve, 1000));
|
||||||
|
|
||||||
setInterval(() => {
|
console.log('4');
|
||||||
console.log('deltasProposed count', deltasProposed.length,
|
|
||||||
'deltasAccepted count', deltasAccepted.length);
|
|
||||||
}, 5000)
|
|
||||||
|
|
||||||
const taliesin = users.upsert({
|
const taliesin = users.upsert({
|
||||||
// id: 'taliesin-1',
|
// id: 'taliesin-1',
|
||||||
name: 'Taliesin',
|
name: 'Taliesin',
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
import express from "express";
|
import express from "express";
|
||||||
import { runDeltas } from "./deltas";
|
import { runDeltas } from "./deltas";
|
||||||
import {ENABLE_HTTP_API, HTTP_API_ADDR, HTTP_API_PORT} from "./config";
|
import {HTTP_API_ENABLE, HTTP_API_ADDR, HTTP_API_PORT} from "./config";
|
||||||
|
|
||||||
const app = express()
|
const app = express()
|
||||||
|
|
||||||
|
@ -8,7 +8,7 @@ app.get("/", (req: express.Request, res: express.Response) => {
|
||||||
res.json({ message: "Welcome to the Express + TypeScript Server!" });
|
res.json({ message: "Welcome to the Express + TypeScript Server!" });
|
||||||
});
|
});
|
||||||
|
|
||||||
if (ENABLE_HTTP_API) {
|
if (HTTP_API_ENABLE) {
|
||||||
app.listen(HTTP_API_PORT, HTTP_API_ADDR, () => {
|
app.listen(HTTP_API_PORT, HTTP_API_ADDR, () => {
|
||||||
console.log(`HTTP API bound to http://${HTTP_API_ADDR}:${HTTP_API_PORT}`);
|
console.log(`HTTP API bound to http://${HTTP_API_ADDR}:${HTTP_API_PORT}`);
|
||||||
});
|
});
|
||||||
|
|
|
@ -7,7 +7,7 @@
|
||||||
// - As typescript interfaces?
|
// - As typescript interfaces?
|
||||||
// - As typescript classes?
|
// - As typescript classes?
|
||||||
|
|
||||||
import { CREATOR, HOST } from "./config";
|
import { CREATOR, HOST_ID } from "./config";
|
||||||
import { Delta, PropertyTypes } from "./types";
|
import { Delta, PropertyTypes } from "./types";
|
||||||
|
|
||||||
export type EntityProperties = {
|
export type EntityProperties = {
|
||||||
|
@ -31,7 +31,7 @@ export class EntityPropertiesDeltaBuilder {
|
||||||
constructor(entityId: string) {
|
constructor(entityId: string) {
|
||||||
this.delta = {
|
this.delta = {
|
||||||
creator: CREATOR,
|
creator: CREATOR,
|
||||||
host: HOST,
|
host: HOST_ID,
|
||||||
pointers: [{
|
pointers: [{
|
||||||
localContext: 'id',
|
localContext: 'id',
|
||||||
target: entityId,
|
target: entityId,
|
||||||
|
|
41
src/peers.ts
41
src/peers.ts
|
@ -1,22 +1,23 @@
|
||||||
import { PUBLISH_BIND_ADDR, PUBLISH_BIND_PORT } from "./config";
|
import {PUBLISH_BIND_HOST, PUBLISH_BIND_PORT, REQUEST_BIND_HOST, REQUEST_BIND_PORT, SEED_PEERS} from "./config";
|
||||||
import { registerRequestHandler, PeerRequest, ResponseSocket } from "./request-reply";
|
|
||||||
import { RequestSocket, } from "./request-reply";
|
|
||||||
import { SEED_PEERS } from "./config";
|
|
||||||
import {connectSubscribe} from "./pub-sub";
|
|
||||||
import {deltasAccepted, deltasProposed, ingestAll, receiveDelta} from "./deltas";
|
import {deltasAccepted, deltasProposed, ingestAll, receiveDelta} from "./deltas";
|
||||||
import {Delta} from "./types";
|
import {connectSubscribe} from "./pub-sub";
|
||||||
|
import {PeerRequest, registerRequestHandler, RequestSocket, ResponseSocket} from "./request-reply";
|
||||||
|
import {Delta, PeerAddress} from "./types";
|
||||||
|
|
||||||
export enum PeerMethods {
|
export enum PeerMethods {
|
||||||
GetPublishAddress,
|
GetPublishAddress,
|
||||||
AskForDeltas
|
AskForDeltas
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export const myRequestAddr = new PeerAddress(REQUEST_BIND_HOST, REQUEST_BIND_PORT);
|
||||||
|
export const myPublishAddr = new PeerAddress(PUBLISH_BIND_HOST, PUBLISH_BIND_PORT);
|
||||||
|
|
||||||
registerRequestHandler(async (req: PeerRequest, res: ResponseSocket) => {
|
registerRequestHandler(async (req: PeerRequest, res: ResponseSocket) => {
|
||||||
console.log('inspecting peer request');
|
console.log('inspecting peer request');
|
||||||
switch (req.method) {
|
switch (req.method) {
|
||||||
case PeerMethods.GetPublishAddress: {
|
case PeerMethods.GetPublishAddress: {
|
||||||
console.log('it\'s a request for our publish address');
|
console.log('it\'s a request for our publish address');
|
||||||
await res.send(publishAddr);
|
await res.send(myPublishAddr.toAddrString());
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case PeerMethods.AskForDeltas: {
|
case PeerMethods.AskForDeltas: {
|
||||||
|
@ -29,29 +30,20 @@ registerRequestHandler(async (req: PeerRequest, res: ResponseSocket) => {
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
export type PeerAddress = {
|
|
||||||
addr: string,
|
|
||||||
port: number
|
|
||||||
};
|
|
||||||
|
|
||||||
const publishAddr: PeerAddress = {
|
|
||||||
addr: PUBLISH_BIND_ADDR,
|
|
||||||
port: PUBLISH_BIND_PORT
|
|
||||||
};
|
|
||||||
|
|
||||||
class Peer {
|
class Peer {
|
||||||
|
reqAddr: PeerAddress;
|
||||||
reqSock: RequestSocket;
|
reqSock: RequestSocket;
|
||||||
publishAddr: PeerAddress | undefined;
|
publishAddr: PeerAddress | undefined;
|
||||||
constructor(addr: string, port: number) {
|
constructor(addr: string, port: number) {
|
||||||
|
this.reqAddr = new PeerAddress(addr, port);
|
||||||
this.reqSock = new RequestSocket(addr, port);
|
this.reqSock = new RequestSocket(addr, port);
|
||||||
}
|
}
|
||||||
async subscribe() {
|
async subscribe() {
|
||||||
if (!this.publishAddr) {
|
if (!this.publishAddr) {
|
||||||
const res = await this.reqSock.request(PeerMethods.GetPublishAddress);
|
const res = await this.reqSock.request(PeerMethods.GetPublishAddress);
|
||||||
// TODO: input validation
|
// TODO: input validation
|
||||||
const {addr, port} = JSON.parse(res.toString());
|
this.publishAddr = PeerAddress.fromString(res.toString());
|
||||||
this.publishAddr = {addr, port};
|
connectSubscribe(this.publishAddr!);
|
||||||
connectSubscribe(addr, port);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
async askForDeltas(): Promise<Delta[]> {
|
async askForDeltas(): Promise<Delta[]> {
|
||||||
|
@ -67,12 +59,12 @@ class Peer {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const peers: Peer[] = [];
|
export const peers: Peer[] = [];
|
||||||
|
|
||||||
function newPeer(addr: string, port: number) {
|
function newPeer(addr: string, port: number) {
|
||||||
const peer = new Peer(addr, port);
|
const peer = new Peer(addr, port);
|
||||||
peers.push(peer);
|
peers.push(peer);
|
||||||
return peer;
|
return peer;
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function subscribeToSeeds() {
|
export async function subscribeToSeeds() {
|
||||||
|
@ -90,6 +82,7 @@ export async function askAllPeersForDeltas() {
|
||||||
const deltas = await peer.askForDeltas();
|
const deltas = await peer.askForDeltas();
|
||||||
console.log('received deltas:', deltas);
|
console.log('received deltas:', deltas);
|
||||||
for (const delta of deltas) {
|
for (const delta of deltas) {
|
||||||
|
delta.receivedFrom = peer.reqAddr;
|
||||||
receiveDelta(delta);
|
receiveDelta(delta);
|
||||||
}
|
}
|
||||||
console.log('deltasProposed count', deltasProposed.length);
|
console.log('deltasProposed count', deltasProposed.length);
|
||||||
|
|
|
@ -1,18 +1,21 @@
|
||||||
import { Publisher, Subscriber } from 'zeromq';
|
import {Publisher, Subscriber} from 'zeromq';
|
||||||
import { PUBLISH_BIND_PORT, PUBLISH_BIND_ADDR} from './config';
|
import {PUBLISH_BIND_ADDR, PUBLISH_BIND_PORT} from './config';
|
||||||
|
import {PeerAddress} from './types';
|
||||||
|
|
||||||
export const publishSock = new Publisher();
|
export const publishSock = new Publisher();
|
||||||
export const subscribeSock = new Subscriber();
|
export const subscribeSock = new Subscriber();
|
||||||
|
|
||||||
export async function bindPublish() {
|
export async function bindPublish() {
|
||||||
const addrStr = `tcp://${PUBLISH_BIND_ADDR}:${PUBLISH_BIND_PORT}`;
|
const addrStr = `tcp://${PUBLISH_BIND_ADDR}:${PUBLISH_BIND_PORT}`;
|
||||||
|
console.log('addrStr:', addrStr);
|
||||||
await publishSock.bind(addrStr);
|
await publishSock.bind(addrStr);
|
||||||
console.log(`Publishing socket bound to ${addrStr}`);
|
console.log(`Publishing socket bound to ${addrStr}`);
|
||||||
}
|
}
|
||||||
|
|
||||||
export function connectSubscribe(host: string, port: number) {
|
export function connectSubscribe(publishAddr: PeerAddress) {
|
||||||
// TODO: peer discovery
|
// TODO: peer discovery
|
||||||
const addrStr = `tcp://${host}:${port}`;
|
const addrStr = `tcp://${publishAddr.toAddrString()}`;
|
||||||
|
console.log('connectSubscribe', {addrStr});
|
||||||
subscribeSock.connect(addrStr);
|
subscribeSock.connect(addrStr);
|
||||||
subscribeSock.subscribe("deltas");
|
subscribeSock.subscribe("deltas");
|
||||||
console.log(`Subscribing to ${addrStr}`);
|
console.log(`Subscribing to ${addrStr}`);
|
||||||
|
|
34
src/types.ts
34
src/types.ts
|
@ -4,10 +4,11 @@ export type Pointer = {
|
||||||
targetContext?: string
|
targetContext?: string
|
||||||
};
|
};
|
||||||
|
|
||||||
export type Delta = {
|
export type Delta = {
|
||||||
creator: string,
|
creator: string,
|
||||||
host: string,
|
host: string,
|
||||||
pointers: Pointer[],
|
pointers: Pointer[],
|
||||||
|
receivedFrom?: PeerAddress,
|
||||||
}
|
}
|
||||||
|
|
||||||
export type DeltaContext = Delta & {
|
export type DeltaContext = Delta & {
|
||||||
|
@ -15,17 +16,17 @@ export type DeltaContext = Delta & {
|
||||||
};
|
};
|
||||||
|
|
||||||
export type Query = {
|
export type Query = {
|
||||||
filterExpr: JSON
|
filterExpr: JSON
|
||||||
};
|
};
|
||||||
|
|
||||||
export type QueryResult = {
|
export type QueryResult = {
|
||||||
deltas: Delta[]
|
deltas: Delta[]
|
||||||
};
|
};
|
||||||
|
|
||||||
export enum Decision {
|
export enum Decision {
|
||||||
Accept,
|
Accept,
|
||||||
Reject,
|
Reject,
|
||||||
Defer
|
Defer
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
@ -37,3 +38,24 @@ export type FilterGenerator = () => FilterExpr;
|
||||||
|
|
||||||
export type PropertyTypes = string | number | undefined;
|
export type PropertyTypes = string | number | undefined;
|
||||||
|
|
||||||
|
export class PeerAddress {
|
||||||
|
addr: string;
|
||||||
|
port: number;
|
||||||
|
constructor(addr: string, port: number) {
|
||||||
|
this.addr = addr;
|
||||||
|
this.port = port;
|
||||||
|
}
|
||||||
|
static fromString(addrString: string): PeerAddress {
|
||||||
|
const [addr, port] = addrString.trim().split(':');
|
||||||
|
return new PeerAddress(addr, parseInt(port));
|
||||||
|
}
|
||||||
|
toAddrString() {
|
||||||
|
console.log('toAddrStr...', {addr: this.addr, port: this.port});
|
||||||
|
return `${this.addr}:${this.port}`;
|
||||||
|
}
|
||||||
|
toJSON() {
|
||||||
|
console.log('toAddrStr...', {addr: this.addr, port: this.port});
|
||||||
|
return `${this.addr}:${this.port}`;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue