Observable Flutter: Live debugging!

Video Statistics and Information

Video
Captions Word Cloud
Reddit Comments
Captions
CRAIG LABENZ: Hello, everyone. Welcome to another episode of Observable Flutter or as we could call it today Debuggable Flutter. Actually, we're not really going be doing anything with Flutter today, just Dart. My name, as always, is Craig Labenz, and I am your host. And before I get started, let's move through all the housekeeping. Super important. Remember, folks, that it's very important to me, and I hope to all of you, that we live up to the standards of the Flutter community here. So we're under the YouTube terms of service and the Flutter code of conduct, so let's be very kind and respectful to each other, as always. And I think that's actually it. We're pretty much ready to go now. So I promised you today live debugging, and that is what I'm going to deliver. So it was brought to my attention by the creator of Serverpod, Viktor Lidholt, that there's a problem with connection pooling coming from Cloud Run on GCP going to a Postgres database. And at first glance, that may not seem like a giant problem to everyone, but it's basically a showstopper for deploying Dart code to Cloud Run that wants to talk to a database, because connection pooling is absolutely critical. So if you're not familiar with connection pooling, basically if you don't use it, then every single request you make to your app will probably naively spin up a new connection to the database, and that will both be slow and expensive, and could hammer your database with just crazy amount of connections. So connection pooling means that each worker node kind of maintains this pool of connections and reuses them, and actually stay open, so it's much faster to talk to your database, and the amount of connections going to the database is more consistent and more predictable. So connection pooling is a just best practice of all the best practices in server side development. And it's not working for Cloud Run and Postgres in the Dart Postgres pools package. So let's get going here. So this is the error that you get after a second request. It works after the first request, and then it times out. And this works from other environments, so like Google Compute Engine, GCE, if you deploy your app, there it works just fine. But from Cloud Run, we get this issue, this response. I didn't look at what the error code is here. That's not what I want. I want this. Then we'll go to Network. And I probably have to refresh here, so it might work the first time. There we see, because Cloud Run had turned off since I reproduced this bug last night. But now, if I refresh it's just going to work. What? Refresh. OK, now it's hanging. Does it work twice? That would be a surprising number of it working. Let's see. Randall says, connection pooling is "very important when the amount of work done to connect and disconnect dwarfs the actual query." Yeah, absolutely. If you're just-- and that'll be true for most queries that you want to run in a request response cycle. Any long query is, hopefully, just running on a clone of-- a read-only clone of your database that you use for analytics and whatnot. Anyway, we can see this hanging. It takes a while, so we won't get to see the status for a minute here. But then we will-- I want to make the-- there we go. OK, too big. We won't get to see this until it times out. It might be several minutes, but we'll see the actual status code eventually. So let me show you what I did to reproduce this bug, and then we can begin to debug it. And I have no idea where this episode is going to take us. I didn't even add a single print statement once I produced the bug. So it's all waiting for us here today. So I ran, last night, Dart Create dash t server dash shelf, and made a new server side app called connection pools. And let me go full desktop here, so I'm not blocking stuff. So I just created this app, connection pools, and then that generated some code pretty similar to this. But I just made a few changes to it. So in my pub spec file, I added the Postgres package and the Postgres pool package, that enjoy a nice little bit of version symmetry there. And everything else-- oh, I added GCP, but I didn't use it. I think I'm actually going to get rid of it. I thought it had a good logging utility, but I actually couldn't quite figure out how to use it. So yeah, I added these two libraries. And that is it. Yeah, that's all I did in here, other than the unused GCP package. And so then back in server, I made a Postgres pool. And this just comes, by the way, from the-- if we go to pub.dev, and go to packages Postgres pool, we'll see in the example what to do. So we make a Postgres pool. We pass it a Postgres endpoint. Set some settings. And then when we actually want to run a query, we access our Postgres pool, we call dot run, and that gives us a callback. And this is important because the pool has a fixed size of connections. And so let's say it has four connections as specified here, if we have five queries active right now, maybe because we've parallelized them in our app or there's just a bunch of requests coming in, that fifth one, while the other four are out, it's going to have to wait. It doesn't open a new connection. So it might seem like, well, that's slower, but over time, in aggregate, it's faster to do this, and way more performant, and easier on the database. So that fifth one is just going to have to wait a second. So of course, we pass a callback. And then whenever the pool has an available connection, it will use-- you know, it will kind of run the next callback in the queue. And hopefully, that callback actually runs a query, otherwise, then you're really just abusing your connection pool. And so yeah, and then we don't really have to worry about the futures thing that they're doing here. This is just kind of showing that you can parallelize these queries. So that is kind of what we were-- oh yeah, so that's the documentation. So back here. So I made a Postgres pool. And then down in the main method, which was generated by the command that I ran, Dart create, in the main method, right here-- let me get rid of that window-- I'm making a Postgres pool, and then I'm just kind of farming out the endpoint to a method. And the reason that I'm doing that is simply so that I can have some local host versus production branching. So if there's a DB host environment variable, then that tells me that I'm in Cloud Run. And so then I'm going to do this kind of arcane thing that I'll talk about to get my host for where the database lives. And if there is no DB host environment variable, then I just assume I'm sitting at my computer, right here, and then it's local host is where the database lives. And I'm just running a database from Docker right now. And then all of this stuff, again, I'll take a database name, and user, and password from the environment if they're available, otherwise I'll just use Postgres, Postgres Admin. And then of course, when I deploy to Cloud Run, I supply these environment variables. So I got this all working last night, just because I didn't-- reproducing the bug isn't debugging the bug, and wanted to debug on the show. But now, just walking through it quickly. So we have the PG endpoint that sets up the Postgres pool. I'm just using the same settings from the documentation. And then all the rest of this is, again, generated by Dart create. So I didn't touch any of this code. But then back up top here, now, we just have one route. So the home root says get time. And it runs that the pool that we made, the Postgres pool dot run. And we pass it a callback. And our callback is incredibly simple. We are just asking the database what time it is, not particularly high value usage of a database. But it will at least run a connection. It will use a connection, and run a query, all that good stuff. And then all these queries return lists of lists, because, in general, queries are going to return rows and each of those roles are going to have values. Even though our query merely returns a single value, it'll still be packaged as a list of lists. So we just kind of drill in, and get the time. And that was how-- well, we saw the time before, but now it is timed out and we've gotten a 504, which I guess, is just time out. I'm not going to lie, I have not seen 504. Yeah, gateway time out. OK. It's funny, I feel like normally that ends up being a 502 bad gateway. I don't think I've really seen a 504 very much. Interesting. That may even be our first bit of data to debug. I don't know. Let me look at some chat here. "Where are you from," Samandar says. Well, I'm originally from a little place called Michigan in an area around a city of Detroit. But I'm currently in California, where the weather is much warmer than Michigan. Randall says, "determining the number of parallel items is a tuning issue and will vary between setups. I've seen the number between 5 and 50 personally. You just have to watch where queries are mostly blocked." Yeah, database administration is incredibly involved. There's no-- you just have to get into it and look at what's going on. There's no other way around that. OK, also we see here the time was five minutes. And that's probably-- I wonder where that comes from. I wonder if that's in this settings variable here. Where are you? Let's go into the PG settings class, and see is there any-- oh, query timeout, five minutes. OK. So the library thinks the query is timing out. And that's another piece of data. So 504-- the server sent a 504. I wonder if the 504 was omitted, like, I wonder if the client bailed and set the 504, and nothing came back from the server. I don't know, these are interesting questions. I don't know the answer to that. Waiting for a response from server, five minutes. And then we did get a little content download. So upstream request timeout, that probably did come from the server. And I haven't debugged a web thing like this in a long time. You don't do this very often in Flutter. All right, so we have a couple of things to go on. And yeah, let's get started here. Where should we begin? Now, one thing that I already was thinking last night, but I didn't add that print statement-- but the first thing that I was wondering is do we even get into this method? Does the pool try to run another query? And I'm guessing it does, but I don't know. So let's just print running query. Seems pretty simple. Now, we get to redeploy to Google Cloud. So that was stuff that I also set up last night, because I didn't, again, want to kind of slow down this stream with that. So I have this makefile here. And this isn't a very Windows friendly pattern, because I don't think makefiles work on Windows. But they're still pretty nice overall. if nothing else, as just a bit of documentation for yourself on like how does this work, again. So there's two steps that we need to run to redeploy our code to Cloud run. The first thing is that we need to use Docker to repackage our code and upload that to Google's servers. And actually, it happens in the other direction, we upload and then it builds on Google's servers. And that happens with GCloud builds submit, this command right here. Now GCloud builds submit will use a Cloud build dot YAML file to specify how and what it builds. And that's just an implicit file name. If you have a different file name, then you pass a flag. So in Cloud Build dot YAML there's some more arcane stuff here. Actually, some commented code, because I decided to separate my build and deploy. So I'm just going to delete that right now. So there's two steps to this where we're going to build an image from our code using Docker. And then we're going to push that image to a container registry on Google's servers. And again, I'll just get rid of some more commented code. So if I run, I can either type GCloud builds submit or I can run my make command, and either of those will rebuild the app. So I'm going to do that now. And I'll use make. I'm going to say make build. And this syntax here means-- so the build command depends on the project command. And the project command just sets what project runs. So that'll it'll be redundant after every other time that I use it. But if you're switching back and forth between projects, it's pretty nice. And then I've just kind of set a couple of variables up here for myself. So make build. We'll see that it first sets that project, and then it just dives right into all of this stuff about rebuilding. We really don't need to study this output, because it is just a bunch of Docker output that is being streamed back to us from Google servers. Vikas asks, "what is the theme of this in VS Code." And it's a Synthwave 84. And it's really quite nice, isn't it? Synthwave 84. That will give you this juicy, purple goodness. All right, so Docker is still running, which means we'll read some more chat. "Makefiles date back to Unix V7 45 years ago." All right, so that's an interesting factoid. And my question for you, Randall, is did you have to Google that? Because, boy, would I have had to. Dayamoy, awe, well, shucks. I'm really glad to hear that. Thanks for the kind words. All right, we're still building. This is all, again, all happening on Google servers. And this actually important, building in this way, especially if you're using an ARM like Mac-- like I'm currently using-- then building on Google's servers is very important, because it builds with the architecture of your current machine. So the first time I got an updated Mac that has the ARM, Apple Silicon architecture, I built a Docker artifact. And I sent it to Cloud Run. And it broke. And it exploded immediately. And I got some very weird-- maybe the word architecture did appear in the error. I don't remember. But it was just, like, what the heck is this? And then realized, like, oh, my Docker file was built locally or my Docker image was built locally to run on an ARM architecture of my laptop, and then I sent it to Google's computers that presumably are running Intel architecture. And it exploded. So building on Google servers gets rid of that problem. OK, so the first step is done. We have built. And now if we just scroll down on our makefile, again, I was building and deploying kind of separately. I didn't want a deploy to be dependent on a build. So I have a separate command here, deploy. And it depends, again, only on project, not on Build, again. And it just runs this big long deploy command. So make deploy. And this will now run that command, and deploy the app. And this is very fast, because it's just moving-- everything's already on Google servers, it's just kind of hopping over into the Cloud Run spot. So it's pretty fast. And we're done. Great. So this should also mean the old worker has definitely been turned off. So the first request, it should work. And this means it will get to look at some logs. So I'm going to refresh the app. And we got a time. And when I look in my logs, let's have this refresh. I think, yeah, it's still just showing the logs from last night. There we go. Now it's loading at the bottom here. And when I scroll down, we see running query. Great. Well, it's funny that running query came through-- oh, it basically-- oh, no, listening-- server listening on port came through after. How did that happen? Server listening on port comes from the bottom of the file. I mean there's some-- you can't really predict the order of log statements sometimes, but this is a real surprise that this came 6 seconds after, right? 9:19:48-- this is California time-- versus 9:19:42 for running query. I have no guesstimates about that. Anyway, let's see when I refresh and it hangs, do we get that log statement again? It's hanging. And let's try to refresh our logs. Nothing. No new log statement. All right, this is our first data point. Run is not-- here we go. The callback is never executed. Hmm, let's dive into run and start to see how on Earth this works. So it runs a function outside of a transaction. OK. There's probably a sibling function that runs it in a transaction. And so here's our function that we pass, retry options, or else, retry if. Interesting. I mean, I don't think we want to retry after a timeout, and then we'll just wait for another round of timeouts. So I don't think that's going to help. Trace ID, session ID. If we get further into this and we make some progress debugging this, maybe we'd use some tracer session IDs. I'm not sure where those end up going. OK, so there's retry options, yeah, so we're probably using some default, like, don't retry that comes off the shelf. But anyway, so retry options, dot retry. And so with connection, let's think here. I'm guessing with connection is-- so I think if we put a print statement here-- and we're editing code not in my app right now, so this is generally a bad idea. But actually, I might have to pull this in. If I want to add print statements to Postgres pool, I might have to just copy the code into my own app and pretend it's my code. But anyway, I anticipate that coming shortly. I bet if I put a print statement here, it would print. And then if I look in with connection, that's where the error would be. Because there's just nothing here that could have really broken yet. I mean, I guess retry could have broken, because this is also taking a callback. Let's peek at retry. Yeah, so we're looking at some attempt logic. And this will be attempt zero. And then it just immediately runs the function. Yeah, so we don't have any retry logic, but that doesn't kick in until after the function runs, so that's not the issue. So it is going to be in with connection. So I'm not going to bother making the print statement here, deploying, seeing it. I just see no way that the error could come before this with connection function. So as we dive in here, executor scheduled task. OK. And then this says use or create. So we have two things to look at, scheduled task and use or create. Oh, you know what? Also, there's a couple other high level things that I want to check. I was thinking about this as I was falling asleep last night. I want to check some high level networking things in Google Cloud. I can't believe I almost forgot this. So let me open my database page. We're going to come back to this, but I want to rule out a couple other things. Because, remember, we know that this code works from Google Compute Engine. So if there's a bug in here somewhere, it's very subtle and sneaky, and it's just some intersection between this code and, like, Cloud runs networking, or something, right? It's going to be very specific kind of narrow needle to thread. So I do want to rule out a couple other things. So I'm going to switch over to the My Database Settings. I didn't actually mean to close that. So I'm going to keep my logs open. And then in this tab, we'll switch over to Cloud SQL. And in this pool's database, I want to play with a couple other things here, like getting rid of the public IP address. I don't know if that's going to change how this works. Also, a little primer on database connections with Postgres. So remember there was, back in server dot Dart at the bottom, I had all of this nonsense. And if you've not connected to a Postgres database before, especially on Cloud Run, or just using the socket file, you might see this and think, I don't know. So what's happening here is a common way to connect to databases, especially Postgres, is through a socket file that sits on your computer. And I'm not going to lie, I don't even know how socket files work. I think they're black magic. But this socket file, it's not even really a file, it's like a fake, virtual file. And it's essentially just a connection to some other computer, and it keeps that connection open. And when you have a database connected to a Cloud Run instance, which we can see. If we go back to Cloud run, and we look in our edit and deploy, blah, blah, blah. And then I scroll all the way to the bottom, we see here that Cloud SQL connections is pointing to this database. So this bit here is enough to tell Cloud GCP, like, hey, these two things should be allowed to talk to each other. Cloud Run is on the list when he shows up at the database's bouncer, it's like what's your name? And he says Cloud Run, they're like, OK, you're in. So they're able to talk by way of this connection. And then when you do that, the next time you deploy Cloud Run, by the magic of all the stuff going on behind the scenes, it will add a socket file at this exact location, and it's slash Cloud SQL, so from the root of the runtime operating system, slash Cloud SQL. And then all of this is the name of your database. So that comes from your database page, and it's literally this connection name right here. So I'll just copy this in. And we'll have a commented version of this. That not how you make a comment in Dart. So it's slash Cloud SQL, slash all of that, and then this part is actually just pure Postgres. This is very standard Postgres stuff, so this follows you everywhere you go. So at this location, on your running Cloud Run workers, there is a file right there. And you can use that as a host, just like an IP address, just like a domain or something, right, if you had mapped it that way. Even though it's on your computer, you pass it as the host. And then you'll often, in the wrapper libraries that set all this up, you just tell it that it's a UNIX socket, and then they do some other things differently based on that. And again, I'm deriving whether or not this is a UNIX socket, again, by the presence of this DB host. This is just a convention that I invented for myself in this project. This is not a global concept. So anyway, that's how we're connecting. And this makes me think-- right, remember, I said that this connection, the socket file acts like a host, like an IP address for your local code. Well, I'm just wondering-- these things are timing out, something's going wrong with the connection. What if this didn't have a public IP address? Would that put it in a different profile in terms of the Google Cloud networking setup that will maybe unlock this? I have no idea, but I want to find out. So it's been a minute since I've done this. Maybe it's in the connections tab networking. Yeah, I'm going to unassign this public IP address. It will no longer have a public IP. I have to select a type of connectivity. I hope if I put on private IP address that the socket will still work. Yeah, because now I'm going to need to add a VPC. And I can, I guess, add the Cloud Run instance to the same thing. Let's see here, I don't know. I'll put it on my VPC. I have no idea how old that is. This sandbox project is not new. Sure. My VPC. I could be barking up the wrong tree here, don't know. Let's see. Kuasai says, "I'm curious about the code created in one file, how many lines of code?" Which file are you talking about? And I'd love to know what you mean there. Well, let's see. There's a whole lot of chat going on here. Are you guys solving the issue for me here? Oh, Randall, didn't have to Google it. He was there. Don't tell me to do that, when I saw it happen. First typed to ls in 1977. Randall, I was a mere negative 9 years old at that time. All right, there's chat about background tasks, OK. I'm going to hide myself here. We see in the bottom right that this change that I just made is going through. So as soon as that's done, then we can try to reconnect and see how this goes. I don't know, I might have to redeploy Cloud SQL-- or sorry, Cloud Run. I might have to redeploy a Cloud Run if the connection path to the database is different enough. Let's see. Hopefully, this finishes quickly. I don't know if it will. While it runs, I'm going to just try to brainstorm other things that could be going on. Let's look at the code some more. Postgres pool. Where were we? Why are we all the way down here in this file? Where was run? There's run, there's with connection. There we go. All right, so yeah, we had two new players here, the executor and scheduled task. Also, concurrency, that's interesting. So settings concurrency is written to executor concurrency. And this is the maximum number of concurrent sessions, which defaults to one. But we remember that it had been upped to four in our settings. So four concurrent connections we shouldn't be approaching that, as this is merely our second query, and the first one is done. So it shouldn't be an issue there. OK, back to run. And then back to with connection. All right, so that's on an issue. Executor scheduled task, let's see what this does. It schedules an async task and returns with a future that completes when the task is finished. The task may not get executed immediately, indeed. So we need to find the implementation of this. Abstract executor, so what is our kind of executor? Let's find where this comes from. We just saw that this was abstract. Yeah, you-- oh, there's a factory, which implements-- oh, it sends underscore executor. OK. How do you work? You must be in this file. You were. You're all in the implementation file, is that like a part situation, part implementation? Yes. OK. What method are we doing on the executor, again? With connection. So it's executor scheduled task. So let's look at scheduled task. Scheduled task. There we go. All right, so we're not getting this error. So it makes an item waiting. OK, this is just a list. And then trigger is probably going to try to run something off the first thing. Oh, man, it just doesn't feel like the error is going to be here, you know? Because it works on GCE. Still kind of instructive, still a little educational. So yeah, make sure that we are following the concurrency rule. And waiting is not empty, because if waiting is empty, then we can just get out of this method. Something about rate. If rate does not equal null, that seems like it's fine. A little bookkeeping on how performant these things are, presumably. We have timeouts, right? So we need to know this. Started is not empty, right. So it's kind of a queue essentially. So we're going to take the first one off, and then run it, make a timer. Looking good. Started. OK, so we put it on a list of things that are in flight, and then we continue. And use a completer. I always love using completers. I think it's really, really fun to use a completer. All right, so then it goes into running. And we call trigger, again. I don't know. It's a little confusing. I don't think the problem is in there. Let's see if our database updated. Nope. Still killing time. Anoj, we are debugging a problem with connection pooling from Cloud Run to Cloud SQL. And I have no idea where the error is. It may be so hidden from us that we never find any hint of the problem in this live stream. We should all be comfortable with that possibility, especially because I've just made this setting change. It's taking five minutes to propagate. And I don't know what it's going to look like to flip it back. And it's still going. All right, so anyway, blah, blah, blah. I just it's really hard to imagine. I haven't fully passed this into my brain yet, but I don't think it's the issue. I don't think it's what's going on. All right, someone says-- Sarbador says, "what is that scheduled task, recently I got that problem in debugging." What? What do you mean? Say more. Identify yourself? All right, scheduled task. Let's go back to this. That's where we were. But I went into trigger there briefly. And then we're waiting the completers future. That's good. Result item, so time out exception. But we did not get this exception. We didn't see anything about this. Except I don't know if that would have really been logged. That's interesting. OK, let's keep that in mind. And then we await the task. So now we're finally-- is closing. Who are you? Where do you come from? Closing, does anything set you to true? Close, we didn't call close. OK, that's not it. That was an efficient rule out. This was scheduled task. OK, so it's not that issue. It's not is closing, so we can jump down here. We await the task. We mark it as complete. I love completers, they're just so fun. I mean, it doesn't seem to complete. So I think we're awaiting the task. But this task is probably just the method that we passed in, right? Yeah, so scheduled task came, it was passed into scheduled task. And scheduled task came from-- oh, it's our method, which was body, wrapped in this use or create. Oh, oh, oh, use or create. This is going to be use or create a connection, because this is a connection pool. [GASP] This is crazy. This is crazy. CryptoWolf says, "what's the exception?" Well, we didn't get one. It just timed out. We got no logs. Nothing. "Maybe some networking like virtual networks and access rates." Well, the first query works. Yeah, these are good hints. These are very good ideas. You may have missed hearing that the first query works, then at times out. This also works from other runtimes, so GCE, it works. GC-- I don't know if Cloud Run has an acronym. But from Cloud Run, it just starts to hang out only when we use this connection pool. So let's see if this database is updated. It is at last. And I don't even know, it might have just broken our whole runtime here or our Cloud Run app. But we're going to find out. So if we go back, there's some button that's just like deploy again, even though nothing changed. Oh, it's literally right there, deploy. So this will just kind of rebuild everything. And it will allow us to try to reconnect to the database. One thing I don't remember is, like, I think the socket will still work even in the private IP universe that we just jumped into. I think this will still be fine. But let's find out. OK, we've updated. So I switch back here, I refresh. It's timing out immediately. So I'm probably just in a broken state. I don't think we should read too much into this. Maybe when you have a private IP, then you can't use this. Oh, no. No, no, no. No, no, no. No, no, no. We put this on a VPC. Yeah, I put it on a VPC, MY VPC in fact. Which means I probably need to do the same thing here. So if I go to a networking, VPC network none. Yeah, OK, how do I edit you? Probably here. Let's edit, networking, there we go. VPC. Why are you not showing My VPC? Connect to a network. I have to make a connector. I really don't like these. I just never know what I'm doing. On my Boring Show, doing a bunch of this stuff with Simon a year ago, we got to a VPC connector. And I was just like, Simon, help. And thankfully, he knew everything off the top of his head, which was really helpful. OK, so I accent, I added the API. Serverless API, make a connector. I don't think I really want to do this. All right, let's try briefly, at least. West2, that's what I picked. So this is My VPC connector. The network should be My VPC, very good. The subnet, I have no idea. Can you just pick a reasonable default for me, please. Create, no. I have to pick a subnet. I have no idea. Custom IP range. Can I just copy this default? I don't have a clue. Maybe that'll work. Don't have a clue. Don't know anything about networking. Tec-X has noticed that the blue of Flutter is very similar to the green of my green screen. Good eye. Randall says, "the problem may be a reuse connection. Maybe there's some meta information that GCR does differently." Yeah, I mean, I think it's going to be something like that. Just don't know what. All right, so we're making this connector, which then maybe means here. Access resources, learn more or create-- so you can learn more or create a serverless VPC connector. I didn't. I could have learned more, I guess. I'm a slow reader though. All right, let's go back to what we were looking at. All right, we have this. What was it called? Get or create or something? Use or create? Use or create, yeah. So this method was called, and it takes our function. This is what we passed in right here, up to body, this goes all the way back to this code here. This method is passed into run. And in run, it works its way down to with connection. And it's the function because this is what it's called here, function. So with connection takes a function that wraps our function. Woo, callbacks on callbacks on callbacks. So then we get into with connection. And so this is already, again, a wrapped version of ours. And we get or create-- or sorry, use or create. That's the body there. So this is a function wrapping our function. And that function that wraps our function takes the connection, which makes sense because it's got to pass that into our function. Not confusing at all. And yeah, this looks interesting. So try, acquire available, or await open. This seems like maybe where the code is going to hang. So I'm going to, at this point, I think, copy this code into my directory and add some print statements. Let's try it, because I'm not going to be able to add print statements here, and then ship them up to the server and have it work. That isn't how Docker works. Remember, it's going to build for my pub spec dot YAML. And so even if I edit my local copy of Postgres pool, it's not going to do anything when I ship it to GCP. So I'm going to open this lib directory here. Can I, like, is there a show in Finder or something? How do I do this, please? OK, we're going to have to do it manually. So what is this? We go to pub cache hosted pub dot dev hosted-- here we go-- pub dot dev and Postgres pool. Postgres pool. Here we go. And I guess I'll go into lib as well. So this is-- and then it's all just in one file. Oh, what is executor in? That's in a different thing called executor. This is getting complicated. Randall says "copy path on right click on editor window tab." Oh, right here. Copy path. Nice. All right, so I also need this executor. How much do you have? It's also just one file, plus the implementation. Why did I-- oh, right, no. It's two files. All right, we're going to move all these over. It's going to be a little tedious. So I'm going to copy Postgres pool to my dev folder, my-- where are you? Where am I? Don't remember where this-- dev folder. I think it might be in my Flutter dev reel folder. Connection pools, there we go. There we are. And then I think it's the same folder again. And then we go into bin. And there it is. Copy. OK, one down, two to go. So now I'll go cd out of this, out of Postgres pool, and into executor. OK. And then if we look in lib, we just see its source and executor. So I'm going to copy the lib directory to again out of this. No, do I have to-- how do you copy it? It's like copy recursively lib? I don't know if it's going to work. Out of the executor folder, no. Root dev Flutter dev rel connection pools connection pools bin. Let's see if it works. Well, OK, great. So now let's just cd to that directory. And look what we got, ll lib Postgres pool. Oh, my gosh. I did it right. Crazy. So now, I'm going to close these other files that I have apparently edited. OK, now you're unedited. Now you're unedited. And I'll unedited this. Great. So in my-- let's lower this. And oh yeah, my connector. Let's check that out. Did the connector land? All right, My VPC connector, looking good. So I want to make connect to a network. Maybe have to refresh this page, because I made the VPC connector after loading the page. Network. Do you see it? You do. Wonderful. All right, only requests to private IP through the VP connector. That seems correct. Let's try that one. OK, and now we continue on our end. So in our pub spec dot YAML file, I'm going to get rid of Postgres pool, which included executor. Keeping Postgres, even though I'm not really using it. And now, in server dot Dart, we don't import Postgres pool. We import local Postgres pool dot Dart. And in local Postgres pool dot Dart, we just import local executor. Oh, we need retry. Son of a gun. I was so close. I hope retry is small. OK. Import. I guess we should go here. So this will be local executor. Oh, no, it's lib. I put it in lib for some reason. It made no sense. Lib executors dot Dart. So that gets rid of you. Now I need to do retry. Boo. All right, I'm going to go back to the previous directory in my terminal. And I'm going to cd out of this and into retry. OK, please be small. So we do lib. One file. Copy, retry to development Flutter dev rel connection pools connection pools. Then go. OK, go back. Oh, I cd'ed into the lib directory, so my go back one is broken. Dev Flutter dev rel connection, connection bin. There we go. We got retry. So now this is just retry dot Dart. This is crazy. I'm probably doing this-- have you ever heard the expression go around the block to get next door? Pretty sure that's what we're doing here. All right, I wonder if this will work. Do you think it'll work? I have no idea. Refresh. Cool. It worked. So we're on the VPC. I'm going to refresh again. Is it going to work? No. Changed nothing. Good though, we ruled it out. Actually, it's bad, because if that had been the problem then we could have just filed a bug. But we're still hunting. So OK, so Postgres pool is now fully migrated. This is some exciting stuff. And PG end point. Right, we were in executor. So we went from the run file into with connection. With connection had use or create. Oh yeah, we're still in the same file but we needed to-- that's right. That's right. That's right. We just needed those other dependencies. OK, so I'm going to perform a little surgery here. And I just want to say, so this is like, we'll call this option one, OK? Also, I'm an out of print here. And this is in use or create. So we have option one. And then we're going to have option two. And this is option one or option two. And then we can say resolved option one with option one. We don't need those curlies. Resolved option two with option two. And then our context is context. Oh, we're talking about cascades. Yeah, cascades are pretty great. So cascade is a shortened version of-- it basically is a Dart language way of allowing everything to chain. So we'll have a real quick demo into this here. I'm going to keep this fast. Language Dart. So normally, let's say you have some class, A, then you have a method void, whatever, right? And then you have a version of A, so a equals A. Then there's some attributes as well, or something, right? So let's say there's an int count equals 0 or something. Let's say you want to call whatever and set the count, well, you'd kind of have to do A dot whatever, and A dot count equals 5. So this is three lines of code to do that. Cascades allow everything to chain. So if you want to void whatever to be chainable, first of all, you'd have to return this. And then you'd say this is A, is now my return type here. Now, I can say A dot whatever dot count equals 5. This would work, right? So now I'm down one line of code. But I couldn't go in the other order, and what if that was important? What if I needed to do this? Because I have kind of not good code, and this depends on count. So this code is bad, and you shouldn't write code like this, where there's this kind of sneaky dependency on the whatever function on the count. But you know, we all write bad code. So A dot count equals 5 dot whatever-- like, this is just incoherent, right? So we couldn't do this in one line of code, even though whatever chains. We're back to A dot whatever. So cascading says whenever I type 2 dots for this, then at the end of this statement, actually, just like the thing that's available right now in scope is whatever I started with. So A dot count equals 5-- like if I say dot here, what am I working with? It's A, again, it like pops one off the stack. And you can just do that for methods. So A dot whatever means if I say dot here, what is IntelliSense going to give me? It's not whatever was returned by whatever. OK, this is getting awkward, a little who's on first. It's not the item that was returned by the whatever method, you know, unless they happen to be the same. I don't really want to save this file, now do I? It's not the item that was returned by the whatever method. It's A, because it pops one off the stack. So that's cascading. And it's nice. And it allows a single line of code here. You can even do it off-- [INAUDIBLE] constructor it was obvious. So we make the settings object, and then we set the connection age. But we pop that off the stack, and then we just set the concurrency all in one line. And there it is. That's cascading. All right, where were we? What are we doing? I really should have started the build before I went into that. Really, really should have. All right, so we have updated. We've added these print statements. I'm just going to say make build and make deploy. I'm in the wrong directory for that command. OK, what should we talk about? Folks, we have a few minutes to kill. Good question, by the way. Yeah, Randall says they are required reading or required listening if you happen to be watching right now. Yeah, Yageshrin, says, "you might have to fix the import for executor implementation." Oh, boy. Thank you for paying attention. That isn't in this file. That's probably in executor dot Dart. Did I not fix that? Oh, I did. No, oh, it just happens to be the same. That's why I copied the folder over that way. Yeah, yeah, yeah. Good eye, though. Yeah, because it wouldn't even compile if that was the case, but great point. Karan, there's not a Discord for this, but Flutter has a Discord. And if you go to Flutter.dev/community, you too can find a link to that Discord and join it. I've not thought about making a Discord for Observable Flutter, honestly. I kind of feel like we're all in enough slacks and Discords. That's my initial thought on the matter. Terminal. Oh, nice it was fast. It's almost done deploying. Almost done. So let's remind ourselves what we're hoping to learn. In the use or create method, I theorized that we were getting blocked on getting this connection that we're either acquiring an available or opening was bombing. And I just want to find out exactly where in that dense line of code this was happening. And so I broke it apart to see where it's happening. And the deploy is done. And we're back at it. So refresh. It works. Let's see that our log statements worked by going back here, looking at logs. Scrolling to the bottom. OK, we see it. So resolved option one null. This absolutely makes sense for the first request, because it make sense if there wouldn't be an available one yet for the first request. So this instance of-- this is really not very helpful other than whether or not it's null. So we got all of our statements. Absolutely makes sense. And then running query happens at the end, of course, because after it does all of this stuff, it finally gets to our method, which is where I added the running query log statement. So now I will refresh again. And it will hang. And we will read the logs. Coming in after 9:58:35. So scrolling. So refreshes. The problem is before this method. Wow. The problem is before this method. Really? Nothing. What does this mean? Double rainbow. What does it mean? OK, so use or create is not the issue. Scheduled task may be. We're just going to add things everywhere. Print in the scheduled task. Print. With connection. I'm shameless now. I'm so shameless. Print in with connection. OK. We're not calling close. Let's go back to run. In run, remember when I ruled out that it could possibly be retry, because that'd be nuts. Print-- we're going to find out-- in run. And here we are. This is where this has to go. So this is with retry, we'll call it. OK. Think a typo c with ctx. That would be in use or create. I think you're talking about the difference between this variable here c and ctx here. But they're actually different variables. So this is just a function that accepts a ctx, and we're calling it c. Apologies, by the way, if you're thinking of something else and I've misread what you're talking about. But this is a function that accepts a connection, and it's called c. And we're first building that connection and storing it in ctx. And then I bet later-- yeah, we pass it into body. So body takes a connection and we are passing in what we build. But it has a different variable name. Oh, we could-- wait, no, I was going to-- I almost got excited. We could put things down here, like even in this timeout. But we don't even get these print statements. So we're not going to get down there. Dinesh says, "could it be a problem with the web server running your container." Yeah, it absolutely could. That's totally, totally possible. And it's quite-- I mean, it's kind of likely, actually. Because this works on a different thing, it runs on Google Compute Engine. I have not re-created that. But I was told that it does work there. So I just trusted that. All right, I'm going to add a couple more prints, because they're cheap. Oh yeah, I have another thing I wanted to test. Because I was so surprised that even happened. Let's just do a little more cascading here. And let's say echo. Actually, I don't even really care. This is just like-- we'll say marco. And then I'll make a new method here. It's just a response called polo. It gets the request. And it returns a response. OK, polo. What's your problem? Oh, get. You have to write actual code. OK, I want to make sure that the whole thing isn't broken. Because I really thought we were going to get those print statements. I think the marco URL will work, but we're going to find out. OK, so I'm going to deploy all of this. Chat with you find people again. What do we think? Should we take bets? What's the last print statement we're going to say we're going to see? I'd love everyone to place your bets. So running query is the very last one. We know that has no chance. We already saw that all of these didn't work. I kind of wish I had done this before. So those are all out of the running. We have, first, it drops into run. Well, that's not run, that's run transaction. So first it drops into run, where the first possible print statements appear in run and with retry. So those are the first two answers you could give. Then it goes into with connection, where two more contenders enter the arena. The last print statement we get could be in with connection, if that's the case, it means the schedule task shenanigans fail, or the task is scheduled, and this would be the last thing that we run. "DebugPrint versus print. My linter doesn't like print." Yeah, for sure. DebugPrint is better for local stuff, but I actually want this to run in a production build of this server. So I have to use print. Normally, you don't want print statements in your production build, but we are deep in the weeds. Hamza says, "what are you exactly building?" Well, am exactly debugging a problem connecting to a Postgres database from Cloud Run using the Dart Postgres pool library. The library works from not Cloud Run. So the library may be super-duper innocent. All right, we're deployed. So I'm going to refresh. And we'll see all the print statements. I'm going to go back. And I'm going to look at the print statements. And we look at the print statements. Here we go. OK. In run with retry. In with connection. Scheduled task. In user create. It all works. Lovely. Moment of truth, folks. Moment of truth. I refreshed the wrong page. OK, the real moment of truth. Refresh. Switch back. Is that a band? Isn't there a band called Switch back? Nothing. Are you kidding me? Whoa. Now, we try marco. It's hanging. We don't have any more debugging to do. Wow. Wow. In the words of Owen Wilson, "wow." I'm just so blown away. Wow, we're done debugging. Great. What else do we want to do? We have quite a ticket to solve-- or I mean file. Someone else's to solve. [GROANING] Nothing. The whole thing dies. I'm blown away by this. How is that even possible? What if the concurrency is one? Build. "How do we connect our app to a running app to extract data to have something similar to DevTools?" Oh. "Can you make any tutorials on creating a DevTools replica?" Well, so you can-- basically, the truest answer to this is I don't really know the answer to how to make a DevTools replica. But I can tell you some surrounding knowledge that I have. One, you would need to talk to the analysis engine, or the analysis server in the engine. I don't even know how to connect to those things, to be honest. Two, in terms of wanting to do this, there's a whole team, like a sub team in Flutter, of incredibly talented engineers, who build the DevTools. And it will be an effort to replicate any percentage of what they've done. If you're envisioning something that is missing, you could potentially file some feature requests. I think you'd probably get your feature request faster than if you cloned-- if you did your own. Also, all their code is on GitHub, actually. Flutter DevTools GitHub. If you don't want that non-answer, the real answer is all somewhere in this repository. But I have never looked at this repository. "What if your Dart server died? Can you check inside the container logs?" Well, yeah, that is one of the issues, no more logs ever show up. So finally, some more showed up, because I redeployed. But nothing happens, nothing happens after that. All right, I'm going to refresh again. Wait, no. Let's start with marco. No because the page never resolved the URL, didn't get saved. I don't know why that's a feature. Marco. Polo. Great. Now, I can refresh. Cloud Run works. Good. I just spammed the lungs that was wasteful. OK, now we refresh here. And we get a time. Lovely. Now, concurrency one, does it help? Refresh marco. No. It doesn't do anything. Refresh hangs. Wait. No, that came back. Now, it hangs. We saw that once before, where it would like do one more, and then hang. So it's actually slightly intermittent, because it did a time or two before not hang until the, like, third query, instead of the second. I think we have video proof of that in this very stream. I said, "this is hacking." I might have mumbled. Does anyone remember what I said? "This is hacking." I don't remember saying those words. But I'd love to explain what happened when I said something that sounded like "this is "hacking. If I even know what happened. This has been an exercise in me not knowing what happened. All right, so we're going to get all those. Yep, this is when I was just spamming control-r on the marco file. That worked. Then we ran one query, and then we got that second query, and then it died. Super weird. I have no idea. "Can you Zoom in to Cloud Run log?" That's what we're looking at right now. But, oh, zoom in, right. I'm zooming in on the wrong screen, because I'm a genius. Yeah, sorry. Good call. Good call. Hey, Viktor. What have you seen? We have uncovered some shenanigans, let me tell you. These are proper shenanigans. Viktor, would you be surprised to learn that a simple HTTP request also hangs in this state? It's not just an attempt to load-- to run another query. I added this URL, Viktor. Wrap your mind around this. Marco just sends back polo. Little summer, pool shenanigans. Hangs. All right, well, I think we can basically call this. And I have a bug to file. Does anyone have any other ideas? Oh, Viktor, you had said another thing. "Perhaps it used another instance when it works twice." Oh, that's totally possible. But it would be really surprising if Cloud Run scaled so early, right? But that's totally possible. Oh, that's a good idea. That's a good idea. That's a good idea. We can dive into this more, because I bet we're going to-- no, I don't bet. I have no idea. But we might get some resource. Are you going to tell us anything? Cloud Run revision labels, like, our vision name. I'm wondering if we're going to get an actual worker ID that could be differentiate-able to tell if it did scale, this is on Worker 5, this is on Worker 12, you know, if you had a ton of traffic. Revision name, location, ID, no. None of this it would seem like it's going to differentiate. Oh, instance ID. Hey, hey, good stuff. OK, so this starts with 00F. Let's go up to the same thing here. 00f, OK, and it ends with 0b1. And that ends with-- where'd it go? It is a different instance ID. Genius. Not surprised. I wonder if it just does that when it launches as like some precaution. Interesting. Yeah, I totally agree. It's something with Google Cloud Run, and the proxy, and the database. Another thing we tried, Viktor, was getting rid of the private IP on the database. So I put it-- sorry, getting rid of the public. So I put it on a private IP, I added it to a VPC. And then I made a VPC connector. And then I added Cloud Run to the VPC via the connector. Didn't work. Didn't help. Randal, "print some sort of process ID or hostname each time." Well, I think we did end up getting that. Though, that would have been what we would do next if the instance ID wasn't in the logs. But we got in here labels, instance ID. This ended in e3f, and if we go up to the previous one that worked, as well-- and we're like, why'd it work twice-- does not end in e3f. It's a different instance ID. Oh. These are good theories. These are real good theories. Proxy needs a public ID according to the docs. Well, those docs are wrong. I think-- yeah, those acts are just stale. It needs a public IP or to be on a VPC with something, and then it can access the private IP. I don't know why those docs are so abridged. Huh. Huh. Huh. Well, we learned something. I have a ticket that I can file that I believe will-- I wonder how else I could get-- how else could I get that fatal error? Oh. Oh. Oh. Oh. Oh. Oh. Remember-- remember. But we're not getting-- I wonder if it's like here somewhere. I wonder if it's somewhere down here, right? But the problem is if it errors here-- oh, this would be like on the way out. OK, this is so odd. This is so odd. It would have to be in it's not-- the first request completes successfully. So it would be extremely surprising if adding any print statements to all of these try catches actually printed, right? Because if we go to the logs, we see the final, like, all done here log, right? So this means we win. That request is complete. OK, I have, like, a lot of thoughts popping in my head here. So that request is complete. So the trailing try catches that are not in that file-- are they in that file? Well, they're literally highlighted. All these try catches are not going to print. And then on the next request, nothing before them prints. Nothing prints. So the proxy crashing the instance is a quite good theory. And it would happen like if that's what's going on, it must be happening completely outside of our request response cycle. Oh man. Man, oh man. All right, someone's going to have some-- Cloud Engineer is going to have some digging to do. Viktor also says, "I think it breaks when you do two requests on the same connection possibly. The pool will reuse the first connection. Or maybe fail to close the connection." Yeah, I totally agree with that theory. Absolutely. Yep, totally agree. I think that's what it is in some manner. It's-- well, I don't even know. No, no, no, no, no, no. No, I think that's actually not it. I think it's more back to this, because marco doesn't even work on the next one. So it's like it doesn't even get a chance to reuse the connection. It could be this one-- fails to close the connection and fails in a fatal way. Or return it to the pool, right, the whole point of the connection pool is it doesn't close it. "Tried to use one connection. It still broke." So I did a similar thing with setting concurrency to 1, is that what you did? Yeah. And we're still in that world. I set concurrency to 1. I'm guessing that's how you tried one connection. This is just bananas. It does feel like there's simply a networking issue. Something's going on. All right, well, folks, we debugged. And we didn't fix the bug. We don't have a PR to submit anywhere. But we have an issue to file. And if nothing else, when people say an issue with a good minimal reproduction is a huge contribution, they mean it. Because you just saw how long it can take to do enough investigation to be able to file an issue with a minimal reproduction. So without the pool, still broke. Oh, that's a great test. So the pool has nothing to do with it. It's totally in the-- it's got to be in that proxy. But you know what? This doesn't happen in other languages. It could be a hand-off between the proxy and the package, except it also doesn't happen on Google Compute Engine. It only happens on Cloud Run. So it doesn't happen in other languages. It doesn't happen in Python, doesn't happen in JavaScript. If it did, then that would be the biggest bug in the history of Google Cloud. So it's some handoff between some, like, aligning of the stars just on Cloud Run. Probably the raw Postgres library, because Postgres still uses Postgres library, and you just replicated this bug without the pool library. So some hand-off between the Dart-Postgres library and the proxy. But it's somehow activated only by-- wait. No, I have another test. I have another test we have to run. (SINGING) I have another test. I have another test. La, la, la. I have another test. What am I doing? I wish I still had a public IP. I wonder if it's faster to make a whole new database or switch back to public. Oh, maybe we can do both. I'll just add a public IP. That still will probably take a long time. "Didn't use the proxy on GCE." That's exactly what I want to test. I want to connect, not via the proxy, via, instead, the public IP from Cloud Run. Actually, I kind of vaguely remember that doesn't work. I wonder if we can get the private IP. Does anyone know how to get the private IP of something on a VPC? I do not know how. But I kind of vaguely remember that you are always blocked from using the public IP on Cloud Run. This sounds familiar to me. "Have we determined whether it's two requests on the same run dies, or to requests quickly from two separate runs dies?" I think we have not. Well, let me think about this before I just say words. Same run-- I don't think the speed matters. I don't think quickly matters. So two requests, same run, or two requests quickly from two separate runs. Oh, that's another interesting point. What if we ran another query in the first request. That's another great test. Yes. Let's duplicate this. And whatever. And now here we'll print. Request one done. And we'll see if it hangs but we get request one done. This is an excellent test. "It seems really tricky to use private IP on Google Cloud Run." Yeah, I vaguely remember this. OK, request one done. Did I break anything else or can I just deploy this? I don't think I broke anything else. Deploy. What do we theorize here? I think we theorize that it's going to break, right? The first request here is going to break. That's the theory. If the whole thing comes back, we're shocked. I'm shocked at least. I won't tell you all to be shocked, but I will be shocked. Yeah, I think this isn't going to work. What would it mean if it does? It would mean that there's some-- see I'm still using the pool. But you created-- you, Viktor, replicated this bug without the pool. That means it's not, like, when the pool puts the connection back in the pool, because you recreated the bug whether that whole system wasn't in play. So I don't think it's going to be like some end of request cool down phase, because the raw Postgres library wouldn't have any concept of that. It has no idea about request response. So I think it's just going to be either the closing-- Yeah, it's going to be the closing of any connection, the termination of something. That's my theory, if this all happens, then-- well, it actually it carves away a ton of other possible theories. So it would zoom in our investigation. We would have a smaller target of where the bug could lie. But I don't think that's what we're going to see. "Does Cloud Run pause the instance as soon as it is listening on a port?" Pause as soon as it's listening, what do you mean, Viktor? Like, as soon as it's done listening, like as soon as the request ends? Say that with twice as many words. This could be a failure point of the proxy. Yeah, it's true, it could. All right, let's refresh here. Is this going? So marco works. Show ourselves that. The app is deployed. Load, just once. Nothing. Nothing. But let's see if we got a full round of logs, and we got that request one complete, or whatever I wrote. Logs. Scroll to the bottom. Yeah, request one done. Wait a minute. But then we did get more stuff. And then we just got resolved option one, and we're hanging on option two. Oh, I did actually change the logic of this. I didn't appreciate that earlier. I did change the logic of this, because it never would ask option two in a normal universe, where it loaded option one. What file was that in? Retry? Resolved? Yeah, here we go. So it didn't use to try option two. It never used to call open if it got something here. So I actually changed the behavior of this code. So I will say here this will be option one or option two. Now, I have to deploy again. And this will now mean option one and option two will be the same thing if option one loads. But that's fine, because we're not even getting any intel about these, just as instance of. Re-deploy. Viktor says, "I'm thinking it can be an optimization. If the instance is paused, but the load balancer listens for connections and starts the instance when it detects a connection." I see. Yeah, so Cloud Run does scale down to zero, but not that fast. It's actually a trade a trade secret how much the rate at which it scales down. And I literally don't know the answer. But I know that it's not that fast. I think it's in the order of tens of seconds or something before Cloud Run kills an instance. But I genuinely don't know. But it's not a half a second. OK. Here we go. Here we go. It's loading. It's not loading. It's building. OK, this will complete in a more good way the test that we tried to run last time, which was Randal's idea. Randal, I, at first, glossed over the wisdom in that statement. The quickly is just brilliant. We've never run two queries in the same thing. Aren't we all? Aren't we all? Yeah. Yeah, yeah. All right, it's deployed. Marco works. Hello, polo. Still timing out. Refresh works. Whoa. Refresh hangs. This told us something. Well, the pool is still tricky, because what could happen here is the pool could say, you know, sure, use one of the connections for this query, and then this is so soon after that first query is done, and maybe the first connection isn't back in the pool yet. Maybe this uses the second connection. And then maybe the problem still comes shortly after, when this connection gets, ultimately, terminated, which means the time is now to remove pool from our setup. So I'm going to end all of our pool usage. And to do that, I'm going to go to pub.dev packages Postgres, and I'm just going to steal the connection info. Here we go. It's Postgres Google connection and their mix of positional and named parameters. What is this? Start discussion. The heck did that come from? OK. So we're not using endpoint anymore. We're using PostgreSQL connection. And its host goes here. Oops. Host goes here. Oh, why did I cut this? What did I just do? Oh, I pasted when I meant to cut. Brilliant. OK, Dart test. This is the name of the database. So we'll grab this code. Let's make all of this format. OK, so the name of the database goes here. The user goes here. All right, this one is a named parameter, username. And then the password goes here. And I can't type. And then I bet this also has an is socket connection. Yes, is UNIX socket. I literally can't type anything. This is crazy. Is UNIX socket is a same value. OK, so now we have a PostgreSQL connection. And in our main-- funny thing here is-- so let's just make a variable conn equals PG endpoint. Still not a great name for that. OK, so then how do we use this? Connection dot open and connection dot query, sure. So connection dot open we'll say here. And then we'll have this variable up here, this will be late. PostgreSQL connection conn. So we have the connection. We open it in the main method. And then in here, we don't do any of this anymore. In here, we're now going to say conn dot query. And we pass in all of this, right? No, what am I talking about? I just pass in select now. And this is going to return that same list of lists. Yeah, absolutely. So now we're fine. Whatever, fine. Let's print the first one. So this is going to be-- I'll wait. And then we wrap you in parentheses. And then we grab zero, zero. What's the problem here? What am I doing wrong? I don't think I want-- huh? What's the issue? I'm missing a closing parentheses that's what it is. OK, what's your deal? Void? It can't be used, because you're on print. Oh, that's the issue. There we are. Oh, no, you're still unhappy. Future Postgres result, you're outside of the await. Oh, goodness. This goes here. OK. I'm really looking-- I'm looking good here. Making this look easy. "Have you tried closing the pool at the end of the call?" Wish I would have read that before I ripped the pool out. Hi, Simon. Help. So I'm going to do this. And then we're going to run a second one. And then we're going to return that two string. Well, we don't need to print that the first query ended, because we'll just see the time print. OK. And then there will be some connection closing shenanigans we can play around with at the end of this. But I think this will at least work. Let's run this locally and just make sure it works. So Dart bin server dot Dart. Looking good. Come over to local host. Refresh. Get a time. It keeps loading. Good, good, good. And it should be printing these times. It is. Awesome. It's printing the times right next to a log statement, which also has the time. So not confusing at all. And obviously, marco works. OK, so this isn't 100% broken. So that's a good start. So we can build and deploy. Soon, I'm going to have to take this debugging offline. But I think we're in a pretty-- we're continuing to make progress here. And I'm getting good ideas from you all. So I appreciate it. All right, I think I'm going to just reason about this while it deploys. So there's an open question here of closing the connection, which I am not ever doing. I don't know the implications of this. The connection is opened in main. So when-- I mean, it does kind of seem like it should stay open the whole time, right? And then it will just close when the server ends. I think this is reasonable. There's probably a more elegant way to close it like when the server ends to make sure it closes and the database doesn't think it's still open or something. But I think this isn't going to get in the way of us testing this bug. So we open the connection, we run two simple queries. And Viktor, you've said that you've caused this to error. Train of Thought, we are debugging a problem with talking to a Postgres database on Cloud SQL from Cloud Run using the Dart Postgres library. It works on Cloud Run to Cloud SQL with other languages, so other libraries, and it works with the library, it works with the Dart Postgres library from Google Compute Engine. It is just the confluence of Cloud Run, the Dart Postgres library, and Cloud SQL, they're somehow aligning their stars to cause this problem. Trying to rule out who the guilty party is and isn't. OK. What did I just do? I deployed the app. So now when I refresh here, we'll see something. And that's that it's pending. OK, let's look at the logs. Current time is 42. OK, scrolling down to 42. There is no logs, because I pretty much deleted all the print statements. It was supposed to print the time, though, right? Right there. This is supposed to print the time. And it is not printing the time, why? No, nothing prints there. Does marco work? I should have marco first. Marcos still works. Now it hangs. That's probably a different instance. I keep switching screens when I just mean to switch tabs. So we're going to see marco load once. There it is. And we never did get the time. And we get no error. Nothing. No error. All right, it seems like it's with the proxy. This really seems like it's with the proxy. I know some folks that work on the proxy, and I'm going to talk to him. But I think that is enough-- we got enough to call it today. I'm going to file a bug and talk to the guy. And the proxy is written in Go. He's like this super Go expert-- a couple of other people on the team. But yeah, I know the folks who work on the proxy, and I'm going to talk to them about it. Try to call in a favor. Try to convince them that I even have a favor to call in. OK. Man, this was an adventure. I don't know about you all, but it was a wild ride on my end. A wild, wild ride. Yeah, Viktor, thanks for making Serverpod and being a bit of a Guinea pig on a fairly advanced GCP deploy that you were almost going to have no issues with, but for this. A couple other Config things, we're working out the kinks on there. But yeah, this is the real showstopper for the Cloud Run flavor of Serverpod. Anyway, I'm going to try to get it figured out, because I know that I also want to be able to just deploy Dart code to Cloud Run that talks to a database. And right now I can't do that. So that's not great. Well, folks, thanks for tuning in. Thanks for all the ideas. I would still be probably just staring out the window wondering what things test without your good ideas, so this really was quite a team effort. Man, honestly, kind of, I think, maybe the most fun episode on my end. I don't think we're going to get the viewership of interviewing Remy last week. But debugging with the boys and the gals, doesn't get better than that. So OK, I'll be back next week. I've got a guest lined up. We'll be back in Dart Google Cloud land. It's going to be fun. Stay tuned. Until then, see everybody.
Info
Channel: Flutter
Views: 37,746
Rating: undefined out of 5
Keywords:
Id: oCiYWBvz_rM
Channel Id: undefined
Length: 105min 56sec (6356 seconds)
Published: Thu Mar 16 2023
Related Videos
Note
Please note that this website is currently a work in progress! Lots of interesting data and statistics to come.