Investigation: mod-orders/mod-orders-storage performance

1. Performance testing methods (general information)

Performance testing methods for mod-orders-storage should cover common operations: search (by one/several* fields), filtering (by one/several* fields), filtering & sorting (by one/several* fields). Corresponding endpoints for GET requests for covering common operations are presented in the Table 1.1. JMeter script for measurement performance markers can be founded here mod-orders-storage-perf-scenario.zip. Default script configuration designed to perform 1K requests in one thread for each endpoint from Table 1.1. This configuration can be simple improved/changed in future to obtain more precise performance markers.

Table 1.1 - Endpoints for reference GET requests

Search
/orders-storage/purchase-orders?query=poNumber==${poNumber}Searching purchase order/po-line records by unique value for both direct endpoints and po+po-line view. One need manually set poNumber/poLineNumber value whenever possible in the middle of the range of corresponding values. 
/orders-storage/orders?query=poNumber==${poNumber}
/orders-storage/po-lines?query=poLineNumber==${poLineNumber}
/orders-storage/order-lines?query=poLineNumber==${poLineNumber}
Filtering
/orders-storage/purchase-orders?query=workflowStatus==OpenFiltering purchase order/po-line records by multiple same value for both direct endpoints and po+po-line view. 
/orders-storage/orders?query=workflowStatus==Open
/orders-storage/po-lines?query=orderFormat==Physical Resource
/orders-storage/order-lines?query=orderFormat==Physical Resource
Sorting
/orders-storage/orders?query=workflowStatus==Open AND orderFormat=Physical Resource sortBy metadata.createdDate/sort.ascendingFiltering and sorting purchase order/po-line records by multiple same value for po+po-line view. 
/orders-storage/order-lines?query=workflowStatus==Open AND orderFormat=Physical Resource sortBy metadata.createdDate/sort.ascending


At the present moment searching and filtering performance testing implemented only for one field. It is interesting to add scenarios for several fields searching/filtering.


2. Indexes

2.1 General information

Current RMB implementation contains some PostgreSQL indexes. Correspondence between RMB indexes and standard PostgreSQL indexes is presented in the Table 2.1.

Table 2.1 - Correspondence between RMB indexes and standard PostgreSQL indexes

PostgreSQL indexDescriptionRMB index typeRMB-generated snippetFolio schemas application area
B-treeB-trees fit the most common situations. B-trees can handle equality and range queries on data that can be sorted into some ordering.indexCREATE INDEX IF NOT EXISTS ${table.tableName}_${indexes.fieldName}_idx ON ${myuniversity}_${mymodule}.${table.tableName}B-trees can handle equality and range queries on data that can be sorted into some ordering. This is most common case.
uniqueIndexCREATE UNIQUE INDEX IF NOT EXISTS ${table.tableName}_${indexes.fieldName}_idx_unique ON ${myuniversity}_${mymodule}.${table.tableName}
( ${indexes.fieldPath} )

Unique indexes. This should be used for unique fields, for example, poNumber.

*Currently, only B-tree indexes can be declared unique https://www.postgresql.org/docs/11/indexes-unique.html.

likeIndexCREATE INDEX IF NOT EXISTS ${table.tableName}_${indexes.fieldName}_idx_like ON ${myuniversity}_${mymodule}.${table.tableName}
((${indexes.fieldPath}) text_pattern_ops)
Due to text_pattern_ops the values are compared strictly character by character rather than according to the locale-specific collation rules. This makes these operator classes suitable for use by queries involving pattern matching expressions.
HashHash indexes can only handle simple equality comparisons. The query planner will consider using a hash index whenever an indexed column is involved in a comparison using the = operator.Not supported

GiSTGiST indexes are not a single kind of index, but rather an infrastructure within which many different indexing strategies can be implemented. The standard distribution of PostgreSQL includes GiST operator classes for several two-dimensional geometric data types, which support indexed queries using these operators: <<, &<, &>, >>, <<|, &<|, |&>, |>>, @>, <@, ~=, &&. GiST indexes are also capable of optimizing “nearest-neighbor” searches.Not supported

