రియల్మ్‌లో క్యాస్కేడ్ తొలగింపు సుదీర్ఘ ప్రయోగంలో ఎలా విజయం సాధించింది అనే కథ

వినియోగదారులందరూ మొబైల్ అప్లికేషన్‌లలో వేగవంతమైన లాంచ్ మరియు ప్రతిస్పందించే UIని మంజూరు చేస్తారు. అప్లికేషన్ ప్రారంభించటానికి చాలా సమయం తీసుకుంటే, వినియోగదారు విచారంగా మరియు కోపంగా భావిస్తారు. మీరు వినియోగదారు అనుభవాన్ని సులభంగా పాడు చేయవచ్చు లేదా వినియోగదారు అనువర్తనాన్ని ఉపయోగించడం ప్రారంభించక ముందే పూర్తిగా కోల్పోవచ్చు.

డోడో పిజ్జా యాప్‌ని లాంచ్ చేయడానికి సగటున 3 సెకన్లు పడుతుందని మరియు కొంతమంది “అదృష్టవంతుల” కోసం 15-20 సెకన్లు పడుతుందని మేము ఒకసారి కనుగొన్నాము.

కట్ క్రింద సంతోషకరమైన ముగింపుతో కూడిన కథ ఉంది: రియల్మ్ డేటాబేస్ పెరుగుదల, మెమరీ లీక్, మేము సమూహ వస్తువులను ఎలా సేకరించాము, ఆపై మనల్ని మనం కలిసి లాగి ప్రతిదీ పరిష్కరించాము.

రియల్మ్‌లో క్యాస్కేడ్ తొలగింపు సుదీర్ఘ ప్రయోగంలో ఎలా విజయం సాధించింది అనే కథ

రియల్మ్‌లో క్యాస్కేడ్ తొలగింపు సుదీర్ఘ ప్రయోగంలో ఎలా విజయం సాధించింది అనే కథ
వ్యాస రచయిత: మాగ్జిమ్ కచింకిన్ — డోడో పిజ్జాలో ఆండ్రాయిడ్ డెవలపర్.

అప్లికేషన్ చిహ్నంపై క్లిక్ చేయడం నుండి మొదటి కార్యాచరణ యొక్క onResume() వరకు మూడు సెకన్లు అనంతం. మరియు కొంతమంది వినియోగదారులకు, ప్రారంభ సమయం 15-20 సెకన్లకు చేరుకుంది. ఇది కూడా ఎలా సాధ్యం?

చదవడానికి సమయం లేని వారి కోసం చాలా చిన్న సారాంశం
మా Realm డేటాబేస్ అనంతంగా పెరిగింది. కొన్ని సమూహ వస్తువులు తొలగించబడలేదు, కానీ నిరంతరం సేకరించబడ్డాయి. అప్లికేషన్ ప్రారంభ సమయం క్రమంగా పెరిగింది. అప్పుడు మేము దాన్ని పరిష్కరించాము మరియు ప్రారంభ సమయం లక్ష్యానికి వచ్చింది - ఇది 1 సెకను కంటే తక్కువగా మారింది మరియు ఇకపై పెరగలేదు. వ్యాసం పరిస్థితి యొక్క విశ్లేషణ మరియు రెండు పరిష్కారాలను కలిగి ఉంది - శీఘ్ర మరియు సాధారణమైనది.

సమస్య యొక్క శోధన మరియు విశ్లేషణ

నేడు, ఏదైనా మొబైల్ అప్లికేషన్ త్వరగా ప్రారంభించబడాలి మరియు ప్రతిస్పందించేదిగా ఉండాలి. అయితే ఇది మొబైల్ యాప్ గురించి మాత్రమే కాదు. సేవ మరియు కంపెనీతో పరస్పర చర్య యొక్క వినియోగదారు అనుభవం సంక్లిష్టమైన విషయం. ఉదాహరణకు, మా విషయంలో, డెలివరీ వేగం పిజ్జా సేవ కోసం కీలక సూచికలలో ఒకటి. డెలివరీ వేగంగా ఉంటే, పిజ్జా వేడిగా ఉంటుంది మరియు ఇప్పుడు తినాలనుకునే కస్టమర్ ఎక్కువసేపు వేచి ఉండాల్సిన అవసరం లేదు. అప్లికేషన్ కోసం, వేగవంతమైన సేవ యొక్క అనుభూతిని సృష్టించడం చాలా ముఖ్యం, ఎందుకంటే అప్లికేషన్ ప్రారంభించటానికి 20 సెకన్లు మాత్రమే తీసుకుంటే, మీరు పిజ్జా కోసం ఎంతసేపు వేచి ఉండాలి?

