2017-03-29 12 views
0

Wt::Dbo을 MySQL과 객체 관계형 데이터베이스 관리로 사용하는 프로젝트가 있습니다. 약 1 주일이 지난 후에 나는 데이터베이스 준비 문에서 누수를 언급했다. 이전에이 프로젝트는 SQLite를 사용하고있었습니다.Wt :: Dbo를 사용하면 MySQL에서 준비된 문이 누출됩니다.

다른 flush()을 성공하지 못하고 시도했는데 정확하게이 누출의 원인을 파악할 수 없지만 준비된 문장이 특정 지점에서 커지면 MySQL은 응답을 중지합니다. 프로그램을 다시 시작할 때

$ mysql -uroot -p -e "SHOW SESSION STATUS LIKE '%prepare%';" | grep stmt_count 
Enter password: 
Prepared_stmt_count 260 

유출 된 문이 지워집니다 : 여기

내가 유출 준비된 문장을 모니터링하는 방법입니다.

데이터베이스 :() (

void DataBase::initialize(void) 
{ 
    m_oMutex.lock(); 

    if(!m_bInitialized) 
    { 
     m_bInitialized = true; 

     try 
     { 
      m_oSessionConfigurations.setConnection(*new Wt::Dbo::backend::Sqlite3("databases/configuration.db")); 
      m_oSessionConfigurations.mapClass<InformationSite>("informationsite"); 

      m_oSessionConfigurations.createTables(); 
     } 
     catch(std::exception &e) 
     { 
      ajouterLog("error",std::string("DataBase::initialize() : ") + e.what()); 
     } 

     try 
     { 
      #if defined(DATABASE_TYPE_SQLITE) 
       Wt::Dbo::backend::Sqlite3 *pBackend = new Wt::Dbo::backend::Sqlite3("databases/dataBase.db"); 
      #elif defined(DATABASE_TYPE_MYSQL) 
       Wt::Dbo::backend::MySQL *pBackend; 

       try 
       { 
        pBackend = new Wt::Dbo::backend::MySQL(DATABASE_NAME,DATABASE_USERNAME,DATABASE_PASSWORD,"localhost"); 
       } 
       catch(Wt::Dbo::Exception &e) 
       { 
        ajouterLog("error",std::string("DataBase::initialize() : ") + e.what()); 

        // If MySQL is not available, this cause issue to program until restart. 
        exit(1); 
       } 
      #endif 

      pBackend->setProperty("show-queries","true"); 

      m_oSession.setConnection(*pBackend); 

      m_oSession.setFlushMode(Wt::Dbo::FlushMode::Auto); 

      m_oSession.mapClass<RFNode>("rfnode"); 
      m_oSession.mapClass<NodeMeasure>("nodemeasure"); 

      // Override the default InnoDB from Wt, MyISAM is easier to repair in case of hardware failure with database corruption 
      #if defined(DATABASE_TYPE_MYSQL) 
       try 
       { 
        Wt::Dbo::Transaction oTransaction(m_oSession); 

        m_oSession.execute("SET default_storage_engine=MYISAM;"); 

        oTransaction.commit(); 
       } 
       catch(Wt::Dbo::Exception &e) 
       { 
        ajouterLog("error",std::string("DataBase::initialize() : ") + e.what()); 
       } 
      #endif 

      m_oSession.createTables(); 
     } 
     catch(Wt::Dbo::Exception &e) 
     { 
      ajouterLog("error",std::string("DataBase::initialize() : ") + e.what()); 
     } 
    } 

    m_oMutex.unlock(); 
} 

데이터베이스 :: addNodeMeasure를 초기화 :

모든 데이터베이스 작업

라는 클래스 DataBase 내부에 집중되어, 여기에 누설하는 것으로 알려진 일부 기능은)

void DataBase::addNodeMeasure(NodeMeasure *p_pItem) 
{ 
    m_oMutex.lock(); 

    try 
    { 
     Wt::Dbo::Transaction oTransaction(m_oSession); 

     Wt::Dbo::ptr<NodeMeasure> oItem = m_oSession.add(p_pItem); 

     oItem.flush(); 

     oTransaction.commit(); 
    } 
    catch(std::exception &e) 
    { 
     ajouterLog("error",std::string("Exception DataBase::addNodeMeasure() : ") + e.what()); 
    } 

    m_oMutex.unlock(); 

    printPreparedStatementCount("DataBase::addNodeMeasure()"); 
} 

데이터베이스 :: updateNode()

