@powersync/common 1.50.0 → 1.51.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/bundle.cjs +128 -37
- package/dist/bundle.cjs.map +1 -1
- package/dist/bundle.mjs +128 -38
- package/dist/bundle.mjs.map +1 -1
- package/dist/bundle.node.cjs +128 -37
- package/dist/bundle.node.cjs.map +1 -1
- package/dist/bundle.node.mjs +128 -38
- package/dist/bundle.node.mjs.map +1 -1
- package/dist/index.d.cts +33 -4
- package/lib/utils/mutex.d.ts +32 -3
- package/lib/utils/mutex.js +85 -36
- package/lib/utils/mutex.js.map +1 -1
- package/lib/utils/queue.d.ts +16 -0
- package/lib/utils/queue.js +42 -0
- package/lib/utils/queue.js.map +1 -0
- package/package.json +1 -1
- package/src/utils/mutex.ts +111 -48
- package/src/utils/queue.ts +48 -0
package/dist/bundle.node.cjs
CHANGED
|
@@ -785,19 +785,69 @@ class SyncingService {
|
|
|
785
785
|
}
|
|
786
786
|
|
|
787
787
|
/**
|
|
788
|
-
*
|
|
788
|
+
* A simple fixed-capacity queue implementation.
|
|
789
|
+
*
|
|
790
|
+
* Unlike a naive queue implemented by `array.push()` and `array.shift()`, this avoids moving array elements around
|
|
791
|
+
* and is `O(1)` for {@link addLast} and {@link removeFirst}.
|
|
792
|
+
*/
|
|
793
|
+
class Queue {
|
|
794
|
+
table;
|
|
795
|
+
// Index of the first element in the table.
|
|
796
|
+
head;
|
|
797
|
+
// Amount of items currently in the queue.
|
|
798
|
+
_length;
|
|
799
|
+
constructor(initialItems) {
|
|
800
|
+
this.table = [...initialItems];
|
|
801
|
+
this.head = 0;
|
|
802
|
+
this._length = this.table.length;
|
|
803
|
+
}
|
|
804
|
+
get isEmpty() {
|
|
805
|
+
return this.length == 0;
|
|
806
|
+
}
|
|
807
|
+
get length() {
|
|
808
|
+
return this._length;
|
|
809
|
+
}
|
|
810
|
+
removeFirst() {
|
|
811
|
+
if (this.isEmpty) {
|
|
812
|
+
throw new Error('Queue is empty');
|
|
813
|
+
}
|
|
814
|
+
const result = this.table[this.head];
|
|
815
|
+
this._length--;
|
|
816
|
+
this.table[this.head] = undefined;
|
|
817
|
+
this.head = (this.head + 1) % this.table.length;
|
|
818
|
+
return result;
|
|
819
|
+
}
|
|
820
|
+
addLast(element) {
|
|
821
|
+
if (this.length == this.table.length) {
|
|
822
|
+
throw new Error('Queue is full');
|
|
823
|
+
}
|
|
824
|
+
this.table[(this.head + this._length) % this.table.length] = element;
|
|
825
|
+
this._length++;
|
|
826
|
+
}
|
|
827
|
+
}
|
|
828
|
+
|
|
829
|
+
/**
|
|
830
|
+
* An asynchronous semaphore implementation with associated items per lease.
|
|
789
831
|
*
|
|
790
832
|
* @internal This class is meant to be used in PowerSync SDKs only, and is not part of the public API.
|
|
791
833
|
*/
|
|
792
|
-
class
|
|
793
|
-
|
|
834
|
+
class Semaphore {
|
|
835
|
+
// Available items that are not currently assigned to a waiter.
|
|
836
|
+
available;
|
|
837
|
+
size;
|
|
794
838
|
// Linked list of waiters. We don't expect the wait list to become particularly large, and this allows removing
|
|
795
839
|
// aborted waiters from the middle of the list efficiently.
|
|
796
840
|
firstWaiter;
|
|
797
841
|
lastWaiter;
|
|
798
|
-
|
|
842
|
+
constructor(elements) {
|
|
843
|
+
this.available = new Queue(elements);
|
|
844
|
+
this.size = this.available.length;
|
|
845
|
+
}
|
|
846
|
+
addWaiter(requestedItems, onAcquire) {
|
|
799
847
|
const node = {
|
|
800
848
|
isActive: true,
|
|
849
|
+
acquiredItems: [],
|
|
850
|
+
remainingItems: requestedItems,
|
|
801
851
|
onAcquire,
|
|
802
852
|
prev: this.lastWaiter
|
|
803
853
|
};
|
|
@@ -823,52 +873,92 @@ class Mutex {
|
|
|
823
873
|
if (waiter == this.lastWaiter)
|
|
824
874
|
this.lastWaiter = prev;
|
|
825
875
|
}
|
|
826
|
-
|
|
876
|
+
requestPermits(amount, abort) {
|
|
877
|
+
if (amount <= 0 || amount > this.size) {
|
|
878
|
+
throw new Error(`Invalid amount of items requested (${amount}), must be between 1 and ${this.size}`);
|
|
879
|
+
}
|
|
827
880
|
return new Promise((resolve, reject) => {
|
|
828
881
|
function rejectAborted() {
|
|
829
|
-
reject(abort?.reason ?? new Error('
|
|
882
|
+
reject(abort?.reason ?? new Error('Semaphore acquire aborted'));
|
|
830
883
|
}
|
|
831
884
|
if (abort?.aborted) {
|
|
832
885
|
return rejectAborted();
|
|
833
886
|
}
|
|
834
|
-
let
|
|
887
|
+
let waiter;
|
|
835
888
|
const markCompleted = () => {
|
|
836
|
-
|
|
837
|
-
|
|
838
|
-
|
|
839
|
-
|
|
840
|
-
|
|
841
|
-
|
|
842
|
-
|
|
843
|
-
|
|
889
|
+
const items = waiter.acquiredItems;
|
|
890
|
+
waiter.acquiredItems = []; // Avoid releasing items twice.
|
|
891
|
+
for (const element of items) {
|
|
892
|
+
// Give to next waiter, if possible.
|
|
893
|
+
const nextWaiter = this.firstWaiter;
|
|
894
|
+
if (nextWaiter) {
|
|
895
|
+
nextWaiter.acquiredItems.push(element);
|
|
896
|
+
nextWaiter.remainingItems--;
|
|
897
|
+
if (nextWaiter.remainingItems == 0) {
|
|
898
|
+
nextWaiter.onAcquire();
|
|
899
|
+
}
|
|
900
|
+
}
|
|
901
|
+
else {
|
|
902
|
+
// No pending waiter, return lease into pool.
|
|
903
|
+
this.available.addLast(element);
|
|
904
|
+
}
|
|
844
905
|
}
|
|
845
|
-
|
|
846
|
-
|
|
906
|
+
};
|
|
907
|
+
const onAbort = () => {
|
|
908
|
+
abort?.removeEventListener('abort', onAbort);
|
|
909
|
+
if (waiter.isActive) {
|
|
910
|
+
this.deactivateWaiter(waiter);
|
|
911
|
+
rejectAborted();
|
|
847
912
|
}
|
|
848
913
|
};
|
|
849
|
-
|
|
850
|
-
this.
|
|
851
|
-
|
|
852
|
-
|
|
914
|
+
const resolvePromise = () => {
|
|
915
|
+
this.deactivateWaiter(waiter);
|
|
916
|
+
abort?.removeEventListener('abort', onAbort);
|
|
917
|
+
const items = waiter.acquiredItems;
|
|
918
|
+
resolve({ items, release: markCompleted });
|
|
919
|
+
};
|
|
920
|
+
waiter = this.addWaiter(amount, resolvePromise);
|
|
921
|
+
// If there are items in the pool that haven't been assigned, we can pull them into this waiter. Note that this is
|
|
922
|
+
// only the case if we're the first waiter (otherwise, items would have been assigned to an earlier waiter).
|
|
923
|
+
while (!this.available.isEmpty && waiter.remainingItems > 0) {
|
|
924
|
+
waiter.acquiredItems.push(this.available.removeFirst());
|
|
925
|
+
waiter.remainingItems--;
|
|
853
926
|
}
|
|
854
|
-
|
|
855
|
-
|
|
856
|
-
const onAbort = () => {
|
|
857
|
-
abort?.removeEventListener('abort', onAbort);
|
|
858
|
-
if (node.isActive) {
|
|
859
|
-
this.deactivateWaiter(node);
|
|
860
|
-
rejectAborted();
|
|
861
|
-
}
|
|
862
|
-
};
|
|
863
|
-
node = this.addWaiter(() => {
|
|
864
|
-
abort?.removeEventListener('abort', onAbort);
|
|
865
|
-
holdsMutex = true;
|
|
866
|
-
resolve(markCompleted);
|
|
867
|
-
});
|
|
868
|
-
abort?.addEventListener('abort', onAbort);
|
|
927
|
+
if (waiter.remainingItems == 0) {
|
|
928
|
+
return resolvePromise();
|
|
869
929
|
}
|
|
930
|
+
abort?.addEventListener('abort', onAbort);
|
|
870
931
|
});
|
|
871
932
|
}
|
|
933
|
+
/**
|
|
934
|
+
* Requests a single item from the pool.
|
|
935
|
+
*
|
|
936
|
+
* The returned `release` callback must be invoked to return the item into the pool.
|
|
937
|
+
*/
|
|
938
|
+
async requestOne(abort) {
|
|
939
|
+
const { items, release } = await this.requestPermits(1, abort);
|
|
940
|
+
return { release, item: items[0] };
|
|
941
|
+
}
|
|
942
|
+
/**
|
|
943
|
+
* Requests access to all items from the pool.
|
|
944
|
+
*
|
|
945
|
+
* The returned `release` callback must be invoked to return items into the pool.
|
|
946
|
+
*/
|
|
947
|
+
requestAll(abort) {
|
|
948
|
+
return this.requestPermits(this.size, abort);
|
|
949
|
+
}
|
|
950
|
+
}
|
|
951
|
+
/**
|
|
952
|
+
* An asynchronous mutex implementation.
|
|
953
|
+
*
|
|
954
|
+
* @internal This class is meant to be used in PowerSync SDKs only, and is not part of the public API.
|
|
955
|
+
*/
|
|
956
|
+
class Mutex {
|
|
957
|
+
inner = new Semaphore([null]);
|
|
958
|
+
async acquire(abort) {
|
|
959
|
+
const { release } = await this.inner.requestOne(abort);
|
|
960
|
+
return release;
|
|
961
|
+
}
|
|
872
962
|
async runExclusive(fn, abort) {
|
|
873
963
|
const returnMutex = await this.acquire(abort);
|
|
874
964
|
try {
|
|
@@ -8141,7 +8231,7 @@ function requireDist () {
|
|
|
8141
8231
|
|
|
8142
8232
|
var distExports = requireDist();
|
|
8143
8233
|
|
|
8144
|
-
var version = "1.
|
|
8234
|
+
var version = "1.51.0";
|
|
8145
8235
|
var PACKAGE = {
|
|
8146
8236
|
version: version};
|
|
8147
8237
|
|
|
@@ -12133,6 +12223,7 @@ exports.OnChangeQueryProcessor = OnChangeQueryProcessor;
|
|
|
12133
12223
|
exports.OpType = OpType;
|
|
12134
12224
|
exports.OplogEntry = OplogEntry;
|
|
12135
12225
|
exports.Schema = Schema;
|
|
12226
|
+
exports.Semaphore = Semaphore;
|
|
12136
12227
|
exports.SqliteBucketStorage = SqliteBucketStorage;
|
|
12137
12228
|
exports.SyncDataBatch = SyncDataBatch;
|
|
12138
12229
|
exports.SyncDataBucket = SyncDataBucket;
|