diff -r 000000000000 -r 08ec8eefde2f persistentstorage/dbms/tdbms/t_dbstress.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/persistentstorage/dbms/tdbms/t_dbstress.cpp Fri Jan 22 11:06:30 2010 +0200 @@ -0,0 +1,600 @@ +// Copyright (c) 1998-2009 Nokia Corporation and/or its subsidiary(-ies). +// All rights reserved. +// This component and the accompanying materials are made available +// under the terms of "Eclipse Public License v1.0" +// which accompanies this distribution, and is available +// at the URL "http://www.eclipse.org/legal/epl-v10.html". +// +// Initial Contributors: +// Nokia Corporation - initial contribution. +// +// Contributors: +// +// Description: +// + +#include "t_dbstress.h" + +//#define __DUMP_STATE +#if defined(__WINS__) +//#define _INSTALL_FILE_SYSTEM +#endif + +GLDEF_D RTest test(_L("t_dbstress: Stress testing DBMS")); + +GLDEF_D TPtrC KTestDir=_S("\\DBMS-TST\\"); +GLDEF_D TPtrC KLogFile=_L("T_STRESS.LOG"); +GLDEF_D TPtrC KTestDatabase=_S("T_STRESS.DB"); +GLDEF_D TInt NewCount,OldCount; +GLDEF_D TInt TransId; +GLDEF_D Timer RunTimer; + + +LOCAL_D RFs TheFs; +LOCAL_D RThread TheThread; +LOCAL_D TRequestStatus TheStatus; +LOCAL_D RDbStoreDatabase TheDatabase; +LOCAL_D RDbView Accs; +LOCAL_D RDbView Trans; +LOCAL_D RDbTable TheTable; +LOCAL_D TInt Shot,ShotDuringCommit; +LOCAL_D TInt64 RunningTime(1); +LOCAL_D Timer Stopwatch; + +#ifdef __DUMP_STATE +const TPtrC KDumpFile=_S("T_STRESS.DMP"); +#endif +const TInt KTestCleanupStack=0x20; + +void Timer::Start() + { + iTime.UniversalTime(); + } + +TInt64 Timer::Stop() + { + TTime t; + t.UniversalTime(); + return ((t.MicroSecondsFrom(iTime).Int64()) + 500)/1000; + } + +void Timer::Print() + { + TInt64 milli=Stop(); + test.Printf(_L(" %u milliseconds\n"), I64LOW(milli) ); + } + +class Set + { +public: + struct SColDef + { + const TDesC* iName; + TDbColType iType; + TInt iAttributes; + }; +public: + static CDbColSet* CreateL(const SColDef* aDef); + }; +CDbColSet* Set::CreateL(const SColDef* aDef) + { + CDbColSet *set=CDbColSet::NewLC(); + for (;aDef->iName!=NULL;++aDef) + { + TDbCol col(*aDef->iName,aDef->iType); + col.iAttributes=aDef->iAttributes; + set->AddL(col); + } + CleanupStack::Pop(); + return set; + } + +// Accounts table +const TPtrC KAccounts=_S("ACCOUNTS"); +const TPtrC KAccountsID=_S("ID"); +const TPtrC KAccountsBalance=_S("BALANCE"); +const TPtrC KAccountsStatement=_S("STATEMENT_BALANCE"); +Set::SColDef const AccountsDef[]= + { + {&KAccountsID,EDbColInt32,TDbCol::ENotNull}, + {&KAccountsBalance,EDbColInt32,TDbCol::ENotNull}, + {&KAccountsStatement,EDbColInt32,TDbCol::ENotNull}, + {0} + }; +const TInt KInitialCash=100000; +const TInt KInitialBalance=1000; + +// Transaction table +const TPtrC KTransactions=_S("TRANSACTIONS"); +const TPtrC KTransactionDate=_S("T_DATE"); +const TPtrC KTransactionFrom=_S("FROM_ID"); +const TPtrC KTransactionTo=_S("TO_ID"); +const TPtrC KTransactionAmount=_S("AMOUNT"); +Set::SColDef const TransactionsDef[]= + { +// {&KTransactionDate,EDbColDateTime,TDbCol::ENotNull}, + {&KTransactionDate,EDbColInt32,TDbCol::ENotNull}, + {&KTransactionFrom,EDbColInt32,TDbCol::ENotNull}, + {&KTransactionTo,EDbColInt32,TDbCol::ENotNull}, + {&KTransactionAmount,EDbColInt32,TDbCol::ENotNull}, + {0} + }; + +LOCAL_D TInt32 TotalMonies; +LOCAL_D TBuf<100> Buf; + +GLDEF_C TInt Random(TInt aRange) + { + return (Math::Random()>>11)%aRange; + } + +#undef test +LOCAL_C void Test(TInt aValue,TInt anExpected,TInt aLine) + { + if (aValue==anExpected) + return; + test.Printf(_L("** Expected %d, was %d\n"),anExpected,aValue); + test(EFalse,aLine); + } +#define test1(aTest) test(aTest,__LINE__) +#define test2(aValue,anExpected) Test(aValue,anExpected,__LINE__) + +LOCAL_C void CreateIndexL(RDbDatabase& aDatabase,const TDesC& aTable,const TDesC& aColumn,TBool aUnique) + { + CDbKey* key=CDbKey::NewLC(); + key->AddL(aColumn); + if (aUnique) + key->MakeUnique(); + test2(aDatabase.CreateIndex(aColumn,aTable,*key),KErrNone); + CleanupStack::PopAndDestroy(); + } + +// +// Create the database +// +LOCAL_C void CreateDatabaseL() + { + CFileStore* store=CPermanentFileStore::ReplaceLC(TheFs,KTestDatabase,EFileRead|EFileWrite); + store->SetTypeL(KPermanentFileStoreLayoutUid); + store->SetRootL(TheDatabase.CreateL(store)); +// create the tables + TheDatabase.Begin(); + CDbColSet* set=Set::CreateL(AccountsDef); + test2(TheDatabase.CreateTable(KAccounts,*set),KErrNone); + delete set; + CreateIndexL(TheDatabase,KAccounts,KAccountsID,ETrue); + CreateIndexL(TheDatabase,KAccounts,KAccountsBalance,EFalse); + set=Set::CreateL(TransactionsDef); + test2(TheDatabase.CreateTable(KTransactions,*set),KErrNone); + delete set; + CreateIndexL(TheDatabase,KTransactions,KTransactionDate,EFalse); + test2(TheDatabase.Commit(),KErrNone); + OldCount=NewCount=0; +// prepare Accs table + TheDatabase.Begin(); + test2(Accs.Prepare(TheDatabase,_L("select * from accounts"),Accs.EInsertOnly),KErrNone); + Accs.InsertL(); + Accs.SetColL(1,TInt32(ECash)); + Accs.SetColL(2,KInitialCash); + Accs.SetColL(3,KInitialCash); + Accs.PutL(); + TotalMonies=KInitialCash; + for (TInt ii=EJohn;ii<=EPenny;++ii) + { + Accs.InsertL(); + Accs.SetColL(1,ii); + Accs.SetColL(2,KInitialBalance); + Accs.SetColL(3,KInitialBalance); + Accs.PutL(); + TotalMonies+=KInitialBalance; + } + test2(TheDatabase.Commit(),KErrNone); + Accs.Close(); + TheDatabase.Close(); + CleanupStack::PopAndDestroy(); // store + } + + +#ifdef __DUMP_STATE +LOCAL_C void DumpStateL() + { + RFile file; + CleanupClosePushL(file); + User::LeaveIfError(file.Replace(TheFs,KLogFile,EFileWrite|EFileStreamText)); + RDbRowSet::RConstraint match; + CleanupClosePushL(match); + for (TInt id=ECash;id<=EPenny;++id) + { + Buf.Format(_L("id=%d"),id); + Accs.FirstL(); + test1(Accs.FindL(Accs.EForwards,Buf)>=0); + Accs.GetL(); + TInt balance=Accs.ColInt(2); + Buf.Format(_L("\nStatement for account %d: Previous balance %d\n"),id,balance); + User::LeaveIfError(file.Write(Buf)); + Buf.Format(_L("from_id=%d or to_id=%d"),id,id); + User::LeaveIfError(match.Open(Trans,Buf)); + for (Trans.BeginningL();Trans.NextL();) + { + if (Trans.MatchL(match)) + { + Trans.GetL(); + TInt from=Trans.ColInt(2); + TInt amount=Trans.ColInt(4); + Buf.Format(_L("%04d: %6s %5d\n"),Trans.ColInt(1),from==id?_S("debit"):_S("credit"),amount); + User::LeaveIfError(file.Write(Buf)); + if (from==id) + balance-=amount; + else + balance+=amount; + } + } + Buf.Format(_L("Closing balance %d\n"),balance); + User::LeaveIfError(file.Write(Buf)); + Buf.Format(_L("Account balance %d\n"),Accs.ColInt(3)); + User::LeaveIfError(file.Write(Buf)); + } + CleanupStack::PopAndDestroy(2); + test1(0); + } +#endif + +// +// Check that the database structure is fully intact +// +LOCAL_C void VerifyDatabaseL(CPersistentStore& aStore) + { + TheDatabase.OpenL(&aStore,aStore.Root()); +// check any indexes + test2(TheTable.Open(TheDatabase,KAccounts,TheTable.EReadOnly),KErrNone); + test2(TheTable.CountL(),KAccountIDs); + TInt r=TheTable.SetIndex(KAccountsID); + if (r!=KErrCorrupt) + { + test2(r,KErrNone); + test2(TheTable.CountL(),KAccountIDs); + for (TInt id=ECash;id<=EPenny;++id) + { + test1(TheTable.NextL()); + TheTable.GetL(); + test2(TheTable.ColInt(1),id); + } + test1(!TheTable.NextL()); + } + r=TheTable.SetIndex(KAccountsBalance); + if (r!=KErrCorrupt) + { + test2(r,KErrNone); + test2(TheTable.CountL(),KAccountIDs); + test1(TheTable.FirstL()); + TheTable.GetL(); + TInt last=TheTable.ColInt(2); + for (TInt ii=1;ii=last); + last=bal; + } + test1(!TheTable.NextL()); + } + TheTable.Close(); + test2(TheTable.Open(TheDatabase,KTransactions,TheTable.EReadOnly),KErrNone); + TInt count=TheTable.CountL(); + r=TheTable.SetIndex(KTransactionDate); + if (r!=KErrCorrupt) + { + test2(r,KErrNone); + test2(TheTable.CountL(),count); + if (count) + { + test1(TheTable.FirstL()); + TheTable.GetL(); + TInt last=TheTable.ColInt(1); + while (--count!=0) + { + test1(TheTable.NextL()); + TheTable.GetL(); + TInt date=TheTable.ColInt(1); + test1(date>last); + last=date; + } + test1(!TheTable.NextL()); + } + else + test1(!TheTable.FirstL()); + } + TheTable.Close(); +// verify database integrity + TInt balance[KAccountIDs]; + test2(Accs.Prepare(TheDatabase,_L("select id,statement_balance,balance from accounts"),Accs.EReadOnly),KErrNone); + test2(Accs.CountL(),KAccountIDs); + while (Accs.NextL()) + { + Accs.GetL(); + TInt id=Accs.ColInt(1); + balance[id]=Accs.ColInt(2); + } + test2(Trans.Prepare(TheDatabase,_L("select t_date,from_id,to_id,amount from Transactions"),Trans.EReadOnly),KErrNone); + TInt transact=0; + while (Trans.NextL()) + { + ++transact; + Trans.GetL(); + TInt from=Trans.ColInt(2); + TInt to=Trans.ColInt(3); + TInt amount=Trans.ColInt(4); + balance[from]-=amount; + balance[to]+=amount; + } + test2(transact,Trans.CountL()); + if (NewCount!=-1 && transact!=NewCount) + { + test2(transact,OldCount); + ++ShotDuringCommit; + } + OldCount=NewCount=transact; + TInt total=0; + for (Accs.BeginningL();Accs.NextL();) + { + Accs.GetL(); + TInt id=Accs.ColInt(1); +#ifdef __DUMP_STATE + if (balance[id]!=Accs.ColInt(3)) + DumpStateL(); +#else + test(balance[id]==Accs.ColInt(3)); +#endif + total+=balance[id]; + } + test2(total,TotalMonies); + Trans.Close(); + Accs.Close(); + TheDatabase.Close(); + } + +LOCAL_C TInt Verify(CPersistentStore& aStore) + { + TRAPD(r,VerifyDatabaseL(aStore)); + return r; + } + +LOCAL_C TInt Recover(CPersistentStore& aStore) + { + TRAPD(r,TheDatabase.OpenL(&aStore,aStore.Root())); + if (r==KErrNone) + { + r=TheDatabase.Recover(); + TheDatabase.Close(); + } + return r; + } + +LOCAL_C void CompactL(CStreamStore& aStore) + { + TInt t=aStore.ReclaimL(); + Stopwatch.Start(); + t-=aStore.CompactL(); + test.Printf(_L(" compacted %d byte(s) in"),t); + Stopwatch.Print(); + aStore.CommitL(); + } + +LOCAL_C TInt Compact(CStreamStore& aStore) + { + TRAPD(r,CompactL(aStore)); + return r; + } + +LOCAL_C TInt EndThread() + { + RunningTime+=RunTimer.Stop(); + if (TheStatus==KRequestPending) + TheThread.Kill(1); + User::WaitForRequest(TheStatus); + TInt r; + if (TheThread.ExitType()==EExitKill) + r=TheThread.ExitReason(); + else + r=TheStatus.Int(); + TheThread.Close(); + return r; + } + +//aTestExecutionTime - desired test execution time in minutes +LOCAL_C void RunTestL(TInt aTestExecutionTime = 0) + { + __ASSERT_ALWAYS(aTestExecutionTime >= 0, User::Invariant()); + + RThread().SetPriority(EPriorityMore); + test.Start(_L("Create the database")); + CreateDatabaseL(); + + TTimeIntervalMinutes timeInterval(aTestExecutionTime); + + TTime timeCurrent; + timeCurrent.UniversalTime(); + TTime timeEnd(timeCurrent); + timeEnd += timeInterval; + + for (TBool condition=ETrue; condition; condition = aTestExecutionTime > 0 ? (timeCurrent < timeEnd) : ETrue) + { + test.Next(_L("Main loop")); + test.Start(_L("Kick off the thread")); + test2 (StartThread(TheThread,TheStatus),KErrNone); + // random delay + for (;;) + { + User::After(95000); + if (TheStatus!=KRequestPending) + break; + if (Random(1000)<30) + break; + } + test.Next(_L("End the thread")); + TInt exit=EndThread(); + if (exit!=1) + test.Printf(_L(" thread failed with error %d\n"),exit); +// + ++Shot; + CFileStore* store=NULL; + for (TInt ii=0;;++ii) + { + test.Printf(_L("Opening %d\r"),ii); + TRAPD(r,store=CFileStore::OpenL(TheFs,KTestDatabase,EFileRead|EFileWrite)); + if (r==KErrNone) + break; + test (r==KErrInUse); + User::After(100000); + } + test.Next(_L("Verify & Recover")); + test2 (Verify(*store),KErrNone); + TInt64 tps(TransId); + tps*=1000u; + tps/=RunningTime; + test.Printf(_L(" Iteration %d, TPS %d, during commit %d%%\n"),Shot,I64LOW(tps),(100*ShotDuringCommit)/Shot); + TInt r=Recover(*store); + if (r==KErrNoMemory || r==KErrDiskFull) + { // need to compact before completing recovery + test.Next(_L("No space, compacting")); + test2 (Compact(*store),KErrNone); + test.Next(_L("Verify & Recover again")); + test2 (Verify(*store),KErrNone); + r=Recover(*store); + } + test2 (r,KErrNone); + test.Next(_L("Verify & Compact")); +// test2 (Verify(*store),KErrNone); + test2 (Compact(*store),KErrNone); + test.Next(_L("Verify")); + test2 (Verify(*store),KErrNone); +// + delete store; + test.End(); + + timeCurrent.UniversalTime(); + } + test.End(); + } + +/** +@SYMTestCaseID SYSLIB-DBMS-CT-0636 +@SYMTestCaseDesc DBMS stess testing. +@SYMTestPriority Medium +@SYMTestActions Tests for verifying the database integrity. +@SYMTestExpectedResults Test must not fail +@SYMREQ REQ0000 +*/ +static void RunVerify() + { + test.Start(_L(" @SYMTestCaseID:SYSLIB-DBMS-CT-0636 Open store ")); + CFileStore* store=NULL; + TRAPD(r,store=CFileStore::OpenL(TheFs,KTestDatabase,EFileRead|EFileWrite)); + test2 (r,KErrNone); + test.Next(_L("Verify")); + NewCount=-1; + TotalMonies=KInitialCash + (EPenny-EJohn+1)*KInitialBalance; + test2 (Verify(*store),KErrNone); + test.Next(_L("Recover")); + test2 (Recover(*store),KErrNone); + test.Next(_L("Verify")); + test2 (Verify(*store),KErrNone); + delete store; + test.End(); + } + +// +// Prepare the test directory. +// +LOCAL_C void setupTestDirectory() + { + TInt r=TheFs.Connect(); + test2(r,KErrNone); +// + r=TheFs.MkDir(KTestDir); + test1(r==KErrNone || r==KErrAlreadyExists); + r=TheFs.SetSessionPath(KTestDir); + test2(r,KErrNone); + } + +LOCAL_C CTrapCleanup* setupCleanup() +// +// Initialise the cleanup stack. +// + { + CTrapCleanup* cleanup=CTrapCleanup::New(); + test1(cleanup!=NULL); + TRAPD(r,\ + {\ + for (TInt i=KTestCleanupStack;i>0;i--)\ + CleanupStack::PushL((TAny*)0);\ + CleanupStack::Pop(KTestCleanupStack);\ + }); + test2(r,KErrNone); + return cleanup; + } + +// +// entry point +// +// Parameters usage: +// t_stress [-v]|[0]|[] +// Where: +// -v - a verification test will be run; +// 0 - a stress test will be run for indefinite time; +// - a stress test will be run for minutes; +// If the test is run without arguments, the test execution time will be 10 minutes +// (KDefaultTestExecutionTime constant bellow). +GLDEF_C TInt E32Main() + { + test.Title(); + setupTestDirectory(); + CTrapCleanup* cleanup=setupCleanup(); + __UHEAP_MARK; +// + TBuf<100> cmd; + User::CommandLine(cmd); + TLex lex(cmd); + TInt err = KErrNone; + for(;;) + { + TPtrC arg(lex.NextToken()); + if(arg.Length() == 0) + { + const TInt KDefaultTestExecutionTime = 10;//default test execution time - minutes + TRAP(err, RunTestL(KDefaultTestExecutionTime)); + break; + } + else if(arg.CompareF(_L("-v")) == 0) + { + RunVerify(); + break; + } + else + { + TInt32 testExecutionTime = 0; + lex.Assign(arg); + (void)lex.Val(testExecutionTime); + TRAP(err, RunTestL(testExecutionTime)); + break; + } + } + TInt err2 = TheFs.Delete(KTestDatabase); + if(err2 != KErrNone) + { + RDebug::Print(_L("Error %d deleting \"%S\" file.\n"), err2, &KTestDatabase); + } + err2 = TheFs.Delete(KLogFile); + if(err2 != KErrNone) + { + RDebug::Print(_L("Error %d deleting \"%S\" file.\n"), err2, &KLogFile); + } + test2(err, KErrNone); +// + __UHEAP_MARKEND; + delete cleanup; + TheFs.Close(); + test.Close(); + return 0; + }