SP-GiSTSP-GiST indexes, like GiST indexes, offer an infrastructure that supports various kinds of searches. SP-GiST permits implementation of a wide range of different non-balanced disk-based data structures, such as quadtrees, k-d trees, and radix trees (tries). As an example, the standard distribution of PostgreSQL includes SP-GiST operator classes for two-dimensional points, which support indexed queries using these operators: <<, >>, ~=, <@, <^, >^.Not supported

GINGIN indexes are “inverted indexes” which are appropriate for data values that contain multiple component values, such as arrays. An inverted index contains a separate entry for each component value, and can efficiently handle queries that test for the presence of specific component values.ginIndexCREATE INDEX IF NOT EXISTS ${table}_${field}_idx_gin ON ${tenant}_${module}.${table} USING GIN ((${indexes.fieldPath}) gin_trgm_ops)

The gin_trgm_ops module provides GIN index operator classes that allows to create an index over a text column for the purpose of very fast similarity searches. These indexes do not support equality nor simple comparison operators.

fullTextIndexCREATE INDEX IF NOT EXISTS ${table}_${indexes.fieldName}_idx_ft ON ${myuniversity}_${mymodule}.${table.tableName} USING GIN
( to_tsvector('${ft_defaultDictionary}', ${indexes.fieldPath}) );
This index can be used for used for full text searching, for example, comments, notes, etc. by using function to_tsvector that converts a document to the tsvector data type.
BRINBRIN indexes (a shorthand for Block Range INdexes) store summaries about the values stored in consecutive physical block ranges of a table.Not supported


2.2 Purchase order, purchase order line, piece indexes

In scope of  MODORDSTOR-85 - Getting issue details... STATUS  the following indexes were introduced for PO, PO Line, Piece (Table 2.2)

Table 2.2 - Correspondence between RMB indexes and PO, PO Line, Piece fields

RMB indexField

indexPO

assignedTo, metadata.createdDate, metadata.createdByUserId, workflowStatus, approved, dateOrdered, orderType, reEncumber, renewal.manualRenewal, renewal.renewalDate, renewal.reviewPeriod

PO Lineeresource.accessProvider, eresource.activated, eresource.expectedActivation, eresource.trial, eresource.materialType, details.subscriptionFrom, details.subscriptionTo, location.locationId, physical.receiptDue, physical.expectedReceiptDate, physical.volumes, physical.materialType, claim.grace, claim.sent, collection, receiptStatus, paymentStatus, orderFormat,
receiptDate, metadata.createdDate, rush, source
Pieceformat, itemId, locationId, poLineId, receivingStatus, receivedDate, supplement
uniqueIndexPOpoNumber
PO Line-
Piece-
ginIndexPO-
PO LinefundDistribution.code, details.productIds, title, vendor.vendorAccount, vendor.refNumber, tags, contributors, poLineNumber, requester
Piececaption
fullTextIndexPOcloseReason.reason, closeReason.note
PO Linepublisher, donor, selector
Piececomment, caption

Indexes can be verified by using query:

SELECT
    tablename,
    indexname,
    indexdef
FROM
    pg_indexes
WHERE
    schemaname = 'diku_mod_orders_storage'
ORDER BY
    tablename,
    indexname;
 Output
tablenameindexnameindexdef
acquisitions_unitsacquisitions_units_pkey

CREATE UNIQUE INDEX acquisitions_units_pkey ON diku_mod_orders_storage.acquisitions_units USING btree (id)

alertalert_pkey

CREATE UNIQUE INDEX alert_pkey ON diku_mod_orders_storage.alert USING btree (id)

order_invoice_relationshiporder_invoice_relationship_pkey

CREATE UNIQUE INDEX order_invoice_relationship_pkey ON diku_mod_orders_storage.order_invoice_relationship USING btree (id)

piecespieces_caption_idx_gin

CREATE INDEX pieces_caption_idx_gin ON diku_mod_orders_storage.pieces USING gin (lower(f_unaccent((jsonb ->> 'caption'::text))) gin_trgm_ops)

piecespieces_comment_idx_ft

CREATE INDEX pieces_comment_idx_ft ON diku_mod_orders_storage.pieces USING gin (to_tsvector('english'::regconfig, (jsonb ->> 'comment'::text)))

