edge-runtime icon indicating copy to clipboard operation
edge-runtime copied to clipboard

self-hosted function (fcm messages as offical guides ) not working because of cpu hard limit

Open boyhax opened this issue 1 year ago • 1 comments

Bug report

  • [x] I confirm this is a bug with Supabase, not with my own application.
  • [x] I confirm I have searched the Docs, GitHub Discussions, and Discord.

Describe the bug

my fcm messaging function done exactly as the offical guides here https://supabase.com/docs/guides/functions/examples/push-notifications

the function not work and log 2024-06-11T20:38:17.042347571Z CPU time hard limit reached: isolate: 1fd05824-8e53-4f95-a7d3-913b87c79658 2024-06-11T20:38:17.042503172Z failed to send request to user worker: request has been cancelled by supervisor 2024-06-11T20:38:17.043659394Z user worker failed to respond: request has been cancelled by supervisor

i readed the docs the hard limit is the problem and thhat mean time limit but i dont find a way to fix this problem look here https://supabase.com/docs/guides/functions/debugging#limitations

in local development the function works fine every time.

To Reproduce

1-self host supabase 2- add fcm messaging function as above link for offical guide 3.invoke the function from any source curl or postgres webhooks

Expected behavior

function to work and cpu hard limit to be adjusted

System information

im doing selfhost of supabase on hostinger vps

  • 8 gb ram
  • 2 vcore
  • OS: Ubuntu 24.04
  • Browser na
  • Version of supabase-js: na
  • Version of Node.js:na onlu supabase installed using coolify panel

boyhax avatar Jun 11 '24 20:06 boyhax

I am having the same issue... Already build Deno with other settings for the max cpu time, but still the same error

chhofi avatar Aug 31 '24 20:08 chhofi

I am 12XL add-ons and still have the issue of push edge function. I think its supporter is not so professional to handle this issue.

fan123199 avatar Oct 15 '24 09:10 fan123199

If you are running the edge runtime in a self-hosted environment, you can adjust the cpu time and worker timeout at will.

cpu time limit: https://github.com/supabase/edge-runtime/blob/main/examples/main/index.ts#L108-L111

worker timeout: https://github.com/supabase/edge-runtime/blob/main/examples/main/index.ts#L82

memory limit: https://github.com/supabase/edge-runtime/blob/main/examples/main/index.ts#L82

All of this can be adjusted in the script of the main worker that creates the user worker. You can also find a script like this in the self-hosted template in the supabase/supabase repository. https://github.com/supabase/supabase/blob/master/docker/volumes/functions/main/index.ts

For cpu time limits, you can completely disable these limits by assigning 0 to cpuTimeSoftLimitMs and cpuTimeHardLimitMs respectively.

i readed the docs the hard limit is the problem and thhat mean time limit but i dont find a way to fix this problem look here https://supabase.com/docs/guides/functions/debugging#limitations in local development the function works fine every time.

Mmm... It seems that the main script in the supabase repository doesn't specify separate cpuTimeSoftLimitMs and cpuTimeHardLimitMs. https://github.com/supabase/supabase/blob/master/docker/volumes/functions/main/index.ts#L78-L82

The default values are therefore set. In this case they are set to very small values of 50ms and 100ms respectively. https://github.com/supabase/edge-runtime/blob/fbddb1620732e0490b170d2c081361bc834fe124/crates/sb_workers/user_workers.js#L123-L124

This makes it likely that you could have reached the CPU limit more easily than the limit described in the guide you referenced.

But since you're in a self-hosted environment, you can modify the main script to put this completely under your control.

nyannyacha avatar Oct 15 '24 16:10 nyannyacha

I am closing this issue for the following reasons:

  • The latest main branch now sets default resource limits as described in the docs (if no resource limits are set in the main worker. See my recent comment above regarding how to set resource limits on the main script)
  • If you are in a self-hosted environment, you can always override these resource limits via the main worker script. If you deployed the edge runtime via a docker-compose template published in supabase/supabase, you can easily override the resource limits by modifying this script.

nyannyacha avatar Nov 05 '24 22:11 nyannyacha

im self hosting supabase. managed to get it to work with this:

supabase/docker/volumes/functions/main/index.ts

import { serve } from 'https://deno.land/[email protected]/http/server.ts'
import * as jose from 'https://deno.land/x/[email protected]/index.ts'

console.log('main function started')


const JWT_SECRET = Deno.env.get('JWT_SECRET')
const VERIFY_JWT = Deno.env.get('VERIFY_JWT') === 'true'

function getAuthToken(req: Request) {
  const authHeader = req.headers.get('authorization')
  if (!authHeader) {
    throw new Error('Missing authorization header')
  }
  const [bearer, token] = authHeader.split(' ')
  if (bearer !== 'Bearer') {
    throw new Error(`Auth header is not 'Bearer {token}'`)
  }
  return token
}

async function verifyJWT(jwt: string): Promise<boolean> {
  const encoder = new TextEncoder()
  const secretKey = encoder.encode(JWT_SECRET)
  try {
    await jose.jwtVerify(jwt, secretKey)
  } catch (err) {
    console.error(err)
    return false
  }
  return true
}

serve(async (req: Request) => {
  if (req.method !== 'OPTIONS' && VERIFY_JWT) {
    try {
      const token = getAuthToken(req)
      const isValidJWT = await verifyJWT(token)

      if (!isValidJWT) {
        return new Response(JSON.stringify({ msg: 'Invalid JWT' }), {
          status: 401,
          headers: { 'Content-Type': 'application/json' },
        })
      }
    } catch (e) {
      console.error(e)
      return new Response(JSON.stringify({ msg: e.toString() }), {
        status: 401,
        headers: { 'Content-Type': 'application/json' },
      })
    }
  }

  const url = new URL(req.url)
  const { pathname } = url
  const path_parts = pathname.split('/')
  const service_name = path_parts[1]

  if (!service_name || service_name === '') {
    const error = { msg: 'missing function name in request' }
    return new Response(JSON.stringify(error), {
      status: 400,
      headers: { 'Content-Type': 'application/json' },
    })
  }

  const servicePath = `/home/deno/functions/${service_name}`
  console.error(`serving the request with ${servicePath}`)

  const memoryLimitMb = 150
  const workerTimeoutMs = 1 * 60 * 1000
  const noModuleCache = false
  const importMapPath = null
  const envVarsObj = Deno.env.toObject()
  const forceCreate = false;
  const netAccessDisabled = false;
  const cpuTimeSoftLimitMs = 10000;
  const cpuTimeHardLimitMs = 20000;
  const envVars = Object.keys(envVarsObj).map((k) => [k, envVarsObj[k]])

  try {
    const worker = await EdgeRuntime.userWorkers.create({
      servicePath,
      memoryLimitMb,
      workerTimeoutMs,
      noModuleCache,
      importMapPath,
      envVars,
      forceCreate,
      netAccessDisabled,
      cpuTimeSoftLimitMs,
      cpuTimeHardLimitMs,
      // maybeEszip,
      // maybeEntrypoint,
      // maybeModuleCode,
  });
    return await worker.fetch(req)
  } catch (e) {
    const error = { msg: e.toString() }
    return new Response(JSON.stringify(error), {
      status: 500,
      headers: { 'Content-Type': 'application/json' },
    })
  }
})

sinameraji avatar Nov 06 '24 05:11 sinameraji