@budibase/backend-core 2.21.4 → 2.21.6

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.
Files changed (72) hide show
  1. package/dist/index.js +249 -32
  2. package/dist/index.js.map +4 -4
  3. package/dist/index.js.meta.json +1 -1
  4. package/dist/package.json +4 -4
  5. package/dist/plugins.js.meta.json +1 -1
  6. package/dist/src/cache/base/index.d.ts +32 -2
  7. package/dist/src/cache/base/index.js +60 -1
  8. package/dist/src/cache/base/index.js.map +1 -1
  9. package/dist/src/cache/docWritethrough.d.ts +21 -0
  10. package/dist/src/cache/docWritethrough.js +107 -0
  11. package/dist/src/cache/docWritethrough.js.map +1 -0
  12. package/dist/src/cache/generic.d.ts +2 -2
  13. package/dist/src/cache/generic.js.map +1 -1
  14. package/dist/src/cache/index.d.ts +1 -0
  15. package/dist/src/cache/index.js +2 -1
  16. package/dist/src/cache/index.js.map +1 -1
  17. package/dist/src/configs/configs.d.ts +1 -1
  18. package/dist/src/constants/db.d.ts +3 -0
  19. package/dist/src/constants/db.js +3 -0
  20. package/dist/src/constants/db.js.map +1 -1
  21. package/dist/src/context/mainContext.d.ts +1 -0
  22. package/dist/src/context/mainContext.js +13 -1
  23. package/dist/src/context/mainContext.js.map +1 -1
  24. package/dist/src/db/couch/DatabaseImpl.d.ts +3 -1
  25. package/dist/src/db/couch/DatabaseImpl.js +18 -1
  26. package/dist/src/db/couch/DatabaseImpl.js.map +1 -1
  27. package/dist/src/db/instrumentation.d.ts +1 -1
  28. package/dist/src/db/instrumentation.js +5 -2
  29. package/dist/src/db/instrumentation.js.map +1 -1
  30. package/dist/src/environment.d.ts +1 -0
  31. package/dist/src/environment.js +1 -1
  32. package/dist/src/environment.js.map +1 -1
  33. package/dist/src/events/analytics.d.ts +1 -1
  34. package/dist/src/index.d.ts +1 -0
  35. package/dist/src/queue/constants.d.ts +2 -1
  36. package/dist/src/queue/constants.js +1 -0
  37. package/dist/src/queue/constants.js.map +1 -1
  38. package/dist/src/queue/inMemoryQueue.d.ts +23 -13
  39. package/dist/src/queue/inMemoryQueue.js +83 -30
  40. package/dist/src/queue/inMemoryQueue.js.map +1 -1
  41. package/dist/src/queue/listeners.js +2 -0
  42. package/dist/src/queue/listeners.js.map +1 -1
  43. package/dist/src/queue/queue.d.ts +1 -0
  44. package/dist/src/queue/queue.js.map +1 -1
  45. package/dist/src/redis/init.d.ts +1 -0
  46. package/dist/src/redis/init.js +12 -2
  47. package/dist/src/redis/init.js.map +1 -1
  48. package/dist/src/redis/redis.d.ts +1 -0
  49. package/dist/src/redis/redis.js +6 -0
  50. package/dist/src/redis/redis.js.map +1 -1
  51. package/dist/src/redis/utils.d.ts +2 -1
  52. package/dist/src/redis/utils.js +1 -0
  53. package/dist/src/redis/utils.js.map +1 -1
  54. package/package.json +4 -4
  55. package/src/cache/base/index.ts +62 -4
  56. package/src/cache/docWritethrough.ts +97 -0
  57. package/src/cache/generic.ts +3 -2
  58. package/src/cache/index.ts +1 -0
  59. package/src/cache/tests/docWritethrough.spec.ts +293 -0
  60. package/src/constants/db.ts +3 -0
  61. package/src/context/mainContext.ts +11 -0
  62. package/src/db/couch/DatabaseImpl.ts +18 -1
  63. package/src/db/instrumentation.ts +5 -2
  64. package/src/db/tests/DatabaseImpl.spec.ts +55 -0
  65. package/src/environment.ts +1 -0
  66. package/src/queue/constants.ts +1 -0
  67. package/src/queue/inMemoryQueue.ts +79 -24
  68. package/src/queue/listeners.ts +2 -0
  69. package/src/queue/queue.ts +2 -0
  70. package/src/redis/init.ts +12 -1
  71. package/src/redis/redis.ts +5 -0
  72. package/src/redis/utils.ts +1 -0
@@ -1,5 +1,14 @@
1
1
  import events from "events"
2
- import { timeout } from "../utils"
2
+ import { newid, timeout } from "../utils"
3
+ import { Queue, QueueOptions, JobOptions } from "./queue"
4
+
5
+ interface JobMessage {
6
+ id: string
7
+ timestamp: number
8
+ queue: string
9
+ data: any
10
+ opts?: JobOptions
11
+ }
3
12
 