piecespieces_format_idx

CREATE INDEX pieces_format_idx ON diku_mod_orders_storage.pieces USING btree (lower(f_unaccent((jsonb ->> 'format'::text))))

piecespieces_itemid_idx

CREATE INDEX pieces_itemid_idx ON diku_mod_orders_storage.pieces USING btree (lower(f_unaccent((jsonb ->> 'itemId'::text))))

piecespieces_locationid_idx

CREATE INDEX pieces_locationid_idx ON diku_mod_orders_storage.pieces USING btree (lower(f_unaccent((jsonb ->> 'locationId'::text))))

piecespieces_pkey

CREATE UNIQUE INDEX pieces_pkey ON diku_mod_orders_storage.pieces USING btree (id)

piecespieces_polineid_idx

CREATE INDEX pieces_polineid_idx ON diku_mod_orders_storage.pieces USING btree (lower(f_unaccent((jsonb ->> 'poLineId'::text))))

piecespieces_receiveddate_idx

CREATE INDEX pieces_receiveddate_idx ON diku_mod_orders_storage.pieces USING btree (lower(f_unaccent((jsonb ->> 'receivedDate'::text))))

piecespieces_receivingstatus_idx

CREATE INDEX pieces_receivingstatus_idx ON diku_mod_orders_storage.pieces USING btree (lower(f_unaccent((jsonb ->> 'receivingStatus'::text))))

piecespieces_supplement_idx

CREATE INDEX pieces_supplement_idx ON diku_mod_orders_storage.pieces USING btree (lower(f_unaccent((jsonb ->> 'supplement'::text))))

po_linepo_line_claim_grace_idx

CREATE INDEX po_line_claim_grace_idx ON diku_mod_orders_storage.po_line USING btree (lower(f_unaccent(((jsonb -> 'claim'::text) ->> 'grace'::text))))

po_linepo_line_claim_sent_idx

CREATE INDEX po_line_claim_sent_idx ON diku_mod_orders_storage.po_line USING btree (lower(f_unaccent(((jsonb -> 'claim'::text) ->> 'sent'::text))))

po_linepo_line_collection_idx

CREATE INDEX po_line_collection_idx ON diku_mod_orders_storage.po_line USING btree (lower(f_unaccent((jsonb ->> 'collection'::text))))

po_linepo_line_contributors_idx_gin

CREATE INDEX po_line_contributors_idx_gin ON diku_mod_orders_storage.po_line USING gin (lower(f_unaccent((jsonb ->> 'contributors'::text))) gin_trgm_ops)

po_linepo_line_details_productids_idx_gin

CREATE INDEX po_line_details_productids_idx_gin ON diku_mod_orders_storage.po_line USING gin (lower(f_unaccent(((jsonb -> 'details'::text) ->> 'productIds'::text))) gin_trgm_ops)

po_linepo_line_details_subscriptionfrom_idx

CREATE INDEX po_line_details_subscriptionfrom_idx ON diku_mod_orders_storage.po_line USING btree (lower(f_unaccent(((jsonb -> 'details'::text) ->> 'subscriptionFrom'::text))))

po_linepo_line_details_subscriptionto_idx

CREATE INDEX po_line_details_subscriptionto_idx ON diku_mod_orders_storage.po_line USING btree (lower(f_unaccent(((jsonb -> 'details'::text) ->> 'subscriptionTo'::text))))

po_linepo_line_donor_idx_ft

CREATE INDEX po_line_donor_idx_ft ON diku_mod_orders_storage.po_line USING gin (to_tsvector('english'::regconfig, (jsonb ->> 'donor'::text)))

po_linepo_line_eresource_accessprovider_idx

CREATE INDEX po_line_eresource_accessprovider_idx ON diku_mod_orders_storage.po_line USING btree (lower(f_unaccent(((jsonb -> 'eresource'::text) ->> 'accessProvider'::text))))

po_linepo_line_eresource_activated_idx

CREATE INDEX po_line_eresource_activated_idx ON diku_mod_orders_storage.po_line USING btree (lower(f_unaccent(((jsonb -> 'eresource'::text) ->> 'activated'::text))))

