Skip to content

Arch Forum 2025-02-27

Participants: Backend devs, Zak, Victor

Agenda

  • Localization and current culture
  • Manual renovate
  • Sessions and concurrency issues
  • DB Stuff (datetime2, table vs SP datatypes, index naming, Drop SP in migrations, new SP migration plan)

Summary

Localization and current culture

Given the recent localization bugs related to TransactionDetails we looked at how localization works and the big pitfalls.

By default the culture/language used comes from the thread context's current culture, which is set by the Accept-Language header. This assumption only works if backend is called from the Apps. Its called in another way (like from Hydra, through an event handler or by a job), the culture will for obvious reasons not be set to what the user expects. In those cases its very important to think through the flows and what culture/language to use!

Manual renovate

A FYI: Renovate can be triggered from the "Dependency Dashboard" issue. All repos that renovate triggers in will have this issue, for example here: majority-dev/be-transactions#733 Byt checking the checkboxes you can either make renovate update what packages can be renovated, or make it create/update the renovate PR.

Details on the renovate bot are here

Sessions and concurrency issues

An overview of 3 ways to handle concurrent requests.

1. Smarter SPs

If possible, writing the SP in such a way that it can work even if two calls for the same user/account/xxx happens at the same time. One example is the MajorityAccountStatusChanged handler in transactions:
- https://github.com/majority-dev/be-transactions/blob/master/Minority/Minority.Transactions/Minority.Transactions.Service/Account/EventHandlers/MajorityAccountStatusChangedEventHandler.cs#L53
- https://github.com/majority-dev/be-transactions/blob/master/Minority/Minority.Transactions/Minority.Transactions.Service/Account/MajorityAccountService.cs#L125
- https://github.com/majority-dev/be-transactions/blob/master/Minority/Minority.Transactions/Minority.Transactions.Db/Migrations/StoredProcedures/MajorityAccountUpdateOrInsert.sql

2. Sessions

A middle way for more complex flows is to use the session feature of our platform messaging code. By passing in a session id to the SendEvent method, all events with the same session id will be handled in the order they were published, and at most one at a time. On example in Galileo:
- https://github.com/majority-dev/be-galileo/blob/07b7f869708a24bac650acc490edd8d378768bf7/Minority/Minority.Galileo/Minority.Galileo.Events.Api/Api/TransactionEventsController.cs#L41
- https://github.com/majority-dev/be-galileo/blob/07b7f869708a24bac650acc490edd8d378768bf7/Minority/Minority.Galileo/Minority.Galileo.Service/EventHandlers/SettlementEventHandler.cs#L49

3. Distributed lock

The least desirable method, only as a last resort if other methods don't work is to use a distributed lock. Ideally code should use other ways, but sometimes it wont be possible or require massive refactoring. One such example is in MPay to prevent more than one Mpay on the same account at the same time:
https://github.com/majority-dev/be-mpay/blob/f08287cad8f2087c0b3e1b5f6914efb2d991dfb1/Minority/Minority.MPay/Minority.MPay.Service/Payment/TransferBetweenAccounts/TransferBetweenAccountsAction.cs#L55

DB Stuff

A collection of smaller issues we should think about when dealing with the database:

datetime2

In SQL Server there exists two datetime datatypes, datetime and datetime2. datetime2 is the correct type to use all the time and has no downsides. datetime has less precision and does not map cleanly to C# DateTime. Unfortunately we do have a lot of datetime in our current code, but we should use datetime2 in all new code.

Table vs SP datatypes.

We have had several serious performance degradation bugs because of mixed datatypes. It is very important that the datatypes of arguments into a Stored procedure matches what is in the table. Otherwise the DB might not be able to use index properly.

Below is a simple demo that can be run against the User table in stage that clearly shows the effect (the table uses varchar as its type)

set statistics io, time on
declare @msisdn varchar(50) = '123' 
-- declare @msisdn nvarchar(50) = '123' 

SELECT * FROM [dbo].[User] where msisdn = @msisdn

By using nvarchar the query has to scan all rows and becomes very slow.

Index naming

Always set a name on index. With the autogenerated names its not possible to write script that works in all of local, stage, prod DBs.

The below demo shows a bad and a good example:

CREATE TABLE NamingBad (
    X INT NOT NULL PRIMARY KEY
)

CREATE TABLE NamingGood (
    X INT NOT NULL,
    CONSTRAINT [PK_X] PRIMARY KEY CLUSTERED ([X])
)

The NamingBad table will have a generated name of its PK clustered index, which makes it very troublesome to maintain/change it in the future. Please avoid!

Drop SP in migrations

The DBUp code in platform now automatically removes SPs that once existed as migration scripts but were later removed. No additional handling is needed.

New SP Migration plan

As seen in the Ownership checklist there's now around 15 repos left that hasn't migrated to the new SP handling. We should try to migrate whenever we have the chance, for example when we update the SPs in a non-migrated area!