Don't unload blocks if save failed

Improve error handling in saveBlock()
This commit is contained in:
kwolekr 2014-07-07 01:20:25 -04:00
parent e14c4cdd4c
commit 8b3ed78e53
11 changed files with 108 additions and 67 deletions

@ -45,7 +45,7 @@ int Database_Dummy::Initialized(void)
void Database_Dummy::beginSave() {} void Database_Dummy::beginSave() {}
void Database_Dummy::endSave() {} void Database_Dummy::endSave() {}
void Database_Dummy::saveBlock(MapBlock *block) bool Database_Dummy::saveBlock(MapBlock *block)
{ {
DSTACK(__FUNCTION_NAME); DSTACK(__FUNCTION_NAME);
/* /*
@ -53,7 +53,10 @@ void Database_Dummy::saveBlock(MapBlock *block)
*/ */
if(block->isDummy()) if(block->isDummy())
{ {
return; v3s16 p = block->getPos();
infostream<<"Database_Dummy::saveBlock(): WARNING: Not writing dummy block "
<<"("<<p.X<<","<<p.Y<<","<<p.Z<<")"<<std::endl;
return true;
} }
// Format used for writing // Format used for writing
@ -76,6 +79,7 @@ void Database_Dummy::saveBlock(MapBlock *block)
m_database[getBlockAsInteger(p3d)] = tmp; m_database[getBlockAsInteger(p3d)] = tmp;
// We just wrote it to the disk so clear modified flag // We just wrote it to the disk so clear modified flag
block->resetModified(); block->resetModified();
return true;
} }
MapBlock* Database_Dummy::loadBlock(v3s16 blockpos) MapBlock* Database_Dummy::loadBlock(v3s16 blockpos)

@ -33,8 +33,8 @@ public:
Database_Dummy(ServerMap *map); Database_Dummy(ServerMap *map);
virtual void beginSave(); virtual void beginSave();
virtual void endSave(); virtual void endSave();
virtual void saveBlock(MapBlock *block); virtual bool saveBlock(MapBlock *block);
virtual MapBlock* loadBlock(v3s16 blockpos); virtual MapBlock *loadBlock(v3s16 blockpos);
virtual void listAllLoadableBlocks(std::list<v3s16> &dst); virtual void listAllLoadableBlocks(std::list<v3s16> &dst);
virtual int Initialized(void); virtual int Initialized(void);
~Database_Dummy(); ~Database_Dummy();

@ -37,8 +37,8 @@ LevelDB databases
#include "log.h" #include "log.h"
#define ENSURE_STATUS_OK(s) \ #define ENSURE_STATUS_OK(s) \
if (!s.ok()) { \ if (!(s).ok()) { \
throw FileNotGoodException(std::string("LevelDB error: ") + s.ToString()); \ throw FileNotGoodException(std::string("LevelDB error: ") + (s).ToString()); \
} }
Database_LevelDB::Database_LevelDB(ServerMap *map, std::string savedir) Database_LevelDB::Database_LevelDB(ServerMap *map, std::string savedir)
@ -58,27 +58,29 @@ int Database_LevelDB::Initialized(void)
void Database_LevelDB::beginSave() {} void Database_LevelDB::beginSave() {}
void Database_LevelDB::endSave() {} void Database_LevelDB::endSave() {}
void Database_LevelDB::saveBlock(MapBlock *block) bool Database_LevelDB::saveBlock(MapBlock *block)
{ {
DSTACK(__FUNCTION_NAME); DSTACK(__FUNCTION_NAME);
v3s16 p3d = block->getPos();
/* /*
Dummy blocks are not written Dummy blocks are not written
*/ */
if(block->isDummy()) if(block->isDummy())
{ {
return; errorstream << "WARNING: saveBlock: Not writing dummy block "
<< PP(p3d) << std::endl;
return true;
} }
// Format used for writing // Format used for writing
u8 version = SER_FMT_VER_HIGHEST_WRITE; u8 version = SER_FMT_VER_HIGHEST_WRITE;
// Get destination
v3s16 p3d = block->getPos();
/* /*
[0] u8 serialization version [0] u8 serialization version
[1] data [1] data
*/ */
std::ostringstream o(std::ios_base::binary); std::ostringstream o(std::ios_base::binary);
o.write((char*)&version, 1); o.write((char*)&version, 1);
// Write basic data // Write basic data
@ -86,11 +88,17 @@ void Database_LevelDB::saveBlock(MapBlock *block)
// Write block to database // Write block to database
std::string tmp = o.str(); std::string tmp = o.str();
leveldb::Status status = m_database->Put(leveldb::WriteOptions(), i64tos(getBlockAsInteger(p3d)), tmp); leveldb::Status status = m_database->Put(leveldb::WriteOptions(),
ENSURE_STATUS_OK(status); i64tos(getBlockAsInteger(p3d)), tmp);
if (!status.ok()) {
errorstream << "WARNING: saveBlock: LevelDB error saving block "
<< PP(p3d) << ": " << status.ToString() << std::endl;
return false;
}
// We just wrote it to the disk so clear modified flag // We just wrote it to the disk so clear modified flag
block->resetModified(); block->resetModified();
return true;
} }
MapBlock* Database_LevelDB::loadBlock(v3s16 blockpos) MapBlock* Database_LevelDB::loadBlock(v3s16 blockpos)

