cloud-sql-nodejs-connector
cloud-sql-nodejs-connector copied to clipboard
Error "address already in use" when trying to connect to google cloud SQL
Question
I'm connecting my NextJS app to my Cloud SQL instance. I've followed the documentation and I'm stuck in the following error.
Error: listen EADDRINUSE: address already in use /Users/josechavez/projectName/projectName/.s.PGSQL.5432
at Server.setupListenHandle [as _listen2] (node:net:1812:21)
at listenInCluster (node:net:1877:12)
at Server.listen (node:net:1976:5)
at node:internal/util:410:7
at new Promise (<anonymous>)
at Server.<anonymous> (node:internal/util:396:12)
at Connector.startLocalProxy (webpack-internal:///(rsc)/./node_modules/@google-cloud/cloud-sql-connector/dist/mjs/connector.js:207:22)
at process.processTicksAndRejections (node:internal/process/task_queues:95:5)
at async connect (webpack-internal:///(rsc)/./src/app/lib/connect.ts:16:5)
at async prismaClientSingleton (webpack-internal:///(rsc)/./src/app/lib/prisma.ts:17:16) {
code: 'EADDRINUSE',
errno: -48,
syscall: 'listen',
address: '/Users/josechavez/projectName/projectName/.s.PGSQL.5432',
port: -1
}
Basically, the error is saying that the address is already in use, and I have no clue why. In the console, my instance seems to be correctly configured (with the basics configs, like public IP, etc.)
I've followed your examples on how to connect to the Cloud SQL
This is what my connection code looks like.
import { PrismaAdapter } from '@lucia-auth/adapter-prisma';
import { connect } from './connect';
const instanceConnectionName = process.env.GOOGLE_DB_CONNECTION_NAME ?? '';
const user = process.env.GOOGLE_DB_USERNAME ?? '';
const database = process.env.GOOGLE_DB_NAME ?? '';
const prismaClientSingleton = async () => {
try {
return await connect({ instanceConnectionName, user, database });
} catch (error) {
console.error(error);
}
};
const globalForPrisma = globalThis as unknown as {
prisma: any | undefined;
};
const { prisma, close } = await (globalForPrisma.prisma ??
prismaClientSingleton());
const adapter = new PrismaAdapter(prisma.session, prisma.user);
export default prisma;
export { adapter };
if (process.env.NODE_ENV !== 'production') globalForPrisma.prisma = prisma;
Additional Context
I've tried with 5432, 5433, ... and so on. but nothing still.
I've also tried to use this whole path that is mentioned in the docs /cloudsql/INSTANCE_CONNECTION_NAME/.s.PGSQL.5432
But this time I get an error that says no access/view permissions, but I'm not sure if this is the way.
Someone please help me, Thanks in advance!
Hi @JoseChavez98,
Here's my best guess at what is wrong: The startLocalProxy()
function opens and listens on a unix socket on the local host. If startLocalProxy()
was called by multiple node processes running at the same time, then it would attempt to open same unix socket for listening more than once, resulting in the error Error: listen EADDRINUSE: address already in use
for all but the first node process.
Is this a possible cause?
-Jonathan
Hi @JoseChavez98, I wasn't able to reproduce this behavior. Feel free to comment back if want more help.
Hello Jonathan @hessjcg , thanks for the help. I'm not sure if your above assumption is happening. I'm just running the library as in the example you guys provided.
This is how I connect to the cloud.
import { PrismaAdapter } from '@lucia-auth/adapter-prisma';
import { connect } from './connect';
const instanceConnectionName = process.env.GOOGLE_DB_CONNECTION_NAME ?? '';
const user = process.env.GOOGLE_DB_USERNAME ?? '';
const database = process.env.GOOGLE_DB_NAME ?? '';
const prismaClientSingleton = async () => {
try {
return await connect({ instanceConnectionName, user, database });
} catch (error) {
console.error(error);
}
};
const globalForPrisma = globalThis as unknown as {
prisma: any | undefined;
};
const { prisma, close } = await (globalForPrisma.prisma ??
prismaClientSingleton());
const adapter = new PrismaAdapter(prisma.session, prisma.user);
export default prisma;
export { adapter };
if (process.env.NODE_ENV !== 'production') globalForPrisma.prisma = prisma;
and the connect function is exactly like in your examples.
import { resolve } from 'node:path';
import {
AuthTypes,
Connector,
IpAddressTypes,
} from '@google-cloud/cloud-sql-connector';
import { PrismaClient } from '@prisma/client';
export async function connect({
instanceConnectionName,
user,
database,
}: {
instanceConnectionName: string;
user: string;
database: string;
}) {
const path = resolve('.s.PGSQL.5432'); // postgres-required socket filename
const connector = new Connector();
await connector.startLocalProxy({
instanceConnectionName,
ipType: IpAddressTypes.PUBLIC,
authType: AuthTypes.IAM,
listenOptions: { path },
});
// note that the host parameter needs to point to the parent folder of
// the socket provided in the `path` Connector option, in this example
// that is going to be the current working directory
const datasourceUrl = `postgresql://${user}@localhost/${database}?host=${process.cwd()}`;
const prisma = new PrismaClient({ datasourceUrl });
return {
prisma,
async close() {
await prisma.$disconnect();
connector.close();
},
};
}
This is currently stopping me from moving forward. I will be happy if you let me add you to my project for testing purposes. Thanks in advance!
I wonder if the cause of this error is maybe related to https://github.com/GoogleCloudPlatform/cloud-sql-nodejs-connector/issues/349
@jackwotherspoon I'm gonna say maybe. I made some tests and I was able to successfully connect to my DB using the traditional connection (without the proxy). So the problem is related to that localProxy implementation/connection. Is there a chance We can have this looked up? Its a real deal breaker for me .
Thanks!
@jackwotherspoon I'm gonna say maybe. I made some tests and I was able to successfully connect to my DB using the traditional connection (without the proxy). So the problem is related to that localProxy implementation/connection. Is there a chance We can have this looked up? Its a real deal breaker for me .
@JoseChavez98 Yes something isn't working the way we would like it to. I will be digging into this over the next day or so and fixing these issues 😄
thanks @jackwotherspoon, let me know if I can help with anything.
thanks @jackwotherspoon, let me know if I can help with anything.
@JoseChavez98 thanks! Will keep you in the loop if I have any additional questions 😄
@JoseChavez98 I was able to reproduce this error and now the cause of it makes sense to me. So let me try and explain what is happening to cause the address already in use
error.
TLDR; The close()
returned from const { prisma, close } = await connect({...});
must be called to properly delete the unix socket file ending in s.PGSQL.5432
. (we can probably update our docs/sample to have a try/catch to make sure the close() is being called)
Let me give a detailed explanation using the sample you used as reference:
https://github.com/GoogleCloudPlatform/cloud-sql-nodejs-connector/blob/dc7e0d0a23a419f539ce971350bf76694abcc4c8/examples/prisma/postgresql/connect.ts#L15-L46
In the sample connector.startLocalProxy(...)
creates a local unix socket file at the location specified by path
in the listenOptions
. So in this example and yours it will create the .s.PGSQL.5432
file in the current working directory.
The connect
function returns the Prisma client, along with a close()
function that when called will disconnect the client as well as closes the Cloud SQL Connector object by callingconnector.close()
. This last part is the critical piece that is resulting in your error.
If the close()
is not called, the connector is not properly cleaned up. Part of the Connector's cleanup is to close and delete all local unix socket files. @JoseChavez98 from what I can see, your code never calls close()
thus the local unix domain socket file is not being deleted, hence why on the next call/time your program is run you are seeing the error address already in use
, because the file is still there from the previous run.
Steps to fix the error:
- Delete the stale local unix domain socket file located at
/Users/josechavez/projectName/projectName/.s.PGSQL.5432
- Add the call
await close()
to your code. You will want to call it whenever you are done interacting with the Prisma client to disconnect the client and close the connector cleanly. (Note: Probably will want to add a try/catch that also callsawait close()
when an error is thrown. This way the client and connector are also cleaned up when errors are thrown from the client and before you program exits.
I'll leave this issue open and take an action item for myself of updating our README, examples to clearly point out the close()
must be called.
I confirm that that was the problem, thanks for the help @jackwotherspoon . I know this might be unrelated, but I'm having another issue. It looks like It starts the local proxy but didn't quite make it through the PrismaClient. It feels like it's in a loop or if It is waiting for something. Below is my modest way of debugging and the result in the console.
Logs
As you can see, It doesn't make it through the construction of PrismaClient object.
I'll appreciate your input again.
Thanks for all the help!
@JoseChavez98 do you mind trying this basic SELECT NOW()
example test the basic connectivity. If it passes then it is most likely your prisma usage. Also I think since the original question is answered it might be beneficial to create a new issue to fix the Prisma client hanging if the basis test does not work 😄
const { connect } = await import('./connect.js');
const { prisma, close } = await connect({
instanceConnectionName: "my-project:my-region:my-instance", // change these values
user: "[email protected]", // change these values
database: "my-db", // change these values
});
try {
const [{ now }] = await prisma.$queryRaw`SELECT NOW() as now`
console.log(now); // prints returned time value from server)
await close();
} catch (e) {
console.log("Error occured, closing!");
await close();
throw e
}
If the time is properly printed from the above example then again its probably your usage of Prisma that may be causing the client to hang.
Thanks for all the help @jackwotherspoon .
To respond to my last question, There was a mismatch between prisma and prismaClient library versions. That was causing the weird behavior.
@JoseChavez98 Glad you were able to get it to work! Thanks for being patient here 😄
I've put up a PR adding a more detailed comment to the examples for Prisma clearly mentioning that close()
should be called. Hopefully this helps future users not run into the same issue you faced here. Thanks for raising this issue and bringing it to our attention, we always appreciated the feedback and finding areas to improve our libraries/docs.
@jackwotherspoon thanks for the help. I'd appreciate it if you could take a look at this question since it is slightly related to this issue. Or maybe you know someone who could help me. Thanks in advance
I'd appreciate it if you could take a look at this question since it is slightly related to this issue.
I will take a look at this question sometime today or tomorrow at the latest 😄
Hey @jackwotherspoon. I'm hitting the same issue here, confirmed to be caused by my application attempting to create another socket file when one already exists.
I have a question here regarding what the expected process is for handling this. For example, since my database calls are asynchronous, the connect() is called before the previous one has a chance to properly close().
This can be mitigated by making the socket filename dynamic (such as including the timestamp), but this feels very wrong.
Additionally, how is this supposed to work if I have multiple users connecting to the application at one time? I feel like I'm missing something important here in order to move from the example => production.
Any guidance would be helpful.
Thanks!
@brandinchiu This is great feedback! I have gone ahead and created https://github.com/GoogleCloudPlatform/cloud-sql-nodejs-connector/issues/416 where I outline how we should make our Prisma examples more production friendly.
TLDR; You will want to move the following outside of connect
and have the Connector
and local proxy shared across PrismaClients for different users. You only need one Connector
and one local proxy per Cloud SQL instance.
You will want to move these lines outside of connect
and then pass your connector
into connect
const path = resolve('.s.PGSQL.5432'); // postgres-required socket filename
const connector = new Connector();
await connector.startLocalProxy({
instanceConnectionName,
ipType: IpAddressTypes.PUBLIC,
authType: AuthTypes.IAM,
listenOptions: {path},
});
Thanks, this is very helpful. I'll keep an eye on #416 from here.