* Introduce experimental wait timeout checking to mysql region datastore code 
* This should mean that if the mysql connection has timed out, we should automatically reconnect and not fail or drop queries on region database manipulations
0.6.0-stable
Justin Clarke Casey 2008-06-29 18:10:38 +00:00
parent 7917398451
commit 31c63558c8
2 changed files with 95 additions and 19 deletions

View File

@ -191,7 +191,7 @@ namespace OpenSim.Data.MySQL
{ {
m_log.ErrorFormat( m_log.ErrorFormat(
"[ASSETS DB]: MySql failure fetching asset {0}" + Environment.NewLine + e.ToString() "[ASSETS DB]: MySql failure fetching asset {0}" + Environment.NewLine + e.ToString()
+ Environment.NewLine + "Attempting reconnection", assetID); + Environment.NewLine + "Reconnecting", assetID);
_dbConnection.Reconnect(); _dbConnection.Reconnect();
} }
} }

View File

@ -54,19 +54,32 @@ namespace OpenSim.Data.MySQL
private const string m_landSelect = "select * from land"; private const string m_landSelect = "select * from land";
private const string m_landAccessListSelect = "select * from landaccesslist"; private const string m_landAccessListSelect = "select * from landaccesslist";
private const string m_regionBanListSelect = "select * from regionban"; private const string m_regionBanListSelect = "select * from regionban";
private const string m_regionSettingsSelect = "select * from regionsettings"; private const string m_regionSettingsSelect = "select * from regionsettings";
private const string m_waitTimeoutSelect = "select @@wait_timeout";
private MySqlConnection m_connection;
private string m_connectionString;
/// <summary> /// <summary>
/// We're only using this to version the table! /// Wait timeout for our connection in ticks.
/// </summary> /// </summary>
private long m_waitTimeout;
/// <summary>
/// Make our storage of the timeout this amount smaller than it actually is, to give us a margin on long
/// running database operations.
/// </summary>
private long m_waitTimeoutLeeway = 60 * TimeSpan.TicksPerSecond;
/// <summary>
/// Holds the last tick time that the connection was used.
/// </summary>
private long m_lastConnectionUse;
private DataSet m_dataSet; private DataSet m_dataSet;
private MySqlDataAdapter m_primDataAdapter; private MySqlDataAdapter m_primDataAdapter;
private MySqlDataAdapter m_shapeDataAdapter; private MySqlDataAdapter m_shapeDataAdapter;
private MySqlDataAdapter m_itemsDataAdapter; private MySqlDataAdapter m_itemsDataAdapter;
private MySqlConnection m_connection;
private MySqlDataAdapter m_terrainDataAdapter; private MySqlDataAdapter m_terrainDataAdapter;
private MySqlDataAdapter m_landDataAdapter; private MySqlDataAdapter m_landDataAdapter;
private MySqlDataAdapter m_landAccessListDataAdapter; private MySqlDataAdapter m_landAccessListDataAdapter;
@ -96,14 +109,18 @@ namespace OpenSim.Data.MySQL
/// </summary> /// </summary>
/// <param name="connectionstring"></param> /// <param name="connectionstring"></param>
/// <param name="persistPrimInventories"></param> /// <param name="persistPrimInventories"></param>
public void Initialise(string connectionstring, bool persistPrimInventories) public void Initialise(string connectionString, bool persistPrimInventories)
{ {
m_connectionString = connectionString;
m_dataSet = new DataSet(); m_dataSet = new DataSet();
this.persistPrimInventories = persistPrimInventories; this.persistPrimInventories = persistPrimInventories;
m_log.Info("[REGION DB]: MySql - connecting: " + connectionstring); m_log.Info("[REGION DB]: MySql - connecting: " + m_connectionString);
m_connection = new MySqlConnection(connectionstring); m_connection = new MySqlConnection(m_connectionString);
m_connection.Open(); m_connection.Open();
GetWaitTimeout();
// This actually does the roll forward assembly stuff // This actually does the roll forward assembly stuff
Assembly assem = GetType().Assembly; Assembly assem = GetType().Assembly;
@ -115,7 +132,6 @@ namespace OpenSim.Data.MySQL
m.Update(); m.Update();
MySqlCommand primSelectCmd = new MySqlCommand(m_primSelect, m_connection); MySqlCommand primSelectCmd = new MySqlCommand(m_primSelect, m_connection);
m_primDataAdapter = new MySqlDataAdapter(primSelectCmd); m_primDataAdapter = new MySqlDataAdapter(primSelectCmd);
@ -187,6 +203,58 @@ namespace OpenSim.Data.MySQL
m_regionSettingsDataAdapter.Fill(m_regionSettingsTable); m_regionSettingsDataAdapter.Fill(m_regionSettingsTable);
} }
} }
/// <summary>
/// Get the wait_timeout value for our connection
/// </summary>
protected void GetWaitTimeout()
{
MySqlCommand cmd = new MySqlCommand(m_waitTimeoutSelect, m_connection);
using (MySqlDataReader dbReader = cmd.ExecuteReader(CommandBehavior.SingleRow))
{
if (dbReader.Read())
{
m_waitTimeout
= Convert.ToInt32(dbReader["@@wait_timeout"]) * TimeSpan.TicksPerSecond + m_waitTimeoutLeeway;
}
dbReader.Close();
cmd.Dispose();
}
m_lastConnectionUse = System.DateTime.Now.Ticks;
m_log.DebugFormat(
"[REGION DB]: Connection wait timeout {0} seconds", m_waitTimeout / TimeSpan.TicksPerSecond);
}
/// <summary>
/// Should be called before any db operation. This checks to see if the connection has not timed out
/// </summary>
protected void CheckConnection()
{
//m_log.Debug("[REGION DB]: Checking connection");
long timeNow = System.DateTime.Now.Ticks;
if (timeNow - m_lastConnectionUse > m_waitTimeout || m_connection.State != ConnectionState.Open)
{
m_log.DebugFormat("[REGION DB]: Database connection has gone away - reconnecting");
lock (m_connection)
{
m_connection.Close();
m_connection = new MySqlConnection(m_connectionString);
m_connection.Open();
}
}
// Strictly, we should set this after the actual db operation. But it's more convenient to set here rather
// than require the code to call another method - the timeout leeway should be large enough to cover the
// inaccuracy.
m_lastConnectionUse = timeNow;
}
/// <summary> /// <summary>
/// Given a list of tables, return the version of the tables, as seen in the database /// Given a list of tables, return the version of the tables, as seen in the database
/// </summary> /// </summary>
@ -201,6 +269,8 @@ namespace OpenSim.Data.MySQL
"SELECT TABLE_NAME, TABLE_COMMENT FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_SCHEMA=?dbname", "SELECT TABLE_NAME, TABLE_COMMENT FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_SCHEMA=?dbname",
dbcon); dbcon);
tablesCmd.Parameters.AddWithValue("?dbname", dbcon.Database); tablesCmd.Parameters.AddWithValue("?dbname", dbcon.Database);
CheckConnection();
using (MySqlDataReader tables = tablesCmd.ExecuteReader()) using (MySqlDataReader tables = tablesCmd.ExecuteReader())
{ {
while (tables.Read()) while (tables.Read())
@ -396,6 +466,7 @@ namespace OpenSim.Data.MySQL
lock (m_dataSet) lock (m_dataSet)
{ {
CheckConnection();
DataRow[] primsForRegion = prims.Select(byRegion, orderByParent); DataRow[] primsForRegion = prims.Select(byRegion, orderByParent);
m_log.Info("[REGION DB]: " + m_log.Info("[REGION DB]: " +
"Loaded " + primsForRegion.Length + " prims for region: " + regionUUID); "Loaded " + primsForRegion.Length + " prims for region: " + regionUUID);
@ -473,6 +544,7 @@ namespace OpenSim.Data.MySQL
{ {
lock (m_dataSet) lock (m_dataSet)
{ {
CheckConnection();
//m_log.InfoFormat("[DATASTORE]: Loading inventory for {0}, {1}", prim.Name, prim.UUID); //m_log.InfoFormat("[DATASTORE]: Loading inventory for {0}, {1}", prim.Name, prim.UUID);
DataTable dbItems = m_itemsTable; DataTable dbItems = m_itemsTable;
@ -519,6 +591,8 @@ namespace OpenSim.Data.MySQL
using (cmd) using (cmd)
{ {
delete.Parameters.Add(new MySqlParameter("?RegionUUID", Util.ToRawUuidString(regionID))); delete.Parameters.Add(new MySqlParameter("?RegionUUID", Util.ToRawUuidString(regionID)));
CheckConnection();
delete.ExecuteNonQuery(); delete.ExecuteNonQuery();
cmd.Parameters.Add(new MySqlParameter("?RegionUUID", Util.ToRawUuidString(regionID))); cmd.Parameters.Add(new MySqlParameter("?RegionUUID", Util.ToRawUuidString(regionID)));
@ -554,6 +628,7 @@ namespace OpenSim.Data.MySQL
lock (m_dataSet) lock (m_dataSet)
{ {
CheckConnection();
using (MySqlDataReader row = cmd.ExecuteReader()) using (MySqlDataReader row = cmd.ExecuteReader())
{ {
int rev = 0; int rev = 0;
@ -593,6 +668,7 @@ namespace OpenSim.Data.MySQL
{ {
lock (m_dataSet) lock (m_dataSet)
{ {
CheckConnection();
using (MySqlCommand cmd = new MySqlCommand("delete from land where UUID=?UUID", m_connection)) using (MySqlCommand cmd = new MySqlCommand("delete from land where UUID=?UUID", m_connection))
{ {
cmd.Parameters.Add(new MySqlParameter("?UUID", Util.ToRawUuidString(globalID))); cmd.Parameters.Add(new MySqlParameter("?UUID", Util.ToRawUuidString(globalID)));
@ -616,6 +692,7 @@ namespace OpenSim.Data.MySQL
{ {
lock (m_dataSet) lock (m_dataSet)
{ {
CheckConnection();
DataTable land = m_landTable; DataTable land = m_landTable;
DataTable landaccesslist = m_landAccessListTable; DataTable landaccesslist = m_landAccessListTable;
@ -654,6 +731,7 @@ namespace OpenSim.Data.MySQL
{ {
lock(m_dataSet) lock(m_dataSet)
{ {
CheckConnection();
DataTable regionsettings = m_regionSettingsTable; DataTable regionsettings = m_regionSettingsTable;
string searchExp = "regionUUID = '" + regionUUID.ToString() + "'"; string searchExp = "regionUUID = '" + regionUUID.ToString() + "'";
DataRow[] rawsettings = regionsettings.Select(searchExp); DataRow[] rawsettings = regionsettings.Select(searchExp);
@ -669,6 +747,7 @@ namespace OpenSim.Data.MySQL
{ {
lock (m_dataSet) lock (m_dataSet)
{ {
CheckConnection();
DataTable regionsettings = m_dataSet.Tables["regionsettings"]; DataTable regionsettings = m_dataSet.Tables["regionsettings"];
DataRow settingsRow = regionsettings.Rows.Find(rs.RegionUUID.ToString()); DataRow settingsRow = regionsettings.Rows.Find(rs.RegionUUID.ToString());
@ -695,6 +774,7 @@ namespace OpenSim.Data.MySQL
List<RegionBanListItem> regionbanlist = new List<RegionBanListItem>(); List<RegionBanListItem> regionbanlist = new List<RegionBanListItem>();
lock (m_dataSet) lock (m_dataSet)
{ {
CheckConnection();
DataTable regionban = m_regionBanListTable; DataTable regionban = m_regionBanListTable;
string searchExp = "regionUUID = '" + regionUUID.ToString() + "'"; string searchExp = "regionUUID = '" + regionUUID.ToString() + "'";
DataRow[] rawbanlist = regionban.Select(searchExp); DataRow[] rawbanlist = regionban.Select(searchExp);
@ -724,6 +804,7 @@ namespace OpenSim.Data.MySQL
{ {
lock (m_dataSet) lock (m_dataSet)
{ {
CheckConnection();
DataTable regionban = m_regionBanListTable; DataTable regionban = m_regionBanListTable;
string searchExp = "regionUUID = '" + item.regionUUID.ToString() + "' AND bannedUUID = '" + item.bannedUUID.ToString() + "'"; string searchExp = "regionUUID = '" + item.regionUUID.ToString() + "' AND bannedUUID = '" + item.bannedUUID.ToString() + "'";
DataRow[] rawbanlist = regionban.Select(searchExp); DataRow[] rawbanlist = regionban.Select(searchExp);
@ -748,6 +829,7 @@ namespace OpenSim.Data.MySQL
{ {
lock (m_dataSet) lock (m_dataSet)
{ {
CheckConnection();
DataTable regionban = m_regionBanListTable; DataTable regionban = m_regionBanListTable;
string searchExp = "regionUUID = '" + item.regionUUID.ToString() + "' AND bannedUUID = '" + item.bannedUUID.ToString() + "'"; string searchExp = "regionUUID = '" + item.regionUUID.ToString() + "' AND bannedUUID = '" + item.bannedUUID.ToString() + "'";
DataRow[] rawbanlist = regionban.Select(searchExp); DataRow[] rawbanlist = regionban.Select(searchExp);
@ -760,10 +842,6 @@ namespace OpenSim.Data.MySQL
} }
Commit(); Commit();
} }
if (m_connection.State != ConnectionState.Open)
{
m_connection.Open();
}
using using
( (
@ -773,6 +851,7 @@ namespace OpenSim.Data.MySQL
{ {
cmd.Parameters.Add(new MySqlParameter("?regionUUID", item.regionUUID.ToString())); cmd.Parameters.Add(new MySqlParameter("?regionUUID", item.regionUUID.ToString()));
cmd.Parameters.Add(new MySqlParameter("?bannedUUID", item.bannedUUID.ToString())); cmd.Parameters.Add(new MySqlParameter("?bannedUUID", item.bannedUUID.ToString()));
CheckConnection();
cmd.ExecuteNonQuery(); cmd.ExecuteNonQuery();
} }
@ -788,6 +867,7 @@ namespace OpenSim.Data.MySQL
List<LandData> landDataForRegion = new List<LandData>(); List<LandData> landDataForRegion = new List<LandData>();
lock (m_dataSet) lock (m_dataSet)
{ {
CheckConnection();
DataTable land = m_landTable; DataTable land = m_landTable;
DataTable landaccesslist = m_landAccessListTable; DataTable landaccesslist = m_landAccessListTable;
string searchExp = "RegionUUID = '" + Util.ToRawUuidString(regionUUID) + "'"; string searchExp = "RegionUUID = '" + Util.ToRawUuidString(regionUUID) + "'";
@ -813,13 +893,9 @@ namespace OpenSim.Data.MySQL
/// </summary> /// </summary>
public void Commit() public void Commit()
{ {
if (m_connection.State != ConnectionState.Open)
{
m_connection.Open();
}
lock (m_dataSet) lock (m_dataSet)
{ {
CheckConnection();
// DisplayDataSet(m_dataSet, "Region DataSet"); // DisplayDataSet(m_dataSet, "Region DataSet");
m_primDataAdapter.Update(m_primTable); m_primDataAdapter.Update(m_primTable);