0

I have an ESE database ("Vulcan.edb") that I've copied from an in-use application. Naturally, this means the database is in a "dirty shutdown" state. Fortunately, I do have the log file of the database ("V01.log"), according to every similar question I can find, I should be able to attach the database and have the engine recover it. Here is the code I'm using to attach the database, using the managed ESE API (an unmanaged C++ port should be trivial):

public void ReadFromDatabase(string fileName, string logFileBaseName)
{
    string databaseDirectory = Path.GetDirectoryName(fileName);

    int pageSize;
    Api.JetGetDatabaseFileInfo(fileName, out pageSize, JET_DbInfo.PageSize);
    Api.JetSetSystemParameter(JET_INSTANCE.Nil, JET_SESID.Nil, JET_param.DatabasePageSize, pageSize, null);

    Api.JetSetSystemParameter(JET_INSTANCE.Nil, JET_SESID.Nil, JET_param.Recovery, 0, "Off");

    JET_INSTANCE jetInstance;
    string jetInstanceName = Guid.NewGuid().ToString("N");
    Api.JetCreateInstance(out jetInstance, jetInstanceName);

    Api.JetSetSystemParameter(jetInstance, JET_SESID.Nil, JET_param.BaseName, 0, logFileBaseName);
    Api.JetSetSystemParameter(jetInstance, JET_SESID.Nil, JET_param.LogFilePath, 0, databaseDirectory);
    Api.JetSetSystemParameter(jetInstance, JET_SESID.Nil, JET_param.TempPath, 0, databaseDirectory);
    Api.JetSetSystemParameter(jetInstance, JET_SESID.Nil, JET_param.SystemPath, 0, databaseDirectory);
    Api.JetSetSystemParameter(jetInstance, JET_SESID.Nil, Microsoft.Isam.Esent.Interop.Server2003.Server2003Param.AlternateDatabaseRecoveryPath, 0, databaseDirectory);
    try
    {
        Api.JetInit(ref jetInstance);

        JET_SESID sessionId;
        Api.JetBeginSession(jetInstance, out sessionId, "admin", "");

        try
        {
            Api.JetAttachDatabase(sessionId, fileName, AttachDatabaseGrbit.ReadOnly);

            JET_DBID databaseId;
            Api.JetOpenDatabase(sessionId, fileName, "", out databaseId, OpenDatabaseGrbit.ReadOnly);

            // Do other stuff here...

            Api.JetCloseDatabase(sessionId, databaseId, CloseDatabaseGrbit.None);
        }
        finally
        {
            Api.JetEndSession(sessionId, EndSessionGrbit.None);
        }
    }
    finally
    {
        Api.JetTerm(jetInstance);
    }
}

Indeed, if I copy both the database file and the log file to <directory of database> and run edbntutl.exe /r V01 /l <directory of database> /s <directory of database> /d <directory of database>, the database is rendered usable and attachable in my code. However, if I don't take the step of manually recovering the database, my code fails calling JetAttachDatabase with an error of JET_errDatabaseDirtyShutdown.

So why doesn't my code automatically recover the database? Either edbntutl is doing something undocumented, or I'm missing something in my configuration of the session to allow the engine to recover the database. While the former isn't out of the question, it would be incredibly unfortunate if that were the case, and I'm inclined to believe I'm just missing something instead.

Note: Answers which amount to "run esentutil before running your code" will not be accepted as correct without evidence that the utility is doing something not available in the public API.

JimEvans
  • 27,201
  • 7
  • 83
  • 108

1 Answers1

0

You need to do a bunch of things to robustly handle loading of esent dbs. Here is the relevant code that you need in order to handle dirty shutdown, from the RavenDB codebase:

https://github.com/ravendb/ravendb/blob/552208aed679f4a3936a57f9a7506568657e291e/Raven.Database/Storage/Esent/TransactionalStorage.cs#L721-L736

Ayende Rahien
  • 22,925
  • 1
  • 36
  • 41