Tumgik
#that i think i stumbled on this in my work lib database when i was searching for something else
nyctarian · 3 years
Quote
Now, June sunlight cranks the contrast on the awning’s blue fabric, razzle-dazzles his entire yard, the pastel blooms in the flower bed where a hummingbird flits across the length of it, her long, long beak gleaming like a needle.
It’s Only Vanishing Cream by David Hernandez
0 notes
ace-of-pages · 3 years
Text
Coat of Arms Chapter Three: Flight
It was raining when they left the shop. Lib took a slow breath and turned to walk down the sidewalk. That could have gone better, but it could have gone a lot worse. She cast a glance at Kyan as he caught up beside her. “Are you hungry, Mr. Hamalain?”
“We just ate,” he said.
“Something else you’d like to do?” she asked. “We should stay out for a while, see the city.” She smirked.
He nodded, confusion on his face. “Alright.”
He should go back to Kinnuva, but he’d come for the pickup. He should have a gun, though he wouldn’t be a good shot. They needed to stay public. She checked her com. Twelve hours until pick up. It would be dark several hours before that. She’d paid, and Angela wouldn’t stiff her, but she wouldn’t necessarily leave him alone. She almost certainly wouldn’t. Infinites were valuable.
“What is a forever?”
Lib frowned. “A.. An infinite? It’s someone who’s not in the system.” She lowered her voice. “Remember what I told you about fingerprints?”
Kyan looked at his hands. “Ah, yes. I’m not on the manifest.” She pursed her lips. At least he was being quiet.
“The database, yes,” she muttered. He didn’t respond again, and she stopped at a car station. A streetcar pulled over to them, and she glanced back as she boarded to make sure Kyan followed her. They stood amongst a dozen people or more, all staring off, waiting for their stops, or talking quietly to their coms. Kyan looked over them. He shifted awkwardly and grabbed onto the pole as the car drove off again.
They went to the hydro-towers and walked through a grove of berry bushes, grown in tiered fountains, in rows and rows. A few others walked the room, but most people preferred to have their food delivered. Lib picked a raspberry and held it between her fingers. “Do you have these?”
He shook his head. “It looks similar to a black rash, though.”
She popped it in her mouth, considering that. “This is the only place in the world that smells like anything.”
He frowned. “Are you alright?”
“Other than the minor existential crisis, yes.” She nodded.
He chuckled. “Agreed.”
She looked up at him. “Weird for you, too?” She picked another berry and ate it.
“Just a bit. Is this all free?”
“Sure, why wouldn’t it be?” She frowned. “It’s not in your world?”
“No,” he said, as though it were obvious.
“That’s weird, why would you charge people to eat? That’s like charging them to live.”
“Farmers need things other than what they can grow or raise, don’t they? Who cares for the farmers here?”
She shrugged. “They get a stipend, I think.”
“I see.” He looked around for a moment. “Where are the animals kept?”
“Animals?” she said, her eyebrows raising for a moment. “No, we don’t have those. We used to, a long time ago, but they’re all gone now.”
He was silent for a long moment. “And the case…?”
“The Last War,” she said, nodding. “It was a long time ago, but they did a pretty good job of making sure we’d remember it.”
“That’s terrible.”
She shrugged again. “Maybe. World’s pretty good now, though.”
“Doesn’t smell like anything, though.”
She raised an eyebrow. “My existential crisis does not need your assistance, thanks.” He grinned.
By the time they got back outside, they had passed nearly an hour. Lib scanned the street, looking for anyone who might be following them, working for Angela. There were too many people, though that could work to her advantage. She could probably go back to the condo. She started toward the nearest station, and the air around her rapidly dropped to freezing temperatures. Her breath came out in a puff of fog. She stopped, and Kyan followed suit.
"What is this?" he asked her. Around them, other people had stopped as well and were looking around nervously. Chattering filled the air and brought with it dozens of puffs of fog.
“I don’t know,” she said, and her teeth clacked against each other. Kyan dropped for a moment, slipping his knife out of his boot and hiding it behind his arm. She slipped her hand into her purse, gripping her gun. The two of them backed up to the side of the building, looking around.
The voices around them were silenced as a tearing sound filled the air. Several people shrieked. Kyan looked around wildly, and the space before them split apart. The edge of the autowalk folded into the ground, the people standing on it screamed and fell over, crawling away from the darkening split. The wall of the nearest building tipped away, and a woman standing there tilted with it, growing strange and stretched. Nausea churned in Lib’s stomach, and someone nearby retched.
A dark figure appeared in the split, and the world righted around it, snapping upright. The stretched woman fainted, shaking. The figure was humanoid in shape, but featureless, and dark, as though light refused to reach it. Lib found her feet rooted to the place she stood, and for a moment, nothing moved. Then, the screaming began. Kyan flipped his knife around and stepped forward as others began running. He came in for a jab towards the creature’s center.
It smacked Kyan’s arm away and reached out for his throat. Kyan ducked back and yelled in pain. Lib shook herself and pulled her gun from her purse. Kyan ducked to the side and plunged his knife into the leg of the creature just as Lib fired twice, hitting it in the face and neck. The bearings buried themselves in it, though it seemed to have no flesh, and it let out no sound. Kyan pulled the knife out and stepped back, holding his other arm out in front of Lib. The creature took a step toward them, and Kyan lunged again, this time slashing his blade across its throat. It stopped, frozen for a moment, then vanished, leaving the street as it had been.
“Are you alright?” Kyan asked, glancing toward her.
“Yes,” she said and shoved the gun back into her purse. “We need to get out of here, though. Put that away.” She turned toward the autowalk. He slid the dagger back into its sheath just as they heard the first of the sirens. “Shit, come on!” She grabbed his hand and jumped onto the autowalk, running through the crowd. A drone flew over their heads with flashing blue and orange lights and began to call for the people to remain calm. They only had a few seconds to get to a new block before the people around them stopped running. The corner was close, another hundred feet. The sounds of shoes slapping against the pavement began to lessen, and the people began to slow. She slowed along with the crowd.
“There, those two,” she heard someone say. “With the long coat.” Lib looked behind her and saw a woman pointing at her. Two police officers looked in her direction.
Lib ran again.
“Can’t we just explain it to them?” Kyan called out, following her.
“And say what?” She groaned and turned the corner onto the next street.
“Stop!” one of the officers called after them as they fled.
“Suspects have turned onto Queen Anne’s,” she heard someone yell. They were gaining on her. Ahead, she saw more police look in her direction.
“Shit.” She stopped running and looked around.
“Can we get some drones here?” an officer called.
“Do we fight them?” Kyan asked as he stopped beside her, his hand on his knife.
“Hell no,” she said and stepped into the street. “Come on!” Bells rang angrily as a personal vehicle slowed and swerved to avoid her, and she ran to the center of the street.
“Will these machines harm us?” Kyan called out, dodging the traffic.
“Only if we get hit!” she responded and looked down the street. A streetcar was approaching. “Follow me!” She turned and began running along with the traffic, just beside the wire that ran along the road. Whistles sounded, and two of the police officers were running along the side of the road with them, slowly edging more into the road.
“I’m with you!” Kyan called after her. Lib looked back and saw the streetcar growing closer.
“Get ready!” she called out. She looked to the left. The officers were only a few feet away. So was the streetcar. It pulled up beside them, and Lib grabbed for it. It was moving quickly, but she got the crook of her elbow around the bar and pulled herself up. A woman with a young child looked up at Lib, then to the street where the police were just giving up their chase, and pulled her son onto her lap, her eyes widening. Lib looked back. Kyan was a few feet away, still on the outside of the car, but holding on. She grinned and pushed through the crowd in his direction. She saw the police yelling into their coms in the car’s wake. “Come on,” she said when she got to Kyan, just as he pulled himself into the car.
“That was close, Traveler.”
“We’re not done yet, Sir Hamalain,” she hissed and pushed a path through the car.
Someone grabbed Lib by the arm. “I don’t think so,” he said and pulled her toward a row of seats. He wore a plain brown jacket and a faded gray-blue cap. Lib twisted, pulling her arm down out of the grip before she slammed her elbow into his face. He stumbled back. “They’re running from the police!” he called out, holding his face. “They-”
Kyan punched him in the face. He sat down hard on the empty seat, his nose broken. “Thank you for that,” Lib said. The other passengers all stared at them, preparing for some further violence to break out. She grabbed the cap from the man’s head. “This is our stop,” she said and walked to the opening of the car. “Ready?”
Kyan jumped and stumbled a few steps in the road before catching his feet. Lib followed suit and fell into a roll before running again. He looked back at her, and she gestured toward an alley. They ran for it, their feet slapping against the pavement. More bells rang as they reached the sidewalk, then the alley. She breathed heavily as they made it around a corner, and she stopped running to lean against the wall. Sweat dripped from her hair, and she looked up at Kyan, who seemed less out of breath than she was. “You’re fast,” she said.
“You too.”
“I’m exhausted. I haven’t done this shit in years.” She grinned. Time had taught her caution, but she’d always enjoyed this part of the job. “Take your coat off,” she said, and slipped the cap onto her head, pulling her hair lazily through the opening in the back. Kyan rolled the windbreaker up and handed it to her. She tried to shove it into her purse, but it wouldn’t quite fit.
“Can you just leave it?”
“No,” she said. “If they find it, they’ll have my fingerprints.”
Kyan nodded, a slight frown on his face, and he reached over and took the jacket. He stepped closer to her and pulled one flap of her jacket, opening it just enough to reveal the pockets that lined it. He slid the windbreaker into a pocket, where it disappeared. Lib looked up at him, and a slow grin spread across her face. “Magic coat,” he reminded her.
“Magic coat,” she agreed. “Come on.” She started down the alley, making her way toward the next corner.
“Hands up!” Two police officers turned the corner before they could reach it, brandishing shockers.
“Shit!” Lib cried out again and grabbed Kyan’s hand to pull him further into the alley. They slipped into the space between buildings and saw another officer blocking their path.
“Hey!” she shouted at them. “Stop!”
Lib stopped and looked back. There was nowhere else to go. She’d be arrested in seconds. “Hold on,” she whispered to Kyan and reached into her jacket for the mirror. He grabbed her hand, and she held the mirror in front of them. A loud click followed them into the nothingness.
Lib shivered, gasping at the strange coldness of the dark place, even for a moment after the light had returned. She looked around at the trees that surrounded them. The sharp scent of pine assaulted her as she caught her breath, and stood upright again. She moved to cover her face, her eyes wide, and stopped. Not poisoned.
Probably.
“What is this?” she asked, quietly.
“Forest.”
“Where are we?”
He looked at her and dragged a hand over his face.
She turned, taking it in. Sunlight streamed down through the canopy, a shining blanket on the branches, filtering down to the ground, where they stood in the underbrush. Tiny, colorful animals flew amongst the higher branches, and something shuffled in the bushes near them, out of sight. “I can touch it?” she whispered. “It’s not contaminated?”
“They’re just pines, Traveler.”
“Pines,” she said and looked at him, frowning. “The box the jacket came in was made from pine.”
“They’re fairly common.” He shrugged.
“Are they? We don’t have them.”
“That’s true,” he conceded.
“Where are we? Is this your world?”
He turned his head. “If it is, it’s not in Kinnuva.”
She pursed her lips and reached into the mirror pocket. Nothing. She held up her hands. “Not your world. Or mine. I wonder how many there are.” Her gaze slipped away from him again, and she reached out to run her fingers through a bush. It was rough against her skin, almost sticky. The small sound of animals filled the air around them.
“We should probably find a mirror, Traveler.”
“You don’t have to call me that, you know.”
“Why not? It’s your title.”
“My name is fine.”
“It feels impolite,” he said, after a moment.
She looked back at him. “We don’t really refer to people by their titles.”
“We’re not in your world, though. Traveler.” He smirked.
“We’re not in yours either.”
He sighed. “It’s too familiar.”
“You really want me to call you Sir Hamalain?”
“Well,” he said, thrown off by her incredulity, “yes. That’s -”
“Okay, fine,” she cut him off. “Well, I don’t know how to go about finding a mirror in a forest. Or anything else.”
“We need to find the river.” He looked around, stood still for a moment, picked a leaf, and examined it. “This way, I think.” He started walking.
“Excuse me, what?” Lib said, jogging after him. “Are you a wizard?”
He laughed. “Your entire world can be commanded by voice and you think I’m a wizard because I can find a river in a forest?”
“Well, okay. How, though?”
He pointed up at the sun. “The sun moves from east to west, rivers flow from north to south. The ground slopes slightly that way,” he said, pointing to the right of their path. “So if we go this way, we should find water eventually.”
Lib looked to the right, at the flat ground, and raised an eyebrow. “What if it’s different in this world?”
“If it is, then we’re lost, but we can’t do nothing, can we?”
She stared at him. “Fair point.” She reached into her jacket, into a large pocket that seemed to jump toward her fingers as she reached for it, and pulled out the sword she’d stolen from the dead knight. “Here.”
He stopped for a moment to take it. “Thank you, Traveler.”
“Well, I have my gun now. Besides, it’s not like I can use it.”
“Why don’t you hold onto this,” he said, pulling the knife out of his boot. “You can use these, right?”
“Sort of. I have my gun, though.”
“Just in case,” he said, pushing the knife into her hand.
The air grew warmer as they walked, and a sheen of sweat grew on the back of Lib’s neck. She checked her com. Ten hours until pick up. The shadows shifted around them, deepening in the east. Plants pulled at her pant legs, and bits of them stuck to her ankles. She was glad she’d tucked all her hems in. She didn’t have to worry about leaving DNA around, but who knew what the plant matter would do if it got to her skin instead.
They heard the river before they saw it. It was small and quick, burbling and teeming with white froth. A pair of animals crouched on the edge of it, lapping at the water. They looked up as Lib and Kyan approached, their ears twitching curiously. They had large eyes and round faces covered in orange fur that whisked outward from the sides of their protruding mouths. “Strange cats,” Kyan called them, and kept a grip on his sword as one of them crept cautiously closer. It came a little past Lib’s knees but stretched its head higher to sniff at her.
“What’s it doing?”
“Trying to smell you. I don’t think it’s aggressive,” he said. Lib slowly stretched her hand out and the strange cat sniffed it, then turned and flitted away, only to look back at her over its shoulder. She laughed.
“Is it scared of me?”
“Not scared enough.” He lowered his sword as the pair of cats hopped the small river to watch them from across the water.
“What do you mean?” she asked him.
“It means there probably aren’t many people here.”
“Oh,” she said, then her face fell. “Oh no.”
He nodded for a moment, thinking. “Maybe we can find something naturally reflective.”
Lib ran her hands through her hair and cursed quietly. “Why is this happening?”
Kyan looked over at her. “Traveler, we’ll figure something out.”
“No, no, something is wrong,” she said. “How did we even get here?”
“Your magic-”
“No, I mean, we were running because that… thing showed up. What was that? It wasn’t right.”
He raised his chin in realization. “That wasn’t from your world, then?”
“No,” she said, and paused, thinking. “It felt like the place in between.”
“The dark place?” he asked her, and she nodded. He lifted his face to the sky. The sunlight caught his features, and for the first time, he looked whole without his armor. Then, the words caught up to him. “There’ll be more, then. They’ll come here, too,” he said, quietly.
Lib shook her head. “No, we’ll find something reflective. We’ll get back.” She rushed to the side of the river and crouched to peer into the waters. She stuck her hands in, feeling for anything that might be useful, and he was beside her.
“Not here,” he said, his usual conviction returning to him. “We’ll go downstream, we’ll find a place with more rocks.” Lib stood and met his gaze. He was worried, but ready. She nodded.
The first of the creatures from in between appeared an hour later. They were ready as soon as they saw their breath, silvery in the twilight, and Kyan easily removed its head. It was gone before it hit the ground. The next one came faster and brought allies with it.
Kyan stepped in front of her. “Don’t let them touch you.” His voice was low. He raised his sword in defense as they came forward, and Lib drew her gun and fired three shots in rapid succession. The creature she hit snapped its head back, then vanished. The others moved forward, unperturbed by the loss of their companion. Kyan slashed at the clawed arms of darkness as they reached for him, knocking one of the creatures aside. The other leapt at the opportunity and latched itself onto his arm. He yelled in pain at its touch and drove his sword through its center before pulling away. The creature vanished and he turned on the last of them. He traded blows with it for a turn before Lib put a round in its face.
Kyan clutched his arm, then shook it roughly. “Are you okay?” Lib asked.
“They’re very cold. I’ll be fine,” he said.
“These came faster than the last one.”
“Are the weapons you got us so effective as that?” he asked, his face twisted with pain.
She touched his shoulder. “We need to move or it won’t matter what I got you.”
“Let’s move then.” They turned and took up a quick jog along the riverbank, heading downstream. The sun set and Lib pulled out her com for light. She thought of her upcoming meeting. Angela would be unhappy with her if she missed it. She gritted her teeth at that and watched Kyan ahead of her. He would be unhappy, too. He’d probably insist that she find a way to buy the guns again. He wouldn’t understand the problems that would cause. She had promised, after all, and her word was all she had to her name in Kinnuva. Then again, maybe she could find another world that sold weapons more easily than her own. Were there an infinite number of them? She’d never visit all of them if that was the case, obviously, but the trying would be fun.
“Watch out!” Kyan called back to her as he drew his sword. Reality ripped apart again, a sound that was becoming too familiar, and Lib shone her light around. She could barely see the creatures as they appeared in the darkness, but they were numerous. Six? Eight? She couldn’t be sure. She drew her gun again, and the creatures rushed forward.
Lib stepped up to Kyan and began firing. Several times, she thought she saw the creatures vanish, but she knew she missed some as well until her gun clicked empty. To her right, Kyan grappled with three of the beasts, and they pulled him to the ground. She should run. Her gut told her to run. She was out of bullets, there was nothing she could do. Kyan kicked at one of the monsters and sent it off of him. He stabbed his sword through one of their torsos, ending it. The remaining creature grabbed his sword arm and held it down as the one he’d kicked away returned to the fight.
“Shit,” Lib muttered, and pulled his knife from her pocket. How had he done this? She came up behind the demons and grabbed the one on his sword arm by the head, yanking it back. It was cold! So cold she nearly threw herself back, but she locked her fingers in place, and jammed the knife up into its neck, then stumbled forward as it disappeared. She stepped to the side, and kicked at the last monster. It didn’t fly back, but it flinched to the side, and Kyan rolled toward it and stuck his sword in the creature’s leg. It made no sound, but retaliated with a claw, leaving a gash across his face. Getting one foot under him, Kyan slashed his sword across the creature’s neck, separating it for a moment before it was gone. Lib took him by the arm and pulled him to his feet. He moved slowly, breathed heavily. “Come on,” she said, and pulled him forward.
“Why?” he asked.
“They’re only going after you.” He laughed quietly and pressed his free hand over the side of his face.
“How could you draw them here, Traveler?” a voice squawked, and Lib jumped.
“Who’s there?” she demanded, turning her com’s light in its direction. It was a small creature, a bit more than half her height, covered in red feathers with a large yellow beak.
“No time,” it said. “More coming. Run now!” It rose, and two small wings unfolded from its back.
“Hamalain!” she yelled, stumbling after the creature as it turned to run through the forest. The tearing sounded again, in several places around them, and Lib urged him to run faster.
“What's happening?” he called out.
“More coming!”
“The voidlings!” the bird creature called back to them. “Run! The hive is safe!”
“What are they?” She yelled as the voidlings swooped down at them, and they fell to the ground. Kyan turned over and wrestled one of them onto its back, slamming his elbow into its face. It fell away from him, and he turned and stabbed it. He grabbed Lib by the arm and pulled her up, the fight returning to him. Tears streaked down her cheeks, hot against her skin. She looked around frantically, scanning for the next attack. She screamed, and her foot caught on something. She fell into the dirt, her arms smashing on a root. With a cough, she pushed herself up to move again, scrambling for her fallen com.
“Can you make it?” Kyan grabbed her hand and pulled her along.
“I'm coming,” she replied, forcing her feet to keep moving. The voidlings grew closer. “Run!” How many were there, now? A dozen, at least. “Shit!” she cried out as another of the voidlings dove between them, breaking their grip. He parried it with his sword, but was knocked to the ground. She drew the knife and leapt in front of it as it went for Kyan, driving the blade into its chest. It went in as easily as it did at the neck. Another wave of cold shocked through her, and she stepped backward, her feet dumbly catching each other. He was on his feet already, and caught her as she fumbled. They shared a glance, and turned to run again. As they ran through the woods, more and more of the creatures came down upon them. Something nearly caught him from behind, and he shouted in pain again.
Finally, thankfully, they saw lights up ahead. The strange bird person screeched for them to reach the lights, and they pressed all they could into the last dash. Lib’s legs burned, her chest burned, and her skin and breath froze in the air. The lights drew near, and they passed through a barrier, just at the edge of the glow, and fell to the ground as warmth returned to the world.
Kyan was on his feet again in an instant, his sword at the ready. Lib rolled to her back, clutching the knife in front of her. The dark creatures stood at the edge of the light, shifting spasmodically, then growing still all at once. They had no eyes, or none that showed, but she could feel their stares. She climbed to her feet, slowly, her hands shaking. Kyan stepped forward, edging toward the monsters. The back of his shirt was torn, and stained red with his blood.
A ruffling sound drew her attention, and she turned away from him. The bird person stood watching her, and several others gathered around it. “Traveler,” it said. “Why are you here?”
She managed to find her voice. “I’m not sure. I was running. I didn’t mean to come here. I was just trying to get away.” She looked back at Kyan. He wouldn’t look at her. Shit. “What are those things?”
“Voidlings,” it told her. Or someone told her. He hadn’t moved his mouth, had he? His beak. She squinted.
“What are they doing here? They came for him, right?”
“They will leave when you take him away.”
“You have a mirror for me, then?”
“Yes, yes,” she heard. No, she didn’t hear it. She knew it. Were they speaking to her telepathically?
“Did you want something for it?” she asked.
“We want a favor. A promise.”
She pursed her lips. “Right.” She turned back to Kyan, who still stood at the edge of the light, barely a foot from the nearest voidling. Her stomach clenched as she neared it, and it pulled at her. Like the in between place pulled. “What are you doing?” Her voice was small again. She swallowed.
He turned to look at her. “I’m trying to see them,” he whispered. The gash on his face went across his eye, which was swollen, but not shut. The pain and blood skewed his features, made it hard to read.
“I’m sorry,” she said.
He lowered his sword, and put a hand on her shoulder. “I’m not angry, Traveler. I just didn’t expect to have demon assassins come after me today. I’m guessing you didn’t either.”
Her shoulders released some of their tension. “No,” she said. “I did not. So, you ready to get back to Kinnuva?”
He hesitated, and looked back at the voidlings. “How long until the pick up?”
She checked her com. “Six hours.”
“We’ll go back after we have the weapons.”
“It’s not safe here. Or there.”
“It’s safe in the light.”
She stared at him for a moment. “Are you sure you still want to go with me?”
“Yes,” he said with a grin. “It’s a knight thing.”
She chuckled. “Yea, yea.”
“Well,” he said, letting go of her shoulder, “you did save my life, again.” He stuck his thumb into his throat. “Nice work, by the way.”
“I’m sure you’ll pay me back. Anyway, these birds want some kind of favor before they give me a mirror.”
He nodded. “Let’s work it out.” With a final glance at the voidlings, he sheathed his sword and turned toward the clearing. Three of the birds stood by, patiently waiting.
“Right,” Lib said. “My name is Olivia Kosk, and I am - apparently - the Traveler.” She waved cheekily.
“Traveler, we wish to trade with you.”
“Yea, I got that. Listen, do you have any food? Because we’ve been running through your forest for hours, fighting those beasties, and we’re starving. My friend is hurt. I’m happy to help you out, we’ll trade, make a deal, but I’m in a rough spot at the moment.”
The bird in front, the one who met her in the forest, maybe, ruffled its - his? - feathers for a moment before she received an answer. “Follow us. You will be given respite.”
They were led through the clearing, marked out with dozens or hundreds of the small, pink lights, which blinked in and out in an ineffable pattern. Throughout the hive were several dozen oblong structures, made of twigs and leaves, with round holes in the front, reaching twenty or more feet high. Though the canopy was nearly twice their height, the nest-homes were somehow more imposing. The pink glow lit forth from each door and window. They passed a small pool over which a wooden statue had been built, a statue of a human woman wearing a familiar looking coat. Lib’s jaw slackened.
“Looks like they know you here, too, Traveler,” Kyan whispered, leaning in.
“Well that’s weird as -” She turned to him, and her grin faded. He was pale. Paler than normal. “Shit.”
He laughed lightly. “That bad?”
“You need stitches, Kyan.”
He nodded, and pointed ahead of them, where the bird people had stopped beneath a shelter made from branches tied together. Two of them squatted beside a circle of stones and began to work over the sticks that were piled in the center, while the third faced them. “Rest here, Traveler. We will discuss our trade once you are well.”
As he moved off, another pair of bird people came up to the shelter, carrying bowls of water. “Drink, drink, heal,” they said, and left again.
“Strange people,” Lib said, and they moved beneath the branches. The ground was soft, and they sat. One of the bowls had herbs and leaves mixed in. “This is for cleaning, I guess?” She looked to Kyan for confirmation.
He lifted the bowl and smelled it before nodding. “No cloth, though.”
Lib frowned, then opened her jacket flap. “Cloth, cloth, cloth,” she whispered, running her fingers over the pockets. She reached into one, and pulled out a large handkerchief. “Magic jacket.” She soaked the cloth, then turned to Kyan. “Face first, I think. Wish I had some peroxide, though.”
“What’s peroxide?” He leaned toward her, putting his hands on his knees to brace himself.
“It’s a disinfectant. Hopefully these herbs will be good enough against voidling claws. No telling where they’ve been, after all.”
“I don’t think they’ve been much of anywhere,” he said as she started cleaning the gash in his face.
“You’re funny,” she said. “Maybe you should almost die more often.”
“I’m sure I’ll find another chance.”
“Does your eye hurt?”
“Not much.” She finished cleaning the wound, and pulled out a small, white plastic square from her pockets. After warning him against the pain, she pressed it onto his face, just under his eye, and watched it move along the gash, stitching it closed. He hissed, and grabbed her arm as it worked its way down his face.
“It’s alright,” she said, leaning back, then patted his hand. “It only takes a moment.” He searched her face. Then, it was over, and he relaxed, leaning back. She quickly reached up to take the stitcher off his face, then pressed the cloth to it again, letting the cool water ease the stinging.
“Thank you,” he said.
She shrugged. “Course. How’s the eye, can you see?”
“Less blurry than it was.” He leaned forward and hugged her with one arm. “I think it will be alright.”
“What?” Lib blinked, and patted his shoulder. She laughed awkwardly, and pulled away.
Kyan frowned. “I’m sorry, I didn’t mean to make you uncomfortable.”
“It’s alright, I - we don’t really do that, where I’m from.”
“You don’t touch each other?”
“I mean, some people do, but not strangers,” she told him. “Not casually.” He nodded, considering. The disagreement was clear on his face, but he didn’t seem bothered by her assertion. “Lie down, let me see your back,” she said, gesturing to the moss around them.
He winced as he pulled his shirt off, and laid down on his stomach. He took slow, deep breaths, turning his head to keep his wounds off the ground, and closed his eyes.
“How are you so relaxed right now?” She set the cloth to the scratches on his back.
He laughed softly. “It makes the cleaning easier. That device, it stitches wounds. It’s fast, faster than a person doing it.”
“Machines are better at a lot of things.”
“But you’ve done this before.”
“What, stitched someone up? It’s not hard. You do it by hand in your world, though? That seems harder.”
“It is, unless there’s a healer around.”
“What, magic?”
“Yes. That’s not what I meant, though.”
Lib frowned. “What did you mean?”
“It’s not the first time you’ve cleaned up someone else’s blood.”
She stopped. “No, it’s not.”
“No war,” he said, “and you’re not a doctor.”
“I don’t really like to talk about that.” She dipped the cloth into the water, and rung it out. Blood covered the surface of the water, but after a moment, began to soak into the leaves that floated there. Well, wasn’t that clever.
“Neither do I.”
She pulled out a few more stitchers and pressed them to the ends of the cuts. “Well, other than the blood, today was pretty fun.”
He coughed, and twitched at the pain. “Of course you would think that.” He was really trying to sound admonishing. She laughed.
“I guess you’re normally on the other side of things like that.”
He hesitated before he answered. “I have been.”
“You know, I haven’t had to actually run from the police since I was fourteen. It was novel and nostalgic, all at once.”
He opened his eyes to look up at her. “Are you trying to be vexing?”
She raised an eyebrow. “No, actually.” She sat back, letting the cloth rest in the bowl for a moment. “Okay, you’re a really good person. In fact, you’re one of the most trustworthy people I’ve met. You know how I know that? Because I’m really, really good at reading people. That’s how I know you’re lying to me.” His breath hitched as she said it, but he said nothing, and she continued. “You’re intrigued. You’d never let that sway you, of course, but that doesn’t mean you can’t enjoy a bit of fun in another world.”
He frowned, struggling with it. “How am I supposed to trust you?”
She picked up the cloth again and returned to working on his back, cleaning up behind the stitchers, and soothing the pain. The leaves that floated in the water were beginning to bloat, but they kept the water mostly clean. She picked them out and set them aside. They were nearly done here. “You already do. That’s why I’m being honest. Because your trust, in your world, is valuable. Your word is valuable. I don’t want to mess that up, so try to relax, okay?”
He took several breaths, and closed his eyes again. He calmed, the tension went out of his shoulders, but a strand of worry clung to his face, perhaps caught in the stitches on his cheek. “Are you going to spend much time in Kinnuva, do you think? After our business is done, that is.”
She smiled. “You mean am I going to start a life of crime there? No. Other than helping your rebellion, which is technically illegal.”
His eyes shot open. “They betrayed us. The knight commander murdered the king.”
She stopped, leaned back. “I’m sorry. I didn’t-”
“I know.” He sighed. “I didn’t know either. I thought I knew him.”
“So, you left your order?”
He hesitated just a little. “Yea.” She frowned. There was something else, too. A deeper betrayal. A woman, maybe. Was that what he wanted her help with? Her mirror tricks would certainly make it easier to rescue someone. She wanted to comfort him, but held back. She’d give away too much of what she’d surmised. Better to let people assume her mind was empty as long as possible. She did put a hand on his shoulder, though, like she’d seen Atos do. It was strange, but he seemed fine with it.
“I’m kind of messing this up. I - Well, like I said before, it’s crazy. We don’t even believe in magic in my world. It’s -” She took a breath as he watched her. “But I’ll make it happen. I’ll treat with these bird people, then we’ll get back to Seattle, grab the guns, and make it back to the ship. It’ll be alright.” She nodded.
He watched her for a moment, then laughed, once. “Perhaps.”
“I’m sorry, okay? I don’t know what I’m doing. I just kind of fell into this, that’s not my fault, but I can make it work.”
He sighed again. “Alright. Are the… stitchers? Are they done?”
“Oh, yes,” she said, and plucked them off of his back. He pushed himself up to sit facing her.
He took a deep breath before he spoke, steadying himself. “I know that you are new to magic, but you cannot tell me that none of what has happened since you learned of it is your fault.”
She folded her hands together and pressed them to her mouth. She was being whiny. After a moment, she nodded. “You’re right. You’re right. I’m being reckless.”
A flicker of a haunt passed over his eyes, then he blinked and it was gone. “Good. I can help you, teach you a little about magic. I’m no mage, but I know enough.”
The more she knew. “That would help.”
“Good. We can start with this.” He indicated the scratch below his left eye. “Because these are really starting to burn.”
“They are quite red, even stitched. You need more medicine.”
“We breathe together, to ease pain.”
She frowned. “What?”
“It’s just the latent magic in everybody. The part that keeps us together, gives us form.” He paused, turned his head. “I wonder if you’ve got it, if you’re not from my world. What holds your people together?”
“You mean literally?” He nodded. “Well, it’s not- It doesn’t matter.” She gestured for him to continue. He was starting to sweat.
“I don’t know if this will work, but I’d like to try it.” He leaned forward, and put a hand on her shoulder.
“Okay. What do I do?”
“Just breathe.”
She nodded, and leaned forward, reaching out with her other hand to mirror his position. He inclined his head to rest his forehead on hers, and took a deep breath. Something curled in her stomach, something like déjà vu, flitting its fingers along her spine, and she matched his breaths without thinking. He relaxed almost immediately, the tension going out of his shoulders, and they breathed again.
His eyes were two different colors, and he smelled like sweat and steel and lavender.
She could feel the magic. It sort of billowed up with her breath, lifting into him as he leaned on her. Strange, but not unpleasant. A few minutes later, he leaned back, pulling his arm away. “Oh, that’s better. Thank you, Traveler.”
“It worked. That’s great. I could feel it. It was-”
“Crazy?”
She grinned. “Weird.”
He grinned as well, and took her hand. “I have to say, it’s comforting to know you’ve got some familiar magic in you.”
She looked down at her hand, and pulled it away. “You should rest. I’m gonna go make a deal.”
He opened his mouth, then decided against what he was about to say, and nodded. “Do you want my help?”
“No no, you should rest. I’ll be back in a few.” She rubbed her hand, and squeezed it into a fist for a moment.
“Good luck, Olivia.”
The bird people - the aven - had a long history with her ancestors, as it turned out. “We were beasts once,” they told her. They almost always spoke in the collective, and she was never entirely sure which one of them was speaking. It didn’t sound like a chorus of voices, but it didn’t actually sound like anything. It was all in her head. “The Traveler came, with a flower from another world, and she awakened our minds.” Around the clearing many pairs of eyes peered at them from within the nest-homes, and soft trills danced in the air.
It sounded like something she’d heard before. “Yes, yes, the flower gave us Thought!”
“Well, that's...” Profitable. “Probably pretty lucky for you.” Had they read her mind?
“Lucky, perhaps. It allowed us to be free of the ralinx. They hunted us, when we were beasts. Now they keep our lands safe.” She could not read these people. Their faces didn’t move at all when they spoke.
“Are those the orange-haired creatures? About this tall? The strange-cats?” She held her hand to her knees.
Several of the aven nearby ruffled their feathers before responding. “Yes. Strange-cats. The ralinx.” They were discussing with each other. Telepathy, but body language as well. She couldn’t figure it.
“So, you’ve known my family for a long time, then,” she prompted.
“Yes, yes! A long time, Traveler.”
“And you want to continue knowing us? Me?”
“Yes.”
“What is it you want?”
“We have a sickness,” they told her. “And we believe the flower can cure it.”
“So you want me to bring you a flower from another world? That seems easy enough.”
“Yes, yes!”
She squinted. “You know I need the mirror to get to the other worlds.”
“Of course, we will give it to you if you agree.”
She frowned. That was too easy. She couldn’t really read them, but this didn’t feel like trust, it was something else. Maybe they simply didn’t have a concept of lying, since they were telepathic. “Where do I get the flower? What does it look like? Does this world have a name?”
“It is in the land of Ryf, guarded by a great beast, the Ryf beast!” Of course. “You must defeat the monster, bring us the flower. It will look like a fine powder, like dry red dirt. Will you agree to this, Traveler?” The aven nearest to her had turned its head to the side, and was peering at her with one eye, almost comically. She needed to do a touch of research on animals when she got the chance. How much information did her world have on them anymore? Just that they used to exist, probably. There were always other worlds, however.
“Okay, sure. Seems easy enough,” she said. Even with a great beast, her mirror magic should make it an easy task. The aven ruffled their feathers all around her.
“We will bring you the mirror. Thank you, Traveler!” That was the easiest deal she’d ever made.
The mirror was roundish, the size of a dinner plate, and set into a rock. Its back was a swirl of colors and jagged edges that glinted in the twinkling lights around her. Careful not to glance into it, she slipped it into a pocket. She thanked the aven, and made her way back to the shelter where Kyan rested, face down in the moss.
She roused him, and waited as he put himself together before pulling out the mirror. This one was heavier than the others, and awkward to hold. Well, it would be gone soon enough. “Ready?” She asked, and turned it over. Her reflection held a curious expression as the world folded away. Back in her condo, the lights came on, and she pulled out her com. One hour to go.
1 note · View note
globalmediacampaign · 3 years
Text
#WDILTW – Creating examples can be hard
This week I was evaluating AWS QLDB. Specifically the verifiable history of changes to determine how to simplify present processes that perform auditing via CDC. This is not the first time I have looked at QLDB so there was nothing that new to learn. What I found was that creating a workable solution with an existing application is hard. Even harder is creating an example to publish in this blog (and the purpose of this post). First some background. Using MySQL as the source of information, how can you leverage QLDB? It’s easy to stream data from MySQL Aurora, and it’s easy to stream data from QLDB, but it not that easy to place real-time data into QLDB. AWS DMS is a good way to move data from a source to a target, previously my work has included MySQL to MySQL, MySQL to Redshift, and MySQL to Kinesis, however there is no QLDB target. Turning the problem upside down, and using QLDB as the source of information, and streaming to MySQL for compatibility seemed a way forward. After setting up the QLDB Ledger and an example table, it was time to populate with existing data. The documented reference example looked very JSON compatible. Side bar, it is actually Amazon Ion a superset of JSON. INSERT INTO Person Now, MySQL offers with the X Protocol. This is something that lefred has evangelized for many years, I have seen presented many times, but finally I had a chance to use. The MySQL Shell JSON output looked ideal. { "ID": 1523, "Name": "Wien", "CountryCode": "AUT", "District": "Wien", "Info": { "Population": 1608144 } } { "ID": 1524, "Name": "Graz", "CountryCode": "AUT", "District": "Steiermark", "Info": { "Population": 240967 } } And now, onto some of the things I learned this week. Using AWS RDS Aurora MySQL is the first stumbling block, X Protocol is not supported. As this was a example, simple, mysqldump some reference data and load it into a MySQL 8 instance, and extract into JSON, so as to potentially emulate a pipeline. Here is my experiences of trying to refactor into a demo to write up. Launch a MySQL Docker container as per my standard notes. Harmless, right? MYSQL_ROOT_PASSWORD="$(date | md5sum | cut -c1-20)#" echo $MYSQL_ROOT_PASSWORD docker run --name=qldb-mysql -p3306:3306 -v mysql-volume:/var/lib/mysql -e MYSQL_ROOT_PASSWORD=$MYSQL_ROOT_PASSWORD -d mysql/mysql-server:latest docker logs qldb-mysql docker exec -it qldb-mysql /bin/bash As it's a quick demo, I shortcut credentials to make using the mysql client easier. NOTE: as I always generate a new password each container, it's included here. # echo "[mysql] user=root password='ab6ea7b0436cbc0c0d49#' > .my.cnf # mysql ERROR 1045 (28000): Access denied for user 'root'@'localhost' (using password: NO) What the? Did I make a mistake, I test manually and check # mysql -u root -p # cat .my.cnf Nothing wrong there. Next check # pwd / bash-4.2# grep root /etc/passwd root:x:0:0:root:/root:/bin/bash operator:x:11:0:operator:/root:/sbin/nologin And there is the first Dockerism. I don't live in Docker, so these 101 learnings would be known. First I really thing using "root" by default is a horrible idea. And when you shell in, you are not dropped into the home directory? Solved, we move on. # mv /.my.cnf /root/.my.cnf Mock and example as quickly as I can think. # mysql mysql> create schema if not exists demo; Query OK, 1 row affected (0.00 sec) mysql> use demo; Database changed mysql> create table sample(id int unsigned not null auto_increment, name varchar(30) not null, location varchar(30) not null, domain varchar(50) null, primary key(id)); Query OK, 0 rows affected (0.03 sec) mysql> show create table sample; mysql> insert into sample values (null,'Demo Row','USA',null), (null,'Row 2','AUS','news.com.au'), (null,'Kiwi','NZ', null); Query OK, 3 rows affected (0.00 sec) Records: 3 Duplicates: 0 Warnings: 0 mysql> select * from sample; +----+----------+----------+-------------+ | id | name | location | domain | +----+----------+----------+-------------+ | 1 | Demo Row | USA | NULL | | 2 | Row 2 | AUS | news.com.au | | 3 | Kiwi | NZ | NULL | +----+----------+----------+-------------+ 3 rows in set (0.00 sec) Cool, now to look at it in Javascript using MySQL Shell. Hurdle 2. # mysqlsh MySQL Shell 8.0.22 Copyright (c) 2016, 2020, Oracle and/or its affiliates. Oracle is a registered trademark of Oracle Corporation and/or its affiliates. Other names may be trademarks of their respective owners. MySQL JS > var session=mysqlx.getSession('root:ab6ea7b0436cbc0c0d49#@localhost') mysqlx.getSession: Argument #1: Invalid URI: Illegal character [#] found at position 25 (ArgumentError) What the, it doesn't like the password format. I'm not a Javascript person, and well this is an example for blogging, which is not what was actually setup, so do it the right way, create a user. # mysql mysql> create user demo@localhost identified by 'qldb'; Query OK, 0 rows affected (0.01 sec) mysql> grant ALL ON sample.* to demo@localhost; Query OK, 0 rows affected, 1 warning (0.01 sec) mysql> SHOW GRANTS FOR demo@localhost; +----------------------------------------------------------+ | Grants for demo@localhost | +----------------------------------------------------------+ | GRANT USAGE ON *.* TO `demo`@`localhost` | | GRANT ALL PRIVILEGES ON `sample`.* TO `demo`@`localhost` | +----------------------------------------------------------+ 2 rows in set (0.00 sec) Back into the MySQL Shell, and hurdle 3. MySQL JS > var session=mysqlx.getSession('demo:qldb@localhost') mysqlx.getSession: Access denied for user 'demo'@'127.0.0.1' (using password: YES) (MySQL Error 1045) Did I create the creds wrong, verify. No my password is correct. # mysql -udemo -pqldb -e "SELECT NOW()" mysql: [Warning] Using a password on the command line interface can be insecure. +---------------------+ | NOW() | +---------------------+ | 2021-03-06 23:15:26 | +---------------------+ I don't have time to debug this, User take 2. mysql> drop user demo@localhost; Query OK, 0 rows affected (0.00 sec) mysql> create user demo@'%' identified by 'qldb'; Query OK, 0 rows affected (0.01 sec) mysql> grant all on demo.* to demo@'%' -> ; Query OK, 0 rows affected (0.00 sec) mysql> show grants; +-- | Grants for root@localhost | +--- | GRANT SELECT, INSERT, UPDATE, DELETE, CREATE, DROP, RELOAD, SHUTDOWN, PROCESS, FILE, REFERENCES, INDEX, ALTER, SHOW DATABASES, SUPER, CREATE TEMPORARY TABLES, LOCK TABLES, EXECUTE, REPLICATION SLAVE, REPLICATION CLIENT, CREATE VIEW, SHOW VIEW, CREATE ROUTINE, ALTER ROUTINE, CREATE USER, EVENT, TRIGGER, CREATE TABLESPACE, CREATE ROLE, DROP ROLE ON *.* TO `root`@`localhost` WITH GRANT OPTION | | GRANT APPLICATION_PASSWORD_ADMIN,AUDIT_ADMIN,BACKUP_ADMIN,BINLOG_ADMIN,BINLOG_ENCRYPTION_ADMIN,CLONE_ADMIN,CONNECTION_ADMIN,ENCRYPTION_KEY_ADMIN,FLUSH_OPTIMIZER_COSTS,FLUSH_STATUS,FLUSH_TABLES,FLUSH_USER_RESOURCES,GROUP_REPLICATION_ADMIN,INNODB_REDO_LOG_ARCHIVE,INNODB_REDO_LOG_ENABLE,PERSIST_RO_VARIABLES_ADMIN,REPLICATION_APPLIER,REPLICATION_SLAVE_ADMIN,RESOURCE_GROUP_ADMIN,RESOURCE_GROUP_USER,ROLE_ADMIN,SERVICE_CONNECTION_ADMIN,SESSION_VARIABLES_ADMIN,SET_USER_ID,SHOW_ROUTINE,SYSTEM_USER,SYSTEM_VARIABLES_ADMIN,TABLE_ENCRYPTION_ADMIN,XA_RECOVER_ADMIN ON *.* TO `root`@`localhost` WITH GRANT OPTION | | GRANT PROXY ON ''@'' TO 'root'@'localhost' WITH GRANT OPTION | +--- 3 rows in set (0.00 sec) mysql> show grants for demo@'%'; +--------------------------------------------------+ | Grants for demo@% | +--------------------------------------------------+ | GRANT USAGE ON *.* TO `demo`@`%` | | GRANT ALL PRIVILEGES ON `demo`.* TO `demo`@`%` | +--------------------------------------------------+ 2 rows in set (0.00 sec) Right, initially I showed grants of not new user, but note to self, I should checkout the MySQL 8 Improved grants. I wonder how RDS MySQL 8 handles these, and how Aurora MySQL 8 will (when it ever drops, that's another story). Third try is a charm, so nice to also see queries with 0.0000 execution granularity. MySQL JS > var session=mysqlx.getSession('demo:qldb@localhost') MySQL JS > var sql='SELECT * FROM demo.sample' MySQL JS > session.sql(sql) +----+----------+----------+-------------+ | id | name | location | domain | +----+----------+----------+-------------+ | 1 | Demo Row | USA | NULL | | 2 | Row 2 | AUS | news.com.au | | 3 | Kiwi | NZ | NULL | +----+----------+----------+-------------+ 3 rows in set (0.0006 sec) Get that now in JSON output. NOTE: There are 3 different JSON formats, this matched what I needed. bash-4.2# mysqlsh MySQL Shell 8.0.22 Copyright (c) 2016, 2020, Oracle and/or its affiliates. Oracle is a registered trademark of Oracle Corporation and/or its affiliates. Other names may be trademarks of their respective owners. Type 'help' or '?' for help; 'quit' to exit. MySQL JS > var session=mysqlx.getSession('demo:qldb@localhost') MySQL JS > var sql='SELECT * FROM demo.sample' MySQL JS > shell.options.set('resultFormat','json/array') MySQL JS > session.sql(sql) [ {"id":1,"name":"Demo Row","location":"USA","domain":null}, {"id":2,"name":"Row 2","location":"AUS","domain":"news.com.au"}, {"id":3,"name":"Kiwi","location":"NZ","domain":null} ] 3 rows in set (0.0006 sec) Ok, that works in interactive interface, I need it scripted. # vi bash: vi: command not found # yum install vi Loaded plugins: ovl http://repo.mysql.com/yum/mysql-connectors-community/el/7/x86_64/repodata/repomd.xml: [Errno 14] HTTP Error 403 - Forbidden Trying other mirror. ... And another downer of Docker containers, other tools or easy ways to install them, again I want to focus on the actual example, and not all this preamble, so # echo "var session=mysqlx.getSession('demo:qldb@localhost') var sql='SELECT * FROM demo.sample' shell.options.set('resultFormat','json/array') session.sql(sql)" > dump.js # mysqlsh What the? Hurdle 4. Did I typo this as well, I check the file, and cut/paste it and get what I expect. # cat dump.js var session=mysqlx.getSession('demo:qldb@localhost') var sql='SELECT * FROM demo.sample' shell.options.set('resultFormat','json/array') session.sql(sql) # mysqlsh MySQL Shell 8.0.22 Copyright (c) 2016, 2020, Oracle and/or its affiliates. Oracle is a registered trademark of Oracle Corporation and/or its affiliates. Other names may be trademarks of their respective owners. Type 'help' or '?' for help; 'quit' to exit. MySQL JS > var session=mysqlx.getSession('demo:qldb@localhost') MySQL JS > var sql='SELECT * FROM demo.sample' MySQL JS > shell.options.set('resultFormat','json/array') MySQL JS > session.sql(sql) [ {"id":1,"name":"Demo Row","location":"USA","domain":null}, {"id":2,"name":"Row 2","location":"AUS","domain":"news.com.au"}, {"id":3,"name":"Kiwi","location":"NZ","domain":null} ] 3 rows in set (0.0022 sec) This is getting crazy. # echo '[ > {"id":1,"name":"Demo Row","location":"USA","domain":null}, > {"id":2,"name":"Row 2","location":"AUS","domain":"news.com.au"}, > {"id":3,"name":"Kiwi","location":"NZ","domain":null} > ]' > sample.json bash-4.2# jq . sample.json bash: jq: command not found Oh the docker!!!!. Switching back to my EC2 instance now. $ echo '[ > {"id":1,"name":"Demo Row","location":"USA","domain":null}, > {"id":2,"name":"Row 2","location":"AUS","domain":"news.com.au"}, > {"id":3,"name":"Kiwi","location":"NZ","domain":null} > ]' > sample.json $ jq . sample.json [ { "id": 1, "name": "Demo Row", "location": "USA", "domain": null }, { "id": 2, "name": "Row 2", "location": "AUS", "domain": "news.com.au" }, { "id": 3, "name": "Kiwi", "location": "NZ", "domain": null } ] I am now way of the time I would like to spend on this weekly post, and it's getting way to long, and I'm nowhere near showing what I actually want. Still we trek on. Boy, this stock EC2 image uses version 1, we need I'm sure V2, and well command does not work!!!! $ aws qldb list-ledgers ERROR: $ aws --version $ curl "https://awscli.amazonaws.com/awscli-exe-linux-x86_64.zip" -o "awscliv2.zip" $ unzip awscliv2.zip $ sudo ./aws/install $ export PATH=/usr/local/bin:$PATH $ aws --version Can I finally get a ledger now. $ aws qldb create-ledger --name demo --tags JIRA=DEMO-5826,Owner=RonaldBradford --permissions-mode ALLOW_ALL --no-deletion-protection { "Name": "demo", "Arn": "arn:aws:qldb:us-east-1:999:ledger/demo", "State": "CREATING", "CreationDateTime": "2021-03-06T22:46:41.760000+00:00", "DeletionProtection": false } $ aws qldb list-ledgers { "Ledgers": [ { "Name": "xx", "State": "ACTIVE", "CreationDateTime": "2021-03-05T20:12:44.611000+00:00" }, { "Name": "demo", "State": "ACTIVE", "CreationDateTime": "2021-03-06T22:46:41.760000+00:00" } ] } $ aws qldb describe-ledger --name demo { "Name": "demo", "Arn": "arn:aws:qldb:us-east-1:999:ledger/demo", "State": "ACTIVE", "CreationDateTime": "2021-03-06T22:46:41.760000+00:00", "DeletionProtection": false } Oh the Python 2, and the lack of user packaging, more crud of getting an example. $ pip install pyqldb==3.1.0 ERROR $ echo "alias python=python3 alias pip=pip3" >> ~/.bash_profile source ~/.bash_profile $ pip --version pip 9.0.3 from /usr/lib/python3.6/site-packages (python 3.6) $ python --version Python 3.6.8 $ pip install pyqldb==3.1.0 ERROR $ sudo pip install pyqldb==3.1.0 Yeah!, after all that, my example code works and data is inserted. $ cat demo.py from pyqldb.config.retry_config import RetryConfig from pyqldb.driver.qldb_driver import QldbDriver # Configure retry limit to 3 retry_config = RetryConfig(retry_limit=3) # Initialize the driver print("Initializing the driver") qldb_driver = QldbDriver("demo", retry_config=retry_config) def create_table(transaction_executor, table): print("Creating table {}".format(table)) transaction_executor.execute_statement("Create TABLE {}".format(table)) def create_index(transaction_executor, table, column): print("Creating index {}.{}".format(table, column)) transaction_executor.execute_statement("CREATE INDEX ON {}({})".format(table,column)) def insert_record(transaction_executor, table, values): print("Inserting into {}".format(table)) transaction_executor.execute_statement("INSERT INTO {} ?".format(table), values) table="sample" column="id" qldb_driver.execute_lambda(lambda executor: create_table(executor, table)) qldb_driver.execute_lambda(lambda executor: create_index(executor, table, column)) record1 = { 'id': "1", 'name': "Demo Row", 'location': "USA", 'domain': "" } qldb_driver.execute_lambda(lambda x: insert_record(x, table, record1)) $ python demo.py Initializing the driver Creating table sample Creating index sample.id Inserting into sample One vets in the AWS Console, but you cannot show that in text in this blog, so goes to find a simple client and there is qldbshell What the? I installed it and it complains about pyqldb.driver.pooled_qldb_driver. I literally used that in the last example. $ pip3 install qldbshell Collecting qldbshell Downloading https://artifactory.lifion.oneadp.com/artifactory/api/pypi/pypi/packages/packages/0f/f7/fe984d797e0882c5e141a4888709ae958eb8c48007a23e94000507439f83/qldbshell-1.2.0.tar.gz (68kB) 100% |████████████████████████████████| 71kB 55.6MB/s Requirement already satisfied: boto3>=1.9.237 in /usr/local/lib/python3.6/site-packages (from qldbshell) Collecting amazon.ion=0.5.0 (from qldbshell) Downloading https://artifactory.lifion.oneadp.com/artifactory/api/pypi/pypi/packages/packages/4e/b7/21b7a7577cc6864d1c93fd710701e4764af6cf0f7be36fae4f9673ae11fc/amazon.ion-0.5.0.tar.gz (178kB) 100% |████████████████████████████████| 184kB 78.7MB/s Requirement already satisfied: prompt_toolkit=3.0.5 in /usr/local/lib/python3.6/site-packages (from qldbshell) Requirement already satisfied: ionhash~=1.1.0 in /usr/local/lib/python3.6/site-packages (from qldbshell) Requirement already satisfied: s3transfer=0.3.0 in /usr/local/lib/python3.6/site-packages (from boto3>=1.9.237->qldbshell) Requirement already satisfied: jmespath=0.7.1 in /usr/local/lib/python3.6/site-packages (from boto3>=1.9.237->qldbshell) Requirement already satisfied: botocore=1.20.21 in /usr/local/lib/python3.6/site-packages (from boto3>=1.9.237->qldbshell) Requirement already satisfied: six in /usr/local/lib/python3.6/site-packages (from amazon.ion=0.5.0->qldbshell) Requirement already satisfied: wcwidth in /usr/local/lib/python3.6/site-packages (from prompt_toolkit=3.0.5->qldbshell) Requirement already satisfied: python-dateutil=2.1 in /usr/local/lib/python3.6/site-packages (from botocore=1.20.21->boto3>=1.9.237->qldbshell) Requirement already satisfied: urllib3=1.25.4 in /usr/local/lib/python3.6/site-packages (from botocore=1.20.21->boto3>=1.9.237->qldbshell) Installing collected packages: amazon.ion, qldbshell Found existing installation: amazon.ion 0.7.0 Uninstalling amazon.ion-0.7.0: Exception: Traceback (most recent call last): File "/usr/lib64/python3.6/shutil.py", line 550, in move os.rename(src, real_dst) PermissionError: [Errno 13] Permission denied: '/usr/local/lib/python3.6/site-packages/amazon.ion-0.7.0-py3.6-nspkg.pth' -> '/tmp/pip-p8j4d45d-uninstall/usr/local/lib/python3.6/site-packages/amazon.ion-0.7.0-py3.6-nspkg.pth' During handling of the above exception, another exception occurred: Traceback (most recent call last): File "/usr/lib/python3.6/site-packages/pip/basecommand.py", line 215, in main status = self.run(options, args) File "/usr/lib/python3.6/site-packages/pip/commands/install.py", line 365, in run strip_file_prefix=options.strip_file_prefix, File "/usr/lib/python3.6/site-packages/pip/req/req_set.py", line 783, in install requirement.uninstall(auto_confirm=True) File "/usr/lib/python3.6/site-packages/pip/req/req_install.py", line 754, in uninstall paths_to_remove.remove(auto_confirm) File "/usr/lib/python3.6/site-packages/pip/req/req_uninstall.py", line 115, in remove renames(path, new_path) File "/usr/lib/python3.6/site-packages/pip/utils/__init__.py", line 267, in renames shutil.move(old, new) File "/usr/lib64/python3.6/shutil.py", line 565, in move os.unlink(src) PermissionError: [Errno 13] Permission denied: '/usr/local/lib/python3.6/site-packages/amazon.ion-0.7.0-py3.6-nspkg.pth' [centos@ip-10-204-101-224] ~ $ sudo pip3 install qldbshell WARNING: Running pip install with root privileges is generally not a good idea. Try `pip3 install --user` instead. Collecting qldbshell Downloading https://artifactory.lifion.oneadp.com/artifactory/api/pypi/pypi/packages/packages/0f/f7/fe984d797e0882c5e141a4888709ae958eb8c48007a23e94000507439f83/qldbshell-1.2.0.tar.gz (68kB) 100% |████████████████████████████████| 71kB 49.8MB/s Requirement already satisfied: boto3>=1.9.237 in /usr/local/lib/python3.6/site-packages (from qldbshell) Collecting amazon.ion=0.5.0 (from qldbshell) Downloading https://artifactory.lifion.oneadp.com/artifactory/api/pypi/pypi/packages/packages/4e/b7/21b7a7577cc6864d1c93fd710701e4764af6cf0f7be36fae4f9673ae11fc/amazon.ion-0.5.0.tar.gz (178kB) 100% |████████████████████████████████| 184kB 27.7MB/s Requirement already satisfied: prompt_toolkit=3.0.5 in /usr/local/lib/python3.6/site-packages (from qldbshell) Requirement already satisfied: ionhash~=1.1.0 in /usr/local/lib/python3.6/site-packages (from qldbshell) Requirement already satisfied: botocore=1.20.21 in /usr/local/lib/python3.6/site-packages (from boto3>=1.9.237->qldbshell) Requirement already satisfied: jmespath=0.7.1 in /usr/local/lib/python3.6/site-packages (from boto3>=1.9.237->qldbshell) Requirement already satisfied: s3transfer=0.3.0 in /usr/local/lib/python3.6/site-packages (from boto3>=1.9.237->qldbshell) Requirement already satisfied: six in /usr/local/lib/python3.6/site-packages (from amazon.ion=0.5.0->qldbshell) Requirement already satisfied: wcwidth in /usr/local/lib/python3.6/site-packages (from prompt_toolkit=3.0.5->qldbshell) Requirement already satisfied: python-dateutil=2.1 in /usr/local/lib/python3.6/site-packages (from botocore=1.20.21->boto3>=1.9.237->qldbshell) Requirement already satisfied: urllib3=1.25.4 in /usr/local/lib/python3.6/site-packages (from botocore=1.20.21->boto3>=1.9.237->qldbshell) Installing collected packages: amazon.ion, qldbshell Found existing installation: amazon.ion 0.7.0 Uninstalling amazon.ion-0.7.0: Successfully uninstalled amazon.ion-0.7.0 Running setup.py install for amazon.ion ... done Running setup.py install for qldbshell ... done Successfully installed amazon.ion-0.5.0 qldbshell-1.2.0 $ sudo pip3 install qldbshell $ qldbshell Traceback (most recent call last): File "/usr/local/bin/qldbshell", line 11, in load_entry_point('qldbshell==1.2.0', 'console_scripts', 'qldbshell')() File "/usr/lib/python3.6/site-packages/pkg_resources/__init__.py", line 476, in load_entry_point return get_distribution(dist).load_entry_point(group, name) File "/usr/lib/python3.6/site-packages/pkg_resources/__init__.py", line 2700, in load_entry_point return ep.load() File "/usr/lib/python3.6/site-packages/pkg_resources/__init__.py", line 2318, in load return self.resolve() File "/usr/lib/python3.6/site-packages/pkg_resources/__init__.py", line 2324, in resolve module = __import__(self.module_name, fromlist=['__name__'], level=0) File "/usr/local/lib/python3.6/site-packages/qldbshell/__main__.py", line 25, in from pyqldb.driver.pooled_qldb_driver import PooledQldbDriver ModuleNotFoundError: No module named 'pyqldb.driver.pooled_qldb_driver' $ pip list qldbshell DEPRECATION: The default format will switch to columns in the future. You can use --format=(legacy|columns) (or define a format=(legacy|columns) in your pip.conf under the [list] section) to disable this warning. amazon.ion (0.5.0) boto3 (1.17.21) botocore (1.20.21) ionhash (1.1.0) jmespath (0.10.0) pip (9.0.3) prompt-toolkit (3.0.16) pyqldb (3.1.0) python-dateutil (2.8.1) qldbshell (1.2.0) s3transfer (0.3.4) setuptools (39.2.0) six (1.15.0) urllib3 (1.26.3) So, uninstalled and re-installed and voila, my data. $ qldbshell usage: qldbshell [-h] [-v] [-s QLDB_SESSION_ENDPOINT] [-r REGION] [-p PROFILE] -l LEDGER qldbshell: error: the following arguments are required: -l/--ledger $ qldbshell -l demo Welcome to the Amazon QLDB Shell version 1.2.0 Use 'start' to initiate and interact with a transaction. 'commit' and 'abort' to commit or abort a transaction. Use 'start; statement 1; statement 2; commit; start; statement 3; commit' to create transactions non-interactively. Use 'help' for the help section. All other commands will be interpreted as PartiQL statements until the 'exit' or 'quit' command is issued. qldbshell > qldbshell > SELECT * FROM sample; INFO: { id: "1", name: "Demo Row", location: "USA", domain: "" } INFO: (0.1718s) qldbshell > q WARNING: Error while executing query: An error occurred (BadRequestException) when calling the SendCommand operation: Lexer Error: at line 1, column 1: invalid character at, '' [U+5c]; INFO: (0.1134s) qldbshell > exit Exiting QLDB Shell Right q is a mysqlism of the client, need to rewire myself. Now, I have a ledger, I created an example table, mocked a row of data and verified. Now I can just load my sample data in JSON I created earlier right? Wrong!!! $ cat load.py import json from pyqldb.config.retry_config import RetryConfig from pyqldb.driver.qldb_driver import QldbDriver # Configure retry limit to 3 retry_config = RetryConfig(retry_limit=3) # Initialize the driver print("Initializing the driver") qldb_driver = QldbDriver("demo", retry_config=retry_config) def insert_record(transaction_executor, table, values): print("Inserting into {}".format(table)) transaction_executor.execute_statement("INSERT INTO {} ?".format(table), values) table="sample" with open('sample.json') as f: data=json.load(f) qldb_driver.execute_lambda(lambda x: insert_record(x, table, data)) $ python load.py Traceback (most recent call last): File "load.py", line 2, in from pyqldb.config.retry_config import RetryConfig ModuleNotFoundError: No module named 'pyqldb' [centos@ip-10-204-101-224] ~ Oh sweet, I'd installed that, and used it, and re-installed it. $ pip list | grep pyqldb DEPRECATION: The default format will switch to columns in the future. You can use --format=(legacy|columns) (or define a format=(legacy|columns) in your pip.conf under the [list] section) to disable this warning. [centos@ip-10-204-101-224] ~ $ sudo pip3 install pyqldb WARNING: Running pip install with root privileges is generally not a good idea. Try `pip3 install --user` instead. Collecting pyqldb Downloading https://artifactory.lifion.oneadp.com/artifactory/api/pypi/pypi/packages/packages/5c/b4/9790b1fad87d7df5b863cbf353689db145bd009d31d854d282b31e1c1781/pyqldb-3.1.0.tar.gz Collecting amazon.ion=0.7.0 (from pyqldb) Downloading https://artifactory.lifion.oneadp.com/artifactory/api/pypi/pypi/packages/packages/7d/ac/fd1edee54cefa425c444b51ad00a20e5bc74263a3afbfd4c8743040f8f26/amazon.ion-0.7.0.tar.gz (211kB) 100% |████████████████████████████████| 215kB 24.8MB/s Requirement already satisfied: boto3=1.16.56 in /usr/local/lib/python3.6/site-packages (from pyqldb) Requirement already satisfied: botocore=1.19.56 in /usr/local/lib/python3.6/site-packages (from pyqldb) Requirement already satisfied: ionhash=1.1.0 in /usr/local/lib/python3.6/site-packages (from pyqldb) Requirement already satisfied: six in /usr/local/lib/python3.6/site-packages (from amazon.ion=0.7.0->pyqldb) Requirement already satisfied: s3transfer=0.3.0 in /usr/local/lib/python3.6/site-packages (from boto3=1.16.56->pyqldb) Requirement already satisfied: jmespath=0.7.1 in /usr/local/lib/python3.6/site-packages (from boto3=1.16.56->pyqldb) Requirement already satisfied: python-dateutil=2.1 in /usr/local/lib/python3.6/site-packages (from botocore=1.19.56->pyqldb) Requirement already satisfied: urllib3=1.25.4 in /usr/local/lib/python3.6/site-packages (from botocore=1.19.56->pyqldb) Installing collected packages: amazon.ion, pyqldb Found existing installation: amazon.ion 0.5.0 Uninstalling amazon.ion-0.5.0: Successfully uninstalled amazon.ion-0.5.0 Running setup.py install for amazon.ion ... done Running setup.py install for pyqldb ... done Successfully installed amazon.ion-0.7.0 pyqldb-3.1.0 Load one more time. $ cat load.py import json from pyqldb.config.retry_config import RetryConfig from pyqldb.driver.qldb_driver import QldbDriver # Configure retry limit to 3 retry_config = RetryConfig(retry_limit=3) # Initialize the driver print("Initializing the driver") qldb_driver = QldbDriver("demo", retry_config=retry_config) def insert_record(transaction_executor, table, values): print("Inserting into {}".format(table)) transaction_executor.execute_statement("INSERT INTO {} ?".format(table), values) table="sample" with open('sample.json') as f: data=json.load(f) qldb_driver.execute_lambda(lambda x: insert_record(x, table, data)) $ python load.py Initializing the driver Inserting into sample And done, I've got my JSON extracted MySQL 8 data in QLDB. I go to vett it in the client, and boy, didn't expect yet another package screw up. Clearly, these 2 AWS python packages are incompatible. That's a venv need, but I'm now at double my desired time to show this. $ qldbshell -l demo Traceback (most recent call last): File "/usr/local/bin/qldbshell", line 11, in load_entry_point('qldbshell==1.2.0', 'console_scripts', 'qldbshell')() File "/usr/lib/python3.6/site-packages/pkg_resources/__init__.py", line 476, in load_entry_point return get_distribution(dist).load_entry_point(group, name) File "/usr/lib/python3.6/site-packages/pkg_resources/__init__.py", line 2700, in load_entry_point return ep.load() File "/usr/lib/python3.6/site-packages/pkg_resources/__init__.py", line 2318, in load return self.resolve() File "/usr/lib/python3.6/site-packages/pkg_resources/__init__.py", line 2324, in resolve module = __import__(self.module_name, fromlist=['__name__'], level=0) File "/usr/local/lib/python3.6/site-packages/qldbshell/__main__.py", line 25, in from pyqldb.driver.pooled_qldb_driver import PooledQldbDriver ModuleNotFoundError: No module named 'pyqldb.driver.pooled_qldb_driver' [centos@ip-10-204-101-224] ~ $ pip list | grep qldbshell DEPRECATION: The default format will switch to columns in the future. You can use --format=(legacy|columns) (or define a format=(legacy|columns) in your pip.conf under the [list] section) to disable this warning. qldbshell (1.2.0) $ sudo pip uninstall qldbshell pyqldb $ sudo pip install qldbshell WARNING: Running pip install with root privileges is generally not a good idea. Try `pip3 install --user` instead. Collecting qldbshell Downloading https://artifactory.lifion.oneadp.com/artifactory/api/pypi/pypi/packages/packages/0f/f7/fe984d797e0882c5e141a4888709ae958eb8c48007a23e94000507439f83/qldbshell-1.2.0.tar.gz (68kB) 100% |████████████████████████████████| 71kB 43.4MB/s Requirement already satisfied: boto3>=1.9.237 in /usr/local/lib/python3.6/site-packages (from qldbshell) Requirement already satisfied: amazon.ion=0.5.0 in /usr/local/lib/python3.6/site-packages (from qldbshell) Requirement already satisfied: prompt_toolkit=3.0.5 in /usr/local/lib/python3.6/site-packages (from qldbshell) Requirement already satisfied: ionhash~=1.1.0 in /usr/local/lib/python3.6/site-packages (from qldbshell) Requirement already satisfied: s3transfer=0.3.0 in /usr/local/lib/python3.6/site-packages (from boto3>=1.9.237->qldbshell) Requirement already satisfied: botocore=1.20.21 in /usr/local/lib/python3.6/site-packages (from boto3>=1.9.237->qldbshell) Requirement already satisfied: jmespath=0.7.1 in /usr/local/lib/python3.6/site-packages (from boto3>=1.9.237->qldbshell) Requirement already satisfied: six in /usr/local/lib/python3.6/site-packages (from amazon.ion=0.5.0->qldbshell) Requirement already satisfied: wcwidth in /usr/local/lib/python3.6/site-packages (from prompt_toolkit=3.0.5->qldbshell) Requirement already satisfied: python-dateutil=2.1 in /usr/local/lib/python3.6/site-packages (from botocore=1.20.21->boto3>=1.9.237->qldbshell) Requirement already satisfied: urllib3=1.25.4 in /usr/local/lib/python3.6/site-packages (from botocore=1.20.21->boto3>=1.9.237->qldbshell) Installing collected packages: qldbshell Running setup.py install for qldbshell ... done Successfully installed qldbshell-1.2.0 Can I see my data now $ qldbshell -l demo Welcome to the Amazon QLDB Shell version 1.2.0 Use 'start' to initiate and interact with a transaction. 'commit' and 'abort' to commit or abort a transaction. Use 'start; statement 1; statement 2; commit; start; statement 3; commit' to create transactions non-interactively. Use 'help' for the help section. All other commands will be interpreted as PartiQL statements until the 'exit' or 'quit' command is issued. qldbshell > select * from sample; INFO: { id: 1, name: "Demo Row", location: "USA", domain: null }, { id: 1, name: "Demo Row", location: "USA", domain: null }, { id: "1", name: "Demo Row", location: "USA", domain: "" }, { id: 3, name: "Kiwi", location: "NZ", domain: null }, { id: 2, name: "Row 2", location: "AUS", domain: "news.com.au" }, { id: 3, name: "Kiwi", location: "NZ", domain: null }, { id: 2, name: "Row 2", location: "AUS", domain: "news.com.au" } INFO: (0.0815s) And yes, data, I see it's duplicated, so I must have in between the 10 steps run twice. This does highlight a known limitation of QLDB, no unique constraints. But wait, that data is not really correct, I don't want null. Goes back to the JSON to see the MySQL shell gives that. $ jq . sample.json [ { "id": 1, "name": "Demo Row", "location": "USA", "domain": null }, ... At some point I also got this load error, but by now I've given up documenting how to do something, in order to demonstrate something. NameError: name 'null' is not defined One has to wrap the only nullable column with IFNULL(subdomain,'') as subdomain and redo all those steps again. This is not going to be practical having to wrap all columns in a wider table with IFNULL. However, having exhausted all this time for what was supposed to be a quiet weekend few hours, my post is way to long, and I've learned "Creating examples can be hard". http://ronaldbradford.com/blog/wdiltw-creating-examples-can-be-hard-2021-03-06/
0 notes