Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(ur): enable config to route From-Module #1120

Merged
merged 1 commit into from
Jan 20, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 4 additions & 1 deletion servers/ur/src/config.js
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ const serverConfigSchema = z.object({
}, z.number().positive()),
processToHost: stringifiedJsonSchema.nullish(),
ownerToHost: stringifiedJsonSchema.nullish(),
fromModuleToHost: stringifiedJsonSchema.nullish(),
hosts: z.preprocess(
(arg) => (typeof arg === 'string' ? arg.split(',').map(str => str.trim()) : arg),
z.array(z.string().url())
Expand Down Expand Up @@ -68,6 +69,7 @@ const CONFIG_ENVS = {
hosts: process.env.HOSTS || ['http://127.0.0.1:3005'],
processToHost: process.env.PROCESS_TO_HOST || JSON.stringify({}),
ownerToHost: process.env.OWNER_TO_HOST || JSON.stringify({}),
fromModuleToHost: process.env.FROM_MODULE_TO_HOST || JSON.stringify({}),

/**
* default to the CU for no hassle startup in development mode,
Expand Down Expand Up @@ -95,7 +97,8 @@ const CONFIG_ENVS = {
hosts: process.env.HOSTS,
processToHost: process.env.PROCESS_TO_HOST || JSON.stringify({}),
ownerToHost: process.env.OWNER_TO_HOST || JSON.stringify({}),

fromModuleToHost: process.env.FROM_MODULE_TO_HOST || JSON.stringify({}),

aoUnit: process.env.AO_UNIT,
strategy: process.env.STRATEGY || 'proxy',

Expand Down
43 changes: 41 additions & 2 deletions servers/ur/src/domain.js
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
import { defaultTo, isEmpty, complement, path } from 'ramda'
import { defaultTo, isEmpty, complement, path, propEq } from 'ramda'
import { LRUCache } from 'lru-cache'

const isNotEmpty = complement(isEmpty)

export function bailoutWith ({ fetch, subrouterUrl, surUrl, owners, processToHost, ownerToHost }) {
export function bailoutWith ({ fetch, subrouterUrl, surUrl, owners, processToHost, ownerToHost, fromModuleToHost }) {
const processToOwnerCache = new LRUCache({
/**
* 10MB
Expand All @@ -15,6 +15,17 @@ export function bailoutWith ({ fetch, subrouterUrl, surUrl, owners, processToHos
sizeCalculation: () => 8
})

const fromModuleCache = new LRUCache({
/**
* 10MB
*/
maxSize: 10_000_000,
/**
* A number is 8 bytes
*/
sizeCalculation: () => 8
})

async function findProcessOwner (processId) {
const owner = processToOwnerCache.get(processId)
if (owner) return owner
Expand All @@ -32,6 +43,25 @@ export function bailoutWith ({ fetch, subrouterUrl, surUrl, owners, processToHos
.catch((_e) => null)
}

async function findFromModule (processId) {
const module = fromModuleCache.get(processId)
if (module) return module

return fetch(`${surUrl}/processes/${processId}`)
.then((res) => res.json())
.then(defaultTo({}))
.then(p => {
return p.tags.find(propEq('From-Module', 'name'))?.value
})
.then((module) => {
if (!module) return null

fromModuleCache.set(processId, module)
return module
})
.catch((_e) => null)
}

return async (processId) => {
/**
* If a process has a specific mapping configured,
Expand All @@ -47,6 +77,15 @@ export function bailoutWith ({ fetch, subrouterUrl, surUrl, owners, processToHos
if (ownerToHost[owner]) return ownerToHost[owner]
}

/**
* If there are fromModule -> host configured, then we lookup the
* from-module and return the specific host if found
*/
if (fromModuleToHost && isNotEmpty(fromModuleToHost)) {
const module = await findFromModule(processId)
if (fromModuleToHost[module]) return fromModuleToHost[module]
}

/**
* @deprecated - this functionality is subsumed by ownerToHost
* and will eventually be removed
Expand Down
68 changes: 68 additions & 0 deletions servers/ur/src/domain.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,74 @@ describe('domain', () => {
})
})

describe('fromModuleToHost', () => {
test('should bailout if the process fromModule is mapped to a specific host', async () => {
const fetchMock = async (url) => {
assert.equal(url, 'surUrl1/processes/process-123')
return new Response(JSON.stringify({ owner: { address: 'owner2' }, tags: [{name:'From-Module', value: 'fromModule1'}] }))
}

const bailout = bailoutWith({
fetch: fetchMock,
surUrl: 'surUrl1',
fromModuleToHost: { fromModule1: 'https://specific_owner.host' }
})

const determineHost = determineHostWith({ hosts: HOSTS, bailout })

const host = await determineHost({ processId: 'process-123', failoverAttempt: 0 })
assert.equal(host, 'https://specific_owner.host')
})

test('should NOT bailout if the process owner is not mapped to a specific host', async () => {
const fetchMock = async (url) => {
assert.equal(url, 'surUrl1/processes/process-123')
return new Response(JSON.stringify({ owner: { address: 'owner2' }, tags: [{name:'From-Module', value: 'fromModule1'}] }))
}

const bailout = bailoutWith({
fetch: fetchMock,
surUrl: 'surUrl1',
fromModuleToHost: { notFromModule1: 'https://specific_owner.host' }
})

const determineHost = determineHostWith({ hosts: HOSTS, bailout })

const host = await determineHost({ processId: 'process-123', failoverAttempt: 0 })
assert.ok(host !== 'https://specific_owner.host')
assert.ok(HOSTS.includes(host))
})

test('should NOT bailout if no fromModuleToHost is provided', async () => {
const fetchMock = async (url) => {
assert.equal(url, 'surUrl1/processes/process-123')
return new Response(JSON.stringify({ owner: { address: 'owner2' }, tags: [{name:'From-Module', value: 'fromModule1'}] }))
}

const determineHostEmptyMapping = determineHostWith({
hosts: HOSTS,
bailout: bailoutWith({
fetch: fetchMock,
surUrl: 'surUrl1',
fromModuleToHost: {}
})
})
const host = await determineHostEmptyMapping({ processId: 'process-123', failoverAttempt: 0 })
assert.ok(HOSTS.includes(host))

const determineHostNoMapping = determineHostWith({
hosts: HOSTS,
bailout: bailoutWith({
fetch: fetchMock,
surUrl: 'surUrl1',
ownerToHost: undefined
})
})
const host1 = await determineHostNoMapping({ processId: 'process-123', failoverAttempt: 0 })
assert.ok(HOSTS.includes(host1))
})
})

describe('ownerToHost', () => {
test('should bailout if the process owner is mapped to a specific host', async () => {
const fetchMock = async (url) => {
Expand Down
Loading