మొదట, కొన్నిసార్లు అప్లికేషన్ ప్రారంభించటానికి కొన్ని సెకన్ల సమయం పట్టిందనే వాస్తవాన్ని మనం ఎదుర్కొన్నాము, ఆపై ఎంత సమయం పట్టిందనే దాని గురించి ఇతర సహోద్యోగుల నుండి ఫిర్యాదులను వినడం ప్రారంభించాము. కానీ మేము ఈ పరిస్థితిని స్థిరంగా పునరావృతం చేయలేకపోయాము.

అదెంత పొడుగు? ప్రకారం Google డాక్యుమెంటేషన్, అప్లికేషన్ యొక్క చల్లని ప్రారంభం 5 సెకన్ల కంటే తక్కువ తీసుకుంటే, ఇది "సాధారణంగా" పరిగణించబడుతుంది. డోడో పిజ్జా ఆండ్రాయిడ్ యాప్ ప్రారంభించబడింది (ఫైర్‌బేస్ మెట్రిక్‌ల ప్రకారం _యాప్_ప్రారంభం) వద్ద చల్లని ప్రారంభం సగటున 3 సెకన్లలో - “గొప్పది కాదు, భయంకరమైనది కాదు,” వారు చెప్పినట్లు.

కానీ అప్లికేషన్ లాంచ్ చేయడానికి చాలా, చాలా, చాలా సమయం పట్టిందని ఫిర్యాదులు కనిపించడం ప్రారంభించాయి! ప్రారంభించడానికి, "చాలా, చాలా, చాలా పొడవు" అంటే ఏమిటో కొలవాలని మేము నిర్ణయించుకున్నాము. మరియు మేము దీని కోసం Firebase ట్రేస్‌ని ఉపయోగించాము యాప్ ప్రారంభం ట్రేస్.

రియల్మ్‌లో క్యాస్కేడ్ తొలగింపు సుదీర్ఘ ప్రయోగంలో ఎలా విజయం సాధించింది అనే కథ

ఈ ప్రామాణిక ట్రేస్ వినియోగదారు అనువర్తనాన్ని తెరిచిన క్షణం మరియు మొదటి కార్యాచరణ యొక్క onResume() అమలు చేయబడిన క్షణం మధ్య సమయాన్ని కొలుస్తుంది. ఫైర్‌బేస్ కన్సోల్‌లో ఈ మెట్రిక్‌ని _app_start అంటారు. ఇది తేలింది:

  • మధ్యస్థ కోల్డ్ స్టార్టప్ సమయం 95 సెకన్ల కంటే తక్కువగా ఉన్నప్పటికీ, 20వ పర్సంటైల్ కంటే ఎక్కువ ఉన్న వినియోగదారులకు ప్రారంభ సమయాలు దాదాపు 5 సెకన్లు (కొన్ని ఎక్కువ కూడా ఉంటాయి).
  • ప్రారంభ సమయం స్థిరమైన విలువ కాదు, కానీ కాలక్రమేణా పెరుగుతుంది. కానీ కొన్నిసార్లు చుక్కలు ఉన్నాయి. మేము విశ్లేషణ స్థాయిని 90 రోజులకు పెంచినప్పుడు మేము ఈ నమూనాను కనుగొన్నాము.

రియల్మ్‌లో క్యాస్కేడ్ తొలగింపు సుదీర్ఘ ప్రయోగంలో ఎలా విజయం సాధించింది అనే కథ

రెండు ఆలోచనలు గుర్తుకు వచ్చాయి:

  1. ఏదో లీక్ అవుతోంది.
  2. ఈ "ఏదో" విడుదలైన తర్వాత రీసెట్ చేయబడుతుంది మరియు మళ్లీ లీక్ అవుతుంది.

"బహుశా డేటాబేస్తో ఏదో ఉంది," మేము అనుకున్నాము మరియు మేము సరైనది. ముందుగా, మేము డేటాబేస్‌ను కాష్‌గా ఉపయోగిస్తాము; మైగ్రేషన్ సమయంలో మేము దానిని క్లియర్ చేస్తాము. రెండవది, అప్లికేషన్ ప్రారంభమైనప్పుడు డేటాబేస్ లోడ్ అవుతుంది. ప్రతిదీ కలిసి సరిపోతుంది.

