ThinkGeo.com    |     Documentation    |     Premium Support

MapSuite 6.0 timeout error

Good morning,


We just updated to MapSuite 6 in our application and have begun to see timeout errors in places we don't with the version that uses MapSuite 4.5:


"Timeout expired.  The timeout period elapsed prior to obtaining a connection from the pool.  This may have occurred because all pooled connections were in use and max pool size was reached."


Stack trace:

   at System.Data.ProviderBase.DbConnectionFactory.GetConnection(DbConnection owningConnection)

   at System.Data.ProviderBase.DbConnectionClosed.OpenConnection(DbConnection outerConnection, DbConnectionFactory connectionFactory)

   at System.Data.SqlClient.SqlConnection.Open()

   at ThinkGeo.MapSuite.Core.MsSql2008FeatureSource.OpenCore()

   at ThinkGeo.MapSuite.Core.FeatureSource.Open()

   at ThinkGeo.MapSuite.Core.FeatureLayer.OpenCore()

   at ThinkGeo.MapSuite.DesktopEdition.LayerOverlay.DrawCore(GeoCanvas canvas)

   at ThinkGeo.MapSuite.DesktopEdition.Overlay.rRM=(GeoCanvas rhM=)

   at ThinkGeo.MapSuite.DesktopEdition.Overlay.Draw(GeoCanvas canvas)

   at ThinkGeo.MapSuite.DesktopEdition.WinformsMap.yhU=(IEnumerable`1 yxU=)

   at ThinkGeo.MapSuite.DesktopEdition.WinformsMap.yBU=(RectangleShape yRU=)

   at ThinkGeo.MapSuite.DesktopEdition.WinformsMap.FxY=()

   at ThinkGeo.MapSuite.DesktopEdition.WinformsMap.Refresh()

   at IronCompass.OnSceneXplorer.ThinkGeoExtension.OsxMapCore.LoadMap(MapLoadParameters mapLoadParameters, List`1 hiddenGroups) in C:\Users\ahuber\Documents\Visual Studio 2010\Projects\OSX3 Application\OsxMapCore\WindowsFormsControlLibrary1\OsxMapCore.cs:line 1448


This happens when we switch between "day" and "night" mode, which essentially reloads the map with new styling.  On some computers this error is thrown the very first time the map is switched from day to night mode.  This DOES NOT happen and has not been duplicated when using MapSuite 4.5.


During the conversion from MapSuite 4.5 to 6.0 there we essentially NO CODE CHANGES beyond a method name change in Proj4, so the code base is identical.


I am under pressure to make this a support ticket but am still trying to gather details before I do that.


Allen



I’ve done a little more research… 
  
 The MapSuite 4.5 version of our app was built via Visual Studio 2008 on an XP machine.  When our application has completed its loading process and the map is displayed, SQL Server Management Studio shows 13 processes for the SQL account being used to access the database. 
  
 The MapSuite 6.0 version of our app is being built with Visual Studio 2010 on a Windows 7 machine.  When this version of the application reaches the same state, Management Studio shows 60 process for the account.   
  
 I am working to reduce the number of variables or build a more controllable test case.  I have to wonder if something in the way MapSuite 6.0 connects to SQL Server has changed.  It would be nice if someone at ThinkGeo could look into this a little. I don’t know too much about the topic, but it almost appears that the connection pooling is no longer working. 
  
 Allen

Hello Allen, 
  
 Thanks for your post, from 4.5 to 6.0, it’s really a long time, so a lot of changes and hard to say which one will cause the problem you mentioned, if you can show me how to recreate or provide a sample to me to recreate, our product team can work on this and  dig in the root cause. 
  
 Regards, 
  
 Gary

