github.com/kaleido-io/firefly@v0.0.0-20210622132723-8b4b6aacb971/pkg/database/plugin.go (about) 1 // Copyright © 2021 Kaleido, Inc. 2 // 3 // SPDX-License-Identifier: Apache-2.0 4 // 5 // Licensed under the Apache License, Version 2.0 (the "License"); 6 // you may not use this file except in compliance with the License. 7 // You may obtain a copy of the License at 8 // 9 // http://www.apache.org/licenses/LICENSE-2.0 10 // 11 // Unless required by applicable law or agreed to in writing, software 12 // distributed under the License is distributed on an "AS IS" BASIS, 13 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 // See the License for the specific language governing permissions and 15 // limitations under the License. 16 17 package database 18 19 import ( 20 "context" 21 22 "github.com/kaleido-io/firefly/internal/config" 23 "github.com/kaleido-io/firefly/internal/i18n" 24 "github.com/kaleido-io/firefly/pkg/fftypes" 25 ) 26 27 var ( 28 // HashMismatch sentinel error 29 HashMismatch = i18n.NewError(context.Background(), i18n.MsgHashMismatch) 30 // IDMismatch sentinel error 31 IDMismatch = i18n.NewError(context.Background(), i18n.MsgIDMismatch) 32 // DeleteRecordNotFound sentinel error 33 DeleteRecordNotFound = i18n.NewError(context.Background(), i18n.Msg404NotFound) 34 ) 35 36 // Plugin is the interface implemented by each plugin 37 type Plugin interface { 38 PeristenceInterface // Split out to aid pluggability the next level down (SQL provider etc.) 39 40 // InitPrefix initializes the set of configuration options that are valid, with defaults. Called on all plugins. 41 InitPrefix(prefix config.Prefix) 42 43 // Init initializes the plugin, with configuration 44 // Returns the supported featureset of the interface 45 Init(ctx context.Context, prefix config.Prefix, callbacks Callbacks) error 46 47 // Capabilities returns capabilities - not called until after Init 48 Capabilities() *Capabilities 49 } 50 51 // PeristenceInterface are the operations that must be implemented by a database interfavce plugin. 52 // The database mechanism of Firefly is designed to provide the balance between being able 53 // to query the data a member of the network has transferred/received via Firefly efficiently, 54 // while not trying to become the core database of the application (where full deeply nested 55 // rich query is needed). 56 // 57 // This means that we treat business data as opaque within the stroage, only verifying it against 58 // a data definition within the Firefly core runtime itself. 59 // The data types, indexes and relationships are designed to be simple, and map closely to the 60 // REST semantics of the Firefly API itself. 61 // 62 // As a result, the database interface could be implemented efficiently by most database technologies. 63 // Including both Relational/SQL and Document/NoSQL database technologies. 64 // 65 // As such we suggest the factors in choosing your database should be non-functional, such as: 66 // - Which provides you with the HA/DR capabilities you require 67 // - Which is most familiar within your existing devops pipeline for the application 68 // - Whether you can consolidate the HA/DR and server infrastructure for your app DB with the Firefly DB 69 // 70 // Each database does need an update to the core codebase, to provide a plugin that implements this 71 // interface. 72 // For SQL databases the process of adding a new database is simplified via the common SQL layer. 73 // For NoSQL databases, the code should be straight forward to map the collections, indexes, and operations. 74 // 75 type PeristenceInterface interface { 76 fftypes.Named 77 78 // RunAsGroup instructs the database plugin that all database operations performed within the context 79 // function can be grouped into a single transaction (if supported). 80 // Note, the caller is responsible for passing the context back to all database operations performed within the supplied function. 81 RunAsGroup(ctx context.Context, fn func(ctx context.Context) error) error 82 83 // UpsertNamespace - Upsert a namespace 84 // Throws IDMismatch error if updating and ids don't match 85 UpsertNamespace(ctx context.Context, data *fftypes.Namespace, allowExisting bool) (err error) 86 87 // UpdateNamespace - Update namespace 88 UpdateNamespace(ctx context.Context, id *fftypes.UUID, update Update) (err error) 89 90 // DeleteNamespace - Delete namespace 91 DeleteNamespace(ctx context.Context, id *fftypes.UUID) (err error) 92 93 // GetNamespace - Get an namespace by name 94 GetNamespace(ctx context.Context, name string) (offset *fftypes.Namespace, err error) 95 96 // GetNamespaces - Get namespaces 97 GetNamespaces(ctx context.Context, filter Filter) (offset []*fftypes.Namespace, err error) 98 99 // UpsertMessage - Upsert a message, with all the embedded data references. 100 // allowHashUpdate=false throws HashMismatch error if the updated message has a different hash 101 UpsertMessage(ctx context.Context, message *fftypes.Message, allowExisting, allowHashUpdate bool) (err error) 102 103 // InsertMessageLocal - sets a boolean flag on inserting a new message (cannot be an update) to state it is local. 104 // Only time this flag is ever set. Subsequent updates can affect other fields, but not the local flag. Important to stop infinite message propagation. 105 InsertMessageLocal(ctx context.Context, message *fftypes.Message) (err error) 106 107 // UpdateMessage - Update message 108 UpdateMessage(ctx context.Context, id *fftypes.UUID, update Update) (err error) 109 110 // UpdateMessages - Update messages 111 UpdateMessages(ctx context.Context, filter Filter, update Update) (err error) 112 113 // GetMessageByID - Get a message by ID 114 GetMessageByID(ctx context.Context, id *fftypes.UUID) (message *fftypes.Message, err error) 115 116 // GetMessages - List messages, reverse sorted (newest first) by Confirmed then Created, with pagination, and simple must filters 117 GetMessages(ctx context.Context, filter Filter) (message []*fftypes.Message, err error) 118 119 // GetMessageRefs - Lighter weight query to just get the reference info of messages 120 GetMessageRefs(ctx context.Context, filter Filter) ([]*fftypes.MessageRef, error) 121 122 // GetMessagesForData - List messages where there is a data reference to the specified ID 123 GetMessagesForData(ctx context.Context, dataID *fftypes.UUID, filter Filter) (message []*fftypes.Message, err error) 124 125 // UpsertData - Upsert a data record 126 // allowHashUpdate=false throws HashMismatch error if the updated message has a different hash 127 UpsertData(ctx context.Context, data *fftypes.Data, allowExisting, allowHashUpdate bool) (err error) 128 129 // UpdateData - Update data 130 UpdateData(ctx context.Context, id *fftypes.UUID, update Update) (err error) 131 132 // GetDataByID - Get a data record by ID 133 GetDataByID(ctx context.Context, id *fftypes.UUID, withValue bool) (message *fftypes.Data, err error) 134 135 // GetData - Get data 136 GetData(ctx context.Context, filter Filter) (message []*fftypes.Data, err error) 137 138 // GetDataRefs - Get data references only (no data) 139 GetDataRefs(ctx context.Context, filter Filter) (message fftypes.DataRefs, err error) 140 141 // UpsertBatch - Upsert a batch 142 // allowHashUpdate=false throws HashMismatch error if the updated message has a different hash 143 UpsertBatch(ctx context.Context, data *fftypes.Batch, allowExisting, allowHashUpdate bool) (err error) 144 145 // UpdateBatch - Update data 146 UpdateBatch(ctx context.Context, id *fftypes.UUID, update Update) (err error) 147 148 // GetBatchByID - Get a batch by ID 149 GetBatchByID(ctx context.Context, id *fftypes.UUID) (message *fftypes.Batch, err error) 150 151 // GetBatches - Get batches 152 GetBatches(ctx context.Context, filter Filter) (message []*fftypes.Batch, err error) 153 154 // UpsertTransaction - Upsert a transaction 155 // allowHashUpdate=false throws HashMismatch error if the updated message has a different hash 156 UpsertTransaction(ctx context.Context, data *fftypes.Transaction, allowExisting, allowHashUpdate bool) (err error) 157 158 // UpdateTransaction - Update transaction 159 UpdateTransaction(ctx context.Context, id *fftypes.UUID, update Update) (err error) 160 161 // GetTransactionByID - Get a transaction by ID 162 GetTransactionByID(ctx context.Context, id *fftypes.UUID) (message *fftypes.Transaction, err error) 163 164 // GetTransactions - Get transactions 165 GetTransactions(ctx context.Context, filter Filter) (message []*fftypes.Transaction, err error) 166 167 // UpsertDatatype - Upsert a data definitino 168 UpsertDatatype(ctx context.Context, datadef *fftypes.Datatype, allowExisting bool) (err error) 169 170 // UpdateDatatype - Update data definition 171 UpdateDatatype(ctx context.Context, id *fftypes.UUID, update Update) (err error) 172 173 // GetDatatypeByID - Get a data definition by ID 174 GetDatatypeByID(ctx context.Context, id *fftypes.UUID) (datadef *fftypes.Datatype, err error) 175 176 // GetDatatypeByName - Get a data definition by name 177 GetDatatypeByName(ctx context.Context, ns, name, version string) (datadef *fftypes.Datatype, err error) 178 179 // GetDatatypes - Get data definitions 180 GetDatatypes(ctx context.Context, filter Filter) (datadef []*fftypes.Datatype, err error) 181 182 // UpsertOffset - Upsert an offset 183 UpsertOffset(ctx context.Context, data *fftypes.Offset, allowExisting bool) (err error) 184 185 // UpdateOffset - Update offset 186 UpdateOffset(ctx context.Context, id *fftypes.UUID, update Update) (err error) 187 188 // GetOffset - Get an offset by name 189 GetOffset(ctx context.Context, t fftypes.OffsetType, ns, name string) (offset *fftypes.Offset, err error) 190 191 // GetOffsets - Get offsets 192 GetOffsets(ctx context.Context, filter Filter) (offset []*fftypes.Offset, err error) 193 194 // DeleteOffset - Delete an offset by name 195 DeleteOffset(ctx context.Context, t fftypes.OffsetType, ns, name string) (err error) 196 197 // UpsertPin - Will insert a pin at the end of the sequence, unless the batch+hash+index sequence already exists 198 UpsertPin(ctx context.Context, parked *fftypes.Pin) (err error) 199 200 // GetPins - Get pins 201 GetPins(ctx context.Context, filter Filter) (offset []*fftypes.Pin, err error) 202 203 // SetPinDispatched - Set the dispatched flag to true on the specified pins 204 SetPinDispatched(ctx context.Context, sequence int64) (err error) 205 206 // DeletePin - Delete a pin 207 DeletePin(ctx context.Context, sequence int64) (err error) 208 209 // UpsertOperation - Upsert an operation 210 UpsertOperation(ctx context.Context, operation *fftypes.Operation, allowExisting bool) (err error) 211 212 // UpdateOperation - Update operation by ID 213 UpdateOperation(ctx context.Context, id *fftypes.UUID, update Update) (err error) 214 215 // GetOperationByID - Get an operation by ID 216 GetOperationByID(ctx context.Context, id *fftypes.UUID) (operation *fftypes.Operation, err error) 217 218 // GetOperations - Get operation 219 GetOperations(ctx context.Context, filter Filter) (operation []*fftypes.Operation, err error) 220 221 // UpsertSubscription - Upsert a subscription 222 UpsertSubscription(ctx context.Context, data *fftypes.Subscription, allowExisting bool) (err error) 223 224 // UpdateSubscription - Update subscription 225 // Throws IDMismatch error if updating and ids don't match 226 UpdateSubscription(ctx context.Context, ns, name string, update Update) (err error) 227 228 // GetSubscriptionByName - Get an subscription by name 229 GetSubscriptionByName(ctx context.Context, ns, name string) (offset *fftypes.Subscription, err error) 230 231 // GetSubscriptionByID - Get an subscription by id 232 GetSubscriptionByID(ctx context.Context, id *fftypes.UUID) (offset *fftypes.Subscription, err error) 233 234 // GetSubscriptions - Get subscriptions 235 GetSubscriptions(ctx context.Context, filter Filter) (offset []*fftypes.Subscription, err error) 236 237 // DeleteSubscriptionByID - Delete a subscription 238 DeleteSubscriptionByID(ctx context.Context, id *fftypes.UUID) (err error) 239 240 // UpsertEvent - Upsert an event 241 UpsertEvent(ctx context.Context, data *fftypes.Event, allowExisting bool) (err error) 242 243 // UpdateEvent - Update event 244 UpdateEvent(ctx context.Context, id *fftypes.UUID, update Update) (err error) 245 246 // GetEventByID - Get a event by ID 247 GetEventByID(ctx context.Context, id *fftypes.UUID) (message *fftypes.Event, err error) 248 249 // GetEvents - Get events 250 GetEvents(ctx context.Context, filter Filter) (message []*fftypes.Event, err error) 251 252 // UpsertOrganization - Upsert an organization 253 UpsertOrganization(ctx context.Context, data *fftypes.Organization, allowExisting bool) (err error) 254 255 // UpdateOrganization - Update organization 256 UpdateOrganization(ctx context.Context, id *fftypes.UUID, update Update) (err error) 257 258 // GetOrganizationByIdentity - Get a organization by identity 259 GetOrganizationByIdentity(ctx context.Context, identity string) (org *fftypes.Organization, err error) 260 261 // GetOrganizationByName - Get a organization by name 262 GetOrganizationByName(ctx context.Context, name string) (org *fftypes.Organization, err error) 263 264 // GetOrganizationByID - Get a organization by ID 265 GetOrganizationByID(ctx context.Context, id *fftypes.UUID) (org *fftypes.Organization, err error) 266 267 // GetOrganizations - Get organizations 268 GetOrganizations(ctx context.Context, filter Filter) (org []*fftypes.Organization, err error) 269 270 // UpsertNode - Upsert a node 271 UpsertNode(ctx context.Context, data *fftypes.Node, allowExisting bool) (err error) 272 273 // UpdateNode - Update node 274 UpdateNode(ctx context.Context, id *fftypes.UUID, update Update) (err error) 275 276 // GetNode - Get a node by ID 277 GetNode(ctx context.Context, owner, name string) (node *fftypes.Node, err error) 278 279 // GetNodeByID- Get a node by ID 280 GetNodeByID(ctx context.Context, id *fftypes.UUID) (node *fftypes.Node, err error) 281 282 // GetNodes - Get nodes 283 GetNodes(ctx context.Context, filter Filter) (node []*fftypes.Node, err error) 284 285 // UpserGroup - Upsert a group 286 UpsertGroup(ctx context.Context, data *fftypes.Group, allowExisting bool) (err error) 287 288 // UpdateGroup - Update group 289 UpdateGroup(ctx context.Context, hash *fftypes.Bytes32, update Update) (err error) 290 291 // GetGroupByHash - Get a group by ID 292 GetGroupByHash(ctx context.Context, hash *fftypes.Bytes32) (node *fftypes.Group, err error) 293 294 // GetGroups - Get groups 295 GetGroups(ctx context.Context, filter Filter) (node []*fftypes.Group, err error) 296 297 // UpsertNonceNext - Upsert a context, assigning zero if not found, or the next nonce if it is 298 UpsertNonceNext(ctx context.Context, context *fftypes.Nonce) (err error) 299 300 // GetNonce - Get a context by hash 301 GetNonce(ctx context.Context, hash *fftypes.Bytes32) (message *fftypes.Nonce, err error) 302 303 // GetNonces - Get contexts 304 GetNonces(ctx context.Context, filter Filter) (node []*fftypes.Nonce, err error) 305 306 // DeleteNonce - Delete context by hash 307 DeleteNonce(ctx context.Context, hash *fftypes.Bytes32) (err error) 308 309 // InsertNextPin - insert a nextpin 310 InsertNextPin(ctx context.Context, nextpin *fftypes.NextPin) (err error) 311 312 // GetNextPinByContextAndIdentity - lookup nextpin by context+identity 313 GetNextPinByContextAndIdentity(ctx context.Context, context *fftypes.Bytes32, identity string) (message *fftypes.NextPin, err error) 314 315 // GetNextPinByHash - lookup nextpin by its hash 316 GetNextPinByHash(ctx context.Context, hash *fftypes.Bytes32) (message *fftypes.NextPin, err error) 317 318 // GetNextPins - get nextpins 319 GetNextPins(ctx context.Context, filter Filter) (message []*fftypes.NextPin, err error) 320 321 // UpdateNextPin - update a next hash using its local database ID 322 UpdateNextPin(ctx context.Context, sequence int64, update Update) (err error) 323 324 // DeleteNextPin - delete a next hash, using its local database ID 325 DeleteNextPin(ctx context.Context, sequence int64) (err error) 326 } 327 328 // Callbacks are the methods for passing data from plugin to core 329 // 330 // If Capabilities returns ClusterEvents=true then these should be brodcast to every instance within 331 // a cluster that is connected to the database. 332 // 333 // If Capabilities returns ClusterEvents=false then these events can be simply coupled in-process to 334 // update activities. 335 // 336 // The system does not rely on these events exclusively for data/transaction integrity, but if an event is 337 // missed/delayed it might result in slower processing. 338 // For example, the batch interface will initiate a batch as soon as an event is triggered, but it will use 339 // a subsequent database query as the source of truth of the latest set/order of data, and it will periodically 340 // check for new messages even if it does not receive any events. 341 // 342 // TODO: Clarify the relationship between Leader Election capabilities and Event capabilities 343 // 344 type Callbacks interface { 345 MessageCreated(sequence int64) 346 PinCreated(sequence int64) 347 EventCreated(sequence int64) 348 SubscriptionCreated(id *fftypes.UUID) 349 SubscriptionDeleted(id *fftypes.UUID) 350 } 351 352 // Capabilities defines the capabilities a plugin can report as implementing or not 353 type Capabilities struct { 354 ClusterEvents bool 355 } 356 357 // NamespaceQueryFactory filter fields for namespaces 358 var NamespaceQueryFactory = &queryFields{ 359 "id": &UUIDField{}, 360 "message": &UUIDField{}, 361 "type": &StringField{}, 362 "name": &StringField{}, 363 "description": &StringField{}, 364 "created": &TimeField{}, 365 "confirmed": &TimeField{}, 366 } 367 368 // MessageQueryFactory filter fields for messages 369 var MessageQueryFactory = &queryFields{ 370 "id": &UUIDField{}, 371 "cid": &UUIDField{}, 372 "namespace": &StringField{}, 373 "type": &StringField{}, 374 "author": &StringField{}, 375 "topics": &FFNameArrayField{}, 376 "tag": &StringField{}, 377 "group": &StringField{}, 378 "created": &TimeField{}, 379 "hash": &StringField{}, 380 "pins": &StringField{}, 381 "confirmed": &TimeField{}, 382 "sequence": &Int64Field{}, 383 "tx.type": &StringField{}, 384 "batch": &UUIDField{}, 385 "local": &BoolField{}, 386 } 387 388 // BatchQueryFactory filter fields for batches 389 var BatchQueryFactory = &queryFields{ 390 "id": &UUIDField{}, 391 "namespace": &StringField{}, 392 "type": &StringField{}, 393 "author": &StringField{}, 394 "group": &StringField{}, 395 "hash": &StringField{}, 396 "payloadref": &StringField{}, 397 "created": &TimeField{}, 398 "confirmed": &TimeField{}, 399 "tx.type": &StringField{}, 400 "tx.id": &UUIDField{}, 401 } 402 403 // TransactionQueryFactory filter fields for transactions 404 var TransactionQueryFactory = &queryFields{ 405 "id": &UUIDField{}, 406 "type": &StringField{}, 407 "signer": &StringField{}, 408 "status": &StringField{}, 409 "reference": &UUIDField{}, 410 "protocolid": &StringField{}, 411 "created": &TimeField{}, 412 "sequence": &Int64Field{}, 413 "info": &JSONField{}, 414 "namespace": &StringField{}, 415 } 416 417 // DataQueryFactory filter fields for data 418 var DataQueryFactory = &queryFields{ 419 "id": &UUIDField{}, 420 "namespace": &StringField{}, 421 "validator": &StringField{}, 422 "datatype.name": &StringField{}, 423 "datatype.version": &StringField{}, 424 "hash": &StringField{}, 425 "created": &TimeField{}, 426 } 427 428 // DatatypeQueryFactory filter fields for data definitions 429 var DatatypeQueryFactory = &queryFields{ 430 "id": &UUIDField{}, 431 "message": &UUIDField{}, 432 "namespace": &StringField{}, 433 "validator": &StringField{}, 434 "name": &StringField{}, 435 "version": &StringField{}, 436 "created": &TimeField{}, 437 } 438 439 // OffsetQueryFactory filter fields for data offsets 440 var OffsetQueryFactory = &queryFields{ 441 "namespace": &StringField{}, 442 "name": &StringField{}, 443 "type": &StringField{}, 444 "current": &Int64Field{}, 445 } 446 447 // OperationQueryFactory filter fields for data operations 448 var OperationQueryFactory = &queryFields{ 449 "id": &UUIDField{}, 450 "tx": &UUIDField{}, 451 "type": &StringField{}, 452 "member": &StringField{}, 453 "status": &StringField{}, 454 "error": &StringField{}, 455 "plugin": &StringField{}, 456 "info": &JSONField{}, 457 "backendid": &StringField{}, 458 "created": &TimeField{}, 459 "updated": &TimeField{}, 460 } 461 462 // SubscriptionQueryFactory filter fields for data subscriptions 463 var SubscriptionQueryFactory = &queryFields{ 464 "id": &UUIDField{}, 465 "namespace": &StringField{}, 466 "name": &StringField{}, 467 "transport": &StringField{}, 468 "events": &StringField{}, 469 "filter.topics": &StringField{}, 470 "filter.tag": &StringField{}, 471 "filter.group": &StringField{}, 472 "options": &StringField{}, 473 "created": &TimeField{}, 474 } 475 476 // EventQueryFactory filter fields for data events 477 var EventQueryFactory = &queryFields{ 478 "id": &UUIDField{}, 479 "type": &StringField{}, 480 "namespace": &StringField{}, 481 "reference": &UUIDField{}, 482 "group": &StringField{}, 483 "sequence": &Int64Field{}, 484 "created": &TimeField{}, 485 } 486 487 // PinQueryFactory filter fields for parked contexts 488 var PinQueryFactory = &queryFields{ 489 "sequence": &Int64Field{}, 490 "masked": &BoolField{}, 491 "hash": &StringField{}, 492 "batch": &UUIDField{}, 493 "index": &Int64Field{}, 494 "dispatched": &BoolField{}, 495 "created": &TimeField{}, 496 } 497 498 // OrganizationQueryFactory filter fields for organizations 499 var OrganizationQueryFactory = &queryFields{ 500 "id": &UUIDField{}, 501 "message": &UUIDField{}, 502 "parent": &StringField{}, 503 "identity": &StringField{}, 504 "description": &StringField{}, 505 "profile": &JSONField{}, 506 "created": &TimeField{}, 507 } 508 509 // NodeQueryFactory filter fields for nodes 510 var NodeQueryFactory = &queryFields{ 511 "id": &UUIDField{}, 512 "message": &UUIDField{}, 513 "owner": &StringField{}, 514 "name": &StringField{}, 515 "description": &StringField{}, 516 "dx.peer": &StringField{}, 517 "dx.endpoint": &JSONField{}, 518 "created": &TimeField{}, 519 } 520 521 // GroupQueryFactory filter fields for nodes 522 var GroupQueryFactory = &queryFields{ 523 "hash": &StringField{}, 524 "message": &UUIDField{}, 525 "namespace": &StringField{}, 526 "description": &StringField{}, 527 "ledger": &UUIDField{}, 528 "created": &TimeField{}, 529 } 530 531 // NonceQueryFactory filter fields for nodes 532 var NonceQueryFactory = &queryFields{ 533 "context": &StringField{}, 534 "nonce": &Int64Field{}, 535 "group": &StringField{}, 536 "topic": &StringField{}, 537 } 538 539 // NextPinQueryFactory filter fields for nodes 540 var NextPinQueryFactory = &queryFields{ 541 "context": &StringField{}, 542 "identity": &StringField{}, 543 "hash": &StringField{}, 544 "nonce": &Int64Field{}, 545 }