po_linepo_line_eresource_expectedactivation_idx

CREATE INDEX po_line_eresource_expectedactivation_idx ON diku_mod_orders_storage.po_line USING btree (lower(f_unaccent(((jsonb -> 'eresource'::text) ->> 'expectedActivation'::text))))

po_linepo_line_eresource_materialtype_idx

CREATE INDEX po_line_eresource_materialtype_idx ON diku_mod_orders_storage.po_line USING btree (lower(f_unaccent(((jsonb -> 'eresource'::text) ->> 'materialType'::text))))

po_linepo_line_eresource_trial_idx

CREATE INDEX po_line_eresource_trial_idx ON diku_mod_orders_storage.po_line USING btree (lower(f_unaccent(((jsonb -> 'eresource'::text) ->> 'trial'::text))))

po_linepo_line_funddistribution_code_idx_gin

CREATE INDEX po_line_funddistribution_code_idx_gin ON diku_mod_orders_storage.po_line USING gin (lower(f_unaccent(((jsonb -> 'fundDistribution'::text) ->> 'code'::text))) gin_trgm_ops)

po_linepo_line_location_locationid_idx

CREATE INDEX po_line_location_locationid_idx ON diku_mod_orders_storage.po_line USING btree (lower(f_unaccent(((jsonb -> 'location'::text) ->> 'locationId'::text))))

po_linepo_line_metadata_createddate_idx

CREATE INDEX po_line_metadata_createddate_idx ON diku_mod_orders_storage.po_line USING btree (lower(f_unaccent(((jsonb -> 'metadata'::text) ->> 'createdDate'::text))))

po_linepo_line_orderformat_idx

CREATE INDEX po_line_orderformat_idx ON diku_mod_orders_storage.po_line USING btree (lower(f_unaccent((jsonb ->> 'orderFormat'::text))))

po_linepo_line_paymentstatus_idx

CREATE INDEX po_line_paymentstatus_idx ON diku_mod_orders_storage.po_line USING btree (lower(f_unaccent((jsonb ->> 'paymentStatus'::text))))

po_linepo_line_physical_expectedreceiptdate_idx

CREATE INDEX po_line_physical_expectedreceiptdate_idx ON diku_mod_orders_storage.po_line USING btree (lower(f_unaccent(((jsonb -> 'physical'::text) ->> 'expectedReceiptDate'::text))))

po_linepo_line_physical_materialtype_idx

CREATE INDEX po_line_physical_materialtype_idx ON diku_mod_orders_storage.po_line USING btree (lower(f_unaccent(((jsonb -> 'physical'::text) ->> 'materialType'::text))))

po_linepo_line_physical_receiptdue_idx

CREATE INDEX po_line_physical_receiptdue_idx ON diku_mod_orders_storage.po_line USING btree (lower(f_unaccent(((jsonb -> 'physical'::text) ->> 'receiptDue'::text))))

po_linepo_line_physical_volumes_idx

CREATE INDEX po_line_physical_volumes_idx ON diku_mod_orders_storage.po_line USING btree (lower(f_unaccent(((jsonb -> 'physical'::text) ->> 'volumes'::text))))

po_linepo_line_pkey

CREATE UNIQUE INDEX po_line_pkey ON diku_mod_orders_storage.po_line USING btree (id)

po_linepo_line_polinenumber_idx_gin

CREATE INDEX po_line_polinenumber_idx_gin ON diku_mod_orders_storage.po_line USING gin (lower(f_unaccent((jsonb ->> 'poLineNumber'::text))) gin_trgm_ops)

po_linepo_line_publisher_idx_ft

CREATE INDEX po_line_publisher_idx_ft ON diku_mod_orders_storage.po_line USING gin (to_tsvector('english'::regconfig, (jsonb ->> 'publisher'::text)))

po_linepo_line_receiptdate_idx

CREATE INDEX po_line_receiptdate_idx ON diku_mod_orders_storage.po_line USING btree (lower(f_unaccent((jsonb ->> 'receiptDate'::text))))

po_linepo_line_receiptstatus_idx

