Հեքիաթն այն մասին, թե ինչպես է Կասկադը ջնջվել տիրույթում, հաղթեց երկար մեկնարկին

Բոլոր օգտատերերը բջջային հավելվածներում արագ գործարկումն ու արձագանքող միջերեսը համարում են որպես տրված: Եթե ​​հավելվածի գործարկումը երկար ժամանակ է պահանջում, օգտատերը սկսում է տխրել և զայրանալ: Դուք կարող եք հեշտությամբ փչացնել հաճախորդի փորձը կամ ամբողջությամբ կորցնել օգտատիրոջը նույնիսկ նախքան նա կսկսի օգտագործել հավելվածը:

Մի անգամ մենք հայտնաբերեցինք, որ Dodo Pizza հավելվածը գործարկելու համար տևում է միջինը 3 վայրկյան, իսկ որոշ «հաջողակների» համար՝ 15-20 վայրկյան:

Կտրվածքի տակ մի պատմություն է երջանիկ ավարտով. Realm տվյալների բազայի աճի, հիշողության արտահոսքի մասին, թե ինչպես մենք կուտակեցինք բնադրված առարկաներ, իսկ հետո հավաքվեցինք և շտկեցինք ամեն ինչ:

Հեքիաթն այն մասին, թե ինչպես է Կասկադը ջնջվել տիրույթում, հաղթեց երկար մեկնարկին

Հեքիաթն այն մասին, թե ինչպես է Կասկադը ջնջվել տիրույթում, հաղթեց երկար մեկնարկին
Հոդվածի հեղինակ. Մաքսիմ Կաչինկին — Android ծրագրավորող Dodo Pizza-ում:

Հավելվածի պատկերակի վրա սեղմելուց մինչև առաջին գործունեության onResume() երեք վայրկյանը անսահմանություն է: Իսկ որոշ օգտատերերի համար գործարկման ժամանակը հասել է 15-20 վայրկյանի։ Ինչպե՞ս է դա նույնիսկ հնարավոր:

Շատ կարճ ամփոփում նրանց համար, ովքեր ժամանակ չունեն կարդալու
Մեր Realm տվյալների բազան անվերջ աճեց: Որոշ բնադրված օբյեկտներ չեն ջնջվել, այլ անընդհատ կուտակվել են։ Հավելվածի գործարկման ժամանակը աստիճանաբար ավելացավ: Այնուհետև մենք ուղղեցինք այն, և գործարկման ժամանակը հասավ նպատակին. այն դարձավ 1 վայրկյանից պակաս և այլևս չավելացավ: Հոդվածը պարունակում է իրավիճակի վերլուծություն և երկու լուծում՝ արագ և նորմալ:

Խնդրի որոնում և վերլուծություն

Այսօր ցանկացած բջջային հավելված պետք է արագ գործարկվի և լինի արձագանքող։ Բայց խոսքը միայն բջջային հավելվածի մասին չէ: Ծառայության և ընկերության հետ փոխգործակցության օգտատերերի փորձը բարդ բան է: Օրինակ, մեր դեպքում առաքման արագությունը պիցցայի սպասարկման հիմնական ցուցանիշներից մեկն է: Եթե ​​առաքումն արագ լինի, ապա պիցցան տաք կլինի, իսկ հաճախորդը, ով ցանկանում է հիմա ուտել, երկար սպասել չի ունենա։ Հավելվածի համար, իր հերթին, կարևոր է արագ սպասարկման զգացում ստեղծելը, քանի որ եթե հավելվածը գործարկելու համար տևում է ընդամենը 20 վայրկյան, ապա որքա՞ն ժամանակ պետք է սպասեք պիցցային:

Սկզբում մենք ինքներս բախվեցինք այն փաստի հետ, որ երբեմն դիմումը գործարկելու համար տևում էր մի քանի վայրկյան, իսկ հետո մենք սկսեցինք բողոքներ լսել այլ գործընկերներից, թե որքան ժամանակ է պահանջվում: Բայց մենք չկարողացանք հետեւողականորեն կրկնել այս իրավիճակը։