void DataBase::updateNode(RFNode *p_pItem) 
{ 
    printPreparedStatementCount("DataBase::updateNode() Before"); 

    m_oMutex.lock(); 

    try 
    { 
     Wt::Dbo::Transaction oTransaction(m_oSession); 

     Wt::Dbo::ptr<RFNode> oItem = m_oSession.find<RFNode>().where("mac = ?").bind(p_pItem->mac); 

     oItem.modify()->zone     = p_pItem->zone; 
     oItem.modify()->subZone     = p_pItem->subZone; 
     oItem.modify()->unit     = p_pItem->unit; 
     oItem.modify()->pwm      = p_pItem->pwm; 
     oItem.modify()->led      = p_pItem->led; 
     oItem.modify()->network     = p_pItem->network; 
     oItem.modify()->lastContact    = p_pItem->lastContact; 
     oItem.modify()->ioConfiguration   = p_pItem->ioConfiguration; 
     oItem.modify()->networkAddress   = p_pItem->networkAddress; 
     oItem.modify()->type     = p_pItem->type; 
     oItem.modify()->functionality   = p_pItem->functionality; 
     oItem.modify()->transmitPowerLevel  = p_pItem->transmitPowerLevel; 
     oItem.modify()->lastNetworkRoute  = p_pItem->lastNetworkRoute; 
     oItem.modify()->lastNetworkJumpsCount = p_pItem->lastNetworkJumpsCount; 
     oItem.modify()->lastRequestDuration  = p_pItem->lastRequestDuration; 
     oItem.modify()->hardwareVersion   = p_pItem->hardwareVersion; 
     oItem.modify()->softwareVersion   = p_pItem->softwareVersion; 

     oItem.flush(); 

     oTransaction.commit(); 
    } 
    catch(std::exception &e) 
    { 
     ajouterLog("error",std::string("Exception DataBase::updateNode() : ") + e.what()); 
    } 

    m_oMutex.unlock(); 

    printPreparedStatementCount("DataBase::updateNode() After"); 
} 

데이터베이스 :: getNodeMeasures()

std::vector<NodeMeasure> DataBase::getNodeMeasures(std::string p_sMAC, int p_nType, Wt::WDateTime p_oStartDate, Wt::WDateTime p_oEndDate, std::string p_sOrder, int p_nLimit) 
{ 
    std::vector<NodeMeasure> lNodeMeasures; 

    m_oMutex.lock(); 

    try 
    { 
     Wt::Dbo::Transaction oTransaction(m_oSession); 

     std::string sWhereClause = "", sOrderClause = ""; 

     if(!p_sMAC.empty()) 
     { 
      if(!sWhereClause.empty()) 
      { 
       sWhereClause += " AND "; 
      } 

      sWhereClause += "mac = '" + p_sMAC + "'"; 
     } 

     if(p_nType != -1) 
     { 
      if(!sWhereClause.empty()) 
      { 
       sWhereClause += " AND "; 
      } 

      sWhereClause += "type = " + std::to_string(p_nType); 
     } 

     if(p_oStartDate.isValid()) 
     { 
      if(!sWhereClause.empty()) 
      { 
       sWhereClause += " AND "; 
      } 

      // When not using type, we usually want nodes measures (not external temperature), so we want to find them using batchDate instead of date 
      sWhereClause += (p_nType != -1 ? "date" : "batchDate"); 
      sWhereClause += " >= '"; 
      sWhereClause += p_oStartDate.toString("yyyy-MM-ddTHH:mm:ss").toUTF8(); 
      sWhereClause += "'"; 
     } 

     if(p_oEndDate.isValid()) 
     { 
      if(!sWhereClause.empty()) 
      { 
       sWhereClause += " AND "; 
      } 

      // When not using type, we usually want nodes measures (not external temperature), so we want to find them using batchDate instead of date 
      sWhereClause += (p_nType != -1 ? "date" : "batchDate"); 
      sWhereClause += " <= '"; 
      // Add one second because SQLite have microseconds, and we must include results no matter microseconds field 
      sWhereClause += p_oEndDate.addSecs(1).toString("yyyy-MM-ddTHH:mm:ss").toUTF8(); 
      sWhereClause += "'"; 
     } 

     if(!p_sOrder.empty()) 
     { 
      sOrderClause = " ORDER BY " + p_sOrder; 
     } 

     std::string sQuery = ""; 

     if(!sWhereClause.empty()) 
     { 
      sQuery += " WHERE "; 
      sQuery += sWhereClause; 
     } 

     if(!sOrderClause.empty()) 
     { 
      sQuery += sOrderClause; 
     } 

     //std::cout << "**************************************************************************" << std::endl; 
     //std::cout << sQuery << std::endl; 
     //Wt::WDateTime oStart = Wt::WDateTime::currentDateTime(); 

     if(Configuration::getParameter(Configuration::PARAMETER_DEBUG).getBooleanValue()) 
     { 
      ajouterLog("debug","DataBase::getNodeMeasures() " + sQuery); 
     } 

     // TEST : find vs query 
     Wt::Dbo::collection<Wt::Dbo::ptr<NodeMeasure>> lMeasures = m_oSession.find<NodeMeasure>(sQuery).limit(p_nLimit).resultList(); 

     // TODO : Get it cleaner... can't use Wt::Dbo::ptr outside transaction. 
     for(Wt::Dbo::collection<Wt::Dbo::ptr<NodeMeasure>>::const_iterator pMeasure = lMeasures.begin();pMeasure != lMeasures.end();pMeasure++) 
     { 
      lNodeMeasures.push_back(
        NodeMeasure(
          (*pMeasure)->mac, 
          (*pMeasure)->type, 
          (*pMeasure)->date, 
          (*pMeasure)->batchDate, 
          (*pMeasure)->value 
        ) 
      ); 

      (*pMeasure).flush(); 
     } 

     //lNodeMeasures = m_oSession.find<NodeMeasure>(sQuery).limit(p_nLimit).resultList(); 

     //std::cout << "Result : " << lNodeMeasures.size() << " in " << oStart.secsTo(Wt::WDateTime::currentDateTime()) << "s" << std::endl; 
     //std::cout << "**************************************************************************" << std::endl; 

     oTransaction.commit(); 
    } 
    catch(std::exception &e) 
    { 
     ajouterLog("error",std::string("Exception DataBase::getNodeMeasures() : ") + e.what()); 
    } 

    m_oMutex.unlock(); 

    printPreparedStatementCount("DataBase::getNodeMeasures()"); 

    return lNodeMeasures; 
} 

