Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Sample for ark-core FileStorage JNI bindings usage #110

Open
wants to merge 15 commits into
base: main
Choose a base branch
from

Conversation

oluiscabral
Copy link
Collaborator

No description provided.

binding.tvMapValues.text = getString(R.string.empty_map)
return
}
storage!!.writeFS()
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I should check with the crate implementation, but do we really need flushing? That isn't handled by the crate?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

From what I tested, no

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

storage.set and storage.remove do not write to fs, only storage.erase deletes file

@oluiscabral oluiscabral requested a review from kirillt November 13, 2024 21:12
@@ -16,13 +16,21 @@ dependencyResolutionManagement {
url = URI("https://jitpack.io")
}
maven {
name = "GitHubPackages"
name = "arklib-android GitHub Packages"
url = URI("https://maven.pkg.github.com/ARK-Builders/arklib-android")
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Let's use ark-android to avoid confusion with the archived repo.

It was just a fat lib when we were prototyping, now we moved to the next level with modular framework 😁

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes. I don't know if I understood it correctly. This is indeed a reference to arklib-android archive, it is not a name for the current repo. Should we use another repo instead?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oh, I didn't notice the link leads to the archived repo.

We certainly should not use archived repos.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

But what do we use from arklib-android?

Copy link
Collaborator Author

@oluiscabral oluiscabral Dec 12, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@kirillt It's being used in at least 23 places, just search for dev.arkbuilders.arklib and you'll realize it too. Here's an example on the sample package

import dev.arkbuilders.arklib.data.folders.FoldersRepo
import dev.arkbuilders.arklib.initArkLib
import dev.arkbuilders.arklib.initRustLogger

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

But what do we use from arklib-android?

TagSelector and ScoreWidget dependent on arklib, they need ResourceIndex, TagStorage, ScoreStorage and so on

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks guys, I wasn't realizing it.. I'll come up with plan for the upgrade later.

@kirillt
Copy link
Member

kirillt commented Nov 14, 2024

Doesn't really work on my devices... Tried on Android versions 9 and 11.

Steps to test:

  1. Create an empty folder.
  2. Select it in storage demo.
  3. Either:
    a. Leave the prefix field empty.
    - Adding some values doesn't lead to creation of the storage on disk.
    - Deleting non-existing values doesn't lead to crash or error.
    b. Fill the prefix field.
    - Crash as soon as the field input is complete.

@oluiscabral
Copy link
Collaborator Author

Doesn't really work on my devices... Tried on Android versions 8 and 11.

Steps to test:

  1. Create an empty folder.
  2. Select it in storage demo.
  3. Either:
    a. Leave the prefix field empty.
    • Adding some values doesn't lead to creation of the storage on disk.
    • Deleting non-existing values doesn't lead to crash or error.
      b. Fill the prefix field.
    • Crash as soon as the field input is complete.

Hi! @kirillt Just repeating what we previously discussed: this issue was being caused by the fact that the released APK did not include ark-core JNI binaries.

Now, with the latest updates, it fortunately might be fixed, but let me know if it still persists

@kirillt
Copy link
Member

kirillt commented Nov 30, 2024

@oluiscabral still doesn't work. Nothing is written to disk.

@oluiscabral
Copy link
Collaborator Author

@oluiscabral still doesn't work. Nothing is written to disk.

@kirillt That's unfortunate... But I might know what's happening. I'll try another potential solution and let you know again when it's done

Comment on lines 38 to 42
- name: Download and extract `ark-core` JNI libs
run: |
wget https://github.com/ARK-Builders/ark-core/releases/download/v1.0.0/jniLibs.zip
unzip -d sample/src/main jniLibs.zip

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sorry for suddenly jumping into this, but I think implementation(libraries.core) is enough. In theory we don't have to manually download the JNI libs like this because it's already included in the synced dependency of arkcore.

Copy link
Collaborator Author

@oluiscabral oluiscabral Dec 11, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just repeating what we later discussed: implementation(libraries.core) is not currently enough, since ark-core/java is a JAR, that means JNI libs are not available for use from it. However, I'm working on transforming ark-core/java into an AAR instead (to make JNI libs available directly from the package)

@oluiscabral
Copy link
Collaborator Author

@kirillt Just converted the JAR into an AAR. Let me know if it's still working for you

Copy link
Member

@mdrlzy mdrlzy left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM
btw FileStorage api lacks method to get Kotlin Map of entries, we have only iterator
Binding use own BTreeMap iterator under the hood. But it is not very convenient to building map manually everytime

Copy link
Member

@mdrlzy mdrlzy left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

readFS, sync, syncStatus, merge, get not implemented

@mdrlzy
Copy link
Member

mdrlzy commented Dec 13, 2024

syncStatus called on non-existent file causes crash

JNI DETECTED ERROR IN APPLICATION: JNI FindClass called with pending exception java.lang.RuntimeException: IO error: No such file or directory (os error 2)

@oluiscabral
Copy link
Collaborator Author

readFS, sync, syncStatus, merge, get not implemented

syncStatus called on non-existent file causes crash

JNI DETECTED ERROR IN APPLICATION: JNI FindClass called with pending exception java.lang.RuntimeException: IO error: No such file or directory (os error 2)

Thanks for contributing, @mdrlzy!

It's nice that you created a foundation to test each one of the FileStorage methods. I wonder what @tuancoltech thinks about it, because initially I'd also updated the UI and formatted the code, but ended reverting it back after @kirillt told me that Tuan was actually working on it (UI)

Anyways, I have a few considerations about the points you brought. syncStatus indeed throws an error if the FileStorage is not yet persisted on disk. This is actually the behavior of the source code on ark-core. The same applies for sync (that depends on syncStatus), readFS and even erase. All of them presume that writeFS was already called at least once, that is the FileStorage is on disk.

So all that said, maybe should we disable the buttons related to those methods if the button for writeFS was not yet clicked?

About merge and get, both of them are already working for me. Did it crash when you tested them out?

@mdrlzy
Copy link
Member

mdrlzy commented Dec 13, 2024

It's nice that you created a foundation to test each one of the FileStorage methods. I wonder what @tuancoltech thinks about it, because initially I'd also updated the UI and formatted the code, but ended reverting it back after @kirillt told me that Tuan was actually working on it (UI)

I dont know. Kirill asked me to do review/refactor.

So all that said, maybe should we disable the buttons related to those methods if the button for writeFS was not yet clicked?

Good idea. Done. Can you test it?

About merge and get, both of them are already working for me. Did it crash when you tested them out?

Everything works for me. Except for some cases where the storage file is missing but it seems to be justified

Let's wait for Kirill's review

@kirillt
Copy link
Member

kirillt commented Dec 15, 2024

@oluiscabral to create better API suited for app developers' needs, we need one of these two static functions in fs-storage:

  1. fn list(root: Path): Vec<Path> that lists all storages in a root folder, regardless of their type. I believe that would be too difficult without relying on specific subpath, so we could simply list everything under $root/.ark/user. The sample, or an app could filter this list by the storage name.

    • Example: list("/a/b/c/") returns ["/a/b/c/.ark/user/tags", "/a/b/c/.ark/user/scores"]
  2. fn load(root: Path, name: String): Option<BaseStorage> loads the storage with name name if it exists. The path for any combination of root and name is determined by fs-storage and can change in future. Especially, when we add atomic versions into fs-storage.

    • Example: load("/a/b/c/", "tags") returns Some(FileStorage("/a/b/c/.ark/user/tags"))
    • Example: list("/a/b/c/", "properties") returns Some(FolderStorage("/a/b/c/.ark/user/properties"))
    • Example: list("/a/b/c/", "garbage") returns None

This way, the app developer will be able to specify the "root" folder and the storage name and simply load or initialize the storage without working with the file path directly.

@mdrlzy initially I had the idea of flexible path selection in the sample, to ease testing, but I don't think it's a good idea to expose the path via the API to the app developer. We should prevent incorrect usage of the underlying files of the storage. Could you modify the sample to only allow selection of root and name for the storage? If the storage exists, fs-storage must inform the app developer about the storage type: FileStorage, or FolderStorage. If it doesn't exist yet, the app developer is free to choose the type to initialize new storage.

empty storage is ok to be not persisted, for the rest we have sync status, it sounds ok to have storage in pending state if we have something like "flush" or "sync" operation

@kirillt
Copy link
Member

kirillt commented Dec 15, 2024

@mdrlzy also, would be great to add display for SyncStatus that is returned by fn sync_status

It seems to me (although I'm not 100% sure) that usages of flush method should be replaceable with fn sync.

@oluiscabral other minor changes to the BaseStorage API could be done:

  • restrict visibility to write_fs and read_fs, it should be used only by the storage module itself
  • declare public getter entries returning same type as read_fs, i.e. Result<&BTreeMap<K, V>>
  • change return type of read_fs to simply Result<()>

@mdrlzy
Copy link
Member

mdrlzy commented Dec 15, 2024

initially I had the idea of flexible path selection in the sample, to ease testing, but I don't think it's a good idea to expose the path via the API to the app developer. We should prevent incorrect usage of the underlying files of the storage. Could you modify the sample to only allow selection of root and name for the storage? If the storage exists, fs-storage must inform the app developer about the storage type: FileStorage, or FolderStorage. If it doesn't exist yet, the app developer is free to choose the type to initialize new storage.

empty storage is ok to be not persisted, for the rest we have sync status, it sounds ok to have storage in pending state if we have something like "flush" or "sync" operation

The problem is that if storage file does not exist then syncStatus, sync, readFS, erase causes crash.
So when using api in this form we need to know whether the file exists or not.
Current FileStorage api does not require root at all and it can be placed anywhere.
There is also no binding for FolderStorage yet.

also, would be great to add display for SyncStatus that is returned by fn sync_status

We already have button that shows toast with sync status. Is this not enough?

@kirillt
Copy link
Member

kirillt commented Dec 15, 2024

We already have button that shows toast with sync status. Is this not enough?

Should be enough. Sorry, I haven't tested the APK yet.

@oluiscabral
Copy link
Collaborator Author

@oluiscabral to create better API suited for app developers' needs, we need one of these two static functions in fs-storage:

  1. fn list(root: Path): Vec<Path> that lists all storages in a root folder, regardless of their type. I believe that would be too difficult without relying on specific subpath, so we could simply list everything under $root/.ark/user. The sample, or an app could filter this list by the storage name.

    • Example: list("/a/b/c/") returns ["/a/b/c/.ark/user/tags", "/a/b/c/.ark/user/scores"]
  2. fn load(root: Path, name: String): Option<BaseStorage> loads the storage with name name if it exists. The path for any combination of root and name is determined by fs-storage and can change in future. Especially, when we add atomic versions into fs-storage.

    • Example: load("/a/b/c/", "tags") returns Some(FileStorage("/a/b/c/.ark/user/tags"))
    • Example: list("/a/b/c/", "properties") returns Some(FolderStorage("/a/b/c/.ark/user/properties"))
    • Example: list("/a/b/c/", "garbage") returns None

This way, the app developer will be able to specify the "root" folder and the storage name and simply load or initialize the storage without working with the file path directly.

Sounds great! Do you think it should already be addressed on this PR?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants