Add some sort of caching for config from DB settings #8
Open
opened 2025-02-16 13:52:21 -06:00 by lance
·
7 comments
No Branch/Tag specified
master
custorders
unfi-invoice-parser-tweak
v0.4
v0.21.1
v0.21.0
v0.20.6
v0.20.5
v0.20.4
v0.20.3
v0.20.2
v0.20.1
v0.20.0
v0.19.2
v0.19.1
v0.19.0
v0.18.12
v0.18.11
v0.18.10
v0.18.9
v0.18.8
v0.18.7
v0.18.6
v0.18.5
v0.18.4
v0.18.3
v0.18.2
v0.18.1
v0.18.0
v0.17.11
v0.17.10
v0.17.9
v0.17.8
v0.17.7
v0.17.6
v0.17.5
v0.17.4
v0.17.3
v0.17.2
v0.17.1
v0.17.0
v0.16.1
v0.16.0
v0.15.0
v0.14.8
v0.14.7
v0.14.6
v0.14.5
v0.14.4
v0.14.3
v0.14.2
v0.14.1
v0.14.0
v0.13.5
v0.13.4
v0.13.3
v0.13.2
v0.13.1
v0.13.0
v0.12.9
v0.12.8
v0.12.7
v0.12.6
v0.12.5
v0.12.4
v0.12.3
v0.12.2
v0.12.1
v0.12.0
v0.11.14
v0.11.13
v0.11.12
v0.11.11
v0.11.10
v0.11.9
v0.11.8
v0.11.7
v0.11.6
v0.11.5
v0.11.4
v0.11.3
v0.11.2
v0.11.1
v0.11.0
v0.10.88
v0.10.87
v0.10.86
v0.10.85
v0.10.84
v0.10.83
v0.10.82
v0.10.81
v0.10.80
v0.10.79
v0.10.78
v0.10.77
v0.10.76
v0.10.75
v0.10.74
v0.10.73
v0.10.72
v0.10.71
v0.10.70
v0.10.69
v0.10.68
v0.10.67
v0.10.66
v0.10.65
v0.10.64
v0.10.63
v0.10.62
v0.10.61
v0.10.60
v0.10.59
v0.10.58
v0.10.57
v0.10.56
v0.10.55
v0.10.54
v0.10.53
v0.10.52
v0.10.51
v0.10.50
v0.10.49
v0.10.48
v0.10.47
v0.10.46
v0.10.45
v0.10.44
v0.10.43
v0.10.42
v0.10.41
v0.10.40
v0.10.39
v0.10.38
v0.10.37
v0.10.36
v0.10.35
v0.10.34
v0.10.33
v0.10.32
v0.10.31
v0.10.30
v0.10.29
v0.10.28
v0.10.27
v0.10.26
v0.10.25
v0.10.24
v0.10.23
v0.10.22
v0.10.21
v0.10.20
v0.10.19
v0.10.18
v0.10.17
v0.10.16
v0.10.15
v0.10.14
v0.10.13
v0.10.12
v0.10.11
v0.10.10
v0.10.9
v0.10.8
v0.10.7
v0.10.6
v0.10.5
v0.10.4
v0.10.3
v0.10.2
v0.10.1
v0.10.0
v0.9.348
v0.9.347
v0.9.346
v0.9.345
v0.9.344
v0.9.343
v0.9.342
v0.9.341
v0.9.340
v0.9.339
v0.9.338
v0.9.337
v0.9.336
v0.9.335
v0.9.334
v0.9.333
v0.9.332
v0.9.331
v0.9.330
v0.9.329
v0.9.328
v0.9.327
v0.9.326
v0.9.325
v0.9.324
v0.9.323
v0.9.322
v0.9.321
v0.9.320
v0.9.319
v0.9.318
v0.9.317
v0.9.316
v0.9.315
v0.9.314
v0.9.313
v0.9.312
v0.9.311
v0.9.310
v0.9.309
v0.9.308
v0.9.307
v0.9.306
v0.9.305
v0.9.304
v0.9.303
v0.9.302
v0.9.301
v0.9.300
v0.9.299
v0.9.298
v0.9.297
v0.9.296
v0.9.295
v0.9.294
v0.9.293
v0.9.292
v0.9.291
v0.9.290
v0.9.289
v0.9.288
v0.9.287
v0.9.286
v0.9.285
v0.9.284
v0.9.283
v0.9.282
v0.9.281
v0.9.280
v0.9.279
v0.9.278
v0.9.277
v0.9.276
v0.9.275
v0.9.274
v0.9.273
v0.9.272
v0.9.271
v0.9.270
v0.9.269
v0.9.268
v0.9.267
v0.9.266
v0.9.265
v0.9.264
v0.9.263
v0.9.262
v0.9.261
v0.9.260
v0.9.259
v0.9.258
v0.9.257
v0.9.256
v0.9.255
v0.9.254
v0.9.253
v0.9.252
v0.9.251
v0.9.250
v0.9.249
v0.9.248
v0.9.247
v0.9.246
v0.9.245
v0.9.244
v0.9.243
v0.9.242
v0.9.241
v0.9.240
v0.9.239
v0.9.238
v0.9.237
v0.9.236
v0.9.235
v0.9.234
v0.9.233
v0.9.232
v0.9.231
v0.9.230
v0.9.229
v0.9.228
v0.9.227
v0.9.226
v0.9.225
v0.9.224
v0.9.223
v0.9.222
v0.9.221
v0.9.220
v0.9.219
v0.9.218
v0.9.217
v0.9.216
v0.9.215
v0.9.214
v0.9.213
v0.9.212
v0.9.211
v0.9.210
v0.9.209
v0.9.208
v0.9.207
v0.9.206
v0.9.205
v0.9.204
v0.9.203
v0.9.202
v0.9.201
v0.9.200
v0.9.199
v0.9.198
v0.9.197
v0.9.196
v0.9.195
v0.9.194
v0.9.193
v0.9.192
v0.9.191
v0.9.190
v0.9.189
v0.9.188
v0.9.187
v0.9.186
v0.9.185
v0.9.184
v0.9.183
v0.9.182
v0.9.181
v0.9.180
v0.9.179
v0.9.178
v0.9.177
v0.9.176
v0.9.175
v0.9.174
v0.9.173
v0.9.172
v0.9.171
v0.9.170
v0.9.169
v0.9.168
v0.9.167
v0.9.166
v0.9.165
v0.9.164
v0.9.163
v0.9.162
v0.9.161
v0.9.160
v0.9.159
v0.9.158
v0.9.157
v0.9.156
v0.9.155
v0.9.154
v0.9.153
v0.9.152
v0.9.151
v0.9.150
v0.9.149
v0.9.148
v0.9.147
v0.9.146
v0.9.145
v0.9.144
v0.9.143
v0.9.142
v0.9.141
v0.9.140
v0.9.139
v0.9.138
v0.9.137
v0.9.136
v0.9.135
v0.9.134
v0.9.133
v0.9.132
v0.9.131
v0.9.130
v0.9.129
v0.9.128
v0.9.127
v0.9.126
v0.9.125
v0.9.124
v0.9.123
v0.9.122
v0.9.121
v0.9.120
v0.9.119
v0.9.118
v0.9.117
v0.9.116
v0.9.115
v0.9.114
v0.9.113
v0.9.112
v0.9.111
v0.9.110
v0.9.109
v0.9.108
v0.9.107
v0.9.106
v0.9.105
v0.9.104
v0.9.103
v0.9.102
v0.9.101
v0.9.100
v0.9.99
v0.9.98
v0.9.97
v0.9.96
v0.9.95
v0.9.94
v0.9.93
v0.9.92
v0.9.91
v0.9.90
v0.9.89
v0.9.88
v0.9.87
v0.9.86
v0.9.85
v0.9.84
v0.9.83
v0.9.82
v0.9.81
v0.9.80
v0.9.79
v0.9.78
v0.9.77
v0.9.76
v0.9.75
v0.9.74
v0.9.73
v0.9.72
v0.9.71
v0.9.70
v0.9.69
v0.9.68
v0.9.67
v0.9.66
v0.9.65
v0.9.64
v0.9.63
v0.9.62
v0.9.61
v0.9.60
v0.9.59
v0.9.58
v0.9.57
v0.9.56
v0.9.55
v0.9.54
v0.9.53
v0.9.52
v0.9.51
v0.9.50
v0.9.49
v0.9.48
v0.9.47
v0.9.46
v0.9.45
v0.9.44
v0.9.43
v0.9.42
v0.9.41
v0.9.40
v0.9.39
v0.9.38
v0.9.37
v0.9.36
v0.9.35
v0.9.34
v0.9.33
v0.9.32
v0.9.31
v0.9.30
v0.9.29
v0.9.28
v0.9.27
v0.9.26
v0.9.25
v0.9.24
v0.9.23
v0.9.22
v0.9.21
v0.9.20
v0.9.19
v0.9.18
v0.9.17
v0.9.16
v0.9.15
v0.9.14
v0.9.13
v0.9.12
v0.9.11
v0.9.10
v0.9.9
v0.9.8
v0.9.7
v0.9.6
v0.9.5
v0.9.4
v0.9.3
v0.9.2
v0.9.1
v0.9.0
v0.8.55
v0.8.54
v0.8.53
v0.8.52
v0.8.51
v0.8.50
v0.8.49
v0.8.48
v0.8.47
v0.8.46
v0.8.45
v0.8.44
v0.8.43
v0.8.42
v0.8.41
v0.8.40
v0.8.39
v0.8.38
v0.8.37
v0.8.36
v0.8.35
v0.8.34
v0.8.33
v0.8.32
v0.8.31
v0.8.30
v0.8.29
v0.8.28
v0.8.27
v0.8.26
v0.8.25
v0.8.24
v0.8.23
v0.8.22
v0.8.21
v0.8.20
v0.8.19
v0.8.18
v0.8.17
v0.8.16
v0.8.15
v0.8.14
v0.8.13
v0.8.12
v0.8.11
v0.8.10
v0.8.9
v0.8.8
v0.8.7
v0.8.6
v0.8.5
v0.8.4
v0.8.3
v0.8.2
v0.8.1
v0.8.0
v0.7.95
v0.7.94
v0.7.93
v0.7.92
v0.7.91
v0.7.90
v0.7.89
v0.7.88
v0.7.87
v0.7.86
v0.7.85
v0.7.84
v0.7.83
v0.7.82
v0.7.81
v0.7.80
v0.7.79
v0.7.78
v0.7.77
v0.7.76
v0.7.75
v0.7.74
v0.7.73
v0.7.72
v0.7.71
v0.7.70
v0.7.69
v0.7.68
v0.7.67
v0.7.66
v0.7.65
v0.7.64
v0.7.63
v0.7.62
v0.7.61
v0.7.60
v0.7.59
v0.7.58
v0.7.57
v0.7.56
v0.7.55
v0.7.54
v0.7.53
v0.7.52
v0.7.51
v0.7.50
v0.7.49
v0.7.48
v0.7.47
v0.7.46
v0.7.45
v0.7.44
v0.7.43
v0.7.42
v0.7.41
v0.7.40
v0.7.39
v0.7.38
v0.7.37
v0.7.36
v0.7.35
v0.7.34
v0.7.33
v0.7.32
v0.7.31
v0.7.30
v0.7.29
v0.7.28
v0.7.27
v0.7.26
v0.7.25
v0.7.24
v0.7.23
v0.7.22
v0.7.21
v0.7.20
v0.7.19
v0.7.18
v0.7.17
v0.7.16
v0.7.15
v0.7.14
v0.7.13
v0.7.12
v0.7.11
v0.7.10
v0.7.9
v0.7.8
v0.7.7
v0.7.6
v0.7.5
v0.7.4
v0.7.3
v0.7.2
v0.7.1
v0.7.0
v0.6.26
v0.6.25
v0.6.24
v0.6.23
v0.6.22
v0.6.21
v0.6.20
v0.6.19
v0.6.18
v0.6.17
v0.6.16
v0.6.15
v0.6.14
v0.6.13
v0.6.12
v0.6.11
v0.6.10
v0.6.9
v0.6.8
v0.6.7
v0.6.6
v0.6.5
v0.6.4
v0.6.3
v0.6.2
v0.6.1
v0.6.0
v0.5.36
v0.5.35
v0.5.34
v0.5.33
v0.5.32
v0.5.31
v0.5.30
v0.5.29
v0.5.28
v0.5.27
v0.5.26
v0.5.25
v0.5.24
v0.5.23
v0.5.22
v0.5.21
v0.5.20
v0.5.19
v0.5.18
v0.5.17
v0.5.16
v0.5.15
v0.5.14
v0.5.13
v0.5.12
v0.5.11
v0.5.10
v0.5.9
v0.5.8
v0.5.7
v0.5.6
v0.5.5
v0.5.4
v0.5.3
v0.5.2
v0.5.1
v0.5.0
v0.4.30
v0.4.29
v0.4.28
v0.4.27
v0.4.26
v0.4.25
v0.4.24
v0.4.23
v0.4.22
v0.4.21
v0.4.20
v0.4.19
v0.4.18
v0.4.17
v0.4.16
v0.4.15
v0.4.14
v0.4.13
v0.4.12
v0.4.11
v0.4.10
v0.4.9
v0.4.8
v0.4.7
v0.4.6
v0.4.5
v0.4.4
v0.4.3
v0.4.2
v0.4.1
v0.4.0
v0.3.50
v0.3.49
v0.3.48
v0.3.47
v0.3.46
v0.3.45
v0.3.44
v0.3.43
v0.3.42
v0.3.41
v0.3.40
v0.3.39
v0.3.38
v0.3.37
v0.3.36
v0.3.35
v0.3.34
v0.3.33
v0.3.32
v0.3.31
v0.3.30
v0.3.29
v0.3.28
v0.3.27
v0.3.26
v0.3.25
v0.3.24
v0.3.23
v0.3.22
v0.3.21
v0.3.20
v0.3.19
v0.3.18
v0.3.17
v0.3.16
v0.3.15
v0.3.14
v0.3.13
v0.3.12
v0.3.11
v0.3.10
v0.3.9
v0.3.8
v0.3.7
v0.3.6
v0.3.5
v0.3.4
v0.3.3
v0.3.2
v0.3.1
v0.3a43
v0.3a42
v0.3a41
v0.3a40
v0.3a39
v0.3a38
v0.3a37
v0.3a36
v0.3a35
v0.3a34
v0.3a33
v0.3a32
v0.3a31
v0.3a30
v0.3a29
v0.3a28
v0.3a27
v0.3a26
v0.3a25
v0.3a24
v0.3a23
v0.3a22
v0.3a21
v0.3a20
v0.3a19
v0.3a18
v0.3a17
v0.3a16
v0.3a15
v0.3a14
v0.3a13
v0.3a12
v0.3a11
v0.3a10
v0.3a9
v0.3a8
v0.3a7
v0.3a6
v0.3a5
v0.3a4r1
v0.3a4
v0.3a1
Labels
Clear labels
No items
No labels
Milestone
Clear milestone
No items
No milestone
Projects
Clear projects
No items
No project
Assignees
Clear assignees
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".
No due date set.
Dependencies
No dependencies set.
Reference: rattail/rattail#8
Reference in a new issue
No description provided.
Delete branch "%!s()"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
I've long been aware of inefficiencies caused by constant querying of the
settingtable for real-time config values. It was originally noticed because of the sheer number of queries required e.g. for a single page load in the web app.Accordingly, I've long intended to add some sort of caching mechanism to cut down on the number of queries. However there is a new problem, illustrated by #6 and #7: Not only are there too many queries, but they require too many DB sessions!
So the caching mechanism needs to allow for:
config.get('foo', 'bar')should "just work"I think two approaches make sense:
Basic / Native Caching
First, the
RattailConfigclass should implement a basic caching mechanism. This should keep track of values already fetched from the DB, and when each was fetched. If an already-fetched value is requested from caller, and it was fetched "recently" enough (e.g. within last 30 seconds?) then the cached value is returned to caller as-is. If the fetch was not recent enough, it is re-fetched via DB query.This has the advantage of being built-in to Rattail and therefore can work out of the box with no setup needed. May still want to allow config to disable the caching (perhaps it should even be off by default?), as well as config to determine cache expiry timeout, i.e. TTL for the cached values.
But it has the disadvantage of still tying up DB sessions and running queries semi-regularly. The cutoff of 30 seconds is striking a balance: We don't want to keep running queries if the same values are requested frequently; however the values may be changed by admin user at any time, so it's not safe to "always" return the same value from cache. It's hoped that 30 seconds is often enough to reflect actual changes "quickly" but infrequent enough to help somewhat with session/query overhead.
Beaker Caching
Ultimately I think the best fix here is to split out the config caching to its own "service". Since the web app already uses Beaker (for user sessions) it probably makes sense to use that here as well, since it provides a Caching system with support for multiple back-ends (e.g. file, memcached, redis).
Assuming a "normal" back-end (e.g. file, memcached, redis) then I believe all running Rattail apps could effectively leverage the same cache. For a given app, this would mean not only that it need run fewer queries to fetch settings, but in fact (potentially) zero queries.
Depending on the desired back-end, this may require some more setup. But hopefully a basic file-based cache would require only turning on a single config flag; i.e. the default file path for cache storage could be determined automatically (or overridden via config).
One gotcha here is the question of when/how to "invalidate" the cache for a given setting. This is especially important if multiple apps are leveraging a single cache. Consider:
Anytime a setting is modified in the DB, the cache system needs to know about it so that it will re-fetch the new value when it is next requested by a caller.
I believe that since settings are usually written via dedicated function, we can add some hook to invalidate cache for the setting which is being written. But this will need closer attention when I get to that point.
I'm curious if any of your other clients such as the Sac coop have run into this too-many-queries issue with Milo or are Dtail pageloads just particularly full of settings? Maybe we have more simulataneous users?
In either option if a changed setting isn't invalidated in the cache actually immediately we may encounter unexpected behavior, or outright bugs. So it seems to me we have to have it. Are you saying even if we choose the Beaker cache system, you'll still have to implement some way to invalidate the setting; that that's not something Beaker provides? If instant invalidation must be done either way, then is the only disadvantage of not using Beaker no possibility to have a backend to share settings across all apps?
You mentioned Beaker handles user sessions, but I wonder what proportion of settings are user-specific versus global. I'm not clear on if the setting object that gets passed around is global.
Yes definitely the problem has occurred for other installs. Mostly it shows up when datasync involves "several" nodes, e.g. multi-store. The pseudo-fix has been to increase
pool_sizeandmax_overflowas described in #6. It's trial and error to see how big the pool size must be to avoid the issue. That process already happened elsewhere, you're just late to the party. ;) But also your upgrade triggered me to try to get to the bottom of it instead of just applying thepool_sizeband-aid.You bring up a good point though. My "basic" cache idea w/ 30 second expiry would in fact not work correctly for datasync in particular, since in many cases it will auto-update certain settings as often as once per second. The idea of 30 second expiry was to avoid the complexity of invalidation for "native" caching.
Beaker definitely supports Invalidating; my concern is only that I must be sure to wire that up correctly.
This pretty much tells me that the "basic caching" is not worth fooling with (even though I already implemented it, I may rip that out) and we need to go straight for Beaker. So future installs will have no caching by default, but Beaker caching could be configured as needed.
As for global vs. user-specific, only web session data is user-specific. All "settings" (i.e. values in the
settingtable in DB) are effectively global in nature, although some of those values may only apply to a certain user. In practice none of that should be an issue.I added initial Beaker cache support, and went live with it for a few apps. So far it seems to be working, with one caveat:
In a multi-node datasync setup, there may be "many" settings normally read on app startup. With the beaker cache enabled, each time a setting must be fetched from the DB it uses a new/separate session. Which means errors such as #6 may happen on "first" app startup after enabling beaker cache. However once each setting has been cached, the app is happy to use that with no fetching from DB. This means you can just restart datasync to get past any initial errors, and after that things should run fine.
But that is annoying; the cache should "just work" really. I guess I'll need to figure out a way to share a single session when the cache must fetch settings from DB? Or possibly the cache should "auto-fill" when first used, by pre-fetching all settings in the DB?
The problem described above, with first app startup after enabling beaker cache, may be tricky to solve. I believe the cause is a "burst" of multiple queries at once, which is maybe due to number of threads in datasync? I believe sessions are being closed and returned to the pool..right?? In which case sequential queries should only use "one session at a time", but the use of threads means queries may not be sequential and instead overlap, hence many sessions at once.
If that's all so, then the larger / more complex apps may have the issue but maybe not simpler ones; due to the difference in thread count, and probably sheer number of settings to be fetched for running logic etc.
I'd lean toward leaving the complex apps "unsolved" here since they already need some expertise to maintain, and since it's a one-time problem. And because the real fix here I think would require the setting queries to be made "sequentially" somehow, and that is a can of worms. Not only would various threads need to call a single shared routine (?) to fetch settings from DB, but possibly the various apps ought to share it too. The irony is the "fix" required looks an awful lot like what Beaker is already doing for us - after it gets past any initial "burst o' queries".
Oh and I didn't like the idea of pre-fetching all settings when cache is first enabled, because there may be lots of settings, and many may not be needed so no reason to clutter the cache.
TL;DR i think i'm happy with beaker caching as-is, unless something further comes up
Cache is in use for some fairly major installs now, with no further issues so far. Calling this good for now.
Have added some docs for this at https://rattailproject.org/docs/rattail-manual/base/config/db.html#db-settings-cache
Well dang it. Something is not quite right about the caching. Regardless of backend (have tried file, memcached), "stale" values have been encountered in real-world use. Meaning, the app behaves incorrectly per "older" config values which were since updated, as if it ignores the newer values.
So this feature is not complete; and in fact it is not recommended to use it. Docs updated accordingly.
Even though I've wanted to pursue this feature for a long time, #6 is really what pushed me to tackle it this time around. Not sure yet, what will be the next motivator. I'll lower priority and just ignore it for now... :/