backend leaking Postgres database connections - what I did to fix it

Ғылым және технология

Get my Introduction to Database Engineering course
database.husseinnasser.com
This new backend I built hangs after few requests. Turns out I was leaking database connections from the pool. So new requests were just stuck in the database pool queue waiting. In this video, I try to explain the problem and how I solved it.
Code
github.com/hnasr/javascript_p...
Become a Member on KZread
/ @hnasr
🔥 Members Only Content
• Members-only videos
Support my work on PayPal
bit.ly/33ENps4
🧑‍🏫 Courses I Teach
husseinnasser.com/courses
🏭 Backend Engineering Videos in Order
backend.husseinnasser.com
💾 Database Engineering Videos
• Database Engineering
🎙️Listen to the Backend Engineering Podcast
husseinnasser.com/podcast
Gears and tools used on the Channel (affiliates)
🖼️ Slides and Thumbnail Design
Canva
partner.canva.com/c/2766475/6...
🎙️ Mic Gear
Shure SM7B Cardioid Dynamic Microphone
amzn.to/3o1NiBi
Cloudlifter
amzn.to/2RAeyLo
XLR cables
amzn.to/3tvMJRu
Focusrite Audio Interface
amzn.to/3f2vjGY
📷 Camera Gear
Canon M50 Mark II
amzn.to/3o2ed0c
Micro HDMI to HDMI
amzn.to/3uwCxK3
Video capture card
amzn.to/3f34pyD
AC Wall for constant power
amzn.to/3eueoxP
Stay Awesome,
Hussein

