Skip to content

Commit

Permalink
feat(cluster): adds option for disabling auto discovery
Browse files Browse the repository at this point in the history
  • Loading branch information
mKeRix committed Feb 14, 2020
1 parent 2173ca5 commit 6777e10
Show file tree
Hide file tree
Showing 4 changed files with 53 additions and 15 deletions.
28 changes: 20 additions & 8 deletions docs/guide/cluster.md
Expand Up @@ -4,19 +4,31 @@

If you are running multiple room-assistant instances they come together in a cluster. By default, the instances discover each other using mDNS. This requires all of them live in the same subnet. You can also specify the adresses of other instances and tweak some other things manually.

## Preferred leaders
## Usage tips

### Preferred leaders

In some situations you may want some instances of room-assistant to be the leading ones more than others. This could for example be the case if you have instances that are connected to the network better than others. To accomplish this you can configure custom weights that are then used during the leader election process. The `weight` option should ideally be configured on every instance of room-assistant that you run to achieve consistent behavior. The preferred leader instances need to have the largest weight numbers.

### Some nodes not appearing

If the auto discovery does not seem to pick up all (or any) nodes for your cluster you can approach the problem from two different directions:

1. Make sure your router and network settings are compatible with mDNS/Bonjour. An easy way to test this is pinging your devices by their hostname, e.g. `ping raspberrypi.local`.
2. Disable `autoDiscovery` and provide the `peerAddresses` manually. Make sure to give your devices DHCP reservations or static IPs.

Should the other nodes not appear in your cluster even after configuring it manually you should make sure that the devices can communication with each other via UDP on the port you configured, by default `6425`.

## Settings

| Name | Type | Default | Description |
| ------------------ | ------ | ------- | ------------------------------------------------------------ |
| `networkInterface` | String | | The specific network interface that room-assistant should advertise its presence on, e.g. `eth0`. |
| `port` | Number | `6425` | The UDP port that room-assistant should use for internal communication. |
| `timeout` | Number | `60` | Number of seconds that an instance can go without sending a heartbeat and not be dropped from the cluster. |
| `weight` | Number | Random | Value used to sort when electing a leading instance. The instance with the highest weight in the cluster becomes the leader. |
| `peerAddresses` | Array | | A list of endpoint addresses (IP + port) of other room-assistant instances. |
| Name | Type | Default | Description |
| ------------------ | ------- | ------- | ------------------------------------------------------------ |
| `networkInterface` | String | | The specific network interface that room-assistant should advertise its presence on, e.g. `eth0`. |
| `port` | Number | `6425` | The UDP port that room-assistant should use for internal communication. |
| `timeout` | Number | `60` | Number of seconds that an instance can go without sending a heartbeat and not be dropped from the cluster. |
| `weight` | Number | Random | Value used to sort when electing a leading instance. The instance with the highest weight in the cluster becomes the leader. |
| `autoDiscovery` | Boolean | `true` | Allows mDNS-based auto discovery of other room-assistant instances to be turned off. |
| `peerAddresses` | Array | | A list of endpoint addresses (IP + port) of other room-assistant instances. |

::: details Example Config

Expand Down
1 change: 1 addition & 0 deletions src/cluster/cluster.config.ts
Expand Up @@ -3,5 +3,6 @@ export class ClusterConfig {
port = 6425;
timeout = 60;
weight?: number;
autoDiscovery = true;
peerAddresses: string[] = [];
}
24 changes: 23 additions & 1 deletion src/cluster/cluster.service.spec.ts
Expand Up @@ -26,13 +26,22 @@ import Democracy from 'democracy';
import { Test, TestingModule } from '@nestjs/testing';
import { ClusterService } from './cluster.service';
import { ConfigModule } from '../config/config.module';
import { ClusterConfig } from './cluster.config';
import { ConfigService } from '../config/config.service';
import c from 'config';

jest.mock('os');
jest.mock('democracy');
jest.mock('mdns', () => mockMdns, { virtual: true });

describe('ClusterService', () => {
let service: ClusterService;
const mockConfig = new ClusterConfig();
const configService = {
get: jest.fn().mockImplementation((key: string) => {
return key === 'cluster' ? mockConfig : c.get(key);
})
};

beforeAll(async () => {
(networkInterfaces as jest.Mock).mockReturnValue({
Expand All @@ -59,10 +68,15 @@ describe('ClusterService', () => {
});

beforeEach(async () => {
jest.clearAllMocks();

const module: TestingModule = await Test.createTestingModule({
imports: [ConfigModule],
providers: [ClusterService]
}).compile();
})
.overrideProvider(ConfigService)
.useValue(configService)
.compile();

service = module.get<ClusterService>(ClusterService);
});
Expand Down Expand Up @@ -91,4 +105,12 @@ describe('ClusterService', () => {
expect(mockBrowser.start).toHaveBeenCalled();
expect(mockAdvertisement.start).toHaveBeenCalled();
});

it('should not advertise the instance if auto discovery has been turned off', () => {
mockConfig.autoDiscovery = false;

service.onApplicationBootstrap();
expect(mockMdns.createAdvertisement).not.toHaveBeenCalled();
expect(mockMdns.createBrowser).not.toHaveBeenCalled();
});
});
15 changes: 9 additions & 6 deletions src/cluster/cluster.service.ts
Expand Up @@ -55,12 +55,14 @@ export class ClusterService extends Democracy
}

onApplicationBootstrap(): any {
if (mdns !== undefined) {
this.startBonjourDiscovery();
} else {
this.logger.warn(
'Dependency "mdns" was not found, automatic discovery has been disabled. You will have to provide the addresses of other room-assistant nodes manually in the config.'
);
if (this.config.autoDiscovery) {
if (mdns !== undefined) {
this.startBonjourDiscovery();
} else {
this.logger.warn(
'Dependency "mdns" was not found, automatic discovery has been disabled. You will have to provide the addresses of other room-assistant nodes manually in the config.'
);
}
}

this.on('added', this.handleNodeAdded);
Expand Down Expand Up @@ -96,6 +98,7 @@ export class ClusterService extends Democracy
this.logger.error(e.message, e.trace);
});

this.logger.log('Starting mDNS advertisements and discovery');
this.advertisement.start();
this.browser.start();
}
Expand Down

0 comments on commit 6777e10

Please sign in to comment.