Որքա՞ն ժամանակ է դա: Համաձայն Google-ի փաստաթղթեր, եթե դիմումի սառը մեկնարկը տևում է 5 վայրկյանից պակաս, ապա դա համարվում է «որպես նորմալ»: Գործարկվել է Dodo Pizza Android հավելվածը (ըստ Firebase չափումների _app_start) ժամը սառը սկիզբ միջինը 3 վայրկյանում - «Ոչ հիանալի, ոչ սարսափելի», ինչպես ասում են:

Բայց հետո սկսեցին բողոքներ հայտնվել, որ հավելվածի գործարկումը շատ, շատ, շատ երկար տևեց: Սկզբից մենք որոշեցինք չափել, թե ինչ է «շատ, շատ, շատ երկար»: Եվ մենք դրա համար օգտագործեցինք Firebase հետքը Հավելվածի մեկնարկի հետք.

Հեքիաթն այն մասին, թե ինչպես է Կասկադը ջնջվել տիրույթում, հաղթեց երկար մեկնարկին

Այս ստանդարտ հետքը չափում է օգտատիրոջ կողմից հավելվածը բացելու և առաջին գործունեության onResume()-ի կատարման պահի միջև ընկած ժամանակահատվածը: Firebase Console-ում այս չափանիշը կոչվում է _app_start: Պարզվեց, որ.

  • Գործարկման ժամանակը 95-րդ տոկոսից բարձր օգտատերերի համար կազմում է գրեթե 20 վայրկյան (որոշները նույնիսկ ավելի երկար), չնայած սառը գործարկման միջին ժամանակը 5 վայրկյանից պակաս է:
  • Գործարկման ժամանակը հաստատուն արժեք չէ, այլ ժամանակի ընթացքում աճում է: Բայց երբեմն կաթիլներ են լինում: Մենք գտանք այս օրինաչափությունը, երբ վերլուծության սանդղակը հասցրինք 90 օրվա:

Հեքիաթն այն մասին, թե ինչպես է Կասկադը ջնջվել տիրույթում, հաղթեց երկար մեկնարկին

Երկու միտք ծագեց.

  1. Ինչ-որ բան արտահոսում է:
  2. Այս «ինչ-որ բանը» զրոյացվում է թողարկումից հետո, այնուհետև նորից արտահոսում է:

«Հավանաբար տվյալների բազայի հետ կապված ինչ-որ բան», - մտածեցինք մենք և ճիշտ էինք: Նախ, մենք օգտագործում ենք տվյալների բազան որպես քեշ, միգրացիայի ժամանակ մենք մաքրում ենք այն: Երկրորդ, տվյալների բազան բեռնվում է, երբ դիմումը սկսվում է: Ամեն ինչ տեղավորվում է միասին:

Ինչն է սխալ Realm տվյալների բազայում

Մենք սկսեցինք ստուգել, ​​թե ինչպես է տվյալների բազայի բովանդակությունը փոխվում հավելվածի կյանքի ընթացքում՝ առաջին տեղադրումից և հետագա ակտիվ օգտագործման ընթացքում: Դուք կարող եք դիտել Realm տվյալների բազայի բովանդակությունը միջոցով Ստետո կամ ավելի մանրամասն և հստակ՝ բացելով ֆայլը միջոցով Realm Studio. Տվյալների բազայի բովանդակությունը ԱԶԲ-ի միջոցով դիտելու համար պատճենեք Realm տվյալների բազայի ֆայլը.

adb exec-out run-as ${PACKAGE_NAME} cat files/${DB_NAME}

Տարբեր ժամանակներում դիտարկելով տվյալների բազայի բովանդակությունը՝ պարզեցինք, որ որոշակի տեսակի օբյեկտների թիվը անընդհատ աճում է։

Հեքիաթն այն մասին, թե ինչպես է Կասկադը ջնջվել տիրույթում, հաղթեց երկար մեկնարկին
Նկարում պատկերված է Realm Studio-ի մի հատված երկու ֆայլի համար՝ ձախում՝ հավելվածի բազան տեղադրվելուց որոշ ժամանակ անց, աջում՝ ակտիվ օգտագործումից հետո: Կարելի է տեսնել, որ օբյեկտների թիվը ImageEntity и MoneyType զգալիորեն աճել է (սքրինշոթը ցույց է տալիս յուրաքանչյուր տեսակի օբյեկտների քանակը):

Կապը տվյալների բազայի աճի և գործարկման ժամանակի միջև

