OracleCommand execution blocks if has OracleDependency - oracle10g

I have the following code:
OracleConnection conn = new OracleConnection(connString);
OracleCommand command = new OracleCommand("select * from testtable", conn);
conn.Open();
OracleDependency.Port = 2010;
OracleDependency dependency = new OracleDependency(command);
command.AddRowid = true;
command.Notification.IsNotifiedOnce = false;
dependency.OnChange += new OnChangeEventHandler(dependency_OnChange);
command.CommandTimeout = 1000;
DataTable t = new DataTable();
OracleDataAdapter adapter = new OracleDataAdapter(command);
adapter.Fill(t);
conn.Close();
This is a very straightforward code that uses Oracle Notification Service to receive notifications about particular table changes.
My problem is that when I call adapter.Fill(t); the execution simply blocks. The command executes in an instance if there is no dependency attached to it so it's not the database or the data. I can see the call back registering with the database by querying the table user_change_notification_regs and have also opened the port specified (2010):
net8://(DESCRIPTION=(ADDRESS=(PROTOCOL=tcp)(HOST='myIp')(PORT=2010)))?PR=0
I am at wits end and rand out of things to try.

I have seen an exception raised in a similar situation when I've tried to set the port number to a port already used on my machine. As soon as I commented out setting the port number it ran fine, so perhaps you could try that? And check "netstat -na" for used ports.
The exception I saw was:
Oracle.DataAccess.Client.OracleException: ORA-24912: Listener thread failed. Listen failed.
at Oracle.DataAccess.Client.OracleException.HandleErrorHelper(Int32 errCode, OracleConnection conn, IntPtr opsErrCtx, OpoSqlValCtx* pOpoSqlValCtx, Object src, String procedure, Boolean bCheck)
at Oracle.DataAccess.Client.OracleException.HandleError(Int32 errCode, OracleConnection conn, String procedure, IntPtr opsErrCtx, OpoSqlValCtx* pOpoSqlValCtx, Object src, Boolean bCheck)
at Oracle.DataAccess.Client.OracleCommand.ExecuteReader(Boolean requery, Boolean fillRequest, CommandBehavior behavior)
at Oracle.DataAccess.Client.OracleDataAdapter.Fill(DataSet dataSet, Int32 startRecord, Int32 maxRecords, String srcTable, IDbCommand command, CommandBehavior behavior)
at System.Data.Common.DbDataAdapter.Fill(DataSet dataSet)
The confusing thing (at least for me) was the exception is raised not when the port is set, but later when the first query was executed against it.

Related

SQL Timeout expired. The timeout period elapsed prior to completion of the operation or the server is not responding

