зеркало из https://github.com/mozilla/gecko-dev.git
Bug 599969 - Do not use steps for async visit adding
Part 3 - Use the statement cache from storage to do less work on the background thread. r=mak
This commit is contained in:
Родитель
e9f0fde783
Коммит
4d9df04df8
|
@ -345,35 +345,36 @@ public:
|
||||||
/**
|
/**
|
||||||
* Adds a visit to the database asynchronously.
|
* Adds a visit to the database asynchronously.
|
||||||
*
|
*
|
||||||
|
* @param aConnection
|
||||||
|
* The database connection to use for these operations.
|
||||||
* @param aPlace
|
* @param aPlace
|
||||||
* The location to record a visit.
|
* The location to record a visit.
|
||||||
* @param [optional] aReferrer
|
* @param [optional] aReferrer
|
||||||
* The page that "referred" us to aPlace.
|
* The page that "referred" us to aPlace.
|
||||||
*/
|
*/
|
||||||
static nsresult Start(VisitData& aPlace,
|
static nsresult Start(mozIStorageConnection* aConnection,
|
||||||
|
VisitData& aPlace,
|
||||||
nsIURI* aReferrer = nsnull)
|
nsIURI* aReferrer = nsnull)
|
||||||
{
|
{
|
||||||
NS_PRECONDITION(NS_IsMainThread(),
|
NS_PRECONDITION(NS_IsMainThread(),
|
||||||
"This should be called on the main thread");
|
"This should be called on the main thread");
|
||||||
|
|
||||||
nsRefPtr<InsertVisitedURI> event = new InsertVisitedURI(aPlace, aReferrer);
|
nsRefPtr<InsertVisitedURI> event =
|
||||||
|
new InsertVisitedURI(aConnection, aPlace, aReferrer);
|
||||||
NS_ENSURE_TRUE(event, NS_ERROR_OUT_OF_MEMORY);
|
NS_ENSURE_TRUE(event, NS_ERROR_OUT_OF_MEMORY);
|
||||||
|
|
||||||
nsNavHistory* navhistory = nsNavHistory::GetHistoryService();
|
|
||||||
NS_ENSURE_TRUE(navhistory, NS_ERROR_OUT_OF_MEMORY);
|
|
||||||
nsresult rv = navhistory->GetDBConnection(getter_AddRefs(event->mDBConn));
|
|
||||||
NS_ENSURE_SUCCESS(rv, rv);
|
|
||||||
|
|
||||||
// Speculatively get a new session id for our visit. While it is true that
|
// Speculatively get a new session id for our visit. While it is true that
|
||||||
// we will use the session id from the referrer if the visit was "recent"
|
// we will use the session id from the referrer if the visit was "recent"
|
||||||
// enough, we cannot call this method off of the main thread, so we have to
|
// enough, we cannot call this method off of the main thread, so we have to
|
||||||
// consume an id now.
|
// consume an id now.
|
||||||
|
nsNavHistory* navhistory = nsNavHistory::GetHistoryService();
|
||||||
|
NS_ENSURE_TRUE(navhistory, NS_ERROR_UNEXPECTED);
|
||||||
event->mPlace.sessionId = navhistory->GetNewSessionID();
|
event->mPlace.sessionId = navhistory->GetNewSessionID();
|
||||||
|
|
||||||
// Get the target thread, and then start the work!
|
// Get the target thread, and then start the work!
|
||||||
nsCOMPtr<nsIEventTarget> target = do_GetInterface(event->mDBConn);
|
nsCOMPtr<nsIEventTarget> target = do_GetInterface(aConnection);
|
||||||
NS_ENSURE_TRUE(target, NS_ERROR_OUT_OF_MEMORY);
|
NS_ENSURE_TRUE(target, NS_ERROR_UNEXPECTED);
|
||||||
rv = target->Dispatch(event, NS_DISPATCH_NORMAL);
|
nsresult rv = target->Dispatch(event, NS_DISPATCH_NORMAL);
|
||||||
NS_ENSURE_SUCCESS(rv, rv);
|
NS_ENSURE_SUCCESS(rv, rv);
|
||||||
|
|
||||||
return NS_OK;
|
return NS_OK;
|
||||||
|
@ -392,15 +393,17 @@ public:
|
||||||
if (known) {
|
if (known) {
|
||||||
NS_ASSERTION(mPlace.placeId > 0, "must have a valid place id!");
|
NS_ASSERTION(mPlace.placeId > 0, "must have a valid place id!");
|
||||||
|
|
||||||
nsCOMPtr<mozIStorageStatement> stmt;
|
nsCOMPtr<mozIStorageStatement> stmt =
|
||||||
nsresult rv = mDBConn->CreateStatement(NS_LITERAL_CSTRING(
|
mHistory->syncStatements.GetCachedStatement(
|
||||||
"UPDATE moz_places "
|
"UPDATE moz_places "
|
||||||
"SET hidden = :hidden, typed = :typed "
|
"SET hidden = :hidden, typed = :typed "
|
||||||
"WHERE id = :page_id "
|
"WHERE id = :page_id "
|
||||||
), getter_AddRefs(stmt));
|
);
|
||||||
NS_ENSURE_SUCCESS(rv, rv);
|
NS_ENSURE_STATE(stmt);
|
||||||
|
mozStorageStatementScoper scoper(stmt);
|
||||||
|
|
||||||
rv = stmt->BindInt32ByName(NS_LITERAL_CSTRING("typed"), mPlace.typed);
|
nsresult rv = stmt->BindInt32ByName(NS_LITERAL_CSTRING("typed"),
|
||||||
|
mPlace.typed);
|
||||||
NS_ENSURE_SUCCESS(rv, rv);
|
NS_ENSURE_SUCCESS(rv, rv);
|
||||||
rv = stmt->BindInt32ByName(NS_LITERAL_CSTRING("hidden"), mPlace.hidden);
|
rv = stmt->BindInt32ByName(NS_LITERAL_CSTRING("hidden"), mPlace.hidden);
|
||||||
NS_ENSURE_SUCCESS(rv, rv);
|
NS_ENSURE_SUCCESS(rv, rv);
|
||||||
|
@ -414,16 +417,17 @@ public:
|
||||||
else {
|
else {
|
||||||
NS_ASSERTION(mPlace.placeId == 0, "should not have a valid place id!");
|
NS_ASSERTION(mPlace.placeId == 0, "should not have a valid place id!");
|
||||||
|
|
||||||
nsCOMPtr<mozIStorageStatement> stmt;
|
nsCOMPtr<mozIStorageStatement> stmt =
|
||||||
nsresult rv = mDBConn->CreateStatement(NS_LITERAL_CSTRING(
|
mHistory->syncStatements.GetCachedStatement(
|
||||||
"INSERT INTO moz_places "
|
"INSERT INTO moz_places "
|
||||||
"(url, rev_host, hidden, typed) "
|
"(url, rev_host, hidden, typed) "
|
||||||
"VALUES (:page_url, :rev_host, :hidden, :typed) "
|
"VALUES (:page_url, :rev_host, :hidden, :typed) "
|
||||||
), getter_AddRefs(stmt));
|
);
|
||||||
NS_ENSURE_SUCCESS(rv, rv);
|
NS_ENSURE_STATE(stmt);
|
||||||
|
mozStorageStatementScoper scoper(stmt);
|
||||||
|
|
||||||
nsAutoString revHost;
|
nsAutoString revHost;
|
||||||
rv = GetReversedHostname(mPlace.uri, revHost);
|
nsresult rv = GetReversedHostname(mPlace.uri, revHost);
|
||||||
NS_ENSURE_SUCCESS(rv, rv);
|
NS_ENSURE_SUCCESS(rv, rv);
|
||||||
|
|
||||||
rv = URIBinder::Bind(stmt, NS_LITERAL_CSTRING("page_url"), mPlace.uri);
|
rv = URIBinder::Bind(stmt, NS_LITERAL_CSTRING("page_url"), mPlace.uri);
|
||||||
|
@ -479,9 +483,12 @@ public:
|
||||||
return NS_OK;
|
return NS_OK;
|
||||||
}
|
}
|
||||||
private:
|
private:
|
||||||
InsertVisitedURI(VisitData& aPlace,
|
InsertVisitedURI(mozIStorageConnection* aConnection,
|
||||||
|
VisitData& aPlace,
|
||||||
nsIURI* aReferrer)
|
nsIURI* aReferrer)
|
||||||
: mPlace(aPlace)
|
: mDBConn(aConnection)
|
||||||
|
, mPlace(aPlace)
|
||||||
|
, mHistory(History::GetService())
|
||||||
{
|
{
|
||||||
mReferrer.uri = aReferrer;
|
mReferrer.uri = aReferrer;
|
||||||
}
|
}
|
||||||
|
@ -498,15 +505,17 @@ private:
|
||||||
NS_PRECONDITION(_place.uri || _place.spec.Length(),
|
NS_PRECONDITION(_place.uri || _place.spec.Length(),
|
||||||
"must have a non-null uri or a non-empty spec!");
|
"must have a non-null uri or a non-empty spec!");
|
||||||
|
|
||||||
nsCOMPtr<mozIStorageStatement> stmt;
|
nsCOMPtr<mozIStorageStatement> stmt =
|
||||||
nsresult rv = mDBConn->CreateStatement(NS_LITERAL_CSTRING(
|
mHistory->syncStatements.GetCachedStatement(
|
||||||
"SELECT id, typed, hidden "
|
"SELECT id, typed, hidden "
|
||||||
"FROM moz_places "
|
"FROM moz_places "
|
||||||
"WHERE url = :page_url "
|
"WHERE url = :page_url "
|
||||||
), getter_AddRefs(stmt));
|
);
|
||||||
NS_ENSURE_SUCCESS(rv, false);
|
NS_ENSURE_TRUE(stmt, false);
|
||||||
|
mozStorageStatementScoper scoper(stmt);
|
||||||
|
|
||||||
rv = URIBinder::Bind(stmt, NS_LITERAL_CSTRING("page_url"), _place.uri);
|
nsresult rv = URIBinder::Bind(stmt, NS_LITERAL_CSTRING("page_url"),
|
||||||
|
_place.uri);
|
||||||
NS_ENSURE_SUCCESS(rv, false);
|
NS_ENSURE_SUCCESS(rv, false);
|
||||||
|
|
||||||
PRBool hasResult;
|
PRBool hasResult;
|
||||||
|
@ -557,16 +566,18 @@ private:
|
||||||
NS_PRECONDITION(_place.uri || _place.spec.Length(),
|
NS_PRECONDITION(_place.uri || _place.spec.Length(),
|
||||||
"must have a non-null uri or a non-empty spec!");
|
"must have a non-null uri or a non-empty spec!");
|
||||||
|
|
||||||
nsCOMPtr<mozIStorageStatement> stmt;
|
nsCOMPtr<mozIStorageStatement> stmt =
|
||||||
nsresult rv = mDBConn->CreateStatement(NS_LITERAL_CSTRING(
|
mHistory->syncStatements.GetCachedStatement(
|
||||||
"SELECT id, session, visit_date "
|
"SELECT id, session, visit_date "
|
||||||
"FROM moz_historyvisits "
|
"FROM moz_historyvisits "
|
||||||
"WHERE place_id = (SELECT id FROM moz_places WHERE url = :page_url) "
|
"WHERE place_id = (SELECT id FROM moz_places WHERE url = :page_url) "
|
||||||
"ORDER BY visit_date DESC "
|
"ORDER BY visit_date DESC "
|
||||||
), getter_AddRefs(stmt));
|
);
|
||||||
NS_ENSURE_SUCCESS(rv, false);
|
NS_ENSURE_TRUE(stmt, false);
|
||||||
|
mozStorageStatementScoper scoper(stmt);
|
||||||
|
|
||||||
rv = URIBinder::Bind(stmt, NS_LITERAL_CSTRING("page_url"), _place.uri);
|
nsresult rv = URIBinder::Bind(stmt, NS_LITERAL_CSTRING("page_url"),
|
||||||
|
_place.uri);
|
||||||
NS_ENSURE_SUCCESS(rv, false);
|
NS_ENSURE_SUCCESS(rv, false);
|
||||||
|
|
||||||
PRBool hasResult;
|
PRBool hasResult;
|
||||||
|
@ -604,16 +615,17 @@ private:
|
||||||
nsresult AddVisit(VisitData& _place,
|
nsresult AddVisit(VisitData& _place,
|
||||||
const VisitData& aReferrer)
|
const VisitData& aReferrer)
|
||||||
{
|
{
|
||||||
nsCOMPtr<mozIStorageStatement> stmt;
|
nsCOMPtr<mozIStorageStatement> stmt =
|
||||||
nsresult rv = mDBConn->CreateStatement(NS_LITERAL_CSTRING(
|
mHistory->syncStatements.GetCachedStatement(
|
||||||
"INSERT INTO moz_historyvisits "
|
"INSERT INTO moz_historyvisits "
|
||||||
"(from_visit, place_id, visit_date, visit_type, session) "
|
"(from_visit, place_id, visit_date, visit_type, session) "
|
||||||
"VALUES (:from_visit, :page_id, :visit_date, :visit_type, :session) "
|
"VALUES (:from_visit, :page_id, :visit_date, :visit_type, :session) "
|
||||||
), getter_AddRefs(stmt));
|
);
|
||||||
NS_ENSURE_SUCCESS(rv, rv);
|
NS_ENSURE_STATE(stmt);
|
||||||
|
mozStorageStatementScoper scoper(stmt);
|
||||||
|
|
||||||
rv = stmt->BindInt64ByName(NS_LITERAL_CSTRING("from_visit"),
|
nsresult rv = stmt->BindInt64ByName(NS_LITERAL_CSTRING("from_visit"),
|
||||||
aReferrer.visitId);
|
aReferrer.visitId);
|
||||||
NS_ENSURE_SUCCESS(rv, rv);
|
NS_ENSURE_SUCCESS(rv, rv);
|
||||||
rv = stmt->BindInt64ByName(NS_LITERAL_CSTRING("page_id"),
|
rv = stmt->BindInt64ByName(NS_LITERAL_CSTRING("page_id"),
|
||||||
_place.placeId);
|
_place.placeId);
|
||||||
|
@ -651,43 +663,54 @@ private:
|
||||||
*/
|
*/
|
||||||
nsresult UpdateFrecency(const VisitData& aPlace)
|
nsresult UpdateFrecency(const VisitData& aPlace)
|
||||||
{
|
{
|
||||||
// First, set our frecency to the proper value.
|
{ // First, set our frecency to the proper value.
|
||||||
nsCOMPtr<mozIStorageStatement> stmt;
|
nsCOMPtr<mozIStorageStatement> stmt =
|
||||||
nsresult rv = mDBConn->CreateStatement(NS_LITERAL_CSTRING(
|
mHistory->syncStatements.GetCachedStatement(
|
||||||
"UPDATE moz_places "
|
"UPDATE moz_places "
|
||||||
"SET frecency = CALCULATE_FRECENCY(:page_id) "
|
"SET frecency = CALCULATE_FRECENCY(:page_id) "
|
||||||
"WHERE id = :page_id"
|
"WHERE id = :page_id"
|
||||||
), getter_AddRefs(stmt));
|
);
|
||||||
NS_ENSURE_SUCCESS(rv, rv);
|
NS_ENSURE_STATE(stmt);
|
||||||
|
mozStorageStatementScoper scoper(stmt);
|
||||||
|
|
||||||
rv = stmt->BindInt64ByName(NS_LITERAL_CSTRING("page_id"),
|
nsresult rv = stmt->BindInt64ByName(NS_LITERAL_CSTRING("page_id"),
|
||||||
aPlace.placeId);
|
aPlace.placeId);
|
||||||
NS_ENSURE_SUCCESS(rv, rv);
|
NS_ENSURE_SUCCESS(rv, rv);
|
||||||
rv = stmt->Execute();
|
rv = stmt->Execute();
|
||||||
NS_ENSURE_SUCCESS(rv, rv);
|
NS_ENSURE_SUCCESS(rv, rv);
|
||||||
|
}
|
||||||
|
|
||||||
// Finally, we need to mark the page as not hidden if the frecency is now
|
{ // Now, we need to mark the page as not hidden if the frecency is now
|
||||||
// nonzero.
|
// nonzero.
|
||||||
rv = mDBConn->CreateStatement(NS_LITERAL_CSTRING(
|
nsCOMPtr<mozIStorageStatement> stmt =
|
||||||
"UPDATE moz_places "
|
mHistory->syncStatements.GetCachedStatement(
|
||||||
"SET hidden = 0 "
|
"UPDATE moz_places "
|
||||||
"WHERE id = :page_id AND frecency <> 0"
|
"SET hidden = 0 "
|
||||||
), getter_AddRefs(stmt));
|
"WHERE id = :page_id AND frecency <> 0"
|
||||||
NS_ENSURE_SUCCESS(rv, rv);
|
);
|
||||||
|
NS_ENSURE_STATE(stmt);
|
||||||
|
mozStorageStatementScoper scoper(stmt);
|
||||||
|
|
||||||
rv = stmt->BindInt64ByName(NS_LITERAL_CSTRING("page_id"),
|
nsresult rv = stmt->BindInt64ByName(NS_LITERAL_CSTRING("page_id"),
|
||||||
aPlace.placeId);
|
aPlace.placeId);
|
||||||
NS_ENSURE_SUCCESS(rv, rv);
|
NS_ENSURE_SUCCESS(rv, rv);
|
||||||
rv = stmt->Execute();
|
rv = stmt->Execute();
|
||||||
NS_ENSURE_SUCCESS(rv, rv);
|
NS_ENSURE_SUCCESS(rv, rv);
|
||||||
|
}
|
||||||
|
|
||||||
return NS_OK;
|
return NS_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
nsCOMPtr<mozIStorageConnection> mDBConn;
|
mozIStorageConnection* mDBConn;
|
||||||
|
|
||||||
VisitData mPlace;
|
VisitData mPlace;
|
||||||
VisitData mReferrer;
|
VisitData mReferrer;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Strong reference to the History object because we do not want it to
|
||||||
|
* disappear out from under us.
|
||||||
|
*/
|
||||||
|
nsRefPtr<History> mHistory;
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -868,6 +891,7 @@ History* History::gService = NULL;
|
||||||
|
|
||||||
History::History()
|
History::History()
|
||||||
: mShuttingDown(false)
|
: mShuttingDown(false)
|
||||||
|
, syncStatements(mDBConn)
|
||||||
{
|
{
|
||||||
NS_ASSERTION(!gService, "Ruh-roh! This service has already been created!");
|
NS_ASSERTION(!gService, "Ruh-roh! This service has already been created!");
|
||||||
gService = this;
|
gService = this;
|
||||||
|
@ -977,11 +1001,7 @@ History::GetIsVisitedStatement()
|
||||||
|
|
||||||
// If we don't yet have a database connection, go ahead and clone it now.
|
// If we don't yet have a database connection, go ahead and clone it now.
|
||||||
if (!mReadOnlyDBConn) {
|
if (!mReadOnlyDBConn) {
|
||||||
nsNavHistory* history = nsNavHistory::GetHistoryService();
|
mozIStorageConnection* dbConn = GetDBConn();
|
||||||
NS_ENSURE_TRUE(history, nsnull);
|
|
||||||
|
|
||||||
nsCOMPtr<mozIStorageConnection> dbConn;
|
|
||||||
(void)history->GetDBConnection(getter_AddRefs(dbConn));
|
|
||||||
NS_ENSURE_TRUE(dbConn, nsnull);
|
NS_ENSURE_TRUE(dbConn, nsnull);
|
||||||
|
|
||||||
(void)dbConn->Clone(PR_TRUE, getter_AddRefs(mReadOnlyDBConn));
|
(void)dbConn->Clone(PR_TRUE, getter_AddRefs(mReadOnlyDBConn));
|
||||||
|
@ -1027,6 +1047,22 @@ History::GetSingleton()
|
||||||
return gService;
|
return gService;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
mozIStorageConnection*
|
||||||
|
History::GetDBConn()
|
||||||
|
{
|
||||||
|
if (mDBConn) {
|
||||||
|
return mDBConn;
|
||||||
|
}
|
||||||
|
|
||||||
|
nsNavHistory* history = nsNavHistory::GetHistoryService();
|
||||||
|
NS_ENSURE_TRUE(history, nsnull);
|
||||||
|
|
||||||
|
nsresult rv = history->GetDBConnection(getter_AddRefs(mDBConn));
|
||||||
|
NS_ENSURE_SUCCESS(rv, nsnull);
|
||||||
|
|
||||||
|
return mDBConn;
|
||||||
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
History::StartNextTask()
|
History::StartNextTask()
|
||||||
{
|
{
|
||||||
|
@ -1055,6 +1091,8 @@ History::Shutdown()
|
||||||
}
|
}
|
||||||
|
|
||||||
// Clean up our statements and connection.
|
// Clean up our statements and connection.
|
||||||
|
syncStatements.FinalizeStatements();
|
||||||
|
|
||||||
if (mReadOnlyDBConn) {
|
if (mReadOnlyDBConn) {
|
||||||
if (mIsVisitedStatement) {
|
if (mIsVisitedStatement) {
|
||||||
(void)mIsVisitedStatement->Finalize();
|
(void)mIsVisitedStatement->Finalize();
|
||||||
|
@ -1150,7 +1188,10 @@ History::VisitURI(nsIURI* aURI,
|
||||||
place.visitTime = PR_Now();
|
place.visitTime = PR_Now();
|
||||||
place.uri = aURI;
|
place.uri = aURI;
|
||||||
|
|
||||||
rv = InsertVisitedURI::Start(place, aLastVisitedURI);
|
mozIStorageConnection* dbConn = GetDBConn();
|
||||||
|
NS_ENSURE_STATE(dbConn);
|
||||||
|
|
||||||
|
rv = InsertVisitedURI::Start(dbConn, place, aLastVisitedURI);
|
||||||
NS_ENSURE_SUCCESS(rv, rv);
|
NS_ENSURE_SUCCESS(rv, rv);
|
||||||
|
|
||||||
// Finally, notify that we've been visited.
|
// Finally, notify that we've been visited.
|
||||||
|
@ -1338,7 +1379,7 @@ History::Observe(nsISupports* aSubject, const char* aTopic,
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
//// nsISupports
|
//// nsISupports
|
||||||
|
|
||||||
NS_IMPL_ISUPPORTS2(
|
NS_IMPL_THREADSAFE_ISUPPORTS2(
|
||||||
History
|
History
|
||||||
, IHistory
|
, IHistory
|
||||||
, nsIObserver
|
, nsIObserver
|
||||||
|
|
|
@ -49,6 +49,7 @@
|
||||||
#include "nsDeque.h"
|
#include "nsDeque.h"
|
||||||
#include "nsIObserver.h"
|
#include "nsIObserver.h"
|
||||||
#include "mozIStorageConnection.h"
|
#include "mozIStorageConnection.h"
|
||||||
|
#include "mozilla/storage/StatementCache.h"
|
||||||
|
|
||||||
namespace mozilla {
|
namespace mozilla {
|
||||||
namespace places {
|
namespace places {
|
||||||
|
@ -110,9 +111,28 @@ public:
|
||||||
*/
|
*/
|
||||||
static History* GetSingleton();
|
static History* GetSingleton();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Statement cache that is used for background thread statements only.
|
||||||
|
*/
|
||||||
|
storage::StatementCache<mozIStorageStatement> syncStatements;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
virtual ~History();
|
virtual ~History();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Obtains a read-write database connection.
|
||||||
|
*/
|
||||||
|
mozIStorageConnection* GetDBConn();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A read-write database connection used for adding history visits and setting
|
||||||
|
* a page's title.
|
||||||
|
*
|
||||||
|
* @note this should only be accessed by GetDBConn.
|
||||||
|
* @note this is the same connection as the one found on nsNavHistory.
|
||||||
|
*/
|
||||||
|
nsCOMPtr<mozIStorageConnection> mDBConn;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A read-only database connection used for checking if a URI is visited.
|
* A read-only database connection used for checking if a URI is visited.
|
||||||
*
|
*
|
||||||
|
|
Загрузка…
Ссылка в новой задаче