AWS: cloud code works, saving classes directly does not

Hi,

I am probably just missing something here.
I can successfully call cloud code functions on my AWS elastic beanstalk deployment (e.g. /parse/functions/abc), but saving a class directly via PUT always times out.
Locally on my dev server everything works like a charm.

This is a sample call:

curl -X "PUT" "http://<redacted>.elasticbeanstalk.com/parse/classes/Test/tFSSqF5w4f" \
     -H 'x-parse-session-token: <redacted>' \
     -H 'x-parse-application-id: <redacted>' \
     -H 'x-parse-client-key: <redacted>' \
     -H 'x-parse-os-version: 14.1 (16C50)' \
     -H 'Content-Type: text/plain; charset=utf-8' \
     -d $'{
  "r_instruments": [
    "violin",
    "singer"
  ]
}'

In the EB server logs I found the following error, but I don’t know how to fix it (especially since calling a cloud code function works like a charm).

2022/04/03 21:43:12 [error] 5122#5122: *1 upstream timed out (110: Connection timed out) while reading response header from upstream, client: 24.164.138.198, server: , request: "PUT /parse/classes/Test/tFSSqF5w4f HTTP/1.1", upstream: "http://127.0.0.1:8080/parse/classes/Test/tFSSqF5w4f", host: "<redacted>.elasticbeanstalk.com"

Did anybody else ran into this problem by any chance?
I am totally lost here.

Do you see any error on your parse server logs? It might be connection problem to mongo db. Does your cloud code function perform any kind of database operation? Would you mind to share your cloud code function config and also the code that you are using to initialize and mount parse to the express.js app?

That’s the funny thing, everything executes, no,error, just the result will never be returned to the client.
It gets filtered on the nginx level (see error above).
Everything works fine in my local environment.

This error happens on nginx when the upstream (in this case, your parse server instance) delays to respond and hits the timeout limit. That’s why I wanted to check what you have in this cloud code function (and how it is different from your update call) and also how you are mounting Parse Server in Express.js app. If you can share the nginx setup, it might be also helpful.

Ah, I see. The funny thing though is there is barely any delay, less than 300ms for sure.
All it does is update some user data and triggers some beforeSave and afterSave handlers in the process.

That being said, here is the parse config:

const _ = require("underscore");
const express = require("express");
const ParseServer = require("parse-server").ParseServer;
const OneSignalPushAdapter = require("@fmendoza/parse-server-onesignal-push-adapter");
const Sentry = require('@sentry/node');

let requiredEnvVars = [];
requiredEnvVars.splice(0,0,"MASTER_KEY","APP_ID","PARSE_MOUNT");
requiredEnvVars.splice(0,0,"ENVIRONMENT","SENTRY_DSN");
requiredEnvVars.splice(0,0,"FIREBASE_KEY", "FIREBASE_URL", "FIREBASE_EMAIL", "FIREBASE_PROJECT_ID");
requiredEnvVars.splice(0,0,"AWS_BUCKET","AWS_ID", "AWS_KEY", "AWS_COGNITO_POOL");
requiredEnvVars.splice(0,0,"DATABASE_URI");

const DEV_ENVIRONMENT = "DEV";
const environment = process.env.ENVIRONMENT || DEV_ENVIRONMENT;

const isProductionServer = environment === "PRODUCTION";

_.each(requiredEnvVars, function(envVar) {
	if (!process.env[envVar]) {
		console.error("CRITICAL ERROR: Environment variable '"+envVar+"' not defined!");
		process.exit(1);
	}
});

const app = express();


// **************************************
//        Parse Server setup
// **************************************

const port = process.env.PORT || 1337;
// Serve the Parse API on the /parse URL prefix
const mountPath = process.env.PARSE_MOUNT || "/parse";
const serverUrl = "http://localhost:" + port + mountPath;

const apiVersion = "v1";
const databaseUri = process.env.DATABASE_URI;

console.log("load "+apiVersion+" api");

const api = new ParseServer({
	databaseURI: databaseUri,
	cloud: __dirname + "/api_v1/main.js",
	appId: process.env.APP_ID,
	masterKey: process.env.MASTER_KEY,
	serverURL: serverUrl,
	enableExpressErrorHandler: false,
	enableAnonymousUsers: false,
	directAccess: true,
	allowClientClassCreation: !isProductionServer,
	auth: {
		apple: {
			client_id: "xxx"
		},
		firebase: {
			module: __dirname + "/api_v1/firebase.js"
		}
	}
	,
	protectedFields: {
		_User: {
			"*": ["isArtist"],
			"role:moderator": ["email"],
			"role:admin": []
		},
		Example: {
			"*": ["isApproved"]
		}
	}
});
app.use(mountPath, api);

// Parse Server plays nicely with the rest of your web routes
app.get("/", function (req, res) {
	res.status(200).send("Keep calm and carry on.");
});


app.get("/apple-app-site-association", function (req, res) {
	res.setHeader('content-type', 'application/json');
	res.status(200).send('{"applinks": {"apps": [],"details": [{"appID": "xxx","paths": [ "/oauth/*", "/ios/*" ]}]}}');
});
app.get("/.well-known/apple-app-site-association", function (req, res) {
	res.setHeader('content-type', 'application/json');
	res.status(200).send('{"applinks": {"apps": [],"details": [{"appID": "xxx","paths": [ "/oauth/*", "/ios/*" ]}]}}');
});

app.get("/ios/open/", function (req, res) {
	res.writeHead(302, {'Location': 'https://itunes.apple.com/app/xxx?mt=8'});
	res.end();
});

app.get("/oauth/apple", function (req, res) {
	const url = "xxx://oauth2/apple?code="+req.query.code+"&state="+req.query.state;
	const message = ""; //"Please try again later.";
	res.setHeader('content-type', 'text/html; charset=utf-8');
	res.status(200).send('<html><head><meta http-equiv="refresh" content="0;URL=\''+url+'" /><meta name="viewport" content="width=device-width, initial-scale=1"><style>h1 {font-family: "Open Sans", "Helvetica Neue", "Helvetica", "Arial" }</style></head><body><table width="100%" height="100%"><tr><td align="center" valign="center"><h1>'+message+'</h1></td></tr></table></body></html>');
});

app.listen(port, function () {
	Parse.Cloud.run("startup", {
		secretKey: process.env.AWS_KEY
	}).then(function () {
		console.info("server running on port " + port + " ["+process.env.ENVIRONMENT+"/"+apiVersion+"].");
		console.info("Init code successfully executed.");
	}, function (error) {
		console.error("Couldn't launch server on port " + port + " ["+process.env.ENVIRONMENT+"/"+apiVersion+"].");
		console.error(error);
	});
});

The nginx config is the default one for Elastic Beanstalk, didn’t change anything there (wouldn’t even know how to retrieve it).

I don’t see anything wrong so far. Could you share the triggers that you have in place for this class?

That question put me on the right path. There is an update http request to firebase in the afterSave trigger. It’s very embarrassing, but I forgot to setup the DB on firebase in the staging environment, causing the write call to it to time out.
Everything works like a charm now.
Thank you so much for your help, I kept looking in the wrong place.

1 Like