I am using Microsoft.EntityFrameworkCore.SqlServer 2.2.6. I have a stored procedure which typically takes 1 to 2 seconds to execute.
I am using .NET Core 2.2 with EF Core to execute that stored procedure.
appsettings.json:
{
"SqlCommandTimeout": 120, // seconds
"ConnectionStrings": {
"DefaultConnection": "Server=serverip;Database=MyDataBase;Integrated Security=True;"
}
}
In startup.cs I am setting connection string and timeout
var sqlCommandTimeout = configuration.GetValue<int>("SqlCommandTimeout");
services.AddDbContext<MyDBContext>(options =>
{
options.UseSqlServer(configuration.GetConnectionString("DefaultConnection"),
sqlServerOptions => sqlServerOptions.CommandTimeout(sqlCommandTimeout));
});
This code executes the stored procedure and populates a DTO:
public static async Task<IEnumerable<AvailableWorkDTO>> prcGetAvailableWork(this MyDBContext dbContext, int userID)
{
var timeout = dbContext.Database.GetCommandTimeout() ?? 120;
var result = new List<AvailableWorkDTO>();
using (var cmd = dbContext.Database.GetDbConnection().CreateCommand())
{
var p1 = new SqlParameter("#UserID", SqlDbType.Int)
{
Value = userID
};
cmd.CommandText = "dbo.prcGetAvailableWork";
cmd.CommandType = CommandType.StoredProcedure;
cmd.Parameters.Add(p1);
cmd.CommandTimeout = timeout;
await dbContext.Database.OpenConnectionAsync().ConfigureAwait(false);
using (var reader = await cmd.ExecuteReaderAsync().ConfigureAwait(false))
{
while (await reader.ReadAsync().ConfigureAwait(false))
{
var item = new AvailableWorkDTO();
item.ID = reader.GetInt32(0);
item.Name = reader.GetString(1);
item.Title = reader.GetString(2);
item.Count = reader.GetInt32(3);
result.Add(item);
}
}
}
return result;
}
The stored procedure only reads data from couple of tables using READUNCOMMITTED isolation level. However there is also a another background process that inserts new records into these tables every 3 minutes.
ISSUE
Every now and then when number of users increase we are seeing the timeout exception.
System.Data.SqlClient.SqlException (0x80131904): Timeout expired.
The timeout period elapsed prior to completion of the operation or the server is not responding.
System.ComponentModel.Win32Exception (258): The wait operation timed out
at System.Data.SqlClient.SqlCommand.<>c.b__122_0(Task1 result) at System.Threading.Tasks.ContinuationResultTaskFromResultTask2.InnerInvoke()
at System.Threading.ExecutionContext.RunInternal(ExecutionContext executionContext, ContextCallback callback, Object state)
--- End of stack trace from previous location where exception was thrown ---
at System.Threading.Tasks.Task.ExecuteWithThreadLocal(Task& currentTaskSlot)
--- End of stack trace from previous location where exception was thrown ---
at Data.Entities.StoredProcedures.StoredPrcedureExtensions.prcGetAvailableWork(MyDbContext dbContext, Int32 userID) in D:\Jenkins\xx-production\workspace\Src\Common\Data\Entities\StoredProcedures\StoredPrcedureExtensions.cs:line 56
at ....
....
Microsoft.AspNetCore.Mvc.Internal.ActionMethodExecutor.TaskOfIActionResultExecutor.Execute(IActionResultTypeMapper mapper, ObjectMethodExecutor executor, Object controller, Object[] arguments)
at System.Threading.Tasks.ValueTask`1.get_Result()
at Microsoft.AspNetCore.Mvc.Internal.ControllerActionInvoker.InvokeActionMethodAsync()
at Microsoft.AspNetCore.Mvc.Internal.ControllerActionInvoker.InvokeNextActionFilterAsync()
at Microsoft.AspNetCore.Mvc.Internal.ControllerActionInvoker.Rethrow(ActionExecutedContext context)
at Microsoft.AspNetCore.Mvc.Internal.ControllerActionInvoker.Next(State& next, Scope& scope, Object& state, Boolean& isCompleted)
at Microsoft.AspNetCore.Mvc.Internal.ControllerActionInvoker.InvokeInnerFilterAsync()
at Microsoft.AspNetCore.Mvc.Internal.ResourceInvoker.InvokeNextResourceFilter()
at Microsoft.AspNetCore.Mvc.Internal.ResourceInvoker.Rethrow(ResourceExecutedContext context)
at Microsoft.AspNetCore.Mvc.Internal.ResourceInvoker.Next(State& next, Scope& scope, Object& state, Boolean& isCompleted)
at Microsoft.AspNetCore.Mvc.Internal.ResourceInvoker.InvokeFilterPipelineAsync()
at Microsoft.AspNetCore.Mvc.Internal.ResourceInvoker.InvokeAsync()
at Microsoft.AspNetCore.Routing.EndpointMiddleware.Invoke(HttpContext httpContext)
at Microsoft.AspNetCore.Routing.EndpointRoutingMiddleware.Invoke(HttpContext httpContext)
at Microsoft.AspNetCore.Authentication.AuthenticationMiddleware.Invoke(HttpContext context)
at Microsoft.AspNetCore.StaticFiles.StaticFileMiddleware.Invoke(HttpContext context)
at Microsoft.AspNetCore.Diagnostics.ExceptionHandlerMiddleware.Invoke(HttpContext context)
ClientConnectionId:c10da510-509f-4bfb-8b37-58c0ee8fa3b1
Error Number:-2,State:0,Class:11at
Questions
I am using EF Core 2.2. Based on documentation, for certain async methods SqlCommand.CommandTimeout property is ignored. In the above code will the timeout ignored?
SQL Server Profiler shows stored procedure actually gets executed only once by await cmd.ExecuteReaderAsync() line. However we still need to keep connection open while reading from the reader. Is the CommandTimeOut includes time takes for reading from the reader?
At most 100 users may concurrently access this stored procedure. We have double check indexes and also execute scheduled indexing job every day. We are using
Microsoft SQL Server 2017 (RTM-CU13) (KB4466404) - 14.0.3048.4 (X64) Standard Edition (64-bit)
on Windows Server 2019. Which should be able to handle the load. Is there any settings that we need to look? (SQL Server also has multiple other databases as well)
In the above code will the timeout ignored?
No.
System.Data.SqlClient.SqlException (0x80131904): Timeout expired.
This is a client-side timeout caused by the CommandTimeout.
Is the CommandTimeOut includes time takes for reading from the reader?
Yes. Per docs, SqlCommand.CommandTimeout includes SqlDataReader.Read() time.
Is there any settings that we need to look?
Turn on the Query Store and track the session wait stats to see if you're seeing blocking, CPU contention or something else.
I also had the same issue, in my case I written the stored procedure code in the following way
BEGIN TRAN
BEGIN TRY
some code here....
--COMMIT TRAN // it is missed or written in the wrong place
END TRY
"COMMIT TRAN" code missed in the stored procedure code but "BEGIN TRAN" is there, due to that previous calls are in waiting state and for the next transaction server will not respond

Send email from service fabric actor with SmtpClient fails

I am trying to send an email from a Service fabric actor method. The code is very simple and works without problem in a console application but the very same code inside the actor method generate the exception:
"The remote certificate is invalid according to the validation procedure"
I have no idea why this is happening and what I should add to my code to make it work (I don't want to bypass certificate validation or disable encryption), so I am looking for help on this forum.
Thank you
Here is my code (just replaced credentials and domain with dummy names)
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Net.Mail;
using System.Text;
using System.Threading.Tasks;
namespace testsmtp {
class Program {
static void Main(string[] args) {
SendEmailAlert("myuserid", "mypassword", "recipient#mydomain.com", "test subject", "test body");
}
private static bool SendEmailAlert(string uid, string pwd, string recipient_list, string subject, string body) {
MailMessage msg = new MailMessage();
msg.To.Add(recipient_list);
msg.From = new MailAddress("sender#mydomain.com");
msg.Subject = subject;
msg.Body = body;
msg.IsBodyHtml = false;
SmtpClient client = new SmtpClient();
client.Host = "smtp.mydomain.com";
client.Port = 587;
client.EnableSsl = true;
client.UseDefaultCredentials = false;
client.Credentials = new NetworkCredential(uid, pwd);
try {
client.Send(msg);
return true;
}
catch (Exception e) {
string emsg = e.Source + "\n" + e.Message;
return false;
}
}
}
}
and here is the exception data
Message "The remote certificate is invalid according to the validation procedure." string
StackTrace " at System.Net.Security.SslState.StartSendAuthResetSignal(ProtocolToken message, AsyncProtocolRequest asyncRequest, Exception exception)\r\n at System.Net.Security.SslState.ProcessReceivedBlob(Byte[] buffer, Int32 count, AsyncProtocolRequest asyncRequest)\r\n at System.Net.Security.SslState.StartReceiveBlob(Byte[] buffer, AsyncProtocolRequest asyncRequest)\r\n at System.Net.Security.SslState.ProcessReceivedBlob(Byte[] buffer, Int32 count, AsyncProtocolRequest asyncRequest)\r\n at System.Net.Security.SslState.StartReceiveBlob(Byte[] buffer, AsyncProtocolRequest asyncRequest)\r\n at System.Net.Security.SslState.ProcessReceivedBlob(Byte[] buffer, Int32 count, AsyncProtocolRequest asyncRequest)\r\n at System.Net.Security.SslState.StartReceiveBlob(Byte[] buffer, AsyncProtocolRequest asyncRequest)\r\n at System.Net.Security.SslState.ProcessReceivedBlob(Byte[] buffer, Int32 count, AsyncProtocolRequest asyncRequest)\r\n at System.Net.Security.SslState.StartReceiveBlob(Byte[] buffer, AsyncProtocolRequest asyncRequest)\r\n at System.Net.Security.SslState.ProcessReceivedBlob(Byte[] buffer, Int32 count, AsyncProtocolRequest asyncRequest)\r\n at System.Net.Security.SslState.StartReceiveBlob(Byte[] buffer, AsyncProtocolRequest asyncRequest)\r\n at System.Net.Security.SslState.ProcessReceivedBlob(Byte[] buffer, Int32 count, AsyncProtocolRequest asyncRequest)\r\n at System.Net.Security.SslState.StartReceiveBlob(Byte[] buffer, AsyncProtocolRequest asyncRequest)\r\n at System.Net.Security.SslState.ForceAuthentication(Boolean receiveFirst, Byte[] buffer, AsyncProtocolRequest asyncRequest)\r\n at System.Net.Security.SslState.ProcessAuthentication(LazyAsyncResult lazyResult)\r\n at System.Threading.ExecutionContext.RunInternal(ExecutionContext executionContext, ContextCallback callback, Object state, Boolean preserveSyncCtx)\r\n at System.Threading.ExecutionContext.Run(ExecutionContext executionContext, ContextCallback callback, Object state, Boolean preserveSyncCtx)\r\n at System.Threading.ExecutionContext.Run(ExecutionContext executionContext, ContextCallback callback, Object state)\r\n at System.Net.TlsStream.ProcessAuthentication(LazyAsyncResult result)\r\n at System.Net.TlsStream.Write(Byte[] buffer, Int32 offset, Int32 size)\r\n at System.Net.Mail.SmtpConnection.Flush()\r\n at System.Net.Mail.ReadLinesCommand.Send(SmtpConnection conn)\r\n at System.Net.Mail.EHelloCommand.Send(SmtpConnection conn, String domain)\r\n at System.Net.Mail.SmtpConnection.GetConnection(ServicePoint servicePoint)\r\n at System.Net.Mail.SmtpClient.GetConnection()\r\n at System.Net.Mail.SmtpClient.Send(MailMessage message)\r\n at UserActor.UserActor.DeliverFeedbackMessage(String cur_msg, String remote_ip, String usr_agent) in C:\\testsrc\\DigitalRadar\\UserActor\\UserActor.cs:line 621" string
You're using SSL to connect to the mail server (smtp.mydomain.com). Check if the certificate on the mail server has a valid (CA signed) certificate. Maybe it's self-signed, or expired, or has a weak cypher.
Allright, I found a good workaround (without compromising security) to avoid this problem, I am answering my own question to help anyone that might have the same problem and end up here.
I should mention my SF version is 5.0.217, actors ver is 2.0.217, maybe newer ver. might not have the problem since the SF team is continuosly improving the framework. When I have some time I will check new ver and update this thread in case.
Coming back to the problem it seems the default cert validation fails when SmtpClient.Send is called from within a SF actor method. The same call made from a C# console app works great. The reason is beyond my understanding, maybe some SF glitch, anyway .NET allows to write and register a custom validation procedure to replace the default one, using this approach I solved the problem, but it's important to not blindly say "valid" to any certificate as I have seen suggested in many upvoted posts here on S.O, I recommend to actually check the certificate to see if it's good, otherwise you screw the security, then there is no point to use SSL or certificate at all. Having said that here is my validation code (adapt it to your own situation)
private bool MyValidateSmtpServerCertificate(object sender, X509Certificate certificate,
X509Chain chain, SslPolicyErrors sslPolicyErrors) {
if (sslPolicyErrors != SslPolicyErrors.None)
return false;
string[] subj_params = certificate.Subject.Split(',');
string common_name = string.Empty;
foreach (string param in subj_params) {
string[] sub_params = param.Split('=');
if (sub_params[0].Trim() == "CN")
common_name = sub_params[1].Trim();
}
string[] valid_names = {
common_const.smtpServer,
"*." + common_const.smtpServer,
};
if (!valid_names.Contains(common_name))
return false;
return true;
}
Register this function before calling the SmtpClient.Send with the following line:
ServicePointManager.ServerCertificateValidationCallback = MyValidateSmtpServerCertificate;
That's all, now my code inside the actor method works flawlessly, the email is securely and reliably delivered.

EntityFrameworkCore, trying to execute raw sql query but get error: "The connection was not closed. The connection's current state is open."

I need to run a raw sql query, but I'm getting an error when I try to open the connection to the database. "The connection was not closed. The connection's current state is open."
_loginValidator and _contactService are passed into the controller through DI:
services.AddScoped<ILoginValidator, LoginValidator>();
services.AddScoped<IContactService, ContactService>();
The two lines below are in an action function of the controller. If I switch the two lines, the error goes away...:
var validationErrors = _loginValidator.Validate(id, "");
var user = _contactService.GetContact(id);
Here is _loginValidator.Validate. If I comment out the second line, the error goes away...:
public LoginValidationResult Validate(int userId, string encryptedPassword)
{
var vr = new LoginValidationResult();
var user = _context.Users.Include(u => u.LoginUserQuestionAnswers).FirstOrDefault(u => u.Id == userId);
//...
}
Here is _contactService.GetContact. This is where I get the error:
public ContactDto GetContact(int id)
{
var conn = _context.Database.GetDbConnection();
//ERROR HERE!!!
conn.Open();
//work on conn, for example: ExecuteReader
conn.Close();
}
Notes:
If I comment out the _context line in the Validate(...) function, I do not get the error.
If I switch the two lines I listed in the action function, I do not get the error.
I think the problem is that EntityCore is not closing the connection after I finish using it in _loginValidator.Validate(...)
Anyone know how I can deal with this problem?
DB Connection is an unmanaged resource and you need to close it yourself. The best practice is to use a using statement for your DB connections.
See these links:
http://stackoverflow.com/questions/35077000/entity-framework-7-get-database-time
https://msdn.microsoft.com/en-us/data/dn456849.aspx
The connection being left open after the FirstOrDefault query is a bug. I filed https://github.com/aspnet/EntityFramework/issues/6581 for it and we just triaged it for the 1.0.2 release.
To workaround the bug for now I think you can check if the connection is already open and, if so, don't try to open it again.

JBoss return org.jboss.remoting.ProtocolException: Too many channels open

My program encountered a error:
"org.jboss.remoting3.ProtocolException: Too many channels open"
I have search from internet for some solutions to fix this error.Unfortunately, the suggestions from others is not working for me.
Below is the Code on how I call the jndi remote and the properties that I have used.
public static void createUser(String loginID) throws Exception {
Hashtable props = new Hashtable();
try {
props.put(Context.INITIAL_CONTEXT_FACTORY, "org.jboss.naming.remote.client.InitialContextFactory");
props.put(Context.PROVIDER_URL, "remote://" + localhost:4447);
props.put("jboss.naming.client.ejb.context", "true");
props.put(Context.SECURITY_PRINCIPAL, "userJBoss");
props.put(Context.URL_PKG_PREFIXES, "org.jboss.ejb.client.naming");
context = new InitialContext(props);
context.lookup("ejb:/createUserOperation/CreateUserGenerator!password.api.CreateUserService");
.....
......
LOGGER.info("DONE");
} catch (Exception e) {
LOGGER.error("ERROR");
} finally {
context.close();
}
}
Due to some certain reason I am not able to show all the content of the method.
The "createUser" will be call everytime when there is a needed of create new user. It will be call up to hundred or thousand time.
I did always close the connection when every time it finish execute the method.
Let say I have call the method for 100 times, some of the users will be created successfully whereas some of the users will be failed.
Error below will prompt to me:
2014-12-04 17:23:23,026 - ERROR [Remoting "config-based-naming-client-endpoint" task-4] (org.jboss.ejb.client.remoting.RemotingConnectionEJBReceiver- Line:134) - Failed to open channel for context EJBReceiverContext{clientContext=org.jboss.ejb.client.EJBClientContext#bbaebd6, receiver=Remoting connection EJB receiver [connection=Remoting connection <78e43506>,channel=jboss.ejb,nodename=webdev01]} org.jboss.remoting3.ProtocolException: Too many channels open
Once the error occurred, it required me to restart my jboss.And it comes again after sometimes.
Appreciate it if anyone wound able to help on my problem faced.
Thanks
You are using mixture of context properties.
This should be enough
final Properties ejbProperties = new Properties();
ejbProperties.put("remote.connectionprovider.create.options.org.xnio.Options.SSL_ENABLED", "false");
ejbProperties.put(Context.URL_PKG_PREFIXES, "org.jboss.ejb.client.naming");
ejbProperties.put("remote.connections", "1");
ejbProperties.put("remote.connection.1.host", "localhost");
ejbProperties.put("remote.connection.1.port", "4447");
ejbProperties.put("remote.connection.1.username", "ejbuser");
ejbProperties.put("remote.connection.1.password", "ejbuser123!");
final EJBClientConfiguration ejbClientConfiguration = new PropertiesBasedEJBClientConfiguration(ejbProperties);
final ConfigBasedEJBClientContextSelector selector = new ConfigBasedEJBClientContextSelector(ejbClientConfiguration);
EJBClientContext.setSelector(selector);
final Context context = new InitialContext(ejbProperties);
// lookup
Foo proxy = context.lookup("ejb:/createUserOperation/CreateUserGenerator!password.api.CreateUserService");
when using org.jboss.ejb.client.naming it creates EJBClientContext object.
When closing context you are closing InitialContext not EJBClientContext
to close EJBClientContext:
EJBClientContext.getCurrent().close();
There is a known JBoss bug (EAP 6, AS 7) whereby opening and closing too many InitialContext instances too quickly causes the following error:
ERROR: Failed to open channel for context EJBReceiverContext
Instead of:
final Properties properties = ...
final Context context = new InitialContext( properties );
Try caching the context for a set of properties instead:
private Map<Integer, InitialContext> initialContexts = new HashMap<>();
final Context context = getInitialContext(properties);
private InitialContext getInitialContext(final Properties properties) throws Exception {
final Integer hash = properties.hashCode();
InitialContext result = initialContexts.get(hash);
if (result == null) {
result = new InitialContext(properties);
initialContexts.put(hash, result);
}
return result;
}
Remember to call close() when the context is no longer necessary.

When is a started service not a started service? (SQL Express)

We require programmatic access to a SQL Server Express service as part of our application. Depending on what the user is trying to do, we may have to attach a database, detach a database, back one up, etc. Sometimes the service might not be started before we attempt these operations. So we need to ensure the service is started. Here is where we are running into problems. Apparently the ServiceController.WaitForStatus(ServiceControllerStatus.Running) returns prematurely for SQL Server Express. What is really puzzling is that the master database seems to be immediately available, but not other databases. Here is a console application to demonstrate what I am talking about:
namespace ServiceTest
{
using System;
using System.Data.SqlClient;
using System.Diagnostics;
using System.ServiceProcess;
using System.Threading;
class Program
{
private static readonly ServiceController controller = new ServiceController("MSSQL$SQLEXPRESS");
private static readonly Stopwatch stopWatch = new Stopwatch();
static void Main(string[] args)
{
stopWatch.Start();
EnsureStop();
Start();
OpenAndClose("master");
EnsureStop();
Start();
OpenAndClose("AdventureWorksLT");
Console.ReadLine();
}
private static void EnsureStop()
{
Console.WriteLine("EnsureStop enter, {0:N0}", stopWatch.ElapsedMilliseconds);
if (controller.Status != ServiceControllerStatus.Stopped)
{
controller.Stop();
controller.WaitForStatus(ServiceControllerStatus.Stopped);
Thread.Sleep(5000); // really, really make sure it stopped ... this has a problem too.
}
Console.WriteLine("EnsureStop exit, {0:N0}", stopWatch.ElapsedMilliseconds);
}
private static void Start()
{
Console.WriteLine("Start enter, {0:N0}", stopWatch.ElapsedMilliseconds);
controller.Start();
controller.WaitForStatus(ServiceControllerStatus.Running);
// Thread.Sleep(5000);
Console.WriteLine("Start exit, {0:N0}", stopWatch.ElapsedMilliseconds);
}
private static void OpenAndClose(string database)
{
Console.WriteLine("OpenAndClose enter, {0:N0}", stopWatch.ElapsedMilliseconds);
var connection = new SqlConnection(string.Format(#"Data Source=.\SQLEXPRESS;initial catalog={0};integrated security=SSPI", database));
connection.Open();
connection.Close();
Console.WriteLine("OpenAndClose exit, {0:N0}", stopWatch.ElapsedMilliseconds);
}
}
}
On my machine, this will consistently fail as written. Notice that the connection to "master" has no problems; only the connection to the other database. (You can reverse the order of the connections to verify this.) If you uncomment the Thread.Sleep in the Start() method, it will work fine.
Obviously I want to avoid an arbitrary Thread.Sleep(). Besides the rank code smell, what arbitary value would I put there? The only thing we can think of is to put some dummy connections to our target database in a while loop, catching the SqlException thrown and trying again until it works. But I'm thinking there must be a more elegant solution out there to know when the service is really ready to be used. Any ideas?
EDIT: Based on feedback provided below, I added a check on the status of the database. However, it is still failing. It looks like even the state is not reliable. Here is the function I am calling before OpenAndClose(string):
private static void WaitForOnline(string database)
{
Console.WriteLine("WaitForOnline start, {0:N0}", stopWatch.ElapsedMilliseconds);
using (var connection = new SqlConnection(string.Format(#"Data Source=.\SQLEXPRESS;initial catal
using (var command = connection.CreateCommand())
{
connection.Open();
try
{
command.CommandText = "SELECT [state] FROM sys.databases WHERE [name] = #DatabaseName";
command.Parameters.AddWithValue("#DatabaseName", database);
byte databaseState = (byte)command.ExecuteScalar();
Console.WriteLine("databaseState = {0}", databaseState);
while (databaseState != OnlineState)
{
Thread.Sleep(500);
databaseState = (byte)command.ExecuteScalar();
Console.WriteLine("databaseState = {0}", databaseState);
}
}
finally
{
connection.Close();
}
}
Console.WriteLine("WaitForOnline exit, {0:N0}", stopWatch.ElapsedMilliseconds);
}
I found another discussion dealing with a similar problem. Apparently the solution is to check the sys.database_files of the database in question. But that, of course, is a chicken-and-egg problem. Any other ideas?
Service start != database start.
Service is started when the SQL Server process is running and responded to the SCM that is 'alive'. After that the server will start putting user databases online. As part of this process, it runs the recovery process on each database, to ensure transactional consistency. Recovery of a database can last anywhere from microseconds to whole days, it depends on the ammount of log to be redone and the speed of the disk(s).
After the SCM returns that the service is running, you should connect to 'master' and check your database status in sys.databases. Only when the status is ONLINE can you proceed to open it.