Hi Gary, 
  
 I’m going to go back and start looking at this again.  What I’ve found so far is this, which you should easily be able to duplicate: 
  
 1) With no MapSuite application running, I see no processes running for the login we use in the application for SQL Server, obviously 
  
 2) When I load one SQL layer with MapSuite 4.5, I see ONE process running under the login we use  
 3) When I load three SQL layers with MapSuite 4.5, I see ONE process running under the login 
 [This is a Windows XP computer running SQL Server Express R2 2008 and Windows 2008] 
  
 4) When I load one SQL layer with MapSuite 6.0, I see ONE process running under the login 
 5) When I load two SQL layers with MapSuite 6.0, I see TWO processes running under the login 
 6) When I load three SQL layers with MapSuite 6.0, I see THREE processes running under the login. 
 7) I can only presume the trend continues…for each SQL layer I add more processes are running. 
 [This is a Windows 7 computer running SQL Server Developer 2008 and Windows 2010; however the errors occur on computers running SQL Server Express 2008 as well.] 
  
 Our application has perhaps 25-30 SQL layers.  We also define about 10-15 InMemory and MapShape layers, but since these do not connect to SQL Server I don’t expect they’re involved. As I noted above, when the application switches from day to night mode the map is reloaded and I presume the number of connections keeps increasing, because before too long we get the connection pool error I pasted into the original message.  In addition, our app will make requests via MapSuite to do spatial queries, obtain bounding boxes, etc.  I haven’t determined yet if performing these operations create even more open connections. 
  
 This seems to get into connection and connection pooling limits which I’m still not totally understanding, but this Microsoft page: 
  
 msdn.microsoft.com/en-us/library/8xx3tyca.aspx 
  
 discusses a limit of 100 connections per pool.  By default the same login string information is supposed to reuse connections in a pool, but it seems something changed in MapSuite 6.0 where new connections keep being generated until the limit of 100 his hit, rather than returning connections to the pool and reusing them. 
  
 Allen

Hi Allen, 



We apologize for the delay and any inconvenience this may cause.


Yes, we did some enhancements here to improve the query performance, before,we created the SqlConnection before query and close it at once after using, it consumes lots of time to open/close connection, so we move it to Open/Close method of MsSql2008FeatureSource, Please code below to avoid the issue you mentioned:



    public class CustomLayerOverlay : LayerOverlay
    {
        public CustomLayerOverlay()
            : base()
        { }

        protected override void DrawCore(GeoCanvas canvas)
        {
            base.DrawCore(canvas);

            foreach (var layer in this.Layers)
            {
                if (layer.IsOpen)
                {
                    layer.Close();
                }
            }
        }
    }






Thanks, 

Johnny 

 



Johnny, 
  
 I’m not too clear on what the “Core” stuff does.  Do I now create and add "CustomLayerOverlay"s to my map instead of just "LayerOverlay"s or does just having this code present in my project do some sort of magic? 
  
 Allen

Yeah, you just need to use "CustomLayerOverlay" instead of "LayerOverlay". Also, please get the latest version of package. 
  
 Thanks, 
 Johnny

Thanks, Johnny.  That makes more sense than some sort of “magic code”. 
  
 Is the code you supplied a workaround or the way it’s going to be?  If it’s the latter, it now sounds like we will have a limit of about 100 SQL layers that can be loaded into a map, because this is the default number of connections in SQL Server.  If I try to load 110 SQL layers (and I will do this sometime today to see what happens) I expect to run out of connections before the first time the map even gets displayed–or depending on how your logic works, at the time of the first map display–so I don’t think the logic will even get to the CustomOverlay.Draw code.  Another thing that has me very concerned is the opening and closing of layers in code.  Going back to MapSuite 4.5 this was a source of long-term grief with a lot of people who needed to open a layer to do something with it only to have MapSuite throw errors later because the layer was open when it wasn’t supposed to be or the other way around.  I don’t ever recall that being fixed but perhaps with all the stuff that was rewritten in 6.0 it’s no longer an issue. 
  
 Could you please tell me why I should get the latest version of the package?  Our application consists of dozens of interconnected projects and 100,000 lines of code (if not more) and it’s not a trivial task to just throw in a new version. If it contains a fix I definitely need, I will do it. 
  
 Allen

Just as I feared: if I try to load 105 SQL Server layers, the initial Refresh() throws the same error in my original comment.  So does this mean we now have a limit to the number of SQL layers a map can contain, short of increasing the limit in SQL Server?

