Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Support
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in / Register
Toggle navigation
E
ecommerce-ui
Project overview
Project overview
Details
Activity
Releases
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Issues
0
Issues
0
List
Boards
Labels
Milestones
Merge Requests
0
Merge Requests
0
Analytics
Analytics
Repository
Value Stream
Wiki
Wiki
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Create a new issue
Commits
Issue Boards
Open sidebar
nexedi
ecommerce-ui
Commits
bad40a1d
Commit
bad40a1d
authored
Feb 19, 2014
by
Sven Franck
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
first pass replicate storage with error handling
parent
2074b05c
Changes
1
Hide whitespace changes
Inline
Side-by-side
Showing
1 changed file
with
186 additions
and
134 deletions
+186
-134
js/libs/jio/replicatestorage.js
js/libs/jio/replicatestorage.js
+186
-134
No files found.
js/libs/jio/replicatestorage.js
View file @
bad40a1d
...
...
@@ -17,7 +17,7 @@
*/
/*jslint indent: 2, maxlen: 80, nomen: true */
/*global define, module, require, jIO, RSVP */
/*global define, module, require, jIO, RSVP
, JSON
*/
(
function
(
root
,
dependencies
,
factory
)
{
"
use strict
"
;
...
...
@@ -57,6 +57,41 @@
return
false
;
}
/**
* Make a clean clone of an object with clean referring to removal of all
* flags indicating missing or non-coherent elements.
* @param {Object} obj to clone
* @return {Object} clones object
*/
function
cleanClone
(
obj
)
{
// NOTE: polyfill needed for IE8-
var
r
=
Object
.
create
(
obj
);
if
(
r
.
_missing
)
{
delete
r
.
_missing
;
}
if
(
r
.
_conflict_list
)
{
delete
r
.
_conflict_list
;
}
return
r
;
};
/**
* Generate a checksum from a passed string to compare the response returned
* by multiple storages for the same requests.
* @param {Object} obj to generate a checksum for
* @return {Integer} checksum
*/
function
checksum
(
obj
)
{
var
r
,
i
,
str
=
JSON
.
stringify
(
obj
);
for
(
r
=
0
,
i
=
0
;
i
<
str
.
length
;
i
+=
1
)
{
r
=
(
r
<<
5
)
-
r
+
str
.
charCodeAt
(
i
);
r
&=
r
;
}
return
r
;
};
/**
* Executes a sequence of *then* callbacks. It acts like
* `smth().then(callback).then(callback)...`. The first callback is called
...
...
@@ -202,38 +237,55 @@
}
/**
* Responds with the last modified document recieved. If all promises are
* rejected, it returns the latest rejected promise answer received. Promises
* are cancelled only by calling `lastModified(promise_list).cancel()`. USE
* THIS FUNCTION ONLY FOR GET METHOD!
*
* lastModified(promise_list): Promise
* Test a response across multiple storages. Returns first valid document
* together with the following parameters:
*
* _missing = true > when a storage returns a 404 for this document
* _version_list = [] > when a file has multiple (conflicting) versions
*
* Promises are cancelled only by calling
*
* `checkAndReturn(promise_list).cancel()`
*
* @param {Array} promise_list An array of promises
* @return {Promise} A new promise
*/
function
lastModified
(
promise_list
)
{
function
checkAndReturn
(
promise_list
)
{
var
length
=
promise_list
.
length
;
promise_list
=
promise_list
.
slice
();
return
new
Promise
(
function
(
resolve
,
reject
,
notify
)
{
var
index
,
last_good_answer
,
last_answer
,
count
=
0
,
error_count
=
0
;
var
index
,
doc
,
trace
,
version_list
,
count
,
error_count
,
tag
;
version_list
=
[];
count
=
0
;
error_count
=
0
;
tag
=
function
(
reply
)
{
if
(
version_list
.
length
)
{
reply
.
_conflict_list
=
version_list
;
}
if
(
error_count
)
{
reply
.
_missing
=
true
;
}
return
reply
;
};
function
resolver
(
answer
)
{
last_answer
=
answer
;
if
(
last_good_answer
===
undefined
)
{
if
(
isDate
(
answer
.
data
.
modified
))
{
last_good_answer
=
answer
;
}
if
(
trace
===
undefined
)
{
trace
=
checksum
(
answer
);
doc
=
answer
;
}
else
{
if
(
isDate
(
answer
.
data
.
modified
))
{
if
(
new
Date
(
last_good_answer
.
data
.
modified
)
<
new
Date
(
answer
.
data
.
modified
))
{
last_good_answer
=
answer
;
if
(
trace
!==
checksum
(
answer
))
{
if
(
version
.
list
.
length
===
0
)
{
version_list
.
push
(
doc
);
}
version_list
.
push
(
answer
);
}
}
count
+=
1
;
if
(
count
===
length
)
{
return
resolve
(
last_good_answer
);
return
resolve
(
tag
(
doc
)
);
}
}
function
rejecter
(
answer
)
{
...
...
@@ -243,7 +295,7 @@
}
count
+=
1
;
if
(
count
===
length
)
{
return
resolve
(
last_good_answer
||
last_answer
);
return
resolve
(
tag
(
doc
)
);
}
}
function
notifier
(
index
)
{
...
...
@@ -254,6 +306,7 @@
});
};
}
for
(
index
=
0
;
index
<
length
;
index
+=
1
)
{
promise_list
[
index
].
then
(
resolver
,
rejecter
,
notifier
(
index
));
}
...
...
@@ -265,23 +318,6 @@
});
}
// /**
// * An Universal Unique ID generator
// *
// * @return {String} The new UUID.
// */
// function generateUuid() {
// function S4() {
// return ('0000' + Math.floor(
// Math.random() * 0x10000 /* 65536 */
// ).toString(16)).slice(-4);
// }
// return S4() + S4() + "-" +
// S4() + "-" +
// S4() + "-" +
// S4() + "-" +
// S4() + S4() + S4();
// }
function
ReplicateStorage
(
spec
)
{
if
(
!
Array
.
isArray
(
spec
.
storage_list
))
{
...
...
@@ -293,14 +329,6 @@
ReplicateStorage
.
prototype
.
post
=
function
(
command
,
metadata
,
option
)
{
var
promise_list
=
[],
index
,
length
=
this
.
_storage_list
.
length
;
// if (!isDate(metadata.modified)) {
// command.error(
// 409,
// "invalid 'modified' metadata",
// "The metadata 'modified' should be a valid date string or date object"
// );
// return;
// }
for
(
index
=
0
;
index
<
length
;
index
+=
1
)
{
promise_list
[
index
]
=
success
(
command
.
storage
(
this
.
_storage_list
[
index
]).
post
(
metadata
,
option
)
...
...
@@ -313,14 +341,6 @@
ReplicateStorage
.
prototype
.
put
=
function
(
command
,
metadata
,
option
)
{
var
promise_list
=
[],
index
,
length
=
this
.
_storage_list
.
length
;
// if (!isDate(metadata.modified)) {
// command.error(
// 409,
// "invalid 'modified' metadata",
// "The metadata 'modified' should be a valid date string or date object"
// );
// return;
// }
for
(
index
=
0
;
index
<
length
;
index
+=
1
)
{
promise_list
[
index
]
=
command
.
storage
(
this
.
_storage_list
[
index
]).
put
(
metadata
,
option
);
...
...
@@ -378,7 +398,7 @@
command
.
storage
(
this
.
_storage_list
[
index
]).
get
(
param
,
option
);
}
sequence
([
function
()
{
return
lastModified
(
promise_list
);
return
checkAndReturn
(
promise_list
);
},
[
command
.
success
,
command
.
error
]]);
};
...
...
@@ -401,105 +421,137 @@
promise_list
[
index
]
=
success
(
command
.
storage
(
this
.
_storage_list
[
index
]).
allDocs
(
option
));
}
sequence
([
function
()
{
return
all
(
promise_list
);
},
function
(
answers
)
{
// merge responses
var
i
,
j
,
k
,
found
,
rows
;
// browsing answers
for
(
i
=
0
;
i
<
answers
.
length
;
i
+=
1
)
{
if
(
answers
[
i
].
result
===
"
success
"
)
{
if
(
!
rows
)
{
rows
=
answers
[
i
].
data
.
rows
;
var
i
,
j
,
k
,
l
,
record
,
reply
,
rows
,
count
,
total
,
base
,
test
,
must_add
,
len
=
answers
.
length
;
// loop storage response
// NOTE: every storage may return different records!
for
(
i
=
0
;
i
<
len
;
i
+=
1
)
{
reply
=
answers
[
i
];
total
=
reply
.
data
.
total_rows
;
// loop records returned in response
for
(
j
=
0
;
j
<
total
;
j
+=
1
)
{
record
=
reply
.
data
.
rows
[
j
];
must_add
=
undefined
;
if
(
rows
===
undefined
)
{
rows
=
[
record
];
}
else
{
// browsing answer rows
for
(
j
=
0
;
j
<
answers
[
i
].
data
.
rows
.
length
;
j
+=
1
)
{
found
=
false
;
// browsing result rows
for
(
k
=
0
;
k
<
rows
.
length
;
k
+=
1
)
{
if
(
rows
[
k
].
id
===
answers
[
i
].
data
.
rows
[
j
].
id
)
{
found
=
true
;
break
;
count
=
rows
.
length
;
// loop records already in rows object
for
(
k
=
0
;
k
<
count
;
k
+=
1
)
{
base
=
rows
[
k
];
test
=
checksum
(
cleanClone
(
base
));
// record exists, test for coherence
if
(
base
.
id
===
record
.
id
)
{
must_add
=
undefined
;
if
(
test
!==
checksum
(
record
))
{
if
(
!
base
.
_conflict_list
)
{
base
.
_conflict_list
=
[
base
];
}
base
.
_conflict_list
.
push
(
record
);
}
// need to add record
}
else
{
must_add
=
true
;
}
if
(
!
found
)
{
rows
.
push
(
answers
[
i
].
data
.
rows
[
j
]);
}
// add missing records
if
(
must_add
)
{
// ok on first storage iteration (i), afterwards 404
if
(
i
>
0
)
{
record
.
_missing
=
true
;
}
rows
.
push
(
record
);
}
}
}
}
// if one storage returns rows = 0, make sure all records are labelled
if
(
total
===
0
)
{
rows
=
rows
||
[];
for
(
l
=
0
;
l
<
rows
.
length
;
l
+=
1
)
{
rows
[
l
].
_missing
=
true
;
}
}
return
{
"
data
"
:
{
"
total_rows
"
:
(
rows
||
[]).
length
,
"
rows
"
:
rows
||
[]}};
},
[
command
.
success
,
command
.
error
]]);
};
ReplicateStorage
.
prototype
.
check
=
function
(
command
,
param
,
option
)
{
var
promise_list
=
[],
index
,
length
=
this
.
_storage_list
.
length
;
for
(
index
=
0
;
index
<
length
;
index
+=
1
)
{
promise_list
[
index
]
=
success
(
command
.
storage
(
this
.
_storage_list
[
index
]).
check
(
param
,
option
)
);
}
sequence
([
function
()
{
return
all
(
promise_list
);
},
[
command
.
success
,
command
.
error
]]);
return
command
.
error
(
501
);
};
ReplicateStorage
.
prototype
.
repair
=
function
(
command
,
param
)
{
var
promise_list
=
[],
index
,
that
,
length
=
this
.
_storage_list
.
length
;
that
=
this
;
if
(
typeof
param
.
_id
!==
'
string
'
||
!
param
.
_id
)
{
command
.
success
();
return
;
}
for
(
index
=
0
;
index
<
length
;
index
+=
1
)
{
promise_list
[
index
]
=
success
(
command
.
storage
(
this
.
_storage_list
[
index
]).
get
(
param
));
}
sequence
([
function
()
{
return
all
(
promise_list
);
},
function
(
answers
)
{
var
i
,
list
=
[],
winner
=
null
;
for
(
i
=
0
;
i
<
answers
.
length
;
i
+=
1
)
{
if
(
answers
[
i
].
result
===
"
success
"
)
{
if
(
isDate
(
answers
[
i
].
data
.
modified
))
{
list
[
i
]
=
answers
[
i
].
data
;
if
(
winner
===
null
||
new
Date
(
winner
.
modified
)
<
new
Date
(
answers
[
i
].
data
.
modified
))
{
winner
=
answers
[
i
].
data
;
}
}
}
else
if
(
answers
[
i
].
status
===
404
)
{
list
[
i
]
=
0
;
}
}
for
(
i
=
0
;
i
<
list
.
length
;
i
+=
1
)
{
if
(
list
[
i
]
&&
new
Date
(
list
[
i
].
modified
)
<
new
Date
(
winner
.
modified
))
{
list
[
i
]
=
success
(
command
.
storage
(
that
.
_storage_list
[
i
]).
put
(
winner
));
}
else
if
(
list
[
i
]
===
0
)
{
list
[
i
]
=
dictUpdate
({},
winner
);
delete
list
[
i
].
_id
;
list
[
i
]
=
success
(
command
.
storage
(
that
.
_storage_list
[
i
]).
post
(
list
[
i
]));
}
}
list
=
list
.
reduce
(
function
(
previous
,
current
)
{
if
(
current
)
{
previous
.
push
(
current
);
}
return
previous
;
},
[]);
return
all
(
list
);
},
function
(
answers
)
{
var
i
;
for
(
i
=
0
;
i
<
answers
.
length
;
i
+=
1
)
{
if
(
answers
[
i
].
result
!==
"
success
"
)
{
return
command
.
error
(
409
);
}
}
command
.
success
();
}]);
console
.
log
(
"
repairing
"
)
console
.
log
(
command
)
console
.
log
(
param
)
// var promise_list = [], index, that, length = this._storage_list.length;
// that = this;
// if (typeof param._id !== 'string' || !param._id) {
// command.success();
// return;
// }
// for (index = 0; index < length; index += 1) {
// promise_list[index] =
// success(command.storage(this._storage_list[index]).get(param));
// }
// sequence([function () {
// return all(promise_list);
// }, function (answers) {
// var i, list = [], winner = null;
// for (i = 0; i < answers.length; i += 1) {
// if (answers[i].result === "success") {
// if (isDate(answers[i].data.modified)) {
// list[i] = answers[i].data;
// if (winner === null ||
// new Date(winner.modified) <
// new Date(answers[i].data.modified)) {
// winner = answers[i].data;
// }
// }
// } else if (answers[i].status === 404) {
// list[i] = 0;
// }
// }
// for (i = 0; i < list.length; i += 1) {
// if (list[i] && new Date(list[i].modified) < new Date(winner.modified)) {
// list[i] = success(command.storage(that._storage_list[i]).put(winner));
// } else if (list[i] === 0) {
// list[i] = dictUpdate({}, winner);
// delete list[i]._id;
// list[i] =
// success(command.storage(that._storage_list[i]).post(list[i]));
// }
// }
// list = list.reduce(function (previous, current) {
// if (current) {
// previous.push(current);
// }
// return previous;
// }, []);
// return all(list);
// }, function (answers) {
// var i;
// for (i = 0; i < answers.length; i += 1) {
// if (answers[i].result !== "success") {
// return command.error(409);
// }
// }
// command.success();
// }]);
};
addStorageFunction
(
'
replicate
'
,
ReplicateStorage
);
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment