I am using the @ionic/storage
package in Ionic 5, in retrospect I wish I would have used the SQLite plugin, but, anyways… here we are.
So I don’t really know what is going on under-the-hood that well as the docs don’t really provide that information. Is there a way to create tables? So my application has many separated sections, for example I have peerMessages, groupMessages, contacts, notifications, etc. And, currently in each of their respective service I am creating a new database(I think? once again docs not too clear) by doing
private messageStorage: Storage;
private groupMessageStorage: Storage;
public contructor() {
this.messageStorage = new Storage({
name: 'messageStorage',
storeName: '_messageStorage',
});
this.groupMessageStorage = new Storage({
name: 'groupMessage',
storeName: '_groupMessage',
});
}
now from the localforge docs I am creating multiple instances. Which I am assuming that this is not the same as a table, but, instead a whole other database??
I guess what my question is, if this option that I have done above is ok and will it scale when I create more of these instances? because as it is now when the application is started and I am inspecting through google chrome I see 6 instances of waiting for the Storage being opened, which as I add more instances is only going to slow down the start of the application. What I would like to do is create one db and then just access the table messages
or groupMessages
and save data/etc.
@ionic/storage
is a key/value pair storage API. It does not have the concept of tables.
As stated in the docs
Ionic Storage is a free, open source alternative for indie devs, students & hobbyists. It provides an easy way to store key/value pairs and JSON objects.
I’d suggest looking over the example in the docs cus your example is not correct.
but the docs of Localforage, which it is just an extrapolation, says that creating these instances is feasible.
Localforage != ionic/storage.
We create a single instance through Angular’s injectable setup.
You could just use Localforage directly instead.
So then, how would it be recommended that I create instances? As I have been developing this for months almost a year using this approach… it just doesn’t make sense that I would create one instance and handle it from there especially when we are talking about real world apps and not a todo app…
As stated in the docs.
import { IonicStorageModule } from '@ionic/storage';
@NgModule({
declarations: [
// ...
],
imports: [
BrowserModule,
IonicModule.forRoot(MyApp),
IonicStorageModule.forRoot()
],
bootstrap: [IonicApp],
entryComponents: [
// ...
],
providers: [
// ...
]
})
export class AppModule {}
It’s a standard nosql/key-value storage mechanism. While the current approach covers 90% of use cases, you are free to not use the library and use something else directly.
unessesry comment IMO
1 Like
so If I am to get this correct, then it’s ill advised to create a DatabaseService
because whenever you inject it into a service then each service (like MessageService, ContactService, etc) upon injection already has its independent instance and won’t write over each other.
No, not at all. I’m saying that if you are using it like the docs suggest, you will only ever one instance. So you will be writing to the same storage instances, and it’s up to you to make sure you do not overwrite things.
// app.module.ts
import { IonicStorageModule } from '@ionic/storage';
@NgModule({
declarations: [
// ...
],
imports: [
BrowserModule,
IonicModule.forRoot(MyApp),
// create my single global storage instance.
IonicStorageModule.forRoot()
],
bootstrap: [AppComponent],
declarations: [AppComponent],
})
export class AppModule {}
export class MessageService{
constructor(private storage: Storage){
//this.storage is the same global storage
}
}
export class ContactService{
constructor(private storage: Storage){
//this.storage is the same global storage
}
}
export class DatabaseService{
constructor(private storage: Storage){
//this.storage is the same global storage
}
}
You will only ever have one instance of a storage setup if you use ionic/storage.
Ok thank you for that. How well do you think this would scale? Part of the reason why I liked multiple instances is because the lookup would be faster if you had smaller “tables” plus, when it comes to app initializing when it comes to getting all the “contacts” I know that they are all there. I don’t need to have additional sorting methods for what is a “contact”, “groupMessage”, “peerMessage”, etc. All I would have to do is just sort things alphabetically, or some other constraint.
for example:
export class MessageService{
constructor(private storage: Storage){}
public async loadPeerMessages(userId): Promise<Message[]> {
return await this.storage.get(userId);
}
public async saveMessage(message: Message): Promise<void> {
this.storage.set(message.userId, message);
}
}
with that I can be sure that I get only the messages from the userId I specified. Otherwise if I had to use the global object method I would have to do something like:
export class MessageService{
public peerMessages: Message[] = [];
constructor(private storage: Storage){}
public async loadPeerMessages(userId): Promise<Message[]> {
const allMessages: Message[] = await this.storage.get('PEER_MESSAGES');
allMessages.forEach((message: Message): void => {
if (message.id === userId) {
this.peerMessages.push(message);
}
});
this.behaviorSubject.next(this.peerMessages);
}
public async saveMessage(message: Message): Promise<void> {
const allMessages: Message[] = await this.storage.get('PEER_MESSAGES');
allMessages.push(message)
this.storage.set('PEER_MESSAGES', allMessages);
}
}
while the above approach works I think it is clear to see that there are many additional steps that could slow it down especially as there are additional conversations.
so looks like I am going to have to completely get rid of the @ionic/storage plugin as it is just not actually good for apps outside of hobby apps. Forgive my rudeness but I wasted so much time using this package.
Obviously if you decide you need a relational database, you need a relational database. I would just like to relate how I view Ionic Storage, in the hopes that it may work for your situation as well.
Some apps are sort of like Guy Pearce’s character in Memento with the short-term memory loss. Every night when they go to sleep (terminate) they forget everything, and when they wake up again (get launched the next time) we have a mess.
While they’re running, though, things are fine. It’s only when asleep that we have problems. And that’s the key in how I deal with Storage. I read everything I need from it only once, at app startup (technically, sometimes this is done lazily on demand, but from the overall design perspective, there is only ever one read per app run). I write whenever something crucial happens that needs to be preserved if the app got killed Real Soon Now. But I never read back during normal operations, so nothing blocks on Storage. All in-app communication is done in RAM using RxJS and ordinary business objects.
I understand what you are talking about, I just figured that since the package is written on top of Localforage and Localforage supports instances then it the package would also, maybe I should open up a ticket on the github for a feature request. Or just clone the repo and make a pull request with the feature.
Storage read/writes tend to not matter much these days. Having one vs many is not really a bottle neck, so I look at it as premature optimization.
Unless you can measure things and prove that it is an issue, there’s no need.
1 Like
Another important part of the equation (for me, at least, because I’m old and forgetful) is that I find it counterintuitive to have to block on writes, lest a different code path end up with a stale read. Treating Storage like a “save game” feature instead of an integral part of in-app communication has the beneficial side-effect of allowing me to safely ignore the Promise
returned by the write.