4
13
  /**
5
14
  * Bull works with a Job wrapper around all messages that contains a lot more information about
@@ -10,12 +19,13 @@ import { timeout } from "../utils"
10
19
  * @returns A new job which can now be put onto the queue, this is mostly an
11
20
  * internal structure so that an in memory queue can be easily swapped for a Bull queue.
12
21
  */
13
- function newJob(queue: string, message: any) {
22
+ function newJob(queue: string, message: any, opts?: JobOptions): JobMessage {
14
23
  return {
24
+ id: newid(),
15
25
  timestamp: Date.now(),
16
26
  queue: queue,
17
27
  data: message,
18
- opts: {},
28
+ opts,
19
29
  }
20
30
  }
21
31
 
@@ -24,26 +34,29 @@ function newJob(queue: string, message: any) {
24
34
  * It is relatively simple, using an event emitter internally to register when messages are available
25
35
  * to the consumers - in can support many inputs and many consumers.
26
36
  */
27
- class InMemoryQueue {
37
+ class InMemoryQueue implements Partial<Queue> {
28
38
  _name: string
29
- _opts?: any
30
- _messages: any[]
39
+ _opts?: QueueOptions
40
+ _messages: JobMessage[]
41
+ _queuedJobIds: Set<string>
31
42
  _emitter: EventEmitter
32
43
  _runCount: number
33
44
  _addCount: number
45
+
34
46
  /**
35
47
  * The constructor the queue, exactly the same as that of Bulls.
36
48
  * @param name The name of the queue which is being configured.
37
49
  * @param opts This is not used by the in memory queue as there is no real use
38
50
  * case when in memory, but is the same API as Bull
39
51
  */
40
- constructor(name: string, opts?: any) {
52
+ constructor(name: string, opts?: QueueOptions) {
41
53
  this._name = name
42
54
  this._opts = opts
43
55
  this._messages = []
44
56
  this._emitter = new events.EventEmitter()
45
57
  this._runCount = 0
46
58
  this._addCount = 0
59
+ this._queuedJobIds = new Set<string>()
47
60
  }
48
61
 
49
62
  /**
@@ -55,22 +68,42 @@ class InMemoryQueue {
55
68
  * note this is incredibly limited compared to Bull as in reality the Job would contain
56
69
  * a lot more information about the queue and current status of Bull cluster.
57
70
  */
58
- process(func: any) {
71
+ async process(func: any) {
59
72
  this._emitter.on("message", async () => {
60
73
  if (this._messages.length <= 0) {
61
74
  return
62
75
  }
63
76
  let msg = this._messages.shift()
77
+
64
78
  let resp = func(msg)
79
+
80
+ async function retryFunc(fnc: any) {
81
+ try {
82
+ await fnc
83
+ } catch (e: any) {
84
+ await new Promise<void>(r => setTimeout(() => r(), 50))
85
+
86
+ await retryFunc(func(msg))
87
+ }
88
+ }
89
+
65
90
  if (resp.then != null) {
66
- await resp
91
+ try {
92
+ await retryFunc(resp)
93
+ } catch (e: any) {
94
+ console.error(e)
95
+ }
67
96
  }
68
97
  this._runCount++
98
+ const jobId = msg?.opts?.jobId?.toString()
99
+ if (jobId && msg?.opts?.removeOnComplete) {
100
+ this._queuedJobIds.delete(jobId)
101
+ }
69
102
  })
70
103
  }
71
104
 
72
105
  async isReady() {
73
- return true
106
+ return this as any
74
107
  }
75
108
 
76
109
  // simply puts a message to the queue and emits to the queue for processing
@@ -83,27 +116,45 @@ class InMemoryQueue {
83
116
  * @param repeat serves no purpose for the import queue.
84
117
  */
85
118
  // eslint-disable-next-line no-unused-vars
86
- add(msg: any, repeat: boolean) {
87
- if (typeof msg !== "object") {
119
+ async add(data: any, opts?: JobOptions) {
120
+ const jobId = opts?.jobId?.toString()
121
+ if (jobId && this._queuedJobIds.has(jobId)) {
122
+ console.log(`Ignoring already queued job ${jobId}`)
123
+ return
124
+ }
125
+
126
+ if (typeof data !== "object") {
88
127
  throw "Queue only supports carrying JSON."
89
128
  }
90
- this._messages.push(newJob(this._name, msg))
91
- this._addCount++
92
- this._emitter.emit("message")
129
+ if (jobId) {
130
+ this._queuedJobIds.add(jobId)
131
+ }
132
+
133
+ const pushMessage = () => {
134
+ this._messages.push(newJob(this._name, data, opts))
135
+ this._addCount++
136
+ this._emitter.emit("message")
137
+ }
138
+
139
+ const delay = opts?.delay
140
+ if (delay) {
141
+ setTimeout(pushMessage, delay)
142
+ } else {
143
+ pushMessage()
144
+ }
145
+ return {} as any
93
146
  }
94
147
 
95
148
  /**
96
149
  * replicating the close function from bull, which waits for jobs to finish.
97
150
  */
98
- async close() {
99
- return []
100
- }
151
+ async close() {}
101
152
 
102
153
  /**
103
154
  * This removes a cron which has been implemented, this is part of Bull API.
104
155
  * @param cronJobId The cron which is to be removed.
105
156
  */
106
- removeRepeatableByKey(cronJobId: string) {
157
+ async removeRepeatableByKey(cronJobId: string) {
107
158
  // TODO: implement for testing
108
159
  console.log(cronJobId)
109
160
  }
@@ -111,12 +162,12 @@ class InMemoryQueue {
111
162
  /**
112
163
  * Implemented for tests
113
164
  */
114
- getRepeatableJobs() {
165
+ async getRepeatableJobs() {
115
166
  return []
116
167
  }
117
168
 
118
169
  // eslint-disable-next-line no-unused-vars
119
- removeJobs(pattern: string) {
170
+ async removeJobs(pattern: string) {
120
171
  // no-op
121
172
  }
122
173
 
@@ -128,18 +179,22 @@ class InMemoryQueue {
128
179
  }
129
180
 
130
181
  async getJob() {
131
- return {}
182
+ return null
132
183
  }
133
184
 
134
185
  on() {
135
186
  // do nothing
136
- return this
187
+ return this as any
137
188
  }
138
189
 
139
190
  async waitForCompletion() {
140
191
  do {
141
192
  await timeout(50)
142
- } while (this._addCount < this._runCount)
193
+ } while (this.hasRunningJobs())
194
+ }
195
+
196
+ hasRunningJobs() {
197
+ return this._addCount > this._runCount
143
198
  }
144
199
  }
145
200
 
@@ -88,6 +88,7 @@ enum QueueEventType {
88
88
  AUDIT_LOG_EVENT = "audit-log-event",
89
89
  SYSTEM_EVENT = "system-event",
90
90
  APP_MIGRATION = "app-migration",
91
+ DOC_WRITETHROUGH = "doc-writethrough",
91
92
  }
92
93
 
93
94
  const EventTypeMap: { [key in JobQueue]: QueueEventType } = {
@@ -96,6 +97,7 @@ const EventTypeMap: { [key in JobQueue]: QueueEventType } = {
96
97
  [JobQueue.AUDIT_LOG]: QueueEventType.AUDIT_LOG_EVENT,
97
98
  [JobQueue.SYSTEM_EVENT_QUEUE]: QueueEventType.SYSTEM_EVENT,
98
99
  [JobQueue.APP_MIGRATION]: QueueEventType.APP_MIGRATION,
100
+ [JobQueue.DOC_WRITETHROUGH_QUEUE]: QueueEventType.DOC_WRITETHROUGH,
99
101
  }
100
102
 
101
103
  function logging(queue: Queue, jobQueue: JobQueue) {
@@ -7,6 +7,8 @@ import { addListeners, StalledFn } from "./listeners"
7
7
  import { Duration } from "../utils"
8
8
  import * as timers from "../timers"
9
9
 
10
+ export { QueueOptions, Queue, JobOptions } from "bull"
11
+
10
12
  // the queue lock is held for 5 minutes
11
13
  const QUEUE_LOCK_MS = Duration.fromMinutes(5).toMs()
12
14
  // queue lock is refreshed every 30 seconds
package/src/redis/init.ts CHANGED
@@ -9,7 +9,8 @@ let userClient: Client,
9
9
  lockClient: Client,
10
10
  socketClient: Client,
11
11
  inviteClient: Client,
12
- passwordResetClient: Client
12
+ passwordResetClient: Client,
13
+ docWritethroughClient: Client
13
14
 
14
15
  export async function init() {
15
16
  userClient = await new Client(utils.Databases.USER_CACHE).init()
@@ -24,6 +25,9 @@ export async function init() {
24
25
  utils.Databases.SOCKET_IO,
25
26
  utils.SelectableDatabase.SOCKET_IO
26
27
  ).init()
28
+ docWritethroughClient = await new Client(
29
+ utils.Databases.DOC_WRITE_THROUGH
30
+ ).init()
27
31
  }
28
32
 
29
33
  export async function shutdown() {
@@ -104,3 +108,10 @@ export async function getPasswordResetClient() {
104
108
  }
105
109
  return passwordResetClient
106
110
  }
111
+
112
+ export async function getDocWritethroughClient() {
113
+ if (!writethroughClient) {
114
+ await init()
115
+ }
116
+ return writethroughClient
117
+ }
@@ -320,6 +320,11 @@ class RedisWrapper {
320
320
  await this.getClient().del(addDbPrefix(db, key))
321
321
  }
322
322
 
323
+ async bulkDelete(keys: string[]) {
324
+ const db = this._db
325
+ await this.getClient().del(keys.map(key => addDbPrefix(db, key)))
326
+ }
327
+
323
328
  async clear() {
324
329
  let items = await this.scan()
325
330
  await Promise.all(items.map((obj: any) => this.delete(obj.key)))
@@ -30,6 +30,7 @@ export enum Databases {
30
30
  LOCKS = "locks",
31
31
  SOCKET_IO = "socket_io",
32
32
  BPM_EVENTS = "bpmEvents",
33
+ DOC_WRITE_THROUGH = "docWriteThrough",
33
34
  }
34
35
 
35
36
  /**