답변

1

는 문장의 크기가 통제 불능 성장합니까? Wt :: Dbo는 MySQL SqlConnection 오브젝트의 모든 명령문을 캐시하고 파기시 지워 버립니다. 애플리케이션은 종종 예를 들어, 크기가 10 인 경우 준비된 명령문의 양은 종종 사용하는 명령문의 양의 배수입니다.

매개 변수 바인딩을 사용하는 대신 구문을 ASCII 텍스트로 작성하는 것처럼 보이므로 캐시에 많은 문이 작성 될 수 있습니다.

DBO 코드 스타일이 유형의 지원

auto measuresQuery = m_oSession.find<NodeMeasure>(sQuery).limit(p_nLimit); 
    if(!p_sMAC.empty()) 
    { 
     measureQuery.where("mac = ?").bind(p_sMAC); 
    } 

    if(p_nType != -1) 
    { 
     measureQuery.where("type = ?").bind(std::to_string(p_nType)); 
    } 
    if(p_oStartDate.isValid()) 
    { 
     // When not using type, we usually want nodes measures (not external temperature), so we want to find them using batchDate instead of date 
     measureQuery.where(p_nType != -1 ? "date >= ?" : "batchDate >= ?").bind(p_oStartData); 
    } 
    if(!p_sOrder.empty()) 
    { 
     measureQuery.orderBy(p_sOrder); 
    } 

    Wt::Dbo::collection<Wt::Dbo::ptr<NodeMeasure>> lMeasures = measureQuery.resultList(); 

이 준비된 명령문의 적은 양을 얻을 것입니다, 그리고 내 생각에, 짧은 선명하고, 더 안전한 코드입니다.

+0

실제로 하나의 연결 만 사용합니다. 응용 프로그램이 시작될 때부터 충돌 또는 재부팅까지 연중 무휴입니다. MySQL이 적절하게 응답하기를 원하지 않을 때까지 (약 5000-10000) 명령문이 커질 수 있습니다. 이 캐시를 사용 중지하는 방법이 있습니까? 나는 다른 프레임 워크를 보지 못했다! –

+0

어디서 바인드했는지, 오래 전에 사용했지만 예상대로 작동하지 않았으므로 다시 시도하겠습니다. –

+0

캐시를 비활성화 할 수 없습니다. MySqlConnection에서 캐시 (clearStatementCahce())를 지우는 보호 된 메서드가 있지만이 문제에 대한 올바른 대답은 아닌 것 같습니다. 준비된 문과 바인딩 매개 변수를 실제로 사용하는 것이 좋습니다. SQL Server는 웹 응용 프로그램에서 가장 중요한 보안 결함 중 하나 인 SQL 주입에 대해 보안 성이 향상됩니다 (SQL Server는 재사용 된 각 쿼리를 한 번만 최적화합니다). – user52875