Пікірлер: 90

  • @hnasr
    @hnasr2 жыл бұрын

    Check out my udemy Introduction to Database Engineering course database.husseinnasser.com Learn the fundamentals of database systems to understand and build performant backend apps

  • @alexander_farkas
    @alexander_farkas2 жыл бұрын

    If you show how to do it correctly, you should have wrap all code in try/finally. Connection should be released in any situation. Even when you do simple select, your database server may fail to process it, so it results in thrown exception and unreleased connection.

  • @hnasr

    @hnasr

    2 жыл бұрын

    That is correct, good catch

  • @ayush.kumar.13907

    @ayush.kumar.13907

    2 жыл бұрын

    @@hnasr good try-catch

  • @alb12345672

    @alb12345672

    2 жыл бұрын

    what if he did pool.connect().then((client)=>{......})

  • @dprophecyguy
    @dprophecyguy2 жыл бұрын

    Hey Hussain loving the new art style for thumbnails

  • @marslogics

    @marslogics

    2 жыл бұрын

    Lovely indeed

  • @aayush_dutt
    @aayush_dutt2 жыл бұрын

    Your videos are getting so much better. I honestly didn't like those long long videos. Really appreciate the move to short, concise video with high quality audio

  • @edwardteach2
    @edwardteach22 жыл бұрын

    Very nice to go hands on and explain the intricacies instead of me bashing my head reading textbooks and documentations for hours only to be explained in 12 minutes.

  • @Chris-cx6wl
    @Chris-cx6wl2 жыл бұрын

    Love this form of short videos of real world problems.

  • @CharuthaBandara
    @CharuthaBandara2 жыл бұрын

    You’re the man. I was struggling with hanging queries for a week

  • @adhendry10
    @adhendry102 жыл бұрын

    Hey Hussein, i like the way you explain things. Next video, could you pls do step by step sizing for a web server hardware, and database. And a bit explanation on things to understand such as latency, troughput, bandwidth, TPS, etc

  • @nontraditionaltech2073
    @nontraditionaltech20732 жыл бұрын

    I just found your channel, subbed and rang the bell. Thanks for your amazing content!! I’m an aerospace/defense SWE (C, some C++) that is just starting their web dev (love backend) journey. Thinking of switching to it in the future. You and Amigoscode are my new favorite channels 😎. Cheers!!

  • @akramdahmani7214

    @akramdahmani7214

    2 жыл бұрын

    you're following the GOATs good luck with your web dev journey

  • @nontraditionaltech2073

    @nontraditionaltech2073

    2 жыл бұрын

    @@akramdahmani7214 thank you! GOATs for sure, best backend content on KZread.

  • @g_mb
    @g_mb2 жыл бұрын

    Hussain , your videos are just fantastic ! Learn something cool in each and every video. Please keep the videos coming 👍🏼

  • @andrewmenshicov2696
    @andrewmenshicov26962 жыл бұрын

    in python we have context managers for that. like this async with connection as pool.acquire(): await connection.execute(...) and cuz of that "with ...", it automatically releases the connection as u r done w/ it 😇

  • @hnasr
    @hnasr2 жыл бұрын

    source code updated with some important comments (thank you) The first is a typo I made while testing this new code I used Pool.query where it should have been client.query . Effectively what it was doing is getting a client and not using it at all and using a connection from the pool instead. The second one which is a nice guard With finally/try catch so it releases the client even in case of an error which I wasn’t accounting for. What a fantastic community I have over here. github.com/hnasr/javascript_playground/tree/master/database-leak

  • @hnasr
    @hnasr2 жыл бұрын

    Get my Introduction to Database Engineering udemy course database.husseinnasser.com

  • @dragon_warrior_
    @dragon_warrior_2 жыл бұрын

    Learning something new every day from these videos

  • @nguyenmanhcuong9434
    @nguyenmanhcuong94342 жыл бұрын

    Hi Hussain, I bought your course on Udemy to support you. As always, your content is simple and well explained, and pretty helpful.

  • @hnasr

    @hnasr

    2 жыл бұрын

    Thank you ❤️

  • @EddyCaffrey
    @EddyCaffrey2 жыл бұрын

    « You want things to be consistent ». 😅😅 Yes you do. Inconsistent problems are nightmares . Thanks for the explanation

  • @isaacfrost9798
    @isaacfrost97982 жыл бұрын

    We love you hussien!!

  • @hnasr

    @hnasr

    2 жыл бұрын

    ❤️

  • @THEBEST-lh6pq
    @THEBEST-lh6pq2 жыл бұрын

    What if I use pool.query(begin) and write each line with pool.query. What happens in that case?

  • @marslogics
    @marslogics2 жыл бұрын

    Between client.quety of begin and commit, you have used pool.query to actually fetch the data, so that will not gonna use your transaction that you created using client instance. The query in between should be executed using client.query instead pool.query to utilize the transaction your created.

  • @hnasr

    @hnasr

    2 жыл бұрын

    Urgg good catch, must have been during preparation of the video forgot to change it back. It should be client.query as you mentioned. It still produces the same effect, but the query will not executed in the transaction that I prepared of course. (Dangerous )

  • @marslogics

    @marslogics

    2 жыл бұрын

    @@hnasr Thank you for providing all the awesome details, really learning a lot from you. Lots of love from Pakistan.

  • @MrLinuxFreak
    @MrLinuxFreak2 жыл бұрын

    isnt there a configuration at the server that you can limit from there your connections?

  • @cappuccinopapi3038
    @cappuccinopapi30382 жыл бұрын

    Great video, thanks Hussein

  • @abhisheksahu616
    @abhisheksahu6162 жыл бұрын

    Very nice! I encountered a similar situation with Mlflow + Hyperopt (Data Science). Mlflow does not release connection automatically so after 100 or so model training, the optimization got stuck.

  • @redouane5626
    @redouane56262 жыл бұрын

    Hi Hussain, Thanks for awesome videos. How do we pick correct number for `max` connection pool in real world application? how do we scale it dynamically?

  • @prabhatism
    @prabhatism2 жыл бұрын

    Did this bug reach production?

  • @Ghandmann1
    @Ghandmann12 жыл бұрын

    Depending on how your real code looked like, you have another bug lurking: You are mixing and matching client.query and pool.query. pool.query is a shorthand helper for getting a connection, running a client and returning the conneciton to the pool. While a client maintains a single connection until released manually. So in your example code you are creating a transaction via client.query("begin"). Than you ran the SELECT-Statement on a totally different connection via pool.query(). And then you commit an empty transaction on the first connection! That's also the reason why your backend is already stuck at request nr. 4 while you allow 4 connections (which would only block you at request 5). But on every request to try to get 2 connections and only return 1. Therefore on request 3 you try to get connection nr. 4 (working) and nr. 5 (blocking) and it's over.

  • @hnasr

    @hnasr

    2 жыл бұрын

    That is correct thanks! Its a typo I made while building this repro and testing different cases, I already fixed it as someone pointed it out earlier

  • @lastpslevin
    @lastpslevin Жыл бұрын

    I have finished this beginner playlist. This was the last video. Which playlist should I start watching next?

  • @solid8403
    @solid84032 жыл бұрын

    This problem can also happen with atomic database updates done in a transaction ... The atomic update never completes ... the connection is never released back to the pool. This continues until all connections in the pool are all used. Now, you can't collect another connection from the pool. Server restart necessary. You need to run select * from pg_stat_activity where datname = 'db_name'; to see the state of connections to postgres itself.

  • @vijaypatneedi
    @vijaypatneedi2 жыл бұрын

    never thought that you would do this silly mistake 😅

  • @nawafb6280
    @nawafb62802 жыл бұрын

    Nice video as usual, what is your mac specs if do not mind to tell us 🙏🏻

  • @VictorMongi
    @VictorMongi2 жыл бұрын

    Hello Hussein, do you have tutorial on how to setup psql on production? ex. tuning up and security. Thank you...

  • @HassanSelim0

    @HassanSelim0

    2 жыл бұрын

    Personally I always prefer to use managed database, whatever my cloud provider provides. Because I don't have the capacity to monitor the database to make sure things are fine and up to date all the time.

  • @Klonzo33
    @Klonzo332 жыл бұрын

    This was awesome. One question though. If max connections is 4, why does the failure happen after the 3rd refresh instead of the 4th?

  • @senpaker

    @senpaker

    2 жыл бұрын

    there were 4 request made, 1. when he first open the page 2. 3 other request he made by refreshing

  • @PhongGT
    @PhongGT2 жыл бұрын

    Hey what do you use for screen recording? How do you zoom in to your mouse like that?

  • @lakhveerchahal

    @lakhveerchahal

    2 жыл бұрын

    It can be done in editing probably.

  • @PhongGT

    @PhongGT

    2 жыл бұрын

    @@lakhveerchahal I guess you can do it with tracking but I think there are screen recording apps that zoom in for you

  • @astral_gaming_0956
    @astral_gaming_09562 жыл бұрын

    Hi, I have a weird question. Assuming you have the code exactly as your code, but on the select query, you get an error, that then gets sent straight to catch instead of finishing with client.release, would it still hang the client, and if yes, what's the best way to release the connection even in the case of an error?

  • @redouane5626

    @redouane5626

    2 жыл бұрын

    call release in `finally` block

  • @astral_gaming_0956

    @astral_gaming_0956

    2 жыл бұрын

    @@redouane5626 But wouldn't I need to declare client outside the try/catch to be able to release it in either finally or catch? Or is holstering affecting try/catch blocks as well (as in once const client got called, it can be referenced in .catch as well) ?

  • @alexander_farkas

    @alexander_farkas

    2 жыл бұрын

    @@astral_gaming_0956 you should define variable outside of try-block. ''' let client; try { client = await connect() ... } finally { client?.release() }

  • @astral_gaming_0956

    @astral_gaming_0956

    2 жыл бұрын

    @@alexander_farkas that makes sense, thanks for the explanation. I had a bit of a brainlag and thought that it might become a weird security vulnerability if you could crash the backend before the connection got released, thus after doing it X (max) number of times, no other user could connect to the database.

  • @coolcoolsafe
    @coolcoolsafe2 жыл бұрын

    You are a legend

  • @IhsanMujdeci
    @IhsanMujdeci2 жыл бұрын

    I mean, you shouldn't be creating db connection inside your handler. You should open one or few and pass service in that use these connections into handlers using DI.

  • @vamshikrishnareddy76
    @vamshikrishnareddy76 Жыл бұрын

    Hi, Hussein I am a bootcamp level programmer (javascript)trying to understand stuff I read an article about golang and they were saying that shifting from node to golang reduced their server cost by xyz% which was on the cloud obviously . I just know that golang is a low level language so therefore it can handle more requests but I am just quercous about how does it reduces the cost ? tried thinking about it but I have gaps In my knowledge I don't know about cloud could you please take some time to help me understand this

  • @vamshikrishnareddy76

    @vamshikrishnareddy76

    Жыл бұрын

    lets say we built the backend in nodejs and deployed it to cloud technically we are renting the servers and then what ? will we choose the number of servers that we want to rent? does the backend language matter to choose the number of servers we want to rent?

  • @siddharthabiswas2147
    @siddharthabiswas21472 жыл бұрын

    Hey Hussein which playlist will this video lie in

  • @hnasr

    @hnasr

    2 жыл бұрын

    backend engineering beginners and Postgres and database Engineering

  • @aatifnazar8203
    @aatifnazar82032 жыл бұрын

    anybody has any idea how hikari handles this ?

  • @9709923699
    @97099236992 жыл бұрын

    So wanted to understand this why don't you have the connection assigned to a global variable and then use it in each api reuqest?

  • @hnasr

    @hnasr

    2 жыл бұрын

    This is a bad practice, when two different HTTP requests come and you only have 1 shared connection, they will served on the same connection as two different queries. If query 2 finished before query 1 than the result of query 2 might be returned to the first HTTP request which is a huge security concern. Unless you can guarantee the database supports pipelining (requests/responses are returned in the order they were received. Pooling is used to circumvent this by marking a connection is busy when a query is executing on it. Plus you can scale.

  • @9709923699

    @9709923699

    2 жыл бұрын

    @@hnasr oh wow thanks a lot for the reply never knew about this. So wanted to get suggestion how do I learn all these things? Does your database engineering course will help in this?

  • @piotrziarek9974
    @piotrziarek99742 жыл бұрын

    Wait a second Hussain - do we really need to begin a transaction/commit when running a SELECT?

  • @hnasr

    @hnasr

    2 жыл бұрын

    No you don’t need to, the database will start one for you always. And yes you can use transactions that purely read data to ensure consistency and isolation. I talk about that on my course

  • @lakhveerchahal
    @lakhveerchahal2 жыл бұрын

    Awesome

  • @clinton11994
    @clinton119942 жыл бұрын

    I have mongo db pool connection leak issue

  • @clinton11994

    @clinton11994

    2 жыл бұрын

    I resolved it by updating nodejs and mongodb lib to latest version.

  • @bhumit070
    @bhumit0702 жыл бұрын

    Hussein why don't you use ORMs ?

  • @hnasr

    @hnasr

    2 жыл бұрын

    ORM does many things behind the scene and result in many leaky abstractions. SQL is leaky as is, add ORM on top and you have a disaster. I like raw SQL where I have more control

  • @supportic
    @supportic2 жыл бұрын

    Well explained! Two questions: 1. Does every user has up to 4 connections or are those total connections which would be ridiculous. 2. Is release() the same as disconnect()? Or does the pool belong to a connection until you disconnect that's why you would need release?

  • @hnasr

    @hnasr

    2 жыл бұрын

    1) there isn’t a concept of users in HTTP, every request will consume one connection while it is executing. One user can make those 4 requests 2) no, release returns the connection to the pool so it can be reused. Disconnect destroys it so one else can use it. Good question

  • @supportic

    @supportic

    2 жыл бұрын

    ​@@hnasr Ok I got the first point, thanks. So is it bad to terminate every DB connection when the transaction is done? Say we have a user display a table with data from the DB. The initial connection was made with a query for the data which means 1 open connection and 1 of 4 pool connections being used which get released after the transaction. Do I keep the DB connection open or do I close it? Maybe the users wants to query more information.

  • @lakhveerchahal

    @lakhveerchahal

    2 жыл бұрын

    @@supportic I think stating max 4 connections means that you can have at max 4 connections in PARALLEL, if there's a 5th parallel connection made it'll go in pending and will be served only after one of the previous 4 connections are done and released. Also, pool is created only once when the server starts. It shouldn't be terminated or recreated again and again otherwise it defeats the purpose of having a pool.

  • @troooooper100
    @troooooper1002 жыл бұрын

    Follow up questions... 1) So obviously pool works for all connection spawning from the same machine. Two seperate machines can't share a pool, correct? 2) I read that pg db by default can accept 100 connections, but in pool you're hard coding to a number, i.e. 4, is there a way to make it auto scale and not hardcode max. 3) when you define max does pool automatically opens that much new connections? or only opens a new connection if all existing connections are under use and new connection request comes in? 4) how would you modify your code so even if db/pool/etc is hung up you can return meaningful http error 5) you said pool queries aren't atomic as pool.query commits right away... so what's the point of lines pool.query('begin')....pool.query('commit') doesn't that make it an atomic transaction?

  • @jackedelic9188

    @jackedelic9188

    2 жыл бұрын

    for pt 5, the second pool.query could be a different client. For the rest im also curious to know 😃

  • @troooooper100

    @troooooper100

    2 жыл бұрын

    @@jackedelic9188 Yea, but I'm wondering if I define pool max to be 1000, will it take memory from the get go, or expand only when necessary. And what can I do to make it expand until it hits bottleneck from db. i.e. DB says connection failed. Ideally pool should not need max, it should expand as much the upstream allow.

  • @m.m.4589
    @m.m.45892 жыл бұрын

    The pg library should do it automatically with commit, we can ask for this feature

  • @hnasr

    @hnasr

    2 жыл бұрын

    Nope, you could have multiple transactions in the same connection.

  • @lakhveerchahal
    @lakhveerchahal2 жыл бұрын

    Hey Hussein, can you also upload your podcast episodes on Amazon music 😅 Would love to subscribe your channel there 🙌

  • @hnasr

    @hnasr

    2 жыл бұрын

    can I really do that? 😅 didn’t know amazon music has podcast

  • @lakhveerchahal

    @lakhveerchahal

    2 жыл бұрын

    They just recently added the podcast feature, earlier it wasn't there.

  • @Themusicbiz
    @Themusicbiz2 жыл бұрын

    Wait your “back end” is “leaking”? lol jk

  • @TJHooper123
    @TJHooper1232 жыл бұрын

    Twitch engineers are taking notes.

  • @scottb2284
    @scottb22842 жыл бұрын

    Black belt.

  • @infinteuniverse
    @infinteuniverse2 жыл бұрын

    What can this be an example of? Starvation? You managed to starve a thread in a single threaded language! JK. Nice video.

  • @rxnniiee

    @rxnniiee

    2 жыл бұрын

    the rest of the application would still work, it only hangs at that single request endpoint since it's always going to be waiting on the exhausted connection pool

  • @foobar69
    @foobar692 жыл бұрын

    so RAII is a neat concept in c++ which ties the lifetime of an object to the scope. using that pattern in cpp to acquire connections would lead to automatic release of connections while exiting. but, that is not available in garbage collected languages.

  • @he2he

    @he2he

    2 жыл бұрын

    It is possible in garbage collected languages. For example in c# you would write "using var client = ...". The using would cause the method Dispose to be called at end of the scope.

  • @Foxy-yz2ld
    @Foxy-yz2ld2 жыл бұрын

    100M

Келесі