Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Destructive vs Nondestructive Editing #25

Open
erosman opened this issue Sep 12, 2023 · 0 comments
Open

Destructive vs Nondestructive Editing #25

erosman opened this issue Sep 12, 2023 · 0 comments

Comments

@erosman
Copy link
Collaborator

erosman commented Sep 12, 2023

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

{
  a: 1,
  b: 2,
  c: 3
}

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)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

1 participant