CREATE INDEX po_line_receiptstatus_idx ON diku_mod_orders_storage.po_line USING btree (lower(f_unaccent((jsonb ->> 'receiptStatus'::text))))

po_linepo_line_requester_idx_gin

CREATE INDEX po_line_requester_idx_gin ON diku_mod_orders_storage.po_line USING gin (lower(f_unaccent((jsonb ->> 'requester'::text))) gin_trgm_ops)

po_linepo_line_rush_idx

CREATE INDEX po_line_rush_idx ON diku_mod_orders_storage.po_line USING btree (lower(f_unaccent((jsonb ->> 'rush'::text))))

po_linepo_line_selector_idx_ft

CREATE INDEX po_line_selector_idx_ft ON diku_mod_orders_storage.po_line USING gin (to_tsvector('english'::regconfig, (jsonb ->> 'selector'::text)))

po_linepo_line_source_idx

CREATE INDEX po_line_source_idx ON diku_mod_orders_storage.po_line USING btree (lower(f_unaccent((jsonb ->> 'source'::text))))

po_linepo_line_tags_idx_gin

CREATE INDEX po_line_tags_idx_gin ON diku_mod_orders_storage.po_line USING gin (lower(f_unaccent((jsonb ->> 'tags'::text))) gin_trgm_ops)

po_linepo_line_title_idx_gin

CREATE INDEX po_line_title_idx_gin ON diku_mod_orders_storage.po_line USING gin (lower(f_unaccent((jsonb ->> 'title'::text))) gin_trgm_ops)

po_linepo_line_vendor_refnumber_idx_gin

CREATE INDEX po_line_vendor_refnumber_idx_gin ON diku_mod_orders_storage.po_line USING gin (lower(f_unaccent(((jsonb -> 'vendor'::text) ->> 'refNumber'::text))) gin_trgm_ops)

po_linepo_line_vendor_vendoraccount_idx_gin

CREATE INDEX po_line_vendor_vendoraccount_idx_gin ON diku_mod_orders_storage.po_line USING gin (lower(f_unaccent(((jsonb -> 'vendor'::text) ->> 'vendorAccount'::text))) gin_trgm_ops)

purchase_orderpurchase_order_approved_idx

CREATE INDEX purchase_order_approved_idx ON diku_mod_orders_storage.purchase_order USING btree (lower(f_unaccent((jsonb ->> 'approved'::text))))

purchase_orderpurchase_order_assignedto_idx

CREATE INDEX purchase_order_assignedto_idx ON diku_mod_orders_storage.purchase_order USING btree (lower(f_unaccent((jsonb ->> 'assignedTo'::text))))

purchase_orderpurchase_order_closereason_note_idx_ft

CREATE INDEX purchase_order_closereason_note_idx_ft ON diku_mod_orders_storage.purchase_order USING gin (to_tsvector('english'::regconfig, ((jsonb -> 'closeReason'::text) ->> 'note'::text)))

purchase_orderpurchase_order_closereason_reason_idx_ft

CREATE INDEX purchase_order_closereason_reason_idx_ft ON diku_mod_orders_storage.purchase_order USING gin (to_tsvector('english'::regconfig, ((jsonb -> 'closeReason'::text) ->> 'reason'::text)))

purchase_orderpurchase_order_dateordered_idx

CREATE INDEX purchase_order_dateordered_idx ON diku_mod_orders_storage.purchase_order USING btree (lower(f_unaccent((jsonb ->> 'dateOrdered'::text))))

purchase_orderpurchase_order_metadata_createdbyuserid_idx

CREATE INDEX purchase_order_metadata_createdbyuserid_idx ON diku_mod_orders_storage.purchase_order USING btree (lower(f_unaccent(((jsonb -> 'metadata'::text) ->> 'createdByUserId'::text))))

purchase_orderpurchase_order_metadata_createddate_idx

CREATE INDEX purchase_order_metadata_createddate_idx ON diku_mod_orders_storage.purchase_order USING btree (lower(f_unaccent(((jsonb -> 'metadata'::text) ->> 'createdDate'::text))))

purchase_orderpurchase_order_ordertype_idx

