Author Topic: Looking for NBDE Improvement Ideas or Known Limitations  (Read 363 times)

Legacy_Thayan

  • Sr. Member
  • ****
  • Posts: 435
  • Karma: +0/-0
Looking for NBDE Improvement Ideas or Known Limitations
« on: December 15, 2014, 03:45:28 pm »


               

Hey all. So we've got a nasty problem that surfaces about 1-2 times a month where a server crash wipes out one of our player housing databases. I'm trying to lock down the root cause of the problem, but it's proven to be very elusive. In the meantime, I'm trying to think of ways to improve the reliability or uncover any potential limitations of the NDBE system that we use for this database.


 


For those familiar with the system, I am fairly confident the NBDE_FlushCampaignDatabase() function is likely the reason the DB is being lost (it's down below). I believe what is happening is that either the DestroyCampaignDatabase or the SCO function within it crash, which results in the DB being destroyed, but then the NBDE object on which all the variables are located is not saved because one of those functions brings down the NWServer app before it can be saved.


 


So my questions to all NWN's clever individuals here are:


1. Is there a limitation to the number of variables that can be stored on an object which could cause problems with the SCO function? The database we are losing is the largest that NBDE handles. And while I have not counted up the variables being stored on the actual objet (I can do that if anyone things it would help), I'm pretty sure we're in the thousands.


 


2. Are there any redunancy checks you would recommend to the function below to avoid that millisecond delay in which the database has been destroyed, and the object only exists in the module before SCO is used to save it?


 


// this will flush (aka write to disk) the specified campaign database in one big swoop
//
// don't use this function in a rapid manner.
// delay each subsequent call to this function by at least 1 second (using delaycommand)
// this way you completely eliminate cpu-spikes, no matter how many database
// you flush.
void NBDE_FlushCampaignDatabase(string sCampaignName)
{
  // get database vault, it holds all database items
  object oVault = GetObjectByTag(NBDE_VAULT_TAG);
  if(GetIsObjectValid(oVault))
  {
    // get database item
    object oDatabase = GetLocalObject(oVault, NBDE_INDEX_PREFIX + sCampaignName);
    // store the whole database via one single StoreCampaignObject call
    // all variables on the item get stored with the item
    if(GetIsObjectValid(oDatabase))
    {
      // delete database on each flush to keep it compact and clean
      DestroyCampaignDatabase(sCampaignName);
      // store database
      StoreCampaignObject(sCampaignName, NBDE_DATABASE_ITEM_VARNAME , oDatabase);
    }
    // database not loaded, no need to flush...
  }
  else // vault container missing
    WriteTimestampedLogEntry("NBDE> Error: unable to locate '"+NBDE_VAULT_TAG+"' vault container object");
}


               
               

               
            

Legacy_Pstemarie

  • Hero Member
  • *****
  • Posts: 4368
  • Karma: +0/-0
Looking for NBDE Improvement Ideas or Known Limitations
« Reply #1 on: December 16, 2014, 01:06:29 am »


               

Send Henesua a PM - he's torn all through NDBE.


 


In my experience NDBE worked great UNTIL I deleted the original set of databases that had been created during testing. When I reloaded the module, NDBE could not recreate the databases and, when I restored the deleted test databases from my recycle bin, it would not save new data to them. I never did figure out what caused this, ultimately abandoning NDBE in favor of the vanilla Bioware system.



               
               

               
            

Legacy_henesua

  • Hero Member
  • *****
  • Posts: 6519
  • Karma: +0/-0
Looking for NBDE Improvement Ideas or Known Limitations
« Reply #2 on: December 16, 2014, 01:21:33 am »


               

I'm not aware of a limit to the number of variables, BUT the issue with the native database is that the database file explodes at a certain size. I forget what the exact size is but I think its the magic number - the value of 32 bits. I had never run into that until I switched to linux. And so I've just recently started to switch to mysql. It caught me by surprise as I had been running NBDE continuously for years on an OS X server and never had a problem.


 


Not sure if thats your problem, but its the only time its been an issue for me.


 


But that said… i would think you'd want a delay on database destruction of maybe 2 or 3 seconds. I know thats an eternity for a device that operates in miliseconds, but I'm a cautious guy. I'm not sure what the code i am using looks like.



               
               

               
            

Legacy_Thayan

  • Sr. Member
  • ****
  • Posts: 435
  • Karma: +0/-0
Looking for NBDE Improvement Ideas or Known Limitations
« Reply #3 on: December 16, 2014, 03:12:42 pm »


               

Thanks for the replies. The database in questions is 1544 KB right now. In my mind that doesn't seem like it is outrageously large, but maybe it is for being only one object...? I've not experienced the problems you've faced, Pstemarie, although maybe this is something of a symptom of that. The Windows event logs don't give me any more information than a general application fault, so I can't even tell if the SCO write actually tried to occur and the file was there/read-only or something, or not.


 


We have a standard Bioware DB for storage which doesn't use NBDE, and that thing can get enormous (at one point upwards of 100 MB before I discovered cdbflite to regularly compact it). But as far as I'm aware it has never caused a server crash and most importantly, it's never simply vanished if the server crashes.


 


henesua, I'm thinking that if I delay the DestroyCampaignDatabase function, it's actually going to wipe out the database that gets created/updated by the SCO function and any crash or reset would wipe out all the databases since the physical files for them would not exist. But if I'm not understanding how you'd plan to work the delay, please let me know. '<img'>



               
               

               
            

Legacy_henesua

  • Hero Member
  • *****
  • Posts: 6519
  • Karma: +0/-0
Looking for NBDE Improvement Ideas or Known Limitations
« Reply #4 on: December 16, 2014, 06:26:10 pm »


               

Inside the destroy campaign database function there must be code which destroys the actual database item in the NBDE chest. DestroyObject(oDBItem) or whatever. I would go inside the DestroyCampaignDatabase function and look for the actual DestroyObject call and determine whether destruction of the object has a long enough delay.



               
               

               
            

Legacy_Thayan

  • Sr. Member
  • ****
  • Posts: 435
  • Karma: +0/-0
Looking for NBDE Improvement Ideas or Known Limitations
« Reply #5 on: December 16, 2014, 06:53:42 pm »


               

DestroyCampaignDatabase is a standard database NWN function, so unfortunately I can't modify that. It removes the database file itself (on the OS).


 


In looking through the nbde_inc, it appears that the database items in the NBDE chest are never actually destroyed once they are created within it. The database item is just exported as *the* database whenever the NBDE_FlushCampaignDatabase function is called, but it always remains in the chest after the NBDE_GetCampaignDatabaseObject creates it (using the RCO database function) when it is first needed.



               
               

               
            

Legacy_Thayan

  • Sr. Member
  • ****
  • Posts: 435
  • Karma: +0/-0
Looking for NBDE Improvement Ideas or Known Limitations
« Reply #6 on: December 16, 2014, 09:55:39 pm »


               

Well, I've got a workaround for now, but I'm not a big fan of it. Still searching for the root cause. But anyway - the following is in a batch file scheduled to run every 15 minutes. This provides file system redundacy to provide a mostly up-to-date backup the property database. I guess it could be run even more often as it's pretty lightweight and the files (in my case) that are being copied are barely over 1 MB.



@echo off
:: variables
set drive=C:\NeverwinterNights\NWN\database
set backupcmd=copy /y

echo ### Backing up database...
%backupcmd% "%drive%\HH_PROPERTY.CDX" "%drive%\HH_PROPERTY_COPY.CDX"
%backupcmd% "%drive%\HH_PROPERTY.DBF" "%drive%\HH_PROPERTY_COPY.DBF"
%backupcmd% "%drive%\HH_PROPERTY.FPT" "%drive%\HH_PROPERTY_COPY.FPT"

echo Backup Complete!

After that, I ended up modifying the NBDE_GetCampaignDatabaseObject to look like this (the only change being the 7 lines below "// not found ? create it"):



// returns database object for the specified campaign database
//
// - auto-creates database object in case it doesn't exist
// - builds index for fast access
//
// you usually don't need to use this function directly...
object NBDE_GetCampaignDatabaseObject(string sCampaignName)
{
  // get database item
  object oDatabase = GetLocalObject(GetObjectByTag(NBDE_VAULT_TAG), NBDE_INDEX_PREFIX + sCampaignName);
  // retrieve/create database if not indexed already
  if(!GetIsObjectValid(oDatabase))
  {
    // get database vault object
    // this container holds all database objects/items
    object oVault = GetObjectByTag(NBDE_VAULT_TAG);
    // check for valid vault
    if(!GetIsObjectValid(oVault))
    {
      WriteTimestampedLogEntry("NBDE> Error: unable to locate '"+NBDE_VAULT_TAG+"' vault container object");
      return OBJECT_INVALID;
    }
    // one time load
    oDatabase = RetrieveCampaignObject(sCampaignName, NBDE_DATABASE_ITEM_VARNAME, GetLocation(oVault), oVault);
    // not found ? create it
    //if(!GetIsObjectValid(oDatabase)) oDatabase = CreateItemOnObject(NBDE_DATABASE_ITEM_RESREF, oVault);
    //Thayan modification for redundancy check. Need to have an OS batch file setup to make the database copy for this to work
    if(!GetIsObjectValid(oDatabase)) {
      oDatabase = RetrieveCampaignObject(sCampaignName+"_COPY", NBDE_DATABASE_ITEM_VARNAME, GetLocation(oVault), oVault);
      if(!GetIsObjectValid(oDatabase)) oDatabase = CreateItemOnObject(NBDE_DATABASE_ITEM_RESREF, oVault) ;
      else WriteTimestampedLogEntry("NBDE> Error: unable to retrieve '"+sCampaignName+"' database. Created from _COPY backup.");
    }
    // check for valid database object
    if(!GetIsObjectValid(oDatabase))
    {
      WriteTimestampedLogEntry("NBDE> Error: unable to create '"+sCampaignName+"' database object");
      return OBJECT_INVALID;
    }
    // index item for fast access
    SetLocalObject(oVault, NBDE_INDEX_PREFIX + sCampaignName, oDatabase);
  }
  return oDatabase;
}

I'm definitely not real happy about this workaround/redundancy option, and if anyone has better recommendations I'd love to hear them. But for now I guess it at least (mostly) masks the problem and, at worst, would mean 'only' 15 minutes of lost data if it ends up falling back to the database copy files.



               
               

               
            

Legacy_WhiZard

  • Hero Member
  • *****
  • Posts: 2149
  • Karma: +0/-0
Looking for NBDE Improvement Ideas or Known Limitations
« Reply #7 on: December 16, 2014, 11:41:27 pm »


               

Knat's system is not all that good if your server is prone to crashes.  Either you are overusing the hard storage from the database item to the actual one object database, in which case you are actually causing more inefficiency, or you are losing data that is not transferred when the server crashes.