Skip to content

Commit adc22ca

Browse files
committed
refactor: do not cache route lookups, instead make matchDomain method smart
1 parent 741308d commit adc22ca

File tree

6 files changed

+86
-51
lines changed

6 files changed

+86
-51
lines changed

README.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -26,8 +26,8 @@ The benchmarking scheme is taken from the Fastify github repo.
2626

2727
| Framework | Version | Router? | Requests/sec |
2828
| :----------------- | :------------------------- | :----------: | ------------: |
29-
| **Fastify** | **2.0.0** | **✓** | **52709** |
30-
| **AdonisJs** | **1.5.4** | **✓** | **47791** |
29+
| **Fastify** | **2.0.0** | **✓** | **58,740** |
30+
| **AdonisJS** | **1.8.1** | **✓** | **54,832** |
3131

3232
You can run the same benchmarks by cloning the repo and then running the following command.
3333

docs/classes/_poppinss_request.request.md

Lines changed: 38 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@ using `request.request` property.
2828

2929
### Properties
3030

31+
* [ctx](_poppinss_request.request.md#optional-ctx)
3132
* [parsedUrl](_poppinss_request.request.md#parsedurl)
3233
* [request](_poppinss_request.request.md#request)
3334
* [response](_poppinss_request.request.md#response)
@@ -110,6 +111,15 @@ Name | Type |
110111

111112
## Properties
112113

114+
### `Optional` ctx
115+
116+
**ctx**? : *HttpContextContract*
117+
118+
The ctx will be set by the context itself. It creates a circular
119+
reference
120+
121+
___
122+
113123
### parsedUrl
114124

115125
**parsedUrl**: *UrlWithStringQuery* = parse(this.request.url!, false)
@@ -156,7 +166,7 @@ Required by Macroable
156166

157167
### accepts
158168

159-
**accepts**(`types`: string[]): *string | null*
169+
**accepts**<**T**>(`types`: T[]): *T | null*
160170

161171
Returns the best type using `Accept` header and
162172
by matching it against the given types.
@@ -178,13 +188,17 @@ switch (request.accepts(['json', 'html'])) {
178188
}
179189
```
180190

191+
**Type parameters:**
192+
193+
**T**: *string*
194+
181195
**Parameters:**
182196

183197
Name | Type |
184198
------ | ------ |
185-
`types` | string[] |
199+
`types` | T[] |
186200

187-
**Returns:** *string | null*
201+
**Returns:** *T | null*
188202

189203
___
190204

@@ -214,7 +228,7 @@ ___
214228

215229
### charset
216230

217-
**charset**(`charsets`: string[]): *string | null*
231+
**charset**<**T**>(`charsets`: T[]): *T | null*
218232

219233
Returns the best charset using `Accept-charset` header
220234
and by matching it against the given charsets.
@@ -234,13 +248,17 @@ switch (request.charset(['utf-8', 'ISO-8859-1'])) {
234248
}
235249
```
236250

251+
**Type parameters:**
252+
253+
**T**: *string*
254+
237255
**Parameters:**
238256

239257
Name | Type |
240258
------ | ------ |
241-
`charsets` | string[] |
259+
`charsets` | T[] |
242260

243-
**Returns:** *string | null*
261+
**Returns:** *T | null*
244262

245263
___
246264

@@ -316,7 +334,7 @@ ___
316334

317335
### encoding
318336

319-
**encoding**(`encodings`: string[]): *string | null*
337+
**encoding**<**T**>(`encodings`: T[]): *T | null*
320338

321339
Returns the best encoding using `Accept-encoding` header
322340
and by matching it against the given encodings.
@@ -326,13 +344,17 @@ If nothing is matched, then `null` will be returned
326344
Make sure to check [accepts](https://www.npmjs.com/package/accepts) package
327345
docs too.
328346

347+
**Type parameters:**
348+
349+
**T**: *string*
350+
329351
**Parameters:**
330352

331353
Name | Type |
332354
------ | ------ |
333-
`encodings` | string[] |
355+
`encodings` | T[] |
334356

335-
**Returns:** *string | null*
357+
**Returns:** *T | null*
336358

337359
___
338360

@@ -650,7 +672,7 @@ ___
650672

651673
### language
652674

653-
**language**(`languages`: string[]): *string | null*
675+
**language**<**T**>(`languages`: T[]): *T | null*
654676

655677
Returns the best language using `Accept-language` header
656678
and by matching it against the given languages.
@@ -672,13 +694,17 @@ switch (request.language(['fr', 'de'])) {
672694
}
673695
```
674696

697+
**Type parameters:**
698+
699+
**T**: *string*
700+
675701
**Parameters:**
676702

677703
Name | Type |
678704
------ | ------ |
679-
`languages` | string[] |
705+
`languages` | T[] |
680706

681-
**Returns:** *string | null*
707+
**Returns:** *T | null*
682708

683709
___
684710

docs/classes/_src_response_index_.response.md

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,7 @@ This is how `explicitEnd` mode works in nutshell.
4242

4343
### Properties
4444

45+
* [ctx](_src_response_index_.response.md#optional-ctx)
4546
* [lazyBody](_src_response_index_.response.md#lazybody)
4647
* [request](_src_response_index_.response.md#request)
4748
* [response](_src_response_index_.response.md#response)
@@ -155,6 +156,15 @@ Name | Type |
155156

156157
## Properties
157158

159+
### `Optional` ctx
160+
161+
**ctx**? : *HttpContextContract*
162+
163+
The ctx will be set by the context itself. It creates a circular
164+
reference
165+
166+
___
167+
158168
### lazyBody
159169

160170
**lazyBody**: *LazyBody | null* = null

docs/classes/_src_router_store_.store.md

Lines changed: 10 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -30,16 +30,25 @@ store.match('posts/1', 'GET')
3030

3131
## Index
3232

33+
### Properties
34+
35+
* [matchDomain](_src_router_store_.store.md#matchdomain)
36+
3337
### Methods
3438

3539
* [add](_src_router_store_.store.md#add)
3640
* [match](_src_router_store_.store.md#match)
37-
* [matchDomain](_src_router_store_.store.md#matchdomain)
3841

3942
### Object literals
4043

4144
* [tree](_src_router_store_.store.md#tree)
4245

46+
## Properties
47+
48+
### matchDomain
49+
50+
**matchDomain**: *any* = this.matchDomainNoop
51+
4352
## Methods
4453

4554
### add
@@ -94,22 +103,6 @@ Name | Type |
94103

95104
**Returns:** *null | MatchedRoute*
96105

97-
___
98-
99-
### matchDomain
100-
101-
**matchDomain**(`domain`: string): *RouteStoreMatch[]*
102-
103-
Matches the domain pattern for a given string
104-
105-
**Parameters:**
106-
107-
Name | Type |
108-
------ | ------ |
109-
`domain` | string |
110-
111-
**Returns:** *RouteStoreMatch[]*
112-
113106
## Object literals
114107

115108
### tree

src/Router/Store.ts

Lines changed: 26 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,25 @@ import {
5353
export class Store {
5454
public tree: RoutesTree = { tokens: [], domains: {} }
5555

56+
/**
57+
* The [[matchDomainReal]] and [[matchDomainNoop]] functions are two
58+
* implementation of matching a domain. We use noop implementation
59+
* by default and once an explicit domain is registered, we
60+
* pivot to [[matchDomainReal]].
61+
*
62+
* This all is done for performance, since we have noticed around 8-10%
63+
* improvement.
64+
*/
65+
private matchDomainReal = function (domain: string): RouteStoreMatch[] {
66+
return matchit.match(domain || 'root', this.tree.tokens)
67+
}.bind(this)
68+
69+
private matchDomainNoop = function (_: string): RouteStoreMatch[] {
70+
return []
71+
}.bind(this)
72+
73+
public matchDomain = this.matchDomainNoop
74+
5675
/**
5776
* Returns the domain node for a given domain. If domain node is missing,
5877
* it will added to the routes object and tokens are also generated
@@ -118,6 +137,13 @@ export class Store {
118137
'name',
119138
])) as RouteNode
120139

140+
/**
141+
* An explicit domain is defined
142+
*/
143+
if (route.domain && route.domain !== 'root' && this.matchDomain !== this.matchDomainReal) {
144+
this.matchDomain = this.matchDomainReal
145+
}
146+
121147
route.methods.forEach((method) => {
122148
const methodRoutes = this.getMethodRoutes(route.domain || 'root', method)
123149

@@ -150,13 +176,6 @@ export class Store {
150176
return this
151177
}
152178

153-
/**
154-
* Matches the domain pattern for a given string
155-
*/
156-
public matchDomain (domain: string): RouteStoreMatch[] {
157-
return matchit.match(domain || 'root', this.tree.tokens)
158-
}
159-
160179
/**
161180
* Matches the url, method and optionally domain to pull the matching
162181
* route. `null` is returned when unable to match the URL against

src/Router/index.ts

Lines changed: 0 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,6 @@
1515

1616
import ms from 'ms'
1717
import { stringify } from 'qs'
18-
import QuickLru from 'quick-lru'
1918
import { Exception } from '@poppinss/utils'
2019
import { EncryptionContract } from '@ioc:Adonis/Core/Encryption'
2120

@@ -92,11 +91,6 @@ export class Router implements RouterContract {
9291
*/
9392
private testRoutePatternCounter = 0
9493

95-
/**
96-
* Cache of routes matched for a given domain, url and method
97-
*/
98-
private matchedRoutes = new QuickLru({ maxSize: 1000 })
99-
10094
private getRecentGroup () {
10195
return this.openedGroups[this.openedGroups.length - 1]
10296
}
@@ -348,12 +342,6 @@ export class Router implements RouterContract {
348342
*
349343
* - Else we search within the routes of the mentioned domain.
350344
*/
351-
352-
const cacheKey = `${url}-${method}-${domain}`
353-
if (this.matchedRoutes.has(cacheKey)) {
354-
return this.matchedRoutes.get(cacheKey) as null | MatchedRoute
355-
}
356-
357345
let response: null | MatchedRoute = null
358346
const matchingDomain = domain ? this.store.matchDomain(domain) : []
359347

@@ -369,7 +357,6 @@ export class Router implements RouterContract {
369357
})
370358
}
371359

372-
this.matchedRoutes.set(cacheKey, response)
373360
return response
374361
}
375362

0 commit comments

Comments
 (0)