cds-caching 1.2.1 → 1.3.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/README.md +195 -1114
- package/app/dashboard/Component-dbg.js +59 -0
- package/app/dashboard/Component-dbg.js.map +1 -0
- package/app/dashboard/Component.js +2 -0
- package/app/dashboard/Component.js.map +1 -0
- package/app/dashboard/Component.ts +59 -0
- package/app/dashboard/controller/App-dbg.controller.js +132 -0
- package/app/dashboard/controller/App-dbg.controller.js.map +1 -0
- package/app/dashboard/controller/App.controller.js +2 -0
- package/app/dashboard/controller/App.controller.js.map +1 -0
- package/app/dashboard/controller/App.controller.ts +147 -0
- package/app/dashboard/controller/BaseController-dbg.js +75 -0
- package/app/dashboard/controller/BaseController-dbg.js.map +1 -0
- package/app/dashboard/controller/BaseController.js +2 -0
- package/app/dashboard/controller/BaseController.js.map +1 -0
- package/app/dashboard/controller/BaseController.ts +88 -0
- package/app/dashboard/controller/Cache-dbg.controller.js +421 -0
- package/app/dashboard/controller/Cache-dbg.controller.js.map +1 -0
- package/app/dashboard/controller/Cache.controller.js +2 -0
- package/app/dashboard/controller/Cache.controller.js.map +1 -0
- package/app/dashboard/controller/Cache.controller.ts +490 -0
- package/app/dashboard/controller/Main-dbg.controller.js +32 -0
- package/app/dashboard/controller/Main-dbg.controller.js.map +1 -0
- package/app/dashboard/controller/Main.controller.js +2 -0
- package/app/dashboard/controller/Main.controller.js.map +1 -0
- package/app/dashboard/controller/Main.controller.ts +31 -0
- package/app/dashboard/controller/SingleMetric-dbg.controller.js +306 -0
- package/app/dashboard/controller/SingleMetric-dbg.controller.js.map +1 -0
- package/app/dashboard/controller/SingleMetric.controller.js +2 -0
- package/app/dashboard/controller/SingleMetric.controller.js.map +1 -0
- package/app/dashboard/controller/SingleMetric.controller.ts +335 -0
- package/app/dashboard/i18n/i18n.properties +61 -0
- package/app/dashboard/i18n/i18n_de.properties +3 -0
- package/app/dashboard/i18n/i18n_en.properties +3 -0
- package/app/dashboard/index-cdn.html +28 -0
- package/app/dashboard/index.html +19 -0
- package/app/dashboard/manifest.json +145 -0
- package/app/dashboard/model/formatter-dbg.js +253 -0
- package/app/dashboard/model/formatter-dbg.js.map +1 -0
- package/app/dashboard/model/formatter.js +2 -0
- package/app/dashboard/model/formatter.js.map +1 -0
- package/app/dashboard/model/formatter.ts +275 -0
- package/app/dashboard/model/models-dbg.js +13 -0
- package/app/dashboard/model/models-dbg.js.map +1 -0
- package/app/dashboard/model/models.js +2 -0
- package/app/dashboard/model/models.js.map +1 -0
- package/app/dashboard/model/models.ts +13 -0
- package/app/dashboard/resources/sap-ui-custom-dbg.js +4695 -0
- package/app/dashboard/resources/sap-ui-custom-dbg.js.map +1 -0
- package/app/dashboard/resources/sap-ui-custom.js +5794 -0
- package/app/dashboard/resources/sap-ui-custom.js.map +1 -0
- package/app/dashboard/service/CacheStatisticsService-dbg.js +267 -0
- package/app/dashboard/service/CacheStatisticsService-dbg.js.map +1 -0
- package/app/dashboard/service/CacheStatisticsService.js +2 -0
- package/app/dashboard/service/CacheStatisticsService.js.map +1 -0
- package/app/dashboard/service/CacheStatisticsService.ts +397 -0
- package/app/dashboard/view/App.view.xml +18 -0
- package/app/dashboard/view/Cache.view.xml +777 -0
- package/app/dashboard/view/Main.view.xml +57 -0
- package/app/dashboard/view/SingleMetric.view.xml +337 -0
- package/cds-plugin.js +52 -0
- package/db/cache-store.cds +12 -0
- package/db/statistics.cds +128 -0
- package/index.cds +2 -128
- package/lib/CachingService.js +13 -0
- package/lib/add.js +17 -0
- package/lib/operations/AsyncOperations.js +162 -111
- package/lib/operations/BasicOperations.js +35 -0
- package/lib/operations/CapOperations.js +92 -40
- package/lib/support/CacheStatisticsHandler.js +35 -10
- package/lib/support/CacheStoreManager.js +29 -0
- package/lib/support/KeyvCDS.js +149 -0
- package/lib/support/MultitenancyDetector.js +25 -0
- package/lib/support/RuntimeConfigurationManager.js +69 -37
- package/lib/support/StatisticsPersistenceManager.js +18 -2
- package/lib/support/Telemetry.js +137 -0
- package/lib/util.js +29 -11
- package/package.json +43 -11
- package/srv/caching-api-service.js +47 -2
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
<mvc:View controllerName="cds.plugin.caching.dashboard.controller.Main" displayBlock="true"
|
|
2
|
+
xmlns="sap.m"
|
|
3
|
+
xmlns:mvc="sap.ui.core.mvc"
|
|
4
|
+
xmlns:core="sap.ui.core"
|
|
5
|
+
xmlns:l="sap.ui.layout"
|
|
6
|
+
xmlns:f="sap.f"
|
|
7
|
+
xmlns:fb="sap.ui.comp.filterbar"
|
|
8
|
+
xmlns:card="sap.f.cards"
|
|
9
|
+
xmlns:cssgrid="sap.ui.layout.cssgrid"
|
|
10
|
+
xmlns:viz="sap.viz.ui5.controls"
|
|
11
|
+
xmlns:viz.feeds="sap.viz.ui5.controls.common.feeds"
|
|
12
|
+
xmlns:viz.data="sap.viz.ui5.data" core:require="{
|
|
13
|
+
formatter: 'cds/plugin/caching/dashboard/model/formatter'
|
|
14
|
+
}">
|
|
15
|
+
|
|
16
|
+
<f:DynamicPage title="cds-caching – Dashboard" headerExpanded="true" headerPinned="true">
|
|
17
|
+
<f:title>
|
|
18
|
+
<f:DynamicPageTitle>
|
|
19
|
+
<f:heading>
|
|
20
|
+
<Title text="cds-caching – Dashboard" />
|
|
21
|
+
</f:heading>
|
|
22
|
+
<f:actions>
|
|
23
|
+
<Button type="Transparent" icon="sap-icon://refresh" text="Refresh" press="onRefresh" />
|
|
24
|
+
</f:actions>
|
|
25
|
+
</f:DynamicPageTitle>
|
|
26
|
+
</f:title>
|
|
27
|
+
|
|
28
|
+
<f:content>
|
|
29
|
+
|
|
30
|
+
<Table id="cachesList" headerText="Caching Services" mode="SingleSelectMaster" items="{path: '/Caches'}" itemPress="onCacheSelect" noDataText="No caches available">
|
|
31
|
+
<columns>
|
|
32
|
+
<Column>
|
|
33
|
+
<Text text="Name" />
|
|
34
|
+
</Column>
|
|
35
|
+
<Column>
|
|
36
|
+
<Text text="Statistic Tracking Enabled" />
|
|
37
|
+
</Column>
|
|
38
|
+
<Column>
|
|
39
|
+
<Text text="Key Tracking Enabled" />
|
|
40
|
+
</Column>
|
|
41
|
+
</columns>
|
|
42
|
+
<items>
|
|
43
|
+
<ColumnListItem type="Navigation">
|
|
44
|
+
<cells>
|
|
45
|
+
<Text text="{name}" />
|
|
46
|
+
<Text text="{path: 'metricsEnabled' }" />
|
|
47
|
+
<Text text="{path: 'keyMetricsEnabled' }" />
|
|
48
|
+
</cells>
|
|
49
|
+
|
|
50
|
+
</ColumnListItem>
|
|
51
|
+
</items>
|
|
52
|
+
</Table>
|
|
53
|
+
</f:content>
|
|
54
|
+
|
|
55
|
+
</f:DynamicPage>
|
|
56
|
+
|
|
57
|
+
</mvc:View>
|
|
@@ -0,0 +1,337 @@
|
|
|
1
|
+
<mvc:View controllerName="cds.plugin.caching.dashboard.controller.SingleMetric"
|
|
2
|
+
xmlns="sap.m"
|
|
3
|
+
xmlns:mvc="sap.ui.core.mvc"
|
|
4
|
+
xmlns:core="sap.ui.core"
|
|
5
|
+
xmlns:l="sap.ui.layout"
|
|
6
|
+
xmlns:f="sap.f"
|
|
7
|
+
xmlns:fb="sap.ui.comp.filterbar"
|
|
8
|
+
xmlns:card="sap.f.cards"
|
|
9
|
+
xmlns:cssgrid="sap.ui.layout.cssgrid"
|
|
10
|
+
xmlns:viz="sap.viz.ui5.controls"
|
|
11
|
+
xmlns:viz.feeds="sap.viz.ui5.controls.common.feeds"
|
|
12
|
+
xmlns:viz.data="sap.viz.ui5.data"
|
|
13
|
+
xmlns:form="sap.ui.layout.form" core:require="{
|
|
14
|
+
formatter: 'cds/plugin/caching/dashboard/model/formatter'
|
|
15
|
+
}">
|
|
16
|
+
|
|
17
|
+
<f:DynamicPage title="Metric Details" showFooter="false">
|
|
18
|
+
<f:title>
|
|
19
|
+
<f:DynamicPageTitle>
|
|
20
|
+
<f:heading>
|
|
21
|
+
<Title text="Metric: {ui>/metricId}" />
|
|
22
|
+
</f:heading>
|
|
23
|
+
<f:actions>
|
|
24
|
+
<Button icon="sap-icon://refresh" text="Refresh" type="Transparent" press="onRefresh" />
|
|
25
|
+
</f:actions>
|
|
26
|
+
<f:navigationActions>
|
|
27
|
+
<OverflowToolbarButton type="Transparent" icon="sap-icon://full-screen" press="handleFullScreen" tooltip="Enter Full Screen Mode" visible="{= ${app>/layout} !== 'EndColumnFullScreen' }"/>
|
|
28
|
+
<OverflowToolbarButton type="Transparent" icon="sap-icon://exit-full-screen" press="handleExitFullScreen" tooltip="Exit Full Screen Mode" visible="{= ${app>/layout} === 'EndColumnFullScreen' }"/>
|
|
29
|
+
<OverflowToolbarButton type="Transparent" icon="sap-icon://decline" press="handleClose" tooltip="Close end column"/>
|
|
30
|
+
</f:navigationActions>
|
|
31
|
+
</f:DynamicPageTitle>
|
|
32
|
+
</f:title>
|
|
33
|
+
|
|
34
|
+
<f:content>
|
|
35
|
+
<cssgrid:CSSGrid id="metricMainGrid" width="100%">
|
|
36
|
+
<cssgrid:customLayout>
|
|
37
|
+
<cssgrid:ResponsiveColumnLayout />
|
|
38
|
+
</cssgrid:customLayout>
|
|
39
|
+
|
|
40
|
+
<!-- Read-Through Performance Section -->
|
|
41
|
+
<VBox renderType="Bare">
|
|
42
|
+
<layoutData>
|
|
43
|
+
<cssgrid:ResponsiveColumnItemLayoutData columns="24"/>
|
|
44
|
+
</layoutData>
|
|
45
|
+
|
|
46
|
+
<Title text="Read-Through Performance" titleStyle="H5" class="sapUiMediumMarginTop sapUiTinyMarginBottom" />
|
|
47
|
+
|
|
48
|
+
<f:GridContainer id="metricCacheThroughGrid" columnsChange=".onColumnsChange">
|
|
49
|
+
<GenericTile class="sapUiTinyMarginBegin sapUiTinyMarginTop tileLayout" header="Hit Ratio" subheader="Cache Hit Rate">
|
|
50
|
+
<TileContent unit="%">
|
|
51
|
+
<NumericContent valueColor="{formatter: 'formatter.formatHitRatioWithColor', path: 'ui>/hitRatio'}" value="{ path: 'ui>/hitRatio', type: 'sap.ui.model.type.Float', formatOptions: {style: 'short', decimals: 2}}" />
|
|
52
|
+
</TileContent>
|
|
53
|
+
<layoutData>
|
|
54
|
+
<f:GridContainerItemLayoutData columns="2" minRows="2"/>
|
|
55
|
+
</layoutData>
|
|
56
|
+
</GenericTile>
|
|
57
|
+
|
|
58
|
+
<GenericTile class="sapUiTinyMarginBegin sapUiTinyMarginTop tileLayout" header="Cache Efficiency" subheader="Performance Factor">
|
|
59
|
+
<TileContent unit="x">
|
|
60
|
+
<NumericContent valueColor="{formatter: 'formatter.formatCacheEfficiencyWithColor', path: 'ui>/cacheEfficiency'}" value="{path: 'ui>/cacheEfficiency', type: 'sap.ui.model.type.Float', formatOptions: {style: 'short', decimals: 2}}" />
|
|
61
|
+
</TileContent>
|
|
62
|
+
<layoutData>
|
|
63
|
+
<f:GridContainerItemLayoutData columns="2" minRows="2"/>
|
|
64
|
+
</layoutData>
|
|
65
|
+
</GenericTile>
|
|
66
|
+
|
|
67
|
+
<GenericTile class="sapUiTinyMarginBegin sapUiTinyMarginTop tileLayout" header="Throughput" subheader="Requests per Second">
|
|
68
|
+
<TileContent unit="req/s">
|
|
69
|
+
<NumericContent value="{ path: 'ui>/throughput', type: 'sap.ui.model.type.Float', formatOptions: {style: 'short', decimals: 2}}" withMargin="false" />
|
|
70
|
+
</TileContent>
|
|
71
|
+
<layoutData>
|
|
72
|
+
<f:GridContainerItemLayoutData columns="2" minRows="2"/>
|
|
73
|
+
</layoutData>
|
|
74
|
+
</GenericTile>
|
|
75
|
+
|
|
76
|
+
<GenericTile class="sapUiTinyMarginBegin sapUiTinyMarginTop tileLayout" header="Error Rate" subheader="Cache Errors">
|
|
77
|
+
<TileContent unit="%">
|
|
78
|
+
<NumericContent valueColor="{formatter: 'formatter.formatErrorRateWithColor', path: 'ui>/errorRate'}" value="{ path: 'ui>/errorRate', formatter: 'formatter.formatPercentageValue' }" />
|
|
79
|
+
</TileContent>
|
|
80
|
+
<layoutData>
|
|
81
|
+
<f:GridContainerItemLayoutData columns="2" minRows="2"/>
|
|
82
|
+
</layoutData>
|
|
83
|
+
</GenericTile>
|
|
84
|
+
|
|
85
|
+
<GenericTile class="sapUiTinyMarginBegin sapUiTinyMarginTop tileLayout" header="Total Requests" subheader="Cache Operations">
|
|
86
|
+
<TileContent unit="req">
|
|
87
|
+
<NumericContent value="{= ${ui>/hits} + ${ui>/misses}}" withMargin="false" />
|
|
88
|
+
</TileContent>
|
|
89
|
+
<layoutData>
|
|
90
|
+
<f:GridContainerItemLayoutData columns="2" minRows="2"/>
|
|
91
|
+
</layoutData>
|
|
92
|
+
</GenericTile>
|
|
93
|
+
|
|
94
|
+
<GenericTile class="sapUiTinyMarginBegin sapUiTinyMarginTop tileLayout" header="Cache Hits" subheader="Successful Retrievals">
|
|
95
|
+
<TileContent unit="hits">
|
|
96
|
+
<NumericContent value="{ui>/hits}" withMargin="false" />
|
|
97
|
+
</TileContent>
|
|
98
|
+
<layoutData>
|
|
99
|
+
<f:GridContainerItemLayoutData columns="2" minRows="2"/>
|
|
100
|
+
</layoutData>
|
|
101
|
+
</GenericTile>
|
|
102
|
+
|
|
103
|
+
<GenericTile class="sapUiTinyMarginBegin sapUiTinyMarginTop tileLayout" header="Cache Misses" subheader="Failed Retrievals">
|
|
104
|
+
<TileContent unit="misses">
|
|
105
|
+
<NumericContent value="{ui>/misses}" withMargin="false" />
|
|
106
|
+
</TileContent>
|
|
107
|
+
<layoutData>
|
|
108
|
+
<f:GridContainerItemLayoutData columns="2" minRows="2"/>
|
|
109
|
+
</layoutData>
|
|
110
|
+
</GenericTile>
|
|
111
|
+
|
|
112
|
+
|
|
113
|
+
<GenericTile class="sapUiTinyMarginBegin sapUiTinyMarginTop tileLayout" header="Cache Errors" subheader="Error Count">
|
|
114
|
+
<TileContent unit="errors">
|
|
115
|
+
<NumericContent value="{ui>/errors}" withMargin="false" />
|
|
116
|
+
</TileContent>
|
|
117
|
+
<layoutData>
|
|
118
|
+
<f:GridContainerItemLayoutData columns="2" minRows="2"/>
|
|
119
|
+
</layoutData>
|
|
120
|
+
</GenericTile>
|
|
121
|
+
|
|
122
|
+
|
|
123
|
+
<GenericTile class="sapUiTinyMarginBegin sapUiTinyMarginTop tileLayout" header="Hit Latency" subheader="Cache Hit Response">
|
|
124
|
+
<TileContent unit="ms">
|
|
125
|
+
<NumericContent valueColor="{formatter: 'formatter.formatLatencyWithColor', path: 'ui>/avgHitLatency'}" value="{ path: 'ui>/avgHitLatency', type: 'sap.ui.model.type.Float', formatOptions: {style: 'short', decimals: 2}}" />
|
|
126
|
+
</TileContent>
|
|
127
|
+
<layoutData>
|
|
128
|
+
<f:GridContainerItemLayoutData columns="2" minRows="2"/>
|
|
129
|
+
</layoutData>
|
|
130
|
+
</GenericTile>
|
|
131
|
+
|
|
132
|
+
<GenericTile class="sapUiTinyMarginBegin sapUiTinyMarginTop tileLayout" header="Miss Latency" subheader="Cache Miss Response">
|
|
133
|
+
<TileContent unit="ms">
|
|
134
|
+
<NumericContent valueColor="{formatter: 'formatter.formatLatencyWithColor', path: 'ui>/avgMissLatency'}" value="{ path: 'ui>/avgMissLatency', type: 'sap.ui.model.type.Float', formatOptions: {style: 'short', decimals: 2}}" />
|
|
135
|
+
</TileContent>
|
|
136
|
+
<layoutData>
|
|
137
|
+
<f:GridContainerItemLayoutData columns="2" minRows="2"/>
|
|
138
|
+
</layoutData>
|
|
139
|
+
</GenericTile>
|
|
140
|
+
|
|
141
|
+
<!-- Read-Through Distribution Chart -->
|
|
142
|
+
<f:Card class="sapUiTinyMargin">
|
|
143
|
+
<f:header>
|
|
144
|
+
<card:Header title="Read-Through Operations" subtitle="Hit/Miss/Error Distribution" />
|
|
145
|
+
</f:header>
|
|
146
|
+
<f:content>
|
|
147
|
+
<l:VerticalLayout width="100%">
|
|
148
|
+
<viz:Popover id="metricCacheThroughPopOver"></viz:Popover>
|
|
149
|
+
<viz:VizFrame id="metricCacheThroughChart" vizType="pie" width="100%" height="300px">
|
|
150
|
+
<viz:dataset>
|
|
151
|
+
<viz.data:FlattenedDataset data="{ui>/cacheThroughData}">
|
|
152
|
+
<viz.data:dimensions>
|
|
153
|
+
<viz.data:DimensionDefinition name="Operation" value="{operation}" />
|
|
154
|
+
</viz.data:dimensions>
|
|
155
|
+
<viz.data:measures>
|
|
156
|
+
<viz.data:MeasureDefinition name="Count" value="{count}" />
|
|
157
|
+
</viz.data:measures>
|
|
158
|
+
</viz.data:FlattenedDataset>
|
|
159
|
+
</viz:dataset>
|
|
160
|
+
<viz:feeds>
|
|
161
|
+
<viz.feeds:FeedItem id="metricCacheThroughValueAxisFeed" uid="size" type="Measure" values="Count" />
|
|
162
|
+
<viz.feeds:FeedItem id="metricCacheThroughCategoryAxisFeed" uid="color" type="Dimension" values="Operation" />
|
|
163
|
+
</viz:feeds>
|
|
164
|
+
</viz:VizFrame>
|
|
165
|
+
</l:VerticalLayout>
|
|
166
|
+
</f:content>
|
|
167
|
+
<f:layoutData>
|
|
168
|
+
<f:GridContainerItemLayoutData columns="4" minRows="4"/>
|
|
169
|
+
</f:layoutData>
|
|
170
|
+
</f:Card>
|
|
171
|
+
|
|
172
|
+
|
|
173
|
+
|
|
174
|
+
|
|
175
|
+
<!-- Latency Chart -->
|
|
176
|
+
<f:Card class="sapUiTinyMargin">
|
|
177
|
+
<f:header>
|
|
178
|
+
<card:Header title="Latency Comparison" subtitle="Read-Through Latency Metrics" />
|
|
179
|
+
</f:header>
|
|
180
|
+
<f:content>
|
|
181
|
+
<l:VerticalLayout width="100%">
|
|
182
|
+
<viz:Popover id="metricLatencyPopOver"></viz:Popover>
|
|
183
|
+
<viz:VizFrame id="metricLatencyChart" vizType="bar" width="100%" height="300px">
|
|
184
|
+
<viz:dataset>
|
|
185
|
+
<viz.data:FlattenedDataset data="{ui>/latencyData}">
|
|
186
|
+
<viz.data:dimensions>
|
|
187
|
+
<viz.data:DimensionDefinition name="Operation" value="{operation}" />
|
|
188
|
+
</viz.data:dimensions>
|
|
189
|
+
<viz.data:measures>
|
|
190
|
+
<viz.data:MeasureDefinition name="Latency" value="{latency}" />
|
|
191
|
+
</viz.data:measures>
|
|
192
|
+
</viz.data:FlattenedDataset>
|
|
193
|
+
</viz:dataset>
|
|
194
|
+
<viz:feeds>
|
|
195
|
+
<viz.feeds:FeedItem id="metricValueAxisFeed" uid="valueAxis" type="Measure" values="Latency" />
|
|
196
|
+
<viz.feeds:FeedItem id="metricCategoryAxisFeed" uid="categoryAxis" type="Dimension" values="Operation" />
|
|
197
|
+
</viz:feeds>
|
|
198
|
+
</viz:VizFrame>
|
|
199
|
+
</l:VerticalLayout>
|
|
200
|
+
</f:content>
|
|
201
|
+
<f:layoutData>
|
|
202
|
+
<f:GridContainerItemLayoutData columns="12" minRows="4"/>
|
|
203
|
+
</f:layoutData>
|
|
204
|
+
</f:Card>
|
|
205
|
+
|
|
206
|
+
|
|
207
|
+
|
|
208
|
+
</f:GridContainer>
|
|
209
|
+
</VBox>
|
|
210
|
+
|
|
211
|
+
<!-- Native Function Performance Section -->
|
|
212
|
+
<VBox renderType="Bare">
|
|
213
|
+
<layoutData>
|
|
214
|
+
<cssgrid:ResponsiveColumnItemLayoutData columns="24"/>
|
|
215
|
+
</layoutData>
|
|
216
|
+
|
|
217
|
+
<Title text="Native Function Performance" titleStyle="H5" class="sapUiMediumMarginTop sapUiTinyMarginBottom" />
|
|
218
|
+
|
|
219
|
+
<f:GridContainer id="metricNativeGrid" columnsChange=".onColumnsChange">
|
|
220
|
+
<GenericTile class="sapUiTinyMarginBegin sapUiTinyMarginTop tileLayout" header="Native Throughput" subheader="Operations per Second">
|
|
221
|
+
<TileContent unit="ops/s">
|
|
222
|
+
<NumericContent value="{ path: 'ui>/nativeThroughput', formatter: 'formatter.formatNumberValue' }" withMargin="false" />
|
|
223
|
+
</TileContent>
|
|
224
|
+
<layoutData>
|
|
225
|
+
<f:GridContainerItemLayoutData columns="2" minRows="2"/>
|
|
226
|
+
</layoutData>
|
|
227
|
+
</GenericTile>
|
|
228
|
+
|
|
229
|
+
<GenericTile class="sapUiTinyMarginBegin sapUiTinyMarginTop tileLayout" header="Native Error Rate" subheader="Native Function Errors">
|
|
230
|
+
<TileContent unit="%">
|
|
231
|
+
<NumericContent valueColor="{formatter: 'formatter.formatErrorRateWithColor', path: 'ui>/nativeErrorRate'}" value="{ path: 'ui>/nativeErrorRate', formatter: 'formatter.formatPercentageValue' }" />
|
|
232
|
+
</TileContent>
|
|
233
|
+
<layoutData>
|
|
234
|
+
<f:GridContainerItemLayoutData columns="2" minRows="2"/>
|
|
235
|
+
</layoutData>
|
|
236
|
+
</GenericTile>
|
|
237
|
+
|
|
238
|
+
<GenericTile class="sapUiTinyMarginBegin sapUiTinyMarginTop tileLayout" header="Native Sets" subheader="Write Operations">
|
|
239
|
+
<TileContent unit="sets">
|
|
240
|
+
<NumericContent value="{ui>/nativeSets}" withMargin="false" />
|
|
241
|
+
</TileContent>
|
|
242
|
+
<layoutData>
|
|
243
|
+
<f:GridContainerItemLayoutData columns="2" minRows="2"/>
|
|
244
|
+
</layoutData>
|
|
245
|
+
</GenericTile>
|
|
246
|
+
|
|
247
|
+
<GenericTile class="sapUiTinyMarginBegin sapUiTinyMarginTop tileLayout" header="Native Gets" subheader="Read Operations">
|
|
248
|
+
<TileContent unit="gets">
|
|
249
|
+
<NumericContent value="{ui>/nativeGets}" withMargin="false" />
|
|
250
|
+
</TileContent>
|
|
251
|
+
<layoutData>
|
|
252
|
+
<f:GridContainerItemLayoutData columns="2" minRows="2"/>
|
|
253
|
+
</layoutData>
|
|
254
|
+
</GenericTile>
|
|
255
|
+
|
|
256
|
+
<GenericTile class="sapUiTinyMarginBegin sapUiTinyMarginTop tileLayout" header="Native Deletes" subheader="Delete Operations">
|
|
257
|
+
<TileContent unit="deletes">
|
|
258
|
+
<NumericContent value="{ui>/nativeDeletes}" withMargin="false" />
|
|
259
|
+
</TileContent>
|
|
260
|
+
<layoutData>
|
|
261
|
+
<f:GridContainerItemLayoutData columns="2" minRows="2"/>
|
|
262
|
+
</layoutData>
|
|
263
|
+
</GenericTile>
|
|
264
|
+
|
|
265
|
+
<GenericTile class="sapUiTinyMarginBegin sapUiTinyMarginTop tileLayout" header="Native Clears" subheader="Clear Operations">
|
|
266
|
+
<TileContent unit="clears">
|
|
267
|
+
<NumericContent value="{ui>/nativeClears}" withMargin="false" />
|
|
268
|
+
</TileContent>
|
|
269
|
+
<layoutData>
|
|
270
|
+
<f:GridContainerItemLayoutData columns="2" minRows="2"/>
|
|
271
|
+
</layoutData>
|
|
272
|
+
</GenericTile>
|
|
273
|
+
|
|
274
|
+
<GenericTile class="sapUiTinyMarginBegin sapUiTinyMarginTop tileLayout" header="Delete By Tags" subheader="Tag-based Deletes">
|
|
275
|
+
<TileContent unit="deletes">
|
|
276
|
+
<NumericContent value="{ui>/nativeDeleteByTags}" withMargin="false" />
|
|
277
|
+
</TileContent>
|
|
278
|
+
<layoutData>
|
|
279
|
+
<f:GridContainerItemLayoutData columns="2" minRows="2"/>
|
|
280
|
+
</layoutData>
|
|
281
|
+
</GenericTile>
|
|
282
|
+
|
|
283
|
+
<GenericTile class="sapUiTinyMarginBegin sapUiTinyMarginTop tileLayout" header="Native Errors" subheader="Error Count">
|
|
284
|
+
<TileContent unit="errors">
|
|
285
|
+
<NumericContent value="{ui>/nativeErrors}" withMargin="false" />
|
|
286
|
+
</TileContent>
|
|
287
|
+
<layoutData>
|
|
288
|
+
<f:GridContainerItemLayoutData columns="2" minRows="2"/>
|
|
289
|
+
</layoutData>
|
|
290
|
+
</GenericTile>
|
|
291
|
+
|
|
292
|
+
<GenericTile class="sapUiTinyMarginBegin sapUiTinyMarginTop tileLayout" header="Total Native Ops" subheader="All Operations">
|
|
293
|
+
<TileContent unit="ops">
|
|
294
|
+
<NumericContent value="{ui>/totalNativeOperations}" withMargin="false" />
|
|
295
|
+
</TileContent>
|
|
296
|
+
<layoutData>
|
|
297
|
+
<f:GridContainerItemLayoutData columns="2" minRows="2"/>
|
|
298
|
+
</layoutData>
|
|
299
|
+
</GenericTile>
|
|
300
|
+
<!-- Native Function Distribution Chart -->
|
|
301
|
+
<f:Card class="sapUiTinyMargin">
|
|
302
|
+
<f:header>
|
|
303
|
+
<card:Header title="Native Function Operations" subtitle="Hit/Miss/Error Distribution" />
|
|
304
|
+
</f:header>
|
|
305
|
+
<f:content>
|
|
306
|
+
<l:VerticalLayout width="100%">
|
|
307
|
+
<viz:Popover id="metricNativePopOver"></viz:Popover>
|
|
308
|
+
<viz:VizFrame id="metricNativeChart" vizType="pie" width="100%" height="300px">
|
|
309
|
+
<viz:dataset>
|
|
310
|
+
<viz.data:FlattenedDataset data="{ui>/nativeData}">
|
|
311
|
+
<viz.data:dimensions>
|
|
312
|
+
<viz.data:DimensionDefinition name="Operation" value="{operation}" />
|
|
313
|
+
</viz.data:dimensions>
|
|
314
|
+
<viz.data:measures>
|
|
315
|
+
<viz.data:MeasureDefinition name="Count" value="{count}" />
|
|
316
|
+
</viz.data:measures>
|
|
317
|
+
</viz.data:FlattenedDataset>
|
|
318
|
+
</viz:dataset>
|
|
319
|
+
<viz:feeds>
|
|
320
|
+
<viz.feeds:FeedItem id="metricNativeValueAxisFeed" uid="size" type="Measure" values="Count" />
|
|
321
|
+
<viz.feeds:FeedItem id="metricNativeCategoryAxisFeed" uid="color" type="Dimension" values="Operation" />
|
|
322
|
+
</viz:feeds>
|
|
323
|
+
</viz:VizFrame>
|
|
324
|
+
</l:VerticalLayout>
|
|
325
|
+
</f:content>
|
|
326
|
+
<f:layoutData>
|
|
327
|
+
<f:GridContainerItemLayoutData columns="4" minRows="4"/>
|
|
328
|
+
</f:layoutData>
|
|
329
|
+
</f:Card>
|
|
330
|
+
</f:GridContainer>
|
|
331
|
+
</VBox>
|
|
332
|
+
|
|
333
|
+
</cssgrid:CSSGrid>
|
|
334
|
+
</f:content>
|
|
335
|
+
</f:DynamicPage>
|
|
336
|
+
|
|
337
|
+
</mvc:View>
|
package/cds-plugin.js
CHANGED
|
@@ -1,7 +1,59 @@
|
|
|
1
1
|
const cds = require('@sap/cds')
|
|
2
|
+
const { fs, path } = cds.utils;
|
|
2
3
|
const CachingService = require('./lib/CachingService')
|
|
3
4
|
const { scanCachingAnnotations } = require('./lib/util')
|
|
4
5
|
|
|
6
|
+
// Auto-register plugin entity models based on service configuration.
|
|
7
|
+
// Pushes absolute file paths (without extension) into cds.env.roots so that
|
|
8
|
+
// cds.resolve('*') picks them up during model compilation — before cds.model is set.
|
|
9
|
+
const cachingConfigs = Object.values(cds.env.requires ?? {}).filter(r => r?.impl === 'cds-caching');
|
|
10
|
+
if (cachingConfigs.some(c => c.store === 'cds')) {
|
|
11
|
+
cds.env.roots.push(path.join(__dirname, 'db', 'cache-store'));
|
|
12
|
+
}
|
|
13
|
+
if (cachingConfigs.some(c => c.statistics)) {
|
|
14
|
+
cds.env.roots.push(path.join(__dirname, 'db', 'statistics'));
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
cds.add?.register?.('caching-dashboard', require('./lib/add'))
|
|
18
|
+
|
|
5
19
|
cds.on('served', scanCachingAnnotations)
|
|
20
|
+
const LOG = cds.log("cds-caching");
|
|
21
|
+
|
|
22
|
+
// Register HANA build plugin to generate .hdbtable artifacts during `cds build`
|
|
23
|
+
cds.build?.register?.('cds-caching', class CachingBuildPlugin extends cds.build.Plugin {
|
|
24
|
+
static taskDefaults = { src: cds.env.folders.db }
|
|
25
|
+
|
|
26
|
+
init() { }
|
|
27
|
+
clean() { }
|
|
28
|
+
|
|
29
|
+
static hasTask() {
|
|
30
|
+
const requires = cds.env.requires || {};
|
|
31
|
+
return Object.values(requires).some(
|
|
32
|
+
r => r.impl === 'cds-caching' && r.store === 'hana'
|
|
33
|
+
);
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
async build() {
|
|
37
|
+
const requires = cds.env.requires || {};
|
|
38
|
+
for (const [, config] of Object.entries(requires)) {
|
|
39
|
+
if (config.impl === 'cds-caching' && config.store === 'hana') {
|
|
40
|
+
const table = config.credentials?.table || 'KEYV';
|
|
41
|
+
const keySize = config.credentials?.keySize || 255;
|
|
42
|
+
const content = [
|
|
43
|
+
`COLUMN TABLE "${table}" (`,
|
|
44
|
+
` "ID" NVARCHAR(${keySize}) PRIMARY KEY,`,
|
|
45
|
+
` "VALUE" NCLOB`,
|
|
46
|
+
`)`,
|
|
47
|
+
].join('\n');
|
|
48
|
+
await this.write(content).to(`src/gen/${table}.hdbtable`);
|
|
49
|
+
LOG.info('Building cds-caching hana table', { table, keySize });
|
|
50
|
+
}
|
|
51
|
+
// For store: 'cds' with HANA, the CacheStore entity from db/cache-store.cds
|
|
52
|
+
// is loaded via cds.env.roots and deployed automatically by the HDI deployer —
|
|
53
|
+
// no manual .hdbtable needed. For SQLite/Postgres, CAP's cds deploy handles
|
|
54
|
+
// table creation from the CDS model.
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
});
|
|
6
58
|
|
|
7
59
|
module.exports = cds.service.impl(CachingService)
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
namespace plugin.cds_caching;
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Key-value store for the CDS cache adapter (`store: 'cds'`).
|
|
5
|
+
* Uses CAP's managed DB connection — works with HANA, PostgreSQL, SQLite.
|
|
6
|
+
* In MTX mode, each tenant's HDI container gets its own table automatically.
|
|
7
|
+
*/
|
|
8
|
+
entity CacheStore {
|
|
9
|
+
key ID : String(900);
|
|
10
|
+
value : LargeString;
|
|
11
|
+
expiresAt : Int64;
|
|
12
|
+
}
|
|
@@ -0,0 +1,128 @@
|
|
|
1
|
+
namespace plugin.cds_caching;
|
|
2
|
+
|
|
3
|
+
entity Caches {
|
|
4
|
+
key name : String;
|
|
5
|
+
config : String;
|
|
6
|
+
metricsEnabled : Boolean default false;
|
|
7
|
+
keyMetricsEnabled : Boolean default false;
|
|
8
|
+
metrics : Composition of many Metrics
|
|
9
|
+
on metrics.cache = $self.name;
|
|
10
|
+
keyMetrics : Composition of many KeyMetrics
|
|
11
|
+
on keyMetrics.cache = $self.name;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
entity Metrics {
|
|
15
|
+
key ID : String; // e.g., 'daily:2024-03-20' or 'hourly:2024-03-20-15'
|
|
16
|
+
key cache : String;
|
|
17
|
+
timestamp : DateTime;
|
|
18
|
+
period : String enum {
|
|
19
|
+
hourly;
|
|
20
|
+
daily;
|
|
21
|
+
monthly;
|
|
22
|
+
}; // Granularity
|
|
23
|
+
|
|
24
|
+
// Read-through metrics (hits and misses only)
|
|
25
|
+
hits : Integer default 0;
|
|
26
|
+
misses : Integer default 0;
|
|
27
|
+
errors : Integer default 0;
|
|
28
|
+
totalRequests : Integer default 0;
|
|
29
|
+
// Read-through latency metrics
|
|
30
|
+
avgHitLatency : Double; // average hit latency in milliseconds
|
|
31
|
+
minHitLatency : Double; // minimum hit latency
|
|
32
|
+
maxHitLatency : Double; // maximum hit latency
|
|
33
|
+
avgMissLatency : Double; // average miss latency in milliseconds
|
|
34
|
+
minMissLatency : Double; // minimum miss latency
|
|
35
|
+
maxMissLatency : Double; // maximum miss latency
|
|
36
|
+
avgReadThroughLatency : Double; // average read through latency in milliseconds
|
|
37
|
+
|
|
38
|
+
// Read-through performance metrics
|
|
39
|
+
hitRatio : Double; // hit ratio as percentage
|
|
40
|
+
throughput : Double; // requests per second
|
|
41
|
+
errorRate : Double; // error rate as percentage
|
|
42
|
+
cacheEfficiency : Double; // ratio of miss latency to hit latency
|
|
43
|
+
|
|
44
|
+
// Native function metrics (basic counts only)
|
|
45
|
+
nativeSets : Integer default 0;
|
|
46
|
+
nativeGets : Integer default 0;
|
|
47
|
+
nativeDeletes : Integer default 0;
|
|
48
|
+
nativeClears : Integer default 0;
|
|
49
|
+
nativeDeleteByTags : Integer default 0;
|
|
50
|
+
nativeErrors : Integer default 0;
|
|
51
|
+
totalNativeOperations : Integer default 0;
|
|
52
|
+
// Native function performance metrics
|
|
53
|
+
nativeThroughput : Double; // native operations per second
|
|
54
|
+
nativeErrorRate : Double; // native operation error rate
|
|
55
|
+
|
|
56
|
+
// Common metrics
|
|
57
|
+
memoryUsage : Integer; // in bytes
|
|
58
|
+
itemCount : Integer;
|
|
59
|
+
uptimeMs : Integer; // uptime in milliseconds
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
entity KeyMetrics {
|
|
63
|
+
key ID : String;
|
|
64
|
+
key cache : String;
|
|
65
|
+
key keyName : String;
|
|
66
|
+
lastAccess : DateTime;
|
|
67
|
+
period : String enum {
|
|
68
|
+
current;
|
|
69
|
+
hourly;
|
|
70
|
+
daily;
|
|
71
|
+
};
|
|
72
|
+
|
|
73
|
+
// Operation type tracking
|
|
74
|
+
operationType : String enum {
|
|
75
|
+
read_through;
|
|
76
|
+
native;
|
|
77
|
+
mixed;
|
|
78
|
+
};
|
|
79
|
+
|
|
80
|
+
// Read-through metrics (hits and misses only)
|
|
81
|
+
hits : Integer default 0;
|
|
82
|
+
misses : Integer default 0;
|
|
83
|
+
errors : Integer default 0;
|
|
84
|
+
totalRequests : Integer default 0;
|
|
85
|
+
hitRatio : Double; // hit ratio as percentage
|
|
86
|
+
cacheEfficiency : Double; // ratio of miss latency to hit latency
|
|
87
|
+
|
|
88
|
+
// Read-through latency metrics
|
|
89
|
+
avgHitLatency : Double; // average hit latency in milliseconds
|
|
90
|
+
minHitLatency : Double; // minimum hit latency
|
|
91
|
+
maxHitLatency : Double; // maximum hit latency
|
|
92
|
+
avgMissLatency : Double; // average miss latency in milliseconds
|
|
93
|
+
minMissLatency : Double; // minimum miss latency
|
|
94
|
+
maxMissLatency : Double; // maximum miss latency
|
|
95
|
+
avgReadThroughLatency : Double; // average read through latency in milliseconds
|
|
96
|
+
|
|
97
|
+
// Read-through performance metrics
|
|
98
|
+
throughput : Double; // requests per second
|
|
99
|
+
errorRate : Double; // error rate as percentage
|
|
100
|
+
|
|
101
|
+
// Native function metrics (counts only)
|
|
102
|
+
nativeHits : Integer default 0;
|
|
103
|
+
nativeMisses : Integer default 0;
|
|
104
|
+
nativeSets : Integer default 0;
|
|
105
|
+
nativeDeletes : Integer default 0;
|
|
106
|
+
nativeClears : Integer default 0;
|
|
107
|
+
nativeDeleteByTags : Integer default 0;
|
|
108
|
+
nativeErrors : Integer default 0;
|
|
109
|
+
totalNativeOperations : Integer default 0;
|
|
110
|
+
// Native function performance metrics
|
|
111
|
+
nativeThroughput : Double; // native operations per second
|
|
112
|
+
nativeErrorRate : Double; // native operation error rate
|
|
113
|
+
|
|
114
|
+
// Metadata fields
|
|
115
|
+
dataType : String;
|
|
116
|
+
operation : String; // concatenated cache service operations (e.g., SET, GET, WRAP, RUN...)
|
|
117
|
+
metadata : LargeString; // JSON string for additional metadata
|
|
118
|
+
// Enhanced context information
|
|
119
|
+
context : String; // JSON string with detailed context
|
|
120
|
+
query : LargeString; // CQL query text if applicable
|
|
121
|
+
subject : LargeString; // JSON string with subject information
|
|
122
|
+
target : String; // Target information
|
|
123
|
+
tenant : String; // Tenant information
|
|
124
|
+
user : String; // User information
|
|
125
|
+
locale : String; // Locale information
|
|
126
|
+
timestamp : DateTime; // When this key was first accessed
|
|
127
|
+
cacheOptions : String; // JSON string with cache options
|
|
128
|
+
}
|