Skip to content

Commit 9a277b2

Browse files
committed
Add routing table abstraction
It contains 3 round robin arrays: routers, readers and writers. It also contains an expiration timestamp which corresponds to `ttl` received from `getServers` procedure call.
1 parent f193925 commit 9a277b2

File tree

2 files changed

+218
-0
lines changed

2 files changed

+218
-0
lines changed

src/v1/internal/routing-table.js

Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
/**
2+
* Copyright (c) 2002-2017 "Neo Technology,","
3+
* Network Engine for Objects in Lund AB [http://neotechnology.com]
4+
*
5+
* This file is part of Neo4j.
6+
*
7+
* Licensed under the Apache License, Version 2.0 (the "License");
8+
* you may not use this file except in compliance with the License.
9+
* You may obtain a copy of the License at
10+
*
11+
* http://www.apache.org/licenses/LICENSE-2.0
12+
*
13+
* Unless required by applicable law or agreed to in writing, software
14+
* distributed under the License is distributed on an "AS IS" BASIS,
15+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16+
* See the License for the specific language governing permissions and
17+
* limitations under the License.
18+
*/
19+
import {int} from "../integer";
20+
21+
const MIN_ROUTERS = 1;
22+
23+
export default class RoutingTable {
24+
25+
constructor(routers, readers, writers, expirationTime) {
26+
this.routers = routers || new RoundRobinArray();
27+
this.readers = readers || new RoundRobinArray();
28+
this.writers = writers || new RoundRobinArray();
29+
30+
this._expirationTime = expirationTime || int(0);
31+
}
32+
33+
forget(address) {
34+
// Don't remove it from the set of routers, since that might mean we lose our ability to re-discover,
35+
// just remove it from the set of readers and writers, so that we don't use it for actual work without
36+
// performing discovery first.
37+
this.readers.remove(address);
38+
this.writers.remove(address);
39+
}
40+
41+
serversDiff(otherRoutingTable) {
42+
const oldServers = new Set(this._allServers());
43+
const newServers = otherRoutingTable._allServers();
44+
newServers.forEach(newServer => oldServers.delete(newServer));
45+
return Array.from(oldServers);
46+
}
47+
48+
isStale() {
49+
return this._expirationTime.lessThan(Date.now()) ||
50+
this.routers.size() <= MIN_ROUTERS ||
51+
this.readers.isEmpty() ||
52+
this.writers.isEmpty();
53+
}
54+
55+
_allServers() {
56+
return [...this.routers.toArray(), ...this.readers.toArray(), ...this.writers.toArray()];
57+
}
58+
}
Lines changed: 160 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,160 @@
1+
/**
2+
* Copyright (c) 2002-2017 "Neo Technology,","
3+
* Network Engine for Objects in Lund AB [http://neotechnology.com]
4+
*
5+
* This file is part of Neo4j.
6+
*
7+
* Licensed under the Apache License, Version 2.0 (the "License");
8+
* you may not use this file except in compliance with the License.
9+
* You may obtain a copy of the License at
10+
*
11+
* http://www.apache.org/licenses/LICENSE-2.0
12+
*
13+
* Unless required by applicable law or agreed to in writing, software
14+
* distributed under the License is distributed on an "AS IS" BASIS,
15+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16+
* See the License for the specific language governing permissions and
17+
* limitations under the License.
18+
*/
19+
import RoutingTable from "../../src/v1/internal/routing-table";
20+
import RoundRobinArray from "../../src/v1/internal/round-robin-array";
21+
import {int} from "../../src/v1/integer";
22+
23+
describe('routing-table', () => {
24+
25+
it('should not be stale when has routers, readers, writers and future expiration date', () => {
26+
const table = createTable([1, 2], [3, 4], [5, 6], notExpired());
27+
expect(table.isStale()).toBeFalsy();
28+
});
29+
30+
it('should be stale when expiration date in the past', () => {
31+
const table = createTable([1, 2], [1, 2], [1, 2], expired());
32+
expect(table.isStale()).toBeTruthy();
33+
});
34+
35+
it('should be stale when has single router', () => {
36+
const table = createTable([1], [2, 3], [4, 5], notExpired());
37+
expect(table.isStale()).toBeTruthy();
38+
});
39+
40+
it('should be stale when no readers', () => {
41+
const table = createTable([1, 2], [], [3, 4], notExpired());
42+
expect(table.isStale()).toBeTruthy();
43+
});
44+
45+
it('should be stale when no writers', () => {
46+
const table = createTable([1, 2], [3, 4], [], notExpired());
47+
expect(table.isStale()).toBeTruthy();
48+
});
49+
50+
it('should not be stale with single reader', () => {
51+
const table = createTable([1, 2], [3], [4, 5], notExpired());
52+
expect(table.isStale()).toBeFalsy();
53+
});
54+
55+
it('should not be stale with single writer', () => {
56+
const table = createTable([1, 2], [3, 4], [5], notExpired());
57+
expect(table.isStale()).toBeFalsy();
58+
});
59+
60+
it('should forget reader, writer but not router', () => {
61+
const table = createTable([1, 2], [1, 2], [1, 2], notExpired());
62+
63+
table.forget(1);
64+
65+
expect(table.routers.toArray()).toEqual([1, 2]);
66+
expect(table.readers.toArray()).toEqual([2]);
67+
expect(table.writers.toArray()).toEqual([2]);
68+
});
69+
70+
it('should forget single reader', () => {
71+
const table = createTable([1, 2], [42], [1, 2, 3], notExpired());
72+
73+
table.forget(42);
74+
75+
expect(table.routers.toArray()).toEqual([1, 2]);
76+
expect(table.readers.toArray()).toEqual([]);
77+
expect(table.writers.toArray()).toEqual([1, 2, 3]);
78+
});
79+
80+
it('should forget single writer', () => {
81+
const table = createTable([1, 2], [3, 4, 5], [42], notExpired());
82+
83+
table.forget(42);
84+
85+
expect(table.routers.toArray()).toEqual([1, 2]);
86+
expect(table.readers.toArray()).toEqual([3, 4, 5]);
87+
expect(table.writers.toArray()).toEqual([]);
88+
});
89+
90+
it('should return all servers in diff when other table is empty', () => {
91+
const oldTable = createTable([1, 2], [3, 4], [5, 6], notExpired());
92+
const newTable = createTable([], [], [], notExpired());
93+
94+
const servers = oldTable.serversDiff(newTable);
95+
96+
expect(servers).toEqual([1, 2, 3, 4, 5, 6]);
97+
});
98+
99+
it('should no servers in diff when this table is empty', () => {
100+
const oldTable = createTable([], [], [], notExpired());
101+
const newTable = createTable([1, 2], [3, 4], [5, 6], notExpired());
102+
103+
const servers = oldTable.serversDiff(newTable);
104+
105+
expect(servers).toEqual([]);
106+
});
107+
108+
it('should include different routers in servers diff', () => {
109+
const oldTable = createTable([1, 7, 2, 42], [3, 4], [5, 6], notExpired());
110+
const newTable = createTable([1, 2], [3, 4], [5, 6], notExpired());
111+
112+
const servers = oldTable.serversDiff(newTable);
113+
114+
expect(servers).toEqual([7, 42]);
115+
});
116+
117+
it('should include different readers in servers diff', () => {
118+
const oldTable = createTable([1, 2], [3, 7, 4, 42], [5, 6], notExpired());
119+
const newTable = createTable([1, 2], [3, 4], [5, 6], notExpired());
120+
121+
const servers = oldTable.serversDiff(newTable);
122+
123+
expect(servers).toEqual([7, 42]);
124+
});
125+
126+
it('should include different writers in servers diff', () => {
127+
const oldTable = createTable([1, 2], [3, 4], [5, 7, 6, 42], notExpired());
128+
const newTable = createTable([1, 2], [3, 4], [5, 6], notExpired());
129+
130+
const servers = oldTable.serversDiff(newTable);
131+
132+
expect(servers).toEqual([7, 42]);
133+
});
134+
135+
it('should include different servers in diff', () => {
136+
const oldTable = createTable([1, 2, 11], [22, 3, 33, 4], [5, 44, 6], notExpired());
137+
const newTable = createTable([1], [2, 3, 4, 6], [5], notExpired());
138+
139+
const servers = oldTable.serversDiff(newTable);
140+
141+
expect(servers).toEqual([11, 22, 33, 44]);
142+
});
143+
144+
function expired() {
145+
return Date.now() - 3600; // expired an hour ago
146+
}
147+
148+
function notExpired() {
149+
return Date.now() + 3600; // will expire in an hour
150+
}
151+
152+
function createTable(routers, readers, writers, expirationTime) {
153+
const routersArray = new RoundRobinArray(routers);
154+
const readersArray = new RoundRobinArray(readers);
155+
const writersArray = new RoundRobinArray(writers);
156+
const expiration = int(expirationTime);
157+
return new RoutingTable(routersArray, readersArray, writersArray, expiration);
158+
}
159+
160+
});

0 commit comments

Comments
 (0)