Skip to content

Commit

Permalink
Dictionary import fix for low RAM and database corrupt on exit fix
Browse files Browse the repository at this point in the history
  • Loading branch information
arianneorpilla committed Mar 22, 2023
1 parent 9de0fbd commit 43385c6
Show file tree
Hide file tree
Showing 4 changed files with 88 additions and 77 deletions.
153 changes: 81 additions & 72 deletions yuuna/lib/src/dictionary/dictionary_utils.dart
Original file line number Diff line number Diff line change
Expand Up @@ -108,118 +108,127 @@ Future<void> depositDictionaryDataHelper(PrepareDictionaryParams params) async {
}
}

/// Write as one transaction. If anything fails, no changes should occur.
/// Write the [Dictionary] entity.
database.writeTxnSync(() {
/// Write the [Dictionary] entity.
database.dictionarys.putSync(params.dictionary);
});

/// Write [DictionaryTag] entities.
int tagCount = 0;
int tagTotal = tags.length;
database.dictionaryTags.putAllSync(tags);
partition<DictionaryTag>(tags, 10000).forEach((batch) {
/// Write [DictionaryTag] entities.
int tagCount = 0;
int tagTotal = tags.length;
partition<DictionaryTag>(tags, 10000).forEach((batch) {
database.writeTxnSync(() {
database.dictionaryTags.putAllSync(batch);
tagCount += batch.length;
params.send(t.import_write_tag(count: tagCount, total: tagTotal));
});
tagCount += batch.length;
params.send(t.import_write_tag(count: tagCount, total: tagTotal));
});

/// Write [DictionaryPitch] entities.
int pitchCount = 0;
int pitchTotal = pitchesByHeading.values.map((e) => e.length).sum;
partition<MapEntry<DictionaryHeading, List<DictionaryPitch>>>(
pitchesByHeading.entries, 10000)
.forEach((batch) {
/// Write [DictionaryPitch] entities.
int pitchCount = 0;
int pitchTotal = pitchesByHeading.values.map((e) => e.length).sum;
partition<MapEntry<DictionaryHeading, List<DictionaryPitch>>>(
pitchesByHeading.entries, 10000)
.forEach((batch) {
database.writeTxnSync(() {
for (MapEntry<DictionaryHeading,
List<DictionaryPitch>> pitchesForHeading in batch) {
DictionaryHeading heading = pitchesForHeading.key;
List<DictionaryPitch> pitches = pitchesForHeading.value;

database.dictionaryHeadings.putSync(heading);
database.dictionaryPitchs.putAllSync(pitches);

pitchCount += pitches.length;
}

params.send(t.import_write_pitch(count: pitchCount, total: pitchTotal));
});

/// Write [DictionaryFrequency] entities.
int frequencyCount = 0;
int frequencyTotal = frequenciesByHeading.values.map((e) => e.length).sum;
partition<MapEntry<DictionaryHeading, List<DictionaryFrequency>>>(
frequenciesByHeading.entries, 10000)
.forEach((batch) {
params.send(t.import_write_pitch(count: pitchCount, total: pitchTotal));
});

/// Write [DictionaryFrequency] entities.
int frequencyCount = 0;
int frequencyTotal = frequenciesByHeading.values.map((e) => e.length).sum;
partition<MapEntry<DictionaryHeading, List<DictionaryFrequency>>>(
frequenciesByHeading.entries, 10000)
.forEach((batch) {
database.writeTxnSync(() {
for (MapEntry<DictionaryHeading,
List<DictionaryFrequency>> frequenciesForHeading in batch) {
DictionaryHeading heading = frequenciesForHeading.key;
List<DictionaryFrequency> frequencies = frequenciesForHeading.value;

database.dictionaryHeadings.putSync(heading);
database.dictionaryFrequencys.putAllSync(frequencies);

frequencyCount += frequencies.length;
}

params.send(t.import_write_frequency(
count: frequencyCount, total: frequencyTotal));
});

/// Used to test the collision resistance of the FNV-1a algorithm used
/// for hashing dictionary headings to each have unique integer IDs.
/// This doesn't seem that heavy but we shouldn't instantiate millions
/// of elements at any given time, so this should be commented out for
/// a production release or when not debugging for collisions.
///
/// For testing, a mix of Japanese bilingual and monolingual dictionaries
/// can be imported in sequence. The collision count should always be
/// zero. Interestingly, the Dart implementation of FNV-1a recommended by
/// Isar seems to produce less collisions than a MurmurHash V3
/// implementation. In any case, the code below can be uncommented for
/// and hash algorithm comparison testing and research.
///
/// The idea is to get the delta number of headings, but also take into
/// account the number of headings already in the database.
// int headingsInDatabase = database.dictionaryHeadings.countSync();
// int headingsToImportAlreadyInDatabase = database.dictionaryHeadings
// .getAllSync(entriesByHeading.keys.map((e) => e.id).toList())
// .whereNotNull()
// .length;
// int headingsToImportNotInDatabase =
// entriesByHeading.keys.length - headingsToImportAlreadyInDatabase;

// debugPrint('Headings In Database: $headingsInDatabase');
// debugPrint(
// 'Headings To Import Already In Database: $headingsToImportAlreadyInDatabase');
// debugPrint(
// 'Headings To Import Not In Database: $headingsToImportNotInDatabase');

/// Write [DictionaryEntry] entities.
int entryCount = 0;
int entryTotal = entriesByHeading.values.map((e) => e.length).sum;
partition<MapEntry<DictionaryHeading, List<DictionaryEntry>>>(
entriesByHeading.entries, 10000)
.forEach((batch) {
params.send(t.import_write_frequency(
count: frequencyCount, total: frequencyTotal));
});

/// Used to test the collision resistance of the FNV-1a algorithm used
/// for hashing dictionary headings to each have unique integer IDs.
/// This doesn't seem that heavy but we shouldn't instantiate millions
/// of elements at any given time, so this should be commented out for
/// a production release or when not debugging for collisions.
///
/// For testing, a mix of Japanese bilingual and monolingual dictionaries
/// can be imported in sequence. The collision count should always be
/// zero. Interestingly, the Dart implementation of FNV-1a recommended by
/// Isar seems to produce less collisions than a MurmurHash V3
/// implementation. In any case, the code below can be uncommented for
/// and hash algorithm comparison testing and research.
///
/// The idea is to get the delta number of headings, but also take into
/// account the number of headings already in the database.
// int headingsInDatabase = database.dictionaryHeadings.countSync();
// int headingsToImportAlreadyInDatabase = database.dictionaryHeadings
// .getAllSync(entriesByHeading.keys.map((e) => e.id).toList())
// .whereNotNull()
// .length;
// int headingsToImportNotInDatabase =
// entriesByHeading.keys.length - headingsToImportAlreadyInDatabase;

// debugPrint('Headings In Database: $headingsInDatabase');
// debugPrint(
// 'Headings To Import Already In Database: $headingsToImportAlreadyInDatabase');
// debugPrint(
// 'Headings To Import Not In Database: $headingsToImportNotInDatabase');

/// Write [DictionaryEntry] entities.
int entryCount = 0;
int entryTotal = entriesByHeading.values.map((e) => e.length).sum;
partition<MapEntry<DictionaryHeading, List<DictionaryEntry>>>(
entriesByHeading.entries, 10000)
.forEach((batch) {
database.writeTxnSync(() {
for (MapEntry<DictionaryHeading,
List<DictionaryEntry>> entriesForHeading in batch) {
DictionaryHeading heading = entriesForHeading.key;
List<DictionaryEntry> entries = entriesForHeading.value;

database.dictionaryHeadings.putSync(heading);
database.dictionaryEntrys.putAllSync(entries);

entryCount += entries.length;
}

params.send(t.import_write_entry(count: entryCount, total: entryTotal));
});

/// Collision count should always be zero.
// int newHeadingsInDatabase = database.dictionaryHeadings.countSync();
// int collisionsFound = newHeadingsInDatabase -
// headingsInDatabase -
// headingsToImportNotInDatabase;
// debugPrint('New Headings In Database: $newHeadingsInDatabase');
// debugPrint('Collisions Found: $collisionsFound');
params.send(t.import_write_entry(count: entryCount, total: entryTotal));
});

/// Collision count should always be zero.
// int newHeadingsInDatabase = database.dictionaryHeadings.countSync();
// int collisionsFound = newHeadingsInDatabase -
// headingsInDatabase -
// headingsToImportNotInDatabase;
// debugPrint('New Headings In Database: $newHeadingsInDatabase');
// debugPrint('Collisions Found: $collisionsFound');
} catch (e, stackTrace) {
params.send(stackTrace);
params.send(e);
Expand Down
5 changes: 3 additions & 2 deletions yuuna/lib/src/pages/implementations/creator_page.dart
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import 'dart:ui';

import 'package:expandable/expandable.dart';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:flutter_exit_app/flutter_exit_app.dart';
import 'package:fluttertoast/fluttertoast.dart';
import 'package:spaces/spaces.dart';
Expand Down Expand Up @@ -57,7 +58,7 @@ class _CreatorPageState extends BasePageState<CreatorPage> {
}

if (widget.killOnPop) {
FlutterExitApp.exitApp();
SystemNavigator.pop();
return false;
}

Expand Down Expand Up @@ -627,7 +628,7 @@ class _CreatorPageState extends BasePageState<CreatorPage> {
icon: Icons.arrow_back,
onTap: () {
if (widget.killOnPop) {
FlutterExitApp.exitApp();
SystemNavigator.pop();
} else {
Navigator.pop(context);
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:flutter_exit_app/flutter_exit_app.dart';
import 'package:material_floating_search_bar/material_floating_search_bar.dart';
import 'package:spaces/spaces.dart';
Expand Down Expand Up @@ -135,7 +136,7 @@ class _RecursiveDictionaryPageState
onFocusChanged: (focused) {
if (!focused) {
if (widget.killOnPop) {
FlutterExitApp.exitApp();
SystemNavigator.pop();
} else {
Navigator.pop(context);
}
Expand Down Expand Up @@ -225,7 +226,7 @@ class _RecursiveDictionaryPageState
icon: Icons.arrow_back,
onTap: () async {
if (widget.killOnPop) {
FlutterExitApp.exitApp();
SystemNavigator.pop();
} else {
Navigator.pop(context);
}
Expand Down
2 changes: 1 addition & 1 deletion yuuna/pubspec.yaml
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
name: yuuna
description: A full-featured immersion language learning suite for mobile.
publish_to: 'none'
version: 2.4.3+43
version: 2.4.4+45
environment:
sdk: ">=2.17.0 <3.0.0"

Expand Down

0 comments on commit 43385c6

Please sign in to comment.