Տվյալների բազայի անվերահսկելի աճը շատ վատ է: Բայց ինչպե՞ս է դա ազդում հավելվածի գործարկման ժամանակի վրա: Դա բավականին հեշտ է չափել ActivityManager-ի միջոցով: Android 4.4-ից ի վեր, logcat-ը ցուցադրում է գրանցամատյանը Ցուցադրված տողի և ժամի հետ: Այս ժամանակը հավասար է հավելվածի գործարկման պահից մինչև գործունեության մատուցման ավարտը ընդմիջմանը: Այս ընթացքում տեղի են ունենում հետևյալ իրադարձությունները.

  • Սկսեք գործընթացը:
  • Օբյեկտների սկզբնավորում.
  • Գործունեության ստեղծում և նախնականացում:
  • Դասավորության ստեղծում:
  • Հավելվածի ներկայացում.

Հարմար է մեզ: Եթե ​​դուք գործարկում եք ԱԶԲ-ն -S և -W դրոշակներով, կարող եք ընդլայնված արդյունք ստանալ գործարկման ժամանակով.

adb shell am start -S -W ru.dodopizza.app/.MainActivity -c android.intent.category.LAUNCHER -a android.intent.action.MAIN

Եթե ​​այնտեղից վերցնես grep -i WaitTime ժամանակ, դուք կարող եք ավտոմատացնել այս չափման հավաքածուն և տեսողականորեն նայել արդյունքներին: Ստորև բերված գրաֆիկը ցույց է տալիս հավելվածի գործարկման ժամանակի կախվածությունը հավելվածի սառը մեկնարկների քանակից:

Հեքիաթն այն մասին, թե ինչպես է Կասկադը ջնջվել տիրույթում, հաղթեց երկար մեկնարկին

Միևնույն ժամանակ, տվյալների բազայի չափի և աճի միջև փոխհարաբերությունների նույն բնույթն էր, որը 4 ՄԲ-ից դարձավ 15 ՄԲ: Ընդհանուր առմամբ, պարզվում է, որ ժամանակի ընթացքում (սառը մեկնարկների աճով) ավելացել են և՛ հավելվածների գործարկման ժամանակը, և՛ տվյալների բազայի չափը։ Մեր ձեռքերում վարկած կա. Այժմ մնում էր միայն հաստատել կախվածությունը։ Հետևաբար, մենք որոշեցինք հեռացնել «արտահոսքերը» և տեսնել, թե արդյոք դա կարագացնի գործարկումը:

Տվյալների բազայի անվերջ աճի պատճառները

Նախքան «արտահոսքերը» հեռացնելը, արժե հասկանալ, թե ինչու են դրանք հայտնվել առաջին հերթին: Դա անելու համար եկեք հիշենք, թե ինչ է թագավորությունը:

Realm-ը ոչ հարաբերական տվյալների բազա է: Այն թույլ է տալիս նկարագրել օբյեկտների միջև փոխհարաբերությունները այնպես, ինչպես նկարագրված են Android-ի ORM-ի հարաբերական տվյալների բազաները: Միևնույն ժամանակ, Realm-ը օբյեկտները պահում է անմիջապես հիշողության մեջ՝ նվազագույն քանակի փոխակերպումներով և քարտեզագրումներով: Սա թույլ է տալիս շատ արագ կարդալ տվյալները սկավառակից, ինչը Realm-ի ուժն է և ինչու է այն սիրում:

(Այս հոդվածի նպատակների համար այս նկարագրությունը բավական կլինի մեզ համար: Դուք կարող եք ավելին կարդալ Realm-ի մասին զով փաստաթղթավորում կամ նրանց մեջ ակադեմիան).

Շատ մշակողներ սովոր են ավելի շատ աշխատել հարաբերական տվյալների բազաների հետ (օրինակ՝ ORM տվյալների բազաները SQL-ի տակ): Եվ այնպիսի բաներ, ինչպիսիք են տվյալների կասկադային ջնջումը, հաճախ տրված են թվում: Բայց ոչ թագավորությունում:

Ի դեպ, կասկադի ջնջման հնարավորությունը վաղուց է պահանջվում։ Սա վերանայում и ուրիշ, կապված դրա հետ, ակտիվորեն քննարկվում էր։ Զգացողություն կար, որ դա շուտով կկատարվի։ Բայց հետո ամեն ինչ վերածվեց ուժեղ և թույլ օղակների ներդրման, ինչը նույնպես ինքնաբերաբար կլուծեր այս խնդիրը: Բավականին աշխույժ և ակտիվ էր այս գործում քաշելու հարցում, որն առայժմ ընդմիջվել է ներքին դժվարությունների պատճառով։

