Skip to content

Commit

Permalink
Create server profiling test (#478)
Browse files Browse the repository at this point in the history
* Create server profiling test (OG-196)

The Relay Server currently makes too many RPC calls while operating.
This commmit does not change that but creates a test to observe  the
number of RPC calls per server method.

Also, some cleanup.
  • Loading branch information
forshtat authored Aug 28, 2020
1 parent 8d0b966 commit 6b6f569
Show file tree
Hide file tree
Showing 6 changed files with 253 additions and 54 deletions.
70 changes: 70 additions & 0 deletions src/common/dev/ProfilingProvider.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
import { HttpProvider } from 'web3-core'
import { JsonRpcPayload, JsonRpcResponse } from 'web3-core-helpers'

type SendCallback = (error: (Error | null), result?: JsonRpcResponse) => void

export class ProfilingProvider implements HttpProvider {
private readonly methodsCount = new Map<string, number>()
private _requestsCount = 0

provider: HttpProvider
logTraffic: boolean

get requestsCount (): number {
return this._requestsCount
}

get connected (): boolean {
return this.provider.connected
}

get host (): string {
return this.provider.host
}

constructor (provider: HttpProvider, logTraffic: boolean = false) {
this.provider = provider
this.logTraffic = logTraffic
}

disconnect (): boolean {
return false
}

supportsSubscriptions (): boolean {
return false
}

send (payload: JsonRpcPayload, callback: SendCallback): void {
this._requestsCount++
const currentCount = this.methodsCount.get(payload.method) ?? 0
this.methodsCount.set(payload.method, currentCount + 1)
let wrappedCallback: SendCallback = callback
if (this.logTraffic) {
wrappedCallback = function (error: (Error | null), result?: JsonRpcResponse): void {
if (error != null) {
console.log(`<<< error: ${error.message ?? 'null error message'}`)
}
console.log(`<<< result: ${JSON.stringify(result) ?? 'null result'}`)
callback(error, result)
}
console.log(`>>> payload: ${JSON.stringify(payload) ?? 'null result'}`)
}
this.provider.send(payload, wrappedCallback)
}

reset (): void {
this._requestsCount = 0
this.methodsCount.clear()
}

log (): void {
console.log('Profiling Provider Stats:')
new Map([...this.methodsCount.entries()].sort(function ([, count1], [, count2]) {
return count2 - count1
})).forEach(function (value, key, map) {
console.log(`Method: ${key.padEnd(30)} was called: ${value.toString().padEnd(3)} times`)
})
console.log(`Total RPC calls: ${this.requestsCount}`)
}
}
27 changes: 12 additions & 15 deletions test/relayserver/RegistrationManager.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,10 +16,10 @@ import { constants } from '../../src/common/Constants'
import {
assertRelayAdded,
bringUpNewRelay,
clearStorage, getTimestampTempWorkdir,
clearStorage,
getTotalTxCosts,
NewRelayParams,
ServerTestConstants
getTemporaryWorkdirs, ServerTestConstants, LocalhostOne
} from './ServerTestUtils'
import { ServerConfigParams, ServerDependencies } from '../../src/relayserver/ServerConfigParams'

Expand All @@ -29,11 +29,7 @@ const { oneEther, weekInSec } = constants
const StakeManager = artifacts.require('StakeManager')
const Penalizer = artifacts.require('Penalizer')

// TODO: remove this
const workerIndex = 0
const workdir = '/tmp/gsn/test/relayserver/' + getTimestampTempWorkdir()
const managerWorkdir = workdir + '/manager'
const workersWorkdir = workdir + '/workers'

contract('RegistrationManager', function (accounts) {
const relayOwner = accounts[1]
Expand All @@ -44,6 +40,7 @@ contract('RegistrationManager', function (accounts) {
let _web3: Web3
let id: string
let newRelayParams: NewRelayParams
let serverTestConstants: ServerTestConstants
let partialConfig: Partial<GSNConfig>

// TODO: move to the 'before'
Expand All @@ -55,22 +52,22 @@ contract('RegistrationManager', function (accounts) {
stakeManager = await StakeManager.new()
const penalizer = await Penalizer.new()
rhub = await deployHub(stakeManager.address, penalizer.address)
serverTestConstants = getTemporaryWorkdirs()
partialConfig = {
relayHubAddress: rhub.address,
stakeManagerAddress: stakeManager.address
}
newRelayParams = {
alertedBlockDelay: 0,
workdir,
ethereumNodeUrl,
relayHubAddress: rhub.address,
relayOwner,
url: ServerTestConstants.localhostOne,
url: LocalhostOne,
web3,
stakeManager
}
const managerKeyManager = new KeyManager(1, managerWorkdir)
const workersKeyManager = new KeyManager(1, workersWorkdir)
const managerKeyManager = new KeyManager(1, serverTestConstants.managerWorkdir)
const workersKeyManager = new KeyManager(1, serverTestConstants.workersWorkdir)
const txStoreManager = new TxStoreManager({ inMemory: true })
const serverWeb3provider = new Web3.providers.HttpProvider(ethereumNodeUrl)
const contractInteractor = new ContractInteractor(serverWeb3provider,
Expand All @@ -87,7 +84,7 @@ contract('RegistrationManager', function (accounts) {
}
const params: Partial<ServerConfigParams> = {
relayHubAddress: rhub.address,
url: ServerTestConstants.localhostOne,
url: LocalhostOne,
baseRelayFee: '0',
pctRelayFee: 0,
gasPriceFactor: 1,
Expand Down Expand Up @@ -148,9 +145,9 @@ contract('RegistrationManager', function (accounts) {
})

it('should start again after restarting process', async () => {
const managerKeyManager = new KeyManager(1, managerWorkdir)
const workersKeyManager = new KeyManager(1, workersWorkdir)
const txStoreManager = new TxStoreManager({ workdir })
const managerKeyManager = new KeyManager(1, serverTestConstants.managerWorkdir)
const workersKeyManager = new KeyManager(1, serverTestConstants.workersWorkdir)
const txStoreManager = new TxStoreManager({ workdir: serverTestConstants.workdir })
const serverWeb3provider = new Web3.providers.HttpProvider(ethereumNodeUrl)
const contractInteractor = new ContractInteractor(serverWeb3provider,
configureGSN({
Expand All @@ -166,7 +163,7 @@ contract('RegistrationManager', function (accounts) {
}
const params: Partial<ServerConfigParams> = {
relayHubAddress: rhub.address,
url: ServerTestConstants.localhostOne,
url: LocalhostOne,
baseRelayFee: '0',
pctRelayFee: 0,
gasPriceFactor: 1,
Expand Down
20 changes: 9 additions & 11 deletions test/relayserver/RelayServer.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -31,13 +31,12 @@ import {
NewRelayParams,
PrepareRelayRequestOption,
RelayTransactionParams,
ServerTestConstants,
bringUpNewRelay,
clearStorage,
getTotalTxCosts,
prepareRelayRequest,
relayTransaction,
relayTransactionFromRequest, getTimestampTempWorkdir, assertRelayAdded
relayTransactionFromRequest, LocalhostOne, getTemporaryWorkdirs, assertRelayAdded
} from './ServerTestUtils'
import { RelayClient } from '../../src/relayclient/RelayClient'
import { SendTransactionDetails, SignedTransactionDetails } from '../../src/relayserver/TransactionManager'
Expand Down Expand Up @@ -111,19 +110,18 @@ contract('RelayServer', function (accounts) {

newRelayParams = {
alertedBlockDelay: 0,
workdir: ServerTestConstants.workdir,
ethereumNodeUrl,
relayHubAddress: rhub.address,
relayOwner,
url: ServerTestConstants.localhostOne,
url: LocalhostOne,
web3,
stakeManager
}
partialConfig = {
relayHubAddress: rhub.address,
stakeManagerAddress: stakeManager.address
}
relayServer = await bringUpNewRelay(newRelayParams, partialConfig, {
relayServer = await bringUpNewRelay(newRelayParams, partialConfig, {}, {
trustedPaymasters: [paymaster.address],
baseRelayFee
})
Expand All @@ -140,7 +138,7 @@ contract('RelayServer', function (accounts) {

const encodedFunction = sr.contract.methods.emitMessage('hello world').encodeABI()
const relayClientConfig = {
preferredRelays: [ServerTestConstants.localhostOne],
preferredRelays: [LocalhostOne],
maxRelayNonceGap: 0,
verbose: process.env.DEBUG != null
}
Expand Down Expand Up @@ -187,8 +185,8 @@ contract('RelayServer', function (accounts) {
it('should initialize relay params (chainId, networkId, gasPrice)', async function () {
const managerKeyManager = new KeyManager(1, undefined, crypto.randomBytes(32).toString())
const workersKeyManager = new KeyManager(1, undefined, crypto.randomBytes(32).toString())
const txStoreManager = new TxStoreManager({ workdir: newRelayParams.workdir + getTimestampTempWorkdir() })
const serverWeb3provider = new Web3.providers.HttpProvider(newRelayParams.ethereumNodeUrl)
const txStoreManager = new TxStoreManager({ workdir: getTemporaryWorkdirs().workdir })
const serverWeb3provider = new Web3.providers.HttpProvider(newRelayParams.ethereumNodeUrl!)
const contractInteractor = new ContractInteractor(serverWeb3provider, configureGSN(partialConfig))
await contractInteractor.init()
const serverDependencies = {
Expand Down Expand Up @@ -532,7 +530,7 @@ contract('RelayServer', function (accounts) {
let relayServer: RelayServer

before(async function () {
relayServer = await bringUpNewRelay(newRelayParams, partialConfig, { registrationBlockRate })
relayServer = await bringUpNewRelay(newRelayParams, partialConfig, {}, { registrationBlockRate })
const latestBlock = await _web3.eth.getBlock('latest')
const receipts = await relayServer._worker(latestBlock.number)
assertRelayAdded(receipts, relayServer) // sanity check
Expand Down Expand Up @@ -619,7 +617,7 @@ contract('RelayServer', function (accounts) {
...newRelayParams,
alertedBlockDelay: 100
}
newServer = await bringUpNewRelay(newRelayParamsAlerted, partialConfig, { alertedBlockDelay: 100 })
newServer = await bringUpNewRelay(newRelayParamsAlerted, partialConfig, {}, { alertedBlockDelay: 100 })
const latestBlock = await _web3.eth.getBlock('latest')
await newServer._worker(latestBlock.number)
rejectingPaymaster = await TestPaymasterConfigurableMisbehavior.new()
Expand Down Expand Up @@ -689,7 +687,7 @@ contract('RelayServer', function (accounts) {
await rejectingPaymaster.setRelayHub(rhub.address)
await rejectingPaymaster.deposit({ value: _web3.utils.toWei('1', 'ether') })
await rejectingPaymaster.setGreedyAcceptanceBudget(true)
newServer = await bringUpNewRelay(newRelayParams, partialConfig, { trustedPaymasters: [rejectingPaymaster.address] })
newServer = await bringUpNewRelay(newRelayParams, partialConfig, {}, { trustedPaymasters: [rejectingPaymaster.address] })
const latestBlock = await _web3.eth.getBlock('latest')
await newServer._worker(latestBlock.number)
relayTransactionParams2 = {
Expand Down
126 changes: 126 additions & 0 deletions test/relayserver/RelayServerRequestsProfiling.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,126 @@
import { RelayServer } from '../../src/relayserver/RelayServer'
import {
bringUpNewRelay,
LocalhostOne, NewRelayParams,
PrepareRelayRequestOption,
relayTransaction,
RelayTransactionParams
} from './ServerTestUtils'
import { deployHub } from '../TestUtils'
import { configureGSN, GSNConfig } from '../../src/relayclient/GSNConfigurator'
import { ServerDependencies } from '../../src/relayserver/ServerConfigParams'
import ContractInteractor from '../../src/relayclient/ContractInteractor'
import { HttpProvider } from 'web3-core'
import { ProfilingProvider } from '../../src/common/dev/ProfilingProvider'
import { Address } from '../../src/relayclient/types/Aliases'
import { RelayClient } from '../../src/relayclient/RelayClient'
import { GsnRequestType } from '../../src/common/EIP712/TypedRequestData'

const TestPaymasterEverythingAccepted = artifacts.require('TestPaymasterEverythingAccepted')
const TestRecipient = artifacts.require('TestRecipient')
const StakeManager = artifacts.require('StakeManager')
const Penalizer = artifacts.require('Penalizer')
const Forwarder = artifacts.require('Forwarder')

contract('RelayServerRequestsProfiling', function ([relayOwner]) {
const callsPerWorker = 63
const callsPerTransaction = 25

let provider: ProfilingProvider
let relayServer: RelayServer
let relayHubAddress: Address

before(async function () {
const stakeManager = await StakeManager.new()
const penalizer = await Penalizer.new()
const rhub = await deployHub(stakeManager.address, penalizer.address)
relayHubAddress = rhub.address
provider = new ProfilingProvider(web3.currentProvider as HttpProvider)
const newRelayParams: NewRelayParams = {
alertedBlockDelay: 0,
relayHubAddress,
relayOwner,
url: LocalhostOne,
web3,
stakeManager
}
const partialConfig: Partial<GSNConfig> = {
relayHubAddress: rhub.address,
stakeManagerAddress: stakeManager.address
}
const contractInteractor = new ContractInteractor(provider, configureGSN(partialConfig))
await contractInteractor.init()
const partialDependencies: Partial<ServerDependencies> = {
contractInteractor
}
relayServer = await bringUpNewRelay(newRelayParams, partialConfig, partialDependencies)
})

beforeEach(function () {
provider.reset()
})

it('should make X requests per block callback', async function () {
const latestBlock = await web3.eth.getBlock('latest')
await relayServer._worker(latestBlock.number)
provider.log()
assert.isAtMost(provider.requestsCount, callsPerWorker)
})

describe('relay transaction', function () {
let gasLess: Address
let relayTransactionParams: RelayTransactionParams
let options: PrepareRelayRequestOption

// TODO: this is a pure copy-paste from Transaction manager test. Create helper code for this!
before(async function () {
gasLess = await web3.eth.personal.newAccount('password')
const forwarder = await Forwarder.new()
// register hub's RelayRequest with forwarder, if not already done.
await forwarder.registerRequestType(
GsnRequestType.typeName,
GsnRequestType.typeSuffix
)

const forwarderAddress = forwarder.address

const paymaster = await TestPaymasterEverythingAccepted.new({ gas: 1e7 })

const paymasterAddress = paymaster.address

await paymaster.setRelayHub(relayHubAddress)
await paymaster.setTrustedForwarder(forwarderAddress)
await paymaster.deposit({ value: web3.utils.toWei('1', 'ether') })

const sr = await TestRecipient.new(forwarderAddress)
const encodedFunction = sr.contract.methods.emitMessage('hello world').encodeABI()
const recipientAddress = sr.address
const relayClient = new RelayClient((web3.currentProvider as HttpProvider), configureGSN({}))
relayTransactionParams = {
gasLess,
recipientAddress,
relayHubAddress,
encodedFunction,
paymasterData: '',
clientId: '',
forwarderAddress,
paymasterAddress,
relayServer,
web3,
relayClient
}
options = {
from: gasLess,
to: sr.address,
pctRelayFee: 0,
baseRelayFee: '0',
paymaster: paymaster.address
}
})

it('should make X requests per relay transaction request', async function () {
await relayTransaction(relayTransactionParams, options)
assert.isAtMost(provider.requestsCount, callsPerTransaction)
})
})
})
Loading

0 comments on commit 6b6f569

Please sign in to comment.