user2471755
user2471755

Reputation: 23

Multiple Npsql connections

I am trying to create a simple server (.Net Core 5.0), that communicates with PostgreSQL DB (v 13.1) via Npgsql + Npgsql.EntityFramework.

I have code that creates a new user, grants some rights and retrieves OID:

        string connString =
            String.Format(
                "Server={0};Username={1};Database={2};Port={3};Password={4};SSLMode=Prefer",
                PSqlHost, User, DBname, PSqlPort, Password);
        NpgsqlConnection psqlConnection = null;
        try
        {
            using (psqlConnection = new NpgsqlConnection(connString))
            {
                psqlConnection.Open();
                StringBuilder cmdBuilder = new StringBuilder();
                cmdBuilder.Append(string.Format("CREATE USER {0} WITH LOGIN PASSWORD '{1}';\r\n", signIn.Login, signIn.Pass));
                cmdBuilder.Append(string.Format("GRANT SELECT ON ALL TABLES IN SCHEMA public TO {0};\r\n", signIn.Login));
                cmdBuilder.Append(string.Format("GRANT SELECT, UPDATE ON ALL TABLES IN SCHEMA users TO {0};\r\n", signIn.Login));
                cmdBuilder.Append(string.Format("SELECT oid FROM pg_catalog.pg_authid WHERE rolname='{0}';\r\n", signIn.Login));
                using (var pgCmd = psqlConnection.CreateCommand())
                {
                    pgCmd.CommandText = cmdBuilder.ToString();
                    if (!pgCmd.IsPrepared) pgCmd.Prepare();
                    using (var reader = pgCmd.ExecuteReader())
                    {
                        if (!reader.HasRows) return TcpMessageSpec.Fail;
                        if (!reader.IsOnRow) reader.Read();
                        var oid = (uint)reader.GetValue(0);
                    }
                }
            }

            return TcpMessageSpec.OK;
        }
        catch (PostgresException ex)
        {
            Log.Exception(ex.MessageText);
            if (ex.SqlState == PostgresErrorCodes.InvalidPassword) return TcpMessageSpec.AuthFailed;
            else if (ex.SqlState == PostgresErrorCodes.DuplicateObject) return TcpMessageSpec.UserExists;
            return TcpMessageSpec.ConnectionFailed;
        }
        catch (Exception ex)
        {
            Log.Exception(ex.InnerException);
            return TcpMessageSpec.ConnectionFailed;
        }
        finally
        {

        }

This code perfectly works from a single thread, but I want it to work from multiple threads.

When I run this code from five or more threads, I catch PostgresException: "XX000: tuple concurrently updated". However, some threads succeed (no exception, OID is retrieved).

Attempts to solve (but the same result):

  1. I have tried async versions of NPSql
  2. Used NpgsqlConnection transactions
  3. Batched/Not batched commands queries
  4. I have read similar questions

But still I do not understand where my mistake is.

Upvotes: 0

Views: 372

Answers (1)

Shay Rojansky
Shay Rojansky

Reputation: 16722

tuple concurrently updated is an error PostgreSQL sometimes returns when certain DDL operations are performed concurrently (see e.g. this discussion). This doesn't have anything to do with Npgsql - you're probably going to have to use synchronization to make sure these DDL operations don't happen in parallel.

Upvotes: 1

Related Questions