Տվյալների արտահոսք՝ առանց կասկադային ջնջման

Ինչպե՞ս են տվյալների արտահոսքը, եթե հիմնվում եք գոյություն չունեցող կասկադային ջնջման վրա: Եթե ​​դուք Nested Realm օբյեկտներ ունեք, ապա դրանք պետք է ջնջվեն:
Դիտարկենք (գրեթե) իրական օրինակ. Մենք ունենք օբյեկտ CartItemEntity:

@RealmClass
class CartItemEntity(
 @PrimaryKey
 override var id: String? = null,
 ...
 var name: String = "",
 var description: String = "",
 var image: ImageEntity? = null,
 var category: String = MENU_CATEGORY_UNKNOWN_ID,
 var customizationEntity: CustomizationEntity? = null,
 var cartComboProducts: RealmList<CartProductEntity> = RealmList(),
 ...
) : RealmObject()

Զամբյուղի ապրանքն ունի տարբեր դաշտեր, այդ թվում՝ նկար ImageEntity, հարմարեցված բաղադրիչներ CustomizationEntity. Բացի այդ, զամբյուղի մեջ գտնվող ապրանքը կարող է համակցված լինել իր ապրանքատեսակների հետ RealmList (CartProductEntity). Բոլոր թվարկված դաշտերը տիրույթի օբյեկտներ են: Եթե ​​մենք տեղադրենք նոր օբյեկտ (copyToRealm() / copyToRealmOrUpdate()) նույն ID-ով, ապա այս օբյեկտն ամբողջությամբ կվերագրվի: Բայց բոլոր ներքին օբյեկտները (պատկեր, հարմարեցումEntity և cartComboProducts) կկորցնեն կապը ծնողի հետ և կմնան տվյալների բազայում:

Քանի որ նրանց հետ կապը կորել է, մենք այլևս չենք կարդում կամ ջնջում դրանք (եթե մենք հստակորեն մուտք գործենք դրանց կամ մաքրենք ամբողջ «աղյուսակը»): Մենք սա անվանեցինք «հիշողության արտահոսք»:

Երբ մենք աշխատում ենք Realm-ի հետ, մենք պետք է հստակորեն անցնենք բոլոր տարրերի միջով և բացահայտորեն ջնջենք ամեն ինչ մինչև նման գործողությունները: Դա կարելի է անել, օրինակ, այսպես.

val entity = realm.where(CartItemEntity::class.java).equalTo("id", id).findFirst()
if (first != null) {
 deleteFromRealm(first.image)
 deleteFromRealm(first.customizationEntity)
 for(cartProductEntity in first.cartComboProducts) {
   deleteFromRealm(cartProductEntity)
 }
 first.deleteFromRealm()
}
// и потом уже сохраняем

Եթե ​​դուք դա անեք, ապա ամեն ինչ կաշխատի այնպես, ինչպես պետք է: Այս օրինակում մենք ենթադրում ենք, որ պատկերի, customizationEntity-ի և cartComboProducts-ի ներսում այլ nested Realm օբյեկտներ չկան, հետևաբար չկան այլ nested loops և deletes:

Արագ լուծում

Առաջին բանը, որ մենք որոշեցինք անել, այն էր, որ մաքրենք ամենաարագ աճող օբյեկտները և ստուգենք արդյունքները՝ տեսնելու, թե արդյոք դա կլուծի մեր սկզբնական խնդիրը: Նախ, ստացվեց ամենապարզ և ինտուիտիվ լուծումը, այն է՝ յուրաքանչյուր առարկա պետք է պատասխանատու լինի իր երեխաներին հեռացնելու համար: Դա անելու համար մենք ներկայացրեցինք ինտերֆեյս, որը վերադարձրեց իր մեջ տեղադրված Realm օբյեկտների ցանկը.

interface NestedEntityAware {
 fun getNestedEntities(): Collection<RealmObject?>
}

Եվ մենք այն իրականացրեցինք մեր տիրույթի օբյեկտներում.

