When we are committing one entry to source chain, if it is not duplicated the address of new entry will return, otherwise the address of existing one.
For example in Anchor scenario, whenever we need to commit new entry with Anchor in relative, we need to execute commit_entry for anchor any time, which can be confusing, in my opinion. (actually we execute commit_entry to get the current one as well)
I was wondering isn’t it better to rename commit_entry to commit_or_get_entry ?
one note: while it’s not duplicated in the DHT or even the user’s own local storage, the user does create a new source chain entry every time they commit it. So it’s not a fully idempotent function, even though it feels like it.
I just came across this in my runtime. I had expected idempotency when committing the same entry multiple times. However it quickly increases my chain length.
So do I get this right that:
content is still deduplicated
duplicate commits do not increase local storage
the entry chain (like a git commit log?) increases
If so, what is then the idiomatic procedure to prevent committing an entry if it’s already in the source chain? Calculate address from entry, try to retrieve contents from the address, and only commit if it’s empty (get_entry_initial?). Or is there a command for this?
I solved my issue with the following. Would love to know if there’s an in-built function instead.
use hdk::prelude::*;
use hdk_proc_macros::zome;
use holochain_wasm_utils::holochain_core_types::entry::Entry;
use holochain_wasm_utils::holochain_core_types::agent::AgentId;
use holochain_wasm_utils::api_serialization::{QueryArgsNames, QueryArgsOptions, QueryResult};
use holochain_wasm_utils::holochain_persistence_api::cas::content::Address;
use holochain_wasm_utils::holochain_core_types::entry::entry_type::AppEntryType;
// TODO: keep global store of all entry addresses for quicker lookups
pub fn commit_entry_once(entry: &Entry) -> ZomeApiResult<Address> {
let address_result = hdk::api::entry_address(entry);
match entry {
Entry::App(app_entry_type, _) => {
if address_result.is_err() {
return address_result;
}
return match entry_exists_local(&app_entry_type.to_string(), &address_result.clone().unwrap()) {
Ok(itdoes) => {
if itdoes {
return address_result;
} else {
return hdk::commit_entry(entry);
}
},
Err(reason) => Err(reason)
}
},
_ => panic!("expected App Entry type!")
}
}
pub fn entry_exists(address: &Address) -> ZomeApiResult<bool> {
let pre_existing = hdk::api::get_entry_initial(address);
return match pre_existing {
Ok(result) => Ok(result.is_some()),
Err(reason) => Err(reason)
}
}
// TODO: there must be an easier way...
// is data not stored with some kind of hashmap functionality? this seems inefficient
pub fn entry_exists_local(typ : &str, address: &Address) -> ZomeApiResult<bool> {
let query_result = hdk::query_result(QueryArgsNames::QueryName(typ.into()), QueryArgsOptions {
start: 0,
limit: 1000,
headers: true,
entries: false
});
match query_result {
Ok(QueryResult::Headers(headers)) => {
for header in headers {
if header.entry_address().eq(address) {
return Ok(true);
}
}
},
_ => panic!("unexpected enum value for QueryResult!")
};
return Ok(false);
}
@ldwm
At this point I suggest you to look at the Holochain as an event sourcing architecture which means agent’s actions and activities are important than data.
as you know the header of user actions goes to DHT that can be used for different scenarios. (based on the application)
From my view bringing the Idempotent concept to the source chain and application is totally based on the definition and business rule of application.
I also would like to hear from @guillemcordoba if there is any special or breaking changes in this topic in holochain 2020
@hedayat not really, it’s up to the application to control in whatever granularity they want.
@ldwm so, here is how the process goes: when you do hdk::commit_entry, that creates the entry, hashes it and creates a header besides it. That header is chained to your other headers, which is the “chain” part of holochain. But, everything that is content addressable is deduplicated in a holochain node. So, if you create a new entry that already existed in the node, it will calculate the hash, look at the CAS (Content Addressable Store), see that’s already there and not store it. It will create a new header, with the hash of the entry, and then append that to your source chain. When querying all the entries of your source chain, it appears as “duplicate” but that’s merely a matter of doing twice the “query_entry” for the same hash in the same store, the entry is not actually duplicated.