{"action":"created","issue":{"url":"https://api.github.com/repos/dalmatinerdb/mstore/issues/2","repository_url":"https://api.github.com/repos/dalmatinerdb/mstore","labels_url":"https://api.github.com/repos/dalmatinerdb/mstore/issues/2/labels{/name}","comments_url":"https://api.github.com/repos/dalmatinerdb/mstore/issues/2/comments","events_url":"https://api.github.com/repos/dalmatinerdb/mstore/issues/2/events","html_url":"https://github.com/dalmatinerdb/mstore/pull/2","id":141615416,"number":2,"title":"Lessen the probability that integrity constraints are violated","user":{"login":"davecromberge","id":164431,"avatar_url":"https://avatars.githubusercontent.com/u/164431?v=3","gravatar_id":"","url":"https://api.github.com/users/davecromberge","html_url":"https://github.com/davecromberge","followers_url":"https://api.github.com/users/davecromberge/followers","following_url":"https://api.github.com/users/davecromberge/following{/other_user}","gists_url":"https://api.github.com/users/davecromberge/gists{/gist_id}","starred_url":"https://api.github.com/users/davecromberge/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/davecromberge/subscriptions","organizations_url":"https://api.github.com/users/davecromberge/orgs","repos_url":"https://api.github.com/users/davecromberge/repos","events_url":"https://api.github.com/users/davecromberge/events{/privacy}","received_events_url":"https://api.github.com/users/davecromberge/received_events","type":"User","site_admin":false},"labels":[],"state":"open","locked":false,"assignee":null,"milestone":null,"comments":1,"created_at":"2016-03-17T15:24:15Z","updated_at":"2016-03-17T16:42:28Z","closed_at":null,"pull_request":{"url":"https://api.github.com/repos/dalmatinerdb/mstore/pulls/2","html_url":"https://github.com/dalmatinerdb/mstore/pull/2","diff_url":"https://github.com/dalmatinerdb/mstore/pull/2.diff","patch_url":"https://github.com/dalmatinerdb/mstore/pull/2.patch"},"body":"The first time that a metric occurs for a given data point, the path has to be appended to the index as well as saving the values to the mstore itself.  These two file operations are not an atomic transaction, and when one write fails the other may succeed.  This creates problems when calculating the offset for future read operations.  \r\n\r\nAn common example of a failure scenario is when the metric path is successfully appended to the index file, but the writing of values fails.  This will crash the process, and the in-memory btrie of \"seen\" metrics will not persist to successive operations.  The next time a write occurs, the metric will be restored in the index, causing an off by one error when calculating read offsets.  This will often result in an `eof` error during handoffs or read operations.\r\n\r\nThe proposed solution strives to balance performance concerns with the simplicity embodied in the design, and is to re-order the operations to attempt the more volatile write first.\r\n\r\nOther alternatives that were considered:\r\n- Single write or command log that either succeeds or fails. and may be retried as long as operations are idempotent (appends are not).\r\n- Modify a temporary file that is copied over the original should the operation succeed.  Not an option because of cost involved.\r\n- Make the relationship between writes more explicit by using forward composition on the two, e . g.\r\n `fun = fcompose(writePoints, writeIndex)` where `fun` is a single side-effectful function."},"comment":{"url":"https://api.github.com/repos/dalmatinerdb/mstore/issues/comments/197967248","html_url":"https://github.com/dalmatinerdb/mstore/pull/2#issuecomment-197967248","issue_url":"https://api.github.com/repos/dalmatinerdb/mstore/issues/2","id":197967248,"user":{"login":"Licenser","id":119093,"avatar_url":"https://avatars.githubusercontent.com/u/119093?v=3","gravatar_id":"","url":"https://api.github.com/users/Licenser","html_url":"https://github.com/Licenser","followers_url":"https://api.github.com/users/Licenser/followers","following_url":"https://api.github.com/users/Licenser/following{/other_user}","gists_url":"https://api.github.com/users/Licenser/gists{/gist_id}","starred_url":"https://api.github.com/users/Licenser/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/Licenser/subscriptions","organizations_url":"https://api.github.com/users/Licenser/orgs","repos_url":"https://api.github.com/users/Licenser/repos","events_url":"https://api.github.com/users/Licenser/events{/privacy}","received_events_url":"https://api.github.com/users/Licenser/received_events","type":"User","site_admin":false},"created_at":"2016-03-17T16:42:28Z","updated_at":"2016-03-17T16:42:28Z","body":"Hi I see the problem you are facing but I partially disagree with approach to resolve it. It opens up the system to data corruption, while the current code only makes data loss a option.\r\n\r\nWrites are atomic in ZFS (that is 1 write operation either succeeds or fails entirely), of cause two write operations are not. So both `file:write_file` and `file:pwrite` will never write partial data. The case where both or neither succeeds is simple as they have clear outcomes. Updating the index before the metric store is important. That way the offset indexing guarantees that even if no data is written the space will later on be reserved (containing NULL's) while doing it the other way around can lead to points being written but the index not being claimed, then later on it can happen that those metrics will be appear in a wrong series.\r\n\r\nHowever that is a bug I see, the write to the indexing needs to check for success, if no index is written no data should ever be written."}}