@RealmClass
class DataPizzeriaEntity(
 @PrimaryKey
 var id: String? = null,
 var name: String? = null,
 var coordinates: CoordinatesEntity? = null,
 var deliverySchedule: ScheduleEntity? = null,
 var restaurantSchedule: ScheduleEntity? = null,
 ...
) : RealmObject(), NestedEntityAware {

 override fun getNestedEntities(): Collection<RealmObject?> {
   return listOf(
       coordinates,
       deliverySchedule,
       restaurantSchedule
   )
 }
}

В getNestedEntities մենք բոլոր երեխաներին վերադարձնում ենք որպես հարթ ցուցակ: Եվ յուրաքանչյուր երեխա օբյեկտ կարող է նաև իրականացնել NestedEntityAware ինտերֆեյսը, նշելով, որ այն ունի ներքին տիրույթի օբյեկտներ ջնջելու համար, օրինակ. ScheduleEntity:

@RealmClass
class ScheduleEntity(
 var monday: DayOfWeekEntity? = null,
 var tuesday: DayOfWeekEntity? = null,
 var wednesday: DayOfWeekEntity? = null,
 var thursday: DayOfWeekEntity? = null,
 var friday: DayOfWeekEntity? = null,
 var saturday: DayOfWeekEntity? = null,
 var sunday: DayOfWeekEntity? = null
) : RealmObject(), NestedEntityAware {

 override fun getNestedEntities(): Collection<RealmObject?> {
   return listOf(
       monday, tuesday, wednesday, thursday, friday, saturday, sunday
   )
 }
}

Եվ այսպես շարունակ, առարկաների բնադրումը կարող է կրկնվել։

Այնուհետև մենք գրում ենք մեթոդ, որը ռեկուրսիվ կերպով ջնջում է բոլոր տեղադրված օբյեկտները: Մեթոդ (պատրաստված է որպես ընդլայնում) deleteAllNestedEntities ստանում է բոլոր վերին մակարդակի օբյեկտները և մեթոդը deleteNestedRecursively Ռեկուրսիվ կերպով հեռացնում է բոլոր տեղադրված օբյեկտները՝ օգտագործելով NestedEntityAware ինտերֆեյսը.

fun <T> Realm.deleteAllNestedEntities(entities: Collection<T>,
 entityClass: Class<out RealmObject>,
 idMapper: (T) -> String,
 idFieldName : String = "id"
 ) {

 val existedObjects = where(entityClass)
     .`in`(idFieldName, entities.map(idMapper).toTypedArray())
     .findAll()

 deleteNestedRecursively(existedObjects)
}

private fun Realm.deleteNestedRecursively(entities: Collection<RealmObject?>) {
 for(entity in entities) {
   entity?.let { realmObject ->
     if (realmObject is NestedEntityAware) {
       deleteNestedRecursively((realmObject as NestedEntityAware).getNestedEntities())
     }
     realmObject.deleteFromRealm()
   }
 }
}

Մենք դա արեցինք ամենաարագ աճող օբյեկտների հետ և ստուգեցինք, թե ինչ է տեղի ունեցել:

Հեքիաթն այն մասին, թե ինչպես է Կասկադը ջնջվել տիրույթում, հաղթեց երկար մեկնարկին

Արդյունքում այն ​​առարկաները, որոնք մենք ծածկել ենք այս լուծույթով, դադարեցին աճել։ Իսկ բազայի ընդհանուր աճը դանդաղեց, բայց չդադարեց։

«Նորմալ» լուծում

Չնայած բազան սկսեց ավելի դանդաղ աճել, այն դեռ աճեց: Այսպիսով, մենք սկսեցինք ավելի հեռուն փնտրել: Մեր նախագիծը շատ ակտիվ օգտագործում է տվյալների քեշավորումը Realm-ում: Հետևաբար, յուրաքանչյուր օբյեկտի համար բոլոր տեղադրված օբյեկտները գրելը աշխատատար է, գումարած, սխալների ռիսկը մեծանում է, քանի որ ծածկագիրը փոխելիս կարող եք մոռանալ նշել օբյեկտները:

Ես ուզում էի համոզվել, որ ես չեմ օգտագործում ինտերֆեյսներ, բայց որ ամեն ինչ աշխատում է ինքնուրույն:

Երբ մենք ցանկանում ենք, որ ինչ-որ բան ինքնուրույն աշխատի, մենք պետք է օգտագործենք արտացոլումը: Դա անելու համար մենք կարող ենք անցնել յուրաքանչյուր դասի դաշտ և ստուգել՝ արդյոք դա Realm օբյեկտ է, թե օբյեկտների ցանկ.

RealmModel::class.java.isAssignableFrom(field.type)

RealmList::class.java.isAssignableFrom(field.type)

Եթե ​​դաշտը RealmModel կամ RealmList է, ապա ավելացրեք այս դաշտի օբյեկտը տեղադրված օբյեկտների ցանկին: Ամեն ինչ ճիշտ նույնն է, ինչ մենք արեցինք վերևում, միայն այստեղ դա ինքնին կկատարվի: Կասկադի ջնջման մեթոդն ինքնին շատ պարզ է և ունի հետևյալ տեսքը.

fun <T : Any> Realm.cascadeDelete(entities: Collection<T?>) {
 if(entities.isEmpty()) {
   return
 }

 entities.filterNotNull().let { notNullEntities ->
   notNullEntities
       .filterRealmObject()
       .flatMap { realmObject -> getNestedRealmObjects(realmObject) }
       .also { realmObjects -> cascadeDelete(realmObjects) }

   notNullEntities
       .forEach { entity ->
         if((entity is RealmObject) && entity.isValid) {
           entity.deleteFromRealm()
         }
       }
 }
}

Ընդլայնումը filterRealmObject զտում և փոխանցում է միայն տիրույթի օբյեկտները: Մեթոդ getNestedRealmObjects արտացոլման միջոցով այն գտնում է տիրույթի բոլոր օբյեկտները և դնում դրանք գծային ցուցակի մեջ: Այնուհետև մենք նույն բանն ենք անում ռեկուրսիվ կերպով: Ջնջելիս անհրաժեշտ է ստուգել օբյեկտի վավերականությունը isValid, քանի որ կարող է լինել, որ տարբեր մայր օբյեկտներ կարող են ունենալ բույն նույնական օբյեկտներ: Ավելի լավ է խուսափել դրանից և պարզապես օգտագործել id-ի ավտոմատ գեներացիան նոր օբյեկտներ ստեղծելիս:

Հեքիաթն այն մասին, թե ինչպես է Կասկադը ջնջվել տիրույթում, հաղթեց երկար մեկնարկին

GetNestedRealmObjects մեթոդի ամբողջական իրականացում

private fun getNestedRealmObjects(realmObject: RealmObject) : List<RealmObject> {
 val nestedObjects = mutableListOf<RealmObject>()
 val fields = realmObject.javaClass.superclass.declaredFields

// Проверяем каждое поле, не является ли оно RealmModel или списком RealmList
 fields.forEach { field ->
   when {
     RealmModel::class.java.isAssignableFrom(field.type) -> {
       try {
         val child = getChildObjectByField(realmObject, field)
         child?.let {
           if (isInstanceOfRealmObject(it)) {
             nestedObjects.add(child as RealmObject)
           }
         }
       } catch (e: Exception) { ... }
     }

     RealmList::class.java.isAssignableFrom(field.type) -> {
       try {
         val childList = getChildObjectByField(realmObject, field)
         childList?.let { list ->
           (list as RealmList<*>).forEach {
             if (isInstanceOfRealmObject(it)) {
               nestedObjects.add(it as RealmObject)
             }
           }
         }
       } catch (e: Exception) { ... }
     }
   }
 }

 return nestedObjects
}

private fun getChildObjectByField(realmObject: RealmObject, field: Field): Any? {
 val methodName = "get${field.name.capitalize()}"
 val method = realmObject.javaClass.getMethod(methodName)
 return method.invoke(realmObject)
}

Արդյունքում, մեր հաճախորդի կոդում մենք օգտագործում ենք «կասկադային ջնջում» տվյալների փոփոխման յուրաքանչյուր գործողության համար: Օրինակ, ներդիրի գործողության համար այն ունի հետևյալ տեսքը.

override fun <T : Entity> insert(
 entityInformation: EntityInformation,
 entities: Collection<T>): Collection<T> = entities.apply {
 realmInstance.cascadeDelete(getManagedEntities(entityInformation, this))
 realmInstance.copyFromRealm(
     realmInstance
         .copyToRealmOrUpdate(this.map { entity -> entity as RealmModel }
 ))
}