CREATE INDEX purchase_order_ordertype_idx ON diku_mod_orders_storage.purchase_order USING btree (lower(f_unaccent((jsonb ->> 'orderType'::text))))

purchase_orderpurchase_order_pkey

CREATE UNIQUE INDEX purchase_order_pkey ON diku_mod_orders_storage.purchase_order USING btree (id)

purchase_orderpurchase_order_po_number_unique_idx

CREATE UNIQUE INDEX purchase_order_po_number_unique_idx ON diku_mod_orders_storage.purchase_order USING btree (((jsonb ->> 'poNumber'::text)))

purchase_orderpurchase_order_reencumber_idx

CREATE INDEX purchase_order_reencumber_idx ON diku_mod_orders_storage.purchase_order USING btree (lower(f_unaccent((jsonb ->> 'reEncumber'::text))))

purchase_orderpurchase_order_renewal_manualrenewal_idx

CREATE INDEX purchase_order_renewal_manualrenewal_idx ON diku_mod_orders_storage.purchase_order USING btree (lower(f_unaccent(((jsonb -> 'renewal'::text) ->> 'manualRenewal'::text))))

purchase_orderpurchase_order_renewal_renewaldate_idx

CREATE INDEX purchase_order_renewal_renewaldate_idx ON diku_mod_orders_storage.purchase_order USING btree (lower(f_unaccent(((jsonb -> 'renewal'::text) ->> 'renewalDate'::text))))

purchase_orderpurchase_order_renewal_reviewperiod_idx

CREATE INDEX purchase_order_renewal_reviewperiod_idx ON diku_mod_orders_storage.purchase_order USING btree (lower(f_unaccent(((jsonb -> 'renewal'::text) ->> 'reviewPeriod'::text))))

purchase_orderpurchase_order_workflowstatus_idx

CREATE INDEX purchase_order_workflowstatus_idx ON diku_mod_orders_storage.purchase_order USING btree (lower(f_unaccent((jsonb ->> 'workflowStatus'::text))))

reporting_codereporting_code_pkey

CREATE UNIQUE INDEX reporting_code_pkey ON diku_mod_orders_storage.reporting_code USING btree (id)

rmb_internalrmb_internal_pkey

CREATE UNIQUE INDEX rmb_internal_pkey ON diku_mod_orders_storage.rmb_internal USING btree (id)

2.3 Performance testing results

Since there is currently no reference environment for measuring performance markers, it is difficult to obtain a time trend for their values. Therefore, it makes sense to compare values before/after critical changes, however, measured on the same environment. Performance testing results (Table 2.3) was measured and averaged over 1000 iterations for requests from Table 1.1 based on sample data generated with using data loader executed with configuration:

number of records (po + po_line) : 5000

orders distribution: open (50%), pending (20%), closed (30%);

order lines distribution:  physical (50%), electronic (20%), p/e_mix (30%).


Table 2.3 - Performance testing results (from JMeter)

Operation

Average, ms

Median, ms

90% Line, ms

95% Line, ms

99% Line, ms

Min, ms

Max, ms

Throughput, op/sec

Non-indexed version

Search : Orders (purchase-orders)

25

23

33

37

58

18

82

0.37987

Search : Orders (orders)

518

511

541

557

638

484

1230

0.37980

Search : Order-Lines (po-lines)

91

89

100

106

128

82

186

0.37988

Search : Order-Lines (order-lines)

303

299

321

336

388

280

499

0.37985

Filtering : Orders (purchase-orders)

22

21

27

31

38

18

76

0.37990

Filtering : Orders (orders)

387

383

408

416

477

360

683

0.37985

Filtering : Order-Lines (po-lines)

74

73

83

87

102

67

129

0.37990

Filtering : Order-Lines (order-lines)

204

201

220

230

255

187

328

0.37989

Sorting : Orders

511

505

536

549

629

467

932

0.37985

Sorting : Order-Lines

493

463

490

508

596

433

13357

0.37985

Indexed version

Search : Orders (purchase-orders)

26

25

35

39

62

19

112

0.44100

Search : Orders (orders)

315

308

330

367