Realm డేటాబేస్‌లో ఏమి తప్పు ఉంది

మేము మొదటి ఇన్‌స్టాలేషన్ నుండి మరియు సక్రియ వినియోగం సమయంలో అప్లికేషన్ యొక్క జీవితంలో డేటాబేస్ యొక్క కంటెంట్‌లు ఎలా మారతాయో తనిఖీ చేయడం ప్రారంభించాము. మీరు దీని ద్వారా రియల్మ్ డేటాబేస్ యొక్క కంటెంట్‌లను వీక్షించవచ్చు స్టెతో లేదా ఫైల్‌ను తెరవడం ద్వారా మరింత వివరంగా మరియు స్పష్టంగా రియల్మ్ స్టూడియో. ADB ద్వారా డేటాబేస్ యొక్క కంటెంట్‌లను వీక్షించడానికి, Realm డేటాబేస్ ఫైల్‌ను కాపీ చేయండి:

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

వేర్వేరు సమయాల్లో డేటాబేస్ యొక్క కంటెంట్లను చూసిన తరువాత, ఒక నిర్దిష్ట రకం వస్తువుల సంఖ్య నిరంతరం పెరుగుతోందని మేము కనుగొన్నాము.

రియల్మ్‌లో క్యాస్కేడ్ తొలగింపు సుదీర్ఘ ప్రయోగంలో ఎలా విజయం సాధించింది అనే కథ
చిత్రం రెండు ఫైల్‌ల కోసం రియల్మ్ స్టూడియో యొక్క భాగాన్ని చూపుతుంది: ఎడమ వైపున - అప్లికేషన్ బేస్ ఇన్‌స్టాలేషన్ తర్వాత కొంత సమయం వరకు, కుడి వైపున - క్రియాశీల ఉపయోగం తర్వాత. వస్తువుల సంఖ్యను చూడవచ్చు ImageEntity и MoneyType గణనీయంగా పెరిగింది (స్క్రీన్‌షాట్ ప్రతి రకం వస్తువుల సంఖ్యను చూపుతుంది).

డేటాబేస్ పెరుగుదల మరియు ప్రారంభ సమయం మధ్య సంబంధం

అనియంత్రిత డేటాబేస్ వృద్ధి చాలా చెడ్డది. అయితే ఇది అప్లికేషన్ ప్రారంభ సమయాన్ని ఎలా ప్రభావితం చేస్తుంది? ActivityManager ద్వారా దీన్ని కొలవడం చాలా సులభం. Android 4.4 నుండి, logcat ప్రదర్శించబడిన స్ట్రింగ్ మరియు సమయంతో లాగ్‌ను ప్రదర్శిస్తుంది. ఈ సమయం అప్లికేషన్ ప్రారంభించబడిన క్షణం నుండి కార్యాచరణ రెండరింగ్ ముగిసే వరకు విరామానికి సమానం. ఈ సమయంలో క్రింది సంఘటనలు జరుగుతాయి:

  • ప్రక్రియను ప్రారంభించండి.
  • వస్తువులను ప్రారంభించడం.
  • కార్యకలాపాలను సృష్టించడం మరియు ప్రారంభించడం.
  • లేఅవుట్‌ను సృష్టిస్తోంది.
  • అప్లికేషన్ రెండరింగ్.

మాకు సరిపోతుంది. మీరు -S మరియు -W ఫ్లాగ్‌లతో ADBని అమలు చేస్తే, మీరు ప్రారంభ సమయంతో పొడిగించిన అవుట్‌పుట్ పొందవచ్చు:

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

అక్కడి నుంచి పట్టుకుంటే grep -i WaitTime సమయం, మీరు ఈ మెట్రిక్ సేకరణను ఆటోమేట్ చేయవచ్చు మరియు ఫలితాలను దృశ్యమానంగా చూడవచ్చు. దిగువన ఉన్న గ్రాఫ్ అప్లికేషన్ యొక్క శీతల ప్రారంభాల సంఖ్యపై అప్లికేషన్ ప్రారంభ సమయం యొక్క ఆధారపడటాన్ని చూపుతుంది.

రియల్మ్‌లో క్యాస్కేడ్ తొలగింపు సుదీర్ఘ ప్రయోగంలో ఎలా విజయం సాధించింది అనే కథ

