self-hosted function (fcm messages as offical guides ) not working because of cpu hard limit
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
I am having the same issue... Already build Deno with other settings for the max cpu time, but still the same error
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.
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.
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.
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' },
})
}
})