437

291

667

0.44095

Search : Order-Lines (po-lines)

76

73

85

93

114

69

237

0.44101

Search : Order-Lines (order-lines)

289

283

305

334

401

268

545

0.44097

Filtering : Orders (purchase-orders)

17

16

23

25

30

14

69

0.44104

Filtering : Orders (orders)

393

385

412

460

534

367

649

0.44097

Filtering : Order-Lines (po-lines)

47

45

55

59

70

42

106

0.44103

Filtering : Order-Lines (order-lines)

177

173

192

203

244

163

339

0.44101

Sorting : Orders

480

471

502

547

660

450

818

0.44096

Sorting : Order-Lines

434

424

460

505

611

402

733

0.44097

Comparison of the non-indexed and indexed version is presented in Table 2.4.

Table 2.4 - Average time decreasing (positive values are expected - time should be decreasing), throughput increasing (positive values are expected - throughput should be increasing)

OperationAverage time decreasing, %Throughput increasing, %

Search : Orders (purchase-orders)

-416,09

Search : Orders (orders)

3916,10

Search : Order-Lines (po-lines)

1616,09

Search : Order-Lines (order-lines)

516,09

Filtering : Orders (purchase-orders)

2316,09

Filtering : Orders (orders)

-216,09

Filtering : Order-Lines (po-lines)

3616,09

Filtering : Order-Lines (order-lines)

1316,09

Sorting : Orders

616,09

Sorting : Order-Lines

1216,09


2.4 Indexes: summary

The Table 2.4 demonstrates that there is a general trend of decreasing the average request time for most endpoints from Table 1.1. For some types of requests (standard order searching and orders filtering by view) the changes are slightly negative, indicating a degradation of performance after the introduction of indexes. However, the magnitude of the decrease is small enough to talk about performance degradation. In general, one can conclude that the introduction of indexes for the fields has a positive effect on the performance of the mod-orders-storage. The throughput for all request types increased by about 16% which also indicates an improvement in performance due to the introduction of indexes.

3. Performance analysis

3.1 Reference testing methodology

3.1.1 Methodology

Open system load.

Virtual users arrive in a certain amount at equal time intervals and cyclically perform the sequence of operations C-U-R-D. This load mode corresponds to an open system. Open system have no control over the number concurrent users: users keep on arriving even though applications has some trouble serving them. This load mode allows to study the dependence of the response time of the system depending on the number of clients/number of active requests in the conditions of a real service workload and peak service throughtput for which service fails.

Constant load for a fixed time / number of iterations.

Such a load scenario implies the cyclic execution of sequential operations C-U-R-D for virtual users, the number of which is constant during the test. This load scenario is used for the main purpose - measuring the throughput of services in the case of one (or several) request flows. Additionally, it can be used to check the stability of the system over time when the service is used continuously for a long time.

3.1.2 JMeter script

In order to analyze performance changes one need to have a reference script that allows to cover scenarios  that are close to the actual module operating. Among these scenarios the most universal are CRUD operations and Searching/Filtering/Sorting. At the same time it is important to analyze the performance of the system for parallel requests from different clients. JMeter script has been developed for this purposes: https://github.com/folio-org/folio-perf-test/tree/mod-orders-performance-test/Folio-Test-Plans/mod-orders/performance-test.

At the moment it contains the following scenarious:

  • CRUD operations (cyclic creating, updating, reading and deleting) for order and order-line;
  • Searching/Filtering/Sorting for orders/order-lines by query.

In accordance with JMeter ramp-up period feature this script allows to execute scenarious cyclically with increasing the number of clients up to num_of_threads every ramp_up_period (in seconds) during test_duration (in seconds). This means for typical configuration num_of_threads=20, ramp_up_period=1200 and during test_duration=1200 that each new thread will be started in 1200/20 = 60 s and all test execution will be finished after 1200 s. It is important that test_duration  >= ramp_up_period so that all threads are created during the script execution.

3.1.3 Gatling tool

3.2 Results analysis

TBD

4. Performance-related stories list

MODORDSTOR-85 - Getting issue details... STATUS  : PO, PO Line, Piece fields indexes introduced