Мы разрабатываем приложение группового чата и используем PostgreSQL для хранения сообщений чата.
CREATE TABLE public.chatmessage
(
chatmessageid uuid NOT NULL DEFAULT uuid_generate_v4(),
text character varying COLLATE pg_catalog."default",
planid uuid NOT NULL,
userid uuid NOT NULL,
createdat timestamp with time zone NOT NULL DEFAULT timezone('utc'::text, now()),
updatedat timestamp with time zone,
deleted boolean NOT NULL DEFAULT false,
viewedallstatus boolean,
vieweduserids uuid[],
alloweduserids uuid[],
CONSTRAINT chatmessage_pkey PRIMARY KEY (chatmessageid)
)
Для управления статусом чтения мы храним userIds просматриваемых участников в столбце просматриваемых пользователей.
При интенсивном использовании группового чата с 5 или более участниками мы получаем следующее исключение:
Npgsql.PostgresException (0x80004005): 40P01: deadlock detected
at Npgsql.NpgsqlConnector.<DoReadMessage>d__157.MoveNext()
--- End of stack trace from previous location where exception was thrown ---
at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()
at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
at System.Runtime.CompilerServices.TaskAwaiter`1.GetResult()
at System.Runtime.CompilerServices.ValueTaskAwaiter`1.GetResult()
at Npgsql.NpgsqlConnector.<ReadMessage>d__156.MoveNext()
--- End of stack trace from previous location where exception was thrown ---
at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()
at Npgsql.NpgsqlConnector.<ReadMessage>d__156.MoveNext()
--- End of stack trace from previous location where exception was thrown ---
at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()
at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
at System.Runtime.CompilerServices.TaskAwaiter`1.GetResult()
at System.Runtime.CompilerServices.ValueTaskAwaiter`1.GetResult()
at Npgsql.NpgsqlDataReader.<NextResult>d__32.MoveNext()
--- End of stack trace from previous location where exception was thrown ---
at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()
at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
at Npgsql.NpgsqlDataReader.<<NextResultAsync>b__31_0>d.MoveNext()
--- End of stack trace from previous location where exception was thrown ---
at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()
at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
at Npgsql.NpgsqlCommand.<Execute>d__71.MoveNext()
--- End of stack trace from previous location where exception was thrown ---
at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()
at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
at System.Runtime.CompilerServices.TaskAwaiter`1.GetResult()
at System.Runtime.CompilerServices.ValueTaskAwaiter`1.GetResult()
at Npgsql.NpgsqlCommand.<ExecuteNonQuery>d__84.MoveNext()
--- End of stack trace from previous location where exception was thrown ---
at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()
at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
at Npgsql.NpgsqlCommand.<>c__DisplayClass83_0.<<ExecuteNonQueryAsync>b__0>d.MoveNext()
--- End of stack trace from previous location where exception was thrown ---
at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()
at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
at Dapper.SqlMapper.<ExecuteImplAsync>d__37.MoveNext() in C:\projects\dapper\Dapper\SqlMapper.Async.cs:line 646
--- End of stack trace from previous location where exception was thrown ---
at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()
at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
Я чувствую, что, поскольку приложение одновременно обновляет один и тот же столбец, возникает эта проблема.Запрос, который обновляет столбец просматриваемых пользователей, выглядит следующим образом:
"Update ChatMessage Set ViewedUserIds = ViewedUserIds || @UserId Where PlanId = @PlanId And @UserId = Any(AllowedUserIds) And Not (@UserId = Any(ViewedUserIds)) And Deleted = False;"
Как мы можем решить эту проблему?