@ -36,8 +36,8 @@ public:
Database_LevelDB(ServerMap *map, std::string savedir); Database_LevelDB(ServerMap *map, std::string savedir);
virtual void beginSave(); virtual void beginSave();
virtual void endSave(); virtual void endSave();
virtual void saveBlock(MapBlock *block); virtual bool saveBlock(MapBlock *block);
virtual MapBlock* loadBlock(v3s16 blockpos); virtual MapBlock *loadBlock(v3s16 blockpos);
virtual void listAllLoadableBlocks(std::list<v3s16> &dst); virtual void listAllLoadableBlocks(std::list<v3s16> &dst);
virtual int Initialized(void); virtual int Initialized(void);
~Database_LevelDB(); ~Database_LevelDB();

@ -81,21 +81,24 @@ void Database_Redis::endSave() {
freeReplyObject(reply); freeReplyObject(reply);
} }
void Database_Redis::saveBlock(MapBlock *block) bool Database_Redis::saveBlock(MapBlock *block)
{ {
DSTACK(__FUNCTION_NAME); DSTACK(__FUNCTION_NAME);
v3s16 p3d = block->getPos();
/* /*
Dummy blocks are not written Dummy blocks are not written
*/ */
if(block->isDummy()) if(block->isDummy())
{ {
return; errorstream << "WARNING: saveBlock: Not writing dummy block "
<< PP(p3d) << std::endl;
return true;
} }
// Format used for writing // Format used for writing
u8 version = SER_FMT_VER_HIGHEST_WRITE; u8 version = SER_FMT_VER_HIGHEST_WRITE;
// Get destination
v3s16 p3d = block->getPos();
/* /*
[0] u8 serialization version [0] u8 serialization version
@ -110,16 +113,26 @@ void Database_Redis::saveBlock(MapBlock *block)
std::string tmp1 = o.str(); std::string tmp1 = o.str();
std::string tmp2 = i64tos(getBlockAsInteger(p3d)); std::string tmp2 = i64tos(getBlockAsInteger(p3d));
redisReply *reply; redisReply *reply = (redisReply *)redisCommand(ctx, "HSET %s %s %b",
reply = (redisReply*) redisCommand(ctx, "HSET %s %s %b", hash.c_str(), tmp2.c_str(), tmp1.c_str(), tmp1.size()); hash.c_str(), tmp2.c_str(), tmp1.c_str(), tmp1.size());
if(!reply) if (!reply) {
throw FileNotGoodException(std::string("redis command 'HSET %s %s %b' failed: ") + ctx->errstr); errorstream << "WARNING: saveBlock: redis command 'HSET' failed on "
if(reply->type == REDIS_REPLY_ERROR) "block " << PP(p3d) << ": " << ctx->errstr << std::endl;
throw FileNotGoodException("Failed to store block in Database"); freeReplyObject(reply);
freeReplyObject(reply); return false;
}
if (reply->type == REDIS_REPLY_ERROR) {
errorstream << "WARNING: saveBlock: save block " << PP(p3d)
<< "failed" << std::endl;
freeReplyObject(reply);
return false;
}
// We just wrote it to the disk so clear modified flag // We just wrote it to the disk so clear modified flag
block->resetModified(); block->resetModified();
freeReplyObject(reply);
return true;
} }
MapBlock* Database_Redis::loadBlock(v3s16 blockpos) MapBlock* Database_Redis::loadBlock(v3s16 blockpos)

@ -36,8 +36,8 @@ public:
Database_Redis(ServerMap *map, std::string savedir); Database_Redis(ServerMap *map, std::string savedir);
virtual void beginSave(); virtual void beginSave();
virtual void endSave(); virtual void endSave();
virtual void saveBlock(MapBlock *block); virtual bool saveBlock(MapBlock *block);
virtual MapBlock* loadBlock(v3s16 blockpos); virtual MapBlock *loadBlock(v3s16 blockpos);
virtual void listAllLoadableBlocks(std::list<v3s16> &dst); virtual void listAllLoadableBlocks(std::list<v3s16> &dst);
virtual int Initialized(void); virtual int Initialized(void);
~Database_Redis(); ~Database_Redis();

@ -136,25 +136,24 @@ void Database_SQLite3::verifyDatabase() {
infostream<<"ServerMap: SQLite3 database opened"<<std::endl; infostream<<"ServerMap: SQLite3 database opened"<<std::endl;
} }
void Database_SQLite3::saveBlock(MapBlock *block) bool Database_SQLite3::saveBlock(MapBlock *block)
{ {
DSTACK(__FUNCTION_NAME); DSTACK(__FUNCTION_NAME);
v3s16 p3d = block->getPos();
/* /*
Dummy blocks are not written Dummy blocks are not written, but is not a save failure
*/ */
if(block->isDummy()) if(block->isDummy())
{ {
/*v3s16 p = block->getPos(); errorstream << "WARNING: saveBlock: Not writing dummy block "
infostream<<"Database_SQLite3::saveBlock(): WARNING: Not writing dummy block " << PP(p3d) << std::endl;
<<"("<<p.X<<","<<p.Y<<","<<p.Z<<")"<<std::endl;*/ return true;
return;
} }
// Format used for writing // Format used for writing
u8 version = SER_FMT_VER_HIGHEST_WRITE; u8 version = SER_FMT_VER_HIGHEST_WRITE;
// Get destination
v3s16 p3d = block->getPos();
#if 0 #if 0
v2s16 p2d(p3d.X, p3d.Z); v2s16 p2d(p3d.X, p3d.Z);
@ -176,29 +175,39 @@ void Database_SQLite3::saveBlock(MapBlock *block)
std::ostringstream o(std::ios_base::binary); std::ostringstream o(std::ios_base::binary);
o.write((char*)&version, 1); o.write((char *)&version, 1);
// Write basic data // Write basic data
block->serialize(o, version, true); block->serialize(o, version, true);
// Write block to database // Write block to database
std::string tmp = o.str(); std::string tmp = o.str();
const char *bytes = tmp.c_str();
if(sqlite3_bind_int64(m_database_write, 1, getBlockAsInteger(p3d)) != SQLITE_OK) if (sqlite3_bind_int64(m_database_write, 1, getBlockAsInteger(p3d)) != SQLITE_OK) {
errorstream<<"WARNING: Block position failed to bind: "<<sqlite3_errmsg(m_database)<<std::endl; errorstream << "WARNING: saveBlock: Block position failed to bind: "
if(sqlite3_bind_blob(m_database_write, 2, (void *)bytes, o.tellp(), NULL) != SQLITE_OK) // TODO this mught not be the right length << PP(p3d) << ": " << sqlite3_errmsg(m_database) << std::endl;
errorstream<<"WARNING: Block data failed to bind: "<<sqlite3_errmsg(m_database)<<std::endl; sqlite3_reset(m_database_write);
int written = sqlite3_step(m_database_write); return false;
if(written != SQLITE_DONE) }
errorstream<<"WARNING: Block failed to save ("<<p3d.X<<", "<<p3d.Y<<", "<<p3d.Z<<") "
<<sqlite3_errmsg(m_database)<<std::endl; if (sqlite3_bind_blob(m_database_write, 2, (void *)tmp.c_str(), tmp.size(), NULL) != SQLITE_OK) {
// Make ready for later reuse errorstream << "WARNING: saveBlock: Block data failed to bind: "
sqlite3_reset(m_database_write); << PP(p3d) << ": " << sqlite3_errmsg(m_database) << std::endl;
sqlite3_reset(m_database_write);
return false;
}
if (sqlite3_step(m_database_write) != SQLITE_DONE) {
errorstream << "WARNING: saveBlock: Block failed to save "
<< PP(p3d) << ": " << sqlite3_errmsg(m_database) << std::endl;
sqlite3_reset(m_database_write);
return false;
}
// We just wrote it to the disk so clear modified flag // We just wrote it to the disk so clear modified flag
block->resetModified(); block->resetModified();
sqlite3_reset(m_database_write);
return true;
} }
MapBlock* Database_SQLite3::loadBlock(v3s16 blockpos) MapBlock* Database_SQLite3::loadBlock(v3s16 blockpos)

