Skip to content

Commit

Permalink
fix(render): reduce memory and handle server file (#10055)
Browse files Browse the repository at this point in the history
Made some improvements to the render deploy command while debugging an
issue with realtime on their platform (see
https://community.redwoodjs.com/t/redwood-realtime-on-render-com/5796
for the original issue). Note that while this PR fixes the original
issue (subscriptions is not an allowed operation), realtime itself still
doesn't work as expected. Currently trying to isolate render if it is
specific to them. But these are valid fixes in the meantime:

- should improve the memory usage problem by 1) requiring
cli-data-migrate to be a devDependency prior to deploy and 2) not
importing from the cli's lib which brings in the whole world 3) not
using execa's shell option which isn't recommended
- render will now use the server file if it's there
  • Loading branch information
jtoar committed Feb 24, 2024
1 parent 1357e73 commit fab9f15
Show file tree
Hide file tree
Showing 3 changed files with 77 additions and 35 deletions.
16 changes: 16 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,22 @@

## Unreleased

- fix(render): reduce memory and handle server file

This PR improves Render deploys by reducing memory consumption and fixing it so that it uses the server file if it's present.

Render deploys seems to consistently run out of memory during the data migration step. This step is configurable and its doubtful that every deploy has data migrations to apply, but it's enabled by default so it runs every time. The main issue is that the data migrate functionality is a plugin so a yarn install kicks off in Render's deploy container which must be more memory-constrained than the build container. (Assuming there are two different containers, which seems to be the case.)

Instead of running data migrations, this PR issues a warning that if you want to run data migrations, you need to first add the `@redwoodjs/cli-data-migrate` package as a devDependency:

```
yarn add -D @redwoodjs/cli-data-migrate
```

That way a `yarn install` won't be necessary to run data migrations.

Although this PR fixes Render deploy so that it uses the server file if present, realtime features still don't seem to work. We're still investigating; in the meantime, consider using another provider like Coherence if you're just getting started and want to try out realtime features.

- Update MetaTags to be Metadata in Docs (#10053)

The tutorial still used the `MetaTags` component instead of the newer `Metadata` component that the generator templates use. This PR updates all instances of `MetaTags` with `Metadata`.
Expand Down
2 changes: 1 addition & 1 deletion packages/api-server/src/apiCLIConfigHandler.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import createFastifyInstance from './fastify'
import { redwoodFastifyAPI } from './plugins/api'
import type { APIParsedOptions } from './types'

export async function handler(options: APIParsedOptions) {
export async function handler(options: APIParsedOptions = {}) {
const timeStart = Date.now()
console.log(chalk.dim.italic('Starting API Server...'))

Expand Down
94 changes: 60 additions & 34 deletions packages/cli/src/commands/deploy/render.js
Original file line number Diff line number Diff line change
@@ -1,32 +1,38 @@
import path from 'path'

import execa from 'execa'
import fs from 'fs-extra'
import terminalLink from 'terminal-link'

import { handler as apiServerHandler } from '@redwoodjs/api-server/dist/apiCLIConfigHandler'
import { recordTelemetryAttributes } from '@redwoodjs/cli-helpers'
import { getConfig } from '@redwoodjs/project-config'
import { getPaths } from '@redwoodjs/project-config'

import { getPaths } from '../../lib'
// It's easy for the api side to exceed Render's free-plan limit.
// Because telemetryMiddleware is added to Yargs as middleware,
// we need to set the env var here outside the handler to correctly disable it.
if (process.argv.slice(2).includes('api')) {
process.env.REDWOOD_DISABLE_TELEMETRY = 1
}

export const command = 'render <side>'
export const description = 'Build, Migrate, and Serve command for Render deploy'
export const description = 'Build, migrate, and serve command for Render deploy'

export const builder = (yargs) => {
yargs
.positional('side', {
choices: ['api', 'web'],
description: 'select side to build',
description: 'Side to deploy',
type: 'string',
})
.option('prisma', {
description: 'Apply database migrations',
type: 'boolean',
default: 'true',
default: true,
})
.option('data-migrate', {
description: 'Migrate the data in your database',
description: 'Apply data migrations',
type: 'boolean',
default: 'true',
default: true,
alias: 'dm',
})
.epilogue(
Expand All @@ -37,14 +43,7 @@ export const builder = (yargs) => {
)
}

// Telemetry mem usage exceeds Render free plan limit for API service
// Because telemetryMiddleware is added to Yargs as middleware,
// we need to set env outside handler to correctly disable Telemetry
if (process.argv.slice(2).includes('api')) {
process.env.REDWOOD_DISABLE_TELEMETRY = 1
}

export const handler = async ({ side, prisma, dm: dataMigrate }) => {
export const handler = async ({ side, prisma, dataMigrate }) => {
recordTelemetryAttributes({
command: 'deploy render',
side,
Expand All @@ -55,39 +54,66 @@ export const handler = async ({ side, prisma, dm: dataMigrate }) => {
const rwjsPaths = getPaths()

const execaConfig = {
cwd: rwjsPaths.base,
shell: true,
stdio: 'inherit',
cwd: rwjsPaths.base,
extendEnv: true,
cleanup: true,
}

async function runApiCommands() {
prisma &&
execa.sync(
path.join(rwjsPaths.base, 'node_modules/.bin/prisma'),
['migrate', 'deploy', '--schema', `"${rwjsPaths.api.dbSchema}"`],
if (prisma) {
console.log('Running database migrations...')
execa.commandSync(
`node_modules/.bin/prisma migrate deploy --schema "${rwjsPaths.api.dbSchema}"`,
execaConfig
)
dataMigrate && execa.sync('yarn rw dataMigrate up', execaConfig)
await apiServerHandler({
port: getConfig().api?.port || 8911,
apiRootPath: '/',
})
}

if (dataMigrate) {
console.log('Running data migrations...')
const packageJson = fs.readJsonSync(
path.join(rwjsPaths.base, 'package.json')
)
const hasDataMigratePackage =
!!packageJson.devDependencies['@redwoodjs/cli-data-migrate']

if (!hasDataMigratePackage) {
console.error(
[
"Skipping data migrations; your project doesn't have the `@redwoodjs/cli-data-migrate` package as a dev dependency.",
"Without it installed, you're likely to run into memory issues during deploy.",
"If you want to run data migrations, add the package to your project's root package.json and deploy again:",
'',
'```',
'yarn add -D @redwoodjs/cli-data-migrate',
'```',
].join('\n')
)
} else {
execa.commandSync('yarn rw dataMigrate up', execaConfig)
}
}

const serverFilePath = path.join(rwjsPaths.api.dist, 'server.js')
const hasServerFile = fs.pathExistsSync(serverFilePath)

if (hasServerFile) {
execa(`yarn node ${serverFilePath}`, execaConfig)
} else {
const { handler } = await import(
'@redwoodjs/api-server/dist/apiCLIConfigHandler.js'
)
handler()
}
}

async function runWebCommands() {
execa.sync('yarn install', execaConfig)
execa.sync('yarn rw build web', execaConfig)
execa.commandSync('yarn install', execaConfig)
execa.commandSync('yarn rw build web --verbose', execaConfig)
}

if (side === 'api') {
runApiCommands()
} else if (side === 'web') {
console.log('\nRunning yarn install and building web...')
runWebCommands()
} else {
console.log('Error with arguments provided')
// you broke something, which should be caught by Yargs
}
}

0 comments on commit fab9f15

Please sign in to comment.