Destructive vs Nondestructive Editing
For the purpose of this article, destructive editing refers to saving each action to disk, while nondestructive refers to having to click a button (i.e. SAVE) to write to disk.
Performance
Disk I/O operations are considerably resource intensive (especially for disk writers) and should be avoided when not imperative.
HDD/SSD Lifespan
The number of times that data can be written to a block on disk is limited. The more writes, the shorter the lifespan.
While it can be argued that the impact of a single extension on disk lifespan is infinitesimal, the disk lifespan consideration in software development is a worthy ethos. Modern devices nowadays run myriad of software and if all developers ignore the consideration, the accumulative impact would be quite considerable.
Furthermore, each disk write often involves many storage blocks (depending on the size of the data), and can results in subsequent disk writes. For example, a change in extension settings, may result in a change in browser settings, and subsequently a change in operating system settings.
Accident Prevention
There are occasions (e.g. in importing data) that due to user error (e.g. wrong data), extension bug, browser bug (also happens) and other reasons, data gets corrupted.
- With destructive editing, data may be lost which can be painful for users with many custom settings
- With nondestructive editing, user is able to check (and edit or discard) the result of the action before saving it to the disk for permanent storage
FIFO & Asynchronous Execution Order
While FIFO (first in, first out) is desired, the execution order of the asynchronous operations are not guaranteed. The issue is especially significant in disk write operations.
In addition to the discussion in Inconsistency: StorageArea callback order, I have personally experienced the issue in developing another extension (FIFO based rapid consecutive userscript storage writes in FireMonkey v2.68 resulted in data loss and had to be reverted in v2.70).
Consider the following examples:
Example 1
When saving a single simple value, absence of FIFO often does not cause any complications.
browser.storage.local.set({a: 10});
browser.storage.local.set({b: 20});
However, with complex values and overwriting, the final outcome is not guaranteed.
let pref = {a: 10, b: 20, c: 30};
browser.storage.local.set(pref);
pref = {a: 100, b: 200, c: 300};
browser.storage.local.set(pref);
Example 2
{
a: {x: 1, y: 2, z: 3},
b: {x: 4, y: 5, z: 6},
c: {x: 7, y: 8, z: 9},
}
browser.storage.local.set({a: {x: 10, y: 2, z: 3}});
browser.storage.local.set({a: {x: 100, y: 2, z: 3}});
Multithreading & Multiprocessing
Modern software have been multithreading & multiprocessing for years to improve performance. Firefox started multiprocessing with v57 in 2017.
Consider the following execution order analogy in a 2 thread set-up showing the reason asynchronous execution Order is not FIFO.
op1 -> thread 1
op2 -> thread 2
op3 -> queued for thread 1 (disk write)
op4 -> queued for thread 2 (disk write)
- If op1 completes before op2, op3 will start before op4
- If op1 completes after op2, op3 will start after op4
Practical Example
Consider having 10 proxies, each with their own configurations, all on port 3128 and you want to change their ports to 443, and then move the last proxy to the top.
It should be noted that in this case, each storage.local.set results in some event listeners firing in the background script, and changes to the extension operational data.
It also results in proxy.settings.set disk write in browser configuration.
In case of having Sync enabled, each disk write would also result in corresponding storage.sync.set disk write.
In case of global browser Sync option, changes in browser configuration results in additional disk writes.
{
a: {order: 1, port: 3128, ... },
b: {order: 2, port: 3128, ... },
c: {order: 3, port: 3128, ... },
d: {order: 4, port: 3128, ... },
e: {order: 5, port: 3128, ... },
f: {order: 6, port: 3128, ... },
g: {order: 7, port: 3128, ... },
h: {order: 8, port: 3128, ... },
i: {order: 9, port: 3128, ... },
j: {order: 10, port: 3128, ... },
}
Destructive Editing
-
Change Port
- Change port
a to 443 => browser.storage.local.set(data)
- Change port
b to 443 => browser.storage.local.set(data)
- Change port
c to 443 => browser.storage.local.set(data)
- Change port
d to 443 => browser.storage.local.set(data)
- Change port
e to 443 => browser.storage.local.set(data)
- Change port
f to 443 => browser.storage.local.set(data)
- Change port
g to 443 => browser.storage.local.set(data)
- Change port
h to 443 => browser.storage.local.set(data)
- Change port
i to 443 => browser.storage.local.set(data)
- Change port
j to 443 => browser.storage.local.set(data)
-
Move
- Move proxy from position 10 to 9 =>
browser.storage.local.set(data)
- Move proxy from position 9 to 8 =>
browser.storage.local.set(data)
- Move proxy from position 8 to 7 =>
browser.storage.local.set(data)
- Move proxy from position 7 to 6 =>
browser.storage.local.set(data)
- Move proxy from position 6 to 5 =>
browser.storage.local.set(data)
- Move proxy from position 5 to 4 =>
browser.storage.local.set(data)
- Move proxy from position 4 to 3 =>
browser.storage.local.set(data)
- Move proxy from position 3 to 2 =>
browser.storage.local.set(data)
- Move proxy from position 2 to 1 =>
browser.storage.local.set(data)
The mentioned process produces 19 disk writes which can become 57+ when considering secondary disk writes.
Due to the lack of guaranteed FIFO, the result of rapid consecutive disk writes is not guaranteed (e.g. in the above move example) .
Nondestructive Editing
-
Change Port
- Change port
a to 443
- Change port
b to 443
- Change port
c to 443
- Change port
d to 443
- Change port
e to 443
- Change port
f to 443
- Change port
g to 443
- Change port
h to 443
- Change port
i to 443
- Change port
j to 443
-
Move
- Move proxy from position 10 to 9
- Move proxy from position 9 to 8
- Move proxy from position 8 to 7
- Move proxy from position 7 to 6
- Move proxy from position 6 to 5
- Move proxy from position 5 to 4
- Move proxy from position 4 to 3
- Move proxy from position 3 to 2
- Move proxy from position 2 to 1
-
Save
- Click SAVE =>
browser.storage.local.set(data)
Destructive vs Nondestructive Editing
For the purpose of this article, destructive editing refers to saving each action to disk, while nondestructive refers to having to click a button (i.e. SAVE) to write to disk.
Performance
Disk I/O operations are considerably resource intensive (especially for disk writers) and should be avoided when not imperative.
HDD/SSD Lifespan
The number of times that data can be written to a block on disk is limited. The more writes, the shorter the lifespan.
While it can be argued that the impact of a single extension on disk lifespan is infinitesimal, the disk lifespan consideration in software development is a worthy ethos. Modern devices nowadays run myriad of software and if all developers ignore the consideration, the accumulative impact would be quite considerable.
Furthermore, each disk write often involves many storage blocks (depending on the size of the data), and can results in subsequent disk writes. For example, a change in extension settings, may result in a change in browser settings, and subsequently a change in operating system settings.
Accident Prevention
There are occasions (e.g. in importing data) that due to user error (e.g. wrong data), extension bug, browser bug (also happens) and other reasons, data gets corrupted.
FIFO & Asynchronous Execution Order
While FIFO (first in, first out) is desired, the execution order of the asynchronous operations are not guaranteed. The issue is especially significant in disk write operations.
In addition to the discussion in Inconsistency: StorageArea callback order, I have personally experienced the issue in developing another extension (FIFO based rapid consecutive userscript storage writes in FireMonkey v2.68 resulted in data loss and had to be reverted in v2.70).
Consider the following examples:
Example 1
When saving a single simple value, absence of FIFO often does not cause any complications.
However, with complex values and overwriting, the final outcome is not guaranteed.
Example 2
Multithreading & Multiprocessing
Modern software have been multithreading & multiprocessing for years to improve performance. Firefox started multiprocessing with v57 in 2017.
Consider the following execution order analogy in a 2 thread set-up showing the reason asynchronous execution Order is not FIFO.
op1 -> thread 1
op2 -> thread 2
op3 -> queued for thread 1 (disk write)
op4 -> queued for thread 2 (disk write)
Practical Example
Consider having 10 proxies, each with their own configurations, all on port 3128 and you want to change their ports to 443, and then move the last proxy to the top.
It should be noted that in this case, each
storage.local.setresults in some event listeners firing in the background script, and changes to the extension operational data.It also results in
proxy.settings.setdisk write in browser configuration.In case of having Sync enabled, each disk write would also result in corresponding
storage.sync.setdisk write.In case of global browser Sync option, changes in browser configuration results in additional disk writes.
Destructive Editing
Change Port
ato 443 =>browser.storage.local.set(data)bto 443 =>browser.storage.local.set(data)cto 443 =>browser.storage.local.set(data)dto 443 =>browser.storage.local.set(data)eto 443 =>browser.storage.local.set(data)fto 443 =>browser.storage.local.set(data)gto 443 =>browser.storage.local.set(data)hto 443 =>browser.storage.local.set(data)ito 443 =>browser.storage.local.set(data)jto 443 =>browser.storage.local.set(data)Move
browser.storage.local.set(data)browser.storage.local.set(data)browser.storage.local.set(data)browser.storage.local.set(data)browser.storage.local.set(data)browser.storage.local.set(data)browser.storage.local.set(data)browser.storage.local.set(data)browser.storage.local.set(data)The mentioned process produces 19 disk writes which can become 57+ when considering secondary disk writes.
Due to the lack of guaranteed FIFO, the result of rapid consecutive disk writes is not guaranteed (e.g. in the above move example) .
Nondestructive Editing
Change Port
ato 443bto 443cto 443dto 443eto 443fto 443gto 443hto 443ito 443jto 443Move
Save
browser.storage.local.set(data)