అదే సమయంలో, డేటాబేస్ పరిమాణం మరియు పెరుగుదల మధ్య సంబంధం యొక్క అదే స్వభావం ఉంది, ఇది 4 MB నుండి 15 MBకి పెరిగింది. మొత్తంగా, కాలక్రమేణా (కోల్డ్ స్టార్ట్‌ల పెరుగుదలతో), అప్లికేషన్ లాంచ్ సమయం మరియు డేటాబేస్ పరిమాణం రెండూ పెరిగాయని తేలింది. మన చేతుల్లో ఒక పరికల్పన ఉంది. ఇప్పుడు మిగిలి ఉన్నది ఆధారపడటాన్ని నిర్ధారించడం. అందువల్ల, మేము "లీక్‌లను" తొలగించాలని నిర్ణయించుకున్నాము మరియు ఇది ప్రయోగాన్ని వేగవంతం చేస్తుందో లేదో చూడండి.

అంతులేని డేటాబేస్ వృద్ధికి కారణాలు

"లీక్స్" ను తొలగించే ముందు, వారు మొదటి స్థానంలో ఎందుకు కనిపించారో అర్థం చేసుకోవడం విలువ. దీన్ని చేయడానికి, రాజ్యం అంటే ఏమిటో గుర్తుంచుకోండి.

Realm అనేది నాన్-రిలేషనల్ డేటాబేస్. ఆండ్రాయిడ్‌లో ఎన్ని ORM రిలేషనల్ డేటాబేస్‌లు వివరించబడ్డాయో అదే విధంగా వస్తువుల మధ్య సంబంధాలను వివరించడానికి ఇది మిమ్మల్ని అనుమతిస్తుంది. అదే సమయంలో, రియల్మ్ తక్కువ మొత్తంలో రూపాంతరాలు మరియు మ్యాపింగ్‌లతో నేరుగా వస్తువులను మెమరీలో నిల్వ చేస్తుంది. ఇది డిస్క్ నుండి డేటాను చాలా త్వరగా చదవడానికి మిమ్మల్ని అనుమతిస్తుంది, ఇది రియల్మ్ యొక్క బలం మరియు ఎందుకు ప్రేమించబడుతోంది.

(ఈ కథనం యొక్క ప్రయోజనాల కోసం, ఈ వివరణ మాకు సరిపోతుంది. మీరు కూల్‌లో రాజ్యం గురించి మరింత చదవవచ్చు డాక్యుమెంటేషన్ లేదా వారి అకాడమీ).

చాలా మంది డెవలపర్‌లు రిలేషనల్ డేటాబేస్‌లతో ఎక్కువ పని చేయడం అలవాటు చేసుకున్నారు (ఉదాహరణకు, హుడ్ కింద SQLతో ORM డేటాబేస్‌లు). మరియు క్యాస్కేడింగ్ డేటా తొలగింపు వంటి విషయాలు తరచుగా ఇచ్చినట్లుగా కనిపిస్తాయి. కానీ రాజ్యంలో కాదు.

మార్గం ద్వారా, క్యాస్కేడ్ తొలగింపు ఫీచర్ చాలా కాలంగా అడుగుతున్నారు. ఈ పునర్విమర్శ и మరొకటి, దానితో అనుబంధం, చురుకుగా చర్చించబడింది. త్వరలోనే అది పూర్తవుతుందన్న భావన నెలకొంది. కానీ అప్పుడు ప్రతిదీ బలమైన మరియు బలహీనమైన లింక్‌ల పరిచయంలోకి అనువదించబడింది, ఇది స్వయంచాలకంగా ఈ సమస్యను కూడా పరిష్కరిస్తుంది. ఈ టాస్క్‌లో చాలా ఉల్లాసంగా మరియు చురుకుగా ఉండేవారు అభ్యర్థనను లాగండి, అంతర్గత ఇబ్బందుల కారణంగా ఇది ప్రస్తుతానికి పాజ్ చేయబడింది.

క్యాస్కేడింగ్ తొలగింపు లేకుండా డేటా లీక్

మీరు ఉనికిలో లేని క్యాస్కేడింగ్ డిలీట్‌పై ఆధారపడితే డేటా సరిగ్గా ఎలా లీక్ అవుతుంది? మీరు గూడులో ఉన్న 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())ని చొప్పించినట్లయితే, ఈ వస్తువు పూర్తిగా భర్తీ చేయబడుతుంది. కానీ అన్ని అంతర్గత వస్తువులు (చిత్రం, అనుకూలీకరణఎంటిటీ మరియు కార్ట్‌కాంబోప్రొడక్ట్‌లు) పేరెంట్‌తో కనెక్షన్‌ను కోల్పోతాయి మరియు డేటాబేస్‌లో ఉంటాయి.

వారితో కనెక్షన్ కోల్పోయినందున, మేము వాటిని ఇకపై చదవము లేదా తొలగించము (మేము వాటిని స్పష్టంగా యాక్సెస్ చేయకపోతే లేదా మొత్తం “టేబుల్”ని క్లియర్ చేస్తే తప్ప). మేము దీనిని "మెమరీ లీక్స్" అని పిలిచాము.

మేము 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()
}
// и потом уже сохраняем

మీరు ఇలా చేస్తే, ప్రతిదీ తప్పనిసరిగా పని చేస్తుంది. ఈ ఉదాహరణలో, ఇమేజ్, కస్టమైజేషన్‌ఎంటిటీ మరియు కార్ట్‌కాంబోప్రొడక్ట్‌లలో ఇతర సమూహ రియల్మ్ వస్తువులు లేవని మేము ఊహిస్తాము, కాబట్టి ఇతర సమూహ లూప్‌లు మరియు తొలగింపులు లేవు.

"త్వరిత" పరిష్కారం

మేము ముందుగా నిర్ణయించుకున్నది వేగంగా అభివృద్ధి చెందుతున్న వస్తువులను శుభ్రపరచడం మరియు ఇది మా అసలు సమస్యను పరిష్కరిస్తుందో లేదో తెలుసుకోవడానికి ఫలితాలను తనిఖీ చేయడం. మొదట, సరళమైన మరియు అత్యంత స్పష్టమైన పరిష్కారం తయారు చేయబడింది, అవి: ప్రతి వస్తువు దాని పిల్లలను తొలగించడానికి బాధ్యత వహించాలి. దీన్ని చేయడానికి, మేము దాని సమూహ రియల్మ్ ఆబ్జెక్ట్‌ల జాబితాను అందించిన ఇంటర్‌ఫేస్‌ను పరిచయం చేసాము:

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()
   }
 }
}

మేము వేగంగా అభివృద్ధి చెందుతున్న వస్తువులతో దీన్ని చేసాము మరియు ఏమి జరిగిందో తనిఖీ చేసాము.

రియల్మ్‌లో క్యాస్కేడ్ తొలగింపు సుదీర్ఘ ప్రయోగంలో ఎలా విజయం సాధించింది అనే కథ

ఫలితంగా, మేము ఈ పరిష్కారంతో కప్పబడిన వస్తువులు పెరగడం ఆగిపోయాయి. మరియు బేస్ యొక్క మొత్తం పెరుగుదల మందగించింది, కానీ ఆగలేదు.

"సాధారణ" పరిష్కారం

బేస్ మరింత నెమ్మదిగా పెరగడం ప్రారంభించినప్పటికీ, అది ఇంకా పెరిగింది. కాబట్టి మేము మరింత వెతకడం ప్రారంభించాము. మా ప్రాజెక్ట్ రియల్మ్‌లో డేటా కాషింగ్‌ను చాలా చురుకుగా ఉపయోగిస్తుంది. అందువల్ల, ప్రతి వస్తువు కోసం అన్ని సమూహ వస్తువులను వ్రాయడం శ్రమతో కూడుకున్నది, అంతేకాకుండా లోపాల ప్రమాదం పెరుగుతుంది, ఎందుకంటే కోడ్‌ను మార్చేటప్పుడు మీరు వస్తువులను పేర్కొనడం మర్చిపోవచ్చు.

నేను ఇంటర్‌ఫేస్‌లను ఉపయోగించలేదని నిర్ధారించుకోవాలనుకున్నాను, కానీ ప్రతిదీ దాని స్వంతదానిపై పని చేస్తుంది.

మనం ఏదైనా సొంతంగా పని చేయాలనుకున్నప్పుడు, మనం ప్రతిబింబాన్ని ఉపయోగించాలి. దీన్ని చేయడానికి, మేము ప్రతి తరగతి ఫీల్డ్ ద్వారా వెళ్లి అది రియల్మ్ ఆబ్జెక్ట్ లేదా ఆబ్జెక్ట్‌ల జాబితా కాదా అని తనిఖీ చేయవచ్చు:

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, ఎందుకంటే వివిధ మాతృ వస్తువులు ఒకేలా ఉండే వాటిని కలిగి ఉండవచ్చు. దీన్ని నివారించడం మరియు కొత్త ఆబ్జెక్ట్‌లను క్రియేట్ చేసేటప్పుడు ఐడి యొక్క ఆటో-జెనరేషన్‌ని ఉపయోగించడం మంచిది.

రియల్మ్‌లో క్యాస్కేడ్ తొలగింపు సుదీర్ఘ ప్రయోగంలో ఎలా విజయం సాధించింది అనే కథ

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 డేటాబేస్ అప్లికేషన్ చాలా నెమ్మదిగా లాంచ్ అయ్యేలా చేస్తోంది. మేము సమూహ వస్తువుల యొక్క మా స్వంత "క్యాస్కేడింగ్ తొలగింపు"తో ఒక నవీకరణను విడుదల చేసాము. మరియు ఇప్పుడు మేము మా నిర్ణయం _app_start మెట్రిక్ ద్వారా అప్లికేషన్ ప్రారంభ సమయాన్ని ఎలా ప్రభావితం చేసిందో పర్యవేక్షిస్తాము మరియు మూల్యాంకనం చేస్తాము.

రియల్మ్‌లో క్యాస్కేడ్ తొలగింపు సుదీర్ఘ ప్రయోగంలో ఎలా విజయం సాధించింది అనే కథ

విశ్లేషణ కోసం, మేము 90 రోజుల వ్యవధిని తీసుకుంటాము మరియు చూడండి: అప్లికేషన్ లాంచ్ సమయం, మధ్యస్థ మరియు 95వ శాతం వినియోగదారులపై వచ్చేది, తగ్గడం ప్రారంభమైంది మరియు ఇకపై పెరగదు.

రియల్మ్‌లో క్యాస్కేడ్ తొలగింపు సుదీర్ఘ ప్రయోగంలో ఎలా విజయం సాధించింది అనే కథ

మీరు ఏడు రోజుల చార్ట్‌ను చూస్తే, _app_start మెట్రిక్ పూర్తిగా సరిపోతుందని మరియు 1 సెకను కంటే తక్కువగా ఉన్నట్లు కనిపిస్తోంది.

డిఫాల్ట్‌గా, Firebase _app_start మధ్యస్థ విలువ 5 సెకన్లు దాటితే నోటిఫికేషన్‌లను పంపుతుందని కూడా జోడించడం విలువైనదే. అయితే, మేము చూడగలిగినట్లుగా, మీరు దీనిపై ఆధారపడకూడదు, కానీ లోపలికి వెళ్లి స్పష్టంగా తనిఖీ చేయండి.

Realm డేటాబేస్ గురించిన ప్రత్యేకత ఏమిటంటే ఇది నాన్-రిలేషనల్ డేటాబేస్. వాడుకలో సౌలభ్యం, ORM సొల్యూషన్స్ మరియు ఆబ్జెక్ట్ లింకింగ్ సారూప్యత ఉన్నప్పటికీ, దీనికి క్యాస్కేడ్ తొలగింపు లేదు.

దీనిని పరిగణనలోకి తీసుకోకపోతే, గూడు కట్టుకున్న వస్తువులు పేరుకుపోతాయి మరియు "లీక్ అవుతాయి." డేటాబేస్ నిరంతరం పెరుగుతుంది, ఇది అప్లికేషన్ యొక్క మందగమనం లేదా ప్రారంభాన్ని ప్రభావితం చేస్తుంది.

రియల్మ్‌లోని వస్తువుల క్యాస్కేడ్ తొలగింపును త్వరగా ఎలా చేయాలనే దానిపై మా అనుభవాన్ని నేను పంచుకున్నాను, ఇది ఇంకా బాక్స్‌లో లేదు, కానీ చాలా కాలంగా మాట్లాడబడింది చెప్పండి и చెప్పండి. మా విషయంలో, ఇది అప్లికేషన్ ప్రారంభ సమయాన్ని బాగా వేగవంతం చేసింది.

ఈ ఫీచర్ యొక్క ఆసన్న రూపాన్ని గురించి చర్చ జరిగినప్పటికీ, Realmలో క్యాస్కేడ్ తొలగింపు లేకపోవడం డిజైన్ ద్వారా చేయబడుతుంది. మీరు కొత్త అప్లికేషన్‌ను డిజైన్ చేస్తుంటే, దీన్ని పరిగణనలోకి తీసుకోండి. మరియు మీరు ఇప్పటికే Realmని ఉపయోగిస్తుంటే, మీకు అలాంటి సమస్యలు ఉన్నాయో లేదో తనిఖీ చేయండి.

మూలం: www.habr.com

ఒక వ్యాఖ్యను జోడించండి