Stroika Library 3.0d23
 
Loading...
Searching...
No Matches
EmployeesDB.cpp
1/*
2 * Copyright(c) Sophist Solutions, Inc. 1990-2026. All rights reserved
3 */
4#include "Stroika/Frameworks/StroikaPreComp.h"
5
6#include <iostream>
7#include <random>
8
10#include "Stroika/Foundation/DataExchange/ObjectVariantMapper.h"
16
17#include "EmployeesDB.h"
18
19using namespace std;
20
21using namespace Stroika::Foundation;
23using namespace Stroika::Foundation::Common;
25using namespace Stroika::Foundation::Database;
27using namespace Stroika::Foundation::Debug;
28using namespace Stroika::Foundation::Execution;
29
30using namespace Database::Document;
31
32namespace {
33
34 /*
35 * Sample data to write to the database
36 */
37 struct Employee {
38 optional<IDType> ID{};
39 String fName;
40 int fAge{};
41 String fAddress;
42 double fSalary{};
43 bool fStillEmployed{};
44
45 static const ObjectVariantMapper kMapper;
46 };
47 /*
48 * Define mapping to VariantValues (think JSON)
49 */
50 const ObjectVariantMapper Employee::kMapper{[] () {
52 mapper.AddCommonType<optional<IDType>> ();
53 mapper.AddClass<Employee> ({
54 {"id"sv, &Employee::ID},
55 {"Name"sv, &Employee::fName},
56 {"Age"sv, &Employee::fAge},
57 {"Address"sv, &Employee::fAddress},
58 {"Salary"sv, &Employee::fSalary},
59 {"Still-Employed"sv, &Employee::fStillEmployed},
60 });
61 return mapper;
62 }()};
63
64 /*
65 * Sample data to write to the database
66 */
67 struct Paycheck {
68 optional<IDType> ID{};
69 IDType fEmployeeRef;
70 double fAmount{};
71 Time::Date fDate;
72
73 static const ObjectVariantMapper kMapper;
74 };
75 /*
76 * Define mapping to VariantValues (think JSON)
77 */
78 const ObjectVariantMapper Paycheck::kMapper{[] () {
80 mapper.AddCommonType<optional<int>> ();
81 mapper.AddClass<Paycheck> ({
82 {"id"sv, &Paycheck::ID},
83 {"Employee-Ref"sv, &Paycheck::fEmployeeRef},
84 {"Amount"sv, &Paycheck::fAmount},
85 {"Date"sv, &Paycheck::fDate},
86 });
87 return mapper;
88 }()};
89
90 /**
91 * Combine all the ObjectVariantMappers for the objects we use in this database into one, and
92 * AMEND any mappers as needed to accommodate possible changes in the mappings (like representing
93 * some things as strings vs. BLOBs etc).
94 */
95 const ObjectVariantMapper kDBObjectMapper_{[] () {
97 mapper += Employee::kMapper;
98 mapper += Paycheck::kMapper;
99 return mapper;
100 }()};
101
102 /*
103 * Example thread making updates to the employees table.
104 */
105 void PeriodicallyUpdateEmployeesTable_ (Connection::Ptr conn)
106 {
107 TraceContextBumper ctx{"{}::PeriodicallyUpdateEmployeesTable_"};
108
109 ObjectCollection::Ptr<Employee> employeeCollection = ObjectCollection::New<Employee> (conn.CreateCollection ("Employees"), kDBObjectMapper_);
110
111 // Add Initial Employees
112 try {
113 employeeCollection.Add (Employee{.fName = "Paul", .fAge = 32, .fAddress = "California", .fSalary = 20000.00, .fStillEmployed = true});
114 employeeCollection.Add (Employee{.fName = "Allen", .fAge = 25, .fAddress = "Texas", .fSalary = 15000.00, .fStillEmployed = true});
115 employeeCollection.Add (Employee{.fName = "Teddy", .fAge = 23, .fAddress = "Norway", .fSalary = 20000.00, .fStillEmployed = true});
116 employeeCollection.Add (Employee{.fName = "Mark", .fAge = 25, .fAddress = "Rich-Mond", .fSalary = 65000.00, .fStillEmployed = true});
117 employeeCollection.Add (Employee{.fName = "David", .fAge = 27, .fAddress = "Texas", .fSalary = 85000.00, .fStillEmployed = true});
118 employeeCollection.Add (Employee{.fName = "Kim", .fAge = 22, .fAddress = "South-Hall", .fSalary = 45000.00, .fStillEmployed = true});
119 employeeCollection.Add (Employee{.fName = "James", .fAge = 24, .fAddress = "Houston", .fSalary = 10000.00, .fStillEmployed = true});
120 }
121 catch (...) {
122 cerr << "\tException adding initial employees to DB - this should generally not happen: {}"_f(current_exception ()) << endl;
123 }
124
125 default_random_engine generator;
126 uniform_int_distribution<int> distribution{1, 6};
127
128 // then keep adding/removing people randomly (but dont really remove just mark no longer employed so we
129 // can REF in paycheck table
130 while (true) {
131 static const Sequence<String> kNames_{"Joe", "Phred", "Barny", "Sue", "Anne"};
132 uniform_int_distribution<int> namesDistr{0, static_cast<int> (kNames_.size () - 1)};
133 uniform_int_distribution<int> ageDistr{25, 50};
134 static const Sequence<String> kAddresses{"Houston", "Pittsburg", "New York", "Paris", "California"};
135 uniform_int_distribution<int> addressesDistr{0, static_cast<int> (kAddresses.size () - 1)};
136 uniform_real_distribution<float> salaryDistr{10000.00, 50000.00};
137
138 try {
139 uniform_int_distribution<int> whatTodoDistr{0, 3};
140 switch (whatTodoDistr (generator)) {
141 case 0:
142 case 1: {
143 String name = kNames_[namesDistr (generator)];
144 cout << "\tAdding employee {}"_f(name) << endl;
145 employeeCollection.Add (Employee{nullopt, name, ageDistr (generator), kAddresses[addressesDistr (generator)],
146 salaryDistr (generator), true});
147 } break;
148 case 2: {
149 // Look somebody up, and fire them
150 auto activeEmps = employeeCollection.GetAll ();
151 if (not activeEmps.empty ()) {
152 uniform_int_distribution<int> empDistr{0, static_cast<int> (activeEmps.size () - 1)};
153 Employee killMe = activeEmps[empDistr (generator)];
154 Assert (killMe.ID.has_value ());
155 cout << "\tFiring employee: {}, {}"_f(*killMe.ID, killMe.fName) << endl;
156 killMe.fStillEmployed = false;
157 employeeCollection.Replace (*killMe.ID, killMe);
158 // employeeTableConnection->Update (killMe);
159 }
160 } break;
161 }
162 }
163 catch (...) {
164 // no need to check for ThreadAbort exception, since Sleep is a cancelation point
165 cerr << "\tException updating database: this should generally not happen: {}"_f(current_exception ()) << endl;
166 }
167
168 Sleep (1s); // **cancelation point**
169 }
170 }
171
172 /*
173 * Example thread making updates to the paychecks table (while consulting the employees table).
174 */
175 void PeriodicallyWriteChecksForEmployeesTable_ (Connection::Ptr conn)
176 {
177 TraceContextBumper ctx{"{}::PeriodicallyWriteChecksForEmployeesTable_"};
178 ObjectCollection::Ptr<Employee> employeeCollection = ObjectCollection::New<Employee> (conn.CreateCollection ("Employees"), kDBObjectMapper_);
179 ObjectCollection::Ptr<Paycheck> paycheckCollection = ObjectCollection::New<Paycheck> (conn.CreateCollection ("Paychecks"), kDBObjectMapper_);
180
181 while (true) {
182 try {
183 for (const auto& employee : employeeCollection.GetAll ()) {
184 Assert (employee.ID != nullopt);
185 cout << "\tWriting paycheck for employee #{} ({}) amount {}"_f(*employee.ID, employee.fName, employee.fSalary) << endl;
186 paycheckCollection.Add (Paycheck{nullopt, *employee.ID, employee.fSalary / 12, DateTime::Now ().GetDate ()});
187 }
188 }
189 catch (...) {
190 // no need to check for ThreadAbort exception, since Sleep is a cancelation point
191 cout << "\tException processing paychecks - this should generally not happen: {}"_f(current_exception ()) << endl;
192 }
193 Sleep (2s); // **cancelation point**
194 }
195 }
196}
197
198void Stroika::Samples::Document::EmployeesDB (const function<Connection::Ptr ()>& connectionFactory)
199{
200 TraceContextBumper ctx{"EmployeesDB"};
201
202 Connection::Ptr conn1 = connectionFactory ();
203 Connection::Ptr conn2 = connectionFactory ();
204
205 /*
206 * Create threads for each of our activities.
207 * When the waitable even times out, the threads will automatically be 'canceled' as they go out of scope.
208 */
209 Thread::CleanupPtr updateEmpDBThread{Thread::CleanupPtr::eAbortBeforeWaiting,
210 Thread::New ([=] () { PeriodicallyUpdateEmployeesTable_ (conn1); }, Thread::eAutoStart, "Update Employee Table"sv)};
211 Thread::CleanupPtr writeChecks{Thread::CleanupPtr::eAbortBeforeWaiting,
212 Thread::New ([=] () { PeriodicallyWriteChecksForEmployeesTable_ (conn2); }, Thread::eAutoStart, "Write Checks"sv)};
214}
String is like std::u32string, except it is much easier to use, often much more space efficient,...
Definition String.h:201
A generalization of a vector: a container whose elements are keyed by the natural numbers.
ObjectVariantMapper can be used to map C++ types to and from variant-union types, which can be transp...
nonvirtual void AddClass(const Traversal::Iterable< StructFieldInfo > &fieldDescriptions, const ClassMapperOptions< CLASS > &mapperOptions={})
nonvirtual void AddCommonType(ARGS &&... args)
nonvirtual Collection::Ptr CreateCollection(const String &name) const
nonvirtual WaitStatus WaitQuietly(Time::DurationSeconds timeout=Time::kInfinity)
Ptr New(const function< void()> &fun2CallOnce, const optional< Characters::String > &name, const optional< Configuration > &configuration)
Definition Thread.cpp:961
void Sleep(Time::Duration seconds2Wait)
Definition Sleep.inl:97
STL namespace.