Մեթոդը նախ getManagedEntities ստանում է բոլոր ավելացված օբյեկտները, այնուհետև մեթոդը cascadeDelete Ռեկուրսիվ կերպով ջնջում է բոլոր հավաքագրված օբյեկտները նորերը գրելուց առաջ: Մենք ի վերջո օգտագործում ենք այս մոտեցումը դիմումի ողջ ընթացքում: Realm-ում հիշողության արտահոսքն ամբողջությամբ վերացել է: Կատարելով գործարկման ժամանակի կախվածության նույն չափումը հավելվածի սառը մեկնարկների քանակից, մենք տեսնում ենք արդյունքը:

Հեքիաթն այն մասին, թե ինչպես է Կասկադը ջնջվել տիրույթում, հաղթեց երկար մեկնարկին

Կանաչ գիծը ցույց է տալիս հավելվածի գործարկման ժամանակի կախվածությունը ցուրտ մեկնարկների քանակից՝ տեղադրված օբյեկտների ավտոմատ կասկադային ջնջման ժամանակ:

Արդյունքներ և եզրակացություններ

Realm-ի անընդհատ աճող տվյալների բազան պատճառ էր դառնում, որ հավելվածը շատ դանդաղ գործարկվի: Մենք թողարկեցինք թարմացում՝ ներդիր օբյեկտների մեր սեփական «կասկադային ջնջմամբ»: Եվ հիմա մենք վերահսկում և գնահատում ենք, թե ինչպես է մեր որոշումը ազդել հավելվածի գործարկման ժամանակի վրա՝ _app_start չափման միջոցով:

Հեքիաթն այն մասին, թե ինչպես է Կասկադը ջնջվել տիրույթում, հաղթեց երկար մեկնարկին

Վերլուծության համար մենք վերցնում ենք 90 օր ժամանակաշրջան և տեսնում ենք. հավելվածի գործարկման ժամանակը, և՛ միջինը, և՛ այն, որը ընկնում է օգտատերերի 95-րդ տոկոսի վրա, սկսեց նվազել և այլևս չի բարձրանում:

Հեքիաթն այն մասին, թե ինչպես է Կասկադը ջնջվել տիրույթում, հաղթեց երկար մեկնարկին

Եթե ​​նայեք յոթ օրվա գծապատկերին, ապա _app_start չափանիշը լիովին համարժեք է թվում և 1 վայրկյանից պակաս է:

Արժե նաև ավելացնել, որ լռելյայնորեն Firebase-ը ծանուցումներ է ուղարկում, եթե _app_start-ի միջին արժեքը գերազանցում է 5 վայրկյանը: Այնուամենայնիվ, ինչպես տեսնում ենք, դուք չպետք է ապավինեք դրա վրա, այլ ավելի շուտ մուտք գործեք և հստակորեն ստուգեք այն:

Realm տվյալների բազայի առանձնահատկությունն այն է, որ այն ոչ հարաբերական տվյալների բազա է: Չնայած օգտագործման հեշտությանը, ORM լուծումներին նմանությանը և օբյեկտների կապակցմանը, այն չունի կասկադային ջնջում:

Եթե ​​դա հաշվի չառնվի, ապա բնադրված առարկաները կկուտակվեն և «կհոսեն»։ Տվյալների բազան անընդհատ կաճի, ինչն իր հերթին կազդի հավելվածի դանդաղեցման կամ գործարկման վրա։

Ես կիսվել եմ մեր փորձով, թե ինչպես կարելի է արագ կատարել օբյեկտների կասկադային ջնջում Realm-ում, որի մասին դեռևս դուրս չի եկել, բայց դրա մասին երկար ժամանակ խոսվել է: նրանք ասում են и նրանք ասում են. Մեր դեպքում դա մեծապես արագացրեց հավելվածի գործարկման ժամանակը:

Չնայած այս հատկանիշի մոտալուտ տեսքի մասին քննարկմանը, Կասկադի ջնջման բացակայությունը Realm-ում կատարվում է դիզայնով: Եթե ​​դուք նոր հավելված եք նախագծում, ապա դա հաշվի առեք։ Իսկ եթե արդեն օգտվում եք Realm-ից, ստուգեք՝ արդյոք նման խնդիրներ ունեք։

Source: www.habr.com

Добавить комментарий