@ -36,8 +36,8 @@ public:
virtual void beginSave(); virtual void beginSave();
virtual void endSave(); virtual void endSave();
virtual void saveBlock(MapBlock *block); virtual bool saveBlock(MapBlock *block);
virtual MapBlock* loadBlock(v3s16 blockpos); virtual MapBlock *loadBlock(v3s16 blockpos);
virtual void listAllLoadableBlocks(std::list<v3s16> &dst); virtual void listAllLoadableBlocks(std::list<v3s16> &dst);
virtual int Initialized(void); virtual int Initialized(void);
~Database_SQLite3(); ~Database_SQLite3();

@ -24,19 +24,23 @@ with this program; if not, write to the Free Software Foundation, Inc.,
#include "irr_v3d.h" #include "irr_v3d.h"
#include "irrlichttypes.h" #include "irrlichttypes.h"
#ifndef PP
#define PP(x) "("<<(x).X<<","<<(x).Y<<","<<(x).Z<<")"
#endif
class MapBlock; class MapBlock;
class Database class Database
{ {
public: public:
virtual void beginSave()=0; virtual void beginSave() = 0;
virtual void endSave()=0; virtual void endSave() = 0;
virtual void saveBlock(MapBlock *block)=0; virtual bool saveBlock(MapBlock *block) = 0;
virtual MapBlock* loadBlock(v3s16 blockpos)=0; virtual MapBlock *loadBlock(v3s16 blockpos) = 0;
s64 getBlockAsInteger(const v3s16 pos) const; s64 getBlockAsInteger(const v3s16 pos) const;
v3s16 getIntegerAsBlock(s64 i) const; v3s16 getIntegerAsBlock(s64 i) const;
virtual void listAllLoadableBlocks(std::list<v3s16> &dst)=0; virtual void listAllLoadableBlocks(std::list<v3s16> &dst) = 0;
virtual int Initialized(void)=0; virtual int Initialized(void)=0;
virtual ~Database() {}; virtual ~Database() {};
}; };

