farm-orchestrator
v1.1.90
Published
Hub package with PostgreSQL and Sequelize support
Readme
@device-farm/hub
Hub package with PostgreSQL database support using Sequelize ORM.
Features
- PostgreSQL database connection management
- Sequelize ORM integration
- Type-safe database configuration
- Connection pooling
- Model initialization support
Installation
Dependencies are managed at the root level. Run:
npm installUsage
Basic Setup
import { Database, initializeModels } from '@device-farm/hub';
// Create database instance
const db = new Database({
host: 'localhost',
port: 5432,
database: 'device_farm',
username: 'postgres',
password: 'password',
pool: {
max: 5,
min: 0,
acquire: 30000,
idle: 10000,
},
logging: console.log, // Optional: log SQL queries
});
// Initialize models
initializeModels(db.getSequelize());
// Connect to database
await db.connect();
// Sync models (development only - use migrations in production)
await db.sync({ alter: true });
// ... use your database connection
// Disconnect when done
await db.disconnect();Creating Models
Create models in src/models/ directory:
import { Model, DataTypes, Sequelize } from 'sequelize';
export class User extends Model {
public id!: number;
public name!: string;
public email!: string;
public readonly createdAt!: Date;
public readonly updatedAt!: Date;
}
export const initUser = (sequelize: Sequelize): typeof User => {
User.init(
{
id: {
type: DataTypes.INTEGER,
autoIncrement: true,
primaryKey: true,
},
name: {
type: DataTypes.STRING,
allowNull: false,
},
email: {
type: DataTypes.STRING,
allowNull: false,
unique: true,
},
},
{
sequelize,
modelName: 'User',
tableName: 'users',
}
);
return User;
};Then register it in src/models/index.ts:
import { initUser } from './user';
export const initializeModels = (sequelize: Sequelize): void => {
initUser(sequelize);
// Add more models here
};Configuration
Environment Variables
It's recommended to use environment variables for database configuration:
DB_HOST=localhost
DB_PORT=5432
DB_NAME=device_farm
DB_USER=postgres
DB_PASSWORD=your_password
DB_POOL_MAX=5
DB_POOL_MIN=0Then use them in your code:
const db = new Database({
host: process.env.DB_HOST || 'localhost',
port: parseInt(process.env.DB_PORT || '5432', 10),
database: process.env.DB_NAME || 'device_farm',
username: process.env.DB_USER || 'postgres',
password: process.env.DB_PASSWORD || '',
pool: {
max: parseInt(process.env.DB_POOL_MAX || '5', 10),
min: parseInt(process.env.DB_POOL_MIN || '0', 10),
},
});WebDriver Session Path Configuration
The WebDriver session endpoint path is configurable. The webdriverSessionPath specifies the base path, and the router automatically appends /session to it. For example, if webdriverSessionPath is /wd/hub, the final endpoint will be /wd/hub/session.
You can set it via:
Configuration File (
hub.config.json):{ "webdriverSessionPath": "/wd/hub" }CLI Arguments:
npm run start -- --webdriver-session-path /custom/path # or short form: npm run start -- -w /custom/pathEnvironment Variable:
WEBDRIVER_SESSION_PATH=/wd/hubCustom Config File:
npm run start -- --config /path/to/hub.config.json # or short form: npm run start -- -c /path/to/hub.config.json
Priority Order: Environment variables > CLI arguments > Config file > Default (/wd/hub)
Note: The router automatically appends /session to the configured path, so the default endpoint will be available at POST /wd/hub/session.
Prepare WDA Command (iOS)
The hub includes a command to build and sign WebDriverAgent (WDA) for iOS real device testing. This command:
- Finds or builds WebDriverAgent from Appium
- Signs it with a mobile provisioning profile
- Creates a signed IPA file ready for deployment
Usage:
# Interactive mode (will prompt for provisioning profile)
npm run start -- prepare-wda
# With specific provisioning file
npm run start -- prepare-wda --mobile-provisioning-file /path/to/profile.mobileprovision
# With custom WDA project path
npm run start -- prepare-wda --wda-project-path /path/to/WebDriverAgent
# Specify platform (ios, tvos, or both)
npm run start -- prepare-wda --platform bothOptions:
| Option | Short | Description |
| ---------------------------- | ----- | -------------------------------------------------------- |
| --mobile-provisioning-file | -m | Path to the mobile provisioning file for signing |
| --wda-project-path | -p | Path to WebDriverAgent Xcode project |
| --platform | | Platform type: ios, tvos, or both (default: ios) |
Requirements:
- macOS with Xcode installed
- Valid Apple Developer provisioning profile
- Appium with XCUITest driver installed (for automatic WDA discovery)
Node Monitor Configuration
The hub includes a node monitor service that tracks node liveness by checking for heartbeat signals. This service acts as a backup mechanism to mark nodes as offline when WebSocket disconnect events don't fire properly.
Configuration Options
You can configure the node monitor using either a configuration file or environment variables:
1. Configuration File (hub.config.json):
{
"nodeMonitor": {
"checkInterval": 60000,
"timeoutMs": 120000
},
"websocket": {
"heartbeatInterval": 30000
}
}2. Environment Variables:
# Node monitor check interval (default: 60000ms = 60 seconds)
NODE_MONITOR_INTERVAL=60000
# Timeout before marking a node as offline (default: 120000ms = 2 minutes)
NODE_TIMEOUT_MS=120000
# WebSocket heartbeat interval (default: 30000ms = 30 seconds)
WEBSOCKET_HEARTBEAT_INTERVAL=30000Priority Order: Environment variables > Config file > Default values
Configuration Parameters
checkInterval(default: 60000ms = 60 seconds)- How often the monitor checks for offline nodes (in milliseconds)
- Lower values provide faster detection but increase database load
- Recommended: 10-60 seconds
timeoutMs(default: 120000ms = 2 minutes)- How long a node can go without sending a heartbeat before being marked offline (in milliseconds)
- Important: This must be greater than
heartbeatIntervalto avoid false positives - Recommended: At least 2x the
heartbeatIntervalvalue (e.g., if heartbeat is 30s, use 60s+ for timeout)
heartbeatInterval(default: 30000ms = 30 seconds)- How often the hub updates the
last_heartbeat_attimestamp in the database (in milliseconds) - This is the interval at which the hub-side heartbeat mechanism runs
- Lower values provide more frequent updates but increase database writes
- Recommended: 20-60 seconds
- How often the hub updates the
How It Works
Heartbeat Updates: When a node connects via WebSocket, the hub sets up a periodic heartbeat that updates the
last_heartbeat_atfield in the database everyheartbeatIntervalmilliseconds.Offline Detection: The node monitor service runs every
checkIntervalmilliseconds and checks for nodes that:- Are marked as
is_online: truein the database - Have a
last_heartbeat_attimestamp older thantimeoutMs(or null)
- Are marked as
Automatic Cleanup: When a stale node is detected, the service:
- Marks the node as
is_online: false - Marks all devices for that node as not busy and inactive
- Marks the node as
Important Notes
⚠️ Configuration Warning: If timeoutMs is set to a value less than heartbeatInterval, nodes will be incorrectly marked as offline before the next heartbeat arrives. The server will log a warning if this condition is detected.
Example Configuration:
{
"nodeMonitor": {
"checkInterval": 10000, // Check every 10 seconds
"timeoutMs": 60000 // Mark offline after 60 seconds of no heartbeat
},
"websocket": {
"heartbeatInterval": 30000 // Update heartbeat every 30 seconds
}
}This configuration ensures:
- Heartbeats are updated every 30 seconds
- The monitor checks for stale nodes every 10 seconds
- Nodes are marked offline after 60 seconds (2x the heartbeat interval), providing a safety buffer
Development
# Build the package
npm run build
# Watch mode
npm run build:watch
# Type check
npm run typecheck
# Lint
npm run lintDependencies
- sequelize: ORM for Node.js
- pg: PostgreSQL client for Node.js
- pg-hstore: Serialization/deserialization of hstore data type