I’m posting a comment to reinforce Allen’s posting…this is a very serious problem for us. The application is for emergency responders and at some point we froze on MapSuite 4.5 to create a stable release. Now we are ready to move to MapSuite 6.0 since we have our product released to customers with an automated update mechanism in place. 
  
 Our entire product is build on SQL Server spatial and tabular data. As such, we have developed a lot of experience using SQL with your product. 
  
 MapSuite 6.0 is really putting us in a bind (as we just added your routing component and can not move this to customers with the connection time-out as it is). 
  
 Allen and I are available to provide feedback, discuss approaches, give samples, or to just better explain how the new approach is causing a real problem in the real world. 
  
 Please let us know how we can help resolve this problem. Thanks. 
  
 -John

 Allen and John,



In Map Suite 4.5 we open the SQL connection before every method and close it after it. So if I call the same method twice, the connection will open, close, reopen and reclose. That’s low efficient and in the newer version, we put the connection.open only in Open() method and put the close only to Close() method, and the methods are expected to be called like this:



msSql2008FestureSource.Open();
msSql2008FestureSource.DoSomethingA();
msSql2008FestureSource.DoSomethingB();
msSql2008FestureSource.DoSomethingC();
msSql2008FestureSource.Close();

So only one Open/Close is needed and as a result, the connection is kept opening before calling Close().


For your issue, here is a workaround that you can create your new SqlFeatureLayer by overriding all the methods you are using, open the connection, call the base’s method and close it, kind of retrieve it back to the 4.5 way. I think that would solve your problem. 



public class MyMsSql2008FeatureSource : MsSql2008FeatureSource
    {
        protected override int ExecuteNonQueryCore(string sqlStatement)
        {
            this.Open();
            int result = base.ExecuteNonQueryCore(sqlStatement);
            this.Close();
            return result;
        }
 
        // override all the methods you are using
    }
 
    public class MyMsSql2008FeatureLayer : MsSql2008FeatureLayer
    {
        public MyMsSql2008FeatureLayer()
            :this(string.Empty,string.Empty,string.Empty,0)
        { }
 
        public MyMsSql2008FeatureLayer(string connection, string tableName, string featureIdColumn)
            :this(connection,tableName,featureIdColumn,0)
        {}
 
        public MyMsSql2008FeatureLayer(string connection, string tableName, string featureIdColumn, int srid)
            : base(connection, tableName, featureIdColumn, srid)
        {
            this.FeatureSource = new MyMsSql2008FeatureSource();
        }
    }

Ben




 


Allen, 


If you just put the SqlLayers to a LayerOverlay and draw them out, you should override the GetFeaturesInsideBoundingBoxCore method. You can also override all the GetFatures* method to be safe. 


By the way we tested the following scenario:



static void Main(string[] args)
        {
            string connectString = "Data Source=192.168.0.212;Initial Catalog=InternalDB;User ID=sa;Password=password";
               SqlConnection[] connections = new SqlConnection[101];
            for (int i = 0; i < 101; i++)
            {
                connections[i] = new SqlConnection(connectString);
                connections[i].Open();
            }
        }

 


And it throws an exception too. It worked fine when changing the loop to 100. From Microsoft doc you provided<u1:p></u1:p>


msdn.microsoft.com/en-us/library/8xx3tyca.aspx<u1:p></u1:p>


we can see “When the application calls Close on the connection, the pooler returns it to the pooled set of active connections instead of closing it. Once the connection is returned to the pool, it is ready to be reused on the next Open call.”<u1:p></u1:p>  So we need to close a layer to return it as active connections in pool, and only then we can reuse it.  


Ben


 


 



Allen, 
  
 Or another way is changing the default max pool size in the connection string. For example in our test we use this: 
 “Data Source=192.168.0.212;Initial Catalog=InternalDB;max pool size=5000;User ID=sa;Password=password” 
  
 and it didn’t throw exception in a 1000 loop. 
  
 Ben