@ -1473,11 +1473,11 @@ void Map::timerUpdate(float dtime, float unload_timeout,
v3s16 p = block->getPos(); v3s16 p = block->getPos();
// Save if modified // Save if modified
if(block->getModified() != MOD_STATE_CLEAN if (block->getModified() != MOD_STATE_CLEAN && save_before_unloading)
&& save_before_unloading)
{ {
modprofiler.add(block->getModifiedReason(), 1); modprofiler.add(block->getModifiedReason(), 1);
saveBlock(block); if (!saveBlock(block))
continue;
saved_blocks_count++; saved_blocks_count++;
} }
@ -3253,20 +3253,23 @@ bool ServerMap::loadSectorFull(v2s16 p2d)
} }
#endif #endif
void ServerMap::beginSave() { void ServerMap::beginSave()
{
dbase->beginSave(); dbase->beginSave();
} }
void ServerMap::endSave() { void ServerMap::endSave()
{
dbase->endSave(); dbase->endSave();
} }
void ServerMap::saveBlock(MapBlock *block) bool ServerMap::saveBlock(MapBlock *block)
{ {
dbase->saveBlock(block); return dbase->saveBlock(block);
} }
void ServerMap::loadBlock(std::string sectordir, std::string blockfile, MapSector *sector, bool save_after_load) void ServerMap::loadBlock(std::string sectordir, std::string blockfile,
MapSector *sector, bool save_after_load)
{ {
DSTACK(__FUNCTION_NAME); DSTACK(__FUNCTION_NAME);

@ -270,7 +270,7 @@ public:
// Server implements this. // Server implements this.
// Client leaves it as no-op. // Client leaves it as no-op.
virtual void saveBlock(MapBlock *block){}; virtual bool saveBlock(MapBlock *block){ return false; };
/* /*
Updates usage timers and unloads unused blocks and sectors. Updates usage timers and unloads unused blocks and sectors.
@ -485,7 +485,7 @@ public:
// Returns true if sector now resides in memory // Returns true if sector now resides in memory
//bool deFlushSector(v2s16 p2d); //bool deFlushSector(v2s16 p2d);
void saveBlock(MapBlock *block); bool saveBlock(MapBlock *block);
// This will generate a sector with getSector if not found. // This will generate a sector with getSector if not found.
void loadBlock(std::string sectordir, std::string blockfile, MapSector *sector, bool save_after_load=false); void loadBlock(std::string sectordir, std::string blockfile, MapSector *sector, bool save_after_load=false);
MapBlock* loadBlock(v3s16 p); MapBlock* loadBlock(v3s16 p);