Ben, 
  
 John and I discussed this whole issue today and decided we have to do a little more experimenting.  We simply do not know what type of impact increasing the maximum pool size will have; many of our customers have old systems with such a small amount of memory it’s kind of amazing SQL Server PLUS the software run at all.  I don’t know if making it 5,000 is practical, but yet something like 200 is, in my opinion, only a temporary fix, since some day someone will exceed that limit and the software will not run.  I would like to know, for example, if I increase the maximum from 100 to 500 connections, is there an increase in memory consumption.  We know connections take a lot of time to build, but I don’t know if they take a lot of memory or CPU as well.  We need to test this. 
  
 As I noted in the initial comment, our application can change map styles, and this was implemented easiest by clearing the entire map by removing all of the overlays and totally reloading it, which, of course, continues to increase the number of connections to SQL under MapSuite 6.0.  John did question, and I don’t have an answer but maybe you do, why, if I clear the map by doing WinFormsMap.Overlays.Clear(), these connections remain open.  I hadn’t thought of this before!  These SQL layers are gone because the overlay they were in have been cleared, but it seems like the connections are still active.  If the SQL layers are being disposed of properly it seems the connections would be closed.  I’m trying to think of a way to test this as well. 
  
 Anyway, we are experimenting with some code that applies the new styles in place instead of completely reloading the map, which might help keep the number of connections down.  I have been going through our code to make sure each time we make a SQL connection that the parameters are identical so that our code is creating as few connections to SQL Server as possible. 
  
 We’ll report back once we’ve done some of this testing. 
  
 Allen

Hello Allen, 
  
 Thanks for your hard work, we expect your result and figure this out together. 
  
 Regards, 
  
 Gary

Allen, 
  
 John is right that when WinFormsMap.Overlays.Clear() is called, it’s just the collection itself is cleared but its item, in this case the overlays, don’t have any change. The SQL connection remains open unless we explicitly close it. I don’t think you have to reload all the layers just for changing the map styles, just update the styles for each layer and call map.refresh()would be fine, the code is easy to write and the performance is better. 
  
 Ben 


Allen, 
  
 John is right that when WinFormsMap.Overlays.Clear() is called, it’s just the collection itself is cleared but its item, in this case the overlays, don’t have any change. The SQL connection remains open unless we explicitly close it. I don’t think you have to reload all the layers just for changing the map styles, just update the styles for each layer and call map.refresh()would be fine, the code is easy to write and the performance is better. 
  
 Ben 


Ben, 
  
 When I first wrote that code several years ago we were fairly new to MapSuite and I wasn’t totally confident that I could successfully update the styles in place (I remember to me it did sound a bit radical at the time to do that) so the easiest solution was to simply reload everything because that logic was already built.  I have the style updating in place working now but there are still at least three scenarios I have to deal with: switching to a new map configuration that has a SQL layer the previous one didn’t (add it), switching to a new map configuration that doesn’t use a SQL layer the previous one did (remove or disable it), and switching to a new map configuration with the same layers but in different orders (move it).  I don’t think any of these are major coding challenges.  The only thing I’m not sure about is how easy it is to move a layer to a new position within the overlay.  I’ll have to consult the documentation for that. 
  
 Hmmm…your comment about Overlays.Clear() just clearing the collection itself makes me wonder if BEFORE doing this executing a Layer.Close loop like Johnny had suggested earlier would help, but if we have the ability to update styles without reloading it’s not terribly important if it works or not.  We would certainly prefer to update the styles in place because if the user changes map styles while certain functions are taking place (like using the GPS and updating the map) the code does things like asking a layer for the map rotation and at times will throw errors because the layer was cleared and hasn’t been reloaded yet.   
  
 I am going to try to do a little experimenting today and maybe before the end of the week we can make a decision about how to deal with all of this. 
  
 Thanks to everyone for their input and suggestions! 
 Allen 


I’m still trying to decipher this exactly, but this chart from MSDN seems to indicate a connection might take about 100 K of memory, but I don’t know if that’s consumed when the connection is made or reserved by SQL based on the max number of connections setting.  It seems to me if you have a “max connections” setting it’s going to reserve memory for that many when it starts because otherwise it would just access a new chunk of memory each time it needs it and not have any need for a “max connections” setting. 
  
 msdn.microsoft.com/en-us/library/aa337559(v=sql.105).aspx 
  


Allen,  
  
 You see both Johnny and my workaround is to close the connection after a method. That’s not contradictory to that the Overlays.Clear() only clear the collection instead clearing the connection itself. Anyway as you said, you would certainly prefer updating the styles in place as it should be more efficient and avoid some potential issues. You can test the memory stuff with different “max connection” but maybe you don’t need it anymore once you find